@openscout/scout 0.2.57 → 0.2.60

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/main.mjs CHANGED
@@ -464,6 +464,7 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
464
464
  let agentName;
465
465
  let displayName;
466
466
  let harness;
467
+ let model;
467
468
  let requesterId = null;
468
469
  let noInput = false;
469
470
  for (let index = 0;index < parsed.args.length; index += 1) {
@@ -486,6 +487,12 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
486
487
  index = value.nextIndex;
487
488
  continue;
488
489
  }
490
+ if (current === "--model" || current.startsWith("--model=")) {
491
+ const value = parseFlagValue(parsed.args, index, "--model");
492
+ model = value.value;
493
+ index = value.nextIndex;
494
+ continue;
495
+ }
489
496
  if (current === "--as" || current.startsWith("--as=")) {
490
497
  const value = parseFlagValue(parsed.args, index, "--as");
491
498
  requesterId = value.value;
@@ -520,6 +527,7 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
520
527
  agentName,
521
528
  displayName,
522
529
  harness,
530
+ model,
523
531
  requesterId,
524
532
  noInput
525
533
  };
@@ -2277,6 +2285,7 @@ function createBuiltInHarnessCatalog() {
2277
2285
  anyOf: entry.readiness.anyOf ? [...entry.readiness.anyOf] : undefined
2278
2286
  } : undefined,
2279
2287
  launch: entry.launch ? { ...entry.launch, args: [...entry.launch.args] } : undefined,
2288
+ resume: entry.resume ? { ...entry.resume } : undefined,
2280
2289
  resolveEnv: entry.resolveEnv ? [...entry.resolveEnv] : undefined,
2281
2290
  capabilities: [...entry.capabilities],
2282
2291
  metadata: entry.metadata ? { ...entry.metadata } : undefined
@@ -2294,6 +2303,7 @@ function mergeHarnessCatalogEntries(baseEntries, overrides = {}) {
2294
2303
  install: mergeInstall(entry.install, override.install),
2295
2304
  readiness: mergeReadiness(entry.readiness, override.readiness),
2296
2305
  launch: mergeLaunch(entry.launch, override.launch),
2306
+ resume: override.resume && entry.resume ? { ...entry.resume, ...override.resume } : entry.resume,
2297
2307
  tags: override.tags ? [...override.tags] : entry.tags,
2298
2308
  capabilities: override.capabilities ? [...override.capabilities] : entry.capabilities,
2299
2309
  resolveEnv: override.resolveEnv ? [...override.resolveEnv] : entry.resolveEnv,
@@ -2322,6 +2332,7 @@ function mergeHarnessCatalogEntries(baseEntries, overrides = {}) {
2322
2332
  install: mergeInstall(undefined, override.install),
2323
2333
  readiness: mergeReadiness(undefined, override.readiness),
2324
2334
  launch: mergeLaunch(undefined, override.launch),
2335
+ resume: override.resume?.command && override.resume?.sessionFlag ? { command: override.resume.command, sessionFlag: override.resume.sessionFlag, cwdFlag: override.resume.cwdFlag } : undefined,
2325
2336
  resolveEnv: override.resolveEnv ? [...override.resolveEnv] : undefined,
2326
2337
  capabilities: [...override.capabilities],
2327
2338
  metadata: override.metadata ? { ...override.metadata } : undefined
@@ -2474,6 +2485,11 @@ var init_harness_catalog = __esm(() => {
2474
2485
  loginCommand: "claude login",
2475
2486
  notReadyMessage: "Claude is installed but not authenticated yet."
2476
2487
  },
2488
+ resume: {
2489
+ command: "claude",
2490
+ sessionFlag: "--resume",
2491
+ cwdFlag: "--cwd"
2492
+ },
2477
2493
  capabilities: ["chat", "invoke", "deliver", "summarize", "review"]
2478
2494
  },
2479
2495
  {
@@ -2505,6 +2521,11 @@ var init_harness_catalog = __esm(() => {
2505
2521
  loginCommand: "codex login",
2506
2522
  notReadyMessage: "Codex is installed but not authenticated yet."
2507
2523
  },
2524
+ resume: {
2525
+ command: "codex",
2526
+ sessionFlag: "--thread",
2527
+ cwdFlag: "--cwd"
2528
+ },
2508
2529
  capabilities: ["chat", "invoke", "deliver", "review", "execute"]
2509
2530
  }
2510
2531
  ];
@@ -4684,7 +4705,8 @@ function buildManagedAgentShellExports(options) {
4684
4705
  // ../runtime/src/claude-stream-json.ts
4685
4706
  import { randomUUID } from "crypto";
4686
4707
  import { spawn } from "child_process";
4687
- import { appendFile, mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "fs/promises";
4708
+ import { appendFile, mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
4709
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
4688
4710
  import { join as join5 } from "path";
4689
4711
  function resolveClaudeStreamJsonOutput(result, fallbackParts) {
4690
4712
  const trimmedResult = result?.trim();
@@ -4699,19 +4721,57 @@ function sessionKey(options) {
4699
4721
  function errorMessage(error) {
4700
4722
  return error instanceof Error ? error.message : String(error);
4701
4723
  }
4702
- async function readOptionalFile(filePath) {
4724
+ function readSessionCatalogSync(runtimeDirectory) {
4725
+ const catalogPath = join5(runtimeDirectory, SESSION_CATALOG_FILENAME);
4703
4726
  try {
4704
- const raw = await readFile4(filePath, "utf8");
4705
- const trimmed = raw.trim();
4706
- return trimmed || null;
4727
+ if (!existsSync4(catalogPath)) {
4728
+ const legacyPath = join5(runtimeDirectory, "claude-session-id.txt");
4729
+ if (existsSync4(legacyPath)) {
4730
+ const legacyId = readFileSync2(legacyPath, "utf8").trim();
4731
+ if (legacyId) {
4732
+ return {
4733
+ activeSessionId: legacyId,
4734
+ sessions: [{ id: legacyId, startedAt: Date.now(), cwd: "" }]
4735
+ };
4736
+ }
4737
+ }
4738
+ return { activeSessionId: null, sessions: [] };
4739
+ }
4740
+ const raw = readFileSync2(catalogPath, "utf8").trim();
4741
+ if (!raw)
4742
+ return { activeSessionId: null, sessions: [] };
4743
+ const parsed = JSON.parse(raw);
4744
+ return {
4745
+ activeSessionId: parsed.activeSessionId ?? null,
4746
+ sessions: Array.isArray(parsed.sessions) ? parsed.sessions : []
4747
+ };
4707
4748
  } catch {
4708
- return null;
4749
+ return { activeSessionId: null, sessions: [] };
4709
4750
  }
4710
4751
  }
4752
+ async function readSessionCatalog(runtimeDirectory) {
4753
+ return readSessionCatalogSync(runtimeDirectory);
4754
+ }
4755
+ async function writeSessionCatalog(runtimeDirectory, catalog) {
4756
+ const catalogPath = join5(runtimeDirectory, SESSION_CATALOG_FILENAME);
4757
+ await writeFile3(catalogPath, JSON.stringify(catalog, null, 2) + `
4758
+ `);
4759
+ }
4760
+ function catalogRecordSession(catalog, sessionId, cwd) {
4761
+ const now = Date.now();
4762
+ const sessions = catalog.sessions.map((s) => s.id === catalog.activeSessionId && !s.endedAt ? { ...s, endedAt: now } : s);
4763
+ if (!sessions.some((s) => s.id === sessionId)) {
4764
+ sessions.push({ id: sessionId, startedAt: now, cwd });
4765
+ }
4766
+ while (sessions.length > SESSION_CATALOG_MAX_ENTRIES) {
4767
+ sessions.shift();
4768
+ }
4769
+ return { activeSessionId: sessionId, sessions };
4770
+ }
4711
4771
 
4712
4772
  class ClaudeStreamJsonSession {
4713
4773
  options;
4714
- sessionStatePath;
4774
+ catalogDirectory;
4715
4775
  stdoutLogPath;
4716
4776
  stderrLogPath;
4717
4777
  process = null;
@@ -4722,7 +4782,7 @@ class ClaudeStreamJsonSession {
4722
4782
  lastConfigSignature;
4723
4783
  constructor(options) {
4724
4784
  this.options = options;
4725
- this.sessionStatePath = join5(options.runtimeDirectory, "claude-session-id.txt");
4785
+ this.catalogDirectory = options.runtimeDirectory;
4726
4786
  this.stdoutLogPath = join5(options.logsDirectory, "stdout.log");
4727
4787
  this.stderrLogPath = join5(options.logsDirectory, "stderr.log");
4728
4788
  this.lastConfigSignature = this.configSignature(options);
@@ -4828,7 +4888,9 @@ class ClaudeStreamJsonSession {
4828
4888
  }
4829
4889
  if (options.resetSession) {
4830
4890
  this.claudeSessionId = null;
4831
- await rm2(this.sessionStatePath, { force: true });
4891
+ const catalog = await readSessionCatalog(this.catalogDirectory);
4892
+ catalog.activeSessionId = null;
4893
+ await writeSessionCatalog(this.catalogDirectory, catalog);
4832
4894
  }
4833
4895
  }
4834
4896
  configSignature(options) {
@@ -4857,7 +4919,8 @@ class ClaudeStreamJsonSession {
4857
4919
  await mkdir3(this.options.runtimeDirectory, { recursive: true });
4858
4920
  await mkdir3(this.options.logsDirectory, { recursive: true });
4859
4921
  await writeFile3(join5(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
4860
- this.claudeSessionId = await readOptionalFile(this.sessionStatePath);
4922
+ const catalog = await readSessionCatalog(this.catalogDirectory);
4923
+ this.claudeSessionId = catalog.activeSessionId;
4861
4924
  const args = [
4862
4925
  "--verbose",
4863
4926
  "--print",
@@ -4936,8 +4999,11 @@ class ClaudeStreamJsonSession {
4936
4999
  const nextSessionId = event.session_id ?? event.sessionId ?? null;
4937
5000
  if (nextSessionId && nextSessionId !== this.claudeSessionId) {
4938
5001
  this.claudeSessionId = nextSessionId;
4939
- writeFile3(this.sessionStatePath, `${nextSessionId}
4940
- `);
5002
+ (async () => {
5003
+ const catalog = await readSessionCatalog(this.catalogDirectory);
5004
+ const updated = catalogRecordSession(catalog, nextSessionId, this.options.cwd);
5005
+ await writeSessionCatalog(this.catalogDirectory, updated);
5006
+ })();
4941
5007
  }
4942
5008
  return;
4943
5009
  }
@@ -5000,14 +5066,16 @@ async function shutdownClaudeStreamJsonAgent(options, shutdownOptions = {}) {
5000
5066
  const session = sessions.get(key);
5001
5067
  if (!session) {
5002
5068
  if (shutdownOptions.resetSession) {
5003
- await rm2(join5(options.runtimeDirectory, "claude-session-id.txt"), { force: true });
5069
+ const catalog = await readSessionCatalog(options.runtimeDirectory);
5070
+ catalog.activeSessionId = null;
5071
+ await writeSessionCatalog(options.runtimeDirectory, catalog);
5004
5072
  }
5005
5073
  return;
5006
5074
  }
5007
5075
  sessions.delete(key);
5008
5076
  await session.shutdown(shutdownOptions);
5009
5077
  }
5010
- var sessions;
5078
+ var SESSION_CATALOG_FILENAME = "session-catalog.json", SESSION_CATALOG_MAX_ENTRIES = 64, sessions;
5011
5079
  var init_claude_stream_json = __esm(() => {
5012
5080
  sessions = new Map;
5013
5081
  });
@@ -6184,7 +6252,7 @@ class ClaudeCodeHistoryParser {
6184
6252
  var init_history = () => {};
6185
6253
 
6186
6254
  // ../agent-sessions/src/adapters/claude-code.ts
6187
- import { existsSync as existsSync4, readdirSync, statSync as statSync2 } from "fs";
6255
+ import { existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
6188
6256
  import { homedir as homedir4 } from "os";
6189
6257
  import { join as join6 } from "path";
6190
6258
  function resolveClaudeResumeContext(config) {
@@ -6194,7 +6262,7 @@ function resolveClaudeResumeContext(config) {
6194
6262
  return null;
6195
6263
  }
6196
6264
  const projectsRoot = join6(homedir4(), ".claude", "projects");
6197
- if (!existsSync4(projectsRoot)) {
6265
+ if (!existsSync5(projectsRoot)) {
6198
6266
  return null;
6199
6267
  }
6200
6268
  let projectSlugs;
@@ -6205,7 +6273,7 @@ function resolveClaudeResumeContext(config) {
6205
6273
  }
6206
6274
  for (const slug of projectSlugs) {
6207
6275
  const sessionPath = join6(projectsRoot, slug, `${resumeId}.jsonl`);
6208
- if (!existsSync4(sessionPath)) {
6276
+ if (!existsSync5(sessionPath)) {
6209
6277
  continue;
6210
6278
  }
6211
6279
  const cwd = decodeClaudeProjectsSlug2(slug);
@@ -6746,7 +6814,7 @@ Referenced files: ${prompt.files.join(", ")}` });
6746
6814
  });
6747
6815
 
6748
6816
  // ../agent-sessions/src/codex-launch-config.ts
6749
- import { accessSync, constants, existsSync as existsSync5 } from "fs";
6817
+ import { accessSync, constants, existsSync as existsSync6 } from "fs";
6750
6818
  import { homedir as homedir5 } from "os";
6751
6819
  import { basename as basename2, delimiter, dirname as dirname4, join as join7, resolve as resolve5 } from "path";
6752
6820
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -6830,7 +6898,7 @@ function resolveRepoScoutScript(currentDirectory) {
6830
6898
  for (const start of starts) {
6831
6899
  for (const candidate of ancestorChain(start)) {
6832
6900
  const scriptPath = join7(candidate, "apps", "desktop", "bin", "scout.ts");
6833
- if (existsSync5(scriptPath)) {
6901
+ if (existsSync6(scriptPath)) {
6834
6902
  return scriptPath;
6835
6903
  }
6836
6904
  }
@@ -6920,7 +6988,7 @@ function errorMessage2(error) {
6920
6988
  }
6921
6989
  return String(error);
6922
6990
  }
6923
- async function readOptionalFile2(filePath) {
6991
+ async function readOptionalFile(filePath) {
6924
6992
  try {
6925
6993
  const raw = await readFile5(filePath, "utf8");
6926
6994
  const trimmed = raw.trim();
@@ -7267,7 +7335,7 @@ var init_codex = __esm(() => {
7267
7335
  async resumeOrStartThread() {
7268
7336
  const options = this.codexOptions;
7269
7337
  const requestedThreadId = options.threadId?.trim() || null;
7270
- const storedThreadId = requestedThreadId ?? await readOptionalFile2(this.threadIdPath);
7338
+ const storedThreadId = requestedThreadId ?? await readOptionalFile(this.threadIdPath);
7271
7339
  if (storedThreadId) {
7272
7340
  try {
7273
7341
  const resumed = await this.request("thread/resume", {
@@ -9386,8 +9454,107 @@ var init_src = __esm(() => {
9386
9454
 
9387
9455
  // ../runtime/src/codex-app-server.ts
9388
9456
  import { spawn as spawn3 } from "child_process";
9389
- import { access as access3, appendFile as appendFile3, constants as constants3, mkdir as mkdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "fs/promises";
9457
+ import { constants as constants3 } from "fs";
9458
+ import { access as access3, appendFile as appendFile3, mkdir as mkdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "fs/promises";
9390
9459
  import { delimiter as delimiter3, join as join9 } from "path";
9460
+ function normalizeCodexModelValue(value) {
9461
+ const trimmed = value?.trim();
9462
+ return trimmed ? trimmed : null;
9463
+ }
9464
+ function encodeCodexModelConfig(model) {
9465
+ return `model=${JSON.stringify(model)}`;
9466
+ }
9467
+ function parseCodexModelConfig(value) {
9468
+ const trimmed = value?.trim();
9469
+ if (!trimmed) {
9470
+ return null;
9471
+ }
9472
+ const separatorIndex = trimmed.indexOf("=");
9473
+ if (separatorIndex <= 0) {
9474
+ return null;
9475
+ }
9476
+ const key = trimmed.slice(0, separatorIndex).trim();
9477
+ if (key !== "model") {
9478
+ return null;
9479
+ }
9480
+ const rawValue = trimmed.slice(separatorIndex + 1).trim();
9481
+ if (!rawValue) {
9482
+ return null;
9483
+ }
9484
+ if (rawValue.startsWith('"') && rawValue.endsWith('"') || rawValue.startsWith("'") && rawValue.endsWith("'")) {
9485
+ return rawValue.slice(1, -1) || null;
9486
+ }
9487
+ return rawValue;
9488
+ }
9489
+ function normalizeCodexAppServerLaunchArgs(launchArgs) {
9490
+ const args = Array.isArray(launchArgs) ? launchArgs.map((entry) => String(entry).trim()).filter(Boolean) : [];
9491
+ const normalized = [];
9492
+ for (let index = 0;index < args.length; index += 1) {
9493
+ const current = args[index] ?? "";
9494
+ if (current === "--model" || current === "-m") {
9495
+ const model = normalizeCodexModelValue(args[index + 1]);
9496
+ if (model) {
9497
+ normalized.push("-c", encodeCodexModelConfig(model));
9498
+ index += 1;
9499
+ continue;
9500
+ }
9501
+ normalized.push(current);
9502
+ continue;
9503
+ }
9504
+ if (current.startsWith("--model=")) {
9505
+ const model = normalizeCodexModelValue(current.slice("--model=".length));
9506
+ if (model) {
9507
+ normalized.push("-c", encodeCodexModelConfig(model));
9508
+ continue;
9509
+ }
9510
+ }
9511
+ if (current.startsWith("-m=")) {
9512
+ const model = normalizeCodexModelValue(current.slice(3));
9513
+ if (model) {
9514
+ normalized.push("-c", encodeCodexModelConfig(model));
9515
+ continue;
9516
+ }
9517
+ }
9518
+ if (current === "-c" || current === "--config") {
9519
+ const next = args[index + 1];
9520
+ if (typeof next === "string") {
9521
+ const model = parseCodexModelConfig(next);
9522
+ normalized.push(current === "--config" ? "--config" : "-c", model ? encodeCodexModelConfig(model) : next);
9523
+ index += 1;
9524
+ continue;
9525
+ }
9526
+ }
9527
+ if (current.startsWith("--config=")) {
9528
+ const value = current.slice("--config=".length);
9529
+ const model = parseCodexModelConfig(value);
9530
+ normalized.push(model ? `--config=${encodeCodexModelConfig(model)}` : current);
9531
+ continue;
9532
+ }
9533
+ normalized.push(current);
9534
+ }
9535
+ return normalized;
9536
+ }
9537
+ function readCodexAppServerModelFromLaunchArgs(launchArgs) {
9538
+ const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
9539
+ for (let index = 0;index < normalized.length; index += 1) {
9540
+ const current = normalized[index] ?? "";
9541
+ if (current === "-c" || current === "--config") {
9542
+ const model = parseCodexModelConfig(normalized[index + 1]);
9543
+ if (model) {
9544
+ return model;
9545
+ }
9546
+ index += 1;
9547
+ continue;
9548
+ }
9549
+ if (current.startsWith("--config=")) {
9550
+ const model = parseCodexModelConfig(current.slice("--config=".length));
9551
+ if (model) {
9552
+ return model;
9553
+ }
9554
+ }
9555
+ }
9556
+ return null;
9557
+ }
9391
9558
  function sessionKey2(options) {
9392
9559
  return `${options.agentName}:${options.sessionId}`;
9393
9560
  }
@@ -9483,7 +9650,7 @@ function isServerRequest2(message) {
9483
9650
  function isNotification2(message) {
9484
9651
  return Boolean(message && typeof message === "object" && "method" in message && !("id" in message));
9485
9652
  }
9486
- async function readOptionalFile3(filePath) {
9653
+ async function readOptionalFile2(filePath) {
9487
9654
  try {
9488
9655
  const raw = await readFile6(filePath, "utf8");
9489
9656
  const trimmed = raw.trim();
@@ -9496,6 +9663,13 @@ function isMissingCodexRolloutError2(error) {
9496
9663
  const message = errorMessage3(error).toLowerCase();
9497
9664
  return message.includes("no rollout found for thread id");
9498
9665
  }
9666
+ function resolveCodexCompletionGraceMs() {
9667
+ const parsed = Number.parseInt(process.env.OPENSCOUT_CODEX_COMPLETION_GRACE_MS ?? "", 10);
9668
+ if (Number.isFinite(parsed) && parsed >= 0) {
9669
+ return parsed;
9670
+ }
9671
+ return 60000;
9672
+ }
9499
9673
 
9500
9674
  class CodexAppServerSession {
9501
9675
  options;
@@ -9685,7 +9859,7 @@ class CodexAppServerSession {
9685
9859
  systemPrompt: options.systemPrompt,
9686
9860
  threadId: options.threadId ?? null,
9687
9861
  requireExistingThread: options.requireExistingThread === true,
9688
- launchArgs: Array.isArray(options.launchArgs) ? options.launchArgs : []
9862
+ launchArgs: normalizeCodexAppServerLaunchArgs(options.launchArgs)
9689
9863
  });
9690
9864
  }
9691
9865
  async ensureStarted() {
@@ -9707,6 +9881,7 @@ class CodexAppServerSession {
9707
9881
  await mkdir5(this.options.logsDirectory, { recursive: true });
9708
9882
  await writeFile5(join9(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
9709
9883
  const codexExecutable = await resolveCodexExecutable2();
9884
+ const launchArgs = normalizeCodexAppServerLaunchArgs(this.options.launchArgs);
9710
9885
  const env = buildManagedAgentEnvironment({
9711
9886
  agentName: this.options.agentName,
9712
9887
  currentDirectory: this.options.cwd,
@@ -9717,7 +9892,8 @@ class CodexAppServerSession {
9717
9892
  ...buildScoutMcpCodexLaunchArgs({
9718
9893
  currentDirectory: this.options.cwd,
9719
9894
  env
9720
- })
9895
+ }),
9896
+ ...launchArgs
9721
9897
  ], {
9722
9898
  cwd: this.options.cwd,
9723
9899
  env,
@@ -9760,7 +9936,7 @@ class CodexAppServerSession {
9760
9936
  }
9761
9937
  async resumeOrStartThread() {
9762
9938
  const requestedThreadId = this.options.threadId?.trim() || null;
9763
- const storedThreadId = requestedThreadId ?? await readOptionalFile3(this.threadIdPath);
9939
+ const storedThreadId = requestedThreadId ?? await readOptionalFile2(this.threadIdPath);
9764
9940
  if (storedThreadId) {
9765
9941
  try {
9766
9942
  const resumed = await this.request("thread/resume", {
@@ -9815,6 +9991,8 @@ class CodexAppServerSession {
9815
9991
  turnId: "",
9816
9992
  startedAt: Date.now(),
9817
9993
  timer: null,
9994
+ graceTimer: null,
9995
+ timedOutAt: null,
9818
9996
  messageOrder: [],
9819
9997
  messageByItemId: new Map,
9820
9998
  resolve: resolve6,
@@ -9822,20 +10000,43 @@ class CodexAppServerSession {
9822
10000
  watchers: []
9823
10001
  };
9824
10002
  turn.timer = setTimeout(() => {
9825
- this.interrupt().catch(() => {
9826
- return;
9827
- });
9828
- if (this.activeTurn === turn) {
9829
- this.activeTurn = null;
9830
- }
9831
- for (const watcher of this.drainTurnWatchers(turn)) {
9832
- watcher.reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
9833
- }
9834
- reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
10003
+ this.scheduleTurnTimeout(turn, timeoutMs);
9835
10004
  }, timeoutMs);
9836
10005
  this.activeTurn = turn;
9837
10006
  return turn;
9838
10007
  }
10008
+ buildTurnTimeoutError(timeoutMs) {
10009
+ return new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`);
10010
+ }
10011
+ scheduleTurnTimeout(turn, timeoutMs) {
10012
+ if (turn.timedOutAt !== null) {
10013
+ return;
10014
+ }
10015
+ turn.timedOutAt = Date.now();
10016
+ this.interrupt().catch(() => {
10017
+ return;
10018
+ });
10019
+ const graceMs = resolveCodexCompletionGraceMs();
10020
+ if (graceMs <= 0) {
10021
+ this.rejectTimedOutTurn(turn, timeoutMs);
10022
+ return;
10023
+ }
10024
+ turn.graceTimer = setTimeout(() => {
10025
+ this.rejectTimedOutTurn(turn, timeoutMs);
10026
+ }, graceMs);
10027
+ }
10028
+ rejectTimedOutTurn(turn, timeoutMs) {
10029
+ if (this.activeTurn !== turn) {
10030
+ this.clearActiveTurn(turn);
10031
+ return;
10032
+ }
10033
+ this.clearActiveTurn(turn);
10034
+ const error = this.buildTurnTimeoutError(timeoutMs);
10035
+ for (const watcher of this.drainTurnWatchers(turn)) {
10036
+ watcher.reject(error);
10037
+ }
10038
+ turn.reject(error);
10039
+ }
9839
10040
  addTurnWatcher(turn, resolve6, reject, timeoutMs) {
9840
10041
  const watcher = {
9841
10042
  resolve: resolve6,
@@ -9869,6 +10070,9 @@ class CodexAppServerSession {
9869
10070
  if (turn.timer) {
9870
10071
  clearTimeout(turn.timer);
9871
10072
  }
10073
+ if (turn.graceTimer) {
10074
+ clearTimeout(turn.graceTimer);
10075
+ }
9872
10076
  if (this.activeTurn === turn) {
9873
10077
  this.activeTurn = null;
9874
10078
  }
@@ -10191,7 +10395,7 @@ function buildCollaborationContractPrompt(agentId) {
10191
10395
 
10192
10396
  // ../runtime/src/broker-service.ts
10193
10397
  import { spawnSync } from "child_process";
10194
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
10398
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
10195
10399
  import { homedir as homedir7 } from "os";
10196
10400
  import { basename as basename3, dirname as dirname5, join as join10, resolve as resolve6 } from "path";
10197
10401
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -10230,7 +10434,7 @@ function runtimePackageDir() {
10230
10434
  return resolve6(moduleDir, "..");
10231
10435
  }
10232
10436
  function isInstalledRuntimePackageDir(candidate) {
10233
- return existsSync6(join10(candidate, "package.json")) && existsSync6(join10(candidate, "bin", "openscout-runtime.mjs"));
10437
+ return existsSync7(join10(candidate, "package.json")) && existsSync7(join10(candidate, "bin", "openscout-runtime.mjs"));
10234
10438
  }
10235
10439
  function findGlobalRuntimeDir() {
10236
10440
  const candidates = [
@@ -10261,7 +10465,7 @@ function findWorkspaceRuntimeDir(startDir) {
10261
10465
  let current = resolve6(startDir);
10262
10466
  while (true) {
10263
10467
  const candidate = join10(current, "packages", "runtime");
10264
- if (existsSync6(join10(candidate, "package.json")) && existsSync6(join10(candidate, "src"))) {
10468
+ if (existsSync7(join10(candidate, "package.json")) && existsSync7(join10(candidate, "src"))) {
10265
10469
  return candidate;
10266
10470
  }
10267
10471
  const parent = dirname5(current);
@@ -10275,18 +10479,18 @@ function resolveBunExecutable2() {
10275
10479
  if (explicit && explicit.trim().length > 0) {
10276
10480
  return explicit;
10277
10481
  }
10278
- if (basename3(process.execPath).startsWith("bun") && existsSync6(process.execPath)) {
10482
+ if (basename3(process.execPath).startsWith("bun") && existsSync7(process.execPath)) {
10279
10483
  return process.execPath;
10280
10484
  }
10281
10485
  const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
10282
10486
  for (const entry of pathEntries) {
10283
10487
  const candidate = join10(entry, "bun");
10284
- if (existsSync6(candidate)) {
10488
+ if (existsSync7(candidate)) {
10285
10489
  return candidate;
10286
10490
  }
10287
10491
  }
10288
10492
  const homeBun = join10(homedir7(), ".bun", "bin", "bun");
10289
- if (existsSync6(homeBun)) {
10493
+ if (existsSync7(homeBun)) {
10290
10494
  return homeBun;
10291
10495
  }
10292
10496
  return "bun";
@@ -10476,10 +10680,10 @@ function launchctlPath() {
10476
10680
  return "/bin/launchctl";
10477
10681
  }
10478
10682
  function readLogLines(path) {
10479
- if (!existsSync6(path)) {
10683
+ if (!existsSync7(path)) {
10480
10684
  return [];
10481
10685
  }
10482
- return readFileSync2(path, "utf8").split(`
10686
+ return readFileSync3(path, "utf8").split(`
10483
10687
  `).map((line) => line.trim()).filter(Boolean);
10484
10688
  }
10485
10689
  function isPackageScriptBanner(line) {
@@ -10576,7 +10780,7 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
10576
10780
  ensureServiceDirectories(config);
10577
10781
  const launchctl = inspectLaunchctl(config);
10578
10782
  const health = await fetchHealthSnapshot(config);
10579
- const installed = existsSync6(config.launchAgentPath);
10783
+ const installed = existsSync7(config.launchAgentPath);
10580
10784
  const lastLogLine = health.reachable ? readLastLogLine([config.stdoutLogPath, config.stderrLogPath]) : readLastLogLine([config.stderrLogPath, config.stdoutLogPath]);
10581
10785
  return {
10582
10786
  label: config.label,
@@ -10638,7 +10842,7 @@ async function restartBrokerService(config = resolveBrokerServiceConfig()) {
10638
10842
  }
10639
10843
  async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
10640
10844
  await stopBrokerService(config);
10641
- if (existsSync6(config.launchAgentPath)) {
10845
+ if (existsSync7(config.launchAgentPath)) {
10642
10846
  rmSync2(config.launchAgentPath, { force: true });
10643
10847
  }
10644
10848
  return brokerServiceStatus(config);
@@ -10717,7 +10921,7 @@ var init_local_agent_template = __esm(() => {
10717
10921
 
10718
10922
  // ../runtime/src/local-agents.ts
10719
10923
  import { execFileSync as execFileSync3, execSync } from "child_process";
10720
- import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
10924
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
10721
10925
  import { mkdir as mkdir6, rm as rm5, stat as stat3, writeFile as writeFile6 } from "fs/promises";
10722
10926
  import { basename as basename4, dirname as dirname6, join as join11, resolve as resolve7 } from "path";
10723
10927
  import { fileURLToPath as fileURLToPath5 } from "url";
@@ -10730,10 +10934,10 @@ function resolveProjectsRoot(projectPath) {
10730
10934
  }
10731
10935
  try {
10732
10936
  const supportPaths = resolveOpenScoutSupportPaths();
10733
- if (!existsSync7(supportPaths.settingsPath)) {
10937
+ if (!existsSync8(supportPaths.settingsPath)) {
10734
10938
  return dirname6(projectPath);
10735
10939
  }
10736
- const raw = JSON.parse(readFileSync3(supportPaths.settingsPath, "utf8"));
10940
+ const raw = JSON.parse(readFileSync4(supportPaths.settingsPath, "utf8"));
10737
10941
  const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
10738
10942
  return workspaceRoot ? resolve7(workspaceRoot) : dirname6(projectPath);
10739
10943
  } catch {
@@ -10746,8 +10950,14 @@ function resolveBrokerUrl() {
10746
10950
  function nowSeconds2() {
10747
10951
  return Math.floor(Date.now() / 1000);
10748
10952
  }
10953
+ function scoutCliPath() {
10954
+ return join11(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
10955
+ }
10956
+ function legacyNodeBrokerRelayCommand() {
10957
+ return `node ${JSON.stringify(scoutCliPath())}`;
10958
+ }
10749
10959
  function brokerRelayCommand() {
10750
- return `node ${JSON.stringify(join11(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs"))}`;
10960
+ return `bun ${JSON.stringify(scoutCliPath())}`;
10751
10961
  }
10752
10962
  function titleCaseLocalAgentName(value) {
10753
10963
  return value.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
@@ -10760,7 +10970,7 @@ function resolveScoutSkillPath() {
10760
10970
  join11(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
10761
10971
  ];
10762
10972
  for (const path of candidatePaths) {
10763
- if (existsSync7(path)) {
10973
+ if (existsSync8(path)) {
10764
10974
  return path;
10765
10975
  }
10766
10976
  }
@@ -10956,7 +11166,7 @@ function buildLegacyBrokerBackedRelayPrompt(agentId, projectName, projectPath, r
10956
11166
  function legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath) {
10957
11167
  const relayHub = resolveRelayHub();
10958
11168
  const brokerUrl = resolveBrokerUrl();
10959
- const relayCommandBases = ["openscout relay", brokerRelayCommand()];
11169
+ const relayCommandBases = ["openscout relay", brokerRelayCommand(), legacyNodeBrokerRelayCommand()];
10960
11170
  const projectPathCandidates = projectPath.endsWith("/") ? [projectPath, projectPath.slice(0, -1)] : [projectPath, `${projectPath}/`];
10961
11171
  const candidates = new Set;
10962
11172
  for (const pathCandidate of projectPathCandidates) {
@@ -10967,12 +11177,28 @@ function legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPat
10967
11177
  }
10968
11178
  return Array.from(candidates);
10969
11179
  }
11180
+ function generatedLocalAgentSystemPromptCandidates(agentId, projectName, projectPath) {
11181
+ const baseContext = buildLocalAgentTemplateContext(agentId, projectName, projectPath);
11182
+ const relayCommands = [brokerRelayCommand(), legacyNodeBrokerRelayCommand()];
11183
+ const transportModes = [undefined, "codex_app_server", "claude_stream_json"];
11184
+ const candidates = new Set;
11185
+ for (const relayCommand of relayCommands) {
11186
+ const context = {
11187
+ ...baseContext,
11188
+ relayCommand
11189
+ };
11190
+ for (const transport of transportModes) {
11191
+ candidates.add(renderLocalAgentSystemPromptTemplate(buildLocalAgentSystemPromptTemplate(), context, transport ? { transport } : {}));
11192
+ }
11193
+ }
11194
+ return Array.from(candidates);
11195
+ }
10970
11196
  function normalizeLocalAgentSystemPrompt(agentId, projectName, projectPath, systemPrompt) {
10971
11197
  const trimmed = systemPrompt?.trim();
10972
11198
  if (!trimmed) {
10973
11199
  return;
10974
11200
  }
10975
- if (legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed)) {
11201
+ if (legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed) || generatedLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed)) {
10976
11202
  return;
10977
11203
  }
10978
11204
  return trimmed;
@@ -11052,6 +11278,128 @@ function normalizeLocalAgentCapabilities(value) {
11052
11278
  function normalizeLocalAgentLaunchArgs(value) {
11053
11279
  return Array.isArray(value) ? value.map((entry) => String(entry).trim()).filter(Boolean) : [];
11054
11280
  }
11281
+ function normalizeRequestedModel(value) {
11282
+ const trimmed = value?.trim();
11283
+ return trimmed ? trimmed : undefined;
11284
+ }
11285
+ function readClaudeLaunchModel(launchArgs) {
11286
+ for (let index = 0;index < launchArgs.length; index += 1) {
11287
+ const current = launchArgs[index] ?? "";
11288
+ if (current === "--model") {
11289
+ const next = launchArgs[index + 1]?.trim();
11290
+ return next || undefined;
11291
+ }
11292
+ if (current.startsWith("--model=")) {
11293
+ const next = current.slice("--model=".length).trim();
11294
+ return next || undefined;
11295
+ }
11296
+ }
11297
+ return;
11298
+ }
11299
+ function normalizeLaunchArgsForHarness(harness, value) {
11300
+ const normalized = normalizeLocalAgentLaunchArgs(value);
11301
+ if (harness === "codex") {
11302
+ return normalizeCodexAppServerLaunchArgs(normalized);
11303
+ }
11304
+ return normalized;
11305
+ }
11306
+ function readLaunchModelForHarness(harness, launchArgs) {
11307
+ if (harness === "codex") {
11308
+ return readCodexAppServerModelFromLaunchArgs(launchArgs) ?? undefined;
11309
+ }
11310
+ if (harness === "claude") {
11311
+ return readClaudeLaunchModel(launchArgs ?? []);
11312
+ }
11313
+ return;
11314
+ }
11315
+ function stripLaunchModelForHarness(harness, launchArgs) {
11316
+ if (harness === "codex") {
11317
+ const next = [];
11318
+ const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
11319
+ for (let index = 0;index < normalized.length; index += 1) {
11320
+ const current = normalized[index] ?? "";
11321
+ if (current === "-c" || current === "--config") {
11322
+ const value = normalized[index + 1];
11323
+ if (readCodexAppServerModelFromLaunchArgs(value ? [current, value] : [current])) {
11324
+ index += 1;
11325
+ continue;
11326
+ }
11327
+ next.push(current);
11328
+ if (value) {
11329
+ next.push(value);
11330
+ index += 1;
11331
+ }
11332
+ continue;
11333
+ }
11334
+ if (current.startsWith("--config=")) {
11335
+ if (readCodexAppServerModelFromLaunchArgs([current])) {
11336
+ continue;
11337
+ }
11338
+ }
11339
+ next.push(current);
11340
+ }
11341
+ return next;
11342
+ }
11343
+ if (harness === "claude") {
11344
+ const next = [];
11345
+ const normalized = normalizeLocalAgentLaunchArgs(launchArgs);
11346
+ for (let index = 0;index < normalized.length; index += 1) {
11347
+ const current = normalized[index] ?? "";
11348
+ if (current === "--model") {
11349
+ index += 1;
11350
+ continue;
11351
+ }
11352
+ if (current.startsWith("--model=")) {
11353
+ continue;
11354
+ }
11355
+ next.push(current);
11356
+ }
11357
+ return next;
11358
+ }
11359
+ return normalizeLocalAgentLaunchArgs(launchArgs);
11360
+ }
11361
+ function buildLaunchArgsForRequestedModel(harness, model) {
11362
+ if (harness === "codex") {
11363
+ return normalizeCodexAppServerLaunchArgs(["--model", model]);
11364
+ }
11365
+ if (harness === "claude") {
11366
+ return ["--model", model];
11367
+ }
11368
+ return [];
11369
+ }
11370
+ function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
11371
+ const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
11372
+ const requestedModel = normalizeRequestedModel(model);
11373
+ if (!requestedModel) {
11374
+ return normalized;
11375
+ }
11376
+ return [
11377
+ ...stripLaunchModelForHarness(harness, normalized),
11378
+ ...buildLaunchArgsForRequestedModel(harness, requestedModel)
11379
+ ];
11380
+ }
11381
+ function defaultHarnessForOverride(override, fallback = "claude") {
11382
+ return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
11383
+ }
11384
+ function launchArgsForOverrideHarness(override, harness) {
11385
+ const profileLaunchArgs = override.harnessProfiles?.[harness]?.launchArgs;
11386
+ if (profileLaunchArgs) {
11387
+ return normalizeLaunchArgsForHarness(harness, profileLaunchArgs);
11388
+ }
11389
+ if (harness === defaultHarnessForOverride(override, harness)) {
11390
+ return normalizeLaunchArgsForHarness(harness, override.launchArgs);
11391
+ }
11392
+ return [];
11393
+ }
11394
+ function overrideHarnessProfile(override, harness) {
11395
+ const profile = override.harnessProfiles?.[harness];
11396
+ return {
11397
+ cwd: normalizeProjectPath(profile?.cwd || override.runtime?.cwd || override.projectRoot || process.cwd()),
11398
+ transport: normalizeLocalAgentTransport(profile?.transport ?? override.runtime?.transport, harness),
11399
+ sessionId: normalizeTmuxSessionName2(profile?.sessionId, `${override.agentId}-${harness}`),
11400
+ launchArgs: launchArgsForOverrideHarness(override, harness)
11401
+ };
11402
+ }
11055
11403
  function normalizeManagedHarness2(value, fallback) {
11056
11404
  return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
11057
11405
  }
@@ -11067,7 +11415,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
11067
11415
  cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
11068
11416
  transport: normalizeLocalAgentTransport(profile.transport, harness),
11069
11417
  sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
11070
- launchArgs: normalizeLocalAgentLaunchArgs(profile.launchArgs)
11418
+ launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
11071
11419
  };
11072
11420
  }
11073
11421
  const runtimeHarness = normalizeManagedHarness2(record.harness, defaultHarness);
@@ -11076,7 +11424,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
11076
11424
  cwd: normalizeProjectPath(record.cwd || process.cwd()),
11077
11425
  transport: normalizeLocalAgentTransport(record.transport, runtimeHarness),
11078
11426
  sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${runtimeHarness}`),
11079
- launchArgs: normalizeLocalAgentLaunchArgs(record.launchArgs)
11427
+ launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs)
11080
11428
  };
11081
11429
  }
11082
11430
  if (!nextProfiles[defaultHarness]) {
@@ -11084,7 +11432,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
11084
11432
  cwd: normalizeProjectPath(record.cwd || process.cwd()),
11085
11433
  transport: normalizeLocalAgentTransport(record.transport, defaultHarness),
11086
11434
  sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${defaultHarness}`),
11087
- launchArgs: normalizeLocalAgentLaunchArgs(record.launchArgs)
11435
+ launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs)
11088
11436
  };
11089
11437
  }
11090
11438
  for (const harness of ["claude", "codex"]) {
@@ -11096,7 +11444,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
11096
11444
  cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
11097
11445
  transport: normalizeLocalAgentTransport(profile.transport, harness),
11098
11446
  sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
11099
- launchArgs: normalizeLocalAgentLaunchArgs(profile.launchArgs)
11447
+ launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
11100
11448
  };
11101
11449
  }
11102
11450
  return nextProfiles;
@@ -11120,14 +11468,14 @@ function recordForHarness(record, harnessOverride) {
11120
11468
  tmuxSession: fallbackSessionId,
11121
11469
  cwd: fallbackCwd,
11122
11470
  transport: fallbackTransport,
11123
- launchArgs: normalizeLocalAgentLaunchArgs(normalized.launchArgs),
11471
+ launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
11124
11472
  harnessProfiles: {
11125
11473
  ...normalized.harnessProfiles,
11126
11474
  [selectedHarness]: {
11127
11475
  cwd: fallbackCwd,
11128
11476
  transport: fallbackTransport,
11129
11477
  sessionId: fallbackSessionId,
11130
- launchArgs: normalizeLocalAgentLaunchArgs(normalized.launchArgs)
11478
+ launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs)
11131
11479
  }
11132
11480
  }
11133
11481
  };
@@ -11169,7 +11517,7 @@ function normalizeLocalAgentRecord(agentId, record) {
11169
11517
  harnessProfiles,
11170
11518
  transport: activeProfile?.transport ?? normalizeLocalAgentTransport(record.transport, harness),
11171
11519
  capabilities: normalizeLocalAgentCapabilities(record.capabilities),
11172
- launchArgs: activeProfile?.launchArgs ?? normalizeLocalAgentLaunchArgs(record.launchArgs)
11520
+ launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs)
11173
11521
  };
11174
11522
  }
11175
11523
  function localAgentStatusFromRecord(agentId, record, source) {
@@ -11238,7 +11586,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
11238
11586
  source: existing?.source && existing.source !== "inferred" ? existing.source : "manual",
11239
11587
  startedAt: normalizedRecord.startedAt,
11240
11588
  systemPrompt: normalizedRecord.systemPrompt,
11241
- launchArgs: normalizeLocalAgentLaunchArgs(normalizedRecord.launchArgs),
11589
+ launchArgs: normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs),
11242
11590
  capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
11243
11591
  defaultHarness: normalizeManagedHarness2(normalizedRecord.defaultHarness, "claude"),
11244
11592
  harnessProfiles: normalizedRecord.harnessProfiles,
@@ -11259,7 +11607,7 @@ function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
11259
11607
  systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "codex_app_server" }),
11260
11608
  runtimeDirectory: relayAgentRuntimeDirectory(agentName),
11261
11609
  logsDirectory: relayAgentLogsDirectory(agentName),
11262
- launchArgs: normalizeLocalAgentLaunchArgs(record.launchArgs)
11610
+ launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs)
11263
11611
  };
11264
11612
  }
11265
11613
  function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
@@ -11270,7 +11618,7 @@ function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
11270
11618
  systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "claude_stream_json" }),
11271
11619
  runtimeDirectory: relayAgentRuntimeDirectory(agentName),
11272
11620
  logsDirectory: relayAgentLogsDirectory(agentName),
11273
- launchArgs: normalizeLocalAgentLaunchArgs(record.launchArgs)
11621
+ launchArgs: normalizeLaunchArgsForHarness("claude", record.launchArgs)
11274
11622
  };
11275
11623
  }
11276
11624
  function isLocalAgentRecordOnline(agentName, record) {
@@ -11304,7 +11652,7 @@ function buildLocalAgentBootstrapPrompt(_harness, _systemPrompt, initialMessage)
11304
11652
  return initialMessage;
11305
11653
  }
11306
11654
  function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile, workerScript) {
11307
- const extraArgs = shellQuoteArguments(normalizeLocalAgentLaunchArgs(record.launchArgs));
11655
+ const extraArgs = shellQuoteArguments(normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs));
11308
11656
  if (normalizeLocalAgentHarness(record.harness) === "codex") {
11309
11657
  return `exec bash ${JSON.stringify(workerScript ?? join11(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
11310
11658
  }
@@ -11460,7 +11808,7 @@ async function ensureLocalAgentOnline(agentName, record) {
11460
11808
  await new Promise((resolve8) => setTimeout(resolve8, 100));
11461
11809
  }
11462
11810
  if (!isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
11463
- const stderrTail = existsSync7(stderrLogFile) ? readFileSync3(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
11811
+ const stderrTail = existsSync8(stderrLogFile) ? readFileSync4(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
11464
11812
  `).trim() : "";
11465
11813
  throw new Error(stderrTail ? `Relay agent ${agentName} failed to stay online:
11466
11814
  ${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
@@ -11676,6 +12024,7 @@ async function startLocalAgent(input) {
11676
12024
  const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
11677
12025
  const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
11678
12026
  const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
12027
+ const launchArgs = applyRequestedModelToLaunchArgs(effectiveHarness, [], input.model);
11679
12028
  const existingForInstance = overrides[instance.id];
11680
12029
  if (existingForInstance && normalizeProjectPath(existingForInstance.projectRoot) !== projectRoot) {
11681
12030
  throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
@@ -11689,13 +12038,14 @@ async function startLocalAgent(input) {
11689
12038
  projectConfigPath: coldProjectConfigPath,
11690
12039
  source: "manual",
11691
12040
  startedAt: nowSeconds2(),
12041
+ launchArgs,
11692
12042
  defaultHarness: effectiveHarness,
11693
12043
  harnessProfiles: {
11694
12044
  [effectiveHarness]: {
11695
12045
  cwd: effectiveCwd ?? projectRoot,
11696
12046
  transport,
11697
12047
  sessionId,
11698
- launchArgs: []
12048
+ launchArgs
11699
12049
  }
11700
12050
  },
11701
12051
  runtime: {
@@ -11716,6 +12066,9 @@ async function startLocalAgent(input) {
11716
12066
  throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
11717
12067
  }
11718
12068
  const resolvedHarness = preferredHarness ?? matchingOverride.runtime?.harness;
12069
+ const nextHarness = normalizeManagedHarness2(resolvedHarness, "claude");
12070
+ const nextDefaultHarness = preferredHarness ? normalizeManagedHarness2(preferredHarness, "claude") : defaultHarnessForOverride(matchingOverride, "claude");
12071
+ const nextLaunchArgs = applyRequestedModelToLaunchArgs(nextHarness, launchArgsForOverrideHarness(matchingOverride, nextHarness), input.model);
11719
12072
  overrides[instance.id] = {
11720
12073
  agentId: instance.id,
11721
12074
  definitionId: requestedDefinitionId,
@@ -11726,10 +12079,16 @@ async function startLocalAgent(input) {
11726
12079
  source: "manual",
11727
12080
  startedAt: matchingOverride.startedAt ?? nowSeconds2(),
11728
12081
  systemPrompt: matchingOverride.systemPrompt,
11729
- launchArgs: matchingOverride.launchArgs,
12082
+ launchArgs: nextHarness === nextDefaultHarness ? nextLaunchArgs : matchingOverride.launchArgs,
11730
12083
  capabilities: matchingOverride.capabilities,
11731
- defaultHarness: normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness, "claude"),
11732
- harnessProfiles: matchingOverride.harnessProfiles,
12084
+ defaultHarness: nextDefaultHarness,
12085
+ harnessProfiles: {
12086
+ ...matchingOverride.harnessProfiles ?? {},
12087
+ [nextHarness]: {
12088
+ ...overrideHarnessProfile(matchingOverride, nextHarness),
12089
+ launchArgs: nextLaunchArgs
12090
+ }
12091
+ },
11733
12092
  runtime: {
11734
12093
  cwd: effectiveCwd ?? matchingOverride.runtime?.cwd ?? matchingProjectRoot,
11735
12094
  harness: resolvedHarness,
@@ -11741,6 +12100,22 @@ async function startLocalAgent(input) {
11741
12100
  await writeRelayAgentOverrides(overrides);
11742
12101
  targetAgentId = instance.id;
11743
12102
  } else {
12103
+ if (input.model) {
12104
+ const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
12105
+ const nextLaunchArgs = applyRequestedModelToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), input.model);
12106
+ overrides[matchingAgentId] = {
12107
+ ...matchingOverride,
12108
+ launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
12109
+ harnessProfiles: {
12110
+ ...matchingOverride.harnessProfiles ?? {},
12111
+ [existingHarness]: {
12112
+ ...overrideHarnessProfile(matchingOverride, existingHarness),
12113
+ launchArgs: nextLaunchArgs
12114
+ }
12115
+ }
12116
+ };
12117
+ await writeRelayAgentOverrides(overrides);
12118
+ }
11744
12119
  targetAgentId = matchingAgentId;
11745
12120
  }
11746
12121
  await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
@@ -11828,13 +12203,38 @@ async function restartAllLocalAgents(options = {}) {
11828
12203
  }));
11829
12204
  return restarted.filter((agent) => Boolean(agent));
11830
12205
  }
12206
+ function readPersistedClaudeSessionId(agentName) {
12207
+ try {
12208
+ const runtimeDir = relayAgentRuntimeDirectory(agentName);
12209
+ const catalogPath = join11(runtimeDir, "session-catalog.json");
12210
+ if (existsSync8(catalogPath)) {
12211
+ const raw = readFileSync4(catalogPath, "utf8").trim();
12212
+ if (raw) {
12213
+ const catalog = JSON.parse(raw);
12214
+ if (typeof catalog.activeSessionId === "string" && catalog.activeSessionId.trim()) {
12215
+ return catalog.activeSessionId.trim();
12216
+ }
12217
+ }
12218
+ }
12219
+ const legacyPath = join11(runtimeDir, "claude-session-id.txt");
12220
+ if (existsSync8(legacyPath)) {
12221
+ const value = readFileSync4(legacyPath, "utf8").trim();
12222
+ return value || null;
12223
+ }
12224
+ return null;
12225
+ } catch {
12226
+ return null;
12227
+ }
12228
+ }
11831
12229
  function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
11832
12230
  const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
12231
+ const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
11833
12232
  const definitionId = normalizedRecord.definitionId ?? agentId;
11834
12233
  const displayName = titleCaseLocalAgentName(definitionId);
11835
12234
  const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
11836
12235
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
11837
12236
  const actorId = instance.id;
12237
+ const externalSessionId = normalizedRecord.transport === "claude_stream_json" ? readPersistedClaudeSessionId(definitionId) : null;
11838
12238
  return {
11839
12239
  actor: {
11840
12240
  id: actorId,
@@ -11853,7 +12253,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
11853
12253
  defaultSelector: instance.defaultSelector,
11854
12254
  nodeQualifier: instance.nodeQualifier,
11855
12255
  workspaceQualifier: instance.workspaceQualifier,
11856
- branch: instance.branch
12256
+ branch: instance.branch,
12257
+ ...configuredModel ? { model: configuredModel } : {}
11857
12258
  }
11858
12259
  },
11859
12260
  agent: {
@@ -11880,7 +12281,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
11880
12281
  defaultSelector: instance.defaultSelector,
11881
12282
  nodeQualifier: instance.nodeQualifier,
11882
12283
  workspaceQualifier: instance.workspaceQualifier,
11883
- branch: instance.branch
12284
+ branch: instance.branch,
12285
+ ...configuredModel ? { model: configuredModel } : {}
11884
12286
  },
11885
12287
  agentClass: "general",
11886
12288
  capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
@@ -11912,7 +12314,9 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
11912
12314
  selector: instance.selector,
11913
12315
  nodeQualifier: instance.nodeQualifier,
11914
12316
  workspaceQualifier: instance.workspaceQualifier,
11915
- branch: instance.branch
12317
+ branch: instance.branch,
12318
+ ...configuredModel ? { model: configuredModel } : {},
12319
+ ...externalSessionId ? { externalSessionId } : {}
11916
12320
  }
11917
12321
  }
11918
12322
  };
@@ -12008,7 +12412,7 @@ var init_local_agents = __esm(async () => {
12008
12412
  });
12009
12413
 
12010
12414
  // ../runtime/src/user-config.ts
12011
- import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
12415
+ import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
12012
12416
  import { homedir as homedir8 } from "os";
12013
12417
  import { dirname as dirname7, join as join12 } from "path";
12014
12418
  function userConfigPath() {
@@ -12016,10 +12420,10 @@ function userConfigPath() {
12016
12420
  }
12017
12421
  function loadUserConfig() {
12018
12422
  const configPath = userConfigPath();
12019
- if (!existsSync8(configPath))
12423
+ if (!existsSync9(configPath))
12020
12424
  return {};
12021
12425
  try {
12022
- return JSON.parse(readFileSync4(configPath, "utf8"));
12426
+ return JSON.parse(readFileSync5(configPath, "utf8"));
12023
12427
  } catch {
12024
12428
  return {};
12025
12429
  }
@@ -13023,8 +13427,73 @@ async function sendScoutMessage(input) {
13023
13427
  }
13024
13428
  const currentDirectory = input.currentDirectory ?? process.cwd();
13025
13429
  const createdAtMs = input.createdAtMs ?? Date.now();
13026
- const mentionResolution = await resolveMentionTargets(broker.snapshot, input.body, currentDirectory);
13027
13430
  const senderId = await resolveConversationActorId(broker.baseUrl, broker.snapshot, broker.node.id, input.senderId, currentDirectory);
13431
+ const requestedTargetLabel = input.targetLabel?.trim();
13432
+ if (requestedTargetLabel) {
13433
+ const targetResolution = await resolveSingleBrokerTarget(broker.snapshot, requestedTargetLabel, currentDirectory);
13434
+ if (targetResolution.kind !== "resolved") {
13435
+ return {
13436
+ usedBroker: true,
13437
+ invokedTargets: [],
13438
+ unresolvedTargets: [requestedTargetLabel]
13439
+ };
13440
+ }
13441
+ const target = targetResolution.target;
13442
+ const targetReady = await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, target.agentId, currentDirectory);
13443
+ if (!targetReady || target.agentId === senderId) {
13444
+ return {
13445
+ usedBroker: true,
13446
+ invokedTargets: [],
13447
+ unresolvedTargets: [requestedTargetLabel]
13448
+ };
13449
+ }
13450
+ let conversation2;
13451
+ if (!input.channel) {
13452
+ const dm = await ensureBrokerDirectConversationBetween(broker.baseUrl, broker.snapshot, broker.node.id, senderId, target.agentId);
13453
+ conversation2 = dm.conversation;
13454
+ } else {
13455
+ conversation2 = await ensureBrokerConversation(broker.baseUrl, broker.snapshot, broker.node.id, input.channel, senderId, [target.agentId]);
13456
+ }
13457
+ const messageId2 = `m-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
13458
+ const speechText2 = input.shouldSpeak ? stripScoutAgentSelectorLabels(input.body) : "";
13459
+ const returnAddress2 = buildScoutReturnAddress2(broker.snapshot, senderId, {
13460
+ conversationId: conversation2.id,
13461
+ replyToMessageId: messageId2
13462
+ });
13463
+ await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.messages, {
13464
+ id: messageId2,
13465
+ conversationId: conversation2.id,
13466
+ actorId: senderId,
13467
+ originNodeId: broker.node.id,
13468
+ class: conversation2.kind === "system" ? "system" : "agent",
13469
+ body: input.body,
13470
+ mentions: [{ actorId: target.agentId, label: target.label }],
13471
+ speech: speechText2 ? { text: speechText2 } : undefined,
13472
+ audience: {
13473
+ notify: [target.agentId],
13474
+ reason: relayAudienceReason(conversation2)
13475
+ },
13476
+ visibility: conversation2.visibility,
13477
+ policy: "durable",
13478
+ createdAt: createdAtMs,
13479
+ metadata: {
13480
+ source: "scout-cli",
13481
+ relayChannel: relayChannelMetadata(conversation2, input.channel),
13482
+ relayTargetIds: [target.agentId],
13483
+ relayMessageId: messageId2,
13484
+ returnAddress: returnAddress2
13485
+ }
13486
+ });
13487
+ return {
13488
+ usedBroker: true,
13489
+ conversationId: conversation2.id,
13490
+ messageId: messageId2,
13491
+ invokedTargets: [target.agentId],
13492
+ unresolvedTargets: [],
13493
+ routeKind: relayRouteKind(conversation2)
13494
+ };
13495
+ }
13496
+ const mentionResolution = await resolveMentionTargets(broker.snapshot, input.body, currentDirectory);
13028
13497
  const availableTargets = (await Promise.all(mentionResolution.resolved.map(async (target) => await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, target.agentId, currentDirectory) ? target : null))).filter((target) => Boolean(target));
13029
13498
  const validTargets = [
13030
13499
  ...new Set(availableTargets.map((target) => target.agentId).filter((target) => target !== senderId && Boolean(broker.snapshot.agents[target])))
@@ -14306,6 +14775,7 @@ function buildScoutAgentCard(binding, options = {}) {
14306
14775
  const selector = binding.agent.selector?.trim() || metadataString3(binding.agent.metadata, "selector");
14307
14776
  const defaultSelector = binding.agent.defaultSelector?.trim() || metadataString3(binding.agent.metadata, "defaultSelector");
14308
14777
  const branch = metadataString3(binding.agent.metadata, "branch") || metadataString3(binding.endpoint.metadata, "branch");
14778
+ const model = metadataString3(binding.endpoint.metadata, "model") || metadataString3(binding.agent.metadata, "model");
14309
14779
  const description = metadataString3(binding.agent.metadata, "description");
14310
14780
  const version = metadataString3(binding.agent.metadata, "version");
14311
14781
  const documentationUrl = metadataString3(binding.agent.metadata, "documentationUrl") || metadataString3(binding.agent.metadata, "docsUrl");
@@ -14363,7 +14833,8 @@ function buildScoutAgentCard(binding, options = {}) {
14363
14833
  metadata: {
14364
14834
  actorId: binding.actor.id,
14365
14835
  endpointId: binding.endpoint.id,
14366
- wakePolicy: binding.agent.wakePolicy
14836
+ wakePolicy: binding.agent.wakePolicy,
14837
+ ...model ? { model } : {}
14367
14838
  }
14368
14839
  };
14369
14840
  }
@@ -14397,6 +14868,7 @@ async function createScoutAgentCard(input) {
14397
14868
  agentName: input.agentName,
14398
14869
  displayName: input.displayName,
14399
14870
  harness: input.harness,
14871
+ model: input.model,
14400
14872
  currentDirectory: input.currentDirectory
14401
14873
  });
14402
14874
  const currentDirectory = input.currentDirectory ?? input.projectPath;
@@ -14481,7 +14953,7 @@ __export(exports_card, {
14481
14953
  import { createInterface } from "readline";
14482
14954
  function renderCardCommandHelp() {
14483
14955
  return [
14484
- "Usage: scout card create [path] [--name <alias>] [--display-name <name>] [--harness <claude|codex>] [--as <requester>] [--no-input] [--path <path>]",
14956
+ "Usage: scout card create [path] [--name <alias>] [--display-name <name>] [--harness <claude|codex>] [--model <model>] [--as <requester>] [--no-input] [--path <path>]",
14485
14957
  "",
14486
14958
  "Create a dedicated Scout agent card with a reply-ready return address.",
14487
14959
  "",
@@ -14490,7 +14962,7 @@ function renderCardCommandHelp() {
14490
14962
  "",
14491
14963
  "Examples:",
14492
14964
  " scout card create",
14493
- " scout card create ~/dev/openscout-worktrees/shell-fix --name shellfix --harness claude"
14965
+ " scout card create ~/dev/openscout-worktrees/shell-fix --name shellfix --harness claude --model claude-sonnet-4-6"
14494
14966
  ].join(`
14495
14967
  `);
14496
14968
  }
@@ -14554,6 +15026,7 @@ async function runCardCommand(context, args) {
14554
15026
  agentName,
14555
15027
  displayName,
14556
15028
  harness: parseScoutHarness(options.harness),
15029
+ model: options.model,
14557
15030
  currentDirectory: options.currentDirectory,
14558
15031
  createdById: resolveScoutAgentName(options.requesterId)
14559
15032
  });
@@ -43090,7 +43563,7 @@ var init_down = __esm(async () => {
43090
43563
  // ../../apps/desktop/src/app/host/runtime-service-client.ts
43091
43564
  import { spawn as spawn4 } from "child_process";
43092
43565
  import { basename as basename6, dirname as dirname8, join as join14 } from "path";
43093
- import { existsSync as existsSync9 } from "fs";
43566
+ import { existsSync as existsSync10 } from "fs";
43094
43567
  import { fileURLToPath as fileURLToPath6 } from "url";
43095
43568
  import { homedir as homedir9 } from "os";
43096
43569
  function tryWhich(executableName) {
@@ -43100,13 +43573,13 @@ function tryWhich(executableName) {
43100
43573
  if (!dir)
43101
43574
  continue;
43102
43575
  const candidate = join14(dir, executableName);
43103
- if (existsSync9(candidate)) {
43576
+ if (existsSync10(candidate)) {
43104
43577
  return candidate;
43105
43578
  }
43106
43579
  if (process.platform === "win32") {
43107
43580
  for (const ext of [".cmd", ".exe", ".bat"]) {
43108
43581
  const withExt = candidate + ext;
43109
- if (existsSync9(withExt))
43582
+ if (existsSync10(withExt))
43110
43583
  return withExt;
43111
43584
  }
43112
43585
  }
@@ -43118,7 +43591,7 @@ function findNodeModulesRuntimeBin() {
43118
43591
  let dir = dirname8(fileURLToPath6(import.meta.url));
43119
43592
  for (let i = 0;i < 24; i++) {
43120
43593
  const candidate = join14(dir, runtimeBinRel);
43121
- if (existsSync9(candidate))
43594
+ if (existsSync10(candidate))
43122
43595
  return candidate;
43123
43596
  const parent = dirname8(dir);
43124
43597
  if (parent === dir)
@@ -43126,7 +43599,7 @@ function findNodeModulesRuntimeBin() {
43126
43599
  dir = parent;
43127
43600
  }
43128
43601
  const bunGlobal = join14(homedir9(), ".bun", "install", "global", "node_modules", "@openscout", "runtime", "bin", "openscout-runtime.mjs");
43129
- if (existsSync9(bunGlobal))
43602
+ if (existsSync10(bunGlobal))
43130
43603
  return bunGlobal;
43131
43604
  return null;
43132
43605
  }
@@ -43134,7 +43607,7 @@ function findMonorepoOpenscoutRuntimeBin() {
43134
43607
  let dir = process.cwd();
43135
43608
  for (let i = 0;i < 24; i++) {
43136
43609
  const candidate = join14(dir, "packages", "runtime", "bin", "openscout-runtime.mjs");
43137
- if (existsSync9(candidate)) {
43610
+ if (existsSync10(candidate)) {
43138
43611
  return candidate;
43139
43612
  }
43140
43613
  const parent = dirname8(dir);
@@ -43147,7 +43620,7 @@ function findMonorepoOpenscoutRuntimeBin() {
43147
43620
  function resolveJavaScriptRuntimeExecutable() {
43148
43621
  const explicit = process.env.OPENSCOUT_RUNTIME_NODE_BIN?.trim();
43149
43622
  if (explicit) {
43150
- if (existsSync9(explicit)) {
43623
+ if (existsSync10(explicit)) {
43151
43624
  return explicit;
43152
43625
  }
43153
43626
  const found = tryWhich(explicit);
@@ -43173,7 +43646,7 @@ function resolveJavaScriptRuntimeExecutable() {
43173
43646
  function resolveRuntimeServiceEntrypoint() {
43174
43647
  const explicit = process.env.OPENSCOUT_RUNTIME_BIN?.trim();
43175
43648
  if (explicit) {
43176
- if (existsSync9(explicit)) {
43649
+ if (existsSync10(explicit)) {
43177
43650
  return explicit;
43178
43651
  }
43179
43652
  const found = tryWhich(explicit);
@@ -43450,16 +43923,16 @@ var init_service3 = __esm(() => {
43450
43923
  });
43451
43924
 
43452
43925
  // ../../apps/desktop/src/shared/paths.ts
43453
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
43926
+ import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
43454
43927
  import { dirname as dirname10, join as join16, resolve as resolve10 } from "path";
43455
43928
  import { fileURLToPath as fileURLToPath7 } from "url";
43456
43929
  function looksLikeWorkspaceRoot(candidate) {
43457
43930
  const packageJsonPath = join16(candidate, "package.json");
43458
- if (!existsSync10(packageJsonPath)) {
43931
+ if (!existsSync11(packageJsonPath)) {
43459
43932
  return false;
43460
43933
  }
43461
43934
  try {
43462
- const parsed = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
43935
+ const parsed = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
43463
43936
  return Array.isArray(parsed.workspaces);
43464
43937
  } catch {
43465
43938
  return false;
@@ -43467,18 +43940,18 @@ function looksLikeWorkspaceRoot(candidate) {
43467
43940
  }
43468
43941
  function looksLikePackagedAppRoot(candidate) {
43469
43942
  const packageJsonPath = join16(candidate, "package.json");
43470
- if (!existsSync10(packageJsonPath)) {
43943
+ if (!existsSync11(packageJsonPath)) {
43471
43944
  return false;
43472
43945
  }
43473
43946
  try {
43474
- const parsed = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
43947
+ const parsed = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
43475
43948
  return parsed.name === "@openscout/scout" || parsed.name === "@openscout/cli";
43476
43949
  } catch {
43477
43950
  return false;
43478
43951
  }
43479
43952
  }
43480
43953
  function looksLikeSourceAppRoot(candidate) {
43481
- return existsSync10(join16(candidate, "bin", "scout.ts"));
43954
+ return existsSync11(join16(candidate, "bin", "scout.ts"));
43482
43955
  }
43483
43956
  function findMatchingAncestor(startDirectory, predicate) {
43484
43957
  let current = resolve10(startDirectory);
@@ -43798,7 +44271,7 @@ var exports_env = {};
43798
44271
  __export(exports_env, {
43799
44272
  runEnvCommand: () => runEnvCommand
43800
44273
  });
43801
- import { existsSync as existsSync11 } from "fs";
44274
+ import { existsSync as existsSync12 } from "fs";
43802
44275
  import { join as join17 } from "path";
43803
44276
  function resolveCommandOnPath(command, env) {
43804
44277
  const pathValue = env.PATH ?? "";
@@ -43808,7 +44281,7 @@ function resolveCommandOnPath(command, env) {
43808
44281
  continue;
43809
44282
  }
43810
44283
  const candidate = join17(trimmed, command);
43811
- if (existsSync11(candidate)) {
44284
+ if (existsSync12(candidate)) {
43812
44285
  return candidate;
43813
44286
  }
43814
44287
  }
@@ -43820,7 +44293,7 @@ function resolveScoutBinPath(appRoot) {
43820
44293
  join17(appRoot, "bin", "scout.mjs")
43821
44294
  ];
43822
44295
  for (const candidate of candidates) {
43823
- if (existsSync11(candidate)) {
44296
+ if (existsSync12(candidate)) {
43824
44297
  return candidate;
43825
44298
  }
43826
44299
  }
@@ -43895,9 +44368,9 @@ var init_env = __esm(async () => {
43895
44368
 
43896
44369
  // ../runtime/src/local-config.ts
43897
44370
  import {
43898
- existsSync as existsSync12,
44371
+ existsSync as existsSync13,
43899
44372
  mkdirSync as mkdirSync4,
43900
- readFileSync as readFileSync6,
44373
+ readFileSync as readFileSync7,
43901
44374
  renameSync,
43902
44375
  writeFileSync as writeFileSync4
43903
44376
  } from "fs";
@@ -43911,10 +44384,10 @@ function localConfigPath() {
43911
44384
  }
43912
44385
  function loadLocalConfig() {
43913
44386
  const configPath = localConfigPath();
43914
- if (!existsSync12(configPath))
44387
+ if (!existsSync13(configPath))
43915
44388
  return { version: LOCAL_CONFIG_VERSION };
43916
44389
  try {
43917
- return validateLocalConfig(JSON.parse(readFileSync6(configPath, "utf8")));
44390
+ return validateLocalConfig(JSON.parse(readFileSync7(configPath, "utf8")));
43918
44391
  } catch {
43919
44392
  return { version: LOCAL_CONFIG_VERSION };
43920
44393
  }
@@ -43954,7 +44427,7 @@ function writeLocalConfig(config2) {
43954
44427
  renameSync(tmp, configPath);
43955
44428
  }
43956
44429
  function localConfigExists() {
43957
- return existsSync12(localConfigPath());
44430
+ return existsSync13(localConfigPath());
43958
44431
  }
43959
44432
  var LOCAL_CONFIG_VERSION = 1, DEFAULT_LOCAL_CONFIG;
43960
44433
  var init_local_config = __esm(() => {
@@ -45284,6 +45757,7 @@ function defaultScoutMcpDependencies(env) {
45284
45757
  agentName,
45285
45758
  displayName,
45286
45759
  harness,
45760
+ model,
45287
45761
  currentDirectory,
45288
45762
  createdById
45289
45763
  }) => createScoutAgentCard({
@@ -45291,12 +45765,21 @@ function defaultScoutMcpDependencies(env) {
45291
45765
  agentName,
45292
45766
  displayName,
45293
45767
  harness,
45768
+ model,
45294
45769
  currentDirectory,
45295
45770
  createdById
45296
45771
  }),
45297
- sendMessage: ({ senderId, body, channel, shouldSpeak, currentDirectory }) => sendScoutMessage({
45772
+ sendMessage: ({
45298
45773
  senderId,
45299
45774
  body,
45775
+ targetLabel,
45776
+ channel,
45777
+ shouldSpeak,
45778
+ currentDirectory
45779
+ }) => sendScoutMessage({
45780
+ senderId,
45781
+ body,
45782
+ targetLabel,
45300
45783
  channel,
45301
45784
  shouldSpeak,
45302
45785
  currentDirectory
@@ -45408,7 +45891,8 @@ function createScoutMcpServer(options) {
45408
45891
  senderId: string2().optional(),
45409
45892
  agentName: string2().optional(),
45410
45893
  displayName: string2().optional(),
45411
- harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional()
45894
+ harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional(),
45895
+ model: string2().optional()
45412
45896
  }),
45413
45897
  outputSchema: cardCreateResultSchema,
45414
45898
  annotations: {
@@ -45423,7 +45907,8 @@ function createScoutMcpServer(options) {
45423
45907
  senderId,
45424
45908
  agentName,
45425
45909
  displayName,
45426
- harness
45910
+ harness,
45911
+ model
45427
45912
  }) => {
45428
45913
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
45429
45914
  const resolvedSenderId = await deps.resolveSenderId(senderId, resolvedCurrentDirectory, env);
@@ -45432,6 +45917,7 @@ function createScoutMcpServer(options) {
45432
45917
  agentName: agentName?.trim() || undefined,
45433
45918
  displayName: displayName?.trim() || undefined,
45434
45919
  harness,
45920
+ model: model?.trim() || undefined,
45435
45921
  currentDirectory: resolvedCurrentDirectory,
45436
45922
  createdById: resolvedSenderId
45437
45923
  });
@@ -45511,11 +45997,12 @@ function createScoutMcpServer(options) {
45511
45997
  });
45512
45998
  server.registerTool("messages_send", {
45513
45999
  title: "Send Scout Message",
45514
- description: "Post a broker-backed Scout tell/update. Use this for heads-up, replies, and status. One explicit target without a channel becomes a DM. Group delivery requires an explicit channel. Use broadcast or channel='shared' for shared updates. For owned work or a reply lifecycle, use invocations_ask instead. Prefer mentionAgentIds for first-class targeting.",
46000
+ description: "Post a broker-backed Scout tell/update. Use this for heads-up, replies, and status. One explicit target without a channel becomes a DM. Group delivery requires an explicit channel. Use broadcast or channel='shared' for shared updates. Pass targetLabel for the single-call broker-resolved path when you know who to contact but do not have an exact id. For owned work or a reply lifecycle, use invocations_ask instead. Prefer mentionAgentIds for first-class targeting when you already have exact ids.",
45515
46001
  inputSchema: object2({
45516
46002
  body: string2().min(1),
45517
46003
  currentDirectory: string2().optional(),
45518
46004
  senderId: string2().optional(),
46005
+ targetLabel: string2().optional(),
45519
46006
  channel: string2().optional(),
45520
46007
  shouldSpeak: boolean2().optional(),
45521
46008
  mentionAgentIds: array(string2()).optional()
@@ -45531,6 +46018,7 @@ function createScoutMcpServer(options) {
45531
46018
  body,
45532
46019
  currentDirectory,
45533
46020
  senderId,
46021
+ targetLabel,
45534
46022
  channel,
45535
46023
  shouldSpeak,
45536
46024
  mentionAgentIds
@@ -45567,6 +46055,32 @@ function createScoutMcpServer(options) {
45567
46055
  structuredContent: structuredContent2
45568
46056
  };
45569
46057
  }
46058
+ if (targetLabel?.trim()) {
46059
+ const result2 = await deps.sendMessage({
46060
+ senderId: resolvedSenderId,
46061
+ body,
46062
+ targetLabel: targetLabel.trim(),
46063
+ channel,
46064
+ shouldSpeak,
46065
+ currentDirectory: resolvedCurrentDirectory
46066
+ });
46067
+ const structuredContent2 = {
46068
+ currentDirectory: resolvedCurrentDirectory,
46069
+ senderId: resolvedSenderId,
46070
+ mode: "target_label",
46071
+ usedBroker: result2.usedBroker,
46072
+ conversationId: result2.conversationId ?? null,
46073
+ messageId: result2.messageId ?? null,
46074
+ invokedTargetIds: result2.invokedTargets,
46075
+ unresolvedTargetIds: result2.unresolvedTargets,
46076
+ routeKind: result2.routeKind ?? null,
46077
+ routingError: result2.routingError ?? null
46078
+ };
46079
+ return {
46080
+ content: createTextContent(structuredContent2),
46081
+ structuredContent: structuredContent2
46082
+ };
46083
+ }
45570
46084
  const result = await deps.sendMessage({
45571
46085
  senderId: resolvedSenderId,
45572
46086
  body,
@@ -45918,7 +46432,7 @@ var init_scout_mcp = __esm(async () => {
45918
46432
  sendResultSchema = object2({
45919
46433
  currentDirectory: string2(),
45920
46434
  senderId: string2(),
45921
- mode: _enum2(["body_mentions", "explicit_targets"]),
46435
+ mode: _enum2(["body_mentions", "explicit_targets", "target_label"]),
45922
46436
  usedBroker: boolean2(),
45923
46437
  conversationId: string2().nullable(),
45924
46438
  messageId: string2().nullable(),
@@ -46011,7 +46525,7 @@ __export(exports_menu, {
46011
46525
  parseMenuCommand: () => parseMenuCommand
46012
46526
  });
46013
46527
  import { spawnSync as spawnSync2 } from "child_process";
46014
- import { existsSync as existsSync13 } from "fs";
46528
+ import { existsSync as existsSync14 } from "fs";
46015
46529
  import { homedir as homedir11 } from "os";
46016
46530
  import { dirname as dirname12, join as join19, resolve as resolve12 } from "path";
46017
46531
  import { fileURLToPath as fileURLToPath8 } from "url";
@@ -46100,7 +46614,7 @@ function findRepoMenuHelper(startDirectory) {
46100
46614
  let current = resolve12(startDirectory);
46101
46615
  while (true) {
46102
46616
  const candidate = join19(current, "apps", "macos", "bin", "openscout-menu.ts");
46103
- if (existsSync13(candidate)) {
46617
+ if (existsSync14(candidate)) {
46104
46618
  return candidate;
46105
46619
  }
46106
46620
  const parent = dirname12(current);
@@ -46110,7 +46624,7 @@ function findRepoMenuHelper(startDirectory) {
46110
46624
  current = parent;
46111
46625
  }
46112
46626
  const sourceRelativeCandidate = fileURLToPath8(new URL("../../../../macos/bin/openscout-menu.ts", import.meta.url));
46113
- return existsSync13(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
46627
+ return existsSync14(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
46114
46628
  }
46115
46629
  function resolveRepoBundlePath(helperPath) {
46116
46630
  return resolve12(dirname12(helperPath), "..", "dist", MENU_BUNDLE_NAME);
@@ -46129,7 +46643,7 @@ function resolveInstalledMenuBundlePath(env) {
46129
46643
  return indexedPath;
46130
46644
  }
46131
46645
  for (const candidate of COMMON_MENU_BUNDLE_PATHS) {
46132
- if (existsSync13(candidate)) {
46646
+ if (existsSync14(candidate)) {
46133
46647
  return candidate;
46134
46648
  }
46135
46649
  }
@@ -46198,7 +46712,7 @@ function runWithRepoHelper(context, helperPath, command) {
46198
46712
  });
46199
46713
  const bundlePath = resolveRepoBundlePath(helperPath);
46200
46714
  const running = command.action === "quit" ? false : isMenuRunning(context.env);
46201
- const installed = existsSync13(bundlePath) || running;
46715
+ const installed = existsSync14(bundlePath) || running;
46202
46716
  return {
46203
46717
  action: command.action,
46204
46718
  mode: "repo-helper",
@@ -46290,6 +46804,26 @@ function parsePeers(status) {
46290
46804
  tags: peer.Tags ?? []
46291
46805
  }));
46292
46806
  }
46807
+ function parseSelf(status) {
46808
+ const self2 = status.Self;
46809
+ if (!self2) {
46810
+ return null;
46811
+ }
46812
+ return {
46813
+ id: self2.ID ?? self2.DNSName ?? self2.HostName ?? "self",
46814
+ name: self2.HostName ?? self2.DNSName ?? "self",
46815
+ dnsName: self2.DNSName,
46816
+ addresses: self2.TailscaleIPs ?? [],
46817
+ online: self2.Online ?? true,
46818
+ hostName: self2.HostName,
46819
+ os: self2.OS,
46820
+ tailnetName: status.CurrentTailnet?.Name,
46821
+ magicDnsSuffix: status.CurrentTailnet?.MagicDNSSuffix
46822
+ };
46823
+ }
46824
+ function isBackendRunning(status) {
46825
+ return (status.BackendState ?? "").trim().toLowerCase() === "running";
46826
+ }
46293
46827
  async function readStatusJsonFromFile(filePath) {
46294
46828
  const raw = await readFile9(filePath, "utf8");
46295
46829
  return parseStatusJson(raw);
@@ -46308,11 +46842,24 @@ async function readStatusJson() {
46308
46842
  }
46309
46843
  }
46310
46844
  async function readTailscalePeers() {
46845
+ const summary = await readTailscaleStatusSummary();
46846
+ if (!summary) {
46847
+ return [];
46848
+ }
46849
+ return summary.peers;
46850
+ }
46851
+ async function readTailscaleStatusSummary() {
46311
46852
  const status = await readStatusJson();
46312
46853
  if (!status) {
46313
- return [];
46854
+ return null;
46314
46855
  }
46315
- return parsePeers(status);
46856
+ return {
46857
+ backendState: status.BackendState ?? null,
46858
+ running: isBackendRunning(status),
46859
+ health: status.Health ?? [],
46860
+ peers: parsePeers(status),
46861
+ self: parseSelf(status)
46862
+ };
46316
46863
  }
46317
46864
  var execFileAsync;
46318
46865
  var init_tailscale = __esm(() => {
@@ -46342,9 +46889,13 @@ function readMeshEnvVars() {
46342
46889
  };
46343
46890
  }
46344
46891
  async function readTailscaleStatus() {
46345
- const peers = await readTailscalePeers();
46892
+ const summary = await readTailscaleStatusSummary();
46893
+ const peers = summary?.peers ?? [];
46346
46894
  return {
46347
- available: peers.length > 0,
46895
+ available: summary !== null,
46896
+ running: summary?.running ?? false,
46897
+ backendState: summary?.backendState ?? null,
46898
+ health: summary?.health ?? [],
46348
46899
  peers,
46349
46900
  onlineCount: peers.filter((p) => p.online).length
46350
46901
  };
@@ -46360,6 +46911,9 @@ function computeWarnings(health, localNode, nodes, tailscale) {
46360
46911
  } else if (localNode?.advertiseScope === "mesh" && localNode.brokerUrl && isLoopbackBrokerUrl(localNode.brokerUrl)) {
46361
46912
  warnings.push("Broker advertises mesh scope but is bound to loopback \u2014 peers cannot reach it. " + "Unset OPENSCOUT_BROKER_HOST (mesh default is 0.0.0.0) or use your Tailscale IP.");
46362
46913
  }
46914
+ if (tailscale.available && !tailscale.running) {
46915
+ warnings.push("Tailscale is installed but currently stopped on this machine. " + "Cached peers may appear, but the broker cannot reach them until Tailscale is running.");
46916
+ }
46363
46917
  const remoteNodes = Object.values(nodes).filter((n) => n.id !== localNode?.id);
46364
46918
  if (!tailscale.available && remoteNodes.length === 0) {
46365
46919
  warnings.push("No Tailscale peers found and no mesh seeds configured. " + "Install Tailscale or set OPENSCOUT_MESH_SEEDS.");
@@ -46525,7 +47079,7 @@ function renderMeshStatus(report) {
46525
47079
  }
46526
47080
  lines.push(` Broker: ${report.brokerUrl}`);
46527
47081
  lines.push(` Scope: ${report.localNode?.advertiseScope === "mesh" ? "mesh-reachable" : "local-only"}`);
46528
- const tsLabel = report.tailscale.available ? `available (${report.tailscale.onlineCount} peer${report.tailscale.onlineCount === 1 ? "" : "s"} online)` : "not available";
47082
+ const tsLabel = !report.tailscale.available ? "not available" : report.tailscale.running ? `running (${report.tailscale.onlineCount} peer${report.tailscale.onlineCount === 1 ? "" : "s"} online)` : `not running (${report.tailscale.backendState ?? "unknown"})`;
46529
47083
  lines.push(` Tailscale: ${tsLabel}`);
46530
47084
  const allNodes = Object.values(report.nodes);
46531
47085
  const remoteNodes = allNodes.filter((n) => n.id !== report.localNode?.id);
@@ -46559,7 +47113,8 @@ function renderMeshDoctor(report) {
46559
47113
  lines.push(` ID: ${report.localNode.id}`);
46560
47114
  lines.push(` Name: ${report.localNode.name}`);
46561
47115
  lines.push(` Mesh: ${report.localNode.meshId}`);
46562
- lines.push(` Broker URL: ${report.brokerUrl}`);
47116
+ lines.push(` Control URL: ${report.brokerUrl}`);
47117
+ lines.push(` Announced URL: ${report.localNode.brokerUrl ?? report.brokerUrl}`);
46563
47118
  lines.push(` Advertise scope: ${report.localNode.advertiseScope}`);
46564
47119
  if (report.localNode.hostName) {
46565
47120
  lines.push(` Hostname: ${report.localNode.hostName}`);
@@ -46570,7 +47125,11 @@ function renderMeshDoctor(report) {
46570
47125
  lines.push("");
46571
47126
  lines.push("Tailscale:");
46572
47127
  if (report.tailscale.available) {
46573
- lines.push(` Status: available (${report.tailscale.peers.length} peer${report.tailscale.peers.length === 1 ? "" : "s"})`);
47128
+ const state = report.tailscale.backendState ?? "unknown";
47129
+ lines.push(` Status: ${report.tailscale.running ? `running (${state})` : `not running (${state})`} (${report.tailscale.peers.length} peer${report.tailscale.peers.length === 1 ? "" : "s"})`);
47130
+ for (const detail of report.tailscale.health) {
47131
+ lines.push(` ! ${detail}`);
47132
+ }
46574
47133
  for (const peer of report.tailscale.peers) {
46575
47134
  const ips = peer.addresses.join(", ");
46576
47135
  const status = peer.online ? "online" : "offline";
@@ -46598,7 +47157,7 @@ function renderMeshDoctor(report) {
46598
47157
  }
46599
47158
  }
46600
47159
  lines.push("");
46601
- lines.push("Configuration:");
47160
+ lines.push("Client environment (this shell):");
46602
47161
  const env = report.envVars;
46603
47162
  lines.push(` OPENSCOUT_MESH_ID: ${env.meshId ?? "(default: openscout)"}`);
46604
47163
  lines.push(` OPENSCOUT_MESH_SEEDS: ${env.meshSeeds || "(not set)"}`);
@@ -46741,7 +47300,7 @@ var init_mesh2 = __esm(async () => {
46741
47300
  });
46742
47301
 
46743
47302
  // ../../apps/desktop/src/core/pairing/runtime/config.ts
46744
- import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
47303
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
46745
47304
  import { homedir as homedir12 } from "os";
46746
47305
  import path from "path";
46747
47306
  function pairingPaths() {
@@ -46758,11 +47317,11 @@ function pairingPaths() {
46758
47317
  }
46759
47318
  function loadPairingConfig() {
46760
47319
  const { configPath } = pairingPaths();
46761
- if (!existsSync14(configPath)) {
47320
+ if (!existsSync15(configPath)) {
46762
47321
  return {};
46763
47322
  }
46764
47323
  try {
46765
- const payload = JSON.parse(readFileSync7(configPath, "utf8"));
47324
+ const payload = JSON.parse(readFileSync8(configPath, "utf8"));
46766
47325
  return typeof payload === "object" && payload ? payload : {};
46767
47326
  } catch {
46768
47327
  return {};
@@ -48845,17 +49404,17 @@ var init_noise = __esm(() => {
48845
49404
  });
48846
49405
 
48847
49406
  // ../../apps/desktop/src/core/pairing/runtime/security/identity.ts
48848
- import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
49407
+ import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
48849
49408
  import { join as join20 } from "path";
48850
49409
  import { homedir as homedir13 } from "os";
48851
49410
  function loadOrCreateIdentity() {
48852
- if (existsSync15(IDENTITY_FILE)) {
49411
+ if (existsSync16(IDENTITY_FILE)) {
48853
49412
  return loadIdentity();
48854
49413
  }
48855
49414
  return createAndSaveIdentity();
48856
49415
  }
48857
49416
  function loadIdentity() {
48858
- const data = JSON.parse(readFileSync8(IDENTITY_FILE, "utf8"));
49417
+ const data = JSON.parse(readFileSync9(IDENTITY_FILE, "utf8"));
48859
49418
  return {
48860
49419
  publicKey: hexToBytes2(data.publicKey),
48861
49420
  privateKey: hexToBytes2(data.privateKey)
@@ -48873,9 +49432,9 @@ function createAndSaveIdentity() {
48873
49432
  return keyPair;
48874
49433
  }
48875
49434
  function loadTrustedPeers() {
48876
- if (!existsSync15(TRUSTED_PEERS_FILE))
49435
+ if (!existsSync16(TRUSTED_PEERS_FILE))
48877
49436
  return new Map;
48878
- const data = JSON.parse(readFileSync8(TRUSTED_PEERS_FILE, "utf8"));
49437
+ const data = JSON.parse(readFileSync9(TRUSTED_PEERS_FILE, "utf8"));
48879
49438
  return new Map(data.map((p) => [p.publicKey, p]));
48880
49439
  }
48881
49440
  function saveTrustedPeer(peer) {
@@ -49056,14 +49615,14 @@ var init_security2 = __esm(() => {
49056
49615
  });
49057
49616
 
49058
49617
  // ../../apps/desktop/src/core/pairing/runtime/runtime-state.ts
49059
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync9, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync7 } from "fs";
49618
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync7 } from "fs";
49060
49619
  function readPairingRuntimeSnapshot() {
49061
49620
  const { runtimeStatePath } = pairingPaths();
49062
- if (!existsSync16(runtimeStatePath)) {
49621
+ if (!existsSync17(runtimeStatePath)) {
49063
49622
  return null;
49064
49623
  }
49065
49624
  try {
49066
- const parsed = JSON.parse(readFileSync9(runtimeStatePath, "utf8"));
49625
+ const parsed = JSON.parse(readFileSync10(runtimeStatePath, "utf8"));
49067
49626
  return parsed?.version === 1 ? parsed : null;
49068
49627
  } catch {
49069
49628
  return null;
@@ -49181,15 +49740,15 @@ var init_bridge = __esm(() => {
49181
49740
  });
49182
49741
 
49183
49742
  // ../../apps/desktop/src/core/pairing/runtime/bridge/config.ts
49184
- import { existsSync as existsSync17, readFileSync as readFileSync10 } from "fs";
49743
+ import { existsSync as existsSync18, readFileSync as readFileSync11 } from "fs";
49185
49744
  import { join as join22 } from "path";
49186
49745
  import { homedir as homedir15 } from "os";
49187
49746
  function loadConfigFile() {
49188
- if (!existsSync17(CONFIG_FILE)) {
49747
+ if (!existsSync18(CONFIG_FILE)) {
49189
49748
  return {};
49190
49749
  }
49191
49750
  try {
49192
- const raw = readFileSync10(CONFIG_FILE, "utf8");
49751
+ const raw = readFileSync11(CONFIG_FILE, "utf8");
49193
49752
  const parsed = JSON.parse(raw);
49194
49753
  return parsed;
49195
49754
  } catch (err) {
@@ -49250,9 +49809,74 @@ var init_config3 = __esm(() => {
49250
49809
  CONFIG_FILE = join22(CONFIG_DIR, "config.json");
49251
49810
  });
49252
49811
 
49812
+ // ../../apps/desktop/src/core/pairing/runtime/bridge/web-handoff.ts
49813
+ import { randomBytes as randomBytes2 } from "crypto";
49814
+ function pruneExpiredWebHandoffs(now = Date.now()) {
49815
+ for (const [token, record3] of activeWebHandoffs) {
49816
+ if (record3.expiresAt <= now) {
49817
+ activeWebHandoffs.delete(token);
49818
+ }
49819
+ }
49820
+ }
49821
+ function scopesMatch(left, right) {
49822
+ if (left.kind !== right.kind) {
49823
+ return false;
49824
+ }
49825
+ if (left.sessionId !== right.sessionId) {
49826
+ return false;
49827
+ }
49828
+ if (left.kind === "session") {
49829
+ return true;
49830
+ }
49831
+ return left.turnId === right.turnId && left.blockId === right.blockId;
49832
+ }
49833
+ function pathForWebHandoffScope(scope) {
49834
+ switch (scope.kind) {
49835
+ case "session":
49836
+ return `/handoff/session/${encodeURIComponent(scope.sessionId)}`;
49837
+ case "file_change":
49838
+ return `/handoff/file-change/${encodeURIComponent(scope.sessionId)}/${encodeURIComponent(scope.turnId)}/${encodeURIComponent(scope.blockId)}`;
49839
+ }
49840
+ }
49841
+ function issueWebHandoff(scope, deviceId) {
49842
+ pruneExpiredWebHandoffs();
49843
+ const token = randomBytes2(24).toString("base64url");
49844
+ const expiresAt = Date.now() + WEB_HANDOFF_TTL_MS;
49845
+ activeWebHandoffs.set(token, {
49846
+ token,
49847
+ deviceId: deviceId?.trim() || null,
49848
+ expiresAt,
49849
+ scope
49850
+ });
49851
+ return { token, expiresAt, scope };
49852
+ }
49853
+ function readAuthorizedWebHandoff(token, scope) {
49854
+ pruneExpiredWebHandoffs();
49855
+ if (!token) {
49856
+ return null;
49857
+ }
49858
+ const record3 = activeWebHandoffs.get(token);
49859
+ if (!record3) {
49860
+ return null;
49861
+ }
49862
+ if (!scopesMatch(record3.scope, scope)) {
49863
+ return null;
49864
+ }
49865
+ return record3;
49866
+ }
49867
+ var SCOUT_WEB_HANDOFF_COOKIE = "scout_handoff", WEB_HANDOFF_TTL_MS, activeWebHandoffs;
49868
+ var init_web_handoff = __esm(() => {
49869
+ WEB_HANDOFF_TTL_MS = 5 * 60 * 1000;
49870
+ activeWebHandoffs = new Map;
49871
+ });
49872
+
49253
49873
  // ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
49254
49874
  import { isAbsolute as isAbsolute2 } from "path";
49255
49875
  import { homedir as homedir16 } from "os";
49876
+ function isTrustedLoopbackHostname(hostname5) {
49877
+ const normalized = hostname5.trim().toLowerCase();
49878
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "::1" || LOOPBACK_IPV4_HOST_PATTERN.test(normalized);
49879
+ }
49256
49880
  function isAllowedPath(filePath) {
49257
49881
  if (!isAbsolute2(filePath))
49258
49882
  return false;
@@ -49263,7 +49887,7 @@ function isAllowedPath(filePath) {
49263
49887
  return ALLOWED_ROOTS.some((root) => filePath.startsWith(root));
49264
49888
  }
49265
49889
  function startFileServer(options) {
49266
- const { port } = options;
49890
+ const { port, bridge } = options;
49267
49891
  let server = null;
49268
49892
  function start() {
49269
49893
  try {
@@ -49271,7 +49895,7 @@ function startFileServer(options) {
49271
49895
  port,
49272
49896
  fetch(req) {
49273
49897
  try {
49274
- return route(new URL(req.url));
49898
+ return route(req, new URL(req.url), bridge);
49275
49899
  } catch (err) {
49276
49900
  console.error(`[fileserver] ${req.url} \u2192`, err.message);
49277
49901
  return new Response("Internal error", { status: 500 });
@@ -49294,12 +49918,19 @@ function startFileServer(options) {
49294
49918
  start();
49295
49919
  return { port, stop, restart };
49296
49920
  }
49297
- function route(url2) {
49921
+ function route(req, url2, bridge) {
49298
49922
  const path2 = url2.pathname;
49299
- if (path2 === "/file")
49923
+ if (path2 === "/file") {
49924
+ if (!isTrustedLoopbackHostname(url2.hostname)) {
49925
+ return new Response("Forbidden", { status: 403 });
49926
+ }
49300
49927
  return serveFile(url2);
49928
+ }
49301
49929
  if (path2 === "/health")
49302
49930
  return Response.json({ ok: true });
49931
+ if (path2.startsWith("/handoff/")) {
49932
+ return serveHandoffPage(req, url2, bridge);
49933
+ }
49303
49934
  return new Response("Pairing file server", { status: 200 });
49304
49935
  }
49305
49936
  function serveFile(url2) {
@@ -49313,9 +49944,525 @@ function serveFile(url2) {
49313
49944
  return new Response("Not found", { status: 404 });
49314
49945
  return new Response(file2);
49315
49946
  }
49316
- var ALLOWED_ROOTS;
49947
+ function serveHandoffPage(req, url2, bridge) {
49948
+ if (!bridge) {
49949
+ return new Response("Secure handoff unavailable", { status: 503 });
49950
+ }
49951
+ const scope = parseHandoffScope(url2);
49952
+ if (!scope) {
49953
+ return new Response("Not found", { status: 404 });
49954
+ }
49955
+ const token = readHandoffToken(req);
49956
+ const authorized = readAuthorizedWebHandoff(token, scope);
49957
+ if (!authorized) {
49958
+ return unauthorizedHandoffResponse();
49959
+ }
49960
+ let html;
49961
+ switch (scope.kind) {
49962
+ case "session": {
49963
+ const snapshot = bridge.getSessionSnapshot(scope.sessionId);
49964
+ if (!snapshot) {
49965
+ return new Response("Session handoff expired", { status: 404 });
49966
+ }
49967
+ html = renderSessionHandoffPage(snapshot);
49968
+ break;
49969
+ }
49970
+ case "file_change": {
49971
+ const snapshot = bridge.getSessionSnapshot(scope.sessionId);
49972
+ if (!snapshot) {
49973
+ return new Response("Session handoff expired", { status: 404 });
49974
+ }
49975
+ const target = findFileChangeBlock(snapshot, scope.turnId, scope.blockId);
49976
+ if (!target) {
49977
+ return new Response("File change handoff expired", { status: 404 });
49978
+ }
49979
+ html = renderFileChangeHandoffPage(snapshot, target.turn, target.block);
49980
+ break;
49981
+ }
49982
+ }
49983
+ const headers = new Headers({
49984
+ "content-type": "text/html; charset=utf-8",
49985
+ "cache-control": "no-store",
49986
+ "x-content-type-options": "nosniff"
49987
+ });
49988
+ headers.set("set-cookie", `${SCOUT_WEB_HANDOFF_COOKIE}=${authorized.token}; Max-Age=300; Path=/handoff; HttpOnly; SameSite=Strict`);
49989
+ return new Response(html, { status: 200, headers });
49990
+ }
49991
+ function unauthorizedHandoffResponse() {
49992
+ return new Response("Secure handoff required", {
49993
+ status: 401,
49994
+ headers: {
49995
+ "cache-control": "no-store",
49996
+ "set-cookie": `${SCOUT_WEB_HANDOFF_COOKIE}=; Max-Age=0; Path=/handoff; HttpOnly; SameSite=Strict`
49997
+ }
49998
+ });
49999
+ }
50000
+ function parseHandoffScope(url2) {
50001
+ const parts = url2.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part));
50002
+ if (parts[0] !== "handoff") {
50003
+ return null;
50004
+ }
50005
+ if (parts[1] === "session" && parts[2]) {
50006
+ return { kind: "session", sessionId: parts[2] };
50007
+ }
50008
+ if (parts[1] === "file-change" && parts[2] && parts[3] && parts[4]) {
50009
+ return {
50010
+ kind: "file_change",
50011
+ sessionId: parts[2],
50012
+ turnId: parts[3],
50013
+ blockId: parts[4]
50014
+ };
50015
+ }
50016
+ return null;
50017
+ }
50018
+ function readHandoffToken(req) {
50019
+ const headerToken = req.headers.get("x-scout-handoff-token")?.trim();
50020
+ if (headerToken) {
50021
+ return headerToken;
50022
+ }
50023
+ const cookieHeader = req.headers.get("cookie");
50024
+ if (!cookieHeader) {
50025
+ return null;
50026
+ }
50027
+ for (const part of cookieHeader.split(";")) {
50028
+ const [name, ...rest] = part.trim().split("=");
50029
+ if (name === SCOUT_WEB_HANDOFF_COOKIE) {
50030
+ const value = rest.join("=").trim();
50031
+ return value || null;
50032
+ }
50033
+ }
50034
+ return null;
50035
+ }
50036
+ function findFileChangeBlock(snapshot, turnId, blockId) {
50037
+ const turn = snapshot.turns.find((candidate) => candidate.id === turnId);
50038
+ if (!turn) {
50039
+ return null;
50040
+ }
50041
+ const blockState = turn.blocks.find((candidate) => candidate.block.id === blockId);
50042
+ const block = blockState?.block;
50043
+ if (!block || block.type !== "action" || block.action.kind !== "file_change") {
50044
+ return null;
50045
+ }
50046
+ return { turn, block };
50047
+ }
50048
+ function renderPage(title, body) {
50049
+ return `<!doctype html>
50050
+ <html lang="en">
50051
+ <head>
50052
+ <meta charset="utf-8" />
50053
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
50054
+ <title>${escapeHtml(title)}</title>
50055
+ <style>
50056
+ :root {
50057
+ color-scheme: dark;
50058
+ --bg: #0b0f14;
50059
+ --panel: rgba(19, 27, 39, 0.92);
50060
+ --panel-strong: rgba(12, 18, 28, 0.96);
50061
+ --line: rgba(148, 163, 184, 0.16);
50062
+ --line-strong: rgba(148, 163, 184, 0.24);
50063
+ --text: #e5ecf5;
50064
+ --muted: #90a0b4;
50065
+ --accent: #63d0ff;
50066
+ --green: #54d38a;
50067
+ --amber: #f8c36a;
50068
+ --red: #ff7b72;
50069
+ --surface-add: rgba(84, 211, 138, 0.08);
50070
+ --surface-del: rgba(255, 123, 114, 0.08);
50071
+ }
50072
+ * { box-sizing: border-box; }
50073
+ body {
50074
+ margin: 0;
50075
+ background:
50076
+ radial-gradient(circle at top left, rgba(99, 208, 255, 0.12), transparent 32%),
50077
+ linear-gradient(180deg, #090d12 0%, #0b0f14 55%, #111723 100%);
50078
+ color: var(--text);
50079
+ font: 14px/1.5 ui-sans-serif, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
50080
+ }
50081
+ .wrap {
50082
+ width: min(980px, calc(100vw - 24px));
50083
+ margin: 0 auto;
50084
+ padding: 20px 0 40px;
50085
+ }
50086
+ .hero, .panel {
50087
+ background: var(--panel);
50088
+ border: 1px solid var(--line);
50089
+ border-radius: 18px;
50090
+ backdrop-filter: blur(18px);
50091
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.28);
50092
+ }
50093
+ .hero {
50094
+ padding: 18px 18px 16px;
50095
+ margin-bottom: 14px;
50096
+ }
50097
+ .eyebrow {
50098
+ display: inline-flex;
50099
+ align-items: center;
50100
+ gap: 8px;
50101
+ color: var(--muted);
50102
+ font-size: 11px;
50103
+ letter-spacing: 0.14em;
50104
+ text-transform: uppercase;
50105
+ }
50106
+ .dot {
50107
+ width: 8px;
50108
+ height: 8px;
50109
+ border-radius: 999px;
50110
+ background: var(--accent);
50111
+ box-shadow: 0 0 14px rgba(99, 208, 255, 0.65);
50112
+ }
50113
+ h1 {
50114
+ margin: 12px 0 10px;
50115
+ font-size: 24px;
50116
+ line-height: 1.15;
50117
+ }
50118
+ .meta, .stack {
50119
+ display: flex;
50120
+ flex-wrap: wrap;
50121
+ gap: 8px;
50122
+ }
50123
+ .pill {
50124
+ display: inline-flex;
50125
+ align-items: center;
50126
+ gap: 6px;
50127
+ padding: 5px 10px;
50128
+ border-radius: 999px;
50129
+ border: 1px solid var(--line-strong);
50130
+ background: rgba(255, 255, 255, 0.03);
50131
+ color: var(--muted);
50132
+ font-size: 12px;
50133
+ }
50134
+ .pill strong {
50135
+ color: var(--text);
50136
+ font-weight: 600;
50137
+ }
50138
+ .stack { flex-direction: column; }
50139
+ .panel {
50140
+ margin-top: 12px;
50141
+ overflow: hidden;
50142
+ }
50143
+ .panel-inner {
50144
+ padding: 16px;
50145
+ }
50146
+ .turn {
50147
+ border-top: 1px solid var(--line);
50148
+ }
50149
+ .turn:first-child {
50150
+ border-top: none;
50151
+ }
50152
+ .turn-head {
50153
+ padding: 14px 16px;
50154
+ display: flex;
50155
+ justify-content: space-between;
50156
+ gap: 12px;
50157
+ align-items: center;
50158
+ background: rgba(255, 255, 255, 0.02);
50159
+ }
50160
+ .turn-title {
50161
+ font-size: 13px;
50162
+ font-weight: 600;
50163
+ letter-spacing: 0.02em;
50164
+ }
50165
+ .turn-subtitle {
50166
+ color: var(--muted);
50167
+ font-size: 12px;
50168
+ margin-top: 2px;
50169
+ }
50170
+ .blocks {
50171
+ padding: 8px;
50172
+ }
50173
+ .block {
50174
+ padding: 12px;
50175
+ border-radius: 14px;
50176
+ border: 1px solid var(--line);
50177
+ background: var(--panel-strong);
50178
+ margin: 8px;
50179
+ }
50180
+ .block-head {
50181
+ display: flex;
50182
+ justify-content: space-between;
50183
+ align-items: center;
50184
+ gap: 12px;
50185
+ margin-bottom: 10px;
50186
+ }
50187
+ .block-type {
50188
+ color: var(--muted);
50189
+ text-transform: uppercase;
50190
+ letter-spacing: 0.12em;
50191
+ font-size: 11px;
50192
+ }
50193
+ .label {
50194
+ color: var(--muted);
50195
+ font-size: 12px;
50196
+ }
50197
+ .label strong {
50198
+ color: var(--text);
50199
+ }
50200
+ pre {
50201
+ margin: 0;
50202
+ white-space: pre-wrap;
50203
+ word-break: break-word;
50204
+ overflow-wrap: anywhere;
50205
+ font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
50206
+ }
50207
+ .code {
50208
+ padding: 12px;
50209
+ border-radius: 12px;
50210
+ border: 1px solid var(--line);
50211
+ background: rgba(255, 255, 255, 0.02);
50212
+ }
50213
+ .diff-line-add { background: var(--surface-add); color: #8ef3b7; }
50214
+ .diff-line-del { background: var(--surface-del); color: #ff9d96; }
50215
+ .details {
50216
+ margin-top: 10px;
50217
+ border: 1px solid var(--line);
50218
+ border-radius: 12px;
50219
+ overflow: hidden;
50220
+ }
50221
+ details > summary {
50222
+ list-style: none;
50223
+ cursor: pointer;
50224
+ padding: 10px 12px;
50225
+ background: rgba(255, 255, 255, 0.03);
50226
+ color: var(--muted);
50227
+ font-size: 12px;
50228
+ text-transform: uppercase;
50229
+ letter-spacing: 0.1em;
50230
+ }
50231
+ details[open] > summary {
50232
+ border-bottom: 1px solid var(--line);
50233
+ }
50234
+ .empty {
50235
+ padding: 28px 18px;
50236
+ color: var(--muted);
50237
+ }
50238
+ .grid {
50239
+ display: grid;
50240
+ gap: 10px;
50241
+ }
50242
+ .grid.two {
50243
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
50244
+ }
50245
+ a.inline {
50246
+ color: var(--accent);
50247
+ text-decoration: none;
50248
+ }
50249
+ .status-streaming { color: var(--amber); }
50250
+ .status-completed { color: var(--green); }
50251
+ .status-failed, .status-error { color: var(--red); }
50252
+ </style>
50253
+ </head>
50254
+ <body>
50255
+ <main class="wrap">
50256
+ ${body}
50257
+ </main>
50258
+ </body>
50259
+ </html>`;
50260
+ }
50261
+ function renderSessionHandoffPage(snapshot) {
50262
+ const title = snapshot.session.name || snapshot.session.id;
50263
+ const hero = `
50264
+ <section class="hero">
50265
+ <div class="eyebrow"><span class="dot"></span> Secure Proxy Session Handoff</div>
50266
+ <h1>${escapeHtml(title)}</h1>
50267
+ <div class="meta">
50268
+ <span class="pill"><strong>ID</strong> ${escapeHtml(snapshot.session.id)}</span>
50269
+ <span class="pill"><strong>Adapter</strong> ${escapeHtml(snapshot.session.adapterType)}</span>
50270
+ <span class="pill"><strong>Status</strong> ${escapeHtml(snapshot.session.status)}</span>
50271
+ ${snapshot.session.model ? `<span class="pill"><strong>Model</strong> ${escapeHtml(snapshot.session.model)}</span>` : ""}
50272
+ ${snapshot.session.cwd ? `<span class="pill"><strong>Workspace</strong> ${escapeHtml(snapshot.session.cwd)}</span>` : ""}
50273
+ </div>
50274
+ </section>
50275
+ `;
50276
+ const turns = snapshot.turns.length > 0 ? snapshot.turns.map((turn) => {
50277
+ const turnBody = turn.blocks.length > 0 ? turn.blocks.map(({ block }) => renderBlock(block)).join("") : `<div class="empty">No blocks were captured for this turn.</div>`;
50278
+ return `
50279
+ <section class="turn">
50280
+ <div class="turn-head">
50281
+ <div>
50282
+ <div class="turn-title">${escapeHtml(turn.id)}</div>
50283
+ <div class="turn-subtitle">${formatTimestamp(turn.startedAt)}${turn.endedAt ? ` to ${formatTimestamp(turn.endedAt)}` : ""}</div>
50284
+ </div>
50285
+ <span class="pill"><strong>Turn</strong> <span class="status-${escapeHtml(turn.status)}">${escapeHtml(turn.status)}</span></span>
50286
+ </div>
50287
+ <div class="blocks">${turnBody}</div>
50288
+ </section>
50289
+ `;
50290
+ }).join("") : `<div class="empty">This session has not produced any turns yet.</div>`;
50291
+ return renderPage(title, `${hero}<section class="panel">${turns}</section>`);
50292
+ }
50293
+ function renderFileChangeHandoffPage(snapshot, turn, block) {
50294
+ const action = block.action;
50295
+ const title = action.path || snapshot.session.name || snapshot.session.id;
50296
+ const hero = `
50297
+ <section class="hero">
50298
+ <div class="eyebrow"><span class="dot"></span> Secure Proxy File Change</div>
50299
+ <h1>${escapeHtml(title)}</h1>
50300
+ <div class="meta">
50301
+ <span class="pill"><strong>Session</strong> ${escapeHtml(snapshot.session.name || snapshot.session.id)}</span>
50302
+ <span class="pill"><strong>Turn</strong> ${escapeHtml(turn.id)}</span>
50303
+ <span class="pill"><strong>Status</strong> ${escapeHtml(action.status)}</span>
50304
+ </div>
50305
+ </section>
50306
+ `;
50307
+ const body = `
50308
+ <section class="panel">
50309
+ <div class="panel-inner stack">
50310
+ <div class="grid two">
50311
+ <div class="pill"><strong>Path</strong> ${escapeHtml(action.path)}</div>
50312
+ <div class="pill"><strong>Block</strong> ${escapeHtml(block.id)}</div>
50313
+ </div>
50314
+ ${action.diff ? renderDetailsSection("Diff", renderDiff(action.diff), true) : `<div class="empty">No diff was recorded for this action.</div>`}
50315
+ ${action.output ? renderDetailsSection("Output", `<div class="code"><pre>${escapeHtml(action.output)}</pre></div>`) : ""}
50316
+ </div>
50317
+ </section>
50318
+ `;
50319
+ return renderPage(title, `${hero}${body}`);
50320
+ }
50321
+ function renderBlock(block) {
50322
+ switch (block.type) {
50323
+ case "text":
50324
+ return renderTextLikeBlock("Text", block.text);
50325
+ case "reasoning":
50326
+ return renderTextLikeBlock("Reasoning", block.text);
50327
+ case "error":
50328
+ return `
50329
+ <article class="block">
50330
+ <div class="block-head">
50331
+ <div class="block-type">Error</div>
50332
+ <span class="pill status-error">${escapeHtml(block.status)}</span>
50333
+ </div>
50334
+ <div class="code"><pre>${escapeHtml(block.message)}</pre></div>
50335
+ </article>
50336
+ `;
50337
+ case "file":
50338
+ return `
50339
+ <article class="block">
50340
+ <div class="block-head">
50341
+ <div class="block-type">File</div>
50342
+ <span class="pill">${escapeHtml(block.status)}</span>
50343
+ </div>
50344
+ <div class="grid">
50345
+ ${block.name ? `<div class="label"><strong>Name</strong> ${escapeHtml(block.name)}</div>` : ""}
50346
+ <div class="label"><strong>MIME</strong> ${escapeHtml(block.mimeType)}</div>
50347
+ </div>
50348
+ </article>
50349
+ `;
50350
+ case "question":
50351
+ return renderQuestionBlock(block);
50352
+ case "action":
50353
+ return renderActionBlock(block);
50354
+ }
50355
+ }
50356
+ function renderTextLikeBlock(label, text) {
50357
+ return `
50358
+ <article class="block">
50359
+ <div class="block-head">
50360
+ <div class="block-type">${escapeHtml(label)}</div>
50361
+ </div>
50362
+ <div class="code"><pre>${escapeHtml(text)}</pre></div>
50363
+ </article>
50364
+ `;
50365
+ }
50366
+ function renderQuestionBlock(block) {
50367
+ const answer = block.answer?.length ? block.answer.join(", ") : "Awaiting answer";
50368
+ const options = block.options?.length ? `<div class="label"><strong>Options</strong> ${escapeHtml(block.options.map((option) => option.label).join(", "))}</div>` : "";
50369
+ return `
50370
+ <article class="block">
50371
+ <div class="block-head">
50372
+ <div class="block-type">Question</div>
50373
+ <span class="pill">${escapeHtml(block.questionStatus)}</span>
50374
+ </div>
50375
+ ${block.header ? `<div class="label"><strong>${escapeHtml(block.header)}</strong></div>` : ""}
50376
+ <div class="code"><pre>${escapeHtml(block.question)}</pre></div>
50377
+ <div class="stack" style="margin-top: 10px;">
50378
+ ${options}
50379
+ <div class="label"><strong>Answer</strong> ${escapeHtml(answer)}</div>
50380
+ </div>
50381
+ </article>
50382
+ `;
50383
+ }
50384
+ function renderActionBlock(block) {
50385
+ const action = block.action;
50386
+ const parts = [];
50387
+ switch (action.kind) {
50388
+ case "file_change":
50389
+ parts.push(`<div class="label"><strong>Path</strong> ${escapeHtml(action.path)}</div>`);
50390
+ if (action.diff) {
50391
+ parts.push(renderDetailsSection("Diff", renderDiff(action.diff)));
50392
+ }
50393
+ break;
50394
+ case "command":
50395
+ parts.push(`<div class="label"><strong>Command</strong></div><div class="code"><pre>${escapeHtml(action.command)}</pre></div>`);
50396
+ if (typeof action.exitCode === "number") {
50397
+ parts.push(`<div class="label"><strong>Exit Code</strong> ${escapeHtml(String(action.exitCode))}</div>`);
50398
+ }
50399
+ break;
50400
+ case "tool_call":
50401
+ parts.push(`<div class="label"><strong>Tool</strong> ${escapeHtml(action.toolName)}</div>`);
50402
+ if (action.toolCallId) {
50403
+ parts.push(`<div class="label"><strong>Call ID</strong> ${escapeHtml(action.toolCallId)}</div>`);
50404
+ }
50405
+ break;
50406
+ case "subagent":
50407
+ parts.push(`<div class="label"><strong>Agent</strong> ${escapeHtml(action.agentName || action.agentId)}</div>`);
50408
+ if (action.prompt) {
50409
+ parts.push(`<div class="code"><pre>${escapeHtml(action.prompt)}</pre></div>`);
50410
+ }
50411
+ break;
50412
+ }
50413
+ if (action.approval) {
50414
+ parts.push(`<div class="label"><strong>Approval</strong> ${escapeHtml(action.approval.risk || "medium")} risk${action.approval.description ? ` - ${escapeHtml(action.approval.description)}` : ""}</div>`);
50415
+ }
50416
+ if (action.output) {
50417
+ parts.push(renderDetailsSection("Output", `<div class="code"><pre>${escapeHtml(action.output)}</pre></div>`));
50418
+ }
50419
+ return `
50420
+ <article class="block">
50421
+ <div class="block-head">
50422
+ <div class="block-type">${escapeHtml(action.kind.replace("_", " "))}</div>
50423
+ <span class="pill">${escapeHtml(action.status)}</span>
50424
+ </div>
50425
+ <div class="stack">${parts.join("")}</div>
50426
+ </article>
50427
+ `;
50428
+ }
50429
+ function renderDetailsSection(title, innerHtml, open2 = false) {
50430
+ return `
50431
+ <details class="details"${open2 ? " open" : ""}>
50432
+ <summary>${escapeHtml(title)}</summary>
50433
+ <div class="panel-inner">${innerHtml}</div>
50434
+ </details>
50435
+ `;
50436
+ }
50437
+ function renderDiff(diff) {
50438
+ const lines = diff.split(`
50439
+ `).map((line) => {
50440
+ const className = line.startsWith("+") ? "diff-line-add" : line.startsWith("-") ? "diff-line-del" : "";
50441
+ return `<div class="${className}"><pre>${escapeHtml(line || " ")}</pre></div>`;
50442
+ }).join("");
50443
+ return `<div class="code">${lines}</div>`;
50444
+ }
50445
+ function escapeHtml(value) {
50446
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
50447
+ }
50448
+ function formatTimestamp(value) {
50449
+ if (typeof value !== "number" || !Number.isFinite(value)) {
50450
+ return "Unknown time";
50451
+ }
50452
+ try {
50453
+ return new Intl.DateTimeFormat("en-US", {
50454
+ dateStyle: "medium",
50455
+ timeStyle: "short"
50456
+ }).format(value);
50457
+ } catch {
50458
+ return new Date(value).toISOString();
50459
+ }
50460
+ }
50461
+ var ALLOWED_ROOTS, LOOPBACK_IPV4_HOST_PATTERN;
49317
50462
  var init_fileserver = __esm(() => {
50463
+ init_web_handoff();
49318
50464
  ALLOWED_ROOTS = [homedir16(), "/tmp"];
50465
+ LOOPBACK_IPV4_HOST_PATTERN = /^127(?:\.\d{1,3}){3}$/;
49319
50466
  });
49320
50467
 
49321
50468
  // ../../apps/desktop/src/server/db-queries.ts
@@ -50102,7 +51249,7 @@ async function deriveNewAgentName(projectName, branch, harness) {
50102
51249
  async function createGitWorktree(projectRoot, agentName) {
50103
51250
  const { execSync: execSync2 } = await import("child_process");
50104
51251
  const { join: join24 } = await import("path");
50105
- const { mkdirSync: mkdirSync9, existsSync: existsSync18 } = await import("fs");
51252
+ const { mkdirSync: mkdirSync9, existsSync: existsSync19 } = await import("fs");
50106
51253
  try {
50107
51254
  execSync2("git rev-parse --git-dir", { cwd: projectRoot, stdio: "pipe" });
50108
51255
  } catch {
@@ -50111,7 +51258,7 @@ async function createGitWorktree(projectRoot, agentName) {
50111
51258
  const branchName = `scout/${agentName}`;
50112
51259
  const worktreeDir = join24(projectRoot, ".scout-worktrees");
50113
51260
  const worktreePath = join24(worktreeDir, agentName);
50114
- if (existsSync18(worktreePath)) {
51261
+ if (existsSync19(worktreePath)) {
50115
51262
  return { path: worktreePath, branch: branchName };
50116
51263
  }
50117
51264
  mkdirSync9(worktreeDir, { recursive: true });
@@ -50154,7 +51301,7 @@ var init_service5 = __esm(async () => {
50154
51301
  });
50155
51302
 
50156
51303
  // ../../apps/desktop/src/core/pairing/runtime/bridge/server.ts
50157
- import { readdirSync as readdirSync2, readFileSync as readFileSync11, realpathSync, statSync as statSync3 } from "fs";
51304
+ import { readdirSync as readdirSync2, readFileSync as readFileSync12, realpathSync, statSync as statSync3 } from "fs";
50158
51305
  import { execSync as execSync2 } from "child_process";
50159
51306
  import { basename as basename9, isAbsolute as isAbsolute3, join as join24, relative as relative2 } from "path";
50160
51307
  import { homedir as homedir18 } from "os";
@@ -50167,6 +51314,20 @@ function readSyncStatus(bridge, sessionId) {
50167
51314
  oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
50168
51315
  };
50169
51316
  }
51317
+ function resolveSyncSessionId(bridge, preferredSessionId) {
51318
+ if (preferredSessionId) {
51319
+ return preferredSessionId;
51320
+ }
51321
+ let latestSessionId = null;
51322
+ let latestActivityAt = Number.NEGATIVE_INFINITY;
51323
+ for (const session of bridge.getSessionSummaries()) {
51324
+ if (session.lastActivityAt > latestActivityAt) {
51325
+ latestActivityAt = session.lastActivityAt;
51326
+ latestSessionId = session.sessionId;
51327
+ }
51328
+ }
51329
+ return latestSessionId;
51330
+ }
50170
51331
  function sessionRegistryErrorResponse(reqId, error48) {
50171
51332
  if (!isSessionRegistryError(error48)) {
50172
51333
  return null;
@@ -50236,23 +51397,36 @@ async function handleRPCInner(bridge, req, deviceId) {
50236
51397
  return { id: req.id, result: { ok: true } };
50237
51398
  }
50238
51399
  case "sync/replay": {
50239
- const p = req.params;
50240
- if (!p.sessionId) {
50241
- return { id: req.id, error: { code: -32602, message: "sessionId is required" } };
51400
+ const p = req.params ?? {};
51401
+ if (typeof p.lastSeq !== "number") {
51402
+ return { id: req.id, error: { code: -32602, message: "lastSeq is required" } };
51403
+ }
51404
+ const sessionId = resolveSyncSessionId(bridge, p.sessionId);
51405
+ if (!sessionId) {
51406
+ return { id: req.id, result: { events: [] } };
50242
51407
  }
50243
- const events2 = replaySyncEvents(bridge, p.sessionId, p.lastSeq);
51408
+ const events2 = replaySyncEvents(bridge, sessionId, p.lastSeq);
50244
51409
  return { id: req.id, result: { events: events2 } };
50245
51410
  }
50246
51411
  case "sync/status": {
50247
51412
  const p = req.params ?? {};
50248
- if (!p.sessionId) {
50249
- return { id: req.id, error: { code: -32602, message: "sessionId is required" } };
51413
+ const sessionCount = bridge.listSessions().length;
51414
+ const sessionId = resolveSyncSessionId(bridge, p.sessionId);
51415
+ if (!sessionId) {
51416
+ return {
51417
+ id: req.id,
51418
+ result: {
51419
+ currentSeq: 0,
51420
+ oldestBufferedSeq: 0,
51421
+ sessionCount
51422
+ }
51423
+ };
50250
51424
  }
50251
51425
  return {
50252
51426
  id: req.id,
50253
51427
  result: {
50254
- ...readSyncStatus(bridge, p.sessionId),
50255
- sessionCount: bridge.listSessions().length
51428
+ ...readSyncStatus(bridge, sessionId),
51429
+ sessionCount
50256
51430
  }
50257
51431
  };
50258
51432
  }
@@ -50493,7 +51667,7 @@ async function handleRPCInner(bridge, req, deviceId) {
50493
51667
  return { id: req.id, error: { code: -32000, message: "Only .jsonl files can be read" } };
50494
51668
  }
50495
51669
  try {
50496
- const content = readFileSync11(p.path, "utf-8");
51670
+ const content = readFileSync12(p.path, "utf-8");
50497
51671
  const lines = content.split(`
50498
51672
  `).filter((l) => l.trim().length > 0);
50499
51673
  const trimmed = lines.length > 500 ? lines.slice(-500) : lines;
@@ -54747,7 +55921,7 @@ var init_thread_events2 = __esm(() => {
54747
55921
 
54748
55922
  // ../runtime/src/mobile-push.ts
54749
55923
  import { Database as Database2 } from "bun:sqlite";
54750
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync12 } from "fs";
55924
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync13 } from "fs";
54751
55925
  import { connect as connectHttp2 } from "http2";
54752
55926
  import { createPrivateKey, sign as signWithKey } from "crypto";
54753
55927
  import { dirname as dirname13, join as join25 } from "path";
@@ -54918,7 +56092,7 @@ function loadApnsCredentials() {
54918
56092
  privateKeyPem = Buffer.from(inlineBase64, "base64").toString("utf8");
54919
56093
  }
54920
56094
  if (!privateKeyPem && path2) {
54921
- privateKeyPem = readFileSync12(path2, "utf8");
56095
+ privateKeyPem = readFileSync13(path2, "utf8");
54922
56096
  }
54923
56097
  if (!teamId || !keyId || !privateKeyPem) {
54924
56098
  return null;
@@ -55142,7 +56316,7 @@ var init_src2 = __esm(async () => {
55142
56316
  });
55143
56317
 
55144
56318
  // ../../apps/desktop/src/core/pairing/runtime/bridge/router.ts
55145
- import { readFileSync as readFileSync13, readdirSync as readdirSync3, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
56319
+ import { readFileSync as readFileSync14, readdirSync as readdirSync3, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
55146
56320
  import { execSync as execSync3 } from "child_process";
55147
56321
  import { basename as basename10, isAbsolute as isAbsolute4, join as join26, relative as relative3 } from "path";
55148
56322
  import { homedir as homedir19 } from "os";
@@ -55432,6 +56606,20 @@ function readSyncStatus2(bridge, sessionId) {
55432
56606
  oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
55433
56607
  };
55434
56608
  }
56609
+ function resolveSyncSessionId2(bridge, preferredSessionId) {
56610
+ if (preferredSessionId) {
56611
+ return preferredSessionId;
56612
+ }
56613
+ let latestSessionId = null;
56614
+ let latestActivityAt = Number.NEGATIVE_INFINITY;
56615
+ for (const session2 of bridge.getSessionSummaries()) {
56616
+ if (session2.lastActivityAt > latestActivityAt) {
56617
+ latestActivityAt = session2.lastActivityAt;
56618
+ latestSessionId = session2.sessionId;
56619
+ }
56620
+ }
56621
+ return latestSessionId;
56622
+ }
55435
56623
  var t, logged, procedure, MARKER_FILES2, sessionRouter, mobileRouter, workspaceRouter, historyRouter, promptRouter, syncRouter, bridgeRouter;
55436
56624
  var init_router = __esm(async () => {
55437
56625
  init_dist3();
@@ -55440,6 +56628,7 @@ var init_router = __esm(async () => {
55440
56628
  init_log();
55441
56629
  init_config3();
55442
56630
  init_db_queries();
56631
+ init_web_handoff();
55443
56632
  await __promiseAll([
55444
56633
  init_service5(),
55445
56634
  init_src2(),
@@ -55647,6 +56836,64 @@ var init_router = __esm(async () => {
55647
56836
  limit: typeof input.limit === "number" ? input.limit : null
55648
56837
  }, resolveMobileCurrentDirectory2());
55649
56838
  }),
56839
+ webHandoff: procedure.input(exports_external.object({
56840
+ kind: exports_external.enum(["session", "file_change"]),
56841
+ sessionId: exports_external.string(),
56842
+ turnId: exports_external.string().optional(),
56843
+ blockId: exports_external.string().optional()
56844
+ })).mutation(({ input, ctx }) => {
56845
+ if (!ctx.deviceId) {
56846
+ throw new TRPCError({
56847
+ code: "UNAUTHORIZED",
56848
+ message: "Secure web handoff requires a paired mobile device"
56849
+ });
56850
+ }
56851
+ const snapshot = ctx.bridge.getSessionSnapshot(input.sessionId);
56852
+ if (!snapshot) {
56853
+ throw new TRPCError({
56854
+ code: "NOT_FOUND",
56855
+ message: `No session: ${input.sessionId}`
56856
+ });
56857
+ }
56858
+ let scope;
56859
+ let title = snapshot.session.name || snapshot.session.id;
56860
+ if (input.kind === "file_change") {
56861
+ if (!input.turnId || !input.blockId) {
56862
+ throw new TRPCError({
56863
+ code: "BAD_REQUEST",
56864
+ message: "turnId and blockId are required for file_change handoffs"
56865
+ });
56866
+ }
56867
+ const turn = snapshot.turns.find((candidate) => candidate.id === input.turnId);
56868
+ const block = turn?.blocks.find((candidate) => candidate.block.id === input.blockId)?.block;
56869
+ if (!turn || !block || block.type !== "action" || block.action.kind !== "file_change") {
56870
+ throw new TRPCError({
56871
+ code: "NOT_FOUND",
56872
+ message: "File change block not found"
56873
+ });
56874
+ }
56875
+ scope = {
56876
+ kind: "file_change",
56877
+ sessionId: input.sessionId,
56878
+ turnId: input.turnId,
56879
+ blockId: input.blockId
56880
+ };
56881
+ title = block.action.path || title;
56882
+ } else {
56883
+ scope = {
56884
+ kind: "session",
56885
+ sessionId: input.sessionId
56886
+ };
56887
+ }
56888
+ const issued = issueWebHandoff(scope, ctx.deviceId);
56889
+ return {
56890
+ kind: input.kind,
56891
+ path: pathForWebHandoffScope(scope),
56892
+ token: issued.token,
56893
+ expiresAt: issued.expiresAt,
56894
+ title
56895
+ };
56896
+ }),
55650
56897
  createSession: procedure.input(exports_external.object({
55651
56898
  workspaceId: exports_external.string(),
55652
56899
  harness: exports_external.string().optional(),
@@ -55822,7 +57069,7 @@ var init_router = __esm(async () => {
55822
57069
  });
55823
57070
  }
55824
57071
  try {
55825
- const content = readFileSync13(input.path, "utf-8");
57072
+ const content = readFileSync14(input.path, "utf-8");
55826
57073
  const lines = content.split(`
55827
57074
  `).filter((l) => l.trim().length > 0);
55828
57075
  const trimmed = lines.length > 500 ? lines.slice(-500) : lines;
@@ -55852,14 +57099,27 @@ var init_router = __esm(async () => {
55852
57099
  })
55853
57100
  });
55854
57101
  syncRouter = t.router({
55855
- replay: procedure.input(exports_external.object({ lastSeq: exports_external.number(), sessionId: exports_external.string() })).query(({ input, ctx }) => {
55856
- const events2 = replaySyncEvents2(ctx.bridge, input.sessionId, input.lastSeq);
57102
+ replay: procedure.input(exports_external.object({ lastSeq: exports_external.number(), sessionId: exports_external.string().optional() })).query(({ input, ctx }) => {
57103
+ const sessionId = resolveSyncSessionId2(ctx.bridge, input.sessionId);
57104
+ if (!sessionId) {
57105
+ return { events: [] };
57106
+ }
57107
+ const events2 = replaySyncEvents2(ctx.bridge, sessionId, input.lastSeq);
55857
57108
  return { events: events2 };
55858
57109
  }),
55859
- status: procedure.input(exports_external.object({ sessionId: exports_external.string() })).query(({ input, ctx }) => {
57110
+ status: procedure.input(exports_external.object({ sessionId: exports_external.string().optional() }).optional()).query(({ input, ctx }) => {
57111
+ const sessionCount = ctx.bridge.listSessions().length;
57112
+ const sessionId = resolveSyncSessionId2(ctx.bridge, input?.sessionId);
57113
+ if (!sessionId) {
57114
+ return {
57115
+ currentSeq: 0,
57116
+ oldestBufferedSeq: 0,
57117
+ sessionCount
57118
+ };
57119
+ }
55860
57120
  return {
55861
- ...readSyncStatus2(ctx.bridge, input.sessionId),
55862
- sessionCount: ctx.bridge.listSessions().length
57121
+ ...readSyncStatus2(ctx.bridge, sessionId),
57122
+ sessionCount
55863
57123
  };
55864
57124
  })
55865
57125
  });
@@ -60468,7 +61728,7 @@ async function startPairingRuntime(options) {
60468
61728
  secure: config2.secure,
60469
61729
  identity: config2.secure ? identity2 : undefined
60470
61730
  });
60471
- const fileServer = startFileServer({ port: config2.port + 2 });
61731
+ const fileServer = startFileServer({ port: config2.port + 2, bridge });
60472
61732
  const relayUrl = options?.relayUrl?.trim() || config2.relay || null;
60473
61733
  if (!relayUrl) {
60474
61734
  fileServer.stop();
@@ -60775,11 +62035,11 @@ var BRIDGE_ABSENCE_GRACE_MS = 30000, ROOM_IDLE_TIMEOUT_MS = 60000;
60775
62035
 
60776
62036
  // ../../apps/desktop/src/core/pairing/runtime/relay-runtime.ts
60777
62037
  import { execSync as execSync4 } from "child_process";
60778
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, readdirSync as readdirSync4 } from "fs";
62038
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, readdirSync as readdirSync4 } from "fs";
60779
62039
  import { homedir as homedir22 } from "os";
60780
62040
  import { join as join27 } from "path";
60781
62041
  function findStoredCerts() {
60782
- if (!existsSync18(PAIRING_DIR2)) {
62042
+ if (!existsSync19(PAIRING_DIR2)) {
60783
62043
  return null;
60784
62044
  }
60785
62045
  try {
@@ -60800,19 +62060,58 @@ function findStoredCerts() {
60800
62060
  return null;
60801
62061
  }
60802
62062
  }
60803
- function getTailscaleHostname() {
62063
+ function readTailscaleStatus2() {
60804
62064
  try {
60805
62065
  const output = execSync4("tailscale status --self=true --peers=false --json", {
60806
62066
  stdio: ["pipe", "pipe", "pipe"],
60807
62067
  timeout: 5000
60808
62068
  }).toString();
60809
62069
  const data = JSON.parse(output);
60810
- const dnsName = data?.Self?.DNSName ?? "";
60811
- return dnsName.replace(/\.$/, "") || null;
62070
+ const dnsName = typeof data.Self?.DNSName === "string" ? data.Self.DNSName.replace(/\.$/, "") : "";
62071
+ return {
62072
+ backendState: typeof data.BackendState === "string" ? data.BackendState : null,
62073
+ dnsName: dnsName || null,
62074
+ online: data.Self?.Online !== false,
62075
+ health: Array.isArray(data.Health) ? data.Health.filter((entry) => typeof entry === "string") : []
62076
+ };
60812
62077
  } catch {
60813
62078
  return null;
60814
62079
  }
60815
62080
  }
62081
+ function resolveRelayEndpointForTailscaleStatus(port, tailscale2) {
62082
+ const backendState = tailscale2?.backendState?.trim().toLowerCase() ?? "";
62083
+ const hostname5 = tailscale2?.dnsName ?? null;
62084
+ const tailscaleRunning = backendState === "running" && tailscale2?.online !== false && Boolean(hostname5);
62085
+ const tls = resolveTls(tailscaleRunning ? hostname5 : null);
62086
+ if (tailscaleRunning && hostname5 && tls) {
62087
+ return {
62088
+ relayUrl: `wss://${hostname5}:${port}`,
62089
+ options: { tls }
62090
+ };
62091
+ }
62092
+ if (tailscaleRunning && hostname5) {
62093
+ pairingLog.warn("relay", "tailscale is running without TLS; falling back to insecure websocket relay", {
62094
+ hostname: hostname5,
62095
+ port
62096
+ });
62097
+ return {
62098
+ relayUrl: `ws://${hostname5}:${port}`,
62099
+ options: {}
62100
+ };
62101
+ }
62102
+ if (hostname5 && backendState && backendState !== "running") {
62103
+ pairingLog.warn("relay", "tailscale hostname detected but tailscale is not running; using local-only relay endpoint", {
62104
+ hostname: hostname5,
62105
+ backendState,
62106
+ health: tailscale2?.health ?? [],
62107
+ port
62108
+ });
62109
+ }
62110
+ return {
62111
+ relayUrl: `ws://127.0.0.1:${port}`,
62112
+ options: {}
62113
+ };
62114
+ }
60816
62115
  function generateTailscaleCerts(hostname5) {
60817
62116
  mkdirSync11(PAIRING_DIR2, { recursive: true });
60818
62117
  const certPath = join27(PAIRING_DIR2, `${hostname5}.crt`);
@@ -60858,28 +62157,7 @@ function resolveTls(hostname5) {
60858
62157
  return generateTailscaleCerts(hostname5);
60859
62158
  }
60860
62159
  function resolveRelayEndpoint(port) {
60861
- const hostname5 = getTailscaleHostname();
60862
- const tls = resolveTls(hostname5);
60863
- if (hostname5 && tls) {
60864
- return {
60865
- relayUrl: `wss://${hostname5}:${port}`,
60866
- options: { tls }
60867
- };
60868
- }
60869
- if (hostname5) {
60870
- pairingLog.warn("relay", "tailscale hostname detected without TLS; falling back to insecure websocket relay", {
60871
- hostname: hostname5,
60872
- port
60873
- });
60874
- return {
60875
- relayUrl: `ws://${hostname5}:${port}`,
60876
- options: {}
60877
- };
60878
- }
60879
- return {
60880
- relayUrl: `ws://127.0.0.1:${port}`,
60881
- options: {}
60882
- };
62160
+ return resolveRelayEndpointForTailscaleStatus(port, readTailscaleStatus2());
60883
62161
  }
60884
62162
  function startManagedRelay(port = 7889) {
60885
62163
  const endpoint = resolveRelayEndpoint(port);
@@ -61831,11 +63109,13 @@ __export(exports_server, {
61831
63109
  runServerCommand: () => runServerCommand,
61832
63110
  resolveScoutWebServerEntry: () => resolveScoutWebServerEntry,
61833
63111
  resolveScoutControlPlaneWebServerEntry: () => resolveScoutControlPlaneWebServerEntry,
63112
+ resolveBunExecutable: () => resolveBunExecutable3,
61834
63113
  renderServerCommandHelp: () => renderServerCommandHelp,
61835
63114
  normalizeServerOpenPath: () => normalizeServerOpenPath
61836
63115
  });
61837
63116
  import { spawn as spawn5 } from "child_process";
61838
- import { existsSync as existsSync19 } from "fs";
63117
+ import { accessSync as accessSync2, constants as constants4, existsSync as existsSync20 } from "fs";
63118
+ import { homedir as homedir23 } from "os";
61839
63119
  import { dirname as dirname14, join as join28, resolve as resolve14 } from "path";
61840
63120
  import { fileURLToPath as fileURLToPath9 } from "url";
61841
63121
  function renderServerCommandHelp() {
@@ -61845,6 +63125,7 @@ function renderServerCommandHelp() {
61845
63125
  "Usage:",
61846
63126
  " scout server start [options]",
61847
63127
  " scout server open [options]",
63128
+ " scout server control-plane open [options] # legacy alias",
61848
63129
  "",
61849
63130
  "Subcommands:",
61850
63131
  " start Start the Scout web UI server.",
@@ -61868,14 +63149,18 @@ function resolveScoutWebServerEntry() {
61868
63149
  function resolveScoutControlPlaneWebServerEntry() {
61869
63150
  const mainDir = dirname14(fileURLToPath9(import.meta.url));
61870
63151
  const bundled = join28(mainDir, "scout-control-plane-web.mjs");
61871
- if (existsSync19(bundled)) {
63152
+ if (existsSync20(bundled)) {
61872
63153
  return bundled;
61873
63154
  }
61874
- const source = fileURLToPath9(new URL("../../server/control-plane-index.ts", import.meta.url));
61875
- if (existsSync19(source)) {
63155
+ const source = fileURLToPath9(new URL("../../../../../packages/web/server/index.ts", import.meta.url).href);
63156
+ if (existsSync20(source)) {
61876
63157
  return source;
61877
63158
  }
61878
- throw new ScoutCliError("Could not find Scout control-plane web server entry. Rebuild @openscout/scout or run from the OpenScout repository.");
63159
+ const legacySource = fileURLToPath9(new URL("../../server/control-plane-index.ts", import.meta.url).href);
63160
+ if (existsSync20(legacySource)) {
63161
+ return legacySource;
63162
+ }
63163
+ throw new ScoutCliError("Could not find Scout web server entry. Rebuild @openscout/scout or run from the OpenScout repository.");
61879
63164
  }
61880
63165
  function parseServerFlags(args) {
61881
63166
  const env = {};
@@ -61949,7 +63234,43 @@ function resolveBundledStaticClientRoot(entry, _mode) {
61949
63234
  const entryDir = dirname14(entry);
61950
63235
  const clientDirectory = join28(entryDir, "client");
61951
63236
  const indexPath = join28(clientDirectory, "index.html");
61952
- return existsSync19(indexPath) ? clientDirectory : null;
63237
+ return existsSync20(indexPath) ? clientDirectory : null;
63238
+ }
63239
+ function isExecutable4(filePath) {
63240
+ if (!filePath) {
63241
+ return false;
63242
+ }
63243
+ try {
63244
+ accessSync2(filePath, constants4.X_OK);
63245
+ return true;
63246
+ } catch {
63247
+ return false;
63248
+ }
63249
+ }
63250
+ function resolveBunExecutable3(env) {
63251
+ const explicitPaths = [
63252
+ env.SCOUT_BUN_BIN,
63253
+ env.OPENSCOUT_BUN_BIN,
63254
+ env.BUN_BIN
63255
+ ].filter((candidate) => Boolean(candidate?.trim()));
63256
+ for (const candidate of explicitPaths) {
63257
+ if (isExecutable4(candidate)) {
63258
+ return candidate;
63259
+ }
63260
+ }
63261
+ const pathEntries = (env.PATH ?? "").split(":").filter(Boolean);
63262
+ const commonDirectories = [
63263
+ join28(homedir23(), ".bun", "bin"),
63264
+ "/opt/homebrew/bin",
63265
+ "/usr/local/bin"
63266
+ ];
63267
+ for (const directory of [...pathEntries, ...commonDirectories]) {
63268
+ const candidate = join28(directory.replace(/^~(?=$|\/)/, homedir23()), "bun");
63269
+ if (isExecutable4(candidate)) {
63270
+ return candidate;
63271
+ }
63272
+ }
63273
+ return "bun";
61953
63274
  }
61954
63275
  function buildMergedServerEnv(entry, mode, flagEnv) {
61955
63276
  const bundledStaticClientRoot = resolveBundledStaticClientRoot(entry, mode);
@@ -61975,7 +63296,7 @@ function parseServerSelection(args) {
61975
63296
  action: "start",
61976
63297
  flagArgs: args.slice(1),
61977
63298
  entry: resolveScoutControlPlaneWebServerEntry(),
61978
- mode: "full"
63299
+ mode: "openscout-web"
61979
63300
  };
61980
63301
  }
61981
63302
  if (args[0] === "open") {
@@ -61983,7 +63304,7 @@ function parseServerSelection(args) {
61983
63304
  action: "open",
61984
63305
  flagArgs: args.slice(1),
61985
63306
  entry: resolveScoutControlPlaneWebServerEntry(),
61986
- mode: "full"
63307
+ mode: "openscout-web"
61987
63308
  };
61988
63309
  }
61989
63310
  if (args[0] === "control-plane") {
@@ -61992,7 +63313,7 @@ function parseServerSelection(args) {
61992
63313
  action: sub,
61993
63314
  flagArgs: args.slice(2),
61994
63315
  entry: resolveScoutControlPlaneWebServerEntry(),
61995
- mode: "control-plane"
63316
+ mode: "openscout-web"
61996
63317
  };
61997
63318
  }
61998
63319
  throw new ScoutCliError(`unknown subcommand: ${args[0]} (try: scout server open)`);
@@ -62028,7 +63349,7 @@ function resolveExpectedCurrentDirectory(env) {
62028
63349
  return resolve14(env.OPENSCOUT_SETUP_CWD?.trim() || process.cwd());
62029
63350
  }
62030
63351
  function renderModeLabel(mode) {
62031
- return mode === "control-plane" ? "Scout control plane" : "Scout";
63352
+ return "Scout web";
62032
63353
  }
62033
63354
  function renderSurfaceLabel(surface) {
62034
63355
  switch (surface) {
@@ -62036,14 +63357,13 @@ function renderSurfaceLabel(surface) {
62036
63357
  return "control-plane";
62037
63358
  case "openscout-web":
62038
63359
  return "@openscout/web";
62039
- case "full":
62040
- default:
62041
- return "full";
62042
63360
  }
62043
63361
  }
62044
63362
  function renderServerOpenResult(result) {
62045
- const prefix = result.mode === "control-plane" ? "Opened Scout control plane" : "Opened Scout";
62046
- return `${prefix} at ${result.url}${result.reusedExistingServer ? "" : " (started server)"}`;
63363
+ return `Opened Scout web at ${result.url}${result.reusedExistingServer ? "" : " (started server)"}`;
63364
+ }
63365
+ function isCurrentScoutWebSurface(surface) {
63366
+ return surface === "openscout-web" || surface === "control-plane";
62047
63367
  }
62048
63368
  function healthUrlForPort(port) {
62049
63369
  return new URL(`/api/health`, `http://127.0.0.1:${port}`);
@@ -62071,7 +63391,7 @@ async function probeScoutServer(port) {
62071
63391
  statusCode: response.status
62072
63392
  };
62073
63393
  }
62074
- if (body.ok === true && (body.surface === "full" || body.surface === "control-plane" || body.surface === "openscout-web") && typeof body.currentDirectory === "string") {
63394
+ if (body.ok === true && (body.surface === "control-plane" || body.surface === "openscout-web") && typeof body.currentDirectory === "string") {
62075
63395
  return {
62076
63396
  status: "healthy",
62077
63397
  health: {
@@ -62124,8 +63444,9 @@ async function openBrowser(url2) {
62124
63444
  });
62125
63445
  }
62126
63446
  async function spawnDetachedServer(entry, env) {
63447
+ const bunExecutable = resolveBunExecutable3(env);
62127
63448
  await new Promise((resolvePromise, rejectPromise) => {
62128
- const child = spawn5("bun", ["run", entry], {
63449
+ const child = spawn5(bunExecutable, ["run", entry], {
62129
63450
  detached: true,
62130
63451
  stdio: "ignore",
62131
63452
  env,
@@ -62150,7 +63471,7 @@ async function waitForScoutServer(port, mode, expectedCurrentDirectory) {
62150
63471
  const probe = await probeScoutServer(port);
62151
63472
  if (probe.status === "healthy") {
62152
63473
  const actualCurrentDirectory = resolve14(probe.health.currentDirectory);
62153
- if (probe.health.surface !== mode) {
63474
+ if (!isCurrentScoutWebSurface(probe.health.surface)) {
62154
63475
  throw new ScoutCliError(`port ${port} is serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(mode)}.`);
62155
63476
  }
62156
63477
  if (actualCurrentDirectory !== expectedCurrentDirectory) {
@@ -62172,7 +63493,7 @@ async function openScoutServer(options) {
62172
63493
  const probe = await probeScoutServer(port);
62173
63494
  if (probe.status === "healthy") {
62174
63495
  const actualCurrentDirectory = resolve14(probe.health.currentDirectory);
62175
- if (probe.health.surface !== options.mode) {
63496
+ if (!isCurrentScoutWebSurface(probe.health.surface)) {
62176
63497
  throw new ScoutCliError(`port ${port} is already serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(options.mode)}.`);
62177
63498
  }
62178
63499
  if (actualCurrentDirectory !== expectedCurrentDirectory) {
@@ -62217,8 +63538,9 @@ async function runServerCommand(context, args) {
62217
63538
  context.output.writeValue(result, renderServerOpenResult);
62218
63539
  return;
62219
63540
  }
63541
+ const bunExecutable = resolveBunExecutable3(mergedEnv);
62220
63542
  await new Promise((resolvePromise, rejectPromise) => {
62221
- const child = spawn5("bun", ["run", selection.entry], {
63543
+ const child = spawn5(bunExecutable, ["run", selection.entry], {
62222
63544
  stdio: "inherit",
62223
63545
  env: mergedEnv
62224
63546
  });
@@ -62370,7 +63692,7 @@ import { EventEmitter as EventEmitter2 } from "events";
62370
63692
  import { resolve as resolve15, dirname as dirname15 } from "path";
62371
63693
  import { fileURLToPath as fileURLToPath10 } from "url";
62372
63694
  import { resolve as resolve22, isAbsolute as isAbsolute5, parse as parse7 } from "path";
62373
- import { existsSync as existsSync20 } from "fs";
63695
+ import { existsSync as existsSync21 } from "fs";
62374
63696
  import { basename as basename11, join as join29 } from "path";
62375
63697
  import os from "os";
62376
63698
  import path2 from "path";
@@ -71416,7 +72738,7 @@ var __defProp3, __returnValue2 = (v) => v, __export2 = (target, all) => {
71416
72738
  configurable: true,
71417
72739
  set: __exportSetter2.bind(all, name)
71418
72740
  });
71419
- }, exports_src, loadYoga, yoga_wasm_base64_esm_default, Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap, constants4, YGEnums_default, Yoga, src_default, VALID_BORDER_STYLES, BorderChars, BorderCharArrays, KeyHandler, InternalKeyHandler, CSS_COLOR_NAMES, block_default, shade_default, slick_default, tiny_default, huge_default, grid_default, pallet_default, fonts, parsedFonts, TextAttributes, ATTRIBUTE_BASE_BITS = 8, ATTRIBUTE_BASE_MASK = 255, DebugOverlayCorner, TargetChannel, ATTRIBUTE_BASE_MASK2 = 255, LINK_ID_SHIFT = 8, LINK_ID_PAYLOAD_MASK = 16777215, BrandedStyledText, StyledText, black = (input) => applyStyle(input, { fg: "black" }), red = (input) => applyStyle(input, { fg: "red" }), green = (input) => applyStyle(input, { fg: "green" }), yellow = (input) => applyStyle(input, { fg: "yellow" }), blue = (input) => applyStyle(input, { fg: "blue" }), magenta = (input) => applyStyle(input, { fg: "magenta" }), cyan = (input) => applyStyle(input, { fg: "cyan" }), white = (input) => applyStyle(input, { fg: "white" }), brightBlack = (input) => applyStyle(input, { fg: "brightBlack" }), brightRed = (input) => applyStyle(input, { fg: "brightRed" }), brightGreen = (input) => applyStyle(input, { fg: "brightGreen" }), brightYellow = (input) => applyStyle(input, { fg: "brightYellow" }), brightBlue = (input) => applyStyle(input, { fg: "brightBlue" }), brightMagenta = (input) => applyStyle(input, { fg: "brightMagenta" }), brightCyan = (input) => applyStyle(input, { fg: "brightCyan" }), brightWhite = (input) => applyStyle(input, { fg: "brightWhite" }), bgBlack = (input) => applyStyle(input, { bg: "black" }), bgRed = (input) => applyStyle(input, { bg: "red" }), bgGreen = (input) => applyStyle(input, { bg: "green" }), bgYellow = (input) => applyStyle(input, { bg: "yellow" }), bgBlue = (input) => applyStyle(input, { bg: "blue" }), bgMagenta = (input) => applyStyle(input, { bg: "magenta" }), bgCyan = (input) => applyStyle(input, { bg: "cyan" }), bgWhite = (input) => applyStyle(input, { bg: "white" }), bold = (input) => applyStyle(input, { bold: true }), italic = (input) => applyStyle(input, { italic: true }), underline = (input) => applyStyle(input, { underline: true }), strikethrough = (input) => applyStyle(input, { strikethrough: true }), dim = (input) => applyStyle(input, { dim: true }), reverse = (input) => applyStyle(input, { reverse: true }), blink = (input) => applyStyle(input, { blink: true }), fg = (color) => (input) => applyStyle(input, { fg: color }), bg = (color) => (input) => applyStyle(input, { bg: color }), link = (url2) => (input) => {
72741
+ }, exports_src, loadYoga, yoga_wasm_base64_esm_default, Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap, constants5, YGEnums_default, Yoga, src_default, VALID_BORDER_STYLES, BorderChars, BorderCharArrays, KeyHandler, InternalKeyHandler, CSS_COLOR_NAMES, block_default, shade_default, slick_default, tiny_default, huge_default, grid_default, pallet_default, fonts, parsedFonts, TextAttributes, ATTRIBUTE_BASE_BITS = 8, ATTRIBUTE_BASE_MASK = 255, DebugOverlayCorner, TargetChannel, ATTRIBUTE_BASE_MASK2 = 255, LINK_ID_SHIFT = 8, LINK_ID_PAYLOAD_MASK = 16777215, BrandedStyledText, StyledText, black = (input) => applyStyle(input, { fg: "black" }), red = (input) => applyStyle(input, { fg: "red" }), green = (input) => applyStyle(input, { fg: "green" }), yellow = (input) => applyStyle(input, { fg: "yellow" }), blue = (input) => applyStyle(input, { fg: "blue" }), magenta = (input) => applyStyle(input, { fg: "magenta" }), cyan = (input) => applyStyle(input, { fg: "cyan" }), white = (input) => applyStyle(input, { fg: "white" }), brightBlack = (input) => applyStyle(input, { fg: "brightBlack" }), brightRed = (input) => applyStyle(input, { fg: "brightRed" }), brightGreen = (input) => applyStyle(input, { fg: "brightGreen" }), brightYellow = (input) => applyStyle(input, { fg: "brightYellow" }), brightBlue = (input) => applyStyle(input, { fg: "brightBlue" }), brightMagenta = (input) => applyStyle(input, { fg: "brightMagenta" }), brightCyan = (input) => applyStyle(input, { fg: "brightCyan" }), brightWhite = (input) => applyStyle(input, { fg: "brightWhite" }), bgBlack = (input) => applyStyle(input, { bg: "black" }), bgRed = (input) => applyStyle(input, { bg: "red" }), bgGreen = (input) => applyStyle(input, { bg: "green" }), bgYellow = (input) => applyStyle(input, { bg: "yellow" }), bgBlue = (input) => applyStyle(input, { bg: "blue" }), bgMagenta = (input) => applyStyle(input, { bg: "magenta" }), bgCyan = (input) => applyStyle(input, { bg: "cyan" }), bgWhite = (input) => applyStyle(input, { bg: "white" }), bold = (input) => applyStyle(input, { bold: true }), italic = (input) => applyStyle(input, { italic: true }), underline = (input) => applyStyle(input, { underline: true }), strikethrough = (input) => applyStyle(input, { strikethrough: true }), dim = (input) => applyStyle(input, { dim: true }), reverse = (input) => applyStyle(input, { reverse: true }), blink = (input) => applyStyle(input, { blink: true }), fg = (color) => (input) => applyStyle(input, { fg: color }), bg = (color) => (input) => applyStyle(input, { bg: color }), link = (url2) => (input) => {
71420
72742
  const chunk = typeof input === "object" && "__isChunk" in input ? input : {
71421
72743
  __isChunk: true,
71422
72744
  text: String(input)
@@ -73076,7 +74398,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
73076
74398
  Wrap2[Wrap2["WrapReverse"] = 2] = "WrapReverse";
73077
74399
  return Wrap2;
73078
74400
  }({});
73079
- constants4 = {
74401
+ constants5 = {
73080
74402
  ALIGN_AUTO: Align.Auto,
73081
74403
  ALIGN_FLEX_START: Align.FlexStart,
73082
74404
  ALIGN_CENTER: Align.Center,
@@ -73150,7 +74472,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
73150
74472
  WRAP_WRAP: Wrap.Wrap,
73151
74473
  WRAP_WRAP_REVERSE: Wrap.WrapReverse
73152
74474
  };
73153
- YGEnums_default = constants4;
74475
+ YGEnums_default = constants5;
73154
74476
  Yoga = wrapAssembly(await yoga_wasm_base64_esm_default());
73155
74477
  src_default = Yoga;
73156
74478
  VALID_BORDER_STYLES = ["single", "double", "rounded", "heavy"];
@@ -76584,7 +77906,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
76584
77906
  worker_path = this.options.workerPath;
76585
77907
  } else {
76586
77908
  worker_path = new URL("./parser.worker.js", import.meta.url).href;
76587
- if (!existsSync20(resolve22(import.meta.dirname, "parser.worker.js"))) {
77909
+ if (!existsSync21(resolve22(import.meta.dirname, "parser.worker.js"))) {
76588
77910
  worker_path = new URL("./parser.worker.ts", import.meta.url).href;
76589
77911
  }
76590
77912
  }
@@ -123658,7 +124980,7 @@ var exports_up = {};
123658
124980
  __export(exports_up, {
123659
124981
  runUpCommand: () => runUpCommand
123660
124982
  });
123661
- import { existsSync as existsSync21 } from "fs";
124983
+ import { existsSync as existsSync23 } from "fs";
123662
124984
  import { resolve as resolve17 } from "path";
123663
124985
  function looksLikePath(value) {
123664
124986
  return value.includes("/") || value.startsWith(".") || value.startsWith("~");
@@ -123667,6 +124989,7 @@ async function runUpCommand(context, args) {
123667
124989
  let target = null;
123668
124990
  let agentName;
123669
124991
  let harness;
124992
+ let model;
123670
124993
  for (let index2 = 0;index2 < args.length; index2 += 1) {
123671
124994
  const current = args[index2] ?? "";
123672
124995
  if (current === "--name") {
@@ -123695,6 +125018,19 @@ async function runUpCommand(context, args) {
123695
125018
  harness = current.slice("--harness=".length);
123696
125019
  continue;
123697
125020
  }
125021
+ if (current === "--model") {
125022
+ const value = args[index2 + 1];
125023
+ if (!value) {
125024
+ throw new ScoutCliError("missing value for --model");
125025
+ }
125026
+ model = value;
125027
+ index2 += 1;
125028
+ continue;
125029
+ }
125030
+ if (current.startsWith("--model=")) {
125031
+ model = current.slice("--model=".length);
125032
+ continue;
125033
+ }
123698
125034
  if (current.startsWith("--")) {
123699
125035
  throw new ScoutCliError(`unexpected argument for up: ${current}`);
123700
125036
  }
@@ -123704,10 +125040,10 @@ async function runUpCommand(context, args) {
123704
125040
  target = current;
123705
125041
  }
123706
125042
  if (!target) {
123707
- throw new ScoutCliError("usage: scout up <name|path> [--name <alias>] [--harness <claude|codex>]");
125043
+ throw new ScoutCliError("usage: scout up <name|path> [--name <alias>] [--harness <claude|codex>] [--model <model>]");
123708
125044
  }
123709
125045
  let projectPath;
123710
- if (looksLikePath(target) || existsSync21(resolve17(target))) {
125046
+ if (looksLikePath(target) || existsSync23(resolve17(target))) {
123711
125047
  projectPath = resolve17(target);
123712
125048
  } else {
123713
125049
  const resolved = await resolveLocalAgentByName(target);
@@ -123720,6 +125056,7 @@ async function runUpCommand(context, args) {
123720
125056
  projectPath,
123721
125057
  agentName,
123722
125058
  harness: parseScoutHarness(harness),
125059
+ model,
123723
125060
  currentDirectory: defaultScoutContextDirectory(context)
123724
125061
  });
123725
125062
  context.output.writeValue(agent, renderScoutUpResult);
@@ -123827,10 +125164,10 @@ var init_whoami = __esm(async () => {
123827
125164
  });
123828
125165
 
123829
125166
  // ../../apps/desktop/src/cli/main.ts
123830
- import { existsSync as existsSync23, readFileSync as readFileSync14, writeFileSync as writeFileSync9, statSync as statSync5, mkdirSync as mkdirSync12 } from "fs";
125167
+ import { existsSync as existsSync25, readFileSync as readFileSync15, writeFileSync as writeFileSync9, statSync as statSync5, mkdirSync as mkdirSync12 } from "fs";
123831
125168
  import { join as join32 } from "path";
123832
125169
  import { spawnSync as spawnSync3 } from "child_process";
123833
- import { homedir as homedir23 } from "os";
125170
+ import { homedir as homedir24 } from "os";
123834
125171
 
123835
125172
  // ../../apps/desktop/src/cli/argv.ts
123836
125173
  function parseScoutArgv(argv) {
@@ -124090,8 +125427,8 @@ var _scoutBinPath = null;
124090
125427
  function getScoutBinPath() {
124091
125428
  if (_scoutBinPath)
124092
125429
  return _scoutBinPath;
124093
- _scoutBinPath = join32(homedir23(), ".bun", "bin", "scout");
124094
- if (!existsSync23(_scoutBinPath)) {
125430
+ _scoutBinPath = join32(homedir24(), ".bun", "bin", "scout");
125431
+ if (!existsSync25(_scoutBinPath)) {
124095
125432
  _scoutBinPath = spawnSync3("which", ["scout"], { encoding: "utf8" }).stdout.trim();
124096
125433
  }
124097
125434
  return _scoutBinPath;
@@ -124099,18 +125436,18 @@ function getScoutBinPath() {
124099
125436
  async function ensureBrokerUptodate() {
124100
125437
  try {
124101
125438
  const mtime = statSync5(getScoutBinPath()).mtimeMs;
124102
- const checkpointDir = join32(homedir23(), ".scout");
125439
+ const checkpointDir = join32(homedir24(), ".scout");
124103
125440
  const mtimePath = join32(checkpointDir, "cli-mtime");
124104
- const lastMtime = existsSync23(mtimePath) ? Number(readFileSync14(mtimePath, "utf8").trim()) : 0;
125441
+ const lastMtime = existsSync25(mtimePath) ? Number(readFileSync15(mtimePath, "utf8").trim()) : 0;
124105
125442
  if (mtime > lastMtime) {
124106
125443
  const uid = process.getuid();
124107
- const plistPath = join32(homedir23(), "Library", "LaunchAgents", "dev.openscout.broker.plist");
125444
+ const plistPath = join32(homedir24(), "Library", "LaunchAgents", "dev.openscout.broker.plist");
124108
125445
  spawnSync3("launchctl", ["bootout", `gui/${uid}/dev.openscout.broker`], { stdio: "ignore" });
124109
125446
  await new Promise((resolve18) => setTimeout(resolve18, 1500));
124110
125447
  spawnSync3("launchctl", ["bootstrap", `gui/${uid}`, plistPath], { stdio: "ignore" });
124111
125448
  await new Promise((resolve18) => setTimeout(resolve18, 2000));
124112
125449
  }
124113
- if (!existsSync23(checkpointDir)) {
125450
+ if (!existsSync25(checkpointDir)) {
124114
125451
  mkdirSync12(checkpointDir, { recursive: true });
124115
125452
  }
124116
125453
  writeFileSync9(mtimePath, String(Math.floor(mtime)), { encoding: "utf8", flag: "w" });