switchroom 0.18.28 → 0.18.29

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.
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.18.28", COMMIT_SHA = "ef0be9a2";
2123
+ var VERSION = "0.18.29", COMMIT_SHA = "8f9b38b3";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -15265,431 +15265,6 @@ var init_users = __esm(() => {
15265
15265
  init_merge();
15266
15266
  });
15267
15267
 
15268
- // src/agents/pane-lock.ts
15269
- function withPaneLock(key, fn) {
15270
- const tail = tails.get(key) ?? Promise.resolve();
15271
- const run = tail.then(() => fn());
15272
- const next = run.then(() => {}, () => {});
15273
- tails.set(key, next);
15274
- next.then(() => {
15275
- if (tails.get(key) === next)
15276
- tails.delete(key);
15277
- });
15278
- return run;
15279
- }
15280
- var tails;
15281
- var init_pane_lock = __esm(() => {
15282
- tails = new Map;
15283
- });
15284
-
15285
- // src/agents/inject.ts
15286
- var exports_inject = {};
15287
- __export(exports_inject, {
15288
- validateInjectCommand: () => validateInjectCommand,
15289
- normalizeInjectCommand: () => normalizeInjectCommand,
15290
- makeTmuxRunner: () => makeTmuxRunner,
15291
- isTuiChromeLine: () => isTuiChromeLine,
15292
- injectSlashCommandWith: () => injectSlashCommandWith,
15293
- injectSlashCommand: () => injectSlashCommand,
15294
- diffPane: () => diffPane,
15295
- dedupeInjectQueue: () => dedupeInjectQueue,
15296
- InjectError: () => InjectError,
15297
- INJECT_COMMANDS: () => INJECT_COMMANDS,
15298
- INJECT_BLOCKLIST: () => INJECT_BLOCKLIST,
15299
- INJECT_BLOCKED: () => INJECT_BLOCKED,
15300
- INJECT_ALLOWLIST: () => INJECT_ALLOWLIST
15301
- });
15302
- import { execFileSync } from "node:child_process";
15303
- function validateInjectCommand(command) {
15304
- if (typeof command !== "string" || command.trim().length === 0) {
15305
- throw new InjectError("invalid", "command is empty");
15306
- }
15307
- const trimmed = command.trim();
15308
- if (!trimmed.startsWith("/")) {
15309
- throw new InjectError("invalid", `command must start with "/": got ${trimmed}`);
15310
- }
15311
- const bare = trimmed.split(/\s+/, 1)[0].toLowerCase();
15312
- const blockedMeta = INJECT_BLOCKED.get(bare);
15313
- if (blockedMeta) {
15314
- throw new InjectError("blocked", `${bare} is explicitly blocked from inject (${blockedMeta.reason}).`);
15315
- }
15316
- const meta = INJECT_COMMANDS.get(bare);
15317
- if (!meta) {
15318
- const allowed = [...INJECT_COMMANDS.keys()].sort().join(", ");
15319
- throw new InjectError("not_allowed", `${bare} is not in the inject allowlist. Allowed: ${allowed}`);
15320
- }
15321
- const hasArgs = trimmed.split(/\s+/).length > 1;
15322
- if (hasArgs && !meta.argsAllowed) {
15323
- throw new InjectError("args_not_allowed", `args not permitted for ${bare} \u2014 inject the bare verb only`);
15324
- }
15325
- return bare;
15326
- }
15327
- function normalizeInjectCommand(command) {
15328
- return command.trim().replace(/\s+/g, " ").toLowerCase();
15329
- }
15330
- function dedupeInjectQueue(commands) {
15331
- const seen = new Set;
15332
- const out = [];
15333
- for (const raw of commands) {
15334
- if (typeof raw !== "string")
15335
- continue;
15336
- const key = normalizeInjectCommand(raw);
15337
- if (key.length === 0)
15338
- continue;
15339
- if (seen.has(key))
15340
- continue;
15341
- seen.add(key);
15342
- out.push(raw.trim());
15343
- }
15344
- return out;
15345
- }
15346
- function defaultSocketName(agentName) {
15347
- return `switchroom-${agentName}`;
15348
- }
15349
- function makeTmuxRunner(tmuxBin) {
15350
- return {
15351
- capture(socket, session) {
15352
- try {
15353
- return execFileSync(tmuxBin, ["-L", socket, "capture-pane", "-p", "-t", session, "-S", "-200"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
15354
- } catch {
15355
- return null;
15356
- }
15357
- },
15358
- send(socket, session, args) {
15359
- const [subcmd, ...rest] = args;
15360
- const flagEnd = rest.findIndex((a) => !a.startsWith("-"));
15361
- const flagsBeforeKeys = flagEnd === -1 ? rest : rest.slice(0, flagEnd);
15362
- const keys = flagEnd === -1 ? [] : rest.slice(flagEnd);
15363
- execFileSync(tmuxBin, ["-L", socket, subcmd, ...flagsBeforeKeys, "-t", session, ...keys], { stdio: ["pipe", "pipe", "pipe"] });
15364
- },
15365
- hasSession(socket, session) {
15366
- try {
15367
- execFileSync(tmuxBin, ["-L", socket, "has-session", "-t", session], {
15368
- stdio: ["pipe", "pipe", "pipe"]
15369
- });
15370
- return true;
15371
- } catch {
15372
- return false;
15373
- }
15374
- }
15375
- };
15376
- }
15377
- function sleep(ms) {
15378
- return new Promise((res) => setTimeout(res, ms));
15379
- }
15380
- function isTuiChromeLine(line) {
15381
- const t = line.trim();
15382
- if (t.length === 0)
15383
- return false;
15384
- if (/^[\s\-_\u2502\u2500\u256d\u256e\u2570\u256f\u250c\u2510\u2514\u2518|]+$/.test(t))
15385
- return true;
15386
- if (/^[\u256d\u256e\u2570\u256f\u2502\s]*[>)\u276f][\u256d\u256e\u2570\u256f\u2502\s]*$/.test(t))
15387
- return true;
15388
- if (/accept edits on/i.test(t))
15389
- return true;
15390
- if (/shift\+tab to cycle/i.test(t))
15391
- return true;
15392
- if (/for agents/i.test(t))
15393
- return true;
15394
- if (/\? for shortcuts/i.test(t))
15395
- return true;
15396
- if (/bypassing permissions/i.test(t))
15397
- return true;
15398
- if (t === "copy")
15399
- return true;
15400
- return false;
15401
- }
15402
- function diffPane(before, after, command) {
15403
- if (command) {
15404
- const escaped = command.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15405
- const lines = after.split(`
15406
- `);
15407
- let anchorIdx = -1;
15408
- for (let i = lines.length - 1;i >= 0; i--) {
15409
- if (/[\u276f>]\s+/.test(lines[i]) && lines[i].includes(escaped)) {
15410
- anchorIdx = i;
15411
- break;
15412
- }
15413
- }
15414
- if (anchorIdx >= 0) {
15415
- const tail = lines.slice(anchorIdx + 1);
15416
- const trimmed = [];
15417
- for (const raw of tail) {
15418
- const line = raw.trimEnd();
15419
- if (line.length === 0 && trimmed.length === 0)
15420
- continue;
15421
- if (isTuiChromeLine(line))
15422
- continue;
15423
- trimmed.push(line);
15424
- }
15425
- while (trimmed.length > 0 && trimmed[trimmed.length - 1].length === 0) {
15426
- trimmed.pop();
15427
- }
15428
- const affordances = /^(esc to cancel|press any key|\u21b5 select)/i;
15429
- while (trimmed.length > 0 && affordances.test(trimmed[trimmed.length - 1].trim())) {
15430
- trimmed.pop();
15431
- }
15432
- if (trimmed.length > 0) {
15433
- return { output: trimmed.join(`
15434
- `), anchored: true };
15435
- }
15436
- }
15437
- }
15438
- const beforeSet = new Set(before.split(`
15439
- `).map((l) => l.trimEnd()));
15440
- const newLines = [];
15441
- for (const raw of after.split(`
15442
- `)) {
15443
- const line = raw.trimEnd();
15444
- if (line.length === 0)
15445
- continue;
15446
- if (beforeSet.has(line))
15447
- continue;
15448
- if (isTuiChromeLine(line))
15449
- continue;
15450
- newLines.push(line);
15451
- }
15452
- return { output: newLines.join(`
15453
- `), anchored: false };
15454
- }
15455
- async function injectSlashCommand(agentName, command, opts = {}) {
15456
- validateInjectCommand(command);
15457
- const tmuxBin = opts.tmuxBin ?? "tmux";
15458
- const socket = opts.socketName ?? defaultSocketName(agentName);
15459
- const session = opts.sessionName ?? agentName;
15460
- const settleMs = opts.settleMs ?? 2000;
15461
- const signalMode = !!(opts.successPattern || opts.errorPattern);
15462
- const timeoutMs = opts.timeoutMs ?? (signalMode ? 8000 : 5000);
15463
- return withPaneLock(`${socket}:${session}`, () => injectSlashCommandWith(makeTmuxRunner(tmuxBin), {
15464
- socket,
15465
- session,
15466
- command: command.trim(),
15467
- settleMs,
15468
- timeoutMs,
15469
- precondition: opts.precondition,
15470
- successPattern: opts.successPattern,
15471
- errorPattern: opts.errorPattern,
15472
- settleBeforeSendMs: opts.settleBeforeSendMs
15473
- }));
15474
- }
15475
- async function injectSlashCommandWith(runner, args) {
15476
- const {
15477
- socket,
15478
- session,
15479
- command,
15480
- settleMs,
15481
- timeoutMs,
15482
- precondition,
15483
- successPattern,
15484
- errorPattern,
15485
- settleBeforeSendMs
15486
- } = args;
15487
- let bareVerb;
15488
- try {
15489
- bareVerb = validateInjectCommand(command);
15490
- } catch (err) {
15491
- if (err instanceof InjectError) {
15492
- return {
15493
- outcome: "failed",
15494
- output: "",
15495
- truncated: false,
15496
- command: command.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? "",
15497
- meta: null,
15498
- errorCode: err.code,
15499
- errorMessage: err.message
15500
- };
15501
- }
15502
- throw err;
15503
- }
15504
- const meta = INJECT_COMMANDS.get(bareVerb) ?? null;
15505
- if (!runner.hasSession(socket, session)) {
15506
- return {
15507
- outcome: "failed",
15508
- output: "",
15509
- truncated: false,
15510
- command: bareVerb,
15511
- meta,
15512
- errorCode: "session_missing",
15513
- errorMessage: `tmux session "${session}" on socket "${socket}" not found. ` + `Is the agent running under the tmux supervisor (the default)? ` + `If experimental.legacy_pty=true is set, inject is unsupported.`
15514
- };
15515
- }
15516
- if (precondition && !precondition()) {
15517
- return {
15518
- outcome: "skipped",
15519
- output: "",
15520
- truncated: false,
15521
- command: bareVerb,
15522
- meta,
15523
- errorCode: "precondition_failed",
15524
- errorMessage: "inject precondition returned false at write time; send aborted " + "(no keys sent)."
15525
- };
15526
- }
15527
- if (settleBeforeSendMs && settleBeforeSendMs > 0) {
15528
- const settleStart = Date.now();
15529
- let prevSettle = runner.capture(socket, session) ?? "";
15530
- while (Date.now() - settleStart < settleBeforeSendMs) {
15531
- await sleep(POLL_INTERVAL_MS);
15532
- const cur = runner.capture(socket, session) ?? "";
15533
- if (cur === prevSettle)
15534
- break;
15535
- prevSettle = cur;
15536
- }
15537
- }
15538
- const before = runner.capture(socket, session) ?? "";
15539
- try {
15540
- runner.send(socket, session, ["send-keys", "-l", command]);
15541
- runner.send(socket, session, ["send-keys", "Enter"]);
15542
- } catch (err) {
15543
- return {
15544
- outcome: "failed",
15545
- output: "",
15546
- truncated: false,
15547
- command: bareVerb,
15548
- meta,
15549
- errorCode: "tmux_failed",
15550
- errorMessage: `tmux send-keys failed: ${err instanceof Error ? err.message : String(err)}`
15551
- };
15552
- }
15553
- const start = Date.now();
15554
- let last = before;
15555
- let stableSince = null;
15556
- const signalMode = !!(successPattern || errorPattern);
15557
- while (Date.now() - start < timeoutMs) {
15558
- await sleep(POLL_INTERVAL_MS);
15559
- const cur = runner.capture(socket, session) ?? "";
15560
- if (signalMode) {
15561
- last = cur;
15562
- if (cur !== before) {
15563
- const { output: region } = diffPane(before, cur, command);
15564
- const regionLines = region.split(`
15565
- `).map((l) => l.trim());
15566
- if (errorPattern && regionLines.some((l) => errorPattern.test(l)))
15567
- break;
15568
- if (successPattern && regionLines.some((l) => successPattern.test(l)))
15569
- break;
15570
- }
15571
- continue;
15572
- }
15573
- if (cur === last && cur !== before) {
15574
- if (stableSince === null) {
15575
- stableSince = Date.now();
15576
- } else if (Date.now() - stableSince >= POLL_INTERVAL_MS) {
15577
- last = cur;
15578
- break;
15579
- }
15580
- } else {
15581
- stableSince = null;
15582
- }
15583
- last = cur;
15584
- if (Date.now() - start >= settleMs && cur !== before) {
15585
- break;
15586
- }
15587
- }
15588
- const { output: rawOutput, anchored } = diffPane(before, last, command);
15589
- let output = rawOutput;
15590
- let truncated = false;
15591
- const bytes = Buffer.byteLength(output, "utf-8");
15592
- if (bytes > OUTPUT_BYTE_CAP) {
15593
- while (Buffer.byteLength(output, "utf-8") > OUTPUT_BYTE_CAP) {
15594
- output = output.slice(Math.floor(output.length * 0.1) + 1);
15595
- }
15596
- truncated = true;
15597
- }
15598
- if (meta?.dialog) {
15599
- try {
15600
- runner.send(socket, session, ["send-keys", "Escape"]);
15601
- } catch {}
15602
- }
15603
- if (output.trim().length === 0) {
15604
- return {
15605
- outcome: "ok_no_output",
15606
- output: "",
15607
- truncated: false,
15608
- command: bareVerb,
15609
- meta,
15610
- ...anchored ? {} : { diagnostic: "anchor_missing" }
15611
- };
15612
- }
15613
- return {
15614
- outcome: "ok",
15615
- output,
15616
- truncated,
15617
- command: bareVerb,
15618
- meta,
15619
- ...truncated ? { diagnostic: "truncated_output" } : {}
15620
- };
15621
- }
15622
- var INJECT_COMMANDS, INJECT_ALLOWLIST, INJECT_BLOCKED, INJECT_BLOCKLIST, InjectError, POLL_INTERVAL_MS = 150, OUTPUT_BYTE_CAP = 3000;
15623
- var init_inject = __esm(() => {
15624
- init_pane_lock();
15625
- INJECT_COMMANDS = new Map([
15626
- ["/cost", { description: "Show session cost", expectsOutput: true, dialog: true, argsAllowed: false }],
15627
- ["/status", { description: "Show session status", expectsOutput: true, dialog: true, argsAllowed: false }],
15628
- ["/usage", { description: "Show plan quota", expectsOutput: true, dialog: true, argsAllowed: false }],
15629
- ["/hooks", { description: "List configured hooks", expectsOutput: true, dialog: true, argsAllowed: false }],
15630
- ["/memory", { description: "Open memory picker", expectsOutput: true, dialog: true, argsAllowed: false }],
15631
- ["/help", { description: "Show help / command discovery", expectsOutput: true, dialog: true, argsAllowed: false }],
15632
- ["/context", { description: "Show context window usage", expectsOutput: true, argsAllowed: false }],
15633
- ["/release-notes", { description: "Show release-notes version list", expectsOutput: true, dialog: true, argsAllowed: false }],
15634
- ["/model", { description: "Open model picker", expectsOutput: true, argsAllowed: true }],
15635
- [
15636
- "/clear",
15637
- {
15638
- description: "Clear session screen",
15639
- expectsOutput: false,
15640
- silentNote: "context cleared \u2014 fresh slate",
15641
- argsAllowed: false
15642
- }
15643
- ],
15644
- [
15645
- "/compact",
15646
- {
15647
- description: "Compact conversation history",
15648
- expectsOutput: false,
15649
- silentNote: "compaction runs silently",
15650
- argsAllowed: false
15651
- }
15652
- ]
15653
- ]);
15654
- INJECT_ALLOWLIST = new Set(INJECT_COMMANDS.keys());
15655
- INJECT_BLOCKED = new Map([
15656
- ["/login", { reason: "would mutate auth state" }],
15657
- ["/logout", { reason: "would terminate the agent's auth session" }],
15658
- ["/exit", { reason: "would kill the agent process" }],
15659
- ["/quit", { reason: "would kill the agent process" }],
15660
- ["/upgrade", { reason: "mutates the Claude Code installation" }],
15661
- ["/init", { reason: "generates/overwrites CLAUDE.md and runs a model turn" }],
15662
- ["/mcp", { reason: "opens an interactive MCP server management dialog" }],
15663
- ["/permissions", { reason: "opens an interactive permissions editor that mutates tool policy" }],
15664
- ["/install-github-app", { reason: "runs a network/OAuth install flow" }],
15665
- ["/add-dir", { reason: "mutates the session's working-directory set" }],
15666
- ["/terminal-setup", { reason: "mutates terminal keybinding configuration" }],
15667
- ["/privacy-settings", { reason: "opens an interactive privacy-settings dialog" }],
15668
- ["/bug", { reason: "submits a bug report over the network" }],
15669
- [
15670
- "/effort",
15671
- {
15672
- reason: "leaves a blocking confirmation modal open and wedges the pane; use the /effort command (it drives the modal), not raw inject"
15673
- }
15674
- ]
15675
- ]);
15676
- INJECT_BLOCKLIST = new Set(INJECT_BLOCKED.keys());
15677
- InjectError = class InjectError extends Error {
15678
- code;
15679
- constructor(code, message) {
15680
- super(message);
15681
- this.name = "InjectError";
15682
- this.code = code;
15683
- }
15684
- };
15685
- });
15686
-
15687
- // src/agents/model-picker.ts
15688
- var init_model_picker = __esm(() => {
15689
- init_inject();
15690
- init_pane_lock();
15691
- });
15692
-
15693
15268
  // telegram-plugin/gateway/model-command.ts
15694
15269
  function isClaudeModel(name) {
15695
15270
  const lower = name.toLowerCase();
@@ -15699,7 +15274,6 @@ function isClaudeModel(name) {
15699
15274
  }
15700
15275
  var MODEL_ALIASES;
15701
15276
  var init_model_command = __esm(() => {
15702
- init_model_picker();
15703
15277
  MODEL_ALIASES = ["opus", "sonnet", "haiku", "fable", "default"];
15704
15278
  });
15705
15279
 
@@ -15771,7 +15345,7 @@ var init_atomic = __esm(() => {
15771
15345
  });
15772
15346
 
15773
15347
  // src/setup/hindsight.ts
15774
- import { execFileSync as execFileSync2 } from "node:child_process";
15348
+ import { execFileSync } from "node:child_process";
15775
15349
  import { createServer } from "node:net";
15776
15350
  function hindsightImageRef(tag) {
15777
15351
  const normalized = normalizeHindsightVersionTag(tag);
@@ -15806,7 +15380,7 @@ function describePortHolder(port) {
15806
15380
  ];
15807
15381
  for (const [cmd, args] of probes) {
15808
15382
  try {
15809
- const out = execFileSync2(cmd, args, {
15383
+ const out = execFileSync(cmd, args, {
15810
15384
  stdio: "pipe",
15811
15385
  encoding: "utf-8"
15812
15386
  }).trim();
@@ -15863,7 +15437,7 @@ async function pickHindsightPorts() {
15863
15437
  }
15864
15438
  function isDockerAvailable() {
15865
15439
  try {
15866
- execFileSync2("docker", ["--version"], { stdio: "pipe" });
15440
+ execFileSync("docker", ["--version"], { stdio: "pipe" });
15867
15441
  return true;
15868
15442
  } catch {
15869
15443
  return false;
@@ -15871,7 +15445,7 @@ function isDockerAvailable() {
15871
15445
  }
15872
15446
  function isHindsightRunning() {
15873
15447
  try {
15874
- const output = execFileSync2("docker", ["ps", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
15448
+ const output = execFileSync("docker", ["ps", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
15875
15449
  return output.trim().length > 0;
15876
15450
  } catch {
15877
15451
  return false;
@@ -15879,7 +15453,7 @@ function isHindsightRunning() {
15879
15453
  }
15880
15454
  function isHindsightContainerExists() {
15881
15455
  try {
15882
- const output = execFileSync2("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Names}}"], { stdio: "pipe", encoding: "utf-8" });
15456
+ const output = execFileSync("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Names}}"], { stdio: "pipe", encoding: "utf-8" });
15883
15457
  return output.trim().length > 0;
15884
15458
  } catch {
15885
15459
  return false;
@@ -15987,7 +15561,7 @@ x-litellm-tags: service:hindsight`);
15987
15561
  }
15988
15562
  args.push("-v", "switchroom-hindsight-data:/home/hindsight/.pg0", "-v", "switchroom-hindsight-backups:/backups", "-v", `${HINDSIGHT_BROKER_SOCK_VOLUME}:/run/switchroom/auth-broker`, ...mirrorDir ? ["-v", `${HINDSIGHT_CREDS_MIRROR_VOLUME}:${HINDSIGHT_CRED_DIR}`] : ["--tmpfs", `${HINDSIGHT_CRED_DIR}:rw,mode=0700,uid=${HINDSIGHT_DEFAULT_UID},gid=${HINDSIGHT_DEFAULT_UID}`], ...envArgs, hindsightImageRef(imageTag));
15989
15563
  if (mirrorDir) {
15990
- execFileSync2("docker", [
15564
+ execFileSync("docker", [
15991
15565
  "run",
15992
15566
  "--rm",
15993
15567
  "--user",
@@ -16001,25 +15575,25 @@ x-litellm-tags: service:hindsight`);
16001
15575
  `chown ${HINDSIGHT_DEFAULT_UID}:${HINDSIGHT_DEFAULT_UID} ${HINDSIGHT_CRED_DIR} && chmod 0700 ${HINDSIGHT_CRED_DIR}`
16002
15576
  ], { stdio: "pipe" });
16003
15577
  }
16004
- execFileSync2("docker", args, { stdio: "pipe" });
15578
+ execFileSync("docker", args, { stdio: "pipe" });
16005
15579
  }
16006
15580
  function stopHindsight() {
16007
15581
  try {
16008
- execFileSync2("docker", ["stop", "switchroom-hindsight"], { stdio: "pipe" });
15582
+ execFileSync("docker", ["stop", "switchroom-hindsight"], { stdio: "pipe" });
16009
15583
  } catch {}
16010
15584
  try {
16011
- execFileSync2("docker", ["rm", "switchroom-hindsight"], { stdio: "pipe" });
15585
+ execFileSync("docker", ["rm", "switchroom-hindsight"], { stdio: "pipe" });
16012
15586
  } catch {}
16013
15587
  }
16014
15588
  function pullHindsightImage(imageTag) {
16015
- execFileSync2("docker", ["pull", hindsightImageRef(imageTag)], { stdio: "inherit" });
15589
+ execFileSync("docker", ["pull", hindsightImageRef(imageTag)], { stdio: "inherit" });
16016
15590
  }
16017
15591
  function getRunningHindsightPorts() {
16018
15592
  const CONTAINER_API_PORT = 8888;
16019
15593
  const CONTAINER_UI_PORT = 9999;
16020
15594
  const readPort = (containerPort) => {
16021
15595
  try {
16022
- const out = execFileSync2("docker", ["port", "switchroom-hindsight", `${containerPort}/tcp`], { stdio: "pipe", encoding: "utf-8" });
15596
+ const out = execFileSync("docker", ["port", "switchroom-hindsight", `${containerPort}/tcp`], { stdio: "pipe", encoding: "utf-8" });
16023
15597
  const m = out.split(`
16024
15598
  `).map((l) => l.trim()).find((l) => l.length > 0);
16025
15599
  if (!m)
@@ -16040,7 +15614,7 @@ function getRunningHindsightPorts() {
16040
15614
  function getHindsightPortsFromEnv() {
16041
15615
  let env2;
16042
15616
  try {
16043
- env2 = execFileSync2("docker", ["inspect", "--format", "{{.Config.Env}}", "switchroom-hindsight"], { stdio: "pipe", encoding: "utf-8" });
15617
+ env2 = execFileSync("docker", ["inspect", "--format", "{{.Config.Env}}", "switchroom-hindsight"], { stdio: "pipe", encoding: "utf-8" });
16044
15618
  } catch {
16045
15619
  return null;
16046
15620
  }
@@ -16060,7 +15634,7 @@ function getHindsightPortsFromEnv() {
16060
15634
  }
16061
15635
  function getHindsightStatus() {
16062
15636
  try {
16063
- const output = execFileSync2("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
15637
+ const output = execFileSync("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
16064
15638
  const status = output.trim();
16065
15639
  return status.length > 0 ? status : null;
16066
15640
  } catch {
@@ -16899,7 +16473,7 @@ function parseSocketIdentity(socketPath, spec) {
16899
16473
  }
16900
16474
 
16901
16475
  // src/vault/broker/peercred.ts
16902
- import { execFileSync as execFileSync3 } from "node:child_process";
16476
+ import { execFileSync as execFileSync2 } from "node:child_process";
16903
16477
  import { readFileSync as readFileSync4, readlinkSync, fstatSync } from "node:fs";
16904
16478
  function socketPathToIdentity(socketPath) {
16905
16479
  return parseSocketIdentity(socketPath, {
@@ -17072,7 +16646,7 @@ function identify(socketPath, socket, execFileSyncOverride) {
17072
16646
  if (process.platform !== "linux") {
17073
16647
  return null;
17074
16648
  }
17075
- const runner = execFileSyncOverride ?? execFileSync3;
16649
+ const runner = execFileSyncOverride ?? execFileSync2;
17076
16650
  let pid = null;
17077
16651
  if (socket !== undefined) {
17078
16652
  const fd = fdFromSocket(socket);
@@ -17165,7 +16739,7 @@ var init_agent_uid = __esm(() => {
17165
16739
  });
17166
16740
 
17167
16741
  // src/agents/agent-owned-tree.ts
17168
- import { execFileSync as execFileSync4 } from "node:child_process";
16742
+ import { execFileSync as execFileSync3 } from "node:child_process";
17169
16743
  import { existsSync as existsSync5, lstatSync, statSync as statSync3 } from "node:fs";
17170
16744
  function alignAgentTreeOwnershipIfRoot(name, agentDir) {
17171
16745
  if (ownershipRuntime.geteuid() !== 0)
@@ -17206,7 +16780,7 @@ var init_agent_owned_tree = __esm(() => {
17206
16780
  ownershipRuntime = {
17207
16781
  geteuid: () => process.geteuid?.(),
17208
16782
  chownTree: (uid, gid, rootDir) => {
17209
- execFileSync4("chown", ["-h", "-R", `${uid}:${gid}`, rootDir], {
16783
+ execFileSync3("chown", ["-h", "-R", `${uid}:${gid}`, rootDir], {
17210
16784
  stdio: ["ignore", "ignore", "pipe"]
17211
16785
  });
17212
16786
  }
@@ -24101,6 +23675,17 @@ var init_reconcile_default_skills = __esm(() => {
24101
23675
  });
24102
23676
 
24103
23677
  // src/agents/sub-agent-telegram-prompt.ts
23678
+ function buildSubAgentLocalTimeLine(tz) {
23679
+ return `
23680
+
23681
+ ## Local time
23682
+
23683
+ This agent's configured timezone is **${tz}**, and the container clock is ALREADY set to it (the \`TZ\` env is wired at apply time). So the system clock is local time, not UTC: \`date\` in a shell and \`Date.now()\` in code both yield correct **local** time in \`${tz}\`. When you report or reason about the current time, "how long ago" something was, or scheduling, treat the local wall clock as "now" \u2014 never assume or emit UTC. If you need a fresh timestamp, read it live (e.g. run \`date '+%A %Y-%m-%d %I:%M %p %Z'\`) rather than guessing.
23684
+ `;
23685
+ }
23686
+ function applySubAgentLocalTimeGuidance(body, tz) {
23687
+ return body + buildSubAgentLocalTimeLine(tz);
23688
+ }
24104
23689
  function shouldAppendTelegramProgressGuidance(args) {
24105
23690
  return args.telegramEnabled && args.defaultChatId != null && args.defaultChatId.length > 0;
24106
23691
  }
@@ -25984,7 +25569,7 @@ var init_onboarding = __esm(() => {
25984
25569
  });
25985
25570
 
25986
25571
  // src/repos/bare-clone.ts
25987
- import { execFileSync as execFileSync5 } from "node:child_process";
25572
+ import { execFileSync as execFileSync4 } from "node:child_process";
25988
25573
  import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "node:fs";
25989
25574
  import { resolve as resolve9 } from "node:path";
25990
25575
  function bareClonePath(slug) {
@@ -25997,7 +25582,7 @@ function ensureBareClone(slug, url) {
25997
25582
  if (!existsSync14(clonePath)) {
25998
25583
  process.stderr.write(`[switchroom] repo "${slug}": cloning ${url} \u2026
25999
25584
  `);
26000
- execFileSync5("git", ["clone", "--bare", url, clonePath], {
25585
+ execFileSync4("git", ["clone", "--bare", url, clonePath], {
26001
25586
  stdio: ["ignore", "pipe", "pipe"]
26002
25587
  });
26003
25588
  process.stderr.write(`[switchroom] repo "${slug}": bare clone ready at ${clonePath}
@@ -26006,7 +25591,7 @@ function ensureBareClone(slug, url) {
26006
25591
  process.stderr.write(`[switchroom] repo "${slug}": fetching ${clonePath} \u2026
26007
25592
  `);
26008
25593
  try {
26009
- execFileSync5("git", ["fetch", "--all"], {
25594
+ execFileSync4("git", ["fetch", "--all"], {
26010
25595
  cwd: clonePath,
26011
25596
  stdio: ["ignore", "pipe", "pipe"]
26012
25597
  });
@@ -26023,7 +25608,7 @@ var init_bare_clone = __esm(() => {
26023
25608
  });
26024
25609
 
26025
25610
  // src/repos/agent-worktree.ts
26026
- import { execFileSync as execFileSync6 } from "node:child_process";
25611
+ import { execFileSync as execFileSync5 } from "node:child_process";
26027
25612
  import { existsSync as existsSync15, mkdirSync as mkdirSync9, readdirSync as readdirSync5 } from "node:fs";
26028
25613
  import { join as join9, resolve as resolve10 } from "node:path";
26029
25614
  function agentBranchName(agentName) {
@@ -26034,7 +25619,7 @@ function agentWorktreePath(agentDir, slug) {
26034
25619
  }
26035
25620
  function isWorktreeDirty(worktreePath) {
26036
25621
  try {
26037
- const out = execFileSync6("git", ["status", "--porcelain"], {
25622
+ const out = execFileSync5("git", ["status", "--porcelain"], {
26038
25623
  cwd: worktreePath,
26039
25624
  stdio: ["ignore", "pipe", "pipe"],
26040
25625
  encoding: "utf-8"
@@ -26046,7 +25631,7 @@ function isWorktreeDirty(worktreePath) {
26046
25631
  }
26047
25632
  function headShortSha(worktreePath) {
26048
25633
  try {
26049
- return execFileSync6("git", ["rev-parse", "--short", "HEAD"], {
25634
+ return execFileSync5("git", ["rev-parse", "--short", "HEAD"], {
26050
25635
  cwd: worktreePath,
26051
25636
  stdio: ["ignore", "pipe", "pipe"],
26052
25637
  encoding: "utf-8"
@@ -26057,7 +25642,7 @@ function headShortSha(worktreePath) {
26057
25642
  }
26058
25643
  function branchExistsInBare(bareClonePath2, branchName) {
26059
25644
  try {
26060
- execFileSync6("git", ["rev-parse", "--verify", `refs/heads/${branchName}`], {
25645
+ execFileSync5("git", ["rev-parse", "--verify", `refs/heads/${branchName}`], {
26061
25646
  cwd: bareClonePath2,
26062
25647
  stdio: ["ignore", "pipe", "pipe"]
26063
25648
  });
@@ -26068,7 +25653,7 @@ function branchExistsInBare(bareClonePath2, branchName) {
26068
25653
  }
26069
25654
  function resolveDefaultBranch(bareClonePath2) {
26070
25655
  try {
26071
- const out = execFileSync6("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
25656
+ const out = execFileSync5("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
26072
25657
  cwd: bareClonePath2,
26073
25658
  stdio: ["ignore", "pipe", "pipe"],
26074
25659
  encoding: "utf-8"
@@ -26086,12 +25671,12 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26086
25671
  if (!existsSync15(worktreePath)) {
26087
25672
  mkdirSync9(join9(agentDir, "work"), { recursive: true });
26088
25673
  if (branchExistsInBare(bareClonePath2, branch)) {
26089
- execFileSync6("git", ["worktree", "add", worktreePath, branch], {
25674
+ execFileSync5("git", ["worktree", "add", worktreePath, branch], {
26090
25675
  cwd: bareClonePath2,
26091
25676
  stdio: ["ignore", "pipe", "pipe"]
26092
25677
  });
26093
25678
  } else {
26094
- execFileSync6("git", [
25679
+ execFileSync5("git", [
26095
25680
  "worktree",
26096
25681
  "add",
26097
25682
  worktreePath,
@@ -26119,11 +25704,11 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26119
25704
  };
26120
25705
  }
26121
25706
  try {
26122
- execFileSync6("git", ["fetch", "origin"], {
25707
+ execFileSync5("git", ["fetch", "origin"], {
26123
25708
  cwd: worktreePath,
26124
25709
  stdio: ["ignore", "pipe", "pipe"]
26125
25710
  });
26126
- execFileSync6("git", ["merge", "--ff-only", `origin/${defaultBranch}`], {
25711
+ execFileSync5("git", ["merge", "--ff-only", `origin/${defaultBranch}`], {
26127
25712
  cwd: worktreePath,
26128
25713
  stdio: ["ignore", "pipe", "pipe"]
26129
25714
  });
@@ -26141,7 +25726,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26141
25726
  const branch = agentBranchName(agentName);
26142
25727
  if (existsSync15(worktreePath)) {
26143
25728
  try {
26144
- execFileSync6("git", ["worktree", "remove", "--force", worktreePath], {
25729
+ execFileSync5("git", ["worktree", "remove", "--force", worktreePath], {
26145
25730
  cwd: bareClonePath2,
26146
25731
  stdio: ["ignore", "pipe", "pipe"]
26147
25732
  });
@@ -26153,7 +25738,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26153
25738
  }
26154
25739
  if (branchExistsInBare(bareClonePath2, branch)) {
26155
25740
  try {
26156
- execFileSync6("git", ["branch", "-D", branch], {
25741
+ execFileSync5("git", ["branch", "-D", branch], {
26157
25742
  cwd: bareClonePath2,
26158
25743
  stdio: ["ignore", "pipe", "pipe"]
26159
25744
  });
@@ -26164,7 +25749,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26164
25749
  }
26165
25750
  }
26166
25751
  try {
26167
- execFileSync6("git", ["worktree", "prune"], {
25752
+ execFileSync5("git", ["worktree", "prune"], {
26168
25753
  cwd: bareClonePath2,
26169
25754
  stdio: ["ignore", "pipe", "pipe"]
26170
25755
  });
@@ -26193,7 +25778,7 @@ import {
26193
25778
  unlinkSync as unlinkSync4
26194
25779
  } from "node:fs";
26195
25780
  import { homedir as homedir5 } from "node:os";
26196
- import { execSync, execFileSync as execFileSync7 } from "node:child_process";
25781
+ import { execSync, execFileSync as execFileSync6 } from "node:child_process";
26197
25782
  import { join as join10, resolve as resolve11 } from "node:path";
26198
25783
  import { createHash as createHash3 } from "node:crypto";
26199
25784
  function prependReplyDiscipline(rendered, context) {
@@ -26327,13 +25912,13 @@ function alignAgentUid(name, agentDir, uid, opts = {}) {
26327
25912
  `));
26328
25913
  }
26329
25914
  try {
26330
- execFileSync7("chown", ["-h", "-R", `${uid}:${uid}`, ...paths], {
25915
+ execFileSync6("chown", ["-h", "-R", `${uid}:${uid}`, ...paths], {
26331
25916
  stdio: ["ignore", "ignore", "pipe"]
26332
25917
  });
26333
25918
  return { chowned: true, paths };
26334
25919
  } catch {}
26335
25920
  try {
26336
- execFileSync7("sudo", ["chown", "-h", "-R", `${uid}:${uid}`, ...paths], {
25921
+ execFileSync6("sudo", ["chown", "-h", "-R", `${uid}:${uid}`, ...paths], {
26337
25922
  stdio: "inherit"
26338
25923
  });
26339
25924
  return { chowned: true, paths };
@@ -27055,7 +26640,7 @@ Thumbs.db
27055
26640
  execSync("git add -A", { cwd: workspaceDir, stdio: "pipe" });
27056
26641
  const userEmail = process.env.GIT_AUTHOR_EMAIL || "switchroom@localhost";
27057
26642
  const userName = process.env.GIT_AUTHOR_NAME || "Switchroom Agent";
27058
- execFileSync7("git", [
26643
+ execFileSync6("git", [
27059
26644
  "-c",
27060
26645
  `user.email=${userEmail}`,
27061
26646
  "-c",
@@ -27780,10 +27365,10 @@ ${v.map((i) => ` - ${i}`).join(`
27780
27365
  }).join(`
27781
27366
  `);
27782
27367
  const rawBody = saDef.prompt ?? `You are the ${saName} sub-agent.`;
27783
- const body = applyTelegramProgressGuidance(rawBody, {
27368
+ const body = applySubAgentLocalTimeGuidance(applyTelegramProgressGuidance(rawBody, {
27784
27369
  telegramEnabled: true,
27785
27370
  defaultChatId: userId
27786
- });
27371
+ }), resolveTimezone(switchroomConfig ?? {}, agentConfig));
27787
27372
  const content = `---
27788
27373
  ${fmLines}
27789
27374
  ---
@@ -28655,10 +28240,10 @@ ${v.map((i) => ` - ${i}`).join(`
28655
28240
  }).join(`
28656
28241
  `);
28657
28242
  const rawBody = saDef.prompt ?? `You are the ${saName} sub-agent.`;
28658
- const body = applyTelegramProgressGuidance(rawBody, {
28243
+ const body = applySubAgentLocalTimeGuidance(applyTelegramProgressGuidance(rawBody, {
28659
28244
  telegramEnabled: true,
28660
28245
  defaultChatId: greetingUserId
28661
- });
28246
+ }), resolveTimezone(switchroomConfig, agentConfig));
28662
28247
  const content = `---
28663
28248
  ${fmLines}
28664
28249
  ---
@@ -30735,7 +30320,7 @@ var init_write_compose = __esm(() => {
30735
30320
  });
30736
30321
 
30737
30322
  // src/agents/tmux.ts
30738
- import { execFileSync as execFileSync8 } from "node:child_process";
30323
+ import { execFileSync as execFileSync7 } from "node:child_process";
30739
30324
  function sendAgentInterrupt(opts) {
30740
30325
  const { agentName } = opts;
30741
30326
  const attempts = typeof opts.attempts === "number" && opts.attempts > 0 ? opts.attempts : 1;
@@ -30745,7 +30330,7 @@ function sendAgentInterrupt(opts) {
30745
30330
  let lastError = null;
30746
30331
  for (let i = 0;i < attempts; i++) {
30747
30332
  try {
30748
- execFileSync8("tmux", args, {
30333
+ execFileSync7("tmux", args, {
30749
30334
  timeout: 3000,
30750
30335
  stdio: ["ignore", "pipe", "pipe"]
30751
30336
  });
@@ -30786,7 +30371,7 @@ var init_compose_env = () => {};
30786
30371
  import { resolve as resolve13 } from "node:path";
30787
30372
  import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync6 } from "node:fs";
30788
30373
  import { homedir as homedir7 } from "node:os";
30789
- import { execFileSync as execFileSync9 } from "node:child_process";
30374
+ import { execFileSync as execFileSync8 } from "node:child_process";
30790
30375
  function resolveSwitchroomHome(explicit) {
30791
30376
  if (explicit && explicit.length > 0)
30792
30377
  return explicit;
@@ -30817,7 +30402,7 @@ function bringUpAgentService(opts) {
30817
30402
  const dockerBin = opts.dockerBin ?? "docker";
30818
30403
  const stdio = opts.stdio ?? "inherit";
30819
30404
  for (const svc of ["vault-broker", "approval-kernel", "switchroom-auth-broker"]) {
30820
- execFileSync9(dockerBin, [
30405
+ execFileSync8(dockerBin, [
30821
30406
  "compose",
30822
30407
  "-f",
30823
30408
  composePath,
@@ -30829,7 +30414,7 @@ function bringUpAgentService(opts) {
30829
30414
  svc
30830
30415
  ], { stdio });
30831
30416
  }
30832
- execFileSync9(dockerBin, [
30417
+ execFileSync8(dockerBin, [
30833
30418
  "compose",
30834
30419
  "-f",
30835
30420
  composePath,
@@ -30848,7 +30433,7 @@ var init_docker_fleet = __esm(() => {
30848
30433
  });
30849
30434
 
30850
30435
  // src/agents/singleton-reconcile.ts
30851
- import { execFileSync as execFileSync10 } from "node:child_process";
30436
+ import { execFileSync as execFileSync9 } from "node:child_process";
30852
30437
  import { readFileSync as readFileSync16 } from "node:fs";
30853
30438
  function resolveSingletonServices(voiceEngine) {
30854
30439
  const engine = voiceEngine ?? loadHostCapabilities()?.voice.engine ?? "cloud";
@@ -30874,7 +30459,7 @@ function readPinnedSingletonImages(composeText, services = SINGLETON_SERVICES) {
30874
30459
  }
30875
30460
  function defaultInspectImage(dockerBin, container) {
30876
30461
  try {
30877
- const out = execFileSync10(dockerBin, ["inspect", "-f", "{{.Config.Image}}", container], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000 }).trim();
30462
+ const out = execFileSync9(dockerBin, ["inspect", "-f", "{{.Config.Image}}", container], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000 }).trim();
30878
30463
  return out.length > 0 ? out : null;
30879
30464
  } catch {
30880
30465
  return null;
@@ -30900,7 +30485,7 @@ function detectSingletonDrift(deps) {
30900
30485
  });
30901
30486
  }
30902
30487
  function defaultRecreate(dockerBin, project, composeFile, service) {
30903
- execFileSync10(dockerBin, ["compose", "-p", project, "-f", composeFile, ...composeEnvFileArgs(composeFile), "up", "-d", "--no-deps", service], { stdio: ["ignore", "pipe", "pipe"], timeout: 120000 });
30488
+ execFileSync9(dockerBin, ["compose", "-p", project, "-f", composeFile, ...composeEnvFileArgs(composeFile), "up", "-d", "--no-deps", service], { stdio: ["ignore", "pipe", "pipe"], timeout: 120000 });
30904
30489
  }
30905
30490
  function reconcileSingletons(deps) {
30906
30491
  const dockerBin = deps.dockerBin ?? "docker";
@@ -30943,7 +30528,7 @@ var init_singleton_reconcile = __esm(() => {
30943
30528
  });
30944
30529
 
30945
30530
  // src/agents/lifecycle.ts
30946
- import { execFileSync as execFileSync11, spawn, spawnSync } from "node:child_process";
30531
+ import { execFileSync as execFileSync10, spawn, spawnSync } from "node:child_process";
30947
30532
  import { existsSync as existsSync21, mkdirSync as mkdirSync14, writeFileSync as writeFileSync7, renameSync as renameSync5, readFileSync as readFileSync17 } from "node:fs";
30948
30533
  import { resolve as resolve14, join as join14 } from "node:path";
30949
30534
  import { connect } from "node:net";
@@ -30974,7 +30559,7 @@ function buildCliRestartReason(opts) {
30974
30559
  if (!buildCommit)
30975
30560
  return "cli: restart";
30976
30561
  try {
30977
- const head = execFileSync11("git", ["rev-parse", "HEAD"], {
30562
+ const head = execFileSync10("git", ["rev-parse", "HEAD"], {
30978
30563
  cwd: cwd ?? process.cwd(),
30979
30564
  encoding: "utf-8",
30980
30565
  stdio: ["ignore", "pipe", "ignore"]
@@ -30985,7 +30570,7 @@ function buildCliRestartReason(opts) {
30985
30570
  return "cli: restart";
30986
30571
  let subject = "";
30987
30572
  try {
30988
- subject = execFileSync11("git", ["log", "-1", "--pretty=%s", head], {
30573
+ subject = execFileSync10("git", ["log", "-1", "--pretty=%s", head], {
30989
30574
  cwd: cwd ?? process.cwd(),
30990
30575
  encoding: "utf-8",
30991
30576
  stdio: ["ignore", "pipe", "ignore"]
@@ -31015,7 +30600,7 @@ function reconcileSingletonImages(log) {
31015
30600
  }
31016
30601
  function dockerSync(args) {
31017
30602
  try {
31018
- return execFileSync11("docker", args, {
30603
+ return execFileSync10("docker", args, {
31019
30604
  encoding: "utf-8",
31020
30605
  stdio: ["ignore", "pipe", "pipe"]
31021
30606
  }).trim();
@@ -31404,12 +30989,12 @@ var init_lifecycle = __esm(() => {
31404
30989
  });
31405
30990
 
31406
30991
  // src/auth/pane-ready-probe.ts
31407
- import { execFileSync as execFileSync13 } from "node:child_process";
30992
+ import { execFileSync as execFileSync12 } from "node:child_process";
31408
30993
  function defaultPaneReadyDeps() {
31409
30994
  return {
31410
30995
  capturePane(sessionName) {
31411
30996
  try {
31412
- return execFileSync13("tmux", ["capture-pane", "-p", "-t", sessionName, "-S", "-200"], {
30997
+ return execFileSync12("tmux", ["capture-pane", "-p", "-t", sessionName, "-S", "-200"], {
31413
30998
  encoding: "utf-8",
31414
30999
  stdio: ["pipe", "pipe", "pipe"]
31415
31000
  }).trim();
@@ -31690,7 +31275,7 @@ var init_accounts = __esm(() => {
31690
31275
  });
31691
31276
 
31692
31277
  // src/auth/manager.ts
31693
- import { execFileSync as execFileSync14 } from "node:child_process";
31278
+ import { execFileSync as execFileSync13 } from "node:child_process";
31694
31279
  import {
31695
31280
  readFileSync as readFileSync22,
31696
31281
  readdirSync as readdirSync11,
@@ -31741,14 +31326,14 @@ function authSessionName(name, slot) {
31741
31326
  return `${base}-${slot.replace(/[^a-zA-Z0-9_.-]/g, "-")}`;
31742
31327
  }
31743
31328
  function tmux(args) {
31744
- return execFileSync14("tmux", args, {
31329
+ return execFileSync13("tmux", args, {
31745
31330
  encoding: "utf-8",
31746
31331
  stdio: ["pipe", "pipe", "pipe"]
31747
31332
  }).trim();
31748
31333
  }
31749
31334
  function tmuxSessionExists(sessionName) {
31750
31335
  try {
31751
- execFileSync14("tmux", ["has-session", "-t", sessionName], {
31336
+ execFileSync13("tmux", ["has-session", "-t", sessionName], {
31752
31337
  stdio: ["pipe", "pipe", "pipe"]
31753
31338
  });
31754
31339
  return true;
@@ -32264,6 +31849,425 @@ function readQuarantineMarkerForAgent(agentsDir, name) {
32264
31849
  var QUARANTINE_FILENAME = "quarantine.json";
32265
31850
  var init_quarantine = () => {};
32266
31851
 
31852
+ // src/agents/pane-lock.ts
31853
+ function withPaneLock(key, fn) {
31854
+ const tail = tails.get(key) ?? Promise.resolve();
31855
+ const run = tail.then(() => fn());
31856
+ const next = run.then(() => {}, () => {});
31857
+ tails.set(key, next);
31858
+ next.then(() => {
31859
+ if (tails.get(key) === next)
31860
+ tails.delete(key);
31861
+ });
31862
+ return run;
31863
+ }
31864
+ var tails;
31865
+ var init_pane_lock = __esm(() => {
31866
+ tails = new Map;
31867
+ });
31868
+
31869
+ // src/agents/inject.ts
31870
+ var exports_inject = {};
31871
+ __export(exports_inject, {
31872
+ validateInjectCommand: () => validateInjectCommand,
31873
+ normalizeInjectCommand: () => normalizeInjectCommand,
31874
+ makeTmuxRunner: () => makeTmuxRunner,
31875
+ isTuiChromeLine: () => isTuiChromeLine,
31876
+ injectSlashCommandWith: () => injectSlashCommandWith,
31877
+ injectSlashCommand: () => injectSlashCommand,
31878
+ diffPane: () => diffPane,
31879
+ dedupeInjectQueue: () => dedupeInjectQueue,
31880
+ InjectError: () => InjectError,
31881
+ INJECT_COMMANDS: () => INJECT_COMMANDS,
31882
+ INJECT_BLOCKLIST: () => INJECT_BLOCKLIST,
31883
+ INJECT_BLOCKED: () => INJECT_BLOCKED,
31884
+ INJECT_ALLOWLIST: () => INJECT_ALLOWLIST
31885
+ });
31886
+ import { execFileSync as execFileSync15 } from "node:child_process";
31887
+ function validateInjectCommand(command) {
31888
+ if (typeof command !== "string" || command.trim().length === 0) {
31889
+ throw new InjectError("invalid", "command is empty");
31890
+ }
31891
+ const trimmed = command.trim();
31892
+ if (!trimmed.startsWith("/")) {
31893
+ throw new InjectError("invalid", `command must start with "/": got ${trimmed}`);
31894
+ }
31895
+ const bare = trimmed.split(/\s+/, 1)[0].toLowerCase();
31896
+ const blockedMeta = INJECT_BLOCKED.get(bare);
31897
+ if (blockedMeta) {
31898
+ throw new InjectError("blocked", `${bare} is explicitly blocked from inject (${blockedMeta.reason}).`);
31899
+ }
31900
+ const meta = INJECT_COMMANDS.get(bare);
31901
+ if (!meta) {
31902
+ const allowed = [...INJECT_COMMANDS.keys()].sort().join(", ");
31903
+ throw new InjectError("not_allowed", `${bare} is not in the inject allowlist. Allowed: ${allowed}`);
31904
+ }
31905
+ const hasArgs = trimmed.split(/\s+/).length > 1;
31906
+ if (hasArgs && !meta.argsAllowed) {
31907
+ throw new InjectError("args_not_allowed", `args not permitted for ${bare} \u2014 inject the bare verb only`);
31908
+ }
31909
+ return bare;
31910
+ }
31911
+ function normalizeInjectCommand(command) {
31912
+ return command.trim().replace(/\s+/g, " ").toLowerCase();
31913
+ }
31914
+ function dedupeInjectQueue(commands) {
31915
+ const seen = new Set;
31916
+ const out = [];
31917
+ for (const raw of commands) {
31918
+ if (typeof raw !== "string")
31919
+ continue;
31920
+ const key = normalizeInjectCommand(raw);
31921
+ if (key.length === 0)
31922
+ continue;
31923
+ if (seen.has(key))
31924
+ continue;
31925
+ seen.add(key);
31926
+ out.push(raw.trim());
31927
+ }
31928
+ return out;
31929
+ }
31930
+ function defaultSocketName(agentName) {
31931
+ return `switchroom-${agentName}`;
31932
+ }
31933
+ function makeTmuxRunner(tmuxBin) {
31934
+ return {
31935
+ capture(socket, session) {
31936
+ try {
31937
+ return execFileSync15(tmuxBin, ["-L", socket, "capture-pane", "-p", "-t", session, "-S", "-200"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
31938
+ } catch {
31939
+ return null;
31940
+ }
31941
+ },
31942
+ send(socket, session, args) {
31943
+ const [subcmd, ...rest] = args;
31944
+ const flagEnd = rest.findIndex((a) => !a.startsWith("-"));
31945
+ const flagsBeforeKeys = flagEnd === -1 ? rest : rest.slice(0, flagEnd);
31946
+ const keys = flagEnd === -1 ? [] : rest.slice(flagEnd);
31947
+ execFileSync15(tmuxBin, ["-L", socket, subcmd, ...flagsBeforeKeys, "-t", session, ...keys], { stdio: ["pipe", "pipe", "pipe"] });
31948
+ },
31949
+ hasSession(socket, session) {
31950
+ try {
31951
+ execFileSync15(tmuxBin, ["-L", socket, "has-session", "-t", session], {
31952
+ stdio: ["pipe", "pipe", "pipe"]
31953
+ });
31954
+ return true;
31955
+ } catch {
31956
+ return false;
31957
+ }
31958
+ }
31959
+ };
31960
+ }
31961
+ function sleep2(ms) {
31962
+ return new Promise((res) => setTimeout(res, ms));
31963
+ }
31964
+ function isTuiChromeLine(line) {
31965
+ const t = line.trim();
31966
+ if (t.length === 0)
31967
+ return false;
31968
+ if (/^[\s\-_\u2502\u2500\u256d\u256e\u2570\u256f\u250c\u2510\u2514\u2518|]+$/.test(t))
31969
+ return true;
31970
+ if (/^[\u256d\u256e\u2570\u256f\u2502\s]*[>)\u276f][\u256d\u256e\u2570\u256f\u2502\s]*$/.test(t))
31971
+ return true;
31972
+ if (/accept edits on/i.test(t))
31973
+ return true;
31974
+ if (/shift\+tab to cycle/i.test(t))
31975
+ return true;
31976
+ if (/for agents/i.test(t))
31977
+ return true;
31978
+ if (/\? for shortcuts/i.test(t))
31979
+ return true;
31980
+ if (/bypassing permissions/i.test(t))
31981
+ return true;
31982
+ if (t === "copy")
31983
+ return true;
31984
+ return false;
31985
+ }
31986
+ function diffPane(before, after, command) {
31987
+ if (command) {
31988
+ const escaped = command.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
31989
+ const lines = after.split(`
31990
+ `);
31991
+ let anchorIdx = -1;
31992
+ for (let i = lines.length - 1;i >= 0; i--) {
31993
+ if (/[\u276f>]\s+/.test(lines[i]) && lines[i].includes(escaped)) {
31994
+ anchorIdx = i;
31995
+ break;
31996
+ }
31997
+ }
31998
+ if (anchorIdx >= 0) {
31999
+ const tail = lines.slice(anchorIdx + 1);
32000
+ const trimmed = [];
32001
+ for (const raw of tail) {
32002
+ const line = raw.trimEnd();
32003
+ if (line.length === 0 && trimmed.length === 0)
32004
+ continue;
32005
+ if (isTuiChromeLine(line))
32006
+ continue;
32007
+ trimmed.push(line);
32008
+ }
32009
+ while (trimmed.length > 0 && trimmed[trimmed.length - 1].length === 0) {
32010
+ trimmed.pop();
32011
+ }
32012
+ const affordances = /^(esc to cancel|press any key|\u21b5 select)/i;
32013
+ while (trimmed.length > 0 && affordances.test(trimmed[trimmed.length - 1].trim())) {
32014
+ trimmed.pop();
32015
+ }
32016
+ if (trimmed.length > 0) {
32017
+ return { output: trimmed.join(`
32018
+ `), anchored: true };
32019
+ }
32020
+ }
32021
+ }
32022
+ const beforeSet = new Set(before.split(`
32023
+ `).map((l) => l.trimEnd()));
32024
+ const newLines = [];
32025
+ for (const raw of after.split(`
32026
+ `)) {
32027
+ const line = raw.trimEnd();
32028
+ if (line.length === 0)
32029
+ continue;
32030
+ if (beforeSet.has(line))
32031
+ continue;
32032
+ if (isTuiChromeLine(line))
32033
+ continue;
32034
+ newLines.push(line);
32035
+ }
32036
+ return { output: newLines.join(`
32037
+ `), anchored: false };
32038
+ }
32039
+ async function injectSlashCommand(agentName, command, opts = {}) {
32040
+ validateInjectCommand(command);
32041
+ const tmuxBin = opts.tmuxBin ?? "tmux";
32042
+ const socket = opts.socketName ?? defaultSocketName(agentName);
32043
+ const session = opts.sessionName ?? agentName;
32044
+ const settleMs = opts.settleMs ?? 2000;
32045
+ const signalMode = !!(opts.successPattern || opts.errorPattern);
32046
+ const timeoutMs = opts.timeoutMs ?? (signalMode ? 8000 : 5000);
32047
+ return withPaneLock(`${socket}:${session}`, () => injectSlashCommandWith(makeTmuxRunner(tmuxBin), {
32048
+ socket,
32049
+ session,
32050
+ command: command.trim(),
32051
+ settleMs,
32052
+ timeoutMs,
32053
+ precondition: opts.precondition,
32054
+ successPattern: opts.successPattern,
32055
+ errorPattern: opts.errorPattern,
32056
+ settleBeforeSendMs: opts.settleBeforeSendMs
32057
+ }));
32058
+ }
32059
+ async function injectSlashCommandWith(runner, args) {
32060
+ const {
32061
+ socket,
32062
+ session,
32063
+ command,
32064
+ settleMs,
32065
+ timeoutMs,
32066
+ precondition,
32067
+ successPattern,
32068
+ errorPattern,
32069
+ settleBeforeSendMs
32070
+ } = args;
32071
+ let bareVerb;
32072
+ try {
32073
+ bareVerb = validateInjectCommand(command);
32074
+ } catch (err) {
32075
+ if (err instanceof InjectError) {
32076
+ return {
32077
+ outcome: "failed",
32078
+ output: "",
32079
+ truncated: false,
32080
+ command: command.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? "",
32081
+ meta: null,
32082
+ errorCode: err.code,
32083
+ errorMessage: err.message
32084
+ };
32085
+ }
32086
+ throw err;
32087
+ }
32088
+ const meta = INJECT_COMMANDS.get(bareVerb) ?? null;
32089
+ if (!runner.hasSession(socket, session)) {
32090
+ return {
32091
+ outcome: "failed",
32092
+ output: "",
32093
+ truncated: false,
32094
+ command: bareVerb,
32095
+ meta,
32096
+ errorCode: "session_missing",
32097
+ errorMessage: `tmux session "${session}" on socket "${socket}" not found. ` + `Is the agent running under the tmux supervisor (the default)? ` + `If experimental.legacy_pty=true is set, inject is unsupported.`
32098
+ };
32099
+ }
32100
+ if (precondition && !precondition()) {
32101
+ return {
32102
+ outcome: "skipped",
32103
+ output: "",
32104
+ truncated: false,
32105
+ command: bareVerb,
32106
+ meta,
32107
+ errorCode: "precondition_failed",
32108
+ errorMessage: "inject precondition returned false at write time; send aborted " + "(no keys sent)."
32109
+ };
32110
+ }
32111
+ if (settleBeforeSendMs && settleBeforeSendMs > 0) {
32112
+ const settleStart = Date.now();
32113
+ let prevSettle = runner.capture(socket, session) ?? "";
32114
+ while (Date.now() - settleStart < settleBeforeSendMs) {
32115
+ await sleep2(POLL_INTERVAL_MS);
32116
+ const cur = runner.capture(socket, session) ?? "";
32117
+ if (cur === prevSettle)
32118
+ break;
32119
+ prevSettle = cur;
32120
+ }
32121
+ }
32122
+ const before = runner.capture(socket, session) ?? "";
32123
+ try {
32124
+ runner.send(socket, session, ["send-keys", "-l", command]);
32125
+ runner.send(socket, session, ["send-keys", "Enter"]);
32126
+ } catch (err) {
32127
+ return {
32128
+ outcome: "failed",
32129
+ output: "",
32130
+ truncated: false,
32131
+ command: bareVerb,
32132
+ meta,
32133
+ errorCode: "tmux_failed",
32134
+ errorMessage: `tmux send-keys failed: ${err instanceof Error ? err.message : String(err)}`
32135
+ };
32136
+ }
32137
+ const start = Date.now();
32138
+ let last = before;
32139
+ let stableSince = null;
32140
+ const signalMode = !!(successPattern || errorPattern);
32141
+ while (Date.now() - start < timeoutMs) {
32142
+ await sleep2(POLL_INTERVAL_MS);
32143
+ const cur = runner.capture(socket, session) ?? "";
32144
+ if (signalMode) {
32145
+ last = cur;
32146
+ if (cur !== before) {
32147
+ const { output: region } = diffPane(before, cur, command);
32148
+ const regionLines = region.split(`
32149
+ `).map((l) => l.trim());
32150
+ if (errorPattern && regionLines.some((l) => errorPattern.test(l)))
32151
+ break;
32152
+ if (successPattern && regionLines.some((l) => successPattern.test(l)))
32153
+ break;
32154
+ }
32155
+ continue;
32156
+ }
32157
+ if (cur === last && cur !== before) {
32158
+ if (stableSince === null) {
32159
+ stableSince = Date.now();
32160
+ } else if (Date.now() - stableSince >= POLL_INTERVAL_MS) {
32161
+ last = cur;
32162
+ break;
32163
+ }
32164
+ } else {
32165
+ stableSince = null;
32166
+ }
32167
+ last = cur;
32168
+ if (Date.now() - start >= settleMs && cur !== before) {
32169
+ break;
32170
+ }
32171
+ }
32172
+ const { output: rawOutput, anchored } = diffPane(before, last, command);
32173
+ let output = rawOutput;
32174
+ let truncated = false;
32175
+ const bytes = Buffer.byteLength(output, "utf-8");
32176
+ if (bytes > OUTPUT_BYTE_CAP) {
32177
+ while (Buffer.byteLength(output, "utf-8") > OUTPUT_BYTE_CAP) {
32178
+ output = output.slice(Math.floor(output.length * 0.1) + 1);
32179
+ }
32180
+ truncated = true;
32181
+ }
32182
+ if (meta?.dialog) {
32183
+ try {
32184
+ runner.send(socket, session, ["send-keys", "Escape"]);
32185
+ } catch {}
32186
+ }
32187
+ if (output.trim().length === 0) {
32188
+ return {
32189
+ outcome: "ok_no_output",
32190
+ output: "",
32191
+ truncated: false,
32192
+ command: bareVerb,
32193
+ meta,
32194
+ ...anchored ? {} : { diagnostic: "anchor_missing" }
32195
+ };
32196
+ }
32197
+ return {
32198
+ outcome: "ok",
32199
+ output,
32200
+ truncated,
32201
+ command: bareVerb,
32202
+ meta,
32203
+ ...truncated ? { diagnostic: "truncated_output" } : {}
32204
+ };
32205
+ }
32206
+ var INJECT_COMMANDS, INJECT_ALLOWLIST, INJECT_BLOCKED, INJECT_BLOCKLIST, InjectError, POLL_INTERVAL_MS = 150, OUTPUT_BYTE_CAP = 3000;
32207
+ var init_inject = __esm(() => {
32208
+ init_pane_lock();
32209
+ INJECT_COMMANDS = new Map([
32210
+ ["/cost", { description: "Show session cost", expectsOutput: true, dialog: true, argsAllowed: false }],
32211
+ ["/status", { description: "Show session status", expectsOutput: true, dialog: true, argsAllowed: false }],
32212
+ ["/usage", { description: "Show plan quota", expectsOutput: true, dialog: true, argsAllowed: false }],
32213
+ ["/hooks", { description: "List configured hooks", expectsOutput: true, dialog: true, argsAllowed: false }],
32214
+ ["/memory", { description: "Open memory picker", expectsOutput: true, dialog: true, argsAllowed: false }],
32215
+ ["/help", { description: "Show help / command discovery", expectsOutput: true, dialog: true, argsAllowed: false }],
32216
+ ["/context", { description: "Show context window usage", expectsOutput: true, argsAllowed: false }],
32217
+ ["/release-notes", { description: "Show release-notes version list", expectsOutput: true, dialog: true, argsAllowed: false }],
32218
+ ["/model", { description: "Open model picker", expectsOutput: true, argsAllowed: true }],
32219
+ [
32220
+ "/clear",
32221
+ {
32222
+ description: "Clear session screen",
32223
+ expectsOutput: false,
32224
+ silentNote: "context cleared \u2014 fresh slate",
32225
+ argsAllowed: false
32226
+ }
32227
+ ],
32228
+ [
32229
+ "/compact",
32230
+ {
32231
+ description: "Compact conversation history",
32232
+ expectsOutput: false,
32233
+ silentNote: "compaction runs silently",
32234
+ argsAllowed: false
32235
+ }
32236
+ ]
32237
+ ]);
32238
+ INJECT_ALLOWLIST = new Set(INJECT_COMMANDS.keys());
32239
+ INJECT_BLOCKED = new Map([
32240
+ ["/login", { reason: "would mutate auth state" }],
32241
+ ["/logout", { reason: "would terminate the agent's auth session" }],
32242
+ ["/exit", { reason: "would kill the agent process" }],
32243
+ ["/quit", { reason: "would kill the agent process" }],
32244
+ ["/upgrade", { reason: "mutates the Claude Code installation" }],
32245
+ ["/init", { reason: "generates/overwrites CLAUDE.md and runs a model turn" }],
32246
+ ["/mcp", { reason: "opens an interactive MCP server management dialog" }],
32247
+ ["/permissions", { reason: "opens an interactive permissions editor that mutates tool policy" }],
32248
+ ["/install-github-app", { reason: "runs a network/OAuth install flow" }],
32249
+ ["/add-dir", { reason: "mutates the session's working-directory set" }],
32250
+ ["/terminal-setup", { reason: "mutates terminal keybinding configuration" }],
32251
+ ["/privacy-settings", { reason: "opens an interactive privacy-settings dialog" }],
32252
+ ["/bug", { reason: "submits a bug report over the network" }],
32253
+ [
32254
+ "/effort",
32255
+ {
32256
+ reason: "leaves a blocking confirmation modal open and wedges the pane; use the /effort command (it drives the modal), not raw inject"
32257
+ }
32258
+ ]
32259
+ ]);
32260
+ INJECT_BLOCKLIST = new Set(INJECT_BLOCKED.keys());
32261
+ InjectError = class InjectError extends Error {
32262
+ code;
32263
+ constructor(code, message) {
32264
+ super(message);
32265
+ this.name = "InjectError";
32266
+ this.code = code;
32267
+ }
32268
+ };
32269
+ });
32270
+
32267
32271
  // src/auth/broker/protocol.ts
32268
32272
  function encodeRequest2(req) {
32269
32273
  const line = JSON.stringify(RequestSchema2.parse(req)) + `
@@ -63948,11 +63952,11 @@ function detectInFlight(opts) {
63948
63952
  async function waitUntilIdle(opts) {
63949
63953
  const pollMs = opts.pollMs ?? 2000;
63950
63954
  const clock = opts.now ?? Date.now;
63951
- const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
63955
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
63952
63956
  const deadline = clock() + opts.timeoutMs;
63953
63957
  let last = detectInFlight(opts);
63954
63958
  while (last.busy && clock() < deadline) {
63955
- await sleep2(pollMs);
63959
+ await sleep(pollMs);
63956
63960
  last = detectInFlight(opts);
63957
63961
  }
63958
63962
  return last;
@@ -64108,7 +64112,7 @@ function spinner(message) {
64108
64112
  // src/agents/status.ts
64109
64113
  import { existsSync as existsSync24, readFileSync as readFileSync20, statSync as statSync12 } from "node:fs";
64110
64114
  import { join as join16 } from "node:path";
64111
- import { execFileSync as execFileSync12 } from "node:child_process";
64115
+ import { execFileSync as execFileSync11 } from "node:child_process";
64112
64116
 
64113
64117
  // src/agents/handoff-summarizer.ts
64114
64118
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync8, renameSync as renameSync6, mkdirSync as mkdirSync15, existsSync as existsSync22, statSync as statSync11, readdirSync as readdirSync9 } from "node:fs";
@@ -64693,7 +64697,7 @@ async function buildAgentStatusReport(inputs) {
64693
64697
  async function waitForAgentReady(inputs, options = {}) {
64694
64698
  const timeoutMs = options.timeoutMs ?? 30000;
64695
64699
  const pollIntervalMs = options.pollIntervalMs ?? 750;
64696
- const sleep2 = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
64700
+ const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
64697
64701
  const now = options.now ?? Date.now;
64698
64702
  const startedAt = now();
64699
64703
  const deadline = startedAt + timeoutMs;
@@ -64708,7 +64712,7 @@ async function waitForAgentReady(inputs, options = {}) {
64708
64712
  notReady
64709
64713
  };
64710
64714
  }
64711
- await sleep2(pollIntervalMs);
64715
+ await sleep(pollIntervalMs);
64712
64716
  report = await buildAgentStatusReport(inputs);
64713
64717
  notReady = readinessGaps(report);
64714
64718
  }
@@ -64785,14 +64789,14 @@ function readLastMessages(historyDbPath) {
64785
64789
  };
64786
64790
  }
64787
64791
  try {
64788
- const out = execFileSync12("bun", [
64792
+ const out = execFileSync11("bun", [
64789
64793
  "-e",
64790
64794
  `const { Database } = require("bun:sqlite"); ` + `const db = new Database(${JSON.stringify(historyDbPath)}, { readonly: true }); ` + `const rows = db.prepare("SELECT role, MAX(ts) as ts FROM messages GROUP BY role").all(); ` + `for (const r of rows) console.log(r.role + "|" + r.ts); ` + `db.close();`
64791
64795
  ], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
64792
64796
  return parseSqliteRoleTsOutput(out);
64793
64797
  } catch {}
64794
64798
  try {
64795
- const out = execFileSync12("sqlite3", [
64799
+ const out = execFileSync11("sqlite3", [
64796
64800
  historyDbPath,
64797
64801
  "SELECT role, MAX(ts) FROM messages GROUP BY role;"
64798
64802
  ], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
@@ -64904,7 +64908,7 @@ function escapeRegex(s) {
64904
64908
  }
64905
64909
  function readDockerContainer(containerName2) {
64906
64910
  try {
64907
- const out = execFileSync12("docker", ["inspect", "--format", "{{json .State}}", containerName2], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
64911
+ const out = execFileSync11("docker", ["inspect", "--format", "{{json .State}}", containerName2], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
64908
64912
  const state = JSON.parse(out.trim());
64909
64913
  const active = state.Status === "running" ? "active" : state.Status === "restarting" ? "restarting" : "inactive";
64910
64914
  const pid = typeof state.Pid === "number" && state.Pid > 0 ? state.Pid : null;
@@ -65019,7 +65023,7 @@ async function pollForDmStart(token, timeoutMs = 120000) {
65019
65023
  const response = await fetch(url);
65020
65024
  data = await response.json();
65021
65025
  } catch {
65022
- await sleep2(2000);
65026
+ await sleep(2000);
65023
65027
  continue;
65024
65028
  }
65025
65029
  if (data.ok && Array.isArray(data.result)) {
@@ -65035,11 +65039,11 @@ async function pollForDmStart(token, timeoutMs = 120000) {
65035
65039
  }
65036
65040
  }
65037
65041
  }
65038
- await sleep2(2000);
65042
+ await sleep(2000);
65039
65043
  }
65040
65044
  throw new Error("Timed out waiting for /start DM");
65041
65045
  }
65042
- function sleep2(ms) {
65046
+ function sleep(ms) {
65043
65047
  return new Promise((resolve17) => setTimeout(resolve17, ms));
65044
65048
  }
65045
65049
 
@@ -65183,7 +65187,7 @@ async function completeCreation(name, code, opts = {}) {
65183
65187
  // src/agents/add-orchestrator.ts
65184
65188
  import { resolve as resolve19, join as join19 } from "node:path";
65185
65189
  import { existsSync as existsSync29, rmSync as rmSync8, statSync as statSync16 } from "node:fs";
65186
- import { execFileSync as execFileSync15 } from "node:child_process";
65190
+ import { execFileSync as execFileSync14 } from "node:child_process";
65187
65191
  init_onboarding();
65188
65192
 
65189
65193
  // src/setup/botfather-walkthrough.ts
@@ -65757,7 +65761,7 @@ function pruneBundledSkills(agentDir, keep, scope) {
65757
65761
  }
65758
65762
  function defaultIsUnitActive(unitName) {
65759
65763
  try {
65760
- const out = execFileSync15("systemctl", ["--user", "is-active", unitName], {
65764
+ const out = execFileSync14("systemctl", ["--user", "is-active", unitName], {
65761
65765
  encoding: "utf-8",
65762
65766
  stdio: ["ignore", "pipe", "pipe"]
65763
65767
  }).trim();