claude-threads 1.34.2 → 1.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2196,6 +2196,231 @@ var init_logger = __esm(() => {
2196
2196
  wsLogger = createLogger("ws", false);
2197
2197
  });
2198
2198
 
2199
+ // src/config/types.ts
2200
+ function isOverheadVisibility(value) {
2201
+ return typeof value === "string" && OVERHEAD_VISIBILITY_VALUES.includes(value);
2202
+ }
2203
+ function resolveOverheadVisibility(value, fieldPath) {
2204
+ if (value === undefined || value === null)
2205
+ return DEFAULT_OVERHEAD_VISIBILITY;
2206
+ if (isOverheadVisibility(value))
2207
+ return value;
2208
+ throw new Error(`Invalid ${fieldPath}: expected one of ${OVERHEAD_VISIBILITY_VALUES.join(", ")}, got ${JSON.stringify(value)}`);
2209
+ }
2210
+ function resolveMemoryConfig(value, fieldPath) {
2211
+ if (value === undefined || value === null || value === true)
2212
+ return DEFAULT_MEMORY_CONFIG;
2213
+ if (value === false)
2214
+ return MEMORY_DISABLED;
2215
+ if (typeof value === "object" && !Array.isArray(value)) {
2216
+ const obj = value;
2217
+ const bool2 = (v, name, dflt) => resolveBooleanFeature(v, `${fieldPath ?? "memory"}.${name}`, { default: dflt, verb: `using default (${dflt})` });
2218
+ const enabled = bool2(obj.enabled, "enabled", true);
2219
+ if (!enabled)
2220
+ return MEMORY_DISABLED;
2221
+ return {
2222
+ enabled: true,
2223
+ repoLayer: bool2(obj.repoLayer, "repoLayer", true),
2224
+ channelLayer: bool2(obj.channelLayer, "channelLayer", true),
2225
+ distillation: bool2(obj.distillation, "distillation", true)
2226
+ };
2227
+ }
2228
+ console.warn(`Invalid ${fieldPath ?? "memory"} config: expected boolean or {enabled, repoLayer, channelLayer, distillation}, got ${JSON.stringify(value)} — using defaults`);
2229
+ return DEFAULT_MEMORY_CONFIG;
2230
+ }
2231
+ function resolveRoutinesEnabled(value, fieldPath) {
2232
+ return resolveBooleanFeature(value, fieldPath ?? "routines", { default: true, verb: "routines stay enabled" });
2233
+ }
2234
+ function resolveTranscriptionEnabled(value, fieldPath) {
2235
+ if (value === undefined || value === null)
2236
+ return true;
2237
+ if (typeof value === "boolean")
2238
+ return value;
2239
+ console.warn(`Invalid ${fieldPath ?? "transcription"}: ${JSON.stringify(value)} — expected true or false. ` + `Transcription is DISABLED for this platform: a value we cannot read is not consent to upload its audio.`);
2240
+ return false;
2241
+ }
2242
+ function resolveBooleanFeature(value, fieldPath, opts) {
2243
+ if (value === true || value === false)
2244
+ return value;
2245
+ if (value === undefined || value === null)
2246
+ return opts.default;
2247
+ console.warn(`Invalid ${fieldPath} config: expected boolean, got ${JSON.stringify(value)} — ${opts.verb}`);
2248
+ return opts.default;
2249
+ }
2250
+ function resolveWatchesEnabled(value, fieldPath) {
2251
+ return resolveBooleanFeature(value, fieldPath ?? "watches", { default: true, verb: "watches stay enabled" });
2252
+ }
2253
+ function resolveAuditLogEnabled(value, fieldPath) {
2254
+ return resolveBooleanFeature(value, fieldPath ?? "auditLog", { default: false, verb: "audit log stays off" });
2255
+ }
2256
+ function isRemoteMcpServer(server) {
2257
+ return server.type === "http" || server.type === "sse";
2258
+ }
2259
+ function resolveStrictMcpConfig(value, fieldPath) {
2260
+ return resolveBooleanFeature(value, fieldPath ?? "strictMcpConfig", {
2261
+ default: false,
2262
+ verb: "the operator's MCP sources stay available"
2263
+ });
2264
+ }
2265
+ function resolveClaudeAiConnectors(value, fieldPath) {
2266
+ return resolveBooleanFeature(value, fieldPath ?? "claudeAiConnectors", {
2267
+ default: false,
2268
+ verb: "claude.ai connectors stay disabled"
2269
+ });
2270
+ }
2271
+ function isStringRecord(value) {
2272
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((v) => typeof v === "string");
2273
+ }
2274
+ function validateMcpServers(value, fieldPath) {
2275
+ if (value === undefined || value === null)
2276
+ return {};
2277
+ if (typeof value !== "object" || Array.isArray(value)) {
2278
+ throw new Error(`Invalid ${fieldPath}: expected a map of server name → {command, args?, env?} or {type: http|sse, url, headers?}`);
2279
+ }
2280
+ const out = {};
2281
+ for (const [name, raw] of Object.entries(value)) {
2282
+ const path = `${fieldPath}.${name}`;
2283
+ if (name === BOT_MCP_SERVER_NAME) {
2284
+ throw new Error(`Invalid ${path}: "${BOT_MCP_SERVER_NAME}" is the bot's own server and cannot be redefined`);
2285
+ }
2286
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(name)) {
2287
+ throw new Error(`Invalid ${path}: server names may contain letters, digits, "_" and "-" only`);
2288
+ }
2289
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
2290
+ throw new Error(`Invalid ${path}: expected an object`);
2291
+ }
2292
+ const s = Object.fromEntries(Object.entries(raw).filter(([, v]) => v !== null && v !== undefined));
2293
+ if (typeof s.command === "string" && typeof s.url === "string") {
2294
+ throw new Error(`Invalid ${path}: has both command (stdio) and url (http/sse); keep one`);
2295
+ }
2296
+ const type2 = s.type ?? (typeof s.url === "string" ? "http" : "stdio");
2297
+ if (type2 === "http" || type2 === "sse") {
2298
+ const unknown = Object.keys(s).filter((k) => !REMOTE_KEYS.has(k));
2299
+ if (unknown.length > 0) {
2300
+ throw new Error(`Invalid ${path}: unknown key(s) ${unknown.join(", ")}; a ${type2} server takes type, url, headers`);
2301
+ }
2302
+ if (typeof s.url !== "string" || s.url.length === 0) {
2303
+ throw new Error(`Invalid ${path}: a ${type2} server needs a url`);
2304
+ }
2305
+ if (s.headers !== undefined && !isStringRecord(s.headers)) {
2306
+ throw new Error(`Invalid ${path}.headers: expected a map of strings`);
2307
+ }
2308
+ out[name] = { type: type2, url: s.url, ...s.headers ? { headers: s.headers } : {} };
2309
+ } else if (type2 === "stdio") {
2310
+ const unknown = Object.keys(s).filter((k) => !STDIO_KEYS.has(k));
2311
+ if (unknown.length > 0) {
2312
+ throw new Error(`Invalid ${path}: unknown key(s) ${unknown.join(", ")}; a stdio server takes type, command, args, env`);
2313
+ }
2314
+ if (typeof s.command !== "string" || s.command.length === 0) {
2315
+ throw new Error(`Invalid ${path}: a stdio server needs a command (or set type: http|sse with a url)`);
2316
+ }
2317
+ if (s.args !== undefined && !(Array.isArray(s.args) && s.args.every((a) => typeof a === "string"))) {
2318
+ throw new Error(`Invalid ${path}.args: expected a list of strings`);
2319
+ }
2320
+ if (s.env !== undefined && !isStringRecord(s.env)) {
2321
+ throw new Error(`Invalid ${path}.env: expected a map of strings`);
2322
+ }
2323
+ out[name] = { type: "stdio", command: s.command, args: s.args ?? [], env: s.env ?? {} };
2324
+ } else {
2325
+ throw new Error(`Invalid ${path}.type: expected stdio, http or sse, got ${JSON.stringify(s.type)}`);
2326
+ }
2327
+ }
2328
+ return out;
2329
+ }
2330
+ function resolveMcpServers(global2, platform, fieldPath) {
2331
+ return { ...validateMcpServers(global2, "mcpServers"), ...validateMcpServers(platform, fieldPath) };
2332
+ }
2333
+ function resolveLimits(limits) {
2334
+ const envMaxSessions = process.env.MAX_SESSIONS ? parseInt(process.env.MAX_SESSIONS, 10) : undefined;
2335
+ const envSessionTimeout = process.env.SESSION_TIMEOUT_MS ? Math.round(parseInt(process.env.SESSION_TIMEOUT_MS, 10) / 60000) : undefined;
2336
+ return {
2337
+ maxSessions: limits?.maxSessions ?? envMaxSessions ?? LIMITS_DEFAULTS.maxSessions,
2338
+ sessionTimeoutMinutes: limits?.sessionTimeoutMinutes ?? envSessionTimeout ?? LIMITS_DEFAULTS.sessionTimeoutMinutes,
2339
+ sessionWarningMinutes: limits?.sessionWarningMinutes ?? LIMITS_DEFAULTS.sessionWarningMinutes,
2340
+ cleanupIntervalMinutes: limits?.cleanupIntervalMinutes ?? LIMITS_DEFAULTS.cleanupIntervalMinutes,
2341
+ maxWorktreeAgeHours: limits?.maxWorktreeAgeHours ?? LIMITS_DEFAULTS.maxWorktreeAgeHours,
2342
+ cleanupWorktrees: limits?.cleanupWorktrees ?? LIMITS_DEFAULTS.cleanupWorktrees,
2343
+ permissionTimeoutSeconds: limits?.permissionTimeoutSeconds ?? LIMITS_DEFAULTS.permissionTimeoutSeconds,
2344
+ flushDelayMs: limits?.flushDelayMs ?? LIMITS_DEFAULTS.flushDelayMs,
2345
+ maxRoutines: limits?.maxRoutines ?? LIMITS_DEFAULTS.maxRoutines,
2346
+ maxWatches: limits?.maxWatches ?? LIMITS_DEFAULTS.maxWatches,
2347
+ watchCooldownMinutes: limits?.watchCooldownMinutes ?? LIMITS_DEFAULTS.watchCooldownMinutes,
2348
+ watchDailyCap: limits?.watchDailyCap ?? LIMITS_DEFAULTS.watchDailyCap
2349
+ };
2350
+ }
2351
+ function resolvePermissionMode(opts) {
2352
+ if (opts.permissionMode)
2353
+ return opts.permissionMode;
2354
+ if (opts.skipPermissions === true)
2355
+ return "bypass";
2356
+ if (opts.skipPermissions === false)
2357
+ return "default";
2358
+ return "default";
2359
+ }
2360
+ function permissionModeDisplay(mode) {
2361
+ const info = MODE_INFO[mode];
2362
+ return { icon: info.icon, label: info.label, chip: `${info.icon} ${info.label}` };
2363
+ }
2364
+ function permissionModeDescription(mode) {
2365
+ return MODE_INFO[mode].description;
2366
+ }
2367
+ function effectivePermissionMode(input) {
2368
+ if (input.override)
2369
+ return input.override;
2370
+ if (input.sessionHasInteractiveOverride)
2371
+ return "default";
2372
+ return input.botWideMode;
2373
+ }
2374
+ var OVERHEAD_VISIBILITY_VALUES, DEFAULT_OVERHEAD_VISIBILITY = "full", DEFAULT_MEMORY_CONFIG, MEMORY_DISABLED, BOT_MCP_SERVER_NAME = "claude-threads-mcp", STDIO_KEYS, REMOTE_KEYS, LIMITS_DEFAULTS, MODE_INFO;
2375
+ var init_types = __esm(() => {
2376
+ OVERHEAD_VISIBILITY_VALUES = ["full", "minimal", "hidden"];
2377
+ DEFAULT_MEMORY_CONFIG = {
2378
+ enabled: true,
2379
+ repoLayer: true,
2380
+ channelLayer: true,
2381
+ distillation: true
2382
+ };
2383
+ MEMORY_DISABLED = {
2384
+ enabled: false,
2385
+ repoLayer: false,
2386
+ channelLayer: false,
2387
+ distillation: false
2388
+ };
2389
+ STDIO_KEYS = new Set(["type", "command", "args", "env"]);
2390
+ REMOTE_KEYS = new Set(["type", "url", "headers"]);
2391
+ LIMITS_DEFAULTS = {
2392
+ maxSessions: 5,
2393
+ sessionTimeoutMinutes: 30,
2394
+ sessionWarningMinutes: 5,
2395
+ cleanupIntervalMinutes: 60,
2396
+ maxWorktreeAgeHours: 24,
2397
+ cleanupWorktrees: true,
2398
+ permissionTimeoutSeconds: 120,
2399
+ flushDelayMs: 500,
2400
+ maxRoutines: 10,
2401
+ maxWatches: 10,
2402
+ watchCooldownMinutes: 5,
2403
+ watchDailyCap: 20
2404
+ };
2405
+ MODE_INFO = {
2406
+ default: {
2407
+ icon: "\uD83D\uDD10",
2408
+ label: "Default",
2409
+ description: "Every tool-use prompts for approval."
2410
+ },
2411
+ auto: {
2412
+ icon: "⚡",
2413
+ label: "Auto",
2414
+ description: "Claude classifier auto-approves low-risk tools; high-risk still prompts."
2415
+ },
2416
+ bypass: {
2417
+ icon: "⚠️",
2418
+ label: "Bypass",
2419
+ description: "No prompts — every tool-use is allowed."
2420
+ }
2421
+ };
2422
+ });
2423
+
2199
2424
  // src/utils/spawn.ts
2200
2425
  import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "child_process";
2201
2426
  function addWindowsShell(options) {
@@ -4058,7 +4283,7 @@ var require_semver2 = __commonJS((exports, module) => {
4058
4283
 
4059
4284
  // src/claude/version-check.ts
4060
4285
  import { execSync } from "child_process";
4061
- import { existsSync as existsSync2 } from "fs";
4286
+ import { existsSync as existsSync3 } from "fs";
4062
4287
  import { join as join3 } from "path";
4063
4288
  function tryClaudeVersion(claudePath) {
4064
4289
  try {
@@ -4117,7 +4342,7 @@ function getClaudeCliVersion() {
4117
4342
  return pathResult;
4118
4343
  }
4119
4344
  for (const path of COMMON_CLAUDE_PATHS) {
4120
- if (existsSync2(path)) {
4345
+ if (existsSync3(path)) {
4121
4346
  const result = tryClaudeVersion(path);
4122
4347
  if (!result.error) {
4123
4348
  return result;
@@ -4156,7 +4381,7 @@ function getClaudePath() {
4156
4381
  return whichResult;
4157
4382
  }
4158
4383
  for (const path of COMMON_CLAUDE_PATHS) {
4159
- if (existsSync2(path)) {
4384
+ if (existsSync3(path)) {
4160
4385
  const result = tryClaudeVersion(path);
4161
4386
  if (!result.error) {
4162
4387
  discoveredClaudePath = path;
@@ -4239,6 +4464,690 @@ var init_version_check = __esm(() => {
4239
4464
  ];
4240
4465
  });
4241
4466
 
4467
+ // src/mcp/outbound-env.ts
4468
+ var OUTBOUND_ENV;
4469
+ var init_outbound_env = __esm(() => {
4470
+ OUTBOUND_ENV = {
4471
+ SESSION_WORKING_DIR: "SESSION_WORKING_DIR",
4472
+ SESSION_UPLOAD_DIR: "SESSION_UPLOAD_DIR",
4473
+ OUTBOUND_FILES_ENABLED: "OUTBOUND_FILES_ENABLED",
4474
+ OUTBOUND_FILES_MAX_BYTES: "OUTBOUND_FILES_MAX_BYTES"
4475
+ };
4476
+ });
4477
+
4478
+ // src/mcp/agent-features-env.ts
4479
+ var AGENT_FEATURES_ENV;
4480
+ var init_agent_features_env = __esm(() => {
4481
+ AGENT_FEATURES_ENV = {
4482
+ MEMORY_CHANNEL_ENABLED: "CT_MEMORY_CHANNEL_ENABLED",
4483
+ ROUTINES_ENABLED: "CT_ROUTINES_ENABLED",
4484
+ WATCHES_ENABLED: "CT_WATCHES_ENABLED",
4485
+ UNATTENDED: "CT_UNATTENDED",
4486
+ DCM: "CT_DCM"
4487
+ };
4488
+ });
4489
+
4490
+ // src/claude/rate-limit-detector.ts
4491
+ function detectRateLimit(text, now = Date.now()) {
4492
+ if (!text)
4493
+ return { detected: false };
4494
+ let matched;
4495
+ for (const phrase of RATE_LIMIT_PHRASES) {
4496
+ const m = text.match(phrase);
4497
+ if (m) {
4498
+ matched = m[0];
4499
+ break;
4500
+ }
4501
+ }
4502
+ if (!matched)
4503
+ return { detected: false };
4504
+ const resetAtEpochMs = extractResetAt(text, now);
4505
+ return { detected: true, matched, resetAtEpochMs };
4506
+ }
4507
+ function cooldownDeadline(hit, now = Date.now()) {
4508
+ if (!hit.detected)
4509
+ return now;
4510
+ return hit.resetAtEpochMs ?? now + DEFAULT_COOLDOWN_MS;
4511
+ }
4512
+ function extractResetAt(text, now) {
4513
+ const relative = text.match(/(?:retry[_\s-]?after|resets?\s+in)\s+(\d+)\s*(second|minute|hour|day)s?/i);
4514
+ if (relative) {
4515
+ const value = parseInt(relative[1], 10);
4516
+ const unit = relative[2].toLowerCase();
4517
+ const unitMs = {
4518
+ second: 1000,
4519
+ minute: 60000,
4520
+ hour: 3600000,
4521
+ day: 86400000
4522
+ };
4523
+ return now + value * unitMs[unit];
4524
+ }
4525
+ const unix = text.match(/\breset(?:_at)?\b\s*["']?\s*[:=]\s*(\d{10,13})/);
4526
+ if (unix) {
4527
+ const raw = parseInt(unix[1], 10);
4528
+ return unix[1].length === 13 ? raw : raw * 1000;
4529
+ }
4530
+ const clock = text.match(/resets?\s+at\s+(\d{1,2}):(\d{2})\s*(utc|gmt)?/i);
4531
+ if (clock) {
4532
+ const hh = parseInt(clock[1], 10);
4533
+ const mm = parseInt(clock[2], 10);
4534
+ if (hh < 24 && mm < 60) {
4535
+ const reference = new Date(now);
4536
+ const target = new Date(Date.UTC(reference.getUTCFullYear(), reference.getUTCMonth(), reference.getUTCDate(), hh, mm)).getTime();
4537
+ return target > now ? target : target + 86400000;
4538
+ }
4539
+ }
4540
+ return;
4541
+ }
4542
+ function parseRateLimitEvent(event, now = Date.now()) {
4543
+ const info = event?.rate_limit_info;
4544
+ if (!info || typeof info !== "object")
4545
+ return { detected: false };
4546
+ const { status, resetsAt } = info;
4547
+ if (status !== "rejected")
4548
+ return { detected: false };
4549
+ const PAST_SKEW_TOLERANCE_MS = 2 * 60000;
4550
+ let resetAtEpochMs;
4551
+ if (typeof resetsAt === "number") {
4552
+ const ms = resetsAt * 1000;
4553
+ if (ms > now && ms - now < 8 * 86400000) {
4554
+ resetAtEpochMs = ms;
4555
+ } else if (ms <= now && now - ms < PAST_SKEW_TOLERANCE_MS) {
4556
+ resetAtEpochMs = now + 60000;
4557
+ }
4558
+ }
4559
+ return { detected: true, matched: `rate_limit_event status=${status}`, resetAtEpochMs };
4560
+ }
4561
+ var RATE_LIMIT_PHRASES, DEFAULT_COOLDOWN_MS;
4562
+ var init_rate_limit_detector = __esm(() => {
4563
+ RATE_LIMIT_PHRASES = [
4564
+ /usage limit reached/i,
4565
+ /rate[_\s-]?limit[_\s-]?error/i,
4566
+ /you have hit the rate limit/i,
4567
+ /quota (has been )?exceeded/i,
4568
+ /\b429\b.*(rate|limit|quota)/i
4569
+ ];
4570
+ DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;
4571
+ });
4572
+
4573
+ // src/claude/cli.ts
4574
+ import { EventEmitter } from "events";
4575
+ import { resolve as resolve2, dirname as dirname2 } from "path";
4576
+ import { fileURLToPath } from "url";
4577
+ import { existsSync as existsSync4, readFileSync as readFileSync2, watchFile, unwatchFile, unlinkSync, statSync, readdirSync, writeFileSync as writeFileSync2 } from "fs";
4578
+ import { tmpdir as tmpdir2 } from "os";
4579
+ import { join as join4 } from "path";
4580
+ function cleanupBrowserBridgeSockets() {
4581
+ try {
4582
+ const tempDir = tmpdir2();
4583
+ const files = readdirSync(tempDir);
4584
+ for (const file of files) {
4585
+ if (file.startsWith("claude-mcp-browser-bridge-")) {
4586
+ const filePath = join4(tempDir, file);
4587
+ try {
4588
+ const stats = statSync(filePath);
4589
+ if (stats.isSocket()) {
4590
+ unlinkSync(filePath);
4591
+ log3.debug(`Removed stale browser bridge socket: ${file}`);
4592
+ }
4593
+ } catch {}
4594
+ }
4595
+ }
4596
+ } catch (err) {
4597
+ log3.debug(`Browser bridge cleanup failed: ${err}`);
4598
+ }
4599
+ }
4600
+ function buildClaudeChildEnv(parentEnv, account, opts) {
4601
+ const env = { ...parentEnv };
4602
+ if (opts?.claudeAiConnectors !== true) {
4603
+ env.ENABLE_CLAUDEAI_MCP_SERVERS = "false";
4604
+ }
4605
+ if (env.MCP_CONNECTION_NONBLOCKING === undefined) {
4606
+ env.MCP_CONNECTION_NONBLOCKING = "true";
4607
+ }
4608
+ if (env.ENABLE_PROMPT_CACHING_1H === undefined) {
4609
+ env.ENABLE_PROMPT_CACHING_1H = "true";
4610
+ }
4611
+ if (opts?.disableAutoMemory) {
4612
+ env.CLAUDE_CODE_DISABLE_AUTO_MEMORY = "1";
4613
+ }
4614
+ if (opts?.decisionBridge && env.MCP_TOOL_TIMEOUT === undefined) {
4615
+ env.MCP_TOOL_TIMEOUT = "3600000";
4616
+ }
4617
+ if (account?.home) {
4618
+ env.HOME = account.home;
4619
+ env.USERPROFILE = account.home;
4620
+ delete env.ANTHROPIC_API_KEY;
4621
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
4622
+ delete env.ANTHROPIC_AUTH_TOKEN;
4623
+ delete env.CLAUDE_CONFIG_DIR;
4624
+ delete env.CLAUDE_SECURESTORAGE_CONFIG_DIR;
4625
+ } else if (account?.apiKey) {
4626
+ env.ANTHROPIC_API_KEY = account.apiKey;
4627
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
4628
+ delete env.ANTHROPIC_AUTH_TOKEN;
4629
+ }
4630
+ return env;
4631
+ }
4632
+ function buildInlineSettings(statusLineCommand, memory, mcp = {}) {
4633
+ const settings = {};
4634
+ if (mcp.claudeAiConnectors !== true) {
4635
+ settings.disableClaudeAiConnectors = true;
4636
+ }
4637
+ if (statusLineCommand) {
4638
+ settings.statusLine = {
4639
+ type: "command",
4640
+ command: statusLineCommand,
4641
+ padding: 0
4642
+ };
4643
+ }
4644
+ if (memory) {
4645
+ settings.autoMemoryEnabled = true;
4646
+ settings.autoMemoryDirectory = memory.autoMemoryDir;
4647
+ }
4648
+ return Object.keys(settings).length > 0 ? settings : null;
4649
+ }
4650
+ function runtimeForScriptPath(scriptPath) {
4651
+ return scriptPath.endsWith(".ts") ? process.execPath : "node";
4652
+ }
4653
+ function isErrorResultEvent(event) {
4654
+ const ev = event;
4655
+ if (typeof ev.subtype === "string" && ev.subtype.startsWith("error"))
4656
+ return true;
4657
+ if (ev.is_error === true)
4658
+ return true;
4659
+ return false;
4660
+ }
4661
+ function materializeMcpConfig(config, sessionId, opts = {}) {
4662
+ if (opts.inline) {
4663
+ return { mode: "inline", value: JSON.stringify(config) };
4664
+ }
4665
+ const dir = opts.tmpDirOverride ?? tmpdir2();
4666
+ const path = join4(dir, `claude-threads-mcp-${sessionId ?? process.pid}-${Date.now()}.json`);
4667
+ writeFileSync2(path, JSON.stringify(config), { mode: 384 });
4668
+ return { mode: "file", path };
4669
+ }
4670
+ function buildPermissionArgs(opts) {
4671
+ const args = [];
4672
+ if (opts.permissionMode === "bypass" && !opts.platformConfig) {
4673
+ args.push("--dangerously-skip-permissions");
4674
+ return { args, tempFile: null };
4675
+ }
4676
+ if (!opts.platformConfig) {
4677
+ throw new Error(`platformConfig is required when permissionMode is '${opts.permissionMode}'`);
4678
+ }
4679
+ const mcpEnv = {
4680
+ PLATFORM_TYPE: opts.platformConfig.type,
4681
+ PLATFORM_URL: opts.platformConfig.url,
4682
+ PLATFORM_TOKEN: opts.platformConfig.token,
4683
+ PLATFORM_CHANNEL_ID: opts.platformConfig.channelId,
4684
+ PLATFORM_THREAD_ID: opts.threadId || "",
4685
+ ALLOWED_USERS: opts.platformConfig.allowedUsers.join(","),
4686
+ DEBUG: opts.debug ? "1" : "",
4687
+ PERMISSION_TIMEOUT_MS: String(opts.permissionTimeoutMs),
4688
+ SESSION_OWNER_USERNAME: opts.sessionOwnerUsername || ""
4689
+ };
4690
+ if (opts.decisionBridgePath) {
4691
+ mcpEnv.DECISION_BRIDGE_PATH = opts.decisionBridgePath;
4692
+ if (process.env.DECISION_BRIDGE_TIMEOUT_MS) {
4693
+ mcpEnv.DECISION_BRIDGE_TIMEOUT_MS = process.env.DECISION_BRIDGE_TIMEOUT_MS;
4694
+ }
4695
+ const features = opts.agentFeatures;
4696
+ if (features) {
4697
+ if (features.memoryChannel)
4698
+ mcpEnv[AGENT_FEATURES_ENV.MEMORY_CHANNEL_ENABLED] = "1";
4699
+ if (features.routines)
4700
+ mcpEnv[AGENT_FEATURES_ENV.ROUTINES_ENABLED] = "1";
4701
+ if (features.watches)
4702
+ mcpEnv[AGENT_FEATURES_ENV.WATCHES_ENABLED] = "1";
4703
+ if (features.unattended)
4704
+ mcpEnv[AGENT_FEATURES_ENV.UNATTENDED] = "1";
4705
+ if (features.dcm)
4706
+ mcpEnv[AGENT_FEATURES_ENV.DCM] = "1";
4707
+ }
4708
+ }
4709
+ if (opts.platformConfig.appToken) {
4710
+ mcpEnv.PLATFORM_APP_TOKEN = opts.platformConfig.appToken;
4711
+ }
4712
+ if (opts.workingDir) {
4713
+ mcpEnv[OUTBOUND_ENV.SESSION_WORKING_DIR] = opts.workingDir;
4714
+ }
4715
+ if (opts.uploadDir) {
4716
+ mcpEnv[OUTBOUND_ENV.SESSION_UPLOAD_DIR] = opts.uploadDir;
4717
+ }
4718
+ if (opts.outboundFiles?.enabled === false) {
4719
+ mcpEnv[OUTBOUND_ENV.OUTBOUND_FILES_ENABLED] = "0";
4720
+ }
4721
+ if (typeof opts.outboundFiles?.maxBytes === "number" && Number.isFinite(opts.outboundFiles.maxBytes) && opts.outboundFiles.maxBytes > 0) {
4722
+ mcpEnv[OUTBOUND_ENV.OUTBOUND_FILES_MAX_BYTES] = String(opts.outboundFiles.maxBytes);
4723
+ }
4724
+ const mcpConfig = {
4725
+ mcpServers: {
4726
+ "claude-threads-mcp": {
4727
+ type: "stdio",
4728
+ command: runtimeForScriptPath(opts.mcpServerPath),
4729
+ args: [opts.mcpServerPath],
4730
+ env: mcpEnv
4731
+ }
4732
+ }
4733
+ };
4734
+ for (const [name, server] of Object.entries(opts.platformConfig.mcpServers ?? {})) {
4735
+ if (name === BOT_MCP_SERVER_NAME)
4736
+ continue;
4737
+ mcpConfig.mcpServers[name] = isRemoteMcpServer(server) ? { type: server.type, url: server.url, ...server.headers ? { headers: server.headers } : {} } : { type: "stdio", command: server.command, args: server.args ?? [], env: server.env ?? {} };
4738
+ }
4739
+ const materialized = materializeMcpConfig(mcpConfig, opts.sessionId, { inline: opts.inline });
4740
+ let tempFile = null;
4741
+ if (materialized.mode === "file") {
4742
+ tempFile = materialized.path;
4743
+ args.push("--mcp-config", materialized.path);
4744
+ } else {
4745
+ args.push("--mcp-config", materialized.value);
4746
+ }
4747
+ if (opts.platformConfig.strictMcpConfig === true) {
4748
+ args.push("--strict-mcp-config");
4749
+ }
4750
+ if (opts.permissionMode === "bypass") {
4751
+ args.push("--dangerously-skip-permissions");
4752
+ } else {
4753
+ args.push("--permission-prompt-tool", "mcp__claude-threads-mcp__permission_prompt");
4754
+ if (opts.permissionMode === "auto") {
4755
+ args.push("--permission-mode", "auto");
4756
+ }
4757
+ }
4758
+ return { args, tempFile };
4759
+ }
4760
+ var log3, STDERR_PER_INSTANCE_CAP = 10240, STDERR_AGGREGATE_SOFT_CAP, totalStderrBytes = 0, ClaudeCli;
4761
+ var init_cli = __esm(() => {
4762
+ init_types();
4763
+ init_spawn();
4764
+ init_logger();
4765
+ init_version_check();
4766
+ init_outbound_env();
4767
+ init_agent_features_env();
4768
+ init_rate_limit_detector();
4769
+ log3 = createLogger("claude");
4770
+ STDERR_AGGREGATE_SOFT_CAP = 10 * 1024 * 1024;
4771
+ ClaudeCli = class ClaudeCli extends EventEmitter {
4772
+ process = null;
4773
+ options;
4774
+ buffer = "";
4775
+ debug = process.env.DEBUG === "1" || process.argv.includes("--debug");
4776
+ statusFilePath = null;
4777
+ lastStatusData = null;
4778
+ stderrBuffer = "";
4779
+ mcpConfigTempFile = null;
4780
+ lastEmittedRateLimitDeadline = 0;
4781
+ lastEmittedHitHadExplicitReset = false;
4782
+ log;
4783
+ constructor(options) {
4784
+ super();
4785
+ this.options = options;
4786
+ this.log = options.logSessionId ? createLogger("claude").forSession(options.logSessionId) : createLogger("claude");
4787
+ }
4788
+ getStatusFilePath() {
4789
+ return this.statusFilePath;
4790
+ }
4791
+ getStatusData() {
4792
+ if (!this.statusFilePath)
4793
+ return null;
4794
+ try {
4795
+ if (existsSync4(this.statusFilePath)) {
4796
+ const data = readFileSync2(this.statusFilePath, "utf8");
4797
+ this.lastStatusData = JSON.parse(data);
4798
+ }
4799
+ } catch (err) {
4800
+ this.log.debug(`Failed to read status file: ${err}`);
4801
+ }
4802
+ return this.lastStatusData;
4803
+ }
4804
+ startStatusWatch() {
4805
+ if (!this.statusFilePath) {
4806
+ this.log.debug("No status file path, skipping status watch");
4807
+ return;
4808
+ }
4809
+ this.log.debug(`Starting status watch: ${this.statusFilePath}`);
4810
+ const checkStatus = () => {
4811
+ const data = this.getStatusData();
4812
+ if (data && data.timestamp !== this.lastStatusData?.timestamp) {
4813
+ this.lastStatusData = data;
4814
+ this.emit("status", data);
4815
+ }
4816
+ };
4817
+ watchFile(this.statusFilePath, { interval: 1000 }, checkStatus);
4818
+ }
4819
+ stopStatusWatch() {
4820
+ if (this.statusFilePath) {
4821
+ unwatchFile(this.statusFilePath);
4822
+ try {
4823
+ if (existsSync4(this.statusFilePath)) {
4824
+ unlinkSync(this.statusFilePath);
4825
+ }
4826
+ } catch {}
4827
+ }
4828
+ }
4829
+ start() {
4830
+ if (this.process)
4831
+ throw new Error("Already running");
4832
+ totalStderrBytes -= this.stderrBuffer.length;
4833
+ this.stderrBuffer = "";
4834
+ this.lastEmittedRateLimitDeadline = 0;
4835
+ this.lastEmittedHitHadExplicitReset = false;
4836
+ cleanupBrowserBridgeSockets();
4837
+ const claudePath = getClaudePath();
4838
+ const args = [
4839
+ "--input-format",
4840
+ "stream-json",
4841
+ "--output-format",
4842
+ "stream-json",
4843
+ "--verbose"
4844
+ ];
4845
+ if (this.options.sessionId) {
4846
+ if (this.options.resume) {
4847
+ args.push("--resume", this.options.sessionId);
4848
+ } else {
4849
+ args.push("--session-id", this.options.sessionId);
4850
+ }
4851
+ }
4852
+ const permissionMode = this.options.permissionMode ?? "default";
4853
+ const permResult = buildPermissionArgs({
4854
+ permissionMode,
4855
+ mcpServerPath: this.getMcpServerPath(),
4856
+ platformConfig: this.options.platformConfig,
4857
+ threadId: this.options.threadId,
4858
+ sessionId: this.options.sessionId,
4859
+ permissionTimeoutMs: this.options.permissionTimeoutMs ?? 120000,
4860
+ debug: this.debug,
4861
+ workingDir: this.options.workingDir,
4862
+ uploadDir: this.options.uploadDir,
4863
+ outboundFiles: this.options.outboundFiles,
4864
+ sessionOwnerUsername: this.options.sessionOwnerUsername,
4865
+ decisionBridgePath: this.options.decisionBridgePath,
4866
+ agentFeatures: this.options.agentFeatures
4867
+ });
4868
+ args.push(...permResult.args);
4869
+ this.mcpConfigTempFile = permResult.tempFile;
4870
+ if (this.options.chrome) {
4871
+ args.push("--chrome");
4872
+ }
4873
+ if (this.options.appendSystemPrompt) {
4874
+ args.push("--append-system-prompt", this.options.appendSystemPrompt);
4875
+ }
4876
+ let statusLineCommand;
4877
+ if (this.options.sessionId) {
4878
+ this.statusFilePath = join4(tmpdir2(), `claude-threads-status-${this.options.sessionId}.json`);
4879
+ const statusLineWriterPath = this.getStatusLineWriterPath();
4880
+ const runtime = runtimeForScriptPath(statusLineWriterPath);
4881
+ statusLineCommand = `${runtime} ${statusLineWriterPath} ${this.options.sessionId}`;
4882
+ }
4883
+ const settings = buildInlineSettings(statusLineCommand, this.options.memory, {
4884
+ claudeAiConnectors: this.options.platformConfig?.claudeAiConnectors
4885
+ });
4886
+ if (settings) {
4887
+ args.push("--settings", JSON.stringify(settings));
4888
+ }
4889
+ this.log.debug(`Starting: ${claudePath} ${args.slice(0, 5).join(" ")}...`);
4890
+ const childEnv = this.buildChildEnv();
4891
+ if (this.options.account) {
4892
+ this.log.debug(`Spawning under Claude account "${this.options.account.id}"`);
4893
+ }
4894
+ this.process = crossSpawn(claudePath, args, {
4895
+ cwd: this.options.workingDir,
4896
+ env: childEnv,
4897
+ stdio: ["pipe", "pipe", "pipe"]
4898
+ });
4899
+ this.log.debug(`Claude process spawned: pid=${this.process.pid}`);
4900
+ this.process.stdout?.on("data", (chunk) => {
4901
+ this.parseOutput(chunk.toString());
4902
+ });
4903
+ this.process.stderr?.on("data", (chunk) => {
4904
+ const text = chunk.toString();
4905
+ const before = this.stderrBuffer.length;
4906
+ this.stderrBuffer += text;
4907
+ const cap = totalStderrBytes > STDERR_AGGREGATE_SOFT_CAP ? 1024 : STDERR_PER_INSTANCE_CAP;
4908
+ if (this.stderrBuffer.length > cap) {
4909
+ this.stderrBuffer = this.stderrBuffer.slice(-cap);
4910
+ }
4911
+ totalStderrBytes += this.stderrBuffer.length - before;
4912
+ this.log.debug(`stderr: ${text.trim()}`);
4913
+ if (process.env.INTEGRATION_TEST === "1") {
4914
+ process.stderr.write(text);
4915
+ }
4916
+ this.maybeEmitRateLimit(text);
4917
+ });
4918
+ this.process.on("error", (err) => {
4919
+ this.log.error(`Claude error: ${err}`);
4920
+ this.emit("error", err);
4921
+ });
4922
+ this.process.on("exit", (code) => {
4923
+ this.log.debug(`Exited ${code}`);
4924
+ this.process = null;
4925
+ this.buffer = "";
4926
+ totalStderrBytes -= this.stderrBuffer.length;
4927
+ if (this.mcpConfigTempFile) {
4928
+ const path = this.mcpConfigTempFile;
4929
+ this.mcpConfigTempFile = null;
4930
+ try {
4931
+ unlinkSync(path);
4932
+ } catch {}
4933
+ }
4934
+ this.emit("exit", code);
4935
+ });
4936
+ }
4937
+ sendMessage(content) {
4938
+ if (!this.process?.stdin)
4939
+ throw new Error("Not running");
4940
+ const msg = JSON.stringify({
4941
+ type: "user",
4942
+ message: { role: "user", content }
4943
+ }) + `
4944
+ `;
4945
+ const preview = content.substring(0, 50);
4946
+ this.log.debug(`Sending: ${preview}...`);
4947
+ if (process.env.INTEGRATION_TEST === "1") {
4948
+ const stack = new Error().stack?.split(`
4949
+ `).slice(2, 6).join(" > ").replace(/\s+at\s+/g, " < ") ?? "?";
4950
+ process.stderr.write(`[claude-cli sendMessage pid=${this.process.pid}] ${preview} | ${stack}
4951
+ `);
4952
+ }
4953
+ this.process.stdin.write(msg);
4954
+ }
4955
+ sendToolResult(toolUseId, content) {
4956
+ if (!this.process?.stdin)
4957
+ throw new Error("Not running");
4958
+ const msg = JSON.stringify({
4959
+ type: "user",
4960
+ message: {
4961
+ role: "user",
4962
+ content: [{
4963
+ type: "tool_result",
4964
+ tool_use_id: toolUseId,
4965
+ content: typeof content === "string" ? content : JSON.stringify(content)
4966
+ }]
4967
+ }
4968
+ }) + `
4969
+ `;
4970
+ this.log.debug(`Sending tool_result for ${toolUseId}`);
4971
+ this.process.stdin.write(msg);
4972
+ }
4973
+ parseOutput(data) {
4974
+ this.buffer += data;
4975
+ const lines = this.buffer.split(`
4976
+ `);
4977
+ this.buffer = lines.pop() || "";
4978
+ for (const line of lines) {
4979
+ const trimmed = line.trim();
4980
+ if (!trimmed)
4981
+ continue;
4982
+ let event;
4983
+ try {
4984
+ event = JSON.parse(trimmed);
4985
+ } catch {
4986
+ continue;
4987
+ }
4988
+ try {
4989
+ this.emit("event", event);
4990
+ } catch (err) {
4991
+ this.log.error(`'event' listener threw while handling a ${event.type} event: ${err}`);
4992
+ }
4993
+ if (event.type === "result" && isErrorResultEvent(event)) {
4994
+ this.maybeEmitRateLimit(trimmed);
4995
+ }
4996
+ if (event.type === "rate_limit_event") {
4997
+ this.maybeEmitRateLimitHit(parseRateLimitEvent(event));
4998
+ }
4999
+ }
5000
+ }
5001
+ maybeEmitRateLimit(text) {
5002
+ this.maybeEmitRateLimitHit(detectRateLimit(text));
5003
+ }
5004
+ maybeEmitRateLimitHit(hit) {
5005
+ if (!hit.detected)
5006
+ return;
5007
+ if (!hit.resetAtEpochMs && this.lastEmittedHitHadExplicitReset && this.lastEmittedRateLimitDeadline > Date.now()) {
5008
+ return;
5009
+ }
5010
+ const newDeadline = cooldownDeadline(hit);
5011
+ const MIN_ADVANCE_MS = 60000;
5012
+ if (newDeadline - this.lastEmittedRateLimitDeadline < MIN_ADVANCE_MS) {
5013
+ if (hit.resetAtEpochMs !== undefined) {
5014
+ this.lastEmittedHitHadExplicitReset = true;
5015
+ }
5016
+ return;
5017
+ }
5018
+ this.lastEmittedRateLimitDeadline = newDeadline;
5019
+ this.lastEmittedHitHadExplicitReset = hit.resetAtEpochMs !== undefined;
5020
+ this.log.warn(`Rate limit detected: ${hit.matched ?? "(no match text)"}`);
5021
+ this.emit("rate-limit", hit);
5022
+ }
5023
+ isRunning() {
5024
+ return this.process !== null;
5025
+ }
5026
+ getLastStderr() {
5027
+ return this.stderrBuffer;
5028
+ }
5029
+ isPermanentFailure() {
5030
+ const stderr = this.stderrBuffer;
5031
+ if (stderr.includes("claude-mcp-browser-bridge") && (stderr.includes("EOPNOTSUPP") || stderr.includes("ENOENT"))) {
5032
+ return true;
5033
+ }
5034
+ if (stderr.includes("No conversation found with session ID")) {
5035
+ return true;
5036
+ }
5037
+ return false;
5038
+ }
5039
+ getPermanentFailureReason() {
5040
+ const stderr = this.stderrBuffer;
5041
+ if (stderr.includes("claude-mcp-browser-bridge") && (stderr.includes("EOPNOTSUPP") || stderr.includes("ENOENT"))) {
5042
+ return "Claude browser bridge state from a previous session is no longer accessible. This typically happens when a session with Chrome integration is resumed after a restart.";
5043
+ }
5044
+ if (stderr.includes("No conversation found with session ID")) {
5045
+ return "The conversation history for this session no longer exists. This can happen if Claude's history was cleared or if the session was created on a different machine.";
5046
+ }
5047
+ return null;
5048
+ }
5049
+ kill() {
5050
+ this.stopStatusWatch();
5051
+ if (!this.process) {
5052
+ this.log.debug("Kill called but process not running");
5053
+ return Promise.resolve();
5054
+ }
5055
+ const proc = this.process;
5056
+ const pid = proc.pid;
5057
+ this.process = null;
5058
+ this.log.debug(`Killing Claude process (pid=${pid})`);
5059
+ if (process.env.INTEGRATION_TEST === "1") {
5060
+ const stack = new Error().stack?.split(`
5061
+ `).slice(2, 7).join(" > ").replace(/\s+at\s+/g, " < ") ?? "?";
5062
+ process.stderr.write(`[claude-cli kill pid=${pid}] | ${stack}
5063
+ `);
5064
+ }
5065
+ return new Promise((resolve3) => {
5066
+ this.log.debug("Sending first SIGINT");
5067
+ proc.kill("SIGINT");
5068
+ const secondSigint = setTimeout(() => {
5069
+ try {
5070
+ this.log.debug("Sending second SIGINT");
5071
+ proc.kill("SIGINT");
5072
+ } catch {}
5073
+ }, 100);
5074
+ const forceKillTimeout = setTimeout(() => {
5075
+ try {
5076
+ this.log.debug("Sending SIGTERM (force kill)");
5077
+ proc.kill("SIGTERM");
5078
+ } catch {}
5079
+ }, 2000);
5080
+ const settle = (reason) => {
5081
+ this.log.debug(`Claude process gone (${reason})`);
5082
+ clearTimeout(secondSigint);
5083
+ clearTimeout(forceKillTimeout);
5084
+ clearTimeout(lastResort);
5085
+ resolve3();
5086
+ };
5087
+ const lastResort = setTimeout(() => {
5088
+ try {
5089
+ this.log.warn("Claude process did not exit after SIGTERM — sending SIGKILL");
5090
+ proc.kill("SIGKILL");
5091
+ } catch {}
5092
+ settle("kill timeout");
5093
+ }, 5000);
5094
+ proc.once("close", (code) => settle(`closed, code=${code}`));
5095
+ proc.once("error", () => settle("spawn error"));
5096
+ });
5097
+ }
5098
+ interrupt() {
5099
+ if (!this.process) {
5100
+ this.log.debug("Interrupt called but process not running");
5101
+ return false;
5102
+ }
5103
+ this.log.debug(`Interrupting Claude process (pid=${this.process.pid})`);
5104
+ this.process.kill("SIGINT");
5105
+ return true;
5106
+ }
5107
+ buildChildEnv() {
5108
+ return buildClaudeChildEnv(process.env, this.options.account, {
5109
+ claudeAiConnectors: this.options.platformConfig?.claudeAiConnectors,
5110
+ decisionBridge: this.options.decisionBridgePath !== undefined,
5111
+ disableAutoMemory: this.options.memory === null
5112
+ });
5113
+ }
5114
+ getMcpServerPath() {
5115
+ const __filename2 = fileURLToPath(import.meta.url);
5116
+ const __dirname2 = dirname2(__filename2);
5117
+ const bundledPath = resolve2(__dirname2, "mcp", "mcp-server.js");
5118
+ if (existsSync4(bundledPath)) {
5119
+ return bundledPath;
5120
+ }
5121
+ const sourceLayoutPath = resolve2(__dirname2, "..", "mcp", "mcp-server.js");
5122
+ if (existsSync4(sourceLayoutPath)) {
5123
+ return sourceLayoutPath;
5124
+ }
5125
+ const tsPath = resolve2(__dirname2, "..", "mcp", "mcp-server.ts");
5126
+ if (existsSync4(tsPath)) {
5127
+ return tsPath;
5128
+ }
5129
+ return sourceLayoutPath;
5130
+ }
5131
+ getStatusLineWriterPath() {
5132
+ const __filename2 = fileURLToPath(import.meta.url);
5133
+ const __dirname2 = dirname2(__filename2);
5134
+ const bundledPath = resolve2(__dirname2, "statusline", "writer.js");
5135
+ if (existsSync4(bundledPath)) {
5136
+ return bundledPath;
5137
+ }
5138
+ const sourceLayoutPath = resolve2(__dirname2, "..", "statusline", "writer.js");
5139
+ if (existsSync4(sourceLayoutPath)) {
5140
+ return sourceLayoutPath;
5141
+ }
5142
+ const tsPath = resolve2(__dirname2, "..", "statusline", "writer.ts");
5143
+ if (existsSync4(tsPath)) {
5144
+ return tsPath;
5145
+ }
5146
+ return sourceLayoutPath;
5147
+ }
5148
+ };
5149
+ });
5150
+
4242
5151
  // src/utils/emoji.ts
4243
5152
  var exports_emoji = {};
4244
5153
  __export(exports_emoji, {
@@ -8699,7 +9608,7 @@ async function quickQuery(options) {
8699
9608
  let resolved = false;
8700
9609
  const proc = crossSpawn(claudePath, args, {
8701
9610
  cwd: workingDir || process.cwd(),
8702
- env: process.env,
9611
+ env: buildClaudeChildEnv(process.env),
8703
9612
  stdio: ["pipe", "pipe", "pipe"]
8704
9613
  });
8705
9614
  const timeoutId = setTimeout(() => {
@@ -8763,6 +9672,7 @@ async function quickQuery(options) {
8763
9672
  var log20;
8764
9673
  var init_quick_query = __esm(() => {
8765
9674
  init_spawn();
9675
+ init_cli();
8766
9676
  init_version_check();
8767
9677
  init_logger();
8768
9678
  log20 = createLogger("query");
@@ -18445,7 +19355,7 @@ var require_react_reconciler_development = __commonJS((exports, module) => {
18445
19355
  return hook.checkDCE ? true : false;
18446
19356
  }
18447
19357
  function setIsStrictModeForDevtools(newIsStrictMode) {
18448
- typeof log54 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
19358
+ typeof log55 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
18449
19359
  if (injectedHook && typeof injectedHook.setStrictMode === "function")
18450
19360
  try {
18451
19361
  injectedHook.setStrictMode(rendererID, newIsStrictMode);
@@ -26529,7 +27439,7 @@ Check the render method of %s.`, getComponentNameFromFiber(current) || "Unknown"
26529
27439
  var fiberStack = [];
26530
27440
  var index$jscomp$0 = -1, emptyContextObject = {};
26531
27441
  Object.freeze(emptyContextObject);
26532
- var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority, log54 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
27442
+ var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority, log55 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
26533
27443
  if (typeof performance === "object" && typeof performance.now === "function") {
26534
27444
  var localPerformance = performance;
26535
27445
  var getCurrentTime = function() {
@@ -46489,7 +47399,7 @@ function getSessionStatus(session) {
46489
47399
  }
46490
47400
 
46491
47401
  // src/config/index.ts
46492
- import { existsSync, readFileSync, writeFileSync, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
47402
+ import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
46493
47403
  import { resolve, dirname } from "path";
46494
47404
  import { homedir as homedir2 } from "os";
46495
47405
 
@@ -49610,154 +50520,43 @@ function requireJsYaml() {
49610
50520
  var jsYamlExports = requireJsYaml();
49611
50521
  var yaml = /* @__PURE__ */ getDefaultExportFromCjs(jsYamlExports);
49612
50522
 
49613
- // src/config/types.ts
49614
- var OVERHEAD_VISIBILITY_VALUES = ["full", "minimal", "hidden"];
49615
- var DEFAULT_OVERHEAD_VISIBILITY = "full";
49616
- function isOverheadVisibility(value) {
49617
- return typeof value === "string" && OVERHEAD_VISIBILITY_VALUES.includes(value);
49618
- }
49619
- function resolveOverheadVisibility(value, fieldPath) {
49620
- if (value === undefined || value === null)
49621
- return DEFAULT_OVERHEAD_VISIBILITY;
49622
- if (isOverheadVisibility(value))
49623
- return value;
49624
- throw new Error(`Invalid ${fieldPath}: expected one of ${OVERHEAD_VISIBILITY_VALUES.join(", ")}, got ${JSON.stringify(value)}`);
49625
- }
49626
- var DEFAULT_MEMORY_CONFIG = {
49627
- enabled: true,
49628
- repoLayer: true,
49629
- channelLayer: true,
49630
- distillation: true
49631
- };
49632
- var MEMORY_DISABLED = {
49633
- enabled: false,
49634
- repoLayer: false,
49635
- channelLayer: false,
49636
- distillation: false
49637
- };
49638
- function resolveMemoryConfig(value, fieldPath) {
49639
- if (value === undefined || value === null || value === true)
49640
- return DEFAULT_MEMORY_CONFIG;
49641
- if (value === false)
49642
- return MEMORY_DISABLED;
49643
- if (typeof value === "object" && !Array.isArray(value)) {
49644
- const obj = value;
49645
- const bool2 = (v, name, dflt) => resolveBooleanFeature(v, `${fieldPath ?? "memory"}.${name}`, { default: dflt, verb: `using default (${dflt})` });
49646
- const enabled = bool2(obj.enabled, "enabled", true);
49647
- if (!enabled)
49648
- return MEMORY_DISABLED;
49649
- return {
49650
- enabled: true,
49651
- repoLayer: bool2(obj.repoLayer, "repoLayer", true),
49652
- channelLayer: bool2(obj.channelLayer, "channelLayer", true),
49653
- distillation: bool2(obj.distillation, "distillation", true)
49654
- };
49655
- }
49656
- console.warn(`Invalid ${fieldPath ?? "memory"} config: expected boolean or {enabled, repoLayer, channelLayer, distillation}, got ${JSON.stringify(value)} — using defaults`);
49657
- return DEFAULT_MEMORY_CONFIG;
49658
- }
49659
- function resolveRoutinesEnabled(value, fieldPath) {
49660
- return resolveBooleanFeature(value, fieldPath ?? "routines", { default: true, verb: "routines stay enabled" });
49661
- }
49662
- function resolveTranscriptionEnabled(value, fieldPath) {
49663
- if (value === undefined || value === null)
49664
- return true;
49665
- if (typeof value === "boolean")
49666
- return value;
49667
- console.warn(`Invalid ${fieldPath ?? "transcription"}: ${JSON.stringify(value)} — expected true or false. ` + `Transcription is DISABLED for this platform: a value we cannot read is not consent to upload its audio.`);
49668
- return false;
49669
- }
49670
- function resolveBooleanFeature(value, fieldPath, opts) {
49671
- if (value === true || value === false)
49672
- return value;
49673
- if (value === undefined || value === null)
49674
- return opts.default;
49675
- console.warn(`Invalid ${fieldPath} config: expected boolean, got ${JSON.stringify(value)} — ${opts.verb}`);
49676
- return opts.default;
49677
- }
49678
- function resolveWatchesEnabled(value, fieldPath) {
49679
- return resolveBooleanFeature(value, fieldPath ?? "watches", { default: true, verb: "watches stay enabled" });
49680
- }
49681
- function resolveAuditLogEnabled(value, fieldPath) {
49682
- return resolveBooleanFeature(value, fieldPath ?? "auditLog", { default: false, verb: "audit log stays off" });
49683
- }
49684
- var LIMITS_DEFAULTS = {
49685
- maxSessions: 5,
49686
- sessionTimeoutMinutes: 30,
49687
- sessionWarningMinutes: 5,
49688
- cleanupIntervalMinutes: 60,
49689
- maxWorktreeAgeHours: 24,
49690
- cleanupWorktrees: true,
49691
- permissionTimeoutSeconds: 120,
49692
- flushDelayMs: 500,
49693
- maxRoutines: 10,
49694
- maxWatches: 10,
49695
- watchCooldownMinutes: 5,
49696
- watchDailyCap: 20
50523
+ // src/config/index.ts
50524
+ init_types();
50525
+
50526
+ // src/config/managed-mcp.ts
50527
+ import { existsSync } from "fs";
50528
+ var MANAGED_MCP_CONFIG_PATHS = {
50529
+ darwin: "/Library/Application Support/ClaudeCode/managed-mcp.json",
50530
+ linux: "/etc/claude-code/managed-mcp.json",
50531
+ win32: "C:\\Program Files\\ClaudeCode\\managed-mcp.json"
49697
50532
  };
49698
- function resolveLimits(limits) {
49699
- const envMaxSessions = process.env.MAX_SESSIONS ? parseInt(process.env.MAX_SESSIONS, 10) : undefined;
49700
- const envSessionTimeout = process.env.SESSION_TIMEOUT_MS ? Math.round(parseInt(process.env.SESSION_TIMEOUT_MS, 10) / 60000) : undefined;
49701
- return {
49702
- maxSessions: limits?.maxSessions ?? envMaxSessions ?? LIMITS_DEFAULTS.maxSessions,
49703
- sessionTimeoutMinutes: limits?.sessionTimeoutMinutes ?? envSessionTimeout ?? LIMITS_DEFAULTS.sessionTimeoutMinutes,
49704
- sessionWarningMinutes: limits?.sessionWarningMinutes ?? LIMITS_DEFAULTS.sessionWarningMinutes,
49705
- cleanupIntervalMinutes: limits?.cleanupIntervalMinutes ?? LIMITS_DEFAULTS.cleanupIntervalMinutes,
49706
- maxWorktreeAgeHours: limits?.maxWorktreeAgeHours ?? LIMITS_DEFAULTS.maxWorktreeAgeHours,
49707
- cleanupWorktrees: limits?.cleanupWorktrees ?? LIMITS_DEFAULTS.cleanupWorktrees,
49708
- permissionTimeoutSeconds: limits?.permissionTimeoutSeconds ?? LIMITS_DEFAULTS.permissionTimeoutSeconds,
49709
- flushDelayMs: limits?.flushDelayMs ?? LIMITS_DEFAULTS.flushDelayMs,
49710
- maxRoutines: limits?.maxRoutines ?? LIMITS_DEFAULTS.maxRoutines,
49711
- maxWatches: limits?.maxWatches ?? LIMITS_DEFAULTS.maxWatches,
49712
- watchCooldownMinutes: limits?.watchCooldownMinutes ?? LIMITS_DEFAULTS.watchCooldownMinutes,
49713
- watchDailyCap: limits?.watchDailyCap ?? LIMITS_DEFAULTS.watchDailyCap
49714
- };
50533
+ function managedMcpConfigPath(platform = process.platform) {
50534
+ return MANAGED_MCP_CONFIG_PATHS[platform] ?? null;
49715
50535
  }
49716
- function resolvePermissionMode(opts) {
49717
- if (opts.permissionMode)
49718
- return opts.permissionMode;
49719
- if (opts.skipPermissions === true)
49720
- return "bypass";
49721
- if (opts.skipPermissions === false)
49722
- return "default";
49723
- return "default";
50536
+ function managedMcpConfigPresent(platform = process.platform, exists = existsSync) {
50537
+ const path = managedMcpConfigPath(platform);
50538
+ return path !== null && exists(path);
49724
50539
  }
49725
- var MODE_INFO = {
49726
- default: {
49727
- icon: "\uD83D\uDD10",
49728
- label: "Default",
49729
- description: "Every tool-use prompts for approval."
49730
- },
49731
- auto: {
49732
- icon: "⚡",
49733
- label: "Auto",
49734
- description: "Claude classifier auto-approves low-risk tools; high-risk still prompts."
49735
- },
49736
- bypass: {
49737
- icon: "⚠️",
49738
- label: "Bypass",
49739
- description: "No prompts — every tool-use is allowed."
50540
+ // src/config/mcp-posture.ts
50541
+ init_types();
50542
+ function resolvePlatformMcpPosture(platforms, globalMcpServers, managedPresent = () => managedMcpConfigPresent()) {
50543
+ const warnings = [];
50544
+ for (const p of platforms) {
50545
+ p.mcpServers = resolveMcpServers(globalMcpServers, p.mcpServers, `platforms[${p.id}].mcpServers`);
50546
+ p.strictMcpConfig = resolveStrictMcpConfig(p.strictMcpConfig, `platforms[${p.id}].strictMcpConfig`);
50547
+ p.claudeAiConnectors = resolveClaudeAiConnectors(p.claudeAiConnectors, `platforms[${p.id}].claudeAiConnectors`);
50548
+ if (p.strictMcpConfig && managedPresent()) {
50549
+ warnings.push(`platforms[${p.id}].strictMcpConfig ignored: an enterprise managed MCP config is present ` + `(${managedMcpConfigPath() ?? "managed-mcp.json"}) and the Claude CLI refuses --strict-mcp-config alongside it.`);
50550
+ p.strictMcpConfig = false;
50551
+ }
49740
50552
  }
49741
- };
49742
- function permissionModeDisplay(mode) {
49743
- const info = MODE_INFO[mode];
49744
- return { icon: info.icon, label: info.label, chip: `${info.icon} ${info.label}` };
49745
- }
49746
- function permissionModeDescription(mode) {
49747
- return MODE_INFO[mode].description;
49748
- }
49749
- function effectivePermissionMode(input) {
49750
- if (input.override)
49751
- return input.override;
49752
- if (input.sessionHasInteractiveOverride)
49753
- return "default";
49754
- return input.botWideMode;
50553
+ return { warnings };
49755
50554
  }
49756
50555
 
49757
50556
  // src/config/index.ts
49758
50557
  var CONFIG_PATH = resolve(homedir2(), ".config", "claude-threads", "config.yaml");
49759
50558
  function loadConfigWithMigration() {
49760
- if (existsSync(CONFIG_PATH)) {
50559
+ if (existsSync2(CONFIG_PATH)) {
49761
50560
  const content = readFileSync(CONFIG_PATH, "utf-8");
49762
50561
  return yaml.load(content);
49763
50562
  }
@@ -49765,7 +50564,7 @@ function loadConfigWithMigration() {
49765
50564
  }
49766
50565
  function saveConfig(config, path = CONFIG_PATH) {
49767
50566
  const configDir = dirname(path);
49768
- if (!existsSync(configDir)) {
50567
+ if (!existsSync2(configDir)) {
49769
50568
  mkdirSync2(configDir, { recursive: true, mode: 448 });
49770
50569
  }
49771
50570
  const yamlContent = yaml.dump(config, {
@@ -49781,7 +50580,7 @@ function saveConfig(config, path = CONFIG_PATH) {
49781
50580
  } catch {}
49782
50581
  }
49783
50582
  function configExists() {
49784
- return existsSync(CONFIG_PATH);
50583
+ return existsSync2(CONFIG_PATH);
49785
50584
  }
49786
50585
 
49787
50586
  // src/session/authorization.ts
@@ -49909,664 +50708,12 @@ class DecisionBridgeServer {
49909
50708
  }
49910
50709
  }
49911
50710
 
49912
- // src/claude/cli.ts
49913
- init_spawn();
49914
- init_logger();
49915
- init_version_check();
49916
- import { EventEmitter } from "events";
49917
- import { resolve as resolve2, dirname as dirname2 } from "path";
49918
- import { fileURLToPath } from "url";
49919
- import { existsSync as existsSync3, readFileSync as readFileSync2, watchFile, unwatchFile, unlinkSync, statSync, readdirSync, writeFileSync as writeFileSync2 } from "fs";
49920
- import { tmpdir as tmpdir2 } from "os";
49921
- import { join as join4 } from "path";
49922
-
49923
- // src/mcp/outbound-env.ts
49924
- var OUTBOUND_ENV = {
49925
- SESSION_WORKING_DIR: "SESSION_WORKING_DIR",
49926
- SESSION_UPLOAD_DIR: "SESSION_UPLOAD_DIR",
49927
- OUTBOUND_FILES_ENABLED: "OUTBOUND_FILES_ENABLED",
49928
- OUTBOUND_FILES_MAX_BYTES: "OUTBOUND_FILES_MAX_BYTES"
49929
- };
49930
-
49931
- // src/mcp/agent-features-env.ts
49932
- var AGENT_FEATURES_ENV = {
49933
- MEMORY_CHANNEL_ENABLED: "CT_MEMORY_CHANNEL_ENABLED",
49934
- ROUTINES_ENABLED: "CT_ROUTINES_ENABLED",
49935
- WATCHES_ENABLED: "CT_WATCHES_ENABLED",
49936
- UNATTENDED: "CT_UNATTENDED",
49937
- DCM: "CT_DCM"
49938
- };
49939
-
49940
- // src/claude/rate-limit-detector.ts
49941
- var RATE_LIMIT_PHRASES = [
49942
- /usage limit reached/i,
49943
- /rate[_\s-]?limit[_\s-]?error/i,
49944
- /you have hit the rate limit/i,
49945
- /quota (has been )?exceeded/i,
49946
- /\b429\b.*(rate|limit|quota)/i
49947
- ];
49948
- var DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;
49949
- function detectRateLimit(text, now = Date.now()) {
49950
- if (!text)
49951
- return { detected: false };
49952
- let matched;
49953
- for (const phrase of RATE_LIMIT_PHRASES) {
49954
- const m = text.match(phrase);
49955
- if (m) {
49956
- matched = m[0];
49957
- break;
49958
- }
49959
- }
49960
- if (!matched)
49961
- return { detected: false };
49962
- const resetAtEpochMs = extractResetAt(text, now);
49963
- return { detected: true, matched, resetAtEpochMs };
49964
- }
49965
- function cooldownDeadline(hit, now = Date.now()) {
49966
- if (!hit.detected)
49967
- return now;
49968
- return hit.resetAtEpochMs ?? now + DEFAULT_COOLDOWN_MS;
49969
- }
49970
- function extractResetAt(text, now) {
49971
- const relative = text.match(/(?:retry[_\s-]?after|resets?\s+in)\s+(\d+)\s*(second|minute|hour|day)s?/i);
49972
- if (relative) {
49973
- const value = parseInt(relative[1], 10);
49974
- const unit = relative[2].toLowerCase();
49975
- const unitMs = {
49976
- second: 1000,
49977
- minute: 60000,
49978
- hour: 3600000,
49979
- day: 86400000
49980
- };
49981
- return now + value * unitMs[unit];
49982
- }
49983
- const unix = text.match(/\breset(?:_at)?\b\s*["']?\s*[:=]\s*(\d{10,13})/);
49984
- if (unix) {
49985
- const raw = parseInt(unix[1], 10);
49986
- return unix[1].length === 13 ? raw : raw * 1000;
49987
- }
49988
- const clock = text.match(/resets?\s+at\s+(\d{1,2}):(\d{2})\s*(utc|gmt)?/i);
49989
- if (clock) {
49990
- const hh = parseInt(clock[1], 10);
49991
- const mm = parseInt(clock[2], 10);
49992
- if (hh < 24 && mm < 60) {
49993
- const reference = new Date(now);
49994
- const target = new Date(Date.UTC(reference.getUTCFullYear(), reference.getUTCMonth(), reference.getUTCDate(), hh, mm)).getTime();
49995
- return target > now ? target : target + 86400000;
49996
- }
49997
- }
49998
- return;
49999
- }
50000
- function parseRateLimitEvent(event, now = Date.now()) {
50001
- const info = event?.rate_limit_info;
50002
- if (!info || typeof info !== "object")
50003
- return { detected: false };
50004
- const { status, resetsAt } = info;
50005
- if (status !== "rejected")
50006
- return { detected: false };
50007
- const PAST_SKEW_TOLERANCE_MS = 2 * 60000;
50008
- let resetAtEpochMs;
50009
- if (typeof resetsAt === "number") {
50010
- const ms = resetsAt * 1000;
50011
- if (ms > now && ms - now < 8 * 86400000) {
50012
- resetAtEpochMs = ms;
50013
- } else if (ms <= now && now - ms < PAST_SKEW_TOLERANCE_MS) {
50014
- resetAtEpochMs = now + 60000;
50015
- }
50016
- }
50017
- return { detected: true, matched: `rate_limit_event status=${status}`, resetAtEpochMs };
50018
- }
50019
-
50020
- // src/claude/cli.ts
50021
- var log3 = createLogger("claude");
50022
- function cleanupBrowserBridgeSockets() {
50023
- try {
50024
- const tempDir = tmpdir2();
50025
- const files = readdirSync(tempDir);
50026
- for (const file of files) {
50027
- if (file.startsWith("claude-mcp-browser-bridge-")) {
50028
- const filePath = join4(tempDir, file);
50029
- try {
50030
- const stats = statSync(filePath);
50031
- if (stats.isSocket()) {
50032
- unlinkSync(filePath);
50033
- log3.debug(`Removed stale browser bridge socket: ${file}`);
50034
- }
50035
- } catch {}
50036
- }
50037
- }
50038
- } catch (err) {
50039
- log3.debug(`Browser bridge cleanup failed: ${err}`);
50040
- }
50041
- }
50042
- function buildClaudeChildEnv(parentEnv, account, opts) {
50043
- const env = { ...parentEnv };
50044
- if (env.MCP_CONNECTION_NONBLOCKING === undefined) {
50045
- env.MCP_CONNECTION_NONBLOCKING = "true";
50046
- }
50047
- if (env.ENABLE_PROMPT_CACHING_1H === undefined) {
50048
- env.ENABLE_PROMPT_CACHING_1H = "true";
50049
- }
50050
- if (opts?.disableAutoMemory) {
50051
- env.CLAUDE_CODE_DISABLE_AUTO_MEMORY = "1";
50052
- }
50053
- if (opts?.decisionBridge && env.MCP_TOOL_TIMEOUT === undefined) {
50054
- env.MCP_TOOL_TIMEOUT = "3600000";
50055
- }
50056
- if (account?.home) {
50057
- env.HOME = account.home;
50058
- env.USERPROFILE = account.home;
50059
- delete env.ANTHROPIC_API_KEY;
50060
- delete env.CLAUDE_CODE_OAUTH_TOKEN;
50061
- delete env.ANTHROPIC_AUTH_TOKEN;
50062
- delete env.CLAUDE_CONFIG_DIR;
50063
- delete env.CLAUDE_SECURESTORAGE_CONFIG_DIR;
50064
- } else if (account?.apiKey) {
50065
- env.ANTHROPIC_API_KEY = account.apiKey;
50066
- delete env.CLAUDE_CODE_OAUTH_TOKEN;
50067
- delete env.ANTHROPIC_AUTH_TOKEN;
50068
- }
50069
- return env;
50070
- }
50071
- function buildInlineSettings(statusLineCommand, memory) {
50072
- const settings = {};
50073
- if (statusLineCommand) {
50074
- settings.statusLine = {
50075
- type: "command",
50076
- command: statusLineCommand,
50077
- padding: 0
50078
- };
50079
- }
50080
- if (memory) {
50081
- settings.autoMemoryEnabled = true;
50082
- settings.autoMemoryDirectory = memory.autoMemoryDir;
50083
- }
50084
- return Object.keys(settings).length > 0 ? settings : null;
50085
- }
50086
- function runtimeForScriptPath(scriptPath) {
50087
- return scriptPath.endsWith(".ts") ? process.execPath : "node";
50088
- }
50089
- function isErrorResultEvent(event) {
50090
- const ev = event;
50091
- if (typeof ev.subtype === "string" && ev.subtype.startsWith("error"))
50092
- return true;
50093
- if (ev.is_error === true)
50094
- return true;
50095
- return false;
50096
- }
50097
- function materializeMcpConfig(config, sessionId, opts = {}) {
50098
- if (opts.inline) {
50099
- return { mode: "inline", value: JSON.stringify(config) };
50100
- }
50101
- const dir = opts.tmpDirOverride ?? tmpdir2();
50102
- const path = join4(dir, `claude-threads-mcp-${sessionId ?? process.pid}-${Date.now()}.json`);
50103
- writeFileSync2(path, JSON.stringify(config), { mode: 384 });
50104
- return { mode: "file", path };
50105
- }
50106
- function buildPermissionArgs(opts) {
50107
- const args = [];
50108
- if (opts.permissionMode === "bypass" && !opts.platformConfig) {
50109
- args.push("--dangerously-skip-permissions");
50110
- return { args, tempFile: null };
50111
- }
50112
- if (!opts.platformConfig) {
50113
- throw new Error(`platformConfig is required when permissionMode is '${opts.permissionMode}'`);
50114
- }
50115
- const mcpEnv = {
50116
- PLATFORM_TYPE: opts.platformConfig.type,
50117
- PLATFORM_URL: opts.platformConfig.url,
50118
- PLATFORM_TOKEN: opts.platformConfig.token,
50119
- PLATFORM_CHANNEL_ID: opts.platformConfig.channelId,
50120
- PLATFORM_THREAD_ID: opts.threadId || "",
50121
- ALLOWED_USERS: opts.platformConfig.allowedUsers.join(","),
50122
- DEBUG: opts.debug ? "1" : "",
50123
- PERMISSION_TIMEOUT_MS: String(opts.permissionTimeoutMs),
50124
- SESSION_OWNER_USERNAME: opts.sessionOwnerUsername || ""
50125
- };
50126
- if (opts.decisionBridgePath) {
50127
- mcpEnv.DECISION_BRIDGE_PATH = opts.decisionBridgePath;
50128
- if (process.env.DECISION_BRIDGE_TIMEOUT_MS) {
50129
- mcpEnv.DECISION_BRIDGE_TIMEOUT_MS = process.env.DECISION_BRIDGE_TIMEOUT_MS;
50130
- }
50131
- const features = opts.agentFeatures;
50132
- if (features) {
50133
- if (features.memoryChannel)
50134
- mcpEnv[AGENT_FEATURES_ENV.MEMORY_CHANNEL_ENABLED] = "1";
50135
- if (features.routines)
50136
- mcpEnv[AGENT_FEATURES_ENV.ROUTINES_ENABLED] = "1";
50137
- if (features.watches)
50138
- mcpEnv[AGENT_FEATURES_ENV.WATCHES_ENABLED] = "1";
50139
- if (features.unattended)
50140
- mcpEnv[AGENT_FEATURES_ENV.UNATTENDED] = "1";
50141
- if (features.dcm)
50142
- mcpEnv[AGENT_FEATURES_ENV.DCM] = "1";
50143
- }
50144
- }
50145
- if (opts.platformConfig.appToken) {
50146
- mcpEnv.PLATFORM_APP_TOKEN = opts.platformConfig.appToken;
50147
- }
50148
- if (opts.workingDir) {
50149
- mcpEnv[OUTBOUND_ENV.SESSION_WORKING_DIR] = opts.workingDir;
50150
- }
50151
- if (opts.uploadDir) {
50152
- mcpEnv[OUTBOUND_ENV.SESSION_UPLOAD_DIR] = opts.uploadDir;
50153
- }
50154
- if (opts.outboundFiles?.enabled === false) {
50155
- mcpEnv[OUTBOUND_ENV.OUTBOUND_FILES_ENABLED] = "0";
50156
- }
50157
- if (typeof opts.outboundFiles?.maxBytes === "number" && Number.isFinite(opts.outboundFiles.maxBytes) && opts.outboundFiles.maxBytes > 0) {
50158
- mcpEnv[OUTBOUND_ENV.OUTBOUND_FILES_MAX_BYTES] = String(opts.outboundFiles.maxBytes);
50159
- }
50160
- const mcpConfig = {
50161
- mcpServers: {
50162
- "claude-threads-mcp": {
50163
- type: "stdio",
50164
- command: runtimeForScriptPath(opts.mcpServerPath),
50165
- args: [opts.mcpServerPath],
50166
- env: mcpEnv
50167
- }
50168
- }
50169
- };
50170
- const materialized = materializeMcpConfig(mcpConfig, opts.sessionId, { inline: opts.inline });
50171
- let tempFile = null;
50172
- if (materialized.mode === "file") {
50173
- tempFile = materialized.path;
50174
- args.push("--mcp-config", materialized.path);
50175
- } else {
50176
- args.push("--mcp-config", materialized.value);
50177
- }
50178
- if (opts.permissionMode === "bypass") {
50179
- args.push("--dangerously-skip-permissions");
50180
- } else {
50181
- args.push("--permission-prompt-tool", "mcp__claude-threads-mcp__permission_prompt");
50182
- if (opts.permissionMode === "auto") {
50183
- args.push("--permission-mode", "auto");
50184
- }
50185
- }
50186
- return { args, tempFile };
50187
- }
50188
- var STDERR_PER_INSTANCE_CAP = 10240;
50189
- var STDERR_AGGREGATE_SOFT_CAP = 10 * 1024 * 1024;
50190
- var totalStderrBytes = 0;
50191
-
50192
- class ClaudeCli extends EventEmitter {
50193
- process = null;
50194
- options;
50195
- buffer = "";
50196
- debug = process.env.DEBUG === "1" || process.argv.includes("--debug");
50197
- statusFilePath = null;
50198
- lastStatusData = null;
50199
- stderrBuffer = "";
50200
- mcpConfigTempFile = null;
50201
- lastEmittedRateLimitDeadline = 0;
50202
- lastEmittedHitHadExplicitReset = false;
50203
- log;
50204
- constructor(options) {
50205
- super();
50206
- this.options = options;
50207
- this.log = options.logSessionId ? createLogger("claude").forSession(options.logSessionId) : createLogger("claude");
50208
- }
50209
- getStatusFilePath() {
50210
- return this.statusFilePath;
50211
- }
50212
- getStatusData() {
50213
- if (!this.statusFilePath)
50214
- return null;
50215
- try {
50216
- if (existsSync3(this.statusFilePath)) {
50217
- const data = readFileSync2(this.statusFilePath, "utf8");
50218
- this.lastStatusData = JSON.parse(data);
50219
- }
50220
- } catch (err) {
50221
- this.log.debug(`Failed to read status file: ${err}`);
50222
- }
50223
- return this.lastStatusData;
50224
- }
50225
- startStatusWatch() {
50226
- if (!this.statusFilePath) {
50227
- this.log.debug("No status file path, skipping status watch");
50228
- return;
50229
- }
50230
- this.log.debug(`Starting status watch: ${this.statusFilePath}`);
50231
- const checkStatus = () => {
50232
- const data = this.getStatusData();
50233
- if (data && data.timestamp !== this.lastStatusData?.timestamp) {
50234
- this.lastStatusData = data;
50235
- this.emit("status", data);
50236
- }
50237
- };
50238
- watchFile(this.statusFilePath, { interval: 1000 }, checkStatus);
50239
- }
50240
- stopStatusWatch() {
50241
- if (this.statusFilePath) {
50242
- unwatchFile(this.statusFilePath);
50243
- try {
50244
- if (existsSync3(this.statusFilePath)) {
50245
- unlinkSync(this.statusFilePath);
50246
- }
50247
- } catch {}
50248
- }
50249
- }
50250
- start() {
50251
- if (this.process)
50252
- throw new Error("Already running");
50253
- totalStderrBytes -= this.stderrBuffer.length;
50254
- this.stderrBuffer = "";
50255
- this.lastEmittedRateLimitDeadline = 0;
50256
- this.lastEmittedHitHadExplicitReset = false;
50257
- cleanupBrowserBridgeSockets();
50258
- const claudePath = getClaudePath();
50259
- const args = [
50260
- "--input-format",
50261
- "stream-json",
50262
- "--output-format",
50263
- "stream-json",
50264
- "--verbose"
50265
- ];
50266
- if (this.options.sessionId) {
50267
- if (this.options.resume) {
50268
- args.push("--resume", this.options.sessionId);
50269
- } else {
50270
- args.push("--session-id", this.options.sessionId);
50271
- }
50272
- }
50273
- const permissionMode = this.options.permissionMode ?? "default";
50274
- const permResult = buildPermissionArgs({
50275
- permissionMode,
50276
- mcpServerPath: this.getMcpServerPath(),
50277
- platformConfig: this.options.platformConfig,
50278
- threadId: this.options.threadId,
50279
- sessionId: this.options.sessionId,
50280
- permissionTimeoutMs: this.options.permissionTimeoutMs ?? 120000,
50281
- debug: this.debug,
50282
- workingDir: this.options.workingDir,
50283
- uploadDir: this.options.uploadDir,
50284
- outboundFiles: this.options.outboundFiles,
50285
- sessionOwnerUsername: this.options.sessionOwnerUsername,
50286
- decisionBridgePath: this.options.decisionBridgePath,
50287
- agentFeatures: this.options.agentFeatures
50288
- });
50289
- args.push(...permResult.args);
50290
- this.mcpConfigTempFile = permResult.tempFile;
50291
- if (this.options.chrome) {
50292
- args.push("--chrome");
50293
- }
50294
- if (this.options.appendSystemPrompt) {
50295
- args.push("--append-system-prompt", this.options.appendSystemPrompt);
50296
- }
50297
- let statusLineCommand;
50298
- if (this.options.sessionId) {
50299
- this.statusFilePath = join4(tmpdir2(), `claude-threads-status-${this.options.sessionId}.json`);
50300
- const statusLineWriterPath = this.getStatusLineWriterPath();
50301
- const runtime = runtimeForScriptPath(statusLineWriterPath);
50302
- statusLineCommand = `${runtime} ${statusLineWriterPath} ${this.options.sessionId}`;
50303
- }
50304
- const settings = buildInlineSettings(statusLineCommand, this.options.memory);
50305
- if (settings) {
50306
- args.push("--settings", JSON.stringify(settings));
50307
- }
50308
- this.log.debug(`Starting: ${claudePath} ${args.slice(0, 5).join(" ")}...`);
50309
- const childEnv = this.buildChildEnv();
50310
- if (this.options.account) {
50311
- this.log.debug(`Spawning under Claude account "${this.options.account.id}"`);
50312
- }
50313
- this.process = crossSpawn(claudePath, args, {
50314
- cwd: this.options.workingDir,
50315
- env: childEnv,
50316
- stdio: ["pipe", "pipe", "pipe"]
50317
- });
50318
- this.log.debug(`Claude process spawned: pid=${this.process.pid}`);
50319
- this.process.stdout?.on("data", (chunk) => {
50320
- this.parseOutput(chunk.toString());
50321
- });
50322
- this.process.stderr?.on("data", (chunk) => {
50323
- const text = chunk.toString();
50324
- const before = this.stderrBuffer.length;
50325
- this.stderrBuffer += text;
50326
- const cap = totalStderrBytes > STDERR_AGGREGATE_SOFT_CAP ? 1024 : STDERR_PER_INSTANCE_CAP;
50327
- if (this.stderrBuffer.length > cap) {
50328
- this.stderrBuffer = this.stderrBuffer.slice(-cap);
50329
- }
50330
- totalStderrBytes += this.stderrBuffer.length - before;
50331
- this.log.debug(`stderr: ${text.trim()}`);
50332
- if (process.env.INTEGRATION_TEST === "1") {
50333
- process.stderr.write(text);
50334
- }
50335
- this.maybeEmitRateLimit(text);
50336
- });
50337
- this.process.on("error", (err) => {
50338
- this.log.error(`Claude error: ${err}`);
50339
- this.emit("error", err);
50340
- });
50341
- this.process.on("exit", (code) => {
50342
- this.log.debug(`Exited ${code}`);
50343
- this.process = null;
50344
- this.buffer = "";
50345
- totalStderrBytes -= this.stderrBuffer.length;
50346
- if (this.mcpConfigTempFile) {
50347
- const path = this.mcpConfigTempFile;
50348
- this.mcpConfigTempFile = null;
50349
- try {
50350
- unlinkSync(path);
50351
- } catch {}
50352
- }
50353
- this.emit("exit", code);
50354
- });
50355
- }
50356
- sendMessage(content) {
50357
- if (!this.process?.stdin)
50358
- throw new Error("Not running");
50359
- const msg = JSON.stringify({
50360
- type: "user",
50361
- message: { role: "user", content }
50362
- }) + `
50363
- `;
50364
- const preview = content.substring(0, 50);
50365
- this.log.debug(`Sending: ${preview}...`);
50366
- if (process.env.INTEGRATION_TEST === "1") {
50367
- const stack = new Error().stack?.split(`
50368
- `).slice(2, 6).join(" > ").replace(/\s+at\s+/g, " < ") ?? "?";
50369
- process.stderr.write(`[claude-cli sendMessage pid=${this.process.pid}] ${preview} | ${stack}
50370
- `);
50371
- }
50372
- this.process.stdin.write(msg);
50373
- }
50374
- sendToolResult(toolUseId, content) {
50375
- if (!this.process?.stdin)
50376
- throw new Error("Not running");
50377
- const msg = JSON.stringify({
50378
- type: "user",
50379
- message: {
50380
- role: "user",
50381
- content: [{
50382
- type: "tool_result",
50383
- tool_use_id: toolUseId,
50384
- content: typeof content === "string" ? content : JSON.stringify(content)
50385
- }]
50386
- }
50387
- }) + `
50388
- `;
50389
- this.log.debug(`Sending tool_result for ${toolUseId}`);
50390
- this.process.stdin.write(msg);
50391
- }
50392
- parseOutput(data) {
50393
- this.buffer += data;
50394
- const lines = this.buffer.split(`
50395
- `);
50396
- this.buffer = lines.pop() || "";
50397
- for (const line of lines) {
50398
- const trimmed = line.trim();
50399
- if (!trimmed)
50400
- continue;
50401
- let event;
50402
- try {
50403
- event = JSON.parse(trimmed);
50404
- } catch {
50405
- continue;
50406
- }
50407
- try {
50408
- this.emit("event", event);
50409
- } catch (err) {
50410
- this.log.error(`'event' listener threw while handling a ${event.type} event: ${err}`);
50411
- }
50412
- if (event.type === "result" && isErrorResultEvent(event)) {
50413
- this.maybeEmitRateLimit(trimmed);
50414
- }
50415
- if (event.type === "rate_limit_event") {
50416
- this.maybeEmitRateLimitHit(parseRateLimitEvent(event));
50417
- }
50418
- }
50419
- }
50420
- maybeEmitRateLimit(text) {
50421
- this.maybeEmitRateLimitHit(detectRateLimit(text));
50422
- }
50423
- maybeEmitRateLimitHit(hit) {
50424
- if (!hit.detected)
50425
- return;
50426
- if (!hit.resetAtEpochMs && this.lastEmittedHitHadExplicitReset && this.lastEmittedRateLimitDeadline > Date.now()) {
50427
- return;
50428
- }
50429
- const newDeadline = cooldownDeadline(hit);
50430
- const MIN_ADVANCE_MS = 60000;
50431
- if (newDeadline - this.lastEmittedRateLimitDeadline < MIN_ADVANCE_MS) {
50432
- if (hit.resetAtEpochMs !== undefined) {
50433
- this.lastEmittedHitHadExplicitReset = true;
50434
- }
50435
- return;
50436
- }
50437
- this.lastEmittedRateLimitDeadline = newDeadline;
50438
- this.lastEmittedHitHadExplicitReset = hit.resetAtEpochMs !== undefined;
50439
- this.log.warn(`Rate limit detected: ${hit.matched ?? "(no match text)"}`);
50440
- this.emit("rate-limit", hit);
50441
- }
50442
- isRunning() {
50443
- return this.process !== null;
50444
- }
50445
- getLastStderr() {
50446
- return this.stderrBuffer;
50447
- }
50448
- isPermanentFailure() {
50449
- const stderr = this.stderrBuffer;
50450
- if (stderr.includes("claude-mcp-browser-bridge") && (stderr.includes("EOPNOTSUPP") || stderr.includes("ENOENT"))) {
50451
- return true;
50452
- }
50453
- if (stderr.includes("No conversation found with session ID")) {
50454
- return true;
50455
- }
50456
- return false;
50457
- }
50458
- getPermanentFailureReason() {
50459
- const stderr = this.stderrBuffer;
50460
- if (stderr.includes("claude-mcp-browser-bridge") && (stderr.includes("EOPNOTSUPP") || stderr.includes("ENOENT"))) {
50461
- return "Claude browser bridge state from a previous session is no longer accessible. This typically happens when a session with Chrome integration is resumed after a restart.";
50462
- }
50463
- if (stderr.includes("No conversation found with session ID")) {
50464
- return "The conversation history for this session no longer exists. This can happen if Claude's history was cleared or if the session was created on a different machine.";
50465
- }
50466
- return null;
50467
- }
50468
- kill() {
50469
- this.stopStatusWatch();
50470
- if (!this.process) {
50471
- this.log.debug("Kill called but process not running");
50472
- return Promise.resolve();
50473
- }
50474
- const proc = this.process;
50475
- const pid = proc.pid;
50476
- this.process = null;
50477
- this.log.debug(`Killing Claude process (pid=${pid})`);
50478
- if (process.env.INTEGRATION_TEST === "1") {
50479
- const stack = new Error().stack?.split(`
50480
- `).slice(2, 7).join(" > ").replace(/\s+at\s+/g, " < ") ?? "?";
50481
- process.stderr.write(`[claude-cli kill pid=${pid}] | ${stack}
50482
- `);
50483
- }
50484
- return new Promise((resolve3) => {
50485
- this.log.debug("Sending first SIGINT");
50486
- proc.kill("SIGINT");
50487
- const secondSigint = setTimeout(() => {
50488
- try {
50489
- this.log.debug("Sending second SIGINT");
50490
- proc.kill("SIGINT");
50491
- } catch {}
50492
- }, 100);
50493
- const forceKillTimeout = setTimeout(() => {
50494
- try {
50495
- this.log.debug("Sending SIGTERM (force kill)");
50496
- proc.kill("SIGTERM");
50497
- } catch {}
50498
- }, 2000);
50499
- const settle = (reason) => {
50500
- this.log.debug(`Claude process gone (${reason})`);
50501
- clearTimeout(secondSigint);
50502
- clearTimeout(forceKillTimeout);
50503
- clearTimeout(lastResort);
50504
- resolve3();
50505
- };
50506
- const lastResort = setTimeout(() => {
50507
- try {
50508
- this.log.warn("Claude process did not exit after SIGTERM — sending SIGKILL");
50509
- proc.kill("SIGKILL");
50510
- } catch {}
50511
- settle("kill timeout");
50512
- }, 5000);
50513
- proc.once("close", (code) => settle(`closed, code=${code}`));
50514
- proc.once("error", () => settle("spawn error"));
50515
- });
50516
- }
50517
- interrupt() {
50518
- if (!this.process) {
50519
- this.log.debug("Interrupt called but process not running");
50520
- return false;
50521
- }
50522
- this.log.debug(`Interrupting Claude process (pid=${this.process.pid})`);
50523
- this.process.kill("SIGINT");
50524
- return true;
50525
- }
50526
- buildChildEnv() {
50527
- return buildClaudeChildEnv(process.env, this.options.account, {
50528
- decisionBridge: this.options.decisionBridgePath !== undefined,
50529
- disableAutoMemory: this.options.memory === null
50530
- });
50531
- }
50532
- getMcpServerPath() {
50533
- const __filename2 = fileURLToPath(import.meta.url);
50534
- const __dirname2 = dirname2(__filename2);
50535
- const bundledPath = resolve2(__dirname2, "mcp", "mcp-server.js");
50536
- if (existsSync3(bundledPath)) {
50537
- return bundledPath;
50538
- }
50539
- const sourceLayoutPath = resolve2(__dirname2, "..", "mcp", "mcp-server.js");
50540
- if (existsSync3(sourceLayoutPath)) {
50541
- return sourceLayoutPath;
50542
- }
50543
- const tsPath = resolve2(__dirname2, "..", "mcp", "mcp-server.ts");
50544
- if (existsSync3(tsPath)) {
50545
- return tsPath;
50546
- }
50547
- return sourceLayoutPath;
50548
- }
50549
- getStatusLineWriterPath() {
50550
- const __filename2 = fileURLToPath(import.meta.url);
50551
- const __dirname2 = dirname2(__filename2);
50552
- const bundledPath = resolve2(__dirname2, "statusline", "writer.js");
50553
- if (existsSync3(bundledPath)) {
50554
- return bundledPath;
50555
- }
50556
- const sourceLayoutPath = resolve2(__dirname2, "..", "statusline", "writer.js");
50557
- if (existsSync3(sourceLayoutPath)) {
50558
- return sourceLayoutPath;
50559
- }
50560
- const tsPath = resolve2(__dirname2, "..", "statusline", "writer.ts");
50561
- if (existsSync3(tsPath)) {
50562
- return tsPath;
50563
- }
50564
- return sourceLayoutPath;
50565
- }
50566
- }
50711
+ // src/session/lifecycle.ts
50712
+ init_cli();
50713
+ init_rate_limit_detector();
50567
50714
 
50568
50715
  // src/persistence/session-store.ts
50569
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "fs";
50716
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "fs";
50570
50717
 
50571
50718
  // src/persistence/atomic-file.ts
50572
50719
  import { chmodSync as chmodSync3, renameSync, writeFileSync as writeFileSync3 } from "fs";
@@ -50641,13 +50788,13 @@ class SessionStore {
50641
50788
  this.sessionsFile = DEFAULT_SESSIONS_FILE;
50642
50789
  this.configDir = DEFAULT_CONFIG_DIR;
50643
50790
  }
50644
- if (!existsSync4(this.configDir)) {
50791
+ if (!existsSync5(this.configDir)) {
50645
50792
  mkdirSync3(this.configDir, { recursive: true });
50646
50793
  }
50647
50794
  }
50648
50795
  load() {
50649
50796
  const sessions = new Map;
50650
- if (!existsSync4(this.sessionsFile)) {
50797
+ if (!existsSync5(this.sessionsFile)) {
50651
50798
  log4.debug("No sessions file found");
50652
50799
  return sessions;
50653
50800
  }
@@ -50858,7 +51005,7 @@ class SessionStore {
50858
51005
  }
50859
51006
  lastReadDegraded = false;
50860
51007
  loadRaw() {
50861
- if (!existsSync4(this.sessionsFile)) {
51008
+ if (!existsSync5(this.sessionsFile)) {
50862
51009
  this.lastReadDegraded = false;
50863
51010
  return { version: STORE_VERSION, sessions: {} };
50864
51011
  }
@@ -50903,7 +51050,7 @@ class SessionStore {
50903
51050
 
50904
51051
  // src/persistence/thread-logger.ts
50905
51052
  init_logger();
50906
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, appendFileSync, readdirSync as readdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, rmdirSync, readFileSync as readFileSync4, chmodSync as chmodSync4 } from "fs";
51053
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, appendFileSync, readdirSync as readdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, rmdirSync, readFileSync as readFileSync4, chmodSync as chmodSync4 } from "fs";
50907
51054
  import { homedir as homedir4 } from "os";
50908
51055
  import { join as join6, dirname as dirname3 } from "path";
50909
51056
  var log5 = createLogger("thread-log");
@@ -50930,7 +51077,7 @@ class ThreadLoggerImpl {
50930
51077
  this.logPath = join6(LOGS_BASE_DIR, platformId, `${claudeSessionId}.jsonl`);
50931
51078
  if (this.enabled) {
50932
51079
  const dir = dirname3(this.logPath);
50933
- if (!existsSync5(dir)) {
51080
+ if (!existsSync6(dir)) {
50934
51081
  mkdirSync4(dir, { recursive: true });
50935
51082
  }
50936
51083
  this.flushTimer = setInterval(() => {
@@ -51065,7 +51212,7 @@ class ThreadLoggerImpl {
51065
51212
  const lines = this.buffer.map((entry) => JSON.stringify(entry)).join(`
51066
51213
  `) + `
51067
51214
  `;
51068
- const isNewFile = !existsSync5(this.logPath);
51215
+ const isNewFile = !existsSync6(this.logPath);
51069
51216
  appendFileSync(this.logPath, lines, { encoding: "utf8", mode: 384 });
51070
51217
  if (isNewFile) {
51071
51218
  chmodSync4(this.logPath, 384);
@@ -51103,7 +51250,7 @@ function createThreadLogger(platformId, threadId, claudeSessionId, options) {
51103
51250
  function cleanupOldLogs(retentionDays = 30) {
51104
51251
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
51105
51252
  let deletedCount = 0;
51106
- if (!existsSync5(LOGS_BASE_DIR)) {
51253
+ if (!existsSync6(LOGS_BASE_DIR)) {
51107
51254
  return 0;
51108
51255
  }
51109
51256
  try {
@@ -51151,7 +51298,7 @@ function getLogFilePath(platformId, sessionId) {
51151
51298
  function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
51152
51299
  const logPath = getLogFilePath(platformId, sessionId);
51153
51300
  log5.debug(`Reading log entries from: ${logPath}`);
51154
- if (!existsSync5(logPath)) {
51301
+ if (!existsSync6(logPath)) {
51155
51302
  log5.debug(`Log file does not exist: ${logPath}`);
51156
51303
  return [];
51157
51304
  }
@@ -51178,7 +51325,7 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
51178
51325
  }
51179
51326
 
51180
51327
  // src/version.ts
51181
- import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
51328
+ import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
51182
51329
  import { dirname as dirname4, resolve as resolve3 } from "path";
51183
51330
  import { fileURLToPath as fileURLToPath2 } from "url";
51184
51331
  var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
@@ -51189,7 +51336,7 @@ function loadPackageJson() {
51189
51336
  resolve3(process.cwd(), "package.json")
51190
51337
  ];
51191
51338
  for (const candidate of candidates) {
51192
- if (existsSync6(candidate)) {
51339
+ if (existsSync7(candidate)) {
51193
51340
  try {
51194
51341
  const pkg = JSON.parse(readFileSync5(candidate, "utf-8"));
51195
51342
  if (pkg.name === "claude-threads") {
@@ -51639,7 +51786,7 @@ ${formatter.formatBold("Reactions:")}
51639
51786
  }
51640
51787
 
51641
51788
  // src/changelog.ts
51642
- import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
51789
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
51643
51790
  import { dirname as dirname5, resolve as resolve4 } from "path";
51644
51791
  import { fileURLToPath as fileURLToPath3 } from "url";
51645
51792
  var __dirname3 = dirname5(fileURLToPath3(import.meta.url));
@@ -51650,7 +51797,7 @@ function getReleaseNotes(version) {
51650
51797
  ];
51651
51798
  let changelogPath = null;
51652
51799
  for (const p of possiblePaths) {
51653
- if (existsSync7(p)) {
51800
+ if (existsSync8(p)) {
51654
51801
  changelogPath = p;
51655
51802
  break;
51656
51803
  }
@@ -52345,7 +52492,7 @@ ${avoidCommands.map((c) => `- \`!${c.command}\` - ${c.reason}`).join(`
52345
52492
  }
52346
52493
  // src/session/lifecycle.ts
52347
52494
  import { randomUUID as randomUUID7 } from "crypto";
52348
- import { existsSync as existsSync12 } from "fs";
52495
+ import { existsSync as existsSync13 } from "fs";
52349
52496
 
52350
52497
  // src/utils/keep-alive.ts
52351
52498
  init_logger();
@@ -52800,6 +52947,9 @@ function updateLastMessage(session, post2) {
52800
52947
  }
52801
52948
  }
52802
52949
 
52950
+ // src/operations/commands/handler.ts
52951
+ init_cli();
52952
+
52803
52953
  // src/operations/streaming/handler.ts
52804
52954
  import { lstat, mkdir as mkdir2, mkdtemp, rm as rm2, writeFile as writeFile2 } from "fs/promises";
52805
52955
  import { tmpdir as tmpdir3 } from "os";
@@ -53146,7 +53296,7 @@ function buildRestartCliOptions(session, ctx) {
53146
53296
  // src/operations/commands/handler.ts
53147
53297
  import { randomUUID as randomUUID6 } from "crypto";
53148
53298
  import { resolve as resolve6 } from "path";
53149
- import { existsSync as existsSync11, statSync as statSync4 } from "fs";
53299
+ import { existsSync as existsSync12, statSync as statSync4 } from "fs";
53150
53300
 
53151
53301
  // node_modules/update-notifier/update-notifier.js
53152
53302
  import process10 from "node:process";
@@ -62129,6 +62279,10 @@ async function buildStatusBar(sessionCount, config, formatter, platformId) {
62129
62279
  }
62130
62280
  items.push(formatter.formatCode(label));
62131
62281
  }
62282
+ if (config.connectorsOff && config.connectorsOff > 0) {
62283
+ const n = config.connectorsOff;
62284
+ items.push(formatter.formatCode(`\uD83D\uDD0C ${n} claude.ai connector${n === 1 ? "" : "s"} off`));
62285
+ }
62132
62286
  items.push(formatter.formatCode(permissionModeDisplay(config.permissionMode).chip));
62133
62287
  if (config.worktreeMode === "require") {
62134
62288
  items.push(formatter.formatCode("\uD83C\uDF3F Worktree: require"));
@@ -62537,7 +62691,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
62537
62691
  // src/memory/store.ts
62538
62692
  import { createHash } from "crypto";
62539
62693
  import {
62540
- existsSync as existsSync8,
62694
+ existsSync as existsSync9,
62541
62695
  mkdirSync as mkdirSync5,
62542
62696
  readFileSync as readFileSync7,
62543
62697
  realpathSync
@@ -62749,7 +62903,7 @@ _(older entries omitted — \`!memory\` shows all)_` : rendered;
62749
62903
  }
62750
62904
  loadLines(platformId) {
62751
62905
  const file = this.channelMemoryPath(platformId);
62752
- if (!existsSync8(file))
62906
+ if (!existsSync9(file))
62753
62907
  return [];
62754
62908
  const raw = readFileSync7(file, "utf-8");
62755
62909
  const lines = [];
@@ -62791,7 +62945,7 @@ _(older entries omitted — \`!memory\` shows all)_` : rendered;
62791
62945
  writeFileAtomic(file, content);
62792
62946
  }
62793
62947
  ensureDir(dir) {
62794
- if (!existsSync8(dir)) {
62948
+ if (!existsSync9(dir)) {
62795
62949
  mkdirSync5(dir, { recursive: true, mode: 448 });
62796
62950
  }
62797
62951
  }
@@ -62938,7 +63092,7 @@ import { join as join12 } from "path";
62938
63092
  import { randomUUID as randomUUID3 } from "crypto";
62939
63093
 
62940
63094
  // src/persistence/platform-list-store.ts
62941
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
63095
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
62942
63096
  import { homedir as homedir7 } from "os";
62943
63097
  import { join as join11 } from "path";
62944
63098
  var STORES_CONFIG_DIR = join11(homedir7(), ".config", "claude-threads");
@@ -63015,7 +63169,7 @@ class PlatformListStore {
63015
63169
  return this.queue.run(fn);
63016
63170
  }
63017
63171
  loadRaw(forWrite = false) {
63018
- if (!existsSync9(this.file)) {
63172
+ if (!existsSync10(this.file)) {
63019
63173
  this.cache = null;
63020
63174
  return { version: STORE_VERSION2, items: {} };
63021
63175
  }
@@ -63712,6 +63866,7 @@ async function suggestBranchNames(workingDir, userMessage) {
63712
63866
 
63713
63867
  // src/operations/worktree/handler.ts
63714
63868
  init_worktree();
63869
+ init_cli();
63715
63870
  import { randomUUID as randomUUID5 } from "crypto";
63716
63871
  init_logger();
63717
63872
  var log26 = createLogger("worktree");
@@ -64410,6 +64565,24 @@ function handleEventPreProcessing(session, event, ctx) {
64410
64565
  }
64411
64566
  if (event.type === "system") {
64412
64567
  const e = event;
64568
+ if (e.subtype === "init" && Array.isArray(e.mcp_servers)) {
64569
+ const summary = [...e.mcp_servers].sort((a, b) => (a.name ?? "").localeCompare(b.name ?? "")).map((s) => `${s.name ?? "?"} (${s.status ?? "unknown"})`).join(", ") || "none";
64570
+ if (session.mcpServersSummary !== summary) {
64571
+ session.mcpServersSummary = summary;
64572
+ sessionLog6(session).info(`MCP servers: ${summary}`);
64573
+ const down = e.mcp_servers.filter((s) => s.status !== "connected" && s.status !== "pending");
64574
+ if (down.length > 0) {
64575
+ sessionLog6(session).warn(`MCP servers not connected: ${down.map((s) => `${s.name ?? "?"} (${s.status ?? "unknown"})`).join(", ")}`);
64576
+ }
64577
+ const connectors = e.mcp_servers.filter((s) => (s.name ?? "").startsWith("claude.ai "));
64578
+ if (connectors.length > 0 && session.platform.getMcpConfig().claudeAiConnectors !== true) {
64579
+ const names = connectors.map((s) => s.name).join(", ");
64580
+ sessionLog6(session).warn(`claude.ai connectors are active although claudeAiConnectors is off: ${names}. ` + `This Claude CLI ignores both disableClaudeAiConnectors and ENABLE_CLAUDEAI_MCP_SERVERS; upgrade it.`);
64581
+ const f = session.platform.getFormatter();
64582
+ withErrorHandling(() => post(session, "warning", `${f.formatBold("claude.ai connectors are active in this session")} (${names}) although ` + `${f.formatCode("claudeAiConnectors")} is off for this platform. This Claude CLI version ignores the ` + `switch; upgrade the CLI on the bot's machine.`), { action: "Post connector warning", session });
64583
+ }
64584
+ }
64585
+ }
64413
64586
  if (e.subtype === "init" && typeof e.model === "string") {
64414
64587
  session.currentModel = e.model;
64415
64588
  }
@@ -65110,7 +65283,7 @@ async function suggestSessionMetadata(context) {
65110
65283
  init_quick_query();
65111
65284
 
65112
65285
  // src/persistence/github-emails-store.ts
65113
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync9 } from "fs";
65286
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync9 } from "fs";
65114
65287
  import { homedir as homedir8 } from "os";
65115
65288
  import { join as join14 } from "path";
65116
65289
  init_logger();
@@ -65136,7 +65309,7 @@ class GitHubEmailsStore {
65136
65309
  this.file = DEFAULT_FILE3;
65137
65310
  this.configDir = DEFAULT_CONFIG_DIR2;
65138
65311
  }
65139
- if (!existsSync10(this.configDir)) {
65312
+ if (!existsSync11(this.configDir)) {
65140
65313
  mkdirSync7(this.configDir, { recursive: true });
65141
65314
  }
65142
65315
  }
@@ -65171,7 +65344,7 @@ class GitHubEmailsStore {
65171
65344
  }
65172
65345
  lastReadDegraded = false;
65173
65346
  loadRaw() {
65174
- if (!existsSync10(this.file)) {
65347
+ if (!existsSync11(this.file)) {
65175
65348
  this.lastReadDegraded = false;
65176
65349
  return { version: STORE_VERSION3, emails: {} };
65177
65350
  }
@@ -65377,7 +65550,7 @@ async function changeDirectory(session, newDir, username, ctx) {
65377
65550
  const expandedDir = newDir.startsWith("~") ? newDir.replace("~", process.env.HOME || "") : newDir;
65378
65551
  const absoluteDir = resolve6(expandedDir);
65379
65552
  const formatter = session.platform.getFormatter();
65380
- if (!existsSync11(absoluteDir)) {
65553
+ if (!existsSync12(absoluteDir)) {
65381
65554
  await postError(session, `Directory does not exist: ${formatter.formatCode(newDir)}`);
65382
65555
  sessionLog8(session).warn(`\uD83D\uDCC2 Directory does not exist: ${newDir}`);
65383
65556
  return;
@@ -65727,6 +65900,16 @@ async function updateSessionHeader(session, ctx) {
65727
65900
  const label = account?.displayName ?? session.claudeAccountId;
65728
65901
  items.push(["\uD83D\uDD11", "Claude account", formatter.formatCode(label)]);
65729
65902
  }
65903
+ {
65904
+ const mcp = session.platform.getMcpConfig();
65905
+ const posture = [];
65906
+ if (mcp.claudeAiConnectors === true)
65907
+ posture.push(`claude.ai connectors ${formatter.formatBold("on")}`);
65908
+ if (mcp.strictMcpConfig === true)
65909
+ posture.push("strict (declared servers only)");
65910
+ if (posture.length > 0)
65911
+ items.push(["\uD83D\uDD0C", "MCP", posture.join(", ")]);
65912
+ }
65730
65913
  items.push(["\uD83C\uDD94", "Session ID", formatter.formatCode(session.claudeSessionId.substring(0, 8))]);
65731
65914
  const logPath = getLogFilePath(session.platform.platformId, session.claudeSessionId);
65732
65915
  const shortLogPath = logPath.replace(process.env.HOME || "", "~");
@@ -66907,7 +67090,7 @@ async function startSessionImpl(options, username, displayName, replyToPostId, p
66907
67090
  const { resolve: resolve7 } = await import("path");
66908
67091
  const requestedDir = initialOptions.workingDir.startsWith("~") ? initialOptions.workingDir.replace("~", process.env.HOME || "") : initialOptions.workingDir;
66909
67092
  const resolvedDir = resolve7(requestedDir);
66910
- if (!existsSync12(resolvedDir)) {
67093
+ if (!existsSync13(resolvedDir)) {
66911
67094
  const msg = `❌ Directory does not exist: ${formatter.formatCode(initialOptions.workingDir)}`;
66912
67095
  if (startPost) {
66913
67096
  await platform.updatePost(startPost.id, msg);
@@ -67147,7 +67330,7 @@ async function resumeSessionImpl(state, ctx, resumedBy) {
67147
67330
  log37.warn(`Max sessions reached, skipping resume for ${shortId}...`);
67148
67331
  return;
67149
67332
  }
67150
- if (!existsSync12(state.workingDir)) {
67333
+ if (!existsSync13(state.workingDir)) {
67151
67334
  log37.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
67152
67335
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
67153
67336
  const resumeFormatter = platform.getFormatter();
@@ -67475,7 +67658,10 @@ async function handleExit(sessionId, code, ctx, source) {
67475
67658
  auditReason: "early-exit"
67476
67659
  });
67477
67660
  const earlyExitFormatter = session.platform.getFormatter();
67478
- await withErrorHandling(() => post(session, "warning", `${earlyExitFormatter.formatBold("Session ended")} before Claude could respond (exit code ${code}). Please start a new session.`), { action: "Post early exit notification", session });
67661
+ const lastStderr = typeof session.claude?.getLastStderr === "function" ? session.claude.getLastStderr() : "";
67662
+ const strictRefused = lastStderr.includes("You cannot use --strict-mcp-config");
67663
+ const earlyExitText = strictRefused ? `${earlyExitFormatter.formatBold("Session ended")} before Claude could respond: the Claude CLI refuses ${earlyExitFormatter.formatCode("--strict-mcp-config")} because an enterprise-managed MCP config is present. Set ${earlyExitFormatter.formatCode("strictMcpConfig: false")} on this platform and restart the bot.` : `${earlyExitFormatter.formatBold("Session ended")} before Claude could respond (exit code ${code}). Please start a new session.`;
67664
+ await withErrorHandling(() => post(session, "warning", earlyExitText), { action: "Post early exit notification", session });
67479
67665
  sessionLog11(session).info(`⚠ Session ended early (exit code ${code})`);
67480
67666
  await ctx.ops.updateStickyMessage();
67481
67667
  return;
@@ -67841,7 +68027,7 @@ function createDmDiscoveryRuntime(deps) {
67841
68027
 
67842
68028
  // src/onboarding.ts
67843
68029
  var import_prompts = __toESM(require_prompts3(), 1);
67844
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
68030
+ import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
67845
68031
  import { join as join15, dirname as dirname8 } from "path";
67846
68032
  import { spawn as spawn3 } from "child_process";
67847
68033
  import { fileURLToPath as fileURLToPath6 } from "url";
@@ -68066,7 +68252,7 @@ async function runOnboarding(reconfigure = false) {
68066
68252
  console.log(dim(" ─────────────────────────────────"));
68067
68253
  console.log("");
68068
68254
  let existingConfig = null;
68069
- if (reconfigure && existsSync13(CONFIG_PATH)) {
68255
+ if (reconfigure && existsSync14(CONFIG_PATH)) {
68070
68256
  try {
68071
68257
  const content = readFileSync10(CONFIG_PATH, "utf-8");
68072
68258
  existingConfig = yaml.load(content);
@@ -69520,6 +69706,9 @@ class MattermostClient extends BasePlatformClient {
69520
69706
  channelId;
69521
69707
  directMessages;
69522
69708
  outboundFiles;
69709
+ mcpServers;
69710
+ strictMcpConfig;
69711
+ claudeAiConnectors;
69523
69712
  userCache = new Map;
69524
69713
  botUserId = null;
69525
69714
  formatter = new MattermostFormatter;
@@ -69535,6 +69724,9 @@ class MattermostClient extends BasePlatformClient {
69535
69724
  this.botName = platformConfig.botName;
69536
69725
  this.allowedUsers = platformConfig.allowedUsers;
69537
69726
  this.outboundFiles = platformConfig.outboundFiles;
69727
+ this.mcpServers = platformConfig.mcpServers;
69728
+ this.strictMcpConfig = platformConfig.strictMcpConfig;
69729
+ this.claudeAiConnectors = platformConfig.claudeAiConnectors;
69538
69730
  this.directChannelMode = resolveDirectChannelMode(platformConfig.directChannelMode);
69539
69731
  this.approvals = platformConfig.approvals;
69540
69732
  this.ackReaction = normalizeAckReaction(platformConfig.ackReaction, `platforms[${platformConfig.id}].ackReaction`);
@@ -69978,7 +70170,10 @@ class MattermostClient extends BasePlatformClient {
69978
70170
  token: this.token,
69979
70171
  channelId: this.channelId,
69980
70172
  allowedUsers: this.allowedUsers,
69981
- outboundFiles: this.outboundFiles
70173
+ outboundFiles: this.outboundFiles,
70174
+ mcpServers: this.mcpServers,
70175
+ strictMcpConfig: this.strictMcpConfig,
70176
+ claudeAiConnectors: this.claudeAiConnectors
69982
70177
  };
69983
70178
  }
69984
70179
  getFormatter() {
@@ -70190,6 +70385,9 @@ class SlackClient extends BasePlatformClient {
70190
70385
  rateLimitDelay = 0;
70191
70386
  rateLimitRetryAfter = 0;
70192
70387
  outboundFiles;
70388
+ mcpServers;
70389
+ strictMcpConfig;
70390
+ claudeAiConnectors;
70193
70391
  statusSentAt = new Map;
70194
70392
  formatter = new SlackFormatter;
70195
70393
  channelClients = new Map;
@@ -70208,6 +70406,9 @@ class SlackClient extends BasePlatformClient {
70208
70406
  this.allowedUsers = platformConfig.allowedUsers;
70209
70407
  this.apiUrl = platformConfig.apiUrl || "https://slack.com/api";
70210
70408
  this.outboundFiles = platformConfig.outboundFiles;
70409
+ this.mcpServers = platformConfig.mcpServers;
70410
+ this.strictMcpConfig = platformConfig.strictMcpConfig;
70411
+ this.claudeAiConnectors = platformConfig.claudeAiConnectors;
70211
70412
  this.directChannelMode = resolveDirectChannelMode(platformConfig.directChannelMode);
70212
70413
  this.approvals = platformConfig.approvals;
70213
70414
  this.ackReaction = normalizeAckReaction(platformConfig.ackReaction, `platforms[${platformConfig.id}].ackReaction`);
@@ -70689,7 +70890,10 @@ class SlackClient extends BasePlatformClient {
70689
70890
  channelId: this.channelId,
70690
70891
  allowedUsers: this.allowedUsers,
70691
70892
  appToken: this.appToken,
70692
- outboundFiles: this.outboundFiles
70893
+ outboundFiles: this.outboundFiles,
70894
+ mcpServers: this.mcpServers,
70895
+ strictMcpConfig: this.strictMcpConfig,
70896
+ claudeAiConnectors: this.claudeAiConnectors
70693
70897
  };
70694
70898
  }
70695
70899
  getFormatter() {
@@ -72221,6 +72425,7 @@ init_logger();
72221
72425
  // src/claude/usage-probe.ts
72222
72426
  init_spawn();
72223
72427
  init_version_check();
72428
+ init_cli();
72224
72429
  init_logger();
72225
72430
  var log47 = createLogger("usage-probe");
72226
72431
  var DEFAULT_USAGE_PROBE_TIMEOUT_MS = 30000;
@@ -72474,13 +72679,91 @@ class AccountPool {
72474
72679
  }
72475
72680
  }
72476
72681
 
72682
+ // src/claude/connector-probe.ts
72683
+ init_spawn();
72684
+ init_version_check();
72685
+ init_cli();
72686
+ init_logger();
72687
+ var log49 = createLogger("connector-probe");
72688
+ var CONNECTOR_PREFIX = "claude.ai ";
72689
+ var DEFAULT_CONNECTOR_PROBE_TIMEOUT_MS = 15000;
72690
+ function parseConnectorNames(stdout) {
72691
+ for (const line of stdout.split(`
72692
+ `)) {
72693
+ const trimmed = line.trim();
72694
+ if (!trimmed.startsWith("{"))
72695
+ continue;
72696
+ let event;
72697
+ try {
72698
+ event = JSON.parse(trimmed);
72699
+ } catch {
72700
+ continue;
72701
+ }
72702
+ if (event.type !== "system" || event.subtype !== "init")
72703
+ continue;
72704
+ return (event.mcp_servers ?? []).map((s) => s.name ?? "").filter((name) => name.startsWith(CONNECTOR_PREFIX)).map((name) => name.slice(CONNECTOR_PREFIX.length));
72705
+ }
72706
+ return null;
72707
+ }
72708
+ async function probeClaudeAiConnectors(account, opts = {}) {
72709
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_CONNECTOR_PROBE_TIMEOUT_MS;
72710
+ const label = account?.id ?? "default account";
72711
+ const claudePath = getClaudePath();
72712
+ const env4 = buildClaudeChildEnv(process.env, account, { claudeAiConnectors: true });
72713
+ return new Promise((resolve7) => {
72714
+ let settled = false;
72715
+ let stdout = "";
72716
+ const finish = (value) => {
72717
+ if (settled)
72718
+ return;
72719
+ settled = true;
72720
+ clearTimeout(timer);
72721
+ resolve7(value);
72722
+ };
72723
+ let child;
72724
+ try {
72725
+ child = crossSpawn(claudePath, ["-p", "/usage", "--output-format", "stream-json", "--verbose"], {
72726
+ env: env4,
72727
+ stdio: ["ignore", "pipe", "pipe"]
72728
+ });
72729
+ } catch (err) {
72730
+ log49.warn(`Connector probe for ${label} failed to spawn: ${err}`);
72731
+ resolve7(null);
72732
+ return;
72733
+ }
72734
+ const timer = setTimeout(() => {
72735
+ log49.debug(`Connector probe for ${label} timed out after ${timeoutMs}ms`);
72736
+ try {
72737
+ child.kill("SIGKILL");
72738
+ } catch {}
72739
+ finish(parseConnectorNames(stdout));
72740
+ }, timeoutMs);
72741
+ child.stdout?.on("data", (chunk) => {
72742
+ stdout += chunk.toString();
72743
+ const names = parseConnectorNames(stdout);
72744
+ if (names !== null) {
72745
+ try {
72746
+ child.kill("SIGTERM");
72747
+ } catch {}
72748
+ finish(names);
72749
+ }
72750
+ });
72751
+ child.stderr?.on("data", () => {});
72752
+ child.on("error", (err) => {
72753
+ log49.warn(`Connector probe for ${label} errored: ${err}`);
72754
+ finish(null);
72755
+ });
72756
+ child.on("close", () => finish(parseConnectorNames(stdout)));
72757
+ });
72758
+ }
72759
+
72477
72760
  // src/cleanup/scheduler.ts
72478
72761
  init_logger();
72479
- import { existsSync as existsSync14 } from "fs";
72762
+ import { existsSync as existsSync15 } from "fs";
72480
72763
  import { readdir, rm as rm3 } from "fs/promises";
72481
72764
  import { join as join16 } from "path";
72482
72765
  init_worktree();
72483
- var log49 = createLogger("cleanup");
72766
+ var log50 = createLogger("cleanup");
72484
72767
  var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
72485
72768
  var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
72486
72769
 
@@ -72503,17 +72786,17 @@ class CleanupScheduler {
72503
72786
  }
72504
72787
  start() {
72505
72788
  if (this.isRunning) {
72506
- log49.debug("Cleanup scheduler already running");
72789
+ log50.debug("Cleanup scheduler already running");
72507
72790
  return;
72508
72791
  }
72509
72792
  this.isRunning = true;
72510
- log49.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
72793
+ log50.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
72511
72794
  this.runCleanup().catch((err) => {
72512
- log49.warn(`Initial cleanup failed: ${err}`);
72795
+ log50.warn(`Initial cleanup failed: ${err}`);
72513
72796
  });
72514
72797
  this.timer = setInterval(() => {
72515
72798
  this.runCleanup().catch((err) => {
72516
- log49.warn(`Periodic cleanup failed: ${err}`);
72799
+ log50.warn(`Periodic cleanup failed: ${err}`);
72517
72800
  });
72518
72801
  }, this.intervalMs);
72519
72802
  }
@@ -72523,11 +72806,11 @@ class CleanupScheduler {
72523
72806
  this.timer = null;
72524
72807
  }
72525
72808
  this.isRunning = false;
72526
- log49.debug("Cleanup scheduler stopped");
72809
+ log50.debug("Cleanup scheduler stopped");
72527
72810
  }
72528
72811
  async runCleanup() {
72529
72812
  const startTime = Date.now();
72530
- log49.debug("Running background cleanup...");
72813
+ log50.debug("Running background cleanup...");
72531
72814
  const stats = {
72532
72815
  logsDeleted: 0,
72533
72816
  worktreesCleaned: 0,
@@ -72553,9 +72836,9 @@ class CleanupScheduler {
72553
72836
  const elapsed = Date.now() - startTime;
72554
72837
  const totalCleaned = stats.logsDeleted + stats.worktreesCleaned + stats.metadataCleaned;
72555
72838
  if (totalCleaned > 0 || stats.errors.length > 0) {
72556
- log49.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
72839
+ log50.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
72557
72840
  } else {
72558
- log49.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
72841
+ log50.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
72559
72842
  }
72560
72843
  return stats;
72561
72844
  }
@@ -72568,7 +72851,7 @@ class CleanupScheduler {
72568
72851
  const deleted = cleanupOldLogs(this.logRetentionDays);
72569
72852
  resolve7(deleted);
72570
72853
  } catch (err) {
72571
- log49.warn(`Log cleanup error: ${err}`);
72854
+ log50.warn(`Log cleanup error: ${err}`);
72572
72855
  resolve7(0);
72573
72856
  }
72574
72857
  });
@@ -72576,8 +72859,8 @@ class CleanupScheduler {
72576
72859
  async cleanupOrphanedWorktrees() {
72577
72860
  const worktreesDir = getWorktreesDir();
72578
72861
  const result = { cleaned: 0, metadata: 0 };
72579
- if (!existsSync14(worktreesDir)) {
72580
- log49.debug("No worktrees directory exists, nothing to clean");
72862
+ if (!existsSync15(worktreesDir)) {
72863
+ log50.debug("No worktrees directory exists, nothing to clean");
72581
72864
  return result;
72582
72865
  }
72583
72866
  const persisted = this.sessionStore.load();
@@ -72595,7 +72878,7 @@ class CleanupScheduler {
72595
72878
  continue;
72596
72879
  const worktreePath = join16(worktreesDir, entry.name);
72597
72880
  if (activeWorktrees.has(worktreePath)) {
72598
- log49.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
72881
+ log50.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
72599
72882
  continue;
72600
72883
  }
72601
72884
  const meta = await readWorktreeMetadata(worktreePath);
@@ -72605,7 +72888,7 @@ class CleanupScheduler {
72605
72888
  const lastActivity = new Date(meta.lastActivityAt).getTime();
72606
72889
  const age = now - lastActivity;
72607
72890
  if (meta.sessionId && age < this.maxWorktreeAgeMs) {
72608
- log49.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
72891
+ log50.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
72609
72892
  continue;
72610
72893
  }
72611
72894
  const merged = age >= this.maxWorktreeAgeMs ? await isBranchMerged(meta.repoRoot, meta.branch).catch(() => false) : false;
@@ -72616,7 +72899,7 @@ class CleanupScheduler {
72616
72899
  shouldCleanup = true;
72617
72900
  cleanupReason = `inactive for ${Math.round(age / 3600000)}h`;
72618
72901
  } else {
72619
- log49.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
72902
+ log50.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
72620
72903
  continue;
72621
72904
  }
72622
72905
  } else {
@@ -72625,7 +72908,7 @@ class CleanupScheduler {
72625
72908
  }
72626
72909
  if (!shouldCleanup)
72627
72910
  continue;
72628
- log49.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
72911
+ log50.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
72629
72912
  try {
72630
72913
  if (meta?.repoRoot) {
72631
72914
  await removeWorktree(meta.repoRoot, worktreePath);
@@ -72636,19 +72919,19 @@ class CleanupScheduler {
72636
72919
  await removeWorktreeMetadata(worktreePath);
72637
72920
  result.metadata++;
72638
72921
  } catch (err) {
72639
- log49.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
72922
+ log50.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
72640
72923
  try {
72641
72924
  await rm3(worktreePath, { recursive: true, force: true });
72642
72925
  result.cleaned++;
72643
72926
  await removeWorktreeMetadata(worktreePath);
72644
72927
  result.metadata++;
72645
72928
  } catch (rmErr) {
72646
- log49.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
72929
+ log50.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
72647
72930
  }
72648
72931
  }
72649
72932
  }
72650
72933
  } catch (err) {
72651
- log49.warn(`Failed to scan worktrees directory: ${err}`);
72934
+ log50.warn(`Failed to scan worktrees directory: ${err}`);
72652
72935
  }
72653
72936
  return result;
72654
72937
  }
@@ -72657,8 +72940,8 @@ class CleanupScheduler {
72657
72940
  init_version_check();
72658
72941
  init_spawn();
72659
72942
  init_logger();
72660
- var log50 = createLogger("plugin");
72661
- var sessionLog12 = createSessionLog(log50);
72943
+ var log51 = createLogger("plugin");
72944
+ var sessionLog12 = createSessionLog(log51);
72662
72945
  async function buildPluginRestartCliOptions(session, ctx) {
72663
72946
  const account = session.claudeAccountId ? ctx.ops.getClaudeAccount(session.claudeAccountId) : undefined;
72664
72947
  const memoryConfig = ctx.ops.getPlatformMemoryConfig(session.platformId);
@@ -72701,7 +72984,7 @@ async function runPluginCommand(args, cwd, timeout2 = 60000) {
72701
72984
  });
72702
72985
  proc.on("error", (err) => {
72703
72986
  resolve7({ stdout, stderr, exitCode: 1 });
72704
- log50.error(`Plugin command error: ${err.message}`);
72987
+ log51.error(`Plugin command error: ${err.message}`);
72705
72988
  });
72706
72989
  });
72707
72990
  }
@@ -72742,6 +73025,9 @@ ${formatter.formatCodeBlock(errorMsg, "text")}`);
72742
73025
  }
72743
73026
  await post(session, "success", `✅ Plugin installed: ${formatter.formatCode(pluginName)}
72744
73027
  \uD83D\uDD04 Restarting Claude to load plugin...`);
73028
+ if (session.platform.getMcpConfig().strictMcpConfig === true) {
73029
+ await post(session, "warning", `This platform runs with ${formatter.formatCode("strictMcpConfig: true")}: MCP servers bundled with the plugin will not load. ` + `Declare them under ${formatter.formatCode("mcpServers")} or set ${formatter.formatCode("strictMcpConfig: false")}.`);
73030
+ }
72745
73031
  const cliOptions = await buildPluginRestartCliOptions(session, ctx);
72746
73032
  const success = await restartClaudeSession(session, cliOptions, ctx, `Plugin installation: ${pluginName}`);
72747
73033
  if (success) {
@@ -72806,7 +73092,7 @@ function shouldPostResumeRefusal(platformId, threadId, username, now = Date.now(
72806
73092
 
72807
73093
  // src/session/reaction-router.ts
72808
73094
  init_logger();
72809
- var log51 = createLogger("manager");
73095
+ var log52 = createLogger("manager");
72810
73096
  async function handleReaction(deps, platformId, postId, emojiName, username, action) {
72811
73097
  const normalizedEmoji = normalizeEmojiName(emojiName);
72812
73098
  if (action === "added" && isResumeEmoji(normalizedEmoji)) {
@@ -72821,7 +73107,7 @@ async function handleReaction(deps, platformId, postId, emojiName, username, act
72821
73107
  return;
72822
73108
  const ownerScoped = resolveApprovals(session.platform.approvals, isDcmThreadId(session.threadId)) === "owner";
72823
73109
  if (!session.sessionAllowedUsers.has(username) && (ownerScoped || !session.platform.isUserAllowed(username))) {
72824
- log51.info(`\uD83D\uDEAB rejected reaction from unauthorized user`, {
73110
+ log52.info(`\uD83D\uDEAB rejected reaction from unauthorized user`, {
72825
73111
  event: "reaction.rejected",
72826
73112
  platformId,
72827
73113
  sessionId: session.sessionId,
@@ -72839,7 +73125,7 @@ async function tryResumeFromReaction(deps, platformId, postId, username) {
72839
73125
  if (!persistedSession)
72840
73126
  return false;
72841
73127
  if (!isRevivable(persistedSession)) {
72842
- log51.debug(`Ignoring resume reaction on stopped session ${persistedSession.threadId.substring(0, 8)}...`);
73128
+ log52.debug(`Ignoring resume reaction on stopped session ${persistedSession.threadId.substring(0, 8)}...`);
72843
73129
  return false;
72844
73130
  }
72845
73131
  const sessionId = `${platformId}:${persistedSession.threadId}`;
@@ -72864,7 +73150,7 @@ async function tryResumeFromReaction(deps, platformId, postId, username) {
72864
73150
  return false;
72865
73151
  }
72866
73152
  const shortId = persistedSession.threadId.substring(0, 8);
72867
- log51.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
73153
+ log52.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
72868
73154
  await resumeSession(persistedSession, deps.getContext(), username);
72869
73155
  return true;
72870
73156
  }
@@ -72894,7 +73180,7 @@ async function dispatch(deps, session, postId, emojiName, username, action) {
72894
73180
  }
72895
73181
  if (session.lastError?.postId === postId && isBugReportEmoji(emojiName)) {
72896
73182
  if (session.startedBy === username || session.platform.isUserAllowed(username) || session.sessionAllowedUsers.has(username)) {
72897
- log51.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
73183
+ log52.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
72898
73184
  await reportBug(session, undefined, username, deps.getContext(), session.lastError);
72899
73185
  return;
72900
73186
  }
@@ -72909,7 +73195,7 @@ async function dispatch(deps, session, postId, emojiName, username, action) {
72909
73195
 
72910
73196
  // src/session/manager.ts
72911
73197
  init_logger();
72912
- var log52 = createLogger("manager");
73198
+ var log53 = createLogger("manager");
72913
73199
  var USAGE_PROBE_TIMEOUT_MS = 1e4;
72914
73200
  var USAGE_REFRESH_DEADLINE_MS = 5000;
72915
73201
  var USAGE_CACHE_TTL_MS = 15000;
@@ -72948,6 +73234,7 @@ class SessionManager extends EventEmitter4 {
72948
73234
  platformWatches = new Map;
72949
73235
  autoUpdateManager = null;
72950
73236
  accountPool;
73237
+ connectorsOff = null;
72951
73238
  usageRefreshInFlight = null;
72952
73239
  usageRefreshedAt = 0;
72953
73240
  constructor(workingDir, permissionModeOrSkipFlag = "default", chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts, respondOnlyWhenMentioned = false, userAttribution = true) {
@@ -73039,7 +73326,7 @@ class SessionManager extends EventEmitter4 {
73039
73326
  markNeedsBump(platformId);
73040
73327
  this.updateStickyMessage();
73041
73328
  });
73042
- log52.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
73329
+ log53.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
73043
73330
  }
73044
73331
  removePlatform(platformId) {
73045
73332
  this.platforms.delete(platformId);
@@ -73060,7 +73347,7 @@ class SessionManager extends EventEmitter4 {
73060
73347
  if (users) {
73061
73348
  users.add(sessionId);
73062
73349
  }
73063
- log52.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
73350
+ log53.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
73064
73351
  }
73065
73352
  unregisterWorktreeUser(worktreePath, sessionId) {
73066
73353
  const users = this.worktreeUsers.get(worktreePath);
@@ -73283,7 +73570,7 @@ class SessionManager extends EventEmitter4 {
73283
73570
  try {
73284
73571
  this.persistSessionUnsafe(session);
73285
73572
  } catch (err) {
73286
- log52.error(`Failed to persist session ${session.sessionId}: ${err}`);
73573
+ log53.error(`Failed to persist session ${session.sessionId}: ${err}`);
73287
73574
  }
73288
73575
  }
73289
73576
  persistSessionUnsafe(session) {
@@ -73369,9 +73656,34 @@ class SessionManager extends EventEmitter4 {
73369
73656
  debug: this.debug,
73370
73657
  description: this.customDescription,
73371
73658
  footer: this.customFooter,
73372
- accountPoolStatus: this.accountPool.isEmpty ? undefined : this.accountPool.status()
73659
+ accountPoolStatus: this.accountPool.isEmpty ? undefined : this.accountPool.status(),
73660
+ connectorsOff: this.connectorsOff ?? undefined
73373
73661
  }, overheadByPlatform);
73374
73662
  }
73663
+ async noticeClaudeAiConnectors() {
73664
+ try {
73665
+ const accounts = this.accountPool.isEmpty ? [undefined] : [...this.accountPool.all];
73666
+ const found = new Set;
73667
+ await Promise.all(accounts.map(async (acc) => {
73668
+ const names2 = await probeClaudeAiConnectors(acc);
73669
+ for (const n of names2 ?? [])
73670
+ found.add(n);
73671
+ }));
73672
+ if (found.size === 0)
73673
+ return;
73674
+ const allowedOn = [...this.platforms.entries()].filter(([, client]) => client.getMcpConfig().claudeAiConnectors === true).map(([id]) => id);
73675
+ const names = [...found].sort().join(", ");
73676
+ if (allowedOn.length > 0) {
73677
+ log53.info(`claude.ai connectors (${names}) are available to sessions on: ${allowedOn.join(", ")}`);
73678
+ return;
73679
+ }
73680
+ log53.warn(`This Claude account has claude.ai connectors (${names}). Sessions do not get them: ` + `since 1.35.0 the bot disables them per session. To let a platform's sessions use them, set ` + `claudeAiConnectors: true on that platform in config.yaml (everyone on its allowedUsers gets them).`);
73681
+ this.connectorsOff = found.size;
73682
+ await this.updateAllStickyMessages();
73683
+ } catch (err) {
73684
+ log53.debug(`Connector notice skipped: ${err}`);
73685
+ }
73686
+ }
73375
73687
  async updateAllStickyMessages() {
73376
73688
  await this.updateStickyMessage();
73377
73689
  }
@@ -73408,11 +73720,11 @@ class SessionManager extends EventEmitter4 {
73408
73720
  }
73409
73721
  }
73410
73722
  if (sessionsToKill.length === 0) {
73411
- log52.info(`No active sessions to pause for platform ${platformId}`);
73723
+ log53.info(`No active sessions to pause for platform ${platformId}`);
73412
73724
  await this.updateStickyMessage();
73413
73725
  return;
73414
73726
  }
73415
- log52.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
73727
+ log53.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
73416
73728
  for (const session of sessionsToKill) {
73417
73729
  try {
73418
73730
  const fmt = session.platform.getFormatter();
@@ -73428,9 +73740,9 @@ class SessionManager extends EventEmitter4 {
73428
73740
  session.claude.kill();
73429
73741
  this.registry.unregister(session.sessionId);
73430
73742
  this.emitSessionRemove(session.sessionId);
73431
- log52.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
73743
+ log53.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
73432
73744
  } catch (err) {
73433
- log52.warn(`Failed to pause session ${session.threadId}: ${err}`);
73745
+ log53.warn(`Failed to pause session ${session.threadId}: ${err}`);
73434
73746
  }
73435
73747
  }
73436
73748
  for (const session of sessionsToKill) {
@@ -73451,17 +73763,17 @@ class SessionManager extends EventEmitter4 {
73451
73763
  sessionsToResume.push(state);
73452
73764
  }
73453
73765
  if (sessionsToResume.length === 0) {
73454
- log52.info(`No paused sessions to resume for platform ${platformId}`);
73766
+ log53.info(`No paused sessions to resume for platform ${platformId}`);
73455
73767
  await this.updateStickyMessage();
73456
73768
  return;
73457
73769
  }
73458
- log52.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
73770
+ log53.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
73459
73771
  for (const state of sessionsToResume) {
73460
73772
  try {
73461
73773
  await resumeSession(state, this.getContext());
73462
- log52.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
73774
+ log53.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
73463
73775
  } catch (err) {
73464
- log52.warn(`Failed to resume session ${state.threadId}: ${err}`);
73776
+ log53.warn(`Failed to resume session ${state.threadId}: ${err}`);
73465
73777
  }
73466
73778
  }
73467
73779
  await this.updateStickyMessage();
@@ -73500,14 +73812,14 @@ class SessionManager extends EventEmitter4 {
73500
73812
  const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
73501
73813
  const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
73502
73814
  if (staleIds.length > 0) {
73503
- log52.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
73815
+ log53.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
73504
73816
  }
73505
73817
  const removedCount = this.sessionStore.cleanHistory();
73506
73818
  if (removedCount > 0) {
73507
- log52.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
73819
+ log53.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
73508
73820
  }
73509
73821
  const persisted = this.sessionStore.load();
73510
- log52.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
73822
+ log53.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
73511
73823
  const excludePostIdsByPlatform = new Map;
73512
73824
  for (const session of persisted.values()) {
73513
73825
  const platformId = session.platformId;
@@ -73527,10 +73839,10 @@ class SessionManager extends EventEmitter4 {
73527
73839
  const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
73528
73840
  platform.getBotUser().then((botUser) => {
73529
73841
  cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
73530
- log52.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
73842
+ log53.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
73531
73843
  });
73532
73844
  }).catch((err) => {
73533
- log52.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
73845
+ log53.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
73534
73846
  });
73535
73847
  }
73536
73848
  if (persisted.size > 0) {
@@ -73544,10 +73856,10 @@ class SessionManager extends EventEmitter4 {
73544
73856
  }
73545
73857
  }
73546
73858
  if (pausedToSkip.length > 0) {
73547
- log52.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
73859
+ log53.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
73548
73860
  }
73549
73861
  if (activeToResume.length > 0) {
73550
- log52.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
73862
+ log53.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
73551
73863
  for (const state of activeToResume) {
73552
73864
  await resumeSession(state, this.getContext());
73553
73865
  }
@@ -73663,7 +73975,7 @@ class SessionManager extends EventEmitter4 {
73663
73975
  try {
73664
73976
  return await transcribeForEvaluation(transcriber, platform, getSessionUploadDir(platformId, post2.rootId || post2.id), files);
73665
73977
  } catch (err) {
73666
- log52.warn(`Watch transcription failed for ${platformId}: ${err instanceof Error ? err.message : String(err)}`);
73978
+ log53.warn(`Watch transcription failed for ${platformId}: ${err instanceof Error ? err.message : String(err)}`);
73667
73979
  return "";
73668
73980
  }
73669
73981
  }
@@ -73999,7 +74311,7 @@ Mention me to start a session in this worktree.`, threadId);
73999
74311
  const message = messageBuilder(formatter);
74000
74312
  await post(session, "info", message);
74001
74313
  } catch (err) {
74002
- log52.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
74314
+ log53.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
74003
74315
  }
74004
74316
  }
74005
74317
  }
@@ -74018,7 +74330,7 @@ Mention me to start a session in this worktree.`, threadId);
74018
74330
  session.messageManager?.setPendingUpdatePrompt({ postId: post2.id });
74019
74331
  this.registerPost(post2.id, session.threadId);
74020
74332
  } catch (err) {
74021
- log52.warn(`Failed to post ask message to ${threadId}: ${err}`);
74333
+ log53.warn(`Failed to post ask message to ${threadId}: ${err}`);
74022
74334
  }
74023
74335
  }
74024
74336
  }
@@ -83941,29 +84253,29 @@ function SessionLog({ logs, maxLines = 20 }) {
83941
84253
  return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
83942
84254
  flexDirection: "column",
83943
84255
  flexShrink: 0,
83944
- children: displayLogs.map((log53) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
84256
+ children: displayLogs.map((log54) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
83945
84257
  flexShrink: 0,
83946
84258
  children: [
83947
84259
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
83948
- color: getColorForLevel(log53.level),
84260
+ color: getColorForLevel(log54.level),
83949
84261
  dimColor: true,
83950
84262
  wrap: "truncate",
83951
84263
  children: [
83952
84264
  "[",
83953
- padComponent(log53.component),
84265
+ padComponent(log54.component),
83954
84266
  "]"
83955
84267
  ]
83956
84268
  }, undefined, true, undefined, this),
83957
84269
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
83958
- color: getColorForLevel(log53.level),
84270
+ color: getColorForLevel(log54.level),
83959
84271
  wrap: "truncate",
83960
84272
  children: [
83961
84273
  " ",
83962
- log53.message
84274
+ log54.message
83963
84275
  ]
83964
84276
  }, undefined, true, undefined, this)
83965
84277
  ]
83966
- }, log53.id, true, undefined, this))
84278
+ }, log54.id, true, undefined, this))
83967
84279
  }, undefined, false, undefined, this);
83968
84280
  }
83969
84281
  // src/ui/components/Footer.tsx
@@ -84487,7 +84799,7 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
84487
84799
  const scrollRef = import_react62.default.useRef(null);
84488
84800
  const { stdout } = use_stdout_default();
84489
84801
  const isDebug = process.env.DEBUG === "1";
84490
- const displayLogs = logs.filter((log53) => isDebug || log53.level !== "debug");
84802
+ const displayLogs = logs.filter((log54) => isDebug || log54.level !== "debug");
84491
84803
  const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
84492
84804
  import_react62.default.useEffect(() => {
84493
84805
  const handleResize = () => scrollRef.current?.remeasure();
@@ -84527,25 +84839,25 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
84527
84839
  overflow: "hidden",
84528
84840
  children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ScrollView, {
84529
84841
  ref: scrollRef,
84530
- children: visibleLogs.map((log53) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
84842
+ children: visibleLogs.map((log54) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
84531
84843
  children: [
84532
84844
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
84533
84845
  dimColor: true,
84534
84846
  children: [
84535
84847
  "[",
84536
- padComponent2(log53.component),
84848
+ padComponent2(log54.component),
84537
84849
  "]"
84538
84850
  ]
84539
84851
  }, undefined, true, undefined, this),
84540
84852
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
84541
- color: getLevelColor(log53.level),
84853
+ color: getLevelColor(log54.level),
84542
84854
  children: [
84543
84855
  " ",
84544
- log53.message
84856
+ log54.message
84545
84857
  ]
84546
84858
  }, undefined, true, undefined, this)
84547
84859
  ]
84548
- }, log53.id, true, undefined, this))
84860
+ }, log54.id, true, undefined, this))
84549
84861
  }, undefined, false, undefined, this)
84550
84862
  }, undefined, false, undefined, this);
84551
84863
  }
@@ -85071,10 +85383,10 @@ function useAppState(initialConfig) {
85071
85383
  });
85072
85384
  }, []);
85073
85385
  const getLogsForSession = import_react63.useCallback((sessionId) => {
85074
- return state.logs.filter((log53) => log53.sessionId === sessionId);
85386
+ return state.logs.filter((log54) => log54.sessionId === sessionId);
85075
85387
  }, [state.logs]);
85076
85388
  const getGlobalLogs = import_react63.useCallback(() => {
85077
- return state.logs.filter((log53) => !log53.sessionId);
85389
+ return state.logs.filter((log54) => !log54.sessionId);
85078
85390
  }, [state.logs]);
85079
85391
  const togglePlatformEnabled = import_react63.useCallback((platformId) => {
85080
85392
  let newEnabled = false;
@@ -86214,7 +86526,7 @@ import { EventEmitter as EventEmitter9 } from "events";
86214
86526
  // src/auto-update/checker.ts
86215
86527
  init_logger();
86216
86528
  import { EventEmitter as EventEmitter7 } from "events";
86217
- var log53 = createLogger("checker");
86529
+ var log54 = createLogger("checker");
86218
86530
  var PACKAGE_NAME = "claude-threads";
86219
86531
  function compareVersions(a, b) {
86220
86532
  const partsA = a.replace(/^v/, "").split(".").map(Number);
@@ -86237,13 +86549,13 @@ async function fetchLatestVersion() {
86237
86549
  }
86238
86550
  });
86239
86551
  if (!response.ok) {
86240
- log53.warn(`Failed to fetch latest version: HTTP ${response.status}`);
86552
+ log54.warn(`Failed to fetch latest version: HTTP ${response.status}`);
86241
86553
  return null;
86242
86554
  }
86243
86555
  const data = await response.json();
86244
86556
  return data.version ?? null;
86245
86557
  } catch (err) {
86246
- log53.warn(`Failed to fetch latest version: ${err}`);
86558
+ log54.warn(`Failed to fetch latest version: ${err}`);
86247
86559
  return null;
86248
86560
  }
86249
86561
  }
@@ -86260,38 +86572,38 @@ class UpdateChecker extends EventEmitter7 {
86260
86572
  }
86261
86573
  start() {
86262
86574
  if (!this.config.enabled) {
86263
- log53.debug("Auto-update disabled, not starting checker");
86575
+ log54.debug("Auto-update disabled, not starting checker");
86264
86576
  return;
86265
86577
  }
86266
86578
  setTimeout(() => {
86267
86579
  this.check().catch((err) => {
86268
- log53.warn(`Initial update check failed: ${err}`);
86580
+ log54.warn(`Initial update check failed: ${err}`);
86269
86581
  });
86270
86582
  }, 5000);
86271
86583
  const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
86272
86584
  this.checkInterval = setInterval(() => {
86273
86585
  this.check().catch((err) => {
86274
- log53.warn(`Periodic update check failed: ${err}`);
86586
+ log54.warn(`Periodic update check failed: ${err}`);
86275
86587
  });
86276
86588
  }, intervalMs);
86277
- log53.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
86589
+ log54.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
86278
86590
  }
86279
86591
  stop() {
86280
86592
  if (this.checkInterval) {
86281
86593
  clearInterval(this.checkInterval);
86282
86594
  this.checkInterval = null;
86283
86595
  }
86284
- log53.debug("Update checker stopped");
86596
+ log54.debug("Update checker stopped");
86285
86597
  }
86286
86598
  async check() {
86287
86599
  if (this.isChecking) {
86288
- log53.debug("Check already in progress, skipping");
86600
+ log54.debug("Check already in progress, skipping");
86289
86601
  return this.lastUpdateInfo;
86290
86602
  }
86291
86603
  this.isChecking = true;
86292
86604
  this.emit("check:start");
86293
86605
  try {
86294
- log53.debug("Checking for updates...");
86606
+ log54.debug("Checking for updates...");
86295
86607
  const latestVersion2 = await fetchLatestVersion();
86296
86608
  if (!latestVersion2) {
86297
86609
  this.emit("check:complete", false);
@@ -86308,18 +86620,18 @@ class UpdateChecker extends EventEmitter7 {
86308
86620
  detectedAt: new Date
86309
86621
  };
86310
86622
  if (!this.lastUpdateInfo || this.lastUpdateInfo.latestVersion !== latestVersion2) {
86311
- log53.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
86623
+ log54.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
86312
86624
  this.lastUpdateInfo = updateInfo;
86313
86625
  this.emit("update", updateInfo);
86314
86626
  }
86315
86627
  this.emit("check:complete", true);
86316
86628
  return updateInfo;
86317
86629
  }
86318
- log53.debug(`Up to date (v${currentVersion})`);
86630
+ log54.debug(`Up to date (v${currentVersion})`);
86319
86631
  this.emit("check:complete", false);
86320
86632
  return null;
86321
86633
  } catch (err) {
86322
- log53.warn(`Update check failed: ${err}`);
86634
+ log54.warn(`Update check failed: ${err}`);
86323
86635
  this.emit("check:error", err);
86324
86636
  return null;
86325
86637
  } finally {
@@ -86390,7 +86702,7 @@ function isInScheduledWindow(window2) {
86390
86702
  }
86391
86703
 
86392
86704
  // src/auto-update/scheduler.ts
86393
- var log54 = createLogger("scheduler");
86705
+ var log55 = createLogger("scheduler");
86394
86706
 
86395
86707
  class UpdateScheduler extends EventEmitter8 {
86396
86708
  config;
@@ -86414,7 +86726,7 @@ class UpdateScheduler extends EventEmitter8 {
86414
86726
  scheduleUpdate(updateInfo) {
86415
86727
  this.pendingUpdate = updateInfo;
86416
86728
  if (this.config.autoRestartMode === "immediate") {
86417
- log54.info("Immediate mode: triggering update now");
86729
+ log55.info("Immediate mode: triggering update now");
86418
86730
  this.emit("ready", updateInfo);
86419
86731
  return;
86420
86732
  }
@@ -86427,19 +86739,19 @@ class UpdateScheduler extends EventEmitter8 {
86427
86739
  this.scheduledRestartAt = null;
86428
86740
  this.askApprovals.clear();
86429
86741
  this.askStartTime = null;
86430
- log54.debug("Update schedule cancelled");
86742
+ log55.debug("Update schedule cancelled");
86431
86743
  }
86432
86744
  deferUpdate(minutes) {
86433
86745
  const deferUntil = new Date(Date.now() + minutes * 60 * 1000);
86434
86746
  this.scheduledRestartAt = null;
86435
86747
  this.idleStartTime = null;
86436
86748
  this.emit("deferred", deferUntil);
86437
- log54.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
86749
+ log55.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
86438
86750
  return deferUntil;
86439
86751
  }
86440
86752
  recordAskResponse(threadId, approved) {
86441
86753
  this.askApprovals.set(threadId, approved);
86442
- log54.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
86754
+ log55.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
86443
86755
  this.checkAskCondition();
86444
86756
  }
86445
86757
  getScheduledRestartAt() {
@@ -86460,7 +86772,7 @@ class UpdateScheduler extends EventEmitter8 {
86460
86772
  return;
86461
86773
  this.checkCondition();
86462
86774
  this.checkTimer = setInterval(() => this.checkCondition(), 1e4);
86463
- log54.debug(`Started checking for ${this.config.autoRestartMode} condition`);
86775
+ log55.debug(`Started checking for ${this.config.autoRestartMode} condition`);
86464
86776
  }
86465
86777
  stopChecking() {
86466
86778
  if (this.checkTimer) {
@@ -86491,17 +86803,17 @@ class UpdateScheduler extends EventEmitter8 {
86491
86803
  if (activity.activeSessionCount === 0) {
86492
86804
  if (!this.idleStartTime) {
86493
86805
  this.idleStartTime = new Date;
86494
- log54.debug("No active sessions, starting idle timer");
86806
+ log55.debug("No active sessions, starting idle timer");
86495
86807
  }
86496
86808
  const idleMs = Date.now() - this.idleStartTime.getTime();
86497
86809
  const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
86498
86810
  if (idleMs >= requiredMs) {
86499
- log54.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
86811
+ log55.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
86500
86812
  this.triggerCountdown();
86501
86813
  }
86502
86814
  } else {
86503
86815
  if (this.idleStartTime) {
86504
- log54.debug("Sessions became active, resetting idle timer");
86816
+ log55.debug("Sessions became active, resetting idle timer");
86505
86817
  this.idleStartTime = null;
86506
86818
  }
86507
86819
  }
@@ -86512,7 +86824,7 @@ class UpdateScheduler extends EventEmitter8 {
86512
86824
  const quietMs = Date.now() - activity.lastActivityAt.getTime();
86513
86825
  const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
86514
86826
  if (quietMs >= requiredMs && !activity.anySessionBusy) {
86515
- log54.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
86827
+ log55.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
86516
86828
  this.triggerCountdown();
86517
86829
  }
86518
86830
  } else if (activity.activeSessionCount === 0) {
@@ -86522,7 +86834,7 @@ class UpdateScheduler extends EventEmitter8 {
86522
86834
  const idleMs = Date.now() - this.idleStartTime.getTime();
86523
86835
  const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
86524
86836
  if (idleMs >= requiredMs) {
86525
- log54.info("No sessions and quiet timeout reached, triggering update");
86837
+ log55.info("No sessions and quiet timeout reached, triggering update");
86526
86838
  this.triggerCountdown();
86527
86839
  }
86528
86840
  }
@@ -86533,13 +86845,13 @@ class UpdateScheduler extends EventEmitter8 {
86533
86845
  }
86534
86846
  const activity = this.getSessionActivity();
86535
86847
  if (activity.activeSessionCount === 0) {
86536
- log54.info("Within scheduled window and no active sessions, triggering update");
86848
+ log55.info("Within scheduled window and no active sessions, triggering update");
86537
86849
  this.triggerCountdown();
86538
86850
  } else if (activity.lastActivityAt) {
86539
86851
  const quietMs = Date.now() - activity.lastActivityAt.getTime();
86540
86852
  const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
86541
86853
  if (quietMs >= requiredMs && !activity.anySessionBusy) {
86542
- log54.info("Within scheduled window and sessions quiet, triggering update");
86854
+ log55.info("Within scheduled window and sessions quiet, triggering update");
86543
86855
  this.triggerCountdown();
86544
86856
  }
86545
86857
  }
@@ -86547,14 +86859,14 @@ class UpdateScheduler extends EventEmitter8 {
86547
86859
  checkAskCondition() {
86548
86860
  const threadIds = this.getActiveThreadIds();
86549
86861
  if (threadIds.length === 0) {
86550
- log54.info("No active threads, proceeding with update");
86862
+ log55.info("No active threads, proceeding with update");
86551
86863
  this.triggerCountdown();
86552
86864
  return;
86553
86865
  }
86554
86866
  if (!this.askStartTime && this.pendingUpdate) {
86555
86867
  this.askStartTime = new Date;
86556
86868
  this.postAskMessage(threadIds, this.pendingUpdate.latestVersion).catch((err) => {
86557
- log54.warn(`Failed to post ask message: ${err}`);
86869
+ log55.warn(`Failed to post ask message: ${err}`);
86558
86870
  });
86559
86871
  return;
86560
86872
  }
@@ -86567,12 +86879,12 @@ class UpdateScheduler extends EventEmitter8 {
86567
86879
  denials++;
86568
86880
  }
86569
86881
  if (approvals > threadIds.length / 2) {
86570
- log54.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
86882
+ log55.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
86571
86883
  this.triggerCountdown();
86572
86884
  return;
86573
86885
  }
86574
86886
  if (denials > threadIds.length / 2) {
86575
- log54.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
86887
+ log55.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
86576
86888
  this.deferUpdate(60);
86577
86889
  return;
86578
86890
  }
@@ -86580,7 +86892,7 @@ class UpdateScheduler extends EventEmitter8 {
86580
86892
  const elapsedMs = Date.now() - this.askStartTime.getTime();
86581
86893
  const timeoutMs = this.config.askTimeoutMinutes * 60 * 1000;
86582
86894
  if (elapsedMs >= timeoutMs) {
86583
- log54.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
86895
+ log55.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
86584
86896
  this.triggerCountdown();
86585
86897
  }
86586
86898
  }
@@ -86600,7 +86912,7 @@ class UpdateScheduler extends EventEmitter8 {
86600
86912
  this.emit("ready", this.pendingUpdate);
86601
86913
  }
86602
86914
  }, 1000);
86603
- log54.info("Update countdown started (60 seconds)");
86915
+ log55.info("Update countdown started (60 seconds)");
86604
86916
  }
86605
86917
  stopCountdown() {
86606
86918
  if (this.countdownTimer) {
@@ -86612,28 +86924,28 @@ class UpdateScheduler extends EventEmitter8 {
86612
86924
 
86613
86925
  // src/auto-update/installer.ts
86614
86926
  import { spawn as spawn4, spawnSync } from "child_process";
86615
- import { existsSync as existsSync16, readFileSync as readFileSync12, mkdirSync as mkdirSync8 } from "fs";
86927
+ import { existsSync as existsSync17, readFileSync as readFileSync12, mkdirSync as mkdirSync8 } from "fs";
86616
86928
  import { dirname as dirname9, resolve as resolve7 } from "path";
86617
86929
  import { homedir as homedir9 } from "os";
86618
86930
  init_logger();
86619
- var log55 = createLogger("installer");
86931
+ var log56 = createLogger("installer");
86620
86932
  function detectPackageManager() {
86621
86933
  const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
86622
86934
  const originalInstaller = detectOriginalInstaller();
86623
86935
  if (originalInstaller) {
86624
- log55.debug(`Detected original installer: ${originalInstaller}`);
86936
+ log56.debug(`Detected original installer: ${originalInstaller}`);
86625
86937
  if (originalInstaller === "bun") {
86626
86938
  const bunCheck2 = spawnSync("bun", ["--version"], { stdio: "ignore" });
86627
86939
  if (bunCheck2.status === 0) {
86628
86940
  return { cmd: "bun", isBun: true };
86629
86941
  }
86630
- log55.warn("Originally installed with bun, but bun not found. Falling back to npm.");
86942
+ log56.warn("Originally installed with bun, but bun not found. Falling back to npm.");
86631
86943
  } else {
86632
86944
  const npmCheck2 = spawnSync(npmCmd, ["--version"], { stdio: "ignore" });
86633
86945
  if (npmCheck2.status === 0) {
86634
86946
  return { cmd: npmCmd, isBun: false };
86635
86947
  }
86636
- log55.warn("Originally installed with npm, but npm not found. Falling back to bun.");
86948
+ log56.warn("Originally installed with npm, but npm not found. Falling back to bun.");
86637
86949
  }
86638
86950
  }
86639
86951
  const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
@@ -86679,34 +86991,34 @@ var STATE_PATH = resolve7(homedir9(), ".config", "claude-threads", UPDATE_STATE_
86679
86991
  var PACKAGE_NAME2 = "claude-threads";
86680
86992
  function loadUpdateState() {
86681
86993
  try {
86682
- if (existsSync16(STATE_PATH)) {
86994
+ if (existsSync17(STATE_PATH)) {
86683
86995
  const content = readFileSync12(STATE_PATH, "utf-8");
86684
86996
  return JSON.parse(content);
86685
86997
  }
86686
86998
  } catch (err) {
86687
- log55.warn(`Failed to load update state: ${err}`);
86999
+ log56.warn(`Failed to load update state: ${err}`);
86688
87000
  }
86689
87001
  return {};
86690
87002
  }
86691
87003
  function saveUpdateState(state) {
86692
87004
  try {
86693
87005
  const dir = dirname9(STATE_PATH);
86694
- if (!existsSync16(dir)) {
87006
+ if (!existsSync17(dir)) {
86695
87007
  mkdirSync8(dir, { recursive: true, mode: 448 });
86696
87008
  }
86697
87009
  writeFileAtomic(STATE_PATH, JSON.stringify(state, null, 2));
86698
- log55.debug("Update state saved");
87010
+ log56.debug("Update state saved");
86699
87011
  } catch (err) {
86700
- log55.warn(`Failed to save update state: ${err}`);
87012
+ log56.warn(`Failed to save update state: ${err}`);
86701
87013
  }
86702
87014
  }
86703
87015
  function clearUpdateState() {
86704
87016
  try {
86705
- if (existsSync16(STATE_PATH)) {
87017
+ if (existsSync17(STATE_PATH)) {
86706
87018
  writeFileAtomic(STATE_PATH, "{}");
86707
87019
  }
86708
87020
  } catch (err) {
86709
- log55.warn(`Failed to clear update state: ${err}`);
87021
+ log56.warn(`Failed to clear update state: ${err}`);
86710
87022
  }
86711
87023
  }
86712
87024
  function checkJustUpdated() {
@@ -86738,11 +87050,11 @@ function clearRuntimeSettings() {
86738
87050
  }
86739
87051
  }
86740
87052
  async function installVersion(version) {
86741
- log55.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
87053
+ log56.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
86742
87054
  const pm = detectPackageManager();
86743
87055
  if (!pm) {
86744
87056
  const error = "Neither bun nor npm found in PATH. Cannot install update.";
86745
- log55.error(`❌ ${error}`);
87057
+ log56.error(`❌ ${error}`);
86746
87058
  return { success: false, error };
86747
87059
  }
86748
87060
  saveUpdateState({
@@ -86754,7 +87066,7 @@ async function installVersion(version) {
86754
87066
  return new Promise((resolve8) => {
86755
87067
  const { cmd, isBun: isBun3 } = pm;
86756
87068
  const args = ["install", "-g", `${PACKAGE_NAME2}@${version}`];
86757
- log55.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
87069
+ log56.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
86758
87070
  const child = spawn4(cmd, args, {
86759
87071
  stdio: ["ignore", "pipe", "pipe"],
86760
87072
  env: {
@@ -86772,7 +87084,7 @@ async function installVersion(version) {
86772
87084
  });
86773
87085
  child.on("close", (code) => {
86774
87086
  if (code === 0) {
86775
- log55.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
87087
+ log56.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
86776
87088
  saveUpdateState({
86777
87089
  previousVersion: VERSION,
86778
87090
  targetVersion: version,
@@ -86782,20 +87094,20 @@ async function installVersion(version) {
86782
87094
  resolve8({ success: true });
86783
87095
  } else {
86784
87096
  const errorMsg = stderr || stdout || `Exit code: ${code}`;
86785
- log55.error(`❌ Installation failed: ${errorMsg}`);
87097
+ log56.error(`❌ Installation failed: ${errorMsg}`);
86786
87098
  clearUpdateState();
86787
87099
  resolve8({ success: false, error: errorMsg });
86788
87100
  }
86789
87101
  });
86790
87102
  child.on("error", (err) => {
86791
- log55.error(`❌ Failed to spawn npm: ${err}`);
87103
+ log56.error(`❌ Failed to spawn npm: ${err}`);
86792
87104
  clearUpdateState();
86793
87105
  resolve8({ success: false, error: err.message });
86794
87106
  });
86795
87107
  setTimeout(() => {
86796
87108
  if (child.exitCode === null) {
86797
87109
  child.kill();
86798
- log55.error("❌ Installation timed out");
87110
+ log56.error("❌ Installation timed out");
86799
87111
  clearUpdateState();
86800
87112
  resolve8({ success: false, error: "Installation timed out" });
86801
87113
  }
@@ -86839,9 +87151,9 @@ class UpdateInstaller {
86839
87151
  // src/auto-update/respawn.ts
86840
87152
  init_logger();
86841
87153
  import { spawn as spawn5 } from "child_process";
86842
- import { existsSync as existsSync17, statSync as statSync5 } from "fs";
87154
+ import { existsSync as existsSync18, statSync as statSync5 } from "fs";
86843
87155
  import { delimiter, join as join17 } from "path";
86844
- var log56 = createLogger("respawn");
87156
+ var log57 = createLogger("respawn");
86845
87157
  function decideRespawn(env5 = process.env, isTTY = !!process.stdout.isTTY) {
86846
87158
  if (env5.CLAUDE_THREADS_BIN) {
86847
87159
  return { kind: "exit-for-supervisor", supervisor: "claude-threads-daemon" };
@@ -86860,7 +87172,7 @@ function decideRespawn(env5 = process.env, isTTY = !!process.stdout.isTTY) {
86860
87172
  }
86861
87173
  return { kind: "self-respawn" };
86862
87174
  }
86863
- function resolveClaudeThreadsBin(_env = process.env, _existsSync = existsSync17, _isFileExecutable = isFileExecutable) {
87175
+ function resolveClaudeThreadsBin(_env = process.env, _existsSync = existsSync18, _isFileExecutable = isFileExecutable) {
86864
87176
  const isWin2 = process.platform === "win32";
86865
87177
  const names = isWin2 ? ["claude-threads.cmd", "claude-threads.exe", "claude-threads.bat"] : ["claude-threads"];
86866
87178
  const path10 = _env.PATH || _env.Path || "";
@@ -86897,7 +87209,7 @@ function isFileExecutable(path10) {
86897
87209
  }
86898
87210
  function spawnReplacement(argv = process.argv.slice(2), binPath = resolveClaudeThreadsBin()) {
86899
87211
  if (!binPath) {
86900
- log56.error("Could not resolve claude-threads on PATH; self-respawn aborted");
87212
+ log57.error("Could not resolve claude-threads on PATH; self-respawn aborted");
86901
87213
  return false;
86902
87214
  }
86903
87215
  if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
@@ -86918,23 +87230,23 @@ function spawnReplacement(argv = process.argv.slice(2), binPath = resolveClaudeT
86918
87230
  shell: useShell
86919
87231
  });
86920
87232
  } catch (err) {
86921
- log56.error(`spawn() threw: ${err instanceof Error ? err.message : String(err)}`);
87233
+ log57.error(`spawn() threw: ${err instanceof Error ? err.message : String(err)}`);
86922
87234
  return false;
86923
87235
  }
86924
87236
  child.once("error", (err) => {
86925
- log56.error(`Replacement process error: ${err.message}`);
87237
+ log57.error(`Replacement process error: ${err.message}`);
86926
87238
  });
86927
87239
  if (child.pid === undefined) {
86928
- log56.error("Spawn returned no pid (binary likely not executable)");
87240
+ log57.error("Spawn returned no pid (binary likely not executable)");
86929
87241
  return false;
86930
87242
  }
86931
87243
  child.unref();
86932
- log56.info(`Spawned replacement pid=${child.pid} from ${binPath}`);
87244
+ log57.info(`Spawned replacement pid=${child.pid} from ${binPath}`);
86933
87245
  return true;
86934
87246
  }
86935
87247
 
86936
87248
  // src/auto-update/manager.ts
86937
- var log57 = createLogger("updater");
87249
+ var log58 = createLogger("updater");
86938
87250
 
86939
87251
  class AutoUpdateManager extends EventEmitter9 {
86940
87252
  config;
@@ -86957,23 +87269,23 @@ class AutoUpdateManager extends EventEmitter9 {
86957
87269
  }
86958
87270
  start() {
86959
87271
  if (!this.config.enabled) {
86960
- log57.info("Auto-update is disabled");
87272
+ log58.info("Auto-update is disabled");
86961
87273
  return;
86962
87274
  }
86963
87275
  const updateResult = this.installer.checkJustUpdated();
86964
87276
  if (updateResult) {
86965
- log57.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
87277
+ log58.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
86966
87278
  this.callbacks.broadcastUpdate((fmt) => `\uD83C\uDF89 ${fmt.formatBold("Bot updated")} from v${updateResult.previousVersion} to v${updateResult.currentVersion}`).catch((err) => {
86967
- log57.warn(`Failed to broadcast update notification: ${err}`);
87279
+ log58.warn(`Failed to broadcast update notification: ${err}`);
86968
87280
  });
86969
87281
  }
86970
87282
  this.checker.start();
86971
- log57.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
87283
+ log58.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
86972
87284
  }
86973
87285
  stop() {
86974
87286
  this.checker.stop();
86975
87287
  this.scheduler.stop();
86976
- log57.debug("Auto-update manager stopped");
87288
+ log58.debug("Auto-update manager stopped");
86977
87289
  }
86978
87290
  getState() {
86979
87291
  return { ...this.state };
@@ -86987,10 +87299,10 @@ class AutoUpdateManager extends EventEmitter9 {
86987
87299
  async forceUpdate() {
86988
87300
  const updateInfo = this.state.updateInfo || await this.checker.check();
86989
87301
  if (!updateInfo) {
86990
- log57.info("No update available");
87302
+ log58.info("No update available");
86991
87303
  return;
86992
87304
  }
86993
- log57.info("Forcing immediate update");
87305
+ log58.info("Forcing immediate update");
86994
87306
  await this.performUpdate(updateInfo);
86995
87307
  }
86996
87308
  deferUpdate(minutes = 60) {
@@ -87056,11 +87368,11 @@ class AutoUpdateManager extends EventEmitter9 {
87056
87368
  await this.callbacks.prepareForRestart();
87057
87369
  } catch (err) {
87058
87370
  const reason = err instanceof Error ? err.message : String(err);
87059
- log57.error(`prepareForRestart failed: ${reason}`);
87371
+ log58.error(`prepareForRestart failed: ${reason}`);
87060
87372
  await this.callbacks.broadcastUpdate((fmt) => `⚠️ ${fmt.formatBold("Restart aborted")}: shutdown sequence failed (${reason}). Sessions may be in an inconsistent state; please run ${fmt.formatCode("claude-threads")} manually.`).catch(() => {});
87061
87373
  process.exit(1);
87062
87374
  }
87063
- log57.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
87375
+ log58.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
87064
87376
  process.stdout.write("\x1B[2J\x1B[H");
87065
87377
  process.stdout.write("\x1B[?25h");
87066
87378
  if (decision.kind === "self-respawn") {
@@ -87069,14 +87381,14 @@ class AutoUpdateManager extends EventEmitter9 {
87069
87381
  if (ok) {
87070
87382
  process.exit(0);
87071
87383
  }
87072
- log57.error("Self-respawn launch failed after binary resolution succeeded");
87384
+ log58.error("Self-respawn launch failed after binary resolution succeeded");
87073
87385
  await this.callbacks.broadcastUpdate((fmt) => `⚠️ ${fmt.formatBold("Auto-restart failed")} after install: please run ${fmt.formatCode("claude-threads")} to bring the bot back. Sessions are persisted and will resume.`).catch(() => {});
87074
87386
  } else {
87075
- log57.error("claude-threads not found on PATH; manual restart required");
87387
+ log58.error("claude-threads not found on PATH; manual restart required");
87076
87388
  }
87077
87389
  process.exit(0);
87078
87390
  }
87079
- log57.debug(`Restart handled by supervisor: ${decision.supervisor}`);
87391
+ log58.debug(`Restart handled by supervisor: ${decision.supervisor}`);
87080
87392
  process.exit(RESTART_EXIT_CODE);
87081
87393
  } else {
87082
87394
  const errorMsg = result.error ?? "Unknown error";
@@ -87282,6 +87594,15 @@ async function startWithoutDaemon() {
87282
87594
  if (!newConfig.platforms || newConfig.platforms.length === 0) {
87283
87595
  throw new Error("No platforms configured. Run with --setup to configure.");
87284
87596
  }
87597
+ let mcpPostureWarnings = [];
87598
+ try {
87599
+ mcpPostureWarnings = resolvePlatformMcpPosture(newConfig.platforms, newConfig.mcpServers).warnings;
87600
+ } catch (err) {
87601
+ console.error(red(` ❌ ${err instanceof Error ? err.message : String(err)}`));
87602
+ process.exit(1);
87603
+ }
87604
+ for (const w of mcpPostureWarnings)
87605
+ console.warn(w);
87285
87606
  const config = newConfig;
87286
87607
  const firstPlatformConfig = config.platforms[0];
87287
87608
  const initialPermissionMode = resolvePermissionMode({
@@ -87433,6 +87754,9 @@ async function startWithoutDaemon() {
87433
87754
  }
87434
87755
  }
87435
87756
  });
87757
+ for (const w of mcpPostureWarnings) {
87758
+ ui.addLog({ level: "warn", component: "config", message: w });
87759
+ }
87436
87760
  setLogHandler((level, component, message, sessionId) => {
87437
87761
  ui.addLog({ level, component, message, sessionId });
87438
87762
  });
@@ -87655,6 +87979,7 @@ async function startWithoutDaemon() {
87655
87979
  });
87656
87980
  autoUpdateManager.start();
87657
87981
  ui.setReady();
87982
+ session.noticeClaudeAiConnectors();
87658
87983
  const shutdown = async (_signal) => {
87659
87984
  if (isShuttingDown2)
87660
87985
  return;