@openscout/scout 0.2.61 → 0.2.62

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
@@ -118,6 +118,15 @@ function parseFlagValue(args, index, flag) {
118
118
  }
119
119
  missingFlagValue(flag);
120
120
  }
121
+ function flagNameFor(current, flagNames) {
122
+ return flagNames.find((flag) => current === flag || current.startsWith(`${flag}=`)) ?? null;
123
+ }
124
+ function resolveInputFilePath(currentDirectory, filePath) {
125
+ return resolve2(currentDirectory, filePath);
126
+ }
127
+ function rejectMixedBodySources(kind) {
128
+ throw new ScoutCliError(kind === "message" ? "provide either an inline message or --message-file/--body-file, not both" : "provide either an inline question or --prompt-file/--body-file, not both");
129
+ }
121
130
  function parseContextRootPrefix(args, defaultCurrentDirectory) {
122
131
  let currentDirectory = defaultCurrentDirectory;
123
132
  const rest = [];
@@ -184,6 +193,7 @@ function parseSendCommandOptions(args, defaultCurrentDirectory) {
184
193
  let channel;
185
194
  let shouldSpeak = false;
186
195
  let harness;
196
+ let messageFile;
187
197
  const messageParts = [];
188
198
  for (let index = 0;index < parsed.args.length; index += 1) {
189
199
  const current = parsed.args[index] ?? "";
@@ -209,10 +219,23 @@ function parseSendCommandOptions(args, defaultCurrentDirectory) {
209
219
  shouldSpeak = true;
210
220
  continue;
211
221
  }
222
+ const fileFlag = flagNameFor(current, ["--message-file", "--body-file"]);
223
+ if (fileFlag) {
224
+ if (messageFile) {
225
+ throw new ScoutCliError("message file was provided more than once");
226
+ }
227
+ const value = parseFlagValue(parsed.args, index, fileFlag);
228
+ messageFile = resolveInputFilePath(parsed.currentDirectory, value.value);
229
+ index = value.nextIndex;
230
+ continue;
231
+ }
212
232
  messageParts.push(current);
213
233
  }
214
234
  const message = messageParts.join(" ").trim();
215
- if (!message) {
235
+ if (message && messageFile) {
236
+ rejectMixedBodySources("message");
237
+ }
238
+ if (!message && !messageFile) {
216
239
  throw new ScoutCliError("no message provided");
217
240
  }
218
241
  return {
@@ -222,7 +245,8 @@ function parseSendCommandOptions(args, defaultCurrentDirectory) {
222
245
  channel,
223
246
  shouldSpeak,
224
247
  harness,
225
- message
248
+ message,
249
+ messageFile
226
250
  };
227
251
  }
228
252
  function parseAskCommandOptions(args, defaultCurrentDirectory) {
@@ -232,6 +256,7 @@ function parseAskCommandOptions(args, defaultCurrentDirectory) {
232
256
  let channel;
233
257
  let harness;
234
258
  let timeoutSeconds;
259
+ let promptFile;
235
260
  const messageParts = [];
236
261
  for (let index = 0;index < parsed.args.length; index += 1) {
237
262
  const current = parsed.args[index] ?? "";
@@ -269,13 +294,26 @@ function parseAskCommandOptions(args, defaultCurrentDirectory) {
269
294
  index = value.nextIndex;
270
295
  continue;
271
296
  }
297
+ const fileFlag = flagNameFor(current, ["--prompt-file", "--body-file"]);
298
+ if (fileFlag) {
299
+ if (promptFile) {
300
+ throw new ScoutCliError("prompt file was provided more than once");
301
+ }
302
+ const value = parseFlagValue(parsed.args, index, fileFlag);
303
+ promptFile = resolveInputFilePath(parsed.currentDirectory, value.value);
304
+ index = value.nextIndex;
305
+ continue;
306
+ }
272
307
  messageParts.push(current);
273
308
  }
274
309
  const message = messageParts.join(" ").trim();
275
310
  if (!targetLabel) {
276
311
  throw new ScoutCliError("--to <name> is required");
277
312
  }
278
- if (!message) {
313
+ if (message && promptFile) {
314
+ rejectMixedBodySources("question");
315
+ }
316
+ if (!message && !promptFile) {
279
317
  throw new ScoutCliError("no question provided");
280
318
  }
281
319
  return {
@@ -286,7 +324,8 @@ function parseAskCommandOptions(args, defaultCurrentDirectory) {
286
324
  channel,
287
325
  harness,
288
326
  timeoutSeconds,
289
- message
327
+ message,
328
+ promptFile
290
329
  };
291
330
  }
292
331
  function parseImplicitAskCommandOptions(args, defaultCurrentDirectory) {
@@ -295,6 +334,7 @@ function parseImplicitAskCommandOptions(args, defaultCurrentDirectory) {
295
334
  let channel;
296
335
  let harness;
297
336
  let timeoutSeconds;
337
+ let promptFile;
298
338
  const messageParts = [];
299
339
  for (let index = 0;index < parsed.args.length; index += 1) {
300
340
  const current = parsed.args[index] ?? "";
@@ -326,6 +366,16 @@ function parseImplicitAskCommandOptions(args, defaultCurrentDirectory) {
326
366
  index = value.nextIndex;
327
367
  continue;
328
368
  }
369
+ const fileFlag = flagNameFor(current, ["--prompt-file", "--body-file"]);
370
+ if (fileFlag) {
371
+ if (promptFile) {
372
+ throw new ScoutCliError("prompt file was provided more than once");
373
+ }
374
+ const value = parseFlagValue(parsed.args, index, fileFlag);
375
+ promptFile = resolveInputFilePath(parsed.currentDirectory, value.value);
376
+ index = value.nextIndex;
377
+ continue;
378
+ }
329
379
  messageParts.push(current);
330
380
  }
331
381
  const input = messageParts.join(" ").trim();
@@ -341,7 +391,10 @@ function parseImplicitAskCommandOptions(args, defaultCurrentDirectory) {
341
391
  }
342
392
  const [target] = mentions;
343
393
  const message = stripMention(input, target);
344
- if (!message) {
394
+ if (message && promptFile) {
395
+ rejectMixedBodySources("question");
396
+ }
397
+ if (!message && !promptFile) {
345
398
  throw new ScoutCliError("no question provided");
346
399
  }
347
400
  return {
@@ -352,7 +405,8 @@ function parseImplicitAskCommandOptions(args, defaultCurrentDirectory) {
352
405
  channel,
353
406
  harness,
354
407
  timeoutSeconds,
355
- message
408
+ message,
409
+ promptFile
356
410
  };
357
411
  }
358
412
  function parseWatchCommandOptions(args, defaultCurrentDirectory) {
@@ -581,6 +635,32 @@ var init_options = __esm(() => {
581
635
  SCOUT_MENTION_PATTERN = /(^|[\s([{'"`])@([A-Za-z0-9._:-]+)(?=$|[\s)\]}",.!?:;'"`])/g;
582
636
  });
583
637
 
638
+ // ../../apps/desktop/src/cli/input-file.ts
639
+ import { readFile } from "fs/promises";
640
+ async function readCliInputFile(filePath, label) {
641
+ let body;
642
+ try {
643
+ body = await readFile(filePath, "utf8");
644
+ } catch (error) {
645
+ const detail = error instanceof Error ? error.message : String(error);
646
+ throw new ScoutCliError(`could not read ${label} file ${filePath}: ${detail}`);
647
+ }
648
+ const normalized = body.replace(/^\uFEFF/, "");
649
+ if (!normalized.trim()) {
650
+ throw new ScoutCliError(`${label} file is empty: ${filePath}`);
651
+ }
652
+ return normalized;
653
+ }
654
+ async function resolvePromptBody(input) {
655
+ return input.promptFile ? readCliInputFile(input.promptFile, "prompt") : input.message;
656
+ }
657
+ async function resolveMessageBody(input) {
658
+ return input.messageFile ? readCliInputFile(input.messageFile, "message") : input.message;
659
+ }
660
+ var init_input_file = __esm(() => {
661
+ init_errors();
662
+ });
663
+
584
664
  // ../protocol/dist/common.js
585
665
  var init_common = () => {};
586
666
 
@@ -996,6 +1076,9 @@ var init_invocations = () => {};
996
1076
  // ../protocol/dist/scout-dispatch.js
997
1077
  var init_scout_dispatch = () => {};
998
1078
 
1079
+ // ../protocol/dist/scout-delivery.js
1080
+ var init_scout_delivery = () => {};
1081
+
999
1082
  // ../protocol/dist/deliveries.js
1000
1083
  var init_deliveries = () => {};
1001
1084
 
@@ -1020,6 +1103,7 @@ var init_dist = __esm(() => {
1020
1103
  init_messages();
1021
1104
  init_invocations();
1022
1105
  init_scout_dispatch();
1106
+ init_scout_delivery();
1023
1107
  init_deliveries();
1024
1108
  init_transports();
1025
1109
  init_events();
@@ -2064,7 +2148,7 @@ var init_support_paths = () => {};
2064
2148
  // ../runtime/src/harness-catalog.ts
2065
2149
  import { execFileSync } from "child_process";
2066
2150
  import { existsSync as existsSync2, statSync } from "fs";
2067
- import { mkdir, readFile, writeFile } from "fs/promises";
2151
+ import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
2068
2152
  import { dirname, join as join2 } from "path";
2069
2153
  function expandHomePath(value) {
2070
2154
  if (value === "~")
@@ -2295,7 +2379,7 @@ function evaluateHarnessReadiness(entry, options = {}) {
2295
2379
  }
2296
2380
  async function readHarnessCatalogOverrides(overridePath = resolveOpenScoutSupportPaths().harnessCatalogPath) {
2297
2381
  try {
2298
- const raw = JSON.parse(await readFile(overridePath, "utf8"));
2382
+ const raw = JSON.parse(await readFile2(overridePath, "utf8"));
2299
2383
  return raw.entries ?? {};
2300
2384
  } catch {
2301
2385
  return {};
@@ -2422,7 +2506,7 @@ var init_harness_catalog = __esm(() => {
2422
2506
  });
2423
2507
 
2424
2508
  // ../runtime/src/user-project-hints.ts
2425
- import { readdir, readFile as readFile2, stat } from "fs/promises";
2509
+ import { readdir, readFile as readFile3, stat } from "fs/promises";
2426
2510
  import { homedir as homedir2 } from "os";
2427
2511
  import { dirname as dirname2, join as join3, resolve as resolve3 } from "path";
2428
2512
  import { fileURLToPath } from "url";
@@ -2547,7 +2631,7 @@ async function appendCodexHistoryPaths(home, roots) {
2547
2631
  }
2548
2632
  let body;
2549
2633
  try {
2550
- body = await readFile2(filePath, "utf8");
2634
+ body = await readFile3(filePath, "utf8");
2551
2635
  } catch {
2552
2636
  continue;
2553
2637
  }
@@ -2586,7 +2670,7 @@ async function appendCursorWorkspacePaths(home, roots) {
2586
2670
  const workspaceJsonPath = join3(base, id, "workspace.json");
2587
2671
  let raw;
2588
2672
  try {
2589
- raw = await readFile2(workspaceJsonPath, "utf8");
2673
+ raw = await readFile3(workspaceJsonPath, "utf8");
2590
2674
  } catch {
2591
2675
  continue;
2592
2676
  }
@@ -2617,7 +2701,7 @@ async function appendCursorWorkspacePaths(home, roots) {
2617
2701
  if (!wsStat.isFile()) {
2618
2702
  continue;
2619
2703
  }
2620
- const wsRaw = await readFile2(wsFile, "utf8");
2704
+ const wsRaw = await readFile3(wsFile, "utf8");
2621
2705
  const wsDoc = JSON.parse(wsRaw);
2622
2706
  const wsDir = dirname2(wsFile);
2623
2707
  for (const f of wsDoc.folders ?? []) {
@@ -2687,7 +2771,7 @@ async function appendClaudeSessionPaths(home, roots) {
2687
2771
  seenFiles += 1;
2688
2772
  let body;
2689
2773
  try {
2690
- body = await readFile2(sessionPath, "utf8");
2774
+ body = await readFile3(sessionPath, "utf8");
2691
2775
  } catch {
2692
2776
  continue;
2693
2777
  }
@@ -2749,7 +2833,7 @@ var init_user_project_hints = __esm(() => {
2749
2833
  // ../runtime/src/setup.ts
2750
2834
  import { execFileSync as execFileSync2 } from "child_process";
2751
2835
  import { existsSync as existsSync3, readFileSync } from "fs";
2752
- import { access, mkdir as mkdir2, readdir as readdir2, readFile as readFile3, realpath, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
2836
+ import { access, mkdir as mkdir2, readdir as readdir2, readFile as readFile4, realpath, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
2753
2837
  import { homedir as homedir3, hostname, userInfo } from "os";
2754
2838
  import { basename, dirname as dirname3, isAbsolute, join as join4, relative, resolve as resolve4 } from "path";
2755
2839
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -3206,7 +3290,7 @@ async function isDirectory(filePath) {
3206
3290
  }
3207
3291
  async function readJsonFile(filePath) {
3208
3292
  try {
3209
- return JSON.parse(await readFile3(filePath, "utf8"));
3293
+ return JSON.parse(await readFile4(filePath, "utf8"));
3210
3294
  } catch {
3211
3295
  return null;
3212
3296
  }
@@ -3352,7 +3436,7 @@ async function readCodexEnvironmentImport(projectRoot) {
3352
3436
  const sourcePath = codexEnvironmentPath(projectRoot);
3353
3437
  let body;
3354
3438
  try {
3355
- body = await readFile3(sourcePath, "utf8");
3439
+ body = await readFile4(sourcePath, "utf8");
3356
3440
  } catch {
3357
3441
  return null;
3358
3442
  }
@@ -3398,7 +3482,7 @@ async function readClaudeProjectImport(projectRoot) {
3398
3482
  const sessionPath = join4(sourcePath, entry);
3399
3483
  let body;
3400
3484
  try {
3401
- body = await readFile3(sessionPath, "utf8");
3485
+ body = await readFile4(sessionPath, "utf8");
3402
3486
  } catch {
3403
3487
  continue;
3404
3488
  }
@@ -3492,7 +3576,7 @@ async function ensureProjectConfigIgnored(projectRoot) {
3492
3576
  const comment = "# OpenScout local state";
3493
3577
  let current = "";
3494
3578
  try {
3495
- current = await readFile3(gitignorePath, "utf8");
3579
+ current = await readFile4(gitignorePath, "utf8");
3496
3580
  } catch {
3497
3581
  current = "";
3498
3582
  }
@@ -3653,6 +3737,35 @@ async function readOpenScoutSettings(options = {}) {
3653
3737
  legacyAgents
3654
3738
  });
3655
3739
  }
3740
+ function resolveOpenScoutSetupContextRoot(options = {}) {
3741
+ const env = options.env ?? process.env;
3742
+ const explicit = env.OPENSCOUT_SETUP_CWD?.trim();
3743
+ if (explicit) {
3744
+ return normalizePath(explicit);
3745
+ }
3746
+ const home = env.HOME?.trim() || homedir3();
3747
+ const supportDirectory = env.OPENSCOUT_SUPPORT_DIRECTORY?.trim() || join4(home, "Library", "Application Support", "OpenScout");
3748
+ const settingsPath = join4(supportDirectory, "settings.json");
3749
+ if (existsSync3(settingsPath)) {
3750
+ try {
3751
+ const raw = JSON.parse(readFileSync(settingsPath, "utf8"));
3752
+ const discovery = raw.discovery;
3753
+ if (typeof discovery?.contextRoot === "string" && discovery.contextRoot.trim()) {
3754
+ return normalizePath(discovery.contextRoot);
3755
+ }
3756
+ if (Array.isArray(discovery?.workspaceRoots)) {
3757
+ const workspaceRoot = discovery.workspaceRoots.find((entry) => typeof entry === "string" && entry.trim().length > 0);
3758
+ if (workspaceRoot) {
3759
+ return normalizePath(workspaceRoot);
3760
+ }
3761
+ }
3762
+ } catch {}
3763
+ }
3764
+ if (options.fallbackDirectory?.trim()) {
3765
+ return normalizePath(options.fallbackDirectory);
3766
+ }
3767
+ return normalizePath(process.cwd());
3768
+ }
3656
3769
  async function writeOpenScoutSettings(settings, options = {}) {
3657
3770
  ensureOpenScoutCleanSlateSync();
3658
3771
  const current = await readOpenScoutSettings(options);
@@ -3715,7 +3828,7 @@ async function installScoutSkillToHarnesses() {
3715
3828
  if (!source) {
3716
3829
  return { source: null, entries: [] };
3717
3830
  }
3718
- const content = await readFile3(source, "utf8");
3831
+ const content = await readFile4(source, "utf8");
3719
3832
  const entries = [];
3720
3833
  for (const harness of MANAGED_AGENT_HARNESSES) {
3721
3834
  const target = SCOUT_SKILL_INSTALL_PATHS[harness];
@@ -4595,7 +4708,7 @@ function buildManagedAgentShellExports(options) {
4595
4708
  // ../runtime/src/claude-stream-json.ts
4596
4709
  import { randomUUID } from "crypto";
4597
4710
  import { spawn } from "child_process";
4598
- import { appendFile, mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
4711
+ import { appendFile, mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
4599
4712
  import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
4600
4713
  import { join as join5 } from "path";
4601
4714
  function resolveClaudeStreamJsonOutput(result, fallbackParts) {
@@ -5061,6 +5174,9 @@ function normalizeApprovalRequest(session, turnId, block) {
5061
5174
  };
5062
5175
  }
5063
5176
 
5177
+ // ../agent-sessions/src/protocol/cost.ts
5178
+ var init_cost = () => {};
5179
+
5064
5180
  // ../agent-sessions/src/state.ts
5065
5181
  function normalizeTrackedTimestamp(value) {
5066
5182
  if (value == null) {
@@ -5607,6 +5723,15 @@ function stringifyUnknown(value) {
5607
5723
  return String(value);
5608
5724
  }
5609
5725
  }
5726
+ function isRecord(value) {
5727
+ return !!value && typeof value === "object" && !Array.isArray(value);
5728
+ }
5729
+ function maybeString(value) {
5730
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
5731
+ }
5732
+ function maybeNumber(value) {
5733
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
5734
+ }
5610
5735
  function renderToolResultContent(content) {
5611
5736
  if (typeof content === "string") {
5612
5737
  return content;
@@ -5663,6 +5788,7 @@ class ClaudeCodeHistoryParser {
5663
5788
  blockById = new Map;
5664
5789
  activeStreamBlocks = new Map;
5665
5790
  sawStreamTextThisTurn = false;
5791
+ assistantUsageByMessageId = new Map;
5666
5792
  constructor(session, baseTimestampMs) {
5667
5793
  this.session = session;
5668
5794
  this.baseTimestampMs = baseTimestampMs;
@@ -5672,6 +5798,7 @@ class ClaudeCodeHistoryParser {
5672
5798
  let parsedLineCount = 0;
5673
5799
  let skippedLineCount = 0;
5674
5800
  let lineCount = 0;
5801
+ let lastCapturedAt = this.baseTimestampMs;
5675
5802
  for (let index = 0;index < lines.length; index += 1) {
5676
5803
  const rawLine = lines[index];
5677
5804
  const trimmed = rawLine.trim();
@@ -5692,12 +5819,19 @@ class ClaudeCodeHistoryParser {
5692
5819
  continue;
5693
5820
  }
5694
5821
  const capturedAt = extractRecordTimestamp(record) ?? this.baseTimestampMs + index;
5822
+ lastCapturedAt = capturedAt;
5695
5823
  if (this.handleRecord(record, capturedAt)) {
5696
5824
  parsedLineCount += 1;
5697
5825
  } else {
5698
5826
  skippedLineCount += 1;
5699
5827
  }
5700
5828
  }
5829
+ if (this.persistObserveUsageMetadata()) {
5830
+ this.emitEvent(lastCapturedAt, {
5831
+ event: "session:update",
5832
+ session: { ...this.session }
5833
+ });
5834
+ }
5701
5835
  return {
5702
5836
  events: this.events,
5703
5837
  lineCount,
@@ -5706,6 +5840,7 @@ class ClaudeCodeHistoryParser {
5706
5840
  };
5707
5841
  }
5708
5842
  handleRecord(record, capturedAt) {
5843
+ this.captureRecordMetadata(record);
5709
5844
  const type = typeof record.type === "string" ? record.type : null;
5710
5845
  if (!type) {
5711
5846
  return false;
@@ -5715,7 +5850,7 @@ class ClaudeCodeHistoryParser {
5715
5850
  this.handleSystem(record, capturedAt);
5716
5851
  return true;
5717
5852
  case "user":
5718
- this.startTurn(capturedAt);
5853
+ this.handleUser(record, capturedAt);
5719
5854
  return true;
5720
5855
  case "assistant":
5721
5856
  this.handleAssistant(record, capturedAt);
@@ -5739,6 +5874,130 @@ class ClaudeCodeHistoryParser {
5739
5874
  return false;
5740
5875
  }
5741
5876
  }
5877
+ captureRecordMetadata(record) {
5878
+ const runtime = this.ensureObserveMetaRecord("observeRuntime");
5879
+ this.assignObserveString(runtime, "entrypoint", record.entrypoint);
5880
+ this.assignObserveString(runtime, "cliVersion", record.version);
5881
+ this.assignObserveString(runtime, "gitBranch", record.gitBranch);
5882
+ this.assignObserveString(runtime, "permissionMode", record.permissionMode);
5883
+ this.assignObserveString(runtime, "userType", record.userType);
5884
+ const recordCwd = maybeString(record.cwd);
5885
+ if (recordCwd && !this.session.cwd) {
5886
+ this.session.cwd = recordCwd;
5887
+ }
5888
+ const message = isRecord(record.message) ? record.message : null;
5889
+ const model = maybeString(message?.model);
5890
+ if (model && !this.session.model) {
5891
+ this.session.model = model;
5892
+ }
5893
+ const usage = this.readClaudeUsageEntry(record);
5894
+ if (!usage) {
5895
+ return;
5896
+ }
5897
+ const messageId = maybeString(message?.id) ?? maybeString(record.requestId) ?? maybeString(record.uuid);
5898
+ if (!messageId) {
5899
+ return;
5900
+ }
5901
+ this.assistantUsageByMessageId.set(messageId, usage);
5902
+ }
5903
+ readClaudeUsageEntry(record) {
5904
+ const type = maybeString(record.type);
5905
+ const message = isRecord(record.message) ? record.message : null;
5906
+ const role = maybeString(message?.role);
5907
+ if (type !== "assistant" && role !== "assistant") {
5908
+ return null;
5909
+ }
5910
+ const usage = isRecord(message?.usage) ? message.usage : null;
5911
+ const serverToolUse = isRecord(usage?.server_tool_use) ? usage.server_tool_use : null;
5912
+ const entry = {
5913
+ inputTokens: maybeNumber(usage?.input_tokens) ?? 0,
5914
+ outputTokens: maybeNumber(usage?.output_tokens) ?? 0,
5915
+ cacheReadInputTokens: maybeNumber(usage?.cache_read_input_tokens) ?? 0,
5916
+ cacheCreationInputTokens: maybeNumber(usage?.cache_creation_input_tokens) ?? 0,
5917
+ webSearchRequests: maybeNumber(serverToolUse?.web_search_requests) ?? 0,
5918
+ webFetchRequests: maybeNumber(serverToolUse?.web_fetch_requests) ?? 0,
5919
+ ...maybeString(message?.service_tier) ? { serviceTier: maybeString(message?.service_tier) } : {},
5920
+ ...maybeString(message?.speed) ? { speed: maybeString(message?.speed) } : {}
5921
+ };
5922
+ const hasUsage = entry.inputTokens > 0 || entry.outputTokens > 0 || entry.cacheReadInputTokens > 0 || entry.cacheCreationInputTokens > 0 || entry.webSearchRequests > 0 || entry.webFetchRequests > 0 || Boolean(entry.serviceTier) || Boolean(entry.speed);
5923
+ return hasUsage ? entry : null;
5924
+ }
5925
+ persistObserveUsageMetadata() {
5926
+ if (this.assistantUsageByMessageId.size === 0) {
5927
+ return false;
5928
+ }
5929
+ const usage = this.ensureObserveMetaRecord("observeUsage");
5930
+ let inputTokens = 0;
5931
+ let outputTokens = 0;
5932
+ let cacheReadInputTokens = 0;
5933
+ let cacheCreationInputTokens = 0;
5934
+ let webSearchRequests = 0;
5935
+ let webFetchRequests = 0;
5936
+ let serviceTier;
5937
+ let speed;
5938
+ for (const entry of this.assistantUsageByMessageId.values()) {
5939
+ inputTokens += entry.inputTokens;
5940
+ outputTokens += entry.outputTokens;
5941
+ cacheReadInputTokens += entry.cacheReadInputTokens;
5942
+ cacheCreationInputTokens += entry.cacheCreationInputTokens;
5943
+ webSearchRequests += entry.webSearchRequests;
5944
+ webFetchRequests += entry.webFetchRequests;
5945
+ if (entry.serviceTier) {
5946
+ serviceTier = entry.serviceTier;
5947
+ }
5948
+ if (entry.speed) {
5949
+ speed = entry.speed;
5950
+ }
5951
+ }
5952
+ let changed = false;
5953
+ const assignNumber = (key, value) => {
5954
+ if (usage[key] !== value) {
5955
+ usage[key] = value;
5956
+ changed = true;
5957
+ }
5958
+ };
5959
+ const assignString = (key, value) => {
5960
+ if (usage[key] !== value) {
5961
+ usage[key] = value;
5962
+ changed = true;
5963
+ }
5964
+ };
5965
+ assignNumber("assistantMessages", this.assistantUsageByMessageId.size);
5966
+ if (inputTokens > 0)
5967
+ assignNumber("inputTokens", inputTokens);
5968
+ if (outputTokens > 0)
5969
+ assignNumber("outputTokens", outputTokens);
5970
+ if (cacheReadInputTokens > 0)
5971
+ assignNumber("cacheReadInputTokens", cacheReadInputTokens);
5972
+ if (cacheCreationInputTokens > 0)
5973
+ assignNumber("cacheCreationInputTokens", cacheCreationInputTokens);
5974
+ if (webSearchRequests > 0)
5975
+ assignNumber("webSearchRequests", webSearchRequests);
5976
+ if (webFetchRequests > 0)
5977
+ assignNumber("webFetchRequests", webFetchRequests);
5978
+ if (serviceTier)
5979
+ assignString("serviceTier", serviceTier);
5980
+ if (speed)
5981
+ assignString("speed", speed);
5982
+ return changed;
5983
+ }
5984
+ ensureObserveMetaRecord(key) {
5985
+ const providerMeta = isRecord(this.session.providerMeta) ? this.session.providerMeta : {};
5986
+ this.session.providerMeta = providerMeta;
5987
+ const existing = providerMeta[key];
5988
+ if (isRecord(existing)) {
5989
+ return existing;
5990
+ }
5991
+ const next = {};
5992
+ providerMeta[key] = next;
5993
+ return next;
5994
+ }
5995
+ assignObserveString(target, key, value) {
5996
+ const next = maybeString(value);
5997
+ if (next) {
5998
+ target[key] = next;
5999
+ }
6000
+ }
5742
6001
  handleSystem(record, capturedAt) {
5743
6002
  if (record.subtype !== "init") {
5744
6003
  return;
@@ -5768,19 +6027,41 @@ class ClaudeCodeHistoryParser {
5768
6027
  });
5769
6028
  }
5770
6029
  }
6030
+ handleUser(record, capturedAt) {
6031
+ const message = record.message && typeof record.message === "object" ? record.message : null;
6032
+ const content = message?.content;
6033
+ if (Array.isArray(content) && content.length > 0) {
6034
+ const toolResults = content.filter((entry) => {
6035
+ return !!entry && typeof entry === "object" && !Array.isArray(entry) && entry.type === "tool_result";
6036
+ });
6037
+ if (toolResults.length === content.length) {
6038
+ for (const toolResult of toolResults) {
6039
+ this.handleToolResult({
6040
+ ...toolResult,
6041
+ tool_use_id: typeof toolResult.tool_use_id === "string" ? toolResult.tool_use_id : toolResult.id,
6042
+ is_error: toolResult.is_error === true
6043
+ }, capturedAt);
6044
+ }
6045
+ return;
6046
+ }
6047
+ }
6048
+ this.startTurn(capturedAt);
6049
+ }
5771
6050
  handleAssistant(record, capturedAt) {
5772
6051
  const turn = this.ensureTurn(capturedAt);
5773
- if (this.sawStreamTextThisTurn) {
5774
- return;
5775
- }
5776
6052
  const content = record.message && typeof record.message === "object" ? record.message.content : record.content;
5777
6053
  if (!Array.isArray(content)) {
6054
+ this.maybeEndTurnFromAssistant(record, capturedAt);
5778
6055
  return;
5779
6056
  }
6057
+ const skipTextBlocks = this.sawStreamTextThisTurn;
5780
6058
  for (const part of content) {
5781
6059
  const contentPart = part;
5782
6060
  const contentType = typeof contentPart.type === "string" ? contentPart.type : "";
5783
6061
  if (contentType === "thinking" || contentType === "reasoning") {
6062
+ if (skipTextBlocks) {
6063
+ continue;
6064
+ }
5784
6065
  const block = this.startBlock(turn, capturedAt, {
5785
6066
  type: "reasoning",
5786
6067
  text: typeof contentPart.thinking === "string" ? contentPart.thinking : typeof contentPart.text === "string" ? contentPart.text : "",
@@ -5788,14 +6069,20 @@ class ClaudeCodeHistoryParser {
5788
6069
  });
5789
6070
  this.emitBlockEnd(capturedAt, turn, block, "completed");
5790
6071
  } else if (contentType === "text") {
6072
+ if (skipTextBlocks) {
6073
+ continue;
6074
+ }
5791
6075
  const block = this.startBlock(turn, capturedAt, {
5792
6076
  type: "text",
5793
6077
  text: typeof contentPart.text === "string" ? contentPart.text : "",
5794
6078
  status: "completed"
5795
6079
  });
5796
6080
  this.emitBlockEnd(capturedAt, turn, block, "completed");
6081
+ } else if (contentType === "tool_use") {
6082
+ this.handleToolUse(contentPart, capturedAt);
5797
6083
  }
5798
6084
  }
6085
+ this.maybeEndTurnFromAssistant(record, capturedAt);
5799
6086
  }
5800
6087
  handleStreamEvent(record, capturedAt) {
5801
6088
  const turn = this.ensureTurn(capturedAt);
@@ -6017,6 +6304,12 @@ class ClaudeCodeHistoryParser {
6017
6304
  this.emitError(turn, capturedAt, message);
6018
6305
  this.endTurn("failed", capturedAt);
6019
6306
  }
6307
+ maybeEndTurnFromAssistant(record, capturedAt) {
6308
+ const stopReason = record.message && typeof record.message === "object" ? record.message.stop_reason : record.stop_reason;
6309
+ if (stopReason === "end_turn") {
6310
+ this.endTurn("completed", capturedAt);
6311
+ }
6312
+ }
6020
6313
  startTurn(capturedAt) {
6021
6314
  if (this.currentTurn) {
6022
6315
  this.endTurn("stopped", capturedAt);
@@ -6839,7 +7132,7 @@ var init_codex_launch_config = () => {};
6839
7132
 
6840
7133
  // ../agent-sessions/src/adapters/codex.ts
6841
7134
  import { spawn as spawn2 } from "child_process";
6842
- import { access as access2, appendFile as appendFile2, constants as constants2, mkdir as mkdir4, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "fs/promises";
7135
+ import { access as access2, appendFile as appendFile2, constants as constants2, mkdir as mkdir4, readFile as readFile6, rm as rm3, writeFile as writeFile4 } from "fs/promises";
6843
7136
  import { delimiter as delimiter2, join as join8 } from "path";
6844
7137
  import { homedir as homedir6 } from "os";
6845
7138
  function parseJsonLine(line) {
@@ -6880,7 +7173,7 @@ function errorMessage2(error) {
6880
7173
  }
6881
7174
  async function readOptionalFile(filePath) {
6882
7175
  try {
6883
- const raw = await readFile5(filePath, "utf8");
7176
+ const raw = await readFile6(filePath, "utf8");
6884
7177
  const trimmed = raw.trim();
6885
7178
  return trimmed || null;
6886
7179
  } catch {
@@ -9340,12 +9633,13 @@ var init_src = __esm(() => {
9340
9633
  init_pi();
9341
9634
  init_echo();
9342
9635
  init_codex_launch_config();
9636
+ init_cost();
9343
9637
  });
9344
9638
 
9345
9639
  // ../runtime/src/codex-app-server.ts
9346
9640
  import { spawn as spawn3 } from "child_process";
9347
9641
  import { constants as constants3 } from "fs";
9348
- import { access as access3, appendFile as appendFile3, mkdir as mkdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "fs/promises";
9642
+ import { access as access3, appendFile as appendFile3, mkdir as mkdir5, readFile as readFile7, rm as rm4, writeFile as writeFile5 } from "fs/promises";
9349
9643
  import { delimiter as delimiter3, join as join9 } from "path";
9350
9644
  function normalizeCodexModelValue(value) {
9351
9645
  const trimmed = value?.trim();
@@ -9542,7 +9836,7 @@ function isNotification2(message) {
9542
9836
  }
9543
9837
  async function readOptionalFile2(filePath) {
9544
9838
  try {
9545
- const raw = await readFile6(filePath, "utf8");
9839
+ const raw = await readFile7(filePath, "utf8");
9546
9840
  const trimmed = raw.trim();
9547
9841
  return trimmed || null;
9548
9842
  } catch {
@@ -10283,12 +10577,227 @@ function buildCollaborationContractPrompt(agentId) {
10283
10577
  `);
10284
10578
  }
10285
10579
 
10286
- // ../runtime/src/broker-service.ts
10287
- import { spawnSync } from "child_process";
10288
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
10580
+ // ../runtime/src/tool-resolution.ts
10581
+ import { accessSync as accessSync2, constants as constants4, existsSync as existsSync7 } from "fs";
10289
10582
  import { homedir as homedir7 } from "os";
10290
10583
  import { basename as basename3, dirname as dirname5, join as join10, resolve as resolve6 } from "path";
10291
10584
  import { fileURLToPath as fileURLToPath4 } from "url";
10585
+ function expandHomePath3(value, env = process.env) {
10586
+ const home = env.HOME ?? homedir7();
10587
+ if (value === "~") {
10588
+ return home;
10589
+ }
10590
+ if (value.startsWith("~/")) {
10591
+ return join10(home, value.slice(2));
10592
+ }
10593
+ return value;
10594
+ }
10595
+ function isExecutablePath(candidate) {
10596
+ if (!candidate) {
10597
+ return false;
10598
+ }
10599
+ try {
10600
+ accessSync2(candidate, constants4.X_OK);
10601
+ return true;
10602
+ } catch {
10603
+ return false;
10604
+ }
10605
+ }
10606
+ function splitPathEntries(env = process.env) {
10607
+ const separator = process.platform === "win32" ? ";" : ":";
10608
+ return (env.PATH ?? "").split(separator).filter(Boolean);
10609
+ }
10610
+ function dedupeDirectories(values, env) {
10611
+ const seen = new Set;
10612
+ const resolvedEntries = [];
10613
+ for (const value of values) {
10614
+ const normalized = resolve6(expandHomePath3(value, env));
10615
+ if (seen.has(normalized)) {
10616
+ continue;
10617
+ }
10618
+ seen.add(normalized);
10619
+ resolvedEntries.push(normalized);
10620
+ }
10621
+ return resolvedEntries;
10622
+ }
10623
+ function resolveExecutableFromSearch(options) {
10624
+ const env = options.env ?? process.env;
10625
+ const envKeys = options.envKeys ?? [];
10626
+ for (const envKey of envKeys) {
10627
+ const explicit = env[envKey]?.trim();
10628
+ if (!explicit) {
10629
+ continue;
10630
+ }
10631
+ const expanded = expandHomePath3(explicit, env);
10632
+ if (isExecutablePath(expanded)) {
10633
+ return { path: resolve6(expanded), source: "env" };
10634
+ }
10635
+ const foundOnPath = findExecutableOnSearchPath(explicit, env);
10636
+ if (foundOnPath) {
10637
+ return { path: foundOnPath.path, source: "env" };
10638
+ }
10639
+ }
10640
+ const commonDirectories = dedupeDirectories([...options.commonDirectories ?? DEFAULT_COMMON_EXECUTABLE_DIRECTORIES], env);
10641
+ const searchDirectories = dedupeDirectories([
10642
+ ...splitPathEntries(env),
10643
+ ...options.extraDirectories ?? [],
10644
+ ...commonDirectories
10645
+ ], env);
10646
+ for (const directory of searchDirectories) {
10647
+ for (const name of options.names) {
10648
+ const candidate = join10(directory, name);
10649
+ if (isExecutablePath(candidate)) {
10650
+ return {
10651
+ path: candidate,
10652
+ source: commonDirectories.includes(directory) ? "common-path" : "path"
10653
+ };
10654
+ }
10655
+ }
10656
+ }
10657
+ return null;
10658
+ }
10659
+ function resolveBunExecutable2(env = process.env) {
10660
+ return resolveExecutableFromSearch({
10661
+ env,
10662
+ envKeys: ["OPENSCOUT_BUN_BIN", "SCOUT_BUN_BIN", "BUN_BIN"],
10663
+ names: ["bun"]
10664
+ });
10665
+ }
10666
+ function resolveJavaScriptRuntime(options = {}) {
10667
+ const env = options.env ?? process.env;
10668
+ const allowNode = options.allowNode ?? true;
10669
+ const allowBun = options.allowBun ?? true;
10670
+ const explicitEnvKeys = options.explicitEnvKeys ?? [];
10671
+ for (const envKey of explicitEnvKeys) {
10672
+ const explicit = env[envKey]?.trim();
10673
+ if (!explicit) {
10674
+ continue;
10675
+ }
10676
+ const expanded = expandHomePath3(explicit, env);
10677
+ const resolvedExplicit = isExecutablePath(expanded) ? resolve6(expanded) : findExecutableOnSearchPath(explicit, env)?.path;
10678
+ if (!resolvedExplicit) {
10679
+ continue;
10680
+ }
10681
+ const kind = javascriptRuntimeKindForPath(resolvedExplicit);
10682
+ if (kind === "node" && allowNode || kind === "bun" && allowBun) {
10683
+ return {
10684
+ path: resolvedExplicit,
10685
+ kind,
10686
+ source: "env"
10687
+ };
10688
+ }
10689
+ }
10690
+ if (options.preferCurrentExecutable ?? true) {
10691
+ const currentExecutable = process.execPath;
10692
+ const kind = javascriptRuntimeKindForPath(currentExecutable);
10693
+ if (kind === "node" && allowNode || kind === "bun" && allowBun) {
10694
+ return {
10695
+ path: currentExecutable,
10696
+ kind,
10697
+ source: "execPath"
10698
+ };
10699
+ }
10700
+ }
10701
+ const searchNames = [];
10702
+ if (allowNode) {
10703
+ searchNames.push("node");
10704
+ }
10705
+ if (allowBun) {
10706
+ searchNames.push("bun");
10707
+ }
10708
+ const resolvedExecutable = resolveExecutableFromSearch({
10709
+ env,
10710
+ names: searchNames
10711
+ });
10712
+ if (!resolvedExecutable) {
10713
+ return null;
10714
+ }
10715
+ return {
10716
+ path: resolvedExecutable.path,
10717
+ kind: javascriptRuntimeKindForPath(resolvedExecutable.path),
10718
+ source: resolvedExecutable.source
10719
+ };
10720
+ }
10721
+ function resolveBundledEntrypoint(moduleUrl, filename) {
10722
+ const moduleDirectory = dirname5(fileURLToPath4(moduleUrl));
10723
+ const candidate = join10(moduleDirectory, filename);
10724
+ return existsSync7(candidate) ? candidate : null;
10725
+ }
10726
+ function resolveOpenScoutRepoRoot(options = {}) {
10727
+ const starts = options.startDirectories?.map((entry) => entry?.trim()).filter((entry) => Boolean(entry));
10728
+ if (!starts || starts.length === 0) {
10729
+ return null;
10730
+ }
10731
+ for (const start of starts) {
10732
+ let current = resolve6(start);
10733
+ while (true) {
10734
+ const scoutEntry = join10(current, "apps", "desktop", "bin", "scout.ts");
10735
+ const runtimeEntry = join10(current, "packages", "runtime", "bin", "openscout-runtime.mjs");
10736
+ if (existsSync7(scoutEntry) && existsSync7(runtimeEntry)) {
10737
+ return current;
10738
+ }
10739
+ const parent = dirname5(current);
10740
+ if (parent === current) {
10741
+ break;
10742
+ }
10743
+ current = parent;
10744
+ }
10745
+ }
10746
+ return null;
10747
+ }
10748
+ function resolveRepoEntrypoint(repoRoot, relativePath) {
10749
+ if (!repoRoot) {
10750
+ return null;
10751
+ }
10752
+ const candidate = join10(repoRoot, relativePath);
10753
+ return existsSync7(candidate) ? candidate : null;
10754
+ }
10755
+ function resolveNodeModulesPackageEntrypoint(moduleUrl, packageSegments, entryRelativePath) {
10756
+ let current = dirname5(fileURLToPath4(moduleUrl));
10757
+ for (let attempt = 0;attempt < 24; attempt += 1) {
10758
+ const candidate = join10(current, "node_modules", ...packageSegments, entryRelativePath);
10759
+ if (existsSync7(candidate)) {
10760
+ return candidate;
10761
+ }
10762
+ const parent = dirname5(current);
10763
+ if (parent === current) {
10764
+ break;
10765
+ }
10766
+ current = parent;
10767
+ }
10768
+ return null;
10769
+ }
10770
+ function javascriptRuntimeKindForPath(filePath) {
10771
+ return basename3(filePath).toLowerCase().startsWith("bun") ? "bun" : "node";
10772
+ }
10773
+ function findExecutableOnSearchPath(name, env) {
10774
+ const searchDirectories = dedupeDirectories([
10775
+ ...splitPathEntries(env),
10776
+ ...DEFAULT_COMMON_EXECUTABLE_DIRECTORIES
10777
+ ], env);
10778
+ for (const directory of searchDirectories) {
10779
+ const candidate = join10(directory, name);
10780
+ if (isExecutablePath(candidate)) {
10781
+ return { path: candidate, source: "path" };
10782
+ }
10783
+ }
10784
+ return null;
10785
+ }
10786
+ var DEFAULT_COMMON_EXECUTABLE_DIRECTORIES;
10787
+ var init_tool_resolution = __esm(() => {
10788
+ DEFAULT_COMMON_EXECUTABLE_DIRECTORIES = [
10789
+ join10(homedir7(), ".bun", "bin"),
10790
+ "/opt/homebrew/bin",
10791
+ "/usr/local/bin"
10792
+ ];
10793
+ });
10794
+
10795
+ // ../runtime/src/broker-service.ts
10796
+ import { spawnSync } from "child_process";
10797
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
10798
+ import { homedir as homedir8 } from "os";
10799
+ import { dirname as dirname6, join as join11, resolve as resolve7 } from "path";
10800
+ import { fileURLToPath as fileURLToPath5 } from "url";
10292
10801
  function isTmpPath(p) {
10293
10802
  return /^\/(?:private\/)?tmp\//.test(p);
10294
10803
  }
@@ -10320,17 +10829,17 @@ function runtimePackageDir() {
10320
10829
  const fromCwd = findWorkspaceRuntimeDir(process.cwd());
10321
10830
  if (fromCwd)
10322
10831
  return fromCwd;
10323
- const moduleDir = dirname5(fileURLToPath4(import.meta.url));
10324
- return resolve6(moduleDir, "..");
10832
+ const moduleDir = dirname6(fileURLToPath5(import.meta.url));
10833
+ return resolve7(moduleDir, "..");
10325
10834
  }
10326
10835
  function isInstalledRuntimePackageDir(candidate) {
10327
- return existsSync7(join10(candidate, "package.json")) && existsSync7(join10(candidate, "bin", "openscout-runtime.mjs"));
10836
+ return existsSync8(join11(candidate, "package.json")) && existsSync8(join11(candidate, "bin", "openscout-runtime.mjs"));
10328
10837
  }
10329
10838
  function findGlobalRuntimeDir() {
10330
10839
  const candidates = [
10331
- join10(homedir7(), ".bun", "node_modules", "@openscout", "runtime"),
10332
- join10(homedir7(), ".bun", "install", "global", "node_modules", "@openscout", "runtime"),
10333
- join10(homedir7(), ".bun", "install", "global", "node_modules", "@openscout", "scout", "node_modules", "@openscout", "runtime")
10840
+ join11(homedir8(), ".bun", "node_modules", "@openscout", "runtime"),
10841
+ join11(homedir8(), ".bun", "install", "global", "node_modules", "@openscout", "runtime"),
10842
+ join11(homedir8(), ".bun", "install", "global", "node_modules", "@openscout", "scout", "node_modules", "@openscout", "runtime")
10334
10843
  ];
10335
10844
  for (const c of candidates) {
10336
10845
  if (isInstalledRuntimePackageDir(c))
@@ -10340,11 +10849,11 @@ function findGlobalRuntimeDir() {
10340
10849
  const result = spawnSync("which", ["scout"], { encoding: "utf8", timeout: 3000 });
10341
10850
  const scoutBin = result.stdout?.trim();
10342
10851
  if (scoutBin) {
10343
- const scoutPkg = resolve6(scoutBin, "..", "..");
10344
- const nested = join10(scoutPkg, "node_modules", "@openscout", "runtime");
10852
+ const scoutPkg = resolve7(scoutBin, "..", "..");
10853
+ const nested = join11(scoutPkg, "node_modules", "@openscout", "runtime");
10345
10854
  if (isInstalledRuntimePackageDir(nested))
10346
10855
  return nested;
10347
- const sibling = resolve6(scoutPkg, "..", "runtime");
10856
+ const sibling = resolve7(scoutPkg, "..", "runtime");
10348
10857
  if (isInstalledRuntimePackageDir(sibling))
10349
10858
  return sibling;
10350
10859
  }
@@ -10352,38 +10861,24 @@ function findGlobalRuntimeDir() {
10352
10861
  return null;
10353
10862
  }
10354
10863
  function findWorkspaceRuntimeDir(startDir) {
10355
- let current = resolve6(startDir);
10864
+ let current = resolve7(startDir);
10356
10865
  while (true) {
10357
- const candidate = join10(current, "packages", "runtime");
10358
- if (existsSync7(join10(candidate, "package.json")) && existsSync7(join10(candidate, "src"))) {
10866
+ const candidate = join11(current, "packages", "runtime");
10867
+ if (existsSync8(join11(candidate, "package.json")) && existsSync8(join11(candidate, "src"))) {
10359
10868
  return candidate;
10360
10869
  }
10361
- const parent = dirname5(current);
10870
+ const parent = dirname6(current);
10362
10871
  if (parent === current)
10363
10872
  return null;
10364
10873
  current = parent;
10365
10874
  }
10366
10875
  }
10367
- function resolveBunExecutable2() {
10368
- const explicit = process.env.OPENSCOUT_BUN_BIN ?? process.env.BUN_BIN;
10369
- if (explicit && explicit.trim().length > 0) {
10370
- return explicit;
10876
+ function resolveBunExecutable3() {
10877
+ const bun = resolveBunExecutable2(process.env);
10878
+ if (bun) {
10879
+ return bun.path;
10371
10880
  }
10372
- if (basename3(process.execPath).startsWith("bun") && existsSync7(process.execPath)) {
10373
- return process.execPath;
10374
- }
10375
- const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
10376
- for (const entry of pathEntries) {
10377
- const candidate = join10(entry, "bun");
10378
- if (existsSync7(candidate)) {
10379
- return candidate;
10380
- }
10381
- }
10382
- const homeBun = join10(homedir7(), ".bun", "bin", "bun");
10383
- if (existsSync7(homeBun)) {
10384
- return homeBun;
10385
- }
10386
- return "bun";
10881
+ throw new Error("Unable to locate Bun for broker service management. Install Bun or set OPENSCOUT_BUN_BIN.");
10387
10882
  }
10388
10883
  function resolveBrokerServiceMode() {
10389
10884
  const explicit = (process.env.OPENSCOUT_BROKER_SERVICE_MODE ?? "").trim().toLowerCase();
@@ -10422,15 +10917,15 @@ function resolveBrokerServiceConfig() {
10422
10917
  const label = resolveBrokerServiceLabel(mode);
10423
10918
  const uid = typeof process.getuid === "function" ? process.getuid() : Number.parseInt(process.env.UID ?? "0", 10);
10424
10919
  const supportPaths = resolveOpenScoutSupportPaths();
10425
- const defaultSupportDir = join10(homedir7(), "Library", "Application Support", "OpenScout");
10920
+ const defaultSupportDir = join11(homedir8(), "Library", "Application Support", "OpenScout");
10426
10921
  const supportDirectory = isTmpPath(supportPaths.supportDirectory) ? defaultSupportDir : supportPaths.supportDirectory;
10427
- const logsDirectory = join10(supportDirectory, "logs", "broker");
10428
- const controlHome = isTmpPath(supportPaths.controlHome) ? join10(homedir7(), ".openscout", "control-plane") : supportPaths.controlHome;
10922
+ const logsDirectory = join11(supportDirectory, "logs", "broker");
10923
+ const controlHome = isTmpPath(supportPaths.controlHome) ? join11(homedir8(), ".openscout", "control-plane") : supportPaths.controlHome;
10429
10924
  const advertiseScope = resolveAdvertiseScope();
10430
10925
  const brokerHost = resolveBrokerHost(advertiseScope);
10431
10926
  const brokerPort = Number.parseInt(process.env.OPENSCOUT_BROKER_PORT ?? String(DEFAULT_BROKER_PORT), 10);
10432
10927
  const brokerUrl = process.env.OPENSCOUT_BROKER_URL ?? buildDefaultBrokerUrl(brokerHost, brokerPort);
10433
- const launchAgentPath = join10(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
10928
+ const launchAgentPath = join11(homedir8(), "Library", "LaunchAgents", `${label}.plist`);
10434
10929
  return {
10435
10930
  label,
10436
10931
  mode,
@@ -10440,11 +10935,11 @@ function resolveBrokerServiceConfig() {
10440
10935
  launchAgentPath,
10441
10936
  supportDirectory,
10442
10937
  logsDirectory,
10443
- stdoutLogPath: join10(logsDirectory, "stdout.log"),
10444
- stderrLogPath: join10(logsDirectory, "stderr.log"),
10938
+ stdoutLogPath: join11(logsDirectory, "stdout.log"),
10939
+ stderrLogPath: join11(logsDirectory, "stderr.log"),
10445
10940
  controlHome,
10446
10941
  runtimePackageDir: runtimePackageDir(),
10447
- bunExecutable: resolveBunExecutable2(),
10942
+ bunExecutable: resolveBunExecutable3(),
10448
10943
  brokerHost,
10449
10944
  brokerPort,
10450
10945
  brokerUrl,
@@ -10461,7 +10956,7 @@ function renderLaunchAgentPlist(config) {
10461
10956
  OPENSCOUT_BROKER_SERVICE_MODE: config.mode,
10462
10957
  OPENSCOUT_BROKER_SERVICE_LABEL: config.label,
10463
10958
  OPENSCOUT_ADVERTISE_SCOPE: config.advertiseScope,
10464
- HOME: homedir7(),
10959
+ HOME: homedir8(),
10465
10960
  PATH: launchPath,
10466
10961
  ...collectOptionalEnvVars([
10467
10962
  "OPENSCOUT_MESH_ID",
@@ -10487,7 +10982,7 @@ function renderLaunchAgentPlist(config) {
10487
10982
  <key>ProgramArguments</key>
10488
10983
  <array>
10489
10984
  <string>${xmlEscape(config.bunExecutable)}</string>
10490
- <string>${xmlEscape(join10(config.runtimePackageDir, "bin", "openscout-runtime.mjs"))}</string>
10985
+ <string>${xmlEscape(join11(config.runtimePackageDir, "bin", "openscout-runtime.mjs"))}</string>
10491
10986
  <string>broker</string>
10492
10987
  </array>
10493
10988
  <key>WorkingDirectory</key>
@@ -10522,7 +11017,7 @@ function collectOptionalEnvVars(keys) {
10522
11017
  }
10523
11018
  function resolveLaunchAgentPATH() {
10524
11019
  const entries = [
10525
- join10(homedir7(), ".bun", "bin"),
11020
+ join11(homedir8(), ".bun", "bin"),
10526
11021
  ...(process.env.PATH ?? "").split(":").filter(Boolean),
10527
11022
  "/opt/homebrew/bin",
10528
11023
  "/usr/local/bin",
@@ -10537,7 +11032,7 @@ function xmlEscape(value) {
10537
11032
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
10538
11033
  }
10539
11034
  function ensureParentDirectory(filePath) {
10540
- mkdirSync2(dirname5(filePath), { recursive: true });
11035
+ mkdirSync2(dirname6(filePath), { recursive: true });
10541
11036
  }
10542
11037
  function ensureServiceDirectories(config) {
10543
11038
  ensureOpenScoutCleanSlateSync();
@@ -10570,7 +11065,7 @@ function launchctlPath() {
10570
11065
  return "/bin/launchctl";
10571
11066
  }
10572
11067
  function readLogLines(path) {
10573
- if (!existsSync7(path)) {
11068
+ if (!existsSync8(path)) {
10574
11069
  return [];
10575
11070
  }
10576
11071
  return readFileSync3(path, "utf8").split(`
@@ -10670,7 +11165,7 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
10670
11165
  ensureServiceDirectories(config);
10671
11166
  const launchctl = inspectLaunchctl(config);
10672
11167
  const health = await fetchHealthSnapshot(config);
10673
- const installed = existsSync7(config.launchAgentPath);
11168
+ const installed = existsSync8(config.launchAgentPath);
10674
11169
  const lastLogLine = health.reachable ? readLastLogLine([config.stdoutLogPath, config.stderrLogPath]) : readLastLogLine([config.stderrLogPath, config.stdoutLogPath]);
10675
11170
  return {
10676
11171
  label: config.label,
@@ -10713,7 +11208,7 @@ async function startBrokerService(config = resolveBrokerServiceConfig()) {
10713
11208
  throw new Error(status.lastLogLine ?? status.health.error ?? "Broker service did not become healthy.");
10714
11209
  }
10715
11210
  function sleep(ms) {
10716
- return new Promise((resolve7) => setTimeout(resolve7, ms));
11211
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
10717
11212
  }
10718
11213
  async function stopBrokerService(config = resolveBrokerServiceConfig()) {
10719
11214
  runCommand(launchctlPath(), ["bootout", config.serviceTarget], { allowFailure: true });
@@ -10732,7 +11227,7 @@ async function restartBrokerService(config = resolveBrokerServiceConfig()) {
10732
11227
  }
10733
11228
  async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
10734
11229
  await stopBrokerService(config);
10735
- if (existsSync7(config.launchAgentPath)) {
11230
+ if (existsSync8(config.launchAgentPath)) {
10736
11231
  rmSync2(config.launchAgentPath, { force: true });
10737
11232
  }
10738
11233
  return brokerServiceStatus(config);
@@ -10793,8 +11288,9 @@ function formatBrokerServiceStatus(status) {
10793
11288
  var DEFAULT_BROKER_HOST = "127.0.0.1", DEFAULT_BROKER_HOST_MESH = "0.0.0.0", DEFAULT_BROKER_PORT = 65535, DEFAULT_ADVERTISE_SCOPE = "local", BROKER_SERVICE_POLL_INTERVAL_MS = 100, DEFAULT_BROKER_START_TIMEOUT_MS = 15000, DEFAULT_BROKER_URL;
10794
11289
  var init_broker_service = __esm(async () => {
10795
11290
  init_support_paths();
11291
+ init_tool_resolution();
10796
11292
  DEFAULT_BROKER_URL = buildDefaultBrokerUrl();
10797
- if (process.argv[1] && fileURLToPath4(import.meta.url) === process.argv[1] && !process.argv[1].endsWith("/main.mjs")) {
11293
+ if (process.argv[1] && fileURLToPath5(import.meta.url) === process.argv[1] && !process.argv[1].endsWith("/main.mjs")) {
10798
11294
  await main();
10799
11295
  }
10800
11296
  });
@@ -10811,27 +11307,27 @@ var init_local_agent_template = __esm(() => {
10811
11307
 
10812
11308
  // ../runtime/src/local-agents.ts
10813
11309
  import { execFileSync as execFileSync3, execSync } from "child_process";
10814
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
11310
+ import { existsSync as existsSync9, readFileSync as readFileSync4 } from "fs";
10815
11311
  import { mkdir as mkdir6, rm as rm5, stat as stat3, writeFile as writeFile6 } from "fs/promises";
10816
- import { basename as basename4, dirname as dirname6, join as join11, resolve as resolve7 } from "path";
10817
- import { fileURLToPath as fileURLToPath5 } from "url";
11312
+ import { basename as basename5, dirname as dirname7, join as join12, resolve as resolve8 } from "path";
11313
+ import { fileURLToPath as fileURLToPath6 } from "url";
10818
11314
  function resolveRelayHub() {
10819
11315
  return resolveOpenScoutSupportPaths().relayHubDirectory;
10820
11316
  }
10821
11317
  function resolveProjectsRoot(projectPath) {
10822
11318
  if (process.env.OPENSCOUT_PROJECTS_ROOT?.trim()) {
10823
- return resolve7(process.env.OPENSCOUT_PROJECTS_ROOT.trim());
11319
+ return resolve8(process.env.OPENSCOUT_PROJECTS_ROOT.trim());
10824
11320
  }
10825
11321
  try {
10826
11322
  const supportPaths = resolveOpenScoutSupportPaths();
10827
- if (!existsSync8(supportPaths.settingsPath)) {
10828
- return dirname6(projectPath);
11323
+ if (!existsSync9(supportPaths.settingsPath)) {
11324
+ return dirname7(projectPath);
10829
11325
  }
10830
11326
  const raw = JSON.parse(readFileSync4(supportPaths.settingsPath, "utf8"));
10831
11327
  const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
10832
- return workspaceRoot ? resolve7(workspaceRoot) : dirname6(projectPath);
11328
+ return workspaceRoot ? resolve8(workspaceRoot) : dirname7(projectPath);
10833
11329
  } catch {
10834
- return dirname6(projectPath);
11330
+ return dirname7(projectPath);
10835
11331
  }
10836
11332
  }
10837
11333
  function resolveBrokerUrl() {
@@ -10841,7 +11337,7 @@ function nowSeconds2() {
10841
11337
  return Math.floor(Date.now() / 1000);
10842
11338
  }
10843
11339
  function scoutCliPath() {
10844
- return join11(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
11340
+ return join12(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
10845
11341
  }
10846
11342
  function legacyNodeBrokerRelayCommand() {
10847
11343
  return `node ${JSON.stringify(scoutCliPath())}`;
@@ -10854,13 +11350,13 @@ function titleCaseLocalAgentName(value) {
10854
11350
  }
10855
11351
  function resolveScoutSkillPath() {
10856
11352
  const candidatePaths = [
10857
- join11(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
10858
- join11(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
10859
- join11(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
10860
- join11(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
11353
+ join12(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
11354
+ join12(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
11355
+ join12(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
11356
+ join12(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
10861
11357
  ];
10862
11358
  for (const path of candidatePaths) {
10863
- if (existsSync8(path)) {
11359
+ if (existsSync9(path)) {
10864
11360
  return path;
10865
11361
  }
10866
11362
  }
@@ -10911,7 +11407,7 @@ function buildLocalAgentCollaborationPrompt(context) {
10911
11407
  function buildLocalAgentTmuxProtocolPrompt(context) {
10912
11408
  return [
10913
11409
  "Relay protocol:",
10914
- ` - Read recent context with: ${context.relayCommand} read --as ${context.agentId}`,
11410
+ ` - Read recent context when needed with: ${context.relayCommand} latest --agent ${context.agentId} --limit 20`,
10915
11411
  ` - Tell one agent with: ${context.relayCommand} send --as ${context.agentId} "@<agent> your message"`,
10916
11412
  ` - Ask one agent and stay attached with: ${context.relayCommand} ask --to <agent> --as ${context.agentId} "your request"`,
10917
11413
  "",
@@ -10923,9 +11419,10 @@ function buildLocalAgentTmuxProtocolPrompt(context) {
10923
11419
  " - If you need multiple agents, use separate DMs or an explicit channel; do not guess a venue from multiple mentions",
10924
11420
  " - Do not use channel.shared for ordinary delegation or follow-up; reserve it for explicit group updates or broadcasts",
10925
11421
  " - If a short @handle may be ambiguous, resolve the exact target before sending; do not guess and do not fall back to shared",
11422
+ " - Treat known offline / on-demand agents as wakeable: use send or ask first and let the broker wake them; only surface ambiguity or unknown-target failures to the operator",
10926
11423
  " - Use send for tells and status; use ask when the meaning is 'do this and get back to me'",
10927
11424
  " - When replying to an [ask:<id>] request, include the same [ask:<id>] tag in your reply",
10928
- " - Use the broker-backed relay read command above to inspect recent context before responding",
11425
+ " - Use the broker-backed latest command above only when recent context is needed before responding",
10929
11426
  ` - Follow the scout skill at ${context.scoutSkill} for agent-to-agent communication`
10930
11427
  ].join(`
10931
11428
  `);
@@ -10936,7 +11433,7 @@ function buildLocalAgentDirectProtocolPrompt(context) {
10936
11433
  " - You are invoked directly by the OpenScout broker",
10937
11434
  " - Return your final answer in the assistant message for the current turn",
10938
11435
  " - Do not shell out to send the final answer through relay yourself",
10939
- ` - If you need recent relay context, inspect it with: ${context.relayCommand} read --as ${context.agentId}`,
11436
+ ` - If you need recent broker context, inspect it with: ${context.relayCommand} latest --agent ${context.agentId} --limit 20`,
10940
11437
  ` - If you need to tell one agent something, use: ${context.relayCommand} send --as ${context.agentId} "@<agent> your message"`,
10941
11438
  ` - If you need another agent to do work, use: ${context.relayCommand} ask --to <agent> --as ${context.agentId} "your request"`,
10942
11439
  " - Default Scout loop: resolve identity, resolve one target, choose DM vs explicit channel, keep follow-up in that same venue",
@@ -10944,6 +11441,7 @@ function buildLocalAgentDirectProtocolPrompt(context) {
10944
11441
  " - If you need multiple agents, use separate DMs or an explicit channel; do not guess a venue from multiple mentions",
10945
11442
  " - Do not use channel.shared for ordinary delegation or follow-up; reserve it for explicit group updates or broadcasts",
10946
11443
  " - If a short @handle may be ambiguous, resolve the exact target before sending; do not guess and do not fall back to shared",
11444
+ " - Treat known offline / on-demand agents as wakeable: use send or ask first and let the broker wake them; only surface ambiguity or unknown-target failures to the operator",
10947
11445
  " - Use send for tells and status; use ask when the meaning is 'do this and get back to me'",
10948
11446
  ` - Follow the scout skill at ${context.scoutSkill} for agent-to-agent communication`
10949
11447
  ].join(`
@@ -11006,7 +11504,7 @@ function buildLegacySimpleRelayPrompt(agentId, projectName, projectPath, relayCo
11006
11504
  "A primary agent may call into you for context, execution, follow-through, and handoff.",
11007
11505
  "",
11008
11506
  `You have full access to the codebase at ${projectPath}.`,
11009
- `There is a structured relay event stream at ${join11(relayHub, "channel.jsonl")} shared by all agents.`,
11507
+ `There is a structured relay event stream at ${join12(relayHub, "channel.jsonl")} shared by all agents.`,
11010
11508
  "",
11011
11509
  "Your job:",
11012
11510
  ` - Respond to @${agentId} mentions from other agents`,
@@ -11110,17 +11608,17 @@ async function writeLocalAgentRegistry(registry) {
11110
11608
  }
11111
11609
  await writeRelayAgentOverrides(nextOverrides);
11112
11610
  }
11113
- function expandHomePath3(value) {
11611
+ function expandHomePath4(value) {
11114
11612
  if (value === "~") {
11115
11613
  return process.env.HOME ?? process.cwd();
11116
11614
  }
11117
11615
  if (value.startsWith("~/")) {
11118
- return join11(process.env.HOME ?? process.cwd(), value.slice(2));
11616
+ return join12(process.env.HOME ?? process.cwd(), value.slice(2));
11119
11617
  }
11120
11618
  return value;
11121
11619
  }
11122
11620
  function normalizeProjectPath(value) {
11123
- return resolve7(expandHomePath3(value.trim() || "."));
11621
+ return resolve8(expandHomePath4(value.trim() || "."));
11124
11622
  }
11125
11623
  function normalizeTmuxSessionName2(value, agentId) {
11126
11624
  const fallback = `relay-${agentId}`;
@@ -11383,7 +11881,7 @@ function recordForHarness(record, harnessOverride) {
11383
11881
  function normalizeLocalAgentRecord(agentId, record) {
11384
11882
  const cwd = normalizeProjectPath(record.cwd || process.cwd());
11385
11883
  const projectRoot = normalizeProjectPath(record.projectRoot || cwd);
11386
- const project = record.project?.trim() || basename4(projectRoot);
11884
+ const project = record.project?.trim() || basename5(projectRoot);
11387
11885
  const definitionId = record.definitionId?.trim() || agentId;
11388
11886
  const defaultHarness = activeLocalHarness(record);
11389
11887
  const harnessProfiles = normalizeLocalHarnessProfiles(agentId, {
@@ -11448,7 +11946,7 @@ function localAgentRecordFromResolvedConfig(config) {
11448
11946
  function localAgentRecordFromRelayAgentOverride(agentId, override) {
11449
11947
  return normalizeLocalAgentRecord(agentId, {
11450
11948
  definitionId: override.definitionId ?? agentId,
11451
- project: override.projectName ?? basename4(override.projectRoot || override.runtime?.cwd || agentId),
11949
+ project: override.projectName ?? basename5(override.projectRoot || override.runtime?.cwd || agentId),
11452
11950
  projectRoot: override.projectRoot ?? override.runtime?.cwd,
11453
11951
  tmuxSession: override.runtime?.sessionId ?? `relay-${agentId}`,
11454
11952
  cwd: override.runtime?.cwd ?? override.projectRoot,
@@ -11544,7 +12042,7 @@ function buildLocalAgentBootstrapPrompt(_harness, _systemPrompt, initialMessage)
11544
12042
  function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile, workerScript) {
11545
12043
  const extraArgs = shellQuoteArguments(normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs));
11546
12044
  if (normalizeLocalAgentHarness(record.harness) === "codex") {
11547
- return `exec bash ${JSON.stringify(workerScript ?? join11(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
12045
+ return `exec bash ${JSON.stringify(workerScript ?? join12(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
11548
12046
  }
11549
12047
  return [
11550
12048
  "claude",
@@ -11590,7 +12088,7 @@ async function ensureLocalAgentOnline(agentName, record) {
11590
12088
  return normalizedRecord;
11591
12089
  }
11592
12090
  const projectPath = normalizedRecord.cwd;
11593
- const projectName = normalizedRecord.project || basename4(projectPath);
12091
+ const projectName = normalizedRecord.project || basename5(projectPath);
11594
12092
  const systemPromptTemplate = normalizedRecord.systemPrompt || buildLocalAgentSystemPromptTemplate();
11595
12093
  const systemPrompt = renderLocalAgentSystemPromptTemplate(systemPromptTemplate, buildLocalAgentTemplateContext(agentName, projectName, projectPath), { transport: normalizedRecord.transport });
11596
12094
  const agentRuntimeDir = relayAgentRuntimeDirectory(agentName);
@@ -11598,7 +12096,7 @@ async function ensureLocalAgentOnline(agentName, record) {
11598
12096
  await mkdir6(agentRuntimeDir, { recursive: true });
11599
12097
  await mkdir6(logsDir, { recursive: true });
11600
12098
  if (normalizedRecord.transport === "codex_app_server") {
11601
- await writeFile6(join11(agentRuntimeDir, "prompt.txt"), systemPrompt);
12099
+ await writeFile6(join12(agentRuntimeDir, "prompt.txt"), systemPrompt);
11602
12100
  await ensureCodexAppServerAgentOnline(buildCodexAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
11603
12101
  const registry2 = await readLocalAgentRegistry();
11604
12102
  registry2[agentName] = {
@@ -11610,7 +12108,7 @@ async function ensureLocalAgentOnline(agentName, record) {
11610
12108
  return registry2[agentName];
11611
12109
  }
11612
12110
  if (normalizedRecord.transport === "claude_stream_json") {
11613
- await writeFile6(join11(agentRuntimeDir, "prompt.txt"), systemPrompt);
12111
+ await writeFile6(join12(agentRuntimeDir, "prompt.txt"), systemPrompt);
11614
12112
  await ensureClaudeStreamJsonAgentOnline(buildClaudeAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
11615
12113
  const registry2 = await readLocalAgentRegistry();
11616
12114
  registry2[agentName] = {
@@ -11623,17 +12121,17 @@ async function ensureLocalAgentOnline(agentName, record) {
11623
12121
  }
11624
12122
  const initialMessage = buildLocalAgentInitialMessage(projectName, agentName);
11625
12123
  const bootstrapPrompt = buildLocalAgentBootstrapPrompt(normalizeLocalAgentHarness(normalizedRecord.harness), systemPrompt, initialMessage);
11626
- const queueDirectory = join11(agentRuntimeDir, "queue");
11627
- const processingDirectory = join11(queueDirectory, "processing");
11628
- const processedDirectory = join11(queueDirectory, "processed");
11629
- const promptFile = join11(agentRuntimeDir, "prompt.txt");
11630
- const initialFile = join11(agentRuntimeDir, "initial.txt");
11631
- const launchScript = join11(agentRuntimeDir, "launch.sh");
11632
- const workerScript = join11(agentRuntimeDir, "codex-worker.sh");
11633
- const codexSessionIdFile = join11(agentRuntimeDir, "codex-session-id.txt");
11634
- const stateFile = join11(agentRuntimeDir, "state.json");
11635
- const stdoutLogFile = join11(logsDir, "stdout.log");
11636
- const stderrLogFile = join11(logsDir, "stderr.log");
12124
+ const queueDirectory = join12(agentRuntimeDir, "queue");
12125
+ const processingDirectory = join12(queueDirectory, "processing");
12126
+ const processedDirectory = join12(queueDirectory, "processed");
12127
+ const promptFile = join12(agentRuntimeDir, "prompt.txt");
12128
+ const initialFile = join12(agentRuntimeDir, "initial.txt");
12129
+ const launchScript = join12(agentRuntimeDir, "launch.sh");
12130
+ const workerScript = join12(agentRuntimeDir, "codex-worker.sh");
12131
+ const codexSessionIdFile = join12(agentRuntimeDir, "codex-session-id.txt");
12132
+ const stateFile = join12(agentRuntimeDir, "state.json");
12133
+ const stdoutLogFile = join12(logsDir, "stdout.log");
12134
+ const stderrLogFile = join12(logsDir, "stderr.log");
11637
12135
  await writeFile6(promptFile, systemPrompt);
11638
12136
  await writeFile6(initialFile, bootstrapPrompt);
11639
12137
  await writeFile6(stderrLogFile, "");
@@ -11695,10 +12193,10 @@ async function ensureLocalAgentOnline(agentName, record) {
11695
12193
  if (isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
11696
12194
  break;
11697
12195
  }
11698
- await new Promise((resolve8) => setTimeout(resolve8, 100));
12196
+ await new Promise((resolve9) => setTimeout(resolve9, 100));
11699
12197
  }
11700
12198
  if (!isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
11701
- const stderrTail = existsSync8(stderrLogFile) ? readFileSync4(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
12199
+ const stderrTail = existsSync9(stderrLogFile) ? readFileSync4(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
11702
12200
  `).trim() : "";
11703
12201
  throw new Error(stderrTail ? `Relay agent ${agentName} failed to stay online:
11704
12202
  ${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
@@ -11748,19 +12246,42 @@ async function restartLocalAgent(agentId, options = {}) {
11748
12246
  }
11749
12247
  return ensureLocalAgentOnline(agentId, normalizedRecord);
11750
12248
  }
11751
- async function resolveLocalAgentByName(name) {
12249
+ function resolvedAgentNameFromOverride(agentId, override) {
12250
+ return {
12251
+ agentId,
12252
+ definitionId: override.definitionId ?? agentId,
12253
+ projectRoot: override.projectRoot
12254
+ };
12255
+ }
12256
+ async function resolveLocalAgentByName(name, options = {}) {
11752
12257
  const normalized = normalizeAgentSelectorSegment(name);
11753
12258
  if (!normalized)
11754
12259
  return null;
11755
12260
  const overrides = await readRelayAgentOverrides();
11756
- for (const [id, override] of Object.entries(overrides)) {
11757
- if (BUILT_IN_AGENT_DEFINITION_IDS.has(id))
11758
- continue;
11759
- const defId = override.definitionId ?? id;
11760
- if (defId === normalized || normalizeAgentSelectorSegment(override.projectName ?? "") === normalized) {
11761
- return { agentId: id, definitionId: defId, projectRoot: override.projectRoot };
12261
+ const entries = Object.entries(overrides).filter(([agentId]) => !BUILT_IN_AGENT_DEFINITION_IDS.has(agentId));
12262
+ const exactAgentIdMatch = entries.find(([agentId]) => normalizeAgentSelectorSegment(agentId) === normalized);
12263
+ if (exactAgentIdMatch) {
12264
+ return resolvedAgentNameFromOverride(exactAgentIdMatch[0], exactAgentIdMatch[1]);
12265
+ }
12266
+ const definitionMatches = entries.filter(([agentId, override]) => (override.definitionId ?? agentId) === normalized);
12267
+ if (definitionMatches.length === 1) {
12268
+ const [agentId, override] = definitionMatches[0];
12269
+ return resolvedAgentNameFromOverride(agentId, override);
12270
+ }
12271
+ if (definitionMatches.length > 1) {
12272
+ const preferredMatch = definitionMatches.find(([agentId]) => /\.(main|master)\./.test(agentId)) ?? definitionMatches[0];
12273
+ if (preferredMatch) {
12274
+ return resolvedAgentNameFromOverride(preferredMatch[0], preferredMatch[1]);
11762
12275
  }
11763
12276
  }
12277
+ if (!options.matchProjectName) {
12278
+ return null;
12279
+ }
12280
+ const projectMatches = entries.filter(([, override]) => normalizeAgentSelectorSegment(override.projectName ?? "") === normalized);
12281
+ if (projectMatches.length === 1) {
12282
+ const [agentId, override] = projectMatches[0];
12283
+ return resolvedAgentNameFromOverride(agentId, override);
12284
+ }
11764
12285
  return null;
11765
12286
  }
11766
12287
  async function listLocalAgents(options = {}) {
@@ -11812,7 +12333,7 @@ async function resolveLocalAgentIdentity(input) {
11812
12333
  nodeQualifier: instance2.nodeQualifier,
11813
12334
  harness: normalizeManagedHarness2(preferredHarness ?? override.defaultHarness, "claude"),
11814
12335
  projectRoot: normalizeProjectPath(override.projectRoot),
11815
- projectName: override.projectName ?? basename4(override.projectRoot),
12336
+ projectName: override.projectName ?? basename5(override.projectRoot),
11816
12337
  source: "existing"
11817
12338
  };
11818
12339
  }
@@ -11831,12 +12352,12 @@ async function resolveLocalAgentIdentity(input) {
11831
12352
  nodeQualifier: instance2.nodeQualifier,
11832
12353
  harness: normalizeManagedHarness2(preferredHarness ?? override.defaultHarness, "claude"),
11833
12354
  projectRoot: normalizeProjectPath(override.projectRoot),
11834
- projectName: override.projectName ?? basename4(override.projectRoot),
12355
+ projectName: override.projectName ?? basename5(override.projectRoot),
11835
12356
  source: "existing"
11836
12357
  };
11837
12358
  }
11838
12359
  const configDefinitionId = config?.agent?.id?.trim() ? normalizeAgentSelectorSegment(config.agent.id.trim()) : "";
11839
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
12360
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
11840
12361
  const configDisplayName = config?.agent?.displayName?.trim() || "";
11841
12362
  const displayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
11842
12363
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
@@ -11850,7 +12371,7 @@ async function resolveLocalAgentIdentity(input) {
11850
12371
  nodeQualifier: instance.nodeQualifier,
11851
12372
  harness: effectiveHarness,
11852
12373
  projectRoot,
11853
- projectName: basename4(projectRoot),
12374
+ projectName: basename5(projectRoot),
11854
12375
  source: configDefinitionId || configDisplayName ? "config" : "new"
11855
12376
  };
11856
12377
  }
@@ -11859,6 +12380,7 @@ async function startLocalAgent(input) {
11859
12380
  const preferredHarness = input.harness ? normalizeLocalAgentHarness(input.harness) : undefined;
11860
12381
  const currentDirectory = input.currentDirectory ?? projectPath;
11861
12382
  const effectiveCwd = input.cwdOverride ? normalizeProjectPath(input.cwdOverride) : undefined;
12383
+ const shouldEnsureOnline = input.ensureOnline !== false;
11862
12384
  const requestedDefinitionId = input.agentName?.trim() ? normalizeAgentSelectorSegment(input.agentName.trim()) : "";
11863
12385
  if (input.agentName?.trim() && !requestedDefinitionId) {
11864
12386
  throw new Error(`Invalid agent name "${input.agentName}".`);
@@ -11906,7 +12428,7 @@ async function startLocalAgent(input) {
11906
12428
  }
11907
12429
  if (!matchingOverride) {
11908
12430
  const configDefinitionId = coldProjectConfig?.agent?.id?.trim() ? normalizeAgentSelectorSegment(coldProjectConfig.agent.id.trim()) : "";
11909
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
12431
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
11910
12432
  const configDisplayName = coldProjectConfig?.agent?.displayName?.trim() || "";
11911
12433
  const effectiveDisplayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
11912
12434
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
@@ -11923,7 +12445,7 @@ async function startLocalAgent(input) {
11923
12445
  agentId: instance.id,
11924
12446
  definitionId,
11925
12447
  displayName: effectiveDisplayName,
11926
- projectName: basename4(projectRoot),
12448
+ projectName: basename5(projectRoot),
11927
12449
  projectRoot,
11928
12450
  projectConfigPath: coldProjectConfigPath,
11929
12451
  source: "manual",
@@ -11963,7 +12485,7 @@ async function startLocalAgent(input) {
11963
12485
  agentId: instance.id,
11964
12486
  definitionId: requestedDefinitionId,
11965
12487
  displayName: input.displayName || titleCaseLocalAgentName(requestedDefinitionId),
11966
- projectName: matchingOverride.projectName ?? basename4(matchingProjectRoot),
12488
+ projectName: matchingOverride.projectName ?? basename5(matchingProjectRoot),
11967
12489
  projectRoot: matchingProjectRoot,
11968
12490
  projectConfigPath: matchingOverride.projectConfigPath ?? null,
11969
12491
  source: "manual",
@@ -12008,12 +12530,14 @@ async function startLocalAgent(input) {
12008
12530
  }
12009
12531
  targetAgentId = matchingAgentId;
12010
12532
  }
12011
- await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
12012
- includeDiscovered: false,
12013
- currentDirectory,
12014
- ensureCurrentProjectConfig: false,
12015
- harness: preferredHarness
12016
- });
12533
+ if (shouldEnsureOnline) {
12534
+ await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
12535
+ includeDiscovered: false,
12536
+ currentDirectory,
12537
+ ensureCurrentProjectConfig: false,
12538
+ harness: preferredHarness
12539
+ });
12540
+ }
12017
12541
  const finalOverrides = await readRelayAgentOverrides();
12018
12542
  const finalOverride = finalOverrides[targetAgentId];
12019
12543
  if (!finalOverride) {
@@ -12096,8 +12620,8 @@ async function restartAllLocalAgents(options = {}) {
12096
12620
  function readPersistedClaudeSessionId(agentName) {
12097
12621
  try {
12098
12622
  const runtimeDir = relayAgentRuntimeDirectory(agentName);
12099
- const catalogPath = join11(runtimeDir, "session-catalog.json");
12100
- if (existsSync8(catalogPath)) {
12623
+ const catalogPath = join12(runtimeDir, "session-catalog.json");
12624
+ if (existsSync9(catalogPath)) {
12101
12625
  const raw = readFileSync4(catalogPath, "utf8").trim();
12102
12626
  if (raw) {
12103
12627
  const catalog = JSON.parse(raw);
@@ -12106,8 +12630,8 @@ function readPersistedClaudeSessionId(agentName) {
12106
12630
  }
12107
12631
  }
12108
12632
  }
12109
- const legacyPath = join11(runtimeDir, "claude-session-id.txt");
12110
- if (existsSync8(legacyPath)) {
12633
+ const legacyPath = join12(runtimeDir, "claude-session-id.txt");
12634
+ if (existsSync9(legacyPath)) {
12111
12635
  const value = readFileSync4(legacyPath, "utf8").trim();
12112
12636
  return value || null;
12113
12637
  }
@@ -12295,22 +12819,22 @@ var init_local_agents = __esm(async () => {
12295
12819
  init_support_paths();
12296
12820
  init_local_agent_template();
12297
12821
  await init_broker_service();
12298
- MODULE_DIRECTORY = dirname6(fileURLToPath5(import.meta.url));
12299
- OPENSCOUT_REPO_ROOT = resolve7(MODULE_DIRECTORY, "..", "..", "..");
12822
+ MODULE_DIRECTORY = dirname7(fileURLToPath6(import.meta.url));
12823
+ OPENSCOUT_REPO_ROOT = resolve8(MODULE_DIRECTORY, "..", "..", "..");
12300
12824
  DEFAULT_LOCAL_AGENT_CAPABILITIES = ["chat", "invoke", "deliver"];
12301
12825
  SUPPORTED_LOCAL_AGENT_HARNESSES = ["claude", "codex"];
12302
12826
  });
12303
12827
 
12304
12828
  // ../runtime/src/user-config.ts
12305
- import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
12306
- import { homedir as homedir8 } from "os";
12307
- import { dirname as dirname7, join as join12 } from "path";
12829
+ import { existsSync as existsSync10, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
12830
+ import { homedir as homedir9 } from "os";
12831
+ import { dirname as dirname8, join as join13 } from "path";
12308
12832
  function userConfigPath() {
12309
- return join12(process.env.OPENSCOUT_HOME ?? join12(homedir8(), ".openscout"), "user.json");
12833
+ return join13(process.env.OPENSCOUT_HOME ?? join13(homedir9(), ".openscout"), "user.json");
12310
12834
  }
12311
12835
  function loadUserConfig() {
12312
12836
  const configPath = userConfigPath();
12313
- if (!existsSync9(configPath))
12837
+ if (!existsSync10(configPath))
12314
12838
  return {};
12315
12839
  try {
12316
12840
  return JSON.parse(readFileSync5(configPath, "utf8"));
@@ -12320,7 +12844,7 @@ function loadUserConfig() {
12320
12844
  }
12321
12845
  function saveUserConfig(config) {
12322
12846
  const configPath = userConfigPath();
12323
- mkdirSync3(dirname7(configPath), { recursive: true });
12847
+ mkdirSync3(dirname8(configPath), { recursive: true });
12324
12848
  writeFileSync3(configPath, JSON.stringify(config, null, 2) + `
12325
12849
  `, "utf8");
12326
12850
  }
@@ -12350,6 +12874,7 @@ var init_paths = __esm(() => {
12350
12874
  endpoints: "/v1/endpoints",
12351
12875
  conversations: "/v1/conversations",
12352
12876
  invocations: "/v1/invocations",
12877
+ deliver: "/v1/deliver",
12353
12878
  activity: "/v1/activity",
12354
12879
  collaborationRecords: "/v1/collaboration/records",
12355
12880
  collaborationEvents: "/v1/collaboration/events"
@@ -12358,8 +12883,8 @@ var init_paths = __esm(() => {
12358
12883
  });
12359
12884
 
12360
12885
  // ../../apps/desktop/src/core/broker/service.ts
12361
- import { mkdir as mkdir7, readFile as readFile7, unlink, writeFile as writeFile7 } from "fs/promises";
12362
- import { basename as basename5, join as join13, resolve as resolve8 } from "path";
12886
+ import { mkdir as mkdir7, readFile as readFile8, unlink, writeFile as writeFile7 } from "fs/promises";
12887
+ import { basename as basename6, join as join14, resolve as resolve9 } from "path";
12363
12888
  function buildScoutBrokerUrlFromEnv() {
12364
12889
  const host = process.env.OPENSCOUT_BROKER_HOST ?? DEFAULT_BROKER_HOST2;
12365
12890
  const port = Number.parseInt(process.env.OPENSCOUT_BROKER_PORT ?? String(DEFAULT_BROKER_PORT2), 10);
@@ -12385,7 +12910,8 @@ function resolveScoutAgentName(agentName) {
12385
12910
  }
12386
12911
  return resolveOperatorName();
12387
12912
  }
12388
- function resolveConfiguredSenderIdForProjectRoot(overrides, projectRoot) {
12913
+ function resolveConfiguredSenderIdForProjectRoot(overrides, projectRoot, preferredDefinitionId) {
12914
+ let fallbackSenderId = null;
12389
12915
  for (const [agentId, override] of Object.entries(overrides)) {
12390
12916
  if (BUILT_IN_AGENT_DEFINITION_IDS.has(agentId)) {
12391
12917
  continue;
@@ -12393,19 +12919,22 @@ function resolveConfiguredSenderIdForProjectRoot(overrides, projectRoot) {
12393
12919
  if (!override.projectRoot || override.projectRoot !== projectRoot) {
12394
12920
  continue;
12395
12921
  }
12396
- return agentId;
12922
+ if (preferredDefinitionId && override.definitionId === preferredDefinitionId) {
12923
+ return agentId;
12924
+ }
12925
+ fallbackSenderId ??= agentId;
12397
12926
  }
12398
- return null;
12927
+ return fallbackSenderId;
12399
12928
  }
12400
12929
  async function inferSenderIdForProjectRoot(projectRoot) {
12401
12930
  const overrides = await readRelayAgentOverrides();
12402
- const configuredSenderId = resolveConfiguredSenderIdForProjectRoot(overrides, projectRoot);
12931
+ const projectConfig = await readProjectConfig(projectRoot);
12932
+ const configuredDefinitionId = normalizeAgentSelectorSegment(projectConfig?.agent?.id?.trim() ?? "");
12933
+ const configuredSenderId = resolveConfiguredSenderIdForProjectRoot(overrides, projectRoot, configuredDefinitionId);
12403
12934
  if (configuredSenderId) {
12404
12935
  return configuredSenderId;
12405
12936
  }
12406
- const projectConfig = await readProjectConfig(projectRoot);
12407
- const configuredDefinitionId = normalizeAgentSelectorSegment(projectConfig?.agent?.id?.trim() ?? "");
12408
- const definitionId = configuredDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
12937
+ const definitionId = configuredDefinitionId || normalizeAgentSelectorSegment(basename6(projectRoot)) || "agent";
12409
12938
  return buildRelayAgentInstance(definitionId, projectRoot).id;
12410
12939
  }
12411
12940
  async function resolveScoutSenderId(agentName, currentDirectory, env = process.env) {
@@ -12523,6 +13052,51 @@ async function brokerPostJson(baseUrl, path, body) {
12523
13052
  }
12524
13053
  return response.json();
12525
13054
  }
13055
+ function renderScoutTargetLabel(targetLabel) {
13056
+ const trimmed = targetLabel.trim();
13057
+ if (!trimmed) {
13058
+ return "";
13059
+ }
13060
+ return trimmed.startsWith("@") ? trimmed : `@${trimmed}`;
13061
+ }
13062
+ function scoutTargetDiagnosticFromDeliveryFailure(delivery) {
13063
+ const dispatch = delivery.kind === "question" ? delivery.question : delivery.rejection;
13064
+ if (dispatch.kind === "ambiguous") {
13065
+ return {
13066
+ state: "ambiguous",
13067
+ candidates: dispatch.candidates.map((candidate) => ({
13068
+ agentId: candidate.agentId,
13069
+ label: candidate.label
13070
+ }))
13071
+ };
13072
+ }
13073
+ if (dispatch.kind === "unavailable" && dispatch.target) {
13074
+ return {
13075
+ agentId: dispatch.target.agentId,
13076
+ state: "unavailable",
13077
+ detail: dispatch.target.detail,
13078
+ wakePolicy: dispatch.target.wakePolicy ?? null,
13079
+ transport: dispatch.target.transport ?? null,
13080
+ projectRoot: dispatch.target.projectRoot ?? null
13081
+ };
13082
+ }
13083
+ if (dispatch.kind === "unknown") {
13084
+ return {
13085
+ agentId: dispatch.askedLabel,
13086
+ state: "unknown",
13087
+ registrationKind: null,
13088
+ projectRoot: null
13089
+ };
13090
+ }
13091
+ if (delivery.kind === "rejected" && dispatch.kind === "unparseable") {
13092
+ return {
13093
+ state: delivery.reason === "missing_target" ? "missing" : "invalid",
13094
+ askedLabel: dispatch.askedLabel,
13095
+ detail: dispatch.detail
13096
+ };
13097
+ }
13098
+ return;
13099
+ }
12526
13100
  async function readScoutBrokerHealth(baseUrl = resolveScoutBrokerUrl()) {
12527
13101
  try {
12528
13102
  const health = await brokerReadJson(baseUrl, scoutBrokerPaths.health);
@@ -12616,84 +13190,6 @@ function formatBrokerTargetLabel(snapshot, agentId) {
12616
13190
  const candidate = buildMentionCandidate(snapshot, current);
12617
13191
  return formatMinimalAgentIdentity(candidate, candidates);
12618
13192
  }
12619
- function normalizeProjectLocalResolutionValue(value) {
12620
- return value?.trim().toLowerCase().replace(/^@+/, "") ?? "";
12621
- }
12622
- function matchesObviousProjectLocalAlias(value, query) {
12623
- const normalized = normalizeProjectLocalResolutionValue(value);
12624
- if (!normalized || !query) {
12625
- return false;
12626
- }
12627
- return normalized === query || normalized.startsWith(`${query}-`) || normalized.startsWith(`${query}.`) || normalized.startsWith(`${query}_`) || normalized.startsWith(`${query} `);
12628
- }
12629
- function agentProjectRoot(snapshot, agentId) {
12630
- const endpoints = Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === agentId);
12631
- const preferred = endpoints.find((endpoint) => endpoint.state === "active") ?? endpoints.find((endpoint) => endpoint.state === "idle" || endpoint.state === "waiting") ?? endpoints[0];
12632
- return preferred?.projectRoot ?? preferred?.cwd ?? metadataString2(snapshot.agents[agentId]?.metadata, "projectRoot") ?? null;
12633
- }
12634
- function agentPreferredState(snapshot, agentId) {
12635
- const endpoints = Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === agentId);
12636
- const preferred = endpoints.find((endpoint) => endpoint.state === "active") ?? endpoints.find((endpoint) => endpoint.state === "idle" || endpoint.state === "waiting") ?? endpoints[0];
12637
- return preferred?.state ?? "offline";
12638
- }
12639
- function scoreProjectLocalBrokerAgent(snapshot, agentId, currentProjectRoot, query) {
12640
- const projectRoot = agentProjectRoot(snapshot, agentId);
12641
- if (!projectRoot || resolve8(projectRoot) !== resolve8(currentProjectRoot)) {
12642
- return -1;
12643
- }
12644
- const agent = snapshot.agents[agentId];
12645
- const formattedLabel = formatBrokerTargetLabel(snapshot, agentId);
12646
- const values = [
12647
- formattedLabel,
12648
- agent?.defaultSelector,
12649
- agent?.selector,
12650
- agent?.handle,
12651
- agent?.displayName,
12652
- agent?.definitionId,
12653
- agentId
12654
- ];
12655
- const matches = values.filter((value) => matchesObviousProjectLocalAlias(value, query));
12656
- if (matches.length === 0) {
12657
- return -1;
12658
- }
12659
- return 1000 + whoStateRank(agentPreferredState(snapshot, agentId)) * 20;
12660
- }
12661
- async function findPreferredProjectLocalBrokerTarget(snapshot, label, currentDirectory) {
12662
- const query = normalizeProjectLocalResolutionValue(label);
12663
- if (!query) {
12664
- return null;
12665
- }
12666
- const currentProjectRoot = await findNearestProjectRoot(currentDirectory) ?? currentDirectory;
12667
- const scored = Object.values(snapshot.agents).filter((agent) => agent.id !== OPERATOR_ID).filter((agent) => !isSupersededBrokerAgent(snapshot, agent.id)).map((agent) => ({
12668
- agentId: agent.id,
12669
- score: scoreProjectLocalBrokerAgent(snapshot, agent.id, currentProjectRoot, query)
12670
- })).filter((entry) => entry.score >= 0).sort((left, right) => {
12671
- const scoreDelta = right.score - left.score;
12672
- if (scoreDelta !== 0)
12673
- return scoreDelta;
12674
- return left.agentId.localeCompare(right.agentId);
12675
- });
12676
- if (scored.length === 0) {
12677
- return null;
12678
- }
12679
- if (scored.length > 1 && scored[0]?.score === scored[1]?.score) {
12680
- return null;
12681
- }
12682
- const agentId = scored[0]?.agentId;
12683
- if (!agentId) {
12684
- return null;
12685
- }
12686
- const resolvedLabel = formatBrokerTargetLabel(snapshot, agentId);
12687
- const selector = extractAgentSelectors(resolvedLabel)[0];
12688
- if (!selector) {
12689
- return null;
12690
- }
12691
- return {
12692
- agentId,
12693
- label: resolvedLabel,
12694
- selector
12695
- };
12696
- }
12697
13193
  async function resolveMentionTargets(snapshot, text, currentDirectory) {
12698
13194
  const mentions = extractAgentMentions(text);
12699
13195
  const selectors = mentions.parsed;
@@ -12764,55 +13260,6 @@ async function resolveMentionTargets(snapshot, text, currentDirectory) {
12764
13260
  ambiguous
12765
13261
  };
12766
13262
  }
12767
- async function resolveSingleBrokerTarget(snapshot, label, currentDirectory) {
12768
- const normalized = label.trim();
12769
- if (!normalized) {
12770
- return { kind: "unresolved" };
12771
- }
12772
- const resolution = await resolveMentionTargets(snapshot, normalized.startsWith("@") ? normalized : `@${normalized}`, currentDirectory);
12773
- const preferredProjectLocalTarget = await findPreferredProjectLocalBrokerTarget(snapshot, normalized, currentDirectory);
12774
- if (preferredProjectLocalTarget) {
12775
- return { kind: "resolved", target: preferredProjectLocalTarget };
12776
- }
12777
- const firstAmbiguous = resolution.ambiguous[0];
12778
- if (firstAmbiguous) {
12779
- return { kind: "ambiguous", candidates: firstAmbiguous.candidates };
12780
- }
12781
- const first = resolution.resolved[0];
12782
- if (first) {
12783
- return { kind: "resolved", target: first };
12784
- }
12785
- return { kind: "unresolved" };
12786
- }
12787
- async function describeScoutTargetAvailability(snapshot, target, currentDirectory) {
12788
- const resolvedConfig = await resolveRelayAgentConfig(target.selector, {
12789
- currentDirectory
12790
- });
12791
- const registrationKind = resolvedConfig?.registrationKind ?? null;
12792
- const endpoints = Object.values(snapshot.endpoints ?? {}).filter((endpoint) => endpoint.agentId === target.agentId);
12793
- if (endpoints.length > 0) {
12794
- return {
12795
- agentId: target.agentId,
12796
- state: whoEntryState(endpoints, registrationKind ?? "broker"),
12797
- registrationKind,
12798
- projectRoot: resolvedConfig?.projectRoot ?? null
12799
- };
12800
- }
12801
- if (registrationKind === "discovered") {
12802
- return {
12803
- agentId: target.agentId,
12804
- state: "discovered",
12805
- registrationKind,
12806
- projectRoot: resolvedConfig?.projectRoot ?? null
12807
- };
12808
- }
12809
- return {
12810
- agentId: target.agentId,
12811
- state: snapshot.agents[target.agentId] || registrationKind === "configured" ? "offline" : "unknown",
12812
- registrationKind,
12813
- projectRoot: resolvedConfig?.projectRoot ?? null
12814
- };
12815
- }
12816
13263
  function resolveConversationShareMode(snapshot, nodeId, participantIds, fallback) {
12817
13264
  if (fallback === "shared")
12818
13265
  return "shared";
@@ -13318,72 +13765,74 @@ async function sendScoutMessage(input) {
13318
13765
  const currentDirectory = input.currentDirectory ?? process.cwd();
13319
13766
  const createdAtMs = input.createdAtMs ?? Date.now();
13320
13767
  const senderId = await resolveConversationActorId(broker.baseUrl, broker.snapshot, broker.node.id, input.senderId, currentDirectory);
13768
+ const mentionResolution = await resolveMentionTargets(broker.snapshot, input.body, currentDirectory);
13769
+ const selectors = extractAgentSelectors(input.body);
13321
13770
  const requestedTargetLabel = input.targetLabel?.trim();
13322
13771
  if (requestedTargetLabel) {
13323
- const targetResolution = await resolveSingleBrokerTarget(broker.snapshot, requestedTargetLabel, currentDirectory);
13324
- if (targetResolution.kind !== "resolved") {
13772
+ const delivery = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.deliver, {
13773
+ id: `deliver-${createdAtMs.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
13774
+ requesterId: senderId,
13775
+ requesterNodeId: broker.node.id,
13776
+ targetLabel: requestedTargetLabel,
13777
+ body: input.body,
13778
+ intent: "tell",
13779
+ channel: input.channel,
13780
+ speechText: input.shouldSpeak ? stripScoutAgentSelectorLabels(input.body) : undefined,
13781
+ createdAt: createdAtMs,
13782
+ messageMetadata: {
13783
+ source: "scout-cli"
13784
+ }
13785
+ });
13786
+ if (delivery.kind !== "delivery") {
13325
13787
  return {
13326
13788
  usedBroker: true,
13327
13789
  invokedTargets: [],
13328
- unresolvedTargets: [requestedTargetLabel]
13790
+ unresolvedTargets: [requestedTargetLabel],
13791
+ targetDiagnostic: scoutTargetDiagnosticFromDeliveryFailure(delivery)
13329
13792
  };
13330
13793
  }
13331
- const target = targetResolution.target;
13332
- const targetReady = await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, target.agentId, currentDirectory);
13333
- if (!targetReady || target.agentId === senderId) {
13794
+ return {
13795
+ usedBroker: true,
13796
+ conversationId: delivery.conversation.id,
13797
+ messageId: delivery.message.id,
13798
+ invokedTargets: delivery.targetAgentId ? [delivery.targetAgentId] : [],
13799
+ unresolvedTargets: [],
13800
+ routeKind: delivery.routeKind
13801
+ };
13802
+ }
13803
+ if (selectors.length === 1 && mentionResolution.resolved.length + mentionResolution.unresolved.length + mentionResolution.ambiguous.length === 1) {
13804
+ const targetLabel = selectors[0].label;
13805
+ const delivery = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.deliver, {
13806
+ id: `deliver-${createdAtMs.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
13807
+ requesterId: senderId,
13808
+ requesterNodeId: broker.node.id,
13809
+ targetLabel,
13810
+ body: input.body,
13811
+ intent: "tell",
13812
+ channel: input.channel,
13813
+ speechText: input.shouldSpeak ? stripScoutAgentSelectorLabels(input.body) : undefined,
13814
+ createdAt: createdAtMs,
13815
+ messageMetadata: {
13816
+ source: "scout-cli"
13817
+ }
13818
+ });
13819
+ if (delivery.kind !== "delivery") {
13334
13820
  return {
13335
13821
  usedBroker: true,
13336
13822
  invokedTargets: [],
13337
- unresolvedTargets: [requestedTargetLabel]
13823
+ unresolvedTargets: [targetLabel],
13824
+ targetDiagnostic: scoutTargetDiagnosticFromDeliveryFailure(delivery)
13338
13825
  };
13339
13826
  }
13340
- let conversation2;
13341
- if (!input.channel) {
13342
- const dm = await ensureBrokerDirectConversationBetween(broker.baseUrl, broker.snapshot, broker.node.id, senderId, target.agentId);
13343
- conversation2 = dm.conversation;
13344
- } else {
13345
- conversation2 = await ensureBrokerConversation(broker.baseUrl, broker.snapshot, broker.node.id, input.channel, senderId, [target.agentId]);
13346
- }
13347
- const messageId2 = `m-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
13348
- const speechText2 = input.shouldSpeak ? stripScoutAgentSelectorLabels(input.body) : "";
13349
- const returnAddress2 = buildScoutReturnAddress2(broker.snapshot, senderId, {
13350
- conversationId: conversation2.id,
13351
- replyToMessageId: messageId2
13352
- });
13353
- await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.messages, {
13354
- id: messageId2,
13355
- conversationId: conversation2.id,
13356
- actorId: senderId,
13357
- originNodeId: broker.node.id,
13358
- class: conversation2.kind === "system" ? "system" : "agent",
13359
- body: input.body,
13360
- mentions: [{ actorId: target.agentId, label: target.label }],
13361
- speech: speechText2 ? { text: speechText2 } : undefined,
13362
- audience: {
13363
- notify: [target.agentId],
13364
- reason: relayAudienceReason(conversation2)
13365
- },
13366
- visibility: conversation2.visibility,
13367
- policy: "durable",
13368
- createdAt: createdAtMs,
13369
- metadata: {
13370
- source: "scout-cli",
13371
- relayChannel: relayChannelMetadata(conversation2, input.channel),
13372
- relayTargetIds: [target.agentId],
13373
- relayMessageId: messageId2,
13374
- returnAddress: returnAddress2
13375
- }
13376
- });
13377
13827
  return {
13378
13828
  usedBroker: true,
13379
- conversationId: conversation2.id,
13380
- messageId: messageId2,
13381
- invokedTargets: [target.agentId],
13829
+ conversationId: delivery.conversation.id,
13830
+ messageId: delivery.message.id,
13831
+ invokedTargets: delivery.targetAgentId ? [delivery.targetAgentId] : [],
13382
13832
  unresolvedTargets: [],
13383
- routeKind: relayRouteKind(conversation2)
13833
+ routeKind: delivery.routeKind
13384
13834
  };
13385
13835
  }
13386
- const mentionResolution = await resolveMentionTargets(broker.snapshot, input.body, currentDirectory);
13387
13836
  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));
13388
13837
  const validTargets = [
13389
13838
  ...new Set(availableTargets.map((target) => target.agentId).filter((target) => target !== senderId && Boolean(broker.snapshot.agents[target])))
@@ -13560,74 +14009,42 @@ async function openScoutPeerSession(input) {
13560
14009
  };
13561
14010
  }
13562
14011
  async function sendScoutDirectMessage(input) {
13563
- const currentDirectory = input.currentDirectory ?? process.cwd();
13564
- const directSession = await openScoutPeerSession({
13565
- sourceId: OPERATOR_ID,
13566
- targetId: input.agentId,
13567
- currentDirectory
13568
- });
13569
14012
  const broker = await requireScoutBrokerContext();
13570
14013
  const createdAt = Date.now();
13571
- const messageId = `msg-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
13572
14014
  const source = input.source?.trim() || "scout-mobile";
13573
- const targetAgentId = directSession.targetId;
13574
- const targetAgent = broker.snapshot.agents[targetAgentId] ?? ("agent" in directSession ? directSession.agent : undefined);
13575
- const targetLabel = `@${targetAgent?.handle?.trim() || targetAgent?.displayName?.trim() || targetAgentId}`;
13576
- const returnAddress = buildScoutReturnAddress2(broker.snapshot, OPERATOR_ID, {
13577
- conversationId: directSession.conversation.id,
13578
- replyToMessageId: messageId
13579
- });
13580
- await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.messages, {
13581
- id: messageId,
13582
- conversationId: directSession.conversation.id,
13583
- replyToMessageId: input.replyToMessageId ?? undefined,
13584
- actorId: OPERATOR_ID,
13585
- originNodeId: broker.node.id,
13586
- class: "agent",
14015
+ const delivery = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.deliver, {
14016
+ id: `deliver-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
14017
+ requesterId: OPERATOR_ID,
14018
+ requesterNodeId: broker.node.id,
14019
+ targetAgentId: input.agentId,
13587
14020
  body: input.body.trim(),
13588
- mentions: [{ actorId: targetAgentId, label: targetLabel }],
13589
- audience: {
13590
- notify: [targetAgentId],
13591
- reason: "direct_message"
13592
- },
13593
- visibility: "private",
13594
- policy: "durable",
14021
+ intent: "consult",
14022
+ replyToMessageId: input.replyToMessageId ?? undefined,
14023
+ execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
14024
+ ensureAwake: true,
13595
14025
  createdAt,
13596
- metadata: {
14026
+ messageMetadata: {
13597
14027
  source,
13598
14028
  destinationKind: "direct",
13599
- destinationId: targetAgentId,
14029
+ destinationId: input.agentId,
13600
14030
  referenceMessageIds: input.referenceMessageIds ?? [],
13601
14031
  clientMessageId: input.clientMessageId ?? null,
13602
- returnAddress,
13603
14032
  ...input.deviceId ? { deviceId: input.deviceId } : {}
13604
- }
13605
- });
13606
- const invocationResponse = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.invocations, {
13607
- id: `inv-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
13608
- requesterId: OPERATOR_ID,
13609
- requesterNodeId: broker.node.id,
13610
- targetAgentId,
13611
- action: "consult",
13612
- task: input.body.trim(),
13613
- conversationId: directSession.conversation.id,
13614
- messageId,
13615
- execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
13616
- ensureAwake: true,
13617
- stream: false,
13618
- createdAt,
13619
- metadata: {
14033
+ },
14034
+ invocationMetadata: {
13620
14035
  source,
13621
14036
  destinationKind: "direct",
13622
- destinationId: targetAgentId,
13623
- returnAddress,
14037
+ destinationId: input.agentId,
13624
14038
  ...input.deviceId ? { deviceId: input.deviceId } : {}
13625
14039
  }
13626
14040
  });
14041
+ if (delivery.kind !== "delivery") {
14042
+ throw new Error(delivery.kind === "question" ? delivery.question.detail : delivery.rejection.detail);
14043
+ }
13627
14044
  return {
13628
- conversationId: directSession.conversation.id,
13629
- messageId,
13630
- flight: invocationResponse.flight
14045
+ conversationId: delivery.conversation.id,
14046
+ messageId: delivery.message.id,
14047
+ flight: delivery.flight
13631
14048
  };
13632
14049
  }
13633
14050
  async function askScoutAgentById(input) {
@@ -13643,103 +14060,46 @@ async function askScoutAgentById(input) {
13643
14060
  if (!targetAgentId) {
13644
14061
  return { usedBroker: true, unresolvedTargetId: input.targetAgentId };
13645
14062
  }
13646
- const targetLabel = formatBrokerTargetLabel(broker.snapshot, targetAgentId);
13647
- const target = {
13648
- agentId: targetAgentId,
13649
- label: targetLabel,
13650
- selector: {
13651
- raw: targetLabel.replace(/^@/, ""),
13652
- label: targetLabel,
13653
- definitionId: broker.snapshot.agents[targetAgentId]?.definitionId ?? metadataString2(broker.snapshot.agents[targetAgentId]?.metadata, "definitionId") ?? targetAgentId,
13654
- ...broker.snapshot.agents[targetAgentId]?.workspaceQualifier ? {
13655
- workspaceQualifier: broker.snapshot.agents[targetAgentId]?.workspaceQualifier
13656
- } : {},
13657
- ...broker.snapshot.agents[targetAgentId]?.nodeQualifier ? {
13658
- nodeQualifier: broker.snapshot.agents[targetAgentId]?.nodeQualifier
13659
- } : {}
13660
- }
13661
- };
13662
- const targetReady = await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, targetAgentId, currentDirectory);
13663
- if (!targetReady) {
14063
+ const delivery = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.deliver, {
14064
+ id: `deliver-${createdAtMs.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
14065
+ requesterId: senderId,
14066
+ requesterNodeId: broker.node.id,
14067
+ targetAgentId,
14068
+ body: input.body.trim(),
14069
+ intent: "consult",
14070
+ channel: input.channel,
14071
+ speechText: input.shouldSpeak ? input.body.trim() : undefined,
14072
+ execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
14073
+ ensureAwake: true,
14074
+ createdAt: createdAtMs,
14075
+ messageMetadata: {
14076
+ source
14077
+ },
14078
+ invocationMetadata: {
14079
+ source
14080
+ }
14081
+ });
14082
+ if (delivery.kind !== "delivery") {
13664
14083
  return {
13665
14084
  usedBroker: true,
13666
14085
  unresolvedTargetId: targetAgentId,
13667
- targetDiagnostic: await describeScoutTargetAvailability(broker.snapshot, target, currentDirectory)
14086
+ targetDiagnostic: scoutTargetDiagnosticFromDeliveryFailure(delivery)
13668
14087
  };
13669
14088
  }
13670
- let conversation;
13671
- if (!input.channel) {
13672
- const dm = await ensureBrokerDirectConversationBetween(broker.baseUrl, broker.snapshot, broker.node.id, senderId, targetAgentId);
13673
- conversation = dm.conversation;
13674
- } else {
13675
- conversation = await ensureBrokerConversation(broker.baseUrl, broker.snapshot, broker.node.id, input.channel, senderId, [targetAgentId]);
13676
- }
13677
- const messageId = `m-${createdAtMs.toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
13678
- const speechText = input.shouldSpeak ? input.body.trim() : "";
13679
- const returnAddress = buildScoutReturnAddress2(broker.snapshot, senderId, {
13680
- conversationId: conversation.id,
13681
- replyToMessageId: messageId
13682
- });
13683
14089
  const workItem = input.workItem ? await createScoutTrackedWorkItem({
13684
14090
  baseUrl: broker.baseUrl,
13685
14091
  senderId,
13686
14092
  targetAgentId,
13687
- conversationId: conversation.id,
14093
+ conversationId: delivery.conversation.id,
13688
14094
  createdAtMs,
13689
14095
  source,
13690
14096
  workItem: input.workItem
13691
14097
  }) : undefined;
13692
- await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.messages, {
13693
- id: messageId,
13694
- conversationId: conversation.id,
13695
- actorId: senderId,
13696
- originNodeId: broker.node.id,
13697
- class: conversation.kind === "system" ? "system" : "agent",
13698
- body: input.body.trim(),
13699
- mentions: [{ actorId: targetAgentId, label: targetLabel }],
13700
- speech: speechText ? { text: speechText } : undefined,
13701
- audience: {
13702
- notify: [targetAgentId],
13703
- reason: relayAudienceReason(conversation)
13704
- },
13705
- visibility: conversation.visibility,
13706
- policy: "durable",
13707
- createdAt: createdAtMs,
13708
- metadata: {
13709
- source,
13710
- relayChannel: relayChannelMetadata(conversation, input.channel),
13711
- relayTarget: targetAgentId,
13712
- collaborationRecordId: workItem?.id,
13713
- returnAddress
13714
- }
13715
- });
13716
- const invocationResponse = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.invocations, {
13717
- id: `inv-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
13718
- requesterId: senderId,
13719
- requesterNodeId: broker.node.id,
13720
- targetAgentId,
13721
- action: "consult",
13722
- task: input.body.trim(),
13723
- collaborationRecordId: workItem?.id,
13724
- conversationId: conversation.id,
13725
- messageId,
13726
- execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
13727
- ensureAwake: true,
13728
- stream: false,
13729
- createdAt: createdAtMs,
13730
- metadata: {
13731
- source,
13732
- relayChannel: relayChannelMetadata(conversation, input.channel),
13733
- relayTarget: targetAgentId,
13734
- collaborationRecordId: workItem?.id,
13735
- returnAddress
13736
- }
13737
- });
13738
14098
  return {
13739
14099
  usedBroker: true,
13740
- flight: invocationResponse.flight,
13741
- conversationId: conversation.id,
13742
- messageId,
14100
+ flight: delivery.flight,
14101
+ conversationId: delivery.conversation.id,
14102
+ messageId: delivery.message.id,
13743
14103
  workItem
13744
14104
  };
13745
14105
  }
@@ -13750,104 +14110,49 @@ async function askScoutQuestion(input) {
13750
14110
  }
13751
14111
  const currentDirectory = input.currentDirectory ?? process.cwd();
13752
14112
  const senderId = await resolveConversationActorId(broker.baseUrl, broker.snapshot, broker.node.id, input.senderId, currentDirectory);
13753
- const targetResolution = await resolveSingleBrokerTarget(broker.snapshot, input.targetLabel, currentDirectory);
13754
- if (targetResolution.kind === "ambiguous") {
13755
- return {
13756
- usedBroker: true,
13757
- unresolvedTarget: input.targetLabel,
13758
- targetDiagnostic: {
13759
- state: "ambiguous",
13760
- candidates: targetResolution.candidates
13761
- }
13762
- };
13763
- }
13764
- if (targetResolution.kind !== "resolved") {
13765
- return { usedBroker: true, unresolvedTarget: input.targetLabel };
13766
- }
13767
- const target = targetResolution.target;
13768
- const targetReady = await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, target.agentId, currentDirectory);
13769
- if (!targetReady) {
14113
+ const createdAt = input.createdAtMs ?? Date.now();
14114
+ const normalizedTargetLabel = renderScoutTargetLabel(input.targetLabel);
14115
+ const messageBody = input.body.trim().startsWith(normalizedTargetLabel) ? input.body.trim() : `${normalizedTargetLabel} ${input.body.trim()}`;
14116
+ const delivery = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.deliver, {
14117
+ id: `deliver-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
14118
+ requesterId: senderId,
14119
+ requesterNodeId: broker.node.id,
14120
+ targetLabel: input.targetLabel,
14121
+ body: messageBody,
14122
+ intent: "consult",
14123
+ channel: input.channel,
14124
+ speechText: input.shouldSpeak ? stripScoutAgentSelectorLabels(messageBody) : undefined,
14125
+ execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
14126
+ ensureAwake: true,
14127
+ createdAt,
14128
+ messageMetadata: {
14129
+ source: "scout-cli"
14130
+ },
14131
+ invocationMetadata: {
14132
+ source: "scout-cli"
14133
+ }
14134
+ });
14135
+ if (delivery.kind !== "delivery") {
13770
14136
  return {
13771
14137
  usedBroker: true,
13772
14138
  unresolvedTarget: input.targetLabel,
13773
- targetDiagnostic: await describeScoutTargetAvailability(broker.snapshot, target, currentDirectory)
14139
+ targetDiagnostic: scoutTargetDiagnosticFromDeliveryFailure(delivery)
13774
14140
  };
13775
14141
  }
13776
- let conversation;
13777
- if (!input.channel) {
13778
- const dm = await ensureBrokerDirectConversationBetween(broker.baseUrl, broker.snapshot, broker.node.id, senderId, target.agentId);
13779
- conversation = dm.conversation;
13780
- } else {
13781
- conversation = await ensureBrokerConversation(broker.baseUrl, broker.snapshot, broker.node.id, input.channel, senderId, [target.agentId]);
13782
- }
13783
- const messageId = `m-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
13784
- const messageBody = input.body.trim().startsWith(target.label) ? input.body.trim() : `${target.label} ${input.body.trim()}`;
13785
- const speechText = input.shouldSpeak ? stripScoutAgentSelectorLabels(messageBody) : "";
13786
- const createdAt = input.createdAtMs ?? Date.now();
13787
- const returnAddress = buildScoutReturnAddress2(broker.snapshot, senderId, {
13788
- conversationId: conversation.id,
13789
- replyToMessageId: messageId
13790
- });
13791
- const workItem = input.workItem ? await createScoutTrackedWorkItem({
14142
+ const workItem = input.workItem && delivery.targetAgentId ? await createScoutTrackedWorkItem({
13792
14143
  baseUrl: broker.baseUrl,
13793
14144
  senderId,
13794
- targetAgentId: target.agentId,
13795
- conversationId: conversation.id,
14145
+ targetAgentId: delivery.targetAgentId,
14146
+ conversationId: delivery.conversation.id,
13796
14147
  createdAtMs: createdAt,
13797
14148
  source: "scout-cli",
13798
14149
  workItem: input.workItem
13799
14150
  }) : undefined;
13800
- await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.messages, {
13801
- id: messageId,
13802
- conversationId: conversation.id,
13803
- actorId: senderId,
13804
- originNodeId: broker.node.id,
13805
- class: conversation.kind === "system" ? "system" : "agent",
13806
- body: messageBody,
13807
- mentions: [{ actorId: target.agentId, label: target.label }],
13808
- speech: speechText ? { text: speechText } : undefined,
13809
- audience: {
13810
- notify: [target.agentId],
13811
- reason: relayAudienceReason(conversation)
13812
- },
13813
- visibility: conversation.visibility,
13814
- policy: "durable",
13815
- createdAt,
13816
- metadata: {
13817
- source: "scout-cli",
13818
- relayChannel: relayChannelMetadata(conversation, input.channel),
13819
- relayTarget: target.agentId,
13820
- collaborationRecordId: workItem?.id,
13821
- returnAddress
13822
- }
13823
- });
13824
- const invocationResponse = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.invocations, {
13825
- id: `inv-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
13826
- requesterId: senderId,
13827
- requesterNodeId: broker.node.id,
13828
- targetAgentId: target.agentId,
13829
- action: "consult",
13830
- task: messageBody,
13831
- collaborationRecordId: workItem?.id,
13832
- conversationId: conversation.id,
13833
- messageId,
13834
- execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
13835
- ensureAwake: true,
13836
- stream: false,
13837
- createdAt,
13838
- metadata: {
13839
- source: "scout-cli",
13840
- relayChannel: relayChannelMetadata(conversation, input.channel),
13841
- relayTarget: target.agentId,
13842
- collaborationRecordId: workItem?.id,
13843
- returnAddress
13844
- }
13845
- });
13846
14151
  return {
13847
14152
  usedBroker: true,
13848
- flight: invocationResponse.flight,
13849
- conversationId: conversation.id,
13850
- messageId,
14153
+ flight: delivery.flight,
14154
+ conversationId: delivery.conversation.id,
14155
+ messageId: delivery.message.id,
13851
14156
  workItem
13852
14157
  };
13853
14158
  }
@@ -13880,7 +14185,7 @@ async function waitForScoutFlight(baseUrl, flightId, options = {}) {
13880
14185
  if (deadline !== null && Date.now() > deadline) {
13881
14186
  throw new Error(`Timed out waiting for flight ${flight.id}.`);
13882
14187
  }
13883
- await new Promise((resolve9) => setTimeout(resolve9, 1000));
14188
+ await new Promise((resolve10) => setTimeout(resolve10, 1000));
13884
14189
  }
13885
14190
  }
13886
14191
  async function loadScoutMessages(options = {}) {
@@ -14085,7 +14390,7 @@ async function listScoutAgents(options = {}) {
14085
14390
  }
14086
14391
  async function loadScoutRelayConfig() {
14087
14392
  try {
14088
- const raw = await readFile7(join13(relayHubDirectory(), "config.json"), "utf8");
14393
+ const raw = await readFile8(join14(relayHubDirectory(), "config.json"), "utf8");
14089
14394
  return JSON.parse(raw);
14090
14395
  } catch {
14091
14396
  return {};
@@ -14107,15 +14412,15 @@ function applyPronunciations(text, pronunciations) {
14107
14412
  async function acquireScoutOnAir(agent, timeoutMs = 30000) {
14108
14413
  const hub = relayHubDirectory();
14109
14414
  await mkdir7(hub, { recursive: true });
14110
- const lockPath = join13(hub, "on-air.lock");
14415
+ const lockPath = join14(hub, "on-air.lock");
14111
14416
  const deadline = Date.now() + timeoutMs;
14112
14417
  while (Date.now() < deadline) {
14113
14418
  try {
14114
- const raw = await readFile7(lockPath, "utf8");
14419
+ const raw = await readFile8(lockPath, "utf8");
14115
14420
  const lock = JSON.parse(raw);
14116
14421
  if (Date.now() - Number(lock.ts ?? 0) > 30000)
14117
14422
  break;
14118
- await new Promise((resolve9) => setTimeout(resolve9, 2000));
14423
+ await new Promise((resolve10) => setTimeout(resolve10, 2000));
14119
14424
  } catch {
14120
14425
  break;
14121
14426
  }
@@ -14125,7 +14430,7 @@ async function acquireScoutOnAir(agent, timeoutMs = 30000) {
14125
14430
  }
14126
14431
  async function releaseScoutOnAir() {
14127
14432
  try {
14128
- await unlink(join13(relayHubDirectory(), "on-air.lock"));
14433
+ await unlink(join14(relayHubDirectory(), "on-air.lock"));
14129
14434
  } catch {}
14130
14435
  }
14131
14436
  async function speakScoutText(text, voice) {
@@ -14172,10 +14477,10 @@ async function speakScoutText(text, voice) {
14172
14477
  player.stdin.write(value);
14173
14478
  }
14174
14479
  player.stdin.end();
14175
- await new Promise((resolve9) => player.on("close", () => resolve9()));
14480
+ await new Promise((resolve10) => player.on("close", () => resolve10()));
14176
14481
  }
14177
14482
  function buildScoutEnrollmentPrompt(input) {
14178
- const relayLogPath = join13(relayHubDirectory(), "channel.log");
14483
+ const relayLogPath = join14(relayHubDirectory(), "channel.log");
14179
14484
  const cliCommand = input.cliCommand?.trim() || "scout";
14180
14485
  const task = input.task?.trim();
14181
14486
  return [
@@ -14219,7 +14524,7 @@ __export(exports_ask, {
14219
14524
  });
14220
14525
  function renderAskCommandHelp() {
14221
14526
  return [
14222
- "Usage: scout ask --to <agent> [--as <sender>] [--channel <name>] [--timeout <seconds>] [--harness <runtime>] <message>",
14527
+ "Usage: scout ask --to <agent> [--as <sender>] [--channel <name>] [--timeout <seconds>] [--harness <runtime>] [--prompt-file <path> | <message>]",
14223
14528
  "",
14224
14529
  "Ask one agent to do work or return a concrete answer.",
14225
14530
  "",
@@ -14231,14 +14536,20 @@ function renderAskCommandHelp() {
14231
14536
  'Use ask when the meaning is "do this and get back to me."',
14232
14537
  "Keep progress, review, and completion in that same DM or explicit channel.",
14233
14538
  "",
14539
+ "Input:",
14540
+ " inline message -> primary prompt body",
14541
+ " --prompt-file <path> -> read the primary prompt body from a UTF-8 file",
14542
+ " --body-file <path> -> alias for --prompt-file",
14543
+ "",
14234
14544
  "Examples:",
14235
14545
  ' scout ask --to hudson "review the parser"',
14546
+ " scout ask --to hudson --prompt-file ./handoff.md",
14236
14547
  ' scout ask --as premotion.master.mini --to hudson "build the editor"',
14237
14548
  ' scout ask --to vox.harness:codex "take another pass on the runtime fix"'
14238
14549
  ].join(`
14239
14550
  `);
14240
14551
  }
14241
- function renderScoutTargetLabel(targetLabel) {
14552
+ function renderScoutTargetLabel2(targetLabel) {
14242
14553
  const trimmed = targetLabel.trim();
14243
14554
  return trimmed.startsWith("@") ? trimmed : `@${trimmed}`;
14244
14555
  }
@@ -14258,7 +14569,7 @@ function renderConversationRoute(conversationId) {
14258
14569
  return conversationId.startsWith("dm.") ? `DM ${conversationId}` : `conversation ${conversationId}`;
14259
14570
  }
14260
14571
  function formatScoutAskRoutingError(result, targetLabel) {
14261
- const renderedTarget = renderScoutTargetLabel(targetLabel);
14572
+ const renderedTarget = renderScoutTargetLabel2(targetLabel);
14262
14573
  const diagnostic = result.targetDiagnostic;
14263
14574
  if (diagnostic?.state === "ambiguous") {
14264
14575
  const rendered = diagnostic.candidates.map((candidate) => renderAmbiguousCandidate(candidate.label || candidate.agentId)).filter((label) => label.length > 0);
@@ -14279,6 +14590,17 @@ function formatScoutAskRoutingError(result, targetLabel) {
14279
14590
  }
14280
14591
  return `target ${renderedTarget} is offline; nothing was sent. Run \`scout who\` to inspect the target before retrying.`;
14281
14592
  }
14593
+ if (diagnostic?.state === "unavailable") {
14594
+ const runtime = diagnostic.transport ? ` (${diagnostic.transport})` : "";
14595
+ const wakePolicy = diagnostic.wakePolicy ? ` [wake:${diagnostic.wakePolicy}]` : "";
14596
+ return `target ${renderedTarget} is known but currently unavailable${runtime}${wakePolicy}; nothing was sent. ${diagnostic.detail}`;
14597
+ }
14598
+ if (diagnostic?.state === "unknown") {
14599
+ return `there is no ${renderedTarget}; nothing was sent.`;
14600
+ }
14601
+ if (diagnostic?.state === "invalid" || diagnostic?.state === "missing") {
14602
+ return `${diagnostic.detail}; nothing was sent.`;
14603
+ }
14282
14604
  return `target ${renderedTarget} is not currently routable; nothing was sent.`;
14283
14605
  }
14284
14606
  async function runAskCommand(context, args) {
@@ -14292,10 +14614,11 @@ async function runAskCommand(context, args) {
14292
14614
  async function runAskWithOptions(context, options) {
14293
14615
  const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
14294
14616
  const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
14617
+ const body = await resolvePromptBody(options);
14295
14618
  const result = await askScoutQuestion({
14296
14619
  senderId,
14297
14620
  targetLabel: options.targetLabel,
14298
- body: options.message,
14621
+ body,
14299
14622
  channel: options.channel,
14300
14623
  executionHarness: parseScoutHarness(options.harness),
14301
14624
  currentDirectory
@@ -14323,6 +14646,7 @@ var HELP_FLAGS;
14323
14646
  var init_ask = __esm(async () => {
14324
14647
  init_context();
14325
14648
  init_options();
14649
+ init_input_file();
14326
14650
  await init_service();
14327
14651
  HELP_FLAGS = new Set(["--help", "-h"]);
14328
14652
  });
@@ -14474,7 +14798,7 @@ __export(exports_send, {
14474
14798
  });
14475
14799
  function renderSendCommandHelp() {
14476
14800
  return [
14477
- "Usage: scout send [--as <sender>] [--channel <name>] [--speak] [--harness <runtime>] <message>",
14801
+ "Usage: scout send [--as <sender>] [--channel <name>] [--speak] [--harness <runtime>] [--message-file <path> | <message>]",
14478
14802
  "",
14479
14803
  "Tell or update another agent or an explicit channel.",
14480
14804
  "",
@@ -14487,8 +14811,14 @@ function renderSendCommandHelp() {
14487
14811
  "Use send for heads-up, replies, and status updates.",
14488
14812
  'Use `scout ask` when the meaning is "do this and get back to me."',
14489
14813
  "",
14814
+ "Input:",
14815
+ " inline message -> message body",
14816
+ " --message-file <path> -> read the message body from a UTF-8 file",
14817
+ " --body-file <path> -> alias for --message-file",
14818
+ "",
14490
14819
  "Examples:",
14491
14820
  ' scout send "@hudson ready for review"',
14821
+ " scout send --channel triage --message-file ./status.md",
14492
14822
  ' scout send --as premotion.master.mini "@hudson editor branch is green"',
14493
14823
  ' scout send --channel triage "need two reviewers"'
14494
14824
  ].join(`
@@ -14501,8 +14831,31 @@ function renderTargetLabel(label) {
14501
14831
  }
14502
14832
  return trimmed.startsWith("@") ? trimmed : `@${trimmed}`;
14503
14833
  }
14504
- function formatScoutSendRoutingError(unresolvedTargets) {
14505
- const rendered = unresolvedTargets.map(renderTargetLabel).filter((label) => label.length > 0);
14834
+ function renderAmbiguousCandidate2(label) {
14835
+ const rendered = renderTargetLabel(label);
14836
+ return rendered || label.trim();
14837
+ }
14838
+ function formatScoutSendRoutingError(result) {
14839
+ const diagnostic = result.targetDiagnostic;
14840
+ if (diagnostic?.state === "unknown") {
14841
+ return `there is no ${renderTargetLabel(diagnostic.agentId)}; nothing was sent.`;
14842
+ }
14843
+ if (diagnostic?.state === "ambiguous") {
14844
+ const renderedCandidates = diagnostic.candidates.map((candidate) => renderAmbiguousCandidate2(candidate.label || candidate.agentId)).filter((label) => label.length > 0);
14845
+ if (renderedCandidates.length > 0) {
14846
+ return `target ${renderTargetLabel(result.unresolvedTargets[0] ?? "")} matches multiple agents: ${renderedCandidates.join(", ")}. Re-run with the fully qualified form (e.g. \`scout send "${renderedCandidates[0]} ..."\`).`;
14847
+ }
14848
+ return `target ${renderTargetLabel(result.unresolvedTargets[0] ?? "")} matches multiple agents; nothing was sent. Re-run with a fully qualified @handle to disambiguate.`;
14849
+ }
14850
+ if (diagnostic?.state === "unavailable") {
14851
+ const runtime = diagnostic.transport ? ` (${diagnostic.transport})` : "";
14852
+ const wakePolicy = diagnostic.wakePolicy ? ` [wake:${diagnostic.wakePolicy}]` : "";
14853
+ return `target ${renderTargetLabel(result.unresolvedTargets[0] ?? diagnostic.agentId)} is known but currently unavailable${runtime}${wakePolicy}; nothing was sent. ${diagnostic.detail}`;
14854
+ }
14855
+ if (diagnostic?.state === "invalid" || diagnostic?.state === "missing") {
14856
+ return `${diagnostic.detail}; nothing was sent.`;
14857
+ }
14858
+ const rendered = result.unresolvedTargets.map(renderTargetLabel).filter((label) => label.length > 0);
14506
14859
  if (rendered.length === 1) {
14507
14860
  return `target ${rendered[0]} is not uniquely routable; nothing was sent.`;
14508
14861
  }
@@ -14522,9 +14875,10 @@ async function runSendCommand(context, args) {
14522
14875
  const options = parseSendCommandOptions(args, defaultScoutContextDirectory(context));
14523
14876
  const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
14524
14877
  const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
14878
+ const body = await resolveMessageBody(options);
14525
14879
  const result = await sendScoutMessage({
14526
14880
  senderId,
14527
- body: options.message,
14881
+ body,
14528
14882
  channel: options.channel,
14529
14883
  shouldSpeak: options.shouldSpeak,
14530
14884
  executionHarness: parseScoutHarness(options.harness),
@@ -14534,7 +14888,7 @@ async function runSendCommand(context, args) {
14534
14888
  throw new Error("broker is not reachable");
14535
14889
  }
14536
14890
  if (result.unresolvedTargets.length > 0) {
14537
- throw new Error(formatScoutSendRoutingError(result.unresolvedTargets));
14891
+ throw new Error(formatScoutSendRoutingError(result));
14538
14892
  }
14539
14893
  if (result.routingError) {
14540
14894
  throw new Error(formatScoutRouteChoiceError(result.routingError));
@@ -14542,7 +14896,7 @@ async function runSendCommand(context, args) {
14542
14896
  context.output.writeValue({
14543
14897
  senderId,
14544
14898
  conversationId: result.conversationId,
14545
- message: options.message,
14899
+ message: body,
14546
14900
  invokedTargets: result.invokedTargets,
14547
14901
  unresolvedTargets: result.unresolvedTargets,
14548
14902
  routeKind: result.routeKind
@@ -14551,6 +14905,7 @@ async function runSendCommand(context, args) {
14551
14905
  var HELP_FLAGS2;
14552
14906
  var init_send = __esm(async () => {
14553
14907
  init_context();
14908
+ init_input_file();
14554
14909
  init_options();
14555
14910
  init_broker();
14556
14911
  await init_service();
@@ -14565,15 +14920,21 @@ __export(exports_broadcast, {
14565
14920
  });
14566
14921
  function renderBroadcastCommandHelp() {
14567
14922
  return [
14568
- "Usage: scout broadcast [--as <sender>] [--harness <runtime>] <message>",
14923
+ "Usage: scout broadcast [--as <sender>] [--harness <runtime>] [--message-file <path> | <message>]",
14569
14924
  "",
14570
14925
  "Broadcast to channel.shared.",
14571
14926
  "",
14572
14927
  "Use this only when the message is genuinely for everyone.",
14573
14928
  "Do not use broadcast for ordinary one-to-one delegation or small-group coordination.",
14574
14929
  "",
14930
+ "Input:",
14931
+ " inline message -> broadcast body",
14932
+ " --message-file <path> -> read the broadcast body from a UTF-8 file",
14933
+ " --body-file <path> -> alias for --message-file",
14934
+ "",
14575
14935
  "Examples:",
14576
14936
  ' scout broadcast "deploying in 15m, pause long flights"',
14937
+ " scout broadcast --message-file ./maintenance.md",
14577
14938
  ' scout broadcast --as scout.main.mini "broker maintenance starts now"'
14578
14939
  ].join(`
14579
14940
  `);
@@ -14589,9 +14950,10 @@ async function runBroadcastCommand(context, args) {
14589
14950
  }
14590
14951
  const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
14591
14952
  const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
14953
+ const body = await resolveMessageBody(options);
14592
14954
  const result = await sendScoutMessage({
14593
14955
  senderId,
14594
- body: `@all ${options.message}`,
14956
+ body: `@all ${body}`,
14595
14957
  channel: "shared",
14596
14958
  executionHarness: parseScoutHarness(options.harness),
14597
14959
  currentDirectory
@@ -14600,10 +14962,10 @@ async function runBroadcastCommand(context, args) {
14600
14962
  throw new Error("broker is not reachable");
14601
14963
  }
14602
14964
  if (result.unresolvedTargets.length > 0) {
14603
- throw new Error(formatScoutSendRoutingError(result.unresolvedTargets));
14965
+ throw new Error(formatScoutSendRoutingError(result));
14604
14966
  }
14605
14967
  context.output.writeValue({
14606
- message: options.message,
14968
+ message: body,
14607
14969
  invokedTargets: result.invokedTargets,
14608
14970
  unresolvedTargets: result.unresolvedTargets,
14609
14971
  routeKind: result.routeKind
@@ -14613,6 +14975,7 @@ var HELP_FLAGS3;
14613
14975
  var init_broadcast = __esm(async () => {
14614
14976
  init_context();
14615
14977
  init_errors();
14978
+ init_input_file();
14616
14979
  init_options();
14617
14980
  init_broker();
14618
14981
  await __promiseAll([
@@ -14759,7 +15122,8 @@ async function createScoutAgentCard(input) {
14759
15122
  displayName: input.displayName,
14760
15123
  harness: input.harness,
14761
15124
  model: input.model,
14762
- currentDirectory: input.currentDirectory
15125
+ currentDirectory: input.currentDirectory,
15126
+ ensureOnline: false
14763
15127
  });
14764
15128
  const currentDirectory = input.currentDirectory ?? input.projectPath;
14765
15129
  const broker = await loadScoutBrokerContext();
@@ -14857,11 +15221,11 @@ function renderCardCommandHelp() {
14857
15221
  `);
14858
15222
  }
14859
15223
  function promptWithDefault(question, defaultValue) {
14860
- return new Promise((resolve9) => {
15224
+ return new Promise((resolve10) => {
14861
15225
  const rl = createInterface({ input: process.stdin, output: process.stderr });
14862
15226
  rl.question(` ${question} [${defaultValue}]: `, (answer) => {
14863
15227
  rl.close();
14864
- resolve9(answer.trim() || defaultValue);
15228
+ resolve10(answer.trim() || defaultValue);
14865
15229
  });
14866
15230
  });
14867
15231
  }
@@ -35590,7 +35954,7 @@ class Protocol {
35590
35954
  return;
35591
35955
  }
35592
35956
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
35593
- await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
35957
+ await new Promise((resolve10) => setTimeout(resolve10, pollInterval));
35594
35958
  options?.signal?.throwIfAborted();
35595
35959
  }
35596
35960
  } catch (error48) {
@@ -35602,7 +35966,7 @@ class Protocol {
35602
35966
  }
35603
35967
  request(request, resultSchema, options) {
35604
35968
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
35605
- return new Promise((resolve9, reject) => {
35969
+ return new Promise((resolve10, reject) => {
35606
35970
  const earlyReject = (error48) => {
35607
35971
  reject(error48);
35608
35972
  };
@@ -35680,7 +36044,7 @@ class Protocol {
35680
36044
  if (!parseResult.success) {
35681
36045
  reject(parseResult.error);
35682
36046
  } else {
35683
- resolve9(parseResult.data);
36047
+ resolve10(parseResult.data);
35684
36048
  }
35685
36049
  } catch (error48) {
35686
36050
  reject(error48);
@@ -35871,12 +36235,12 @@ class Protocol {
35871
36235
  interval = task.pollInterval;
35872
36236
  }
35873
36237
  } catch {}
35874
- return new Promise((resolve9, reject) => {
36238
+ return new Promise((resolve10, reject) => {
35875
36239
  if (signal.aborted) {
35876
36240
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
35877
36241
  return;
35878
36242
  }
35879
- const timeoutId = setTimeout(resolve9, interval);
36243
+ const timeoutId = setTimeout(resolve10, interval);
35880
36244
  signal.addEventListener("abort", () => {
35881
36245
  clearTimeout(timeoutId);
35882
36246
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -36353,11 +36717,11 @@ var require_codegen = __commonJS((exports) => {
36353
36717
  const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`;
36354
36718
  return `${varKind} ${this.name}${rhs};` + _n;
36355
36719
  }
36356
- optimizeNames(names, constants4) {
36720
+ optimizeNames(names, constants5) {
36357
36721
  if (!names[this.name.str])
36358
36722
  return;
36359
36723
  if (this.rhs)
36360
- this.rhs = optimizeExpr(this.rhs, names, constants4);
36724
+ this.rhs = optimizeExpr(this.rhs, names, constants5);
36361
36725
  return this;
36362
36726
  }
36363
36727
  get names() {
@@ -36375,10 +36739,10 @@ var require_codegen = __commonJS((exports) => {
36375
36739
  render({ _n }) {
36376
36740
  return `${this.lhs} = ${this.rhs};` + _n;
36377
36741
  }
36378
- optimizeNames(names, constants4) {
36742
+ optimizeNames(names, constants5) {
36379
36743
  if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
36380
36744
  return;
36381
- this.rhs = optimizeExpr(this.rhs, names, constants4);
36745
+ this.rhs = optimizeExpr(this.rhs, names, constants5);
36382
36746
  return this;
36383
36747
  }
36384
36748
  get names() {
@@ -36444,8 +36808,8 @@ var require_codegen = __commonJS((exports) => {
36444
36808
  optimizeNodes() {
36445
36809
  return `${this.code}` ? this : undefined;
36446
36810
  }
36447
- optimizeNames(names, constants4) {
36448
- this.code = optimizeExpr(this.code, names, constants4);
36811
+ optimizeNames(names, constants5) {
36812
+ this.code = optimizeExpr(this.code, names, constants5);
36449
36813
  return this;
36450
36814
  }
36451
36815
  get names() {
@@ -36475,12 +36839,12 @@ var require_codegen = __commonJS((exports) => {
36475
36839
  }
36476
36840
  return nodes.length > 0 ? this : undefined;
36477
36841
  }
36478
- optimizeNames(names, constants4) {
36842
+ optimizeNames(names, constants5) {
36479
36843
  const { nodes } = this;
36480
36844
  let i = nodes.length;
36481
36845
  while (i--) {
36482
36846
  const n = nodes[i];
36483
- if (n.optimizeNames(names, constants4))
36847
+ if (n.optimizeNames(names, constants5))
36484
36848
  continue;
36485
36849
  subtractNames(names, n.names);
36486
36850
  nodes.splice(i, 1);
@@ -36537,12 +36901,12 @@ var require_codegen = __commonJS((exports) => {
36537
36901
  return;
36538
36902
  return this;
36539
36903
  }
36540
- optimizeNames(names, constants4) {
36904
+ optimizeNames(names, constants5) {
36541
36905
  var _a2;
36542
- this.else = (_a2 = this.else) === null || _a2 === undefined ? undefined : _a2.optimizeNames(names, constants4);
36543
- if (!(super.optimizeNames(names, constants4) || this.else))
36906
+ this.else = (_a2 = this.else) === null || _a2 === undefined ? undefined : _a2.optimizeNames(names, constants5);
36907
+ if (!(super.optimizeNames(names, constants5) || this.else))
36544
36908
  return;
36545
- this.condition = optimizeExpr(this.condition, names, constants4);
36909
+ this.condition = optimizeExpr(this.condition, names, constants5);
36546
36910
  return this;
36547
36911
  }
36548
36912
  get names() {
@@ -36567,10 +36931,10 @@ var require_codegen = __commonJS((exports) => {
36567
36931
  render(opts) {
36568
36932
  return `for(${this.iteration})` + super.render(opts);
36569
36933
  }
36570
- optimizeNames(names, constants4) {
36571
- if (!super.optimizeNames(names, constants4))
36934
+ optimizeNames(names, constants5) {
36935
+ if (!super.optimizeNames(names, constants5))
36572
36936
  return;
36573
- this.iteration = optimizeExpr(this.iteration, names, constants4);
36937
+ this.iteration = optimizeExpr(this.iteration, names, constants5);
36574
36938
  return this;
36575
36939
  }
36576
36940
  get names() {
@@ -36608,10 +36972,10 @@ var require_codegen = __commonJS((exports) => {
36608
36972
  render(opts) {
36609
36973
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
36610
36974
  }
36611
- optimizeNames(names, constants4) {
36612
- if (!super.optimizeNames(names, constants4))
36975
+ optimizeNames(names, constants5) {
36976
+ if (!super.optimizeNames(names, constants5))
36613
36977
  return;
36614
- this.iterable = optimizeExpr(this.iterable, names, constants4);
36978
+ this.iterable = optimizeExpr(this.iterable, names, constants5);
36615
36979
  return this;
36616
36980
  }
36617
36981
  get names() {
@@ -36656,11 +37020,11 @@ var require_codegen = __commonJS((exports) => {
36656
37020
  (_b = this.finally) === null || _b === undefined || _b.optimizeNodes();
36657
37021
  return this;
36658
37022
  }
36659
- optimizeNames(names, constants4) {
37023
+ optimizeNames(names, constants5) {
36660
37024
  var _a2, _b;
36661
- super.optimizeNames(names, constants4);
36662
- (_a2 = this.catch) === null || _a2 === undefined || _a2.optimizeNames(names, constants4);
36663
- (_b = this.finally) === null || _b === undefined || _b.optimizeNames(names, constants4);
37025
+ super.optimizeNames(names, constants5);
37026
+ (_a2 = this.catch) === null || _a2 === undefined || _a2.optimizeNames(names, constants5);
37027
+ (_b = this.finally) === null || _b === undefined || _b.optimizeNames(names, constants5);
36664
37028
  return this;
36665
37029
  }
36666
37030
  get names() {
@@ -36934,7 +37298,7 @@ var require_codegen = __commonJS((exports) => {
36934
37298
  function addExprNames(names, from) {
36935
37299
  return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
36936
37300
  }
36937
- function optimizeExpr(expr, names, constants4) {
37301
+ function optimizeExpr(expr, names, constants5) {
36938
37302
  if (expr instanceof code_1.Name)
36939
37303
  return replaceName(expr);
36940
37304
  if (!canOptimize(expr))
@@ -36949,14 +37313,14 @@ var require_codegen = __commonJS((exports) => {
36949
37313
  return items;
36950
37314
  }, []));
36951
37315
  function replaceName(n) {
36952
- const c = constants4[n.str];
37316
+ const c = constants5[n.str];
36953
37317
  if (c === undefined || names[n.str] !== 1)
36954
37318
  return n;
36955
37319
  delete names[n.str];
36956
37320
  return c;
36957
37321
  }
36958
37322
  function canOptimize(e) {
36959
- return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants4[c.str] !== undefined);
37323
+ return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants5[c.str] !== undefined);
36960
37324
  }
36961
37325
  }
36962
37326
  function subtractNames(names, from) {
@@ -38861,7 +39225,7 @@ var require_compile = __commonJS((exports) => {
38861
39225
  const schOrFunc = root.refs[ref];
38862
39226
  if (schOrFunc)
38863
39227
  return schOrFunc;
38864
- let _sch = resolve9.call(this, root, ref);
39228
+ let _sch = resolve10.call(this, root, ref);
38865
39229
  if (_sch === undefined) {
38866
39230
  const schema = (_a2 = root.localRefs) === null || _a2 === undefined ? undefined : _a2[ref];
38867
39231
  const { schemaId } = this.opts;
@@ -38888,7 +39252,7 @@ var require_compile = __commonJS((exports) => {
38888
39252
  function sameSchemaEnv(s1, s2) {
38889
39253
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
38890
39254
  }
38891
- function resolve9(root, ref) {
39255
+ function resolve10(root, ref) {
38892
39256
  let sch;
38893
39257
  while (typeof (sch = this.refs[ref]) == "string")
38894
39258
  ref = sch;
@@ -39418,7 +39782,7 @@ var require_fast_uri = __commonJS((exports, module) => {
39418
39782
  }
39419
39783
  return uri;
39420
39784
  }
39421
- function resolve9(baseURI, relativeURI, options) {
39785
+ function resolve10(baseURI, relativeURI, options) {
39422
39786
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
39423
39787
  const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
39424
39788
  schemelessOptions.skipEscape = true;
@@ -39646,7 +40010,7 @@ var require_fast_uri = __commonJS((exports, module) => {
39646
40010
  var fastUri = {
39647
40011
  SCHEMES,
39648
40012
  normalize,
39649
- resolve: resolve9,
40013
+ resolve: resolve10,
39650
40014
  resolveComponent,
39651
40015
  equal,
39652
40016
  serialize,
@@ -43029,12 +43393,12 @@ class StdioServerTransport {
43029
43393
  this.onclose?.();
43030
43394
  }
43031
43395
  send(message) {
43032
- return new Promise((resolve9) => {
43396
+ return new Promise((resolve10) => {
43033
43397
  const json2 = serializeMessage(message);
43034
43398
  if (this._stdout.write(json2)) {
43035
- resolve9();
43399
+ resolve10();
43036
43400
  } else {
43037
- this._stdout.once("drain", resolve9);
43401
+ this._stdout.once("drain", resolve10);
43038
43402
  }
43039
43403
  });
43040
43404
  }
@@ -43050,7 +43414,7 @@ var init_product = __esm(() => {
43050
43414
  });
43051
43415
 
43052
43416
  // ../../apps/desktop/src/core/mcp/scout-channel.ts
43053
- import { resolve as resolve9 } from "path";
43417
+ import { resolve as resolve10 } from "path";
43054
43418
  async function resolveAgentId(currentDirectory, env) {
43055
43419
  return resolveScoutSenderId(undefined, currentDirectory, env);
43056
43420
  }
@@ -43123,7 +43487,7 @@ function startBrokerSubscription(broker, agentId, onMessage, signal) {
43123
43487
  }
43124
43488
  async function runScoutChannelServer(options) {
43125
43489
  const env = options.env ?? process.env;
43126
- const currentDirectory = resolve9(options.defaultCurrentDirectory || process.cwd());
43490
+ const currentDirectory = resolve10(options.defaultCurrentDirectory || process.cwd());
43127
43491
  const agentId = await resolveAgentId(currentDirectory, env);
43128
43492
  const brokerUrl = env.OPENSCOUT_BROKER_URL?.trim() || resolveScoutBrokerUrl();
43129
43493
  const broker = await loadScoutBrokerContext(brokerUrl);
@@ -43435,8 +43799,11 @@ async function runDownCommand(context, args) {
43435
43799
  let agent = await downScoutAgent(target);
43436
43800
  if (!agent) {
43437
43801
  const resolved = await resolveLocalAgentByName(target);
43438
- if (resolved) {
43439
- agent = await downScoutAgent(resolved.agentId);
43802
+ const projectMatch = resolved ?? await resolveLocalAgentByName(target, {
43803
+ matchProjectName: true
43804
+ });
43805
+ if (projectMatch) {
43806
+ agent = await downScoutAgent(projectMatch.agentId);
43440
43807
  }
43441
43808
  }
43442
43809
  context.output.writeValue(agent, renderScoutDownResult);
@@ -43452,110 +43819,43 @@ var init_down = __esm(async () => {
43452
43819
 
43453
43820
  // ../../apps/desktop/src/app/host/runtime-service-client.ts
43454
43821
  import { spawn as spawn4 } from "child_process";
43455
- import { basename as basename6, dirname as dirname8, join as join14 } from "path";
43456
- import { existsSync as existsSync10 } from "fs";
43457
- import { fileURLToPath as fileURLToPath6 } from "url";
43458
- import { homedir as homedir9 } from "os";
43459
- function tryWhich(executableName) {
43460
- const pathEnv = process.env.PATH ?? "";
43461
- const sep = process.platform === "win32" ? ";" : ":";
43462
- for (const dir of pathEnv.split(sep)) {
43463
- if (!dir)
43464
- continue;
43465
- const candidate = join14(dir, executableName);
43466
- if (existsSync10(candidate)) {
43467
- return candidate;
43468
- }
43469
- if (process.platform === "win32") {
43470
- for (const ext of [".cmd", ".exe", ".bat"]) {
43471
- const withExt = candidate + ext;
43472
- if (existsSync10(withExt))
43473
- return withExt;
43474
- }
43475
- }
43476
- }
43477
- return null;
43478
- }
43479
- function findNodeModulesRuntimeBin() {
43480
- const runtimeBinRel = join14("node_modules", "@openscout", "runtime", "bin", "openscout-runtime.mjs");
43481
- let dir = dirname8(fileURLToPath6(import.meta.url));
43482
- for (let i = 0;i < 24; i++) {
43483
- const candidate = join14(dir, runtimeBinRel);
43484
- if (existsSync10(candidate))
43485
- return candidate;
43486
- const parent = dirname8(dir);
43487
- if (parent === dir)
43488
- break;
43489
- dir = parent;
43490
- }
43491
- const bunGlobal = join14(homedir9(), ".bun", "install", "global", "node_modules", "@openscout", "runtime", "bin", "openscout-runtime.mjs");
43492
- if (existsSync10(bunGlobal))
43493
- return bunGlobal;
43494
- return null;
43495
- }
43496
- function findMonorepoOpenscoutRuntimeBin() {
43497
- let dir = process.cwd();
43498
- for (let i = 0;i < 24; i++) {
43499
- const candidate = join14(dir, "packages", "runtime", "bin", "openscout-runtime.mjs");
43500
- if (existsSync10(candidate)) {
43501
- return candidate;
43502
- }
43503
- const parent = dirname8(dir);
43504
- if (parent === dir)
43505
- break;
43506
- dir = parent;
43507
- }
43508
- return null;
43509
- }
43822
+ import { dirname as dirname9 } from "path";
43823
+ import { fileURLToPath as fileURLToPath7 } from "url";
43510
43824
  function resolveJavaScriptRuntimeExecutable() {
43511
- const explicit = process.env.OPENSCOUT_RUNTIME_NODE_BIN?.trim();
43512
- if (explicit) {
43513
- if (existsSync10(explicit)) {
43514
- return explicit;
43515
- }
43516
- const found = tryWhich(explicit);
43517
- if (found) {
43518
- return found;
43519
- }
43520
- throw new Error(`OPENSCOUT_RUNTIME_NODE_BIN is set but not found: ${explicit}`);
43521
- }
43522
- const execBase = basename6(process.execPath).toLowerCase();
43523
- if (execBase.startsWith("node") || execBase.startsWith("bun")) {
43524
- return process.execPath;
43525
- }
43526
- const nodeOnPath = tryWhich("node");
43527
- if (nodeOnPath) {
43528
- return nodeOnPath;
43529
- }
43530
- const bunOnPath = tryWhich("bun");
43531
- if (bunOnPath) {
43532
- return bunOnPath;
43825
+ const runtime = resolveJavaScriptRuntime({
43826
+ env: process.env,
43827
+ explicitEnvKeys: ["OPENSCOUT_RUNTIME_NODE_BIN"],
43828
+ allowNode: true,
43829
+ allowBun: true
43830
+ });
43831
+ if (runtime) {
43832
+ return runtime.path;
43533
43833
  }
43534
43834
  throw new Error("No JavaScript runtime found for openscout-runtime script entry. Install Node.js or set OPENSCOUT_RUNTIME_NODE_BIN.");
43535
43835
  }
43536
43836
  function resolveRuntimeServiceEntrypoint() {
43537
- const explicit = process.env.OPENSCOUT_RUNTIME_BIN?.trim();
43538
- if (explicit) {
43539
- if (existsSync10(explicit)) {
43540
- return explicit;
43541
- }
43542
- const found = tryWhich(explicit);
43543
- if (found) {
43544
- return found;
43545
- }
43546
- throw new Error(`OPENSCOUT_RUNTIME_BIN is set but not found: ${explicit}`);
43547
- }
43548
- const onPath = tryWhich("openscout-runtime");
43549
- if (onPath) {
43550
- return onPath;
43837
+ const installedExecutable = resolveExecutableFromSearch({
43838
+ env: process.env,
43839
+ envKeys: ["OPENSCOUT_RUNTIME_BIN"],
43840
+ names: ["openscout-runtime"]
43841
+ });
43842
+ if (installedExecutable) {
43843
+ return installedExecutable.path;
43551
43844
  }
43552
- const fromNodeModules = findNodeModulesRuntimeBin();
43845
+ const fromNodeModules = resolveNodeModulesPackageEntrypoint(import.meta.url, ["@openscout", "runtime"], "bin/openscout-runtime.mjs");
43553
43846
  if (fromNodeModules) {
43554
43847
  return fromNodeModules;
43555
43848
  }
43556
- const monorepo = findMonorepoOpenscoutRuntimeBin();
43557
- if (monorepo) {
43558
- return monorepo;
43849
+ const repoRoot = resolveOpenScoutRepoRoot({
43850
+ startDirectories: [
43851
+ process.env.OPENSCOUT_SETUP_CWD,
43852
+ process.cwd(),
43853
+ dirname9(fileURLToPath7(import.meta.url))
43854
+ ]
43855
+ });
43856
+ const repoEntrypoint = resolveRepoEntrypoint(repoRoot, "packages/runtime/bin/openscout-runtime.mjs");
43857
+ if (repoEntrypoint) {
43858
+ return repoEntrypoint;
43559
43859
  }
43560
43860
  throw new Error("openscout-runtime not found on PATH. Install @openscout/runtime (e.g. npm i -g @openscout/runtime) or set OPENSCOUT_RUNTIME_BIN.");
43561
43861
  }
@@ -43624,12 +43924,14 @@ async function getRuntimeBrokerServiceStatus(options = {}) {
43624
43924
  return inflightBrokerStatus;
43625
43925
  }
43626
43926
  var BROKER_STATUS_CACHE_TTL_MS = 2000, cachedBrokerStatus = null, inflightBrokerStatus = null;
43627
- var init_runtime_service_client = () => {};
43927
+ var init_runtime_service_client = __esm(() => {
43928
+ init_tool_resolution();
43929
+ });
43628
43930
 
43629
43931
  // ../../apps/desktop/src/core/setup/command-lock.ts
43630
- import { mkdir as mkdir8, open, readFile as readFile8, rm as rm6 } from "fs/promises";
43932
+ import { mkdir as mkdir8, open, readFile as readFile9, rm as rm6 } from "fs/promises";
43631
43933
  import { hostname as hostname4 } from "os";
43632
- import { dirname as dirname9, join as join15 } from "path";
43934
+ import { dirname as dirname10, join as join15 } from "path";
43633
43935
  function scoutCoreCommandLockPath() {
43634
43936
  return join15(resolveOpenScoutSupportPaths().runtimeDirectory, "locks", SCOUT_CORE_COMMAND_LOCK_FILE);
43635
43937
  }
@@ -43655,7 +43957,7 @@ function isProcessAlive(pid) {
43655
43957
  }
43656
43958
  async function readLockPayload(lockPath) {
43657
43959
  try {
43658
- const raw = JSON.parse(await readFile8(lockPath, "utf8"));
43960
+ const raw = JSON.parse(await readFile9(lockPath, "utf8"));
43659
43961
  if ((raw.command === "setup" || raw.command === "doctor" || raw.command === "runtimes") && typeof raw.pid === "number" && typeof raw.startedAt === "number" && typeof raw.host === "string") {
43660
43962
  return {
43661
43963
  command: raw.command,
@@ -43671,7 +43973,7 @@ async function readLockPayload(lockPath) {
43671
43973
  }
43672
43974
  async function acquireScoutCoreCommandLock(command) {
43673
43975
  const lockPath = scoutCoreCommandLockPath();
43674
- await mkdir8(dirname9(lockPath), { recursive: true });
43976
+ await mkdir8(dirname10(lockPath), { recursive: true });
43675
43977
  for (let attempt = 0;attempt < 2; attempt += 1) {
43676
43978
  const payload = {
43677
43979
  command,
@@ -43814,8 +44116,8 @@ var init_service3 = __esm(() => {
43814
44116
 
43815
44117
  // ../../apps/desktop/src/shared/paths.ts
43816
44118
  import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
43817
- import { dirname as dirname10, join as join16, resolve as resolve10 } from "path";
43818
- import { fileURLToPath as fileURLToPath7 } from "url";
44119
+ import { dirname as dirname11, join as join16, resolve as resolve11 } from "path";
44120
+ import { fileURLToPath as fileURLToPath8 } from "url";
43819
44121
  function looksLikeWorkspaceRoot(candidate) {
43820
44122
  const packageJsonPath = join16(candidate, "package.json");
43821
44123
  if (!existsSync11(packageJsonPath)) {
@@ -43844,12 +44146,12 @@ function looksLikeSourceAppRoot(candidate) {
43844
44146
  return existsSync11(join16(candidate, "bin", "scout.ts"));
43845
44147
  }
43846
44148
  function findMatchingAncestor(startDirectory, predicate) {
43847
- let current = resolve10(startDirectory);
44149
+ let current = resolve11(startDirectory);
43848
44150
  while (true) {
43849
44151
  if (predicate(current)) {
43850
44152
  return current;
43851
44153
  }
43852
- const parent = dirname10(current);
44154
+ const parent = dirname11(current);
43853
44155
  if (parent === current) {
43854
44156
  return null;
43855
44157
  }
@@ -43857,7 +44159,7 @@ function findMatchingAncestor(startDirectory, predicate) {
43857
44159
  }
43858
44160
  }
43859
44161
  function defaultModuleDirectory() {
43860
- return dirname10(fileURLToPath7(import.meta.url));
44162
+ return dirname11(fileURLToPath8(import.meta.url));
43861
44163
  }
43862
44164
  function uniqueResolutionStarts(options) {
43863
44165
  const starts = [
@@ -43866,7 +44168,7 @@ function uniqueResolutionStarts(options) {
43866
44168
  process.cwd(),
43867
44169
  options.moduleDirectory?.trim()
43868
44170
  ];
43869
- const resolvedStarts = starts.filter((value) => Boolean(value)).map((value) => resolve10(value));
44171
+ const resolvedStarts = starts.filter((value) => Boolean(value)).map((value) => resolve11(value));
43870
44172
  return [...new Set(resolvedStarts)];
43871
44173
  }
43872
44174
  function resolveScoutWorkspaceRoot(options = {}) {
@@ -43895,7 +44197,7 @@ function resolveScoutAppRoot(options = {}) {
43895
44197
  }
43896
44198
  const workspaceRoot = resolveScoutWorkspaceRoot(options);
43897
44199
  for (const relativePath of [["apps", "desktop"], ["apps", "scout"], ["packages", "cli"]]) {
43898
- const candidate = resolve10(workspaceRoot, ...relativePath);
44200
+ const candidate = resolve11(workspaceRoot, ...relativePath);
43899
44201
  if (looksLikeSourceAppRoot(candidate)) {
43900
44202
  return candidate;
43901
44203
  }
@@ -44265,7 +44567,7 @@ import {
44265
44567
  writeFileSync as writeFileSync4
44266
44568
  } from "fs";
44267
44569
  import { homedir as homedir10 } from "os";
44268
- import { dirname as dirname11, join as join18 } from "path";
44570
+ import { dirname as dirname12, join as join18 } from "path";
44269
44571
  function localConfigHome() {
44270
44572
  return process.env.OPENSCOUT_HOME ?? join18(homedir10(), ".openscout");
44271
44573
  }
@@ -44307,9 +44609,12 @@ function validateLocalConfig(input) {
44307
44609
  function isValidPort(value) {
44308
44610
  return typeof value === "number" && Number.isInteger(value) && value > 0 && value < 65536;
44309
44611
  }
44612
+ function resolveWebPort() {
44613
+ return loadLocalConfig().ports?.web ?? DEFAULT_LOCAL_CONFIG.ports.web;
44614
+ }
44310
44615
  function writeLocalConfig(config2) {
44311
44616
  const configPath = localConfigPath();
44312
- mkdirSync4(dirname11(configPath), { recursive: true });
44617
+ mkdirSync4(dirname12(configPath), { recursive: true });
44313
44618
  const body = JSON.stringify({ ...config2, version: LOCAL_CONFIG_VERSION }, null, 2) + `
44314
44619
  `;
44315
44620
  const tmp = `${configPath}.tmp`;
@@ -44742,7 +45047,7 @@ class McpServer {
44742
45047
  let task = createTaskResult.task;
44743
45048
  const pollInterval = task.pollInterval ?? 5000;
44744
45049
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
44745
- await new Promise((resolve11) => setTimeout(resolve11, pollInterval));
45050
+ await new Promise((resolve12) => setTimeout(resolve12, pollInterval));
44746
45051
  const updatedTask = await extra.taskStore.getTask(taskId);
44747
45052
  if (!updatedTask) {
44748
45053
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -45291,7 +45596,41 @@ var init_mcp = __esm(() => {
45291
45596
  });
45292
45597
 
45293
45598
  // ../../apps/desktop/src/core/mcp/scout-mcp.ts
45294
- import { basename as basename7, resolve as resolve11 } from "path";
45599
+ import { basename as basename7, resolve as resolve12 } from "path";
45600
+ function createAgentPickerFieldMeta(input) {
45601
+ return {
45602
+ kind: "agent-picker",
45603
+ selection: input.selection,
45604
+ sourceTool: "agents_search",
45605
+ resolveTool: input.resolveTool,
45606
+ sourceArguments: {
45607
+ query: { from: "value" },
45608
+ currentDirectory: { fromToolArgument: "currentDirectory" }
45609
+ },
45610
+ resultPath: ["structuredContent", "candidates"],
45611
+ valueField: input.valueField,
45612
+ labelField: "label",
45613
+ descriptionField: "displayName",
45614
+ badgeFields: ["harness", "workspace", "node"],
45615
+ icon: scoutAgentAvatarMeta,
45616
+ search: {
45617
+ minQueryLength: 0,
45618
+ debounceMs: 100,
45619
+ cacheBy: ["currentDirectory"]
45620
+ }
45621
+ };
45622
+ }
45623
+ function createToolUiMeta(fields) {
45624
+ const value = {
45625
+ icon: scoutAgentToolIconMeta
45626
+ };
45627
+ if (fields && Object.keys(fields).length > 0) {
45628
+ value.fields = fields;
45629
+ }
45630
+ return {
45631
+ [SCOUT_MCP_UI_META_KEY]: value
45632
+ };
45633
+ }
45295
45634
  function createTextContent(value) {
45296
45635
  return [{ type: "text", text: JSON.stringify(value, null, 2) }];
45297
45636
  }
@@ -45302,9 +45641,9 @@ function isSameProjectRoot(left, right) {
45302
45641
  if (!left || !right) {
45303
45642
  return false;
45304
45643
  }
45305
- return resolve11(left) === resolve11(right);
45644
+ return resolve12(left) === resolve12(right);
45306
45645
  }
45307
- function matchesObviousProjectLocalAlias2(value, query) {
45646
+ function matchesObviousProjectLocalAlias(value, query) {
45308
45647
  const normalized = normalizeSearchValue(value);
45309
45648
  if (!normalized || !query) {
45310
45649
  return false;
@@ -45324,7 +45663,7 @@ function scoreProjectLocalCandidate(candidate, currentProjectRoot, query) {
45324
45663
  candidate.displayName,
45325
45664
  candidate.agentId
45326
45665
  ];
45327
- const matches = values.filter((value) => matchesObviousProjectLocalAlias2(value, query));
45666
+ const matches = values.filter((value) => matchesObviousProjectLocalAlias(value, query));
45328
45667
  if (matches.length === 0) {
45329
45668
  return -1;
45330
45669
  }
@@ -45803,7 +46142,7 @@ function createScoutMcpServer(options) {
45803
46142
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
45804
46143
  const resolvedSenderId = await deps.resolveSenderId(senderId, resolvedCurrentDirectory, env);
45805
46144
  const card = await deps.createAgentCard({
45806
- projectPath: resolve11(projectPath?.trim() || resolvedCurrentDirectory),
46145
+ projectPath: resolve12(projectPath?.trim() || resolvedCurrentDirectory),
45807
46146
  agentName: agentName?.trim() || undefined,
45808
46147
  displayName: displayName?.trim() || undefined,
45809
46148
  harness,
@@ -45825,7 +46164,7 @@ function createScoutMcpServer(options) {
45825
46164
  title: "Search Scout Agents",
45826
46165
  description: "Search the live Scout broker and discovered agent inventory for @mention candidates. Use this after whoami when you know roughly who you need but do not yet have an exact handle.",
45827
46166
  inputSchema: object2({
45828
- query: string2().optional(),
46167
+ query: string2().describe("Partial handle, label, or display name to search for").optional(),
45829
46168
  currentDirectory: string2().optional(),
45830
46169
  limit: number2().int().min(1).max(50).optional()
45831
46170
  }),
@@ -45835,7 +46174,8 @@ function createScoutMcpServer(options) {
45835
46174
  idempotentHint: true,
45836
46175
  destructiveHint: false,
45837
46176
  openWorldHint: false
45838
- }
46177
+ },
46178
+ _meta: createToolUiMeta()
45839
46179
  }, async ({ query, currentDirectory, limit }) => {
45840
46180
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
45841
46181
  const candidates = await deps.searchAgents({
@@ -45857,7 +46197,7 @@ function createScoutMcpServer(options) {
45857
46197
  title: "Resolve Scout Agent",
45858
46198
  description: "Resolve one exact Scout agent handle or return ambiguity details. Use this before send or ask when a short handle may be ambiguous.",
45859
46199
  inputSchema: object2({
45860
- label: string2().min(1),
46200
+ label: string2().min(1).describe("Scout agent handle or selector, such as @talkie"),
45861
46201
  currentDirectory: string2().optional()
45862
46202
  }),
45863
46203
  outputSchema: resolveResultSchema,
@@ -45866,7 +46206,8 @@ function createScoutMcpServer(options) {
45866
46206
  idempotentHint: true,
45867
46207
  destructiveHint: false,
45868
46208
  openWorldHint: false
45869
- }
46209
+ },
46210
+ _meta: createToolUiMeta()
45870
46211
  }, async ({ label, currentDirectory }) => {
45871
46212
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
45872
46213
  const resolution = await deps.resolveAgent({
@@ -45892,10 +46233,10 @@ function createScoutMcpServer(options) {
45892
46233
  body: string2().min(1),
45893
46234
  currentDirectory: string2().optional(),
45894
46235
  senderId: string2().optional(),
45895
- targetLabel: string2().optional(),
46236
+ targetLabel: targetLabelInputSchema,
45896
46237
  channel: string2().optional(),
45897
46238
  shouldSpeak: boolean2().optional(),
45898
- mentionAgentIds: array(string2()).optional()
46239
+ mentionAgentIds: mentionAgentIdsInputSchema
45899
46240
  }),
45900
46241
  outputSchema: sendResultSchema,
45901
46242
  annotations: {
@@ -45903,7 +46244,18 @@ function createScoutMcpServer(options) {
45903
46244
  idempotentHint: false,
45904
46245
  destructiveHint: false,
45905
46246
  openWorldHint: false
45906
- }
46247
+ },
46248
+ _meta: createToolUiMeta({
46249
+ targetLabel: createAgentPickerFieldMeta({
46250
+ selection: "single",
46251
+ valueField: "label",
46252
+ resolveTool: "agents_resolve"
46253
+ }),
46254
+ mentionAgentIds: createAgentPickerFieldMeta({
46255
+ selection: "multiple",
46256
+ valueField: "agentId"
46257
+ })
46258
+ })
45907
46259
  }, async ({
45908
46260
  body,
45909
46261
  currentDirectory,
@@ -45937,6 +46289,7 @@ function createScoutMcpServer(options) {
45937
46289
  messageId: result2.messageId ?? null,
45938
46290
  invokedTargetIds: result2.invokedTargetIds,
45939
46291
  unresolvedTargetIds: result2.unresolvedTargetIds,
46292
+ targetDiagnostic: result2.targetDiagnostic ?? null,
45940
46293
  routeKind: result2.routeKind ?? null,
45941
46294
  routingError: result2.routingError ?? null
45942
46295
  };
@@ -45963,6 +46316,7 @@ function createScoutMcpServer(options) {
45963
46316
  messageId: result2.messageId ?? null,
45964
46317
  invokedTargetIds: result2.invokedTargets,
45965
46318
  unresolvedTargetIds: result2.unresolvedTargets,
46319
+ targetDiagnostic: result2.targetDiagnostic ?? null,
45966
46320
  routeKind: result2.routeKind ?? null,
45967
46321
  routingError: result2.routingError ?? null
45968
46322
  };
@@ -45987,6 +46341,7 @@ function createScoutMcpServer(options) {
45987
46341
  messageId: result.messageId ?? null,
45988
46342
  invokedTargetIds: result.invokedTargets,
45989
46343
  unresolvedTargetIds: result.unresolvedTargets,
46344
+ targetDiagnostic: result.targetDiagnostic ?? null,
45990
46345
  routeKind: result.routeKind ?? null,
45991
46346
  routingError: result.routingError ?? null
45992
46347
  };
@@ -46002,8 +46357,8 @@ function createScoutMcpServer(options) {
46002
46357
  body: string2().min(1),
46003
46358
  currentDirectory: string2().optional(),
46004
46359
  senderId: string2().optional(),
46005
- targetAgentId: string2().optional(),
46006
- targetLabel: string2().optional(),
46360
+ targetAgentId: targetAgentIdInputSchema,
46361
+ targetLabel: targetLabelInputSchema,
46007
46362
  workItem: workItemInputSchema.optional(),
46008
46363
  channel: string2().optional(),
46009
46364
  shouldSpeak: boolean2().optional(),
@@ -46019,7 +46374,18 @@ function createScoutMcpServer(options) {
46019
46374
  idempotentHint: false,
46020
46375
  destructiveHint: false,
46021
46376
  openWorldHint: false
46022
- }
46377
+ },
46378
+ _meta: createToolUiMeta({
46379
+ targetAgentId: createAgentPickerFieldMeta({
46380
+ selection: "single",
46381
+ valueField: "agentId"
46382
+ }),
46383
+ targetLabel: createAgentPickerFieldMeta({
46384
+ selection: "single",
46385
+ valueField: "label",
46386
+ resolveTool: "agents_resolve"
46387
+ })
46388
+ })
46023
46389
  }, async ({
46024
46390
  body,
46025
46391
  currentDirectory,
@@ -46151,7 +46517,7 @@ async function runScoutMcpServer(options) {
46151
46517
  const transport = new StdioServerTransport;
46152
46518
  await server.connect(transport);
46153
46519
  }
46154
- var AGENT_STATE_VALUES, REGISTRATION_KIND_VALUES, RESOLVE_KIND_VALUES, MESSAGE_ROUTE_KIND_VALUES, MESSAGE_ROUTING_ERROR_VALUES, LOCAL_AGENT_HARNESS_VALUES, flightSchema, trackedWorkItemSchema, workItemInputSchema, waitingOnSchema, progressSchema, workItemUpdateSchema, agentCandidateSchema, scoutReturnAddressSchema, scoutAgentCardSchema, whoAmISchema, searchResultSchema, resolveResultSchema, sendResultSchema, cardCreateResultSchema, askResultSchema, workUpdateResultSchema;
46520
+ var AGENT_STATE_VALUES, REGISTRATION_KIND_VALUES, RESOLVE_KIND_VALUES, MESSAGE_ROUTE_KIND_VALUES, MESSAGE_ROUTING_ERROR_VALUES, LOCAL_AGENT_HARNESS_VALUES, SCOUT_MCP_UI_META_KEY = "openscout/ui", scoutAgentToolIconMeta, scoutAgentAvatarMeta, targetLabelInputSchema, targetAgentIdInputSchema, mentionAgentIdsInputSchema, flightSchema, trackedWorkItemSchema, workItemInputSchema, waitingOnSchema, progressSchema, workItemUpdateSchema, agentCandidateSchema, scoutReturnAddressSchema, scoutAgentCardSchema, whoAmISchema, searchResultSchema, resolveResultSchema, sendResultSchema, cardCreateResultSchema, askResultSchema, workUpdateResultSchema;
46155
46521
  var init_scout_mcp = __esm(async () => {
46156
46522
  init_mcp();
46157
46523
  init_stdio2();
@@ -46182,6 +46548,21 @@ var init_scout_mcp = __esm(async () => {
46182
46548
  "multi_target_requires_explicit_channel"
46183
46549
  ];
46184
46550
  LOCAL_AGENT_HARNESS_VALUES = ["claude", "codex"];
46551
+ scoutAgentToolIconMeta = {
46552
+ kind: "semantic",
46553
+ name: "agent",
46554
+ fallbackGlyph: "@"
46555
+ };
46556
+ scoutAgentAvatarMeta = {
46557
+ kind: "agent-avatar",
46558
+ monogramField: "displayName",
46559
+ fallbackField: "handle",
46560
+ colorSeedField: "agentId",
46561
+ fallbackGlyph: "@"
46562
+ };
46563
+ targetLabelInputSchema = string2().describe("Scout agent handle to contact, such as @talkie").optional();
46564
+ targetAgentIdInputSchema = string2().describe("Exact Scout agent id when already known, such as talkie.master.mini").optional();
46565
+ mentionAgentIdsInputSchema = array(string2()).describe("Exact Scout agent ids to target directly when you already know them").optional();
46185
46566
  flightSchema = object2({
46186
46567
  id: string2(),
46187
46568
  invocationId: string2(),
@@ -46328,6 +46709,7 @@ var init_scout_mcp = __esm(async () => {
46328
46709
  messageId: string2().nullable(),
46329
46710
  invokedTargetIds: array(string2()),
46330
46711
  unresolvedTargetIds: array(string2()),
46712
+ targetDiagnostic: object2({}).catchall(unknown()).nullable(),
46331
46713
  routeKind: _enum2(MESSAGE_ROUTE_KIND_VALUES).nullable(),
46332
46714
  routingError: _enum2(MESSAGE_ROUTING_ERROR_VALUES).nullable()
46333
46715
  });
@@ -46417,8 +46799,8 @@ __export(exports_menu, {
46417
46799
  import { spawnSync as spawnSync2 } from "child_process";
46418
46800
  import { existsSync as existsSync14 } from "fs";
46419
46801
  import { homedir as homedir11 } from "os";
46420
- import { dirname as dirname12, join as join19, resolve as resolve12 } from "path";
46421
- import { fileURLToPath as fileURLToPath8 } from "url";
46802
+ import { dirname as dirname13, join as join19, resolve as resolve13 } from "path";
46803
+ import { fileURLToPath as fileURLToPath9 } from "url";
46422
46804
  function renderMenuCommandHelp() {
46423
46805
  return [
46424
46806
  "scout menu \u2014 macOS menu bar app",
@@ -46501,23 +46883,23 @@ function runProcess(command, args, options) {
46501
46883
  };
46502
46884
  }
46503
46885
  function findRepoMenuHelper(startDirectory) {
46504
- let current = resolve12(startDirectory);
46886
+ let current = resolve13(startDirectory);
46505
46887
  while (true) {
46506
46888
  const candidate = join19(current, "apps", "macos", "bin", "openscout-menu.ts");
46507
46889
  if (existsSync14(candidate)) {
46508
46890
  return candidate;
46509
46891
  }
46510
- const parent = dirname12(current);
46892
+ const parent = dirname13(current);
46511
46893
  if (parent === current) {
46512
46894
  break;
46513
46895
  }
46514
46896
  current = parent;
46515
46897
  }
46516
- const sourceRelativeCandidate = fileURLToPath8(new URL("../../../../macos/bin/openscout-menu.ts", import.meta.url));
46898
+ const sourceRelativeCandidate = fileURLToPath9(new URL("../../../../macos/bin/openscout-menu.ts", import.meta.url));
46517
46899
  return existsSync14(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
46518
46900
  }
46519
46901
  function resolveRepoBundlePath(helperPath) {
46520
- return resolve12(dirname12(helperPath), "..", "dist", MENU_BUNDLE_NAME);
46902
+ return resolve13(dirname13(helperPath), "..", "dist", MENU_BUNDLE_NAME);
46521
46903
  }
46522
46904
  function isMenuRunning(env) {
46523
46905
  return runProcess("pgrep", ["-x", MENU_PROCESS_NAME], { env, allowFailure: true }).ok;
@@ -46675,7 +47057,7 @@ var init_menu = __esm(() => {
46675
47057
  });
46676
47058
 
46677
47059
  // ../runtime/src/tailscale.ts
46678
- import { readFile as readFile9 } from "fs/promises";
47060
+ import { readFile as readFile10 } from "fs/promises";
46679
47061
  import { execFile } from "child_process";
46680
47062
  import { promisify } from "util";
46681
47063
  function parseStatusJson(raw) {
@@ -46715,7 +47097,7 @@ function isBackendRunning(status) {
46715
47097
  return (status.BackendState ?? "").trim().toLowerCase() === "running";
46716
47098
  }
46717
47099
  async function readStatusJsonFromFile(filePath) {
46718
- const raw = await readFile9(filePath, "utf8");
47100
+ const raw = await readFile10(filePath, "utf8");
46719
47101
  return parseStatusJson(raw);
46720
47102
  }
46721
47103
  async function readStatusJson() {
@@ -49336,15 +49718,30 @@ function saveTrustedPeer(peer) {
49336
49718
  function isTrustedPeer(publicKeyHex) {
49337
49719
  return loadTrustedPeers().has(publicKeyHex);
49338
49720
  }
49339
- function createQRPayload(bridgePublicKey, relayUrl) {
49721
+ function createQRPayload(bridgePublicKey, relayUrl, fallbackRelayUrls = []) {
49722
+ const fallbackRelays = normalizedRelayUrls(fallbackRelayUrls, relayUrl);
49340
49723
  return {
49341
49724
  v: QR_VERSION,
49342
49725
  relay: relayUrl,
49726
+ ...fallbackRelays.length > 0 ? { fallbackRelays } : {},
49343
49727
  room: crypto.randomUUID(),
49344
49728
  publicKey: bytesToHex2(bridgePublicKey),
49345
49729
  expiresAt: Date.now() + QR_EXPIRY_MS
49346
49730
  };
49347
49731
  }
49732
+ function normalizedRelayUrls(urls, primaryRelayUrl) {
49733
+ const seen = new Set([primaryRelayUrl.trim()]);
49734
+ const normalized = [];
49735
+ for (const url2 of urls) {
49736
+ const trimmed = url2.trim();
49737
+ if (!trimmed || seen.has(trimmed)) {
49738
+ continue;
49739
+ }
49740
+ seen.add(trimmed);
49741
+ normalized.push(trimmed);
49742
+ }
49743
+ return normalized;
49744
+ }
49348
49745
  function bytesToHex2(bytes) {
49349
49746
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
49350
49747
  }
@@ -50639,7 +51036,7 @@ var init_db_queries = __esm(() => {
50639
51036
  });
50640
51037
 
50641
51038
  // ../../apps/desktop/src/core/mobile/service.ts
50642
- import { basename as basename8, resolve as resolve13 } from "path";
51039
+ import { basename as basename8, resolve as resolve14 } from "path";
50643
51040
  function normalizeTimestamp2(value) {
50644
51041
  if (!value)
50645
51042
  return null;
@@ -51028,7 +51425,7 @@ async function createScoutSession(input, currentDirectory, deviceId) {
51028
51425
  if (!rawWorkspaceId) {
51029
51426
  throw new Error(`Invalid workspaceId.`);
51030
51427
  }
51031
- const workspaceRoot = resolve13(rawWorkspaceId);
51428
+ const workspaceRoot = resolve14(rawWorkspaceId);
51032
51429
  const projectName = basename8(workspaceRoot) || workspaceRoot;
51033
51430
  const workspace = {
51034
51431
  id: workspaceRoot,
@@ -52763,7 +53160,7 @@ import { Database as Database2 } from "bun:sqlite";
52763
53160
  import { mkdirSync as mkdirSync9, readFileSync as readFileSync13 } from "fs";
52764
53161
  import { connect as connectHttp2 } from "http2";
52765
53162
  import { createPrivateKey, sign as signWithKey } from "crypto";
52766
- import { dirname as dirname13, join as join25 } from "path";
53163
+ import { dirname as dirname14, join as join25 } from "path";
52767
53164
  function resolveControlPlaneDbPath() {
52768
53165
  return join25(resolveOpenScoutSupportPaths().controlHome, "control-plane.sqlite");
52769
53166
  }
@@ -52779,7 +53176,7 @@ function writeDb() {
52779
53176
  dbHandle = null;
52780
53177
  }
52781
53178
  if (!dbHandle) {
52782
- mkdirSync9(dirname13(nextPath), { recursive: true });
53179
+ mkdirSync9(dirname14(nextPath), { recursive: true });
52783
53180
  dbHandle = new Database2(nextPath, { create: true });
52784
53181
  dbPath = nextPath;
52785
53182
  ensureMobilePushSchema(dbHandle);
@@ -52995,7 +53392,7 @@ async function sendApnsAlertToRegistration(registration, alert) {
52995
53392
  },
52996
53393
  ...alert.payload ? { scout: alert.payload } : {}
52997
53394
  });
52998
- return new Promise((resolve14, reject) => {
53395
+ return new Promise((resolve15, reject) => {
52999
53396
  const client = connectHttp2(authority);
53000
53397
  client.once("error", reject);
53001
53398
  const request = client.request({
@@ -53033,7 +53430,7 @@ async function sendApnsAlertToRegistration(registration, alert) {
53033
53430
  }
53034
53431
  }
53035
53432
  if (status === 200) {
53036
- resolve14({
53433
+ resolve15({
53037
53434
  delivered: true,
53038
53435
  skipped: false,
53039
53436
  status,
@@ -53045,7 +53442,7 @@ async function sendApnsAlertToRegistration(registration, alert) {
53045
53442
  if (reason === "BadDeviceToken" || reason === "Unregistered") {
53046
53443
  deleteMobilePushRegistrationByToken(registration.pushToken);
53047
53444
  }
53048
- resolve14({
53445
+ resolve15({
53049
53446
  delivered: false,
53050
53447
  skipped: false,
53051
53448
  status,
@@ -53290,23 +53687,23 @@ function bridgeEventIterable(bridge, signal) {
53290
53687
  return {
53291
53688
  [Symbol.asyncIterator]() {
53292
53689
  const buffer = [];
53293
- let resolve14 = null;
53690
+ let resolve15 = null;
53294
53691
  let done = false;
53295
53692
  const unsub = bridge.onEvent((event) => {
53296
53693
  if (done)
53297
53694
  return;
53298
53695
  buffer.push(event);
53299
- if (resolve14) {
53300
- resolve14();
53301
- resolve14 = null;
53696
+ if (resolve15) {
53697
+ resolve15();
53698
+ resolve15 = null;
53302
53699
  }
53303
53700
  });
53304
53701
  const cleanup = () => {
53305
53702
  done = true;
53306
53703
  unsub();
53307
- if (resolve14) {
53308
- resolve14();
53309
- resolve14 = null;
53704
+ if (resolve15) {
53705
+ resolve15();
53706
+ resolve15 = null;
53310
53707
  }
53311
53708
  };
53312
53709
  signal?.addEventListener("abort", cleanup, { once: true });
@@ -53319,7 +53716,7 @@ function bridgeEventIterable(bridge, signal) {
53319
53716
  return { done: false, value: buffer.shift() };
53320
53717
  }
53321
53718
  await new Promise((r) => {
53322
- resolve14 = r;
53719
+ resolve15 = r;
53323
53720
  });
53324
53721
  }
53325
53722
  },
@@ -54037,7 +54434,8 @@ var init_router = __esm(async () => {
54037
54434
  // ../../apps/desktop/src/core/pairing/runtime/bridge/relay-client.ts
54038
54435
  function connectToRelay(relayUrl, identity2, bridge, options = {}) {
54039
54436
  const { secure = true, events: events2 } = options;
54040
- const qrPayload = createQRPayload(identity2.publicKey, relayUrl);
54437
+ const publicRelayUrl = options.publicRelayUrl?.trim() || relayUrl;
54438
+ const qrPayload = createQRPayload(identity2.publicKey, publicRelayUrl, options.fallbackRelayUrls);
54041
54439
  let ws = null;
54042
54440
  let eventUnsub = null;
54043
54441
  let stopped = false;
@@ -54051,12 +54449,12 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
54051
54449
  const bridgeKeyHex = bytesToHex2(identity2.publicKey);
54052
54450
  const url2 = buildRelayUrl(relayUrl, qrPayload.room, bridgeKeyHex);
54053
54451
  console.log(`[relay-client] connecting to relay (room: ${qrPayload.room})`);
54054
- events2?.onConnecting?.({ relayUrl, room: qrPayload.room });
54452
+ events2?.onConnecting?.({ relayUrl: publicRelayUrl, room: qrPayload.room });
54055
54453
  ws = new WebSocket(url2, relayWebSocketOptions(relayUrl));
54056
54454
  ws.addEventListener("open", () => {
54057
54455
  console.log(`[relay-client] connected to relay (room: ${qrPayload.room})`);
54058
54456
  backoff = INITIAL_BACKOFF_MS;
54059
- events2?.onConnected?.({ relayUrl, room: qrPayload.room });
54457
+ events2?.onConnected?.({ relayUrl: publicRelayUrl, room: qrPayload.room });
54060
54458
  });
54061
54459
  ws.addEventListener("message", (event) => {
54062
54460
  handleRelaySocketMessage(event.data);
@@ -54064,7 +54462,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
54064
54462
  ws.addEventListener("close", (event) => {
54065
54463
  console.log(`[relay-client] disconnected (code: ${event.code}, reason: ${event.reason})`);
54066
54464
  events2?.onClosed?.({
54067
- relayUrl,
54465
+ relayUrl: publicRelayUrl,
54068
54466
  room: qrPayload.room,
54069
54467
  code: event.code,
54070
54468
  reason: event.reason
@@ -54075,7 +54473,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
54075
54473
  ws.addEventListener("error", () => {
54076
54474
  const error48 = new Error("Relay websocket error");
54077
54475
  console.error("[relay-client] connection error");
54078
- events2?.onError?.({ relayUrl, room: qrPayload.room, error: error48 });
54476
+ events2?.onError?.({ relayUrl: publicRelayUrl, room: qrPayload.room, error: error48 });
54079
54477
  });
54080
54478
  }
54081
54479
  function handleRelaySocketMessage(raw) {
@@ -54129,7 +54527,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
54129
54527
  replaceOlderPeerForSameDevice(peer);
54130
54528
  ensureBridgeEventSubscription();
54131
54529
  console.log(`[relay-client] secure handshake complete (peer: ${pubHex.slice(0, 12)}..., device: ${peer.deviceId}, client: ${clientId})`);
54132
- events2?.onPaired?.({ relayUrl, room: qrPayload.room, remotePublicKey });
54530
+ events2?.onPaired?.({ relayUrl: publicRelayUrl, room: qrPayload.room, remotePublicKey });
54133
54531
  sendExistingSessions((json2) => {
54134
54532
  if (peer.transport.isReady()) {
54135
54533
  peer.transport.send(json2);
@@ -54142,7 +54540,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
54142
54540
  onError: (err) => {
54143
54541
  console.error("[relay-client] secure transport error:", err.message);
54144
54542
  log.error("trns:cry", `decrypt failed for ${clientId} \u2014 resetting handshake: ${err.message}`);
54145
- events2?.onError?.({ relayUrl, room: qrPayload.room, error: err });
54543
+ events2?.onError?.({ relayUrl: publicRelayUrl, room: qrPayload.room, error: err });
54146
54544
  sendRelayClose(clientId, 4002, "Transport reset");
54147
54545
  teardownSecurePeer(clientId);
54148
54546
  },
@@ -54341,7 +54739,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
54341
54739
  if (stopped)
54342
54740
  return;
54343
54741
  console.log(`[relay-client] reconnecting in ${backoff}ms...`);
54344
- events2?.onReconnectScheduled?.({ relayUrl, room: qrPayload.room, delayMs: backoff });
54742
+ events2?.onReconnectScheduled?.({ relayUrl: publicRelayUrl, room: qrPayload.room, delayMs: backoff });
54345
54743
  reconnectTimer = setTimeout(() => {
54346
54744
  reconnectTimer = null;
54347
54745
  connect();
@@ -56224,15 +56622,15 @@ function resolveSelfTuple(promise3) {
56224
56622
  return Unpromise.proxy(promise3).then(() => [promise3]);
56225
56623
  }
56226
56624
  function withResolvers() {
56227
- let resolve14;
56625
+ let resolve15;
56228
56626
  let reject;
56229
56627
  const promise3 = new Promise((_resolve, _reject) => {
56230
- resolve14 = _resolve;
56628
+ resolve15 = _resolve;
56231
56629
  reject = _reject;
56232
56630
  });
56233
56631
  return {
56234
56632
  promise: promise3,
56235
- resolve: resolve14,
56633
+ resolve: resolve15,
56236
56634
  reject
56237
56635
  };
56238
56636
  }
@@ -56271,8 +56669,8 @@ function timerResource(ms) {
56271
56669
  return makeResource({ start() {
56272
56670
  if (timer)
56273
56671
  throw new Error("Timer already started");
56274
- const promise3 = new Promise((resolve14) => {
56275
- timer = setTimeout(() => resolve14(disposablePromiseTimerResult), ms);
56672
+ const promise3 = new Promise((resolve15) => {
56673
+ timer = setTimeout(() => resolve15(disposablePromiseTimerResult), ms);
56276
56674
  });
56277
56675
  return promise3;
56278
56676
  } }, () => {
@@ -56321,15 +56719,15 @@ function _takeWithGrace() {
56321
56719
  return _takeWithGrace.apply(this, arguments);
56322
56720
  }
56323
56721
  function createDeferred() {
56324
- let resolve14;
56722
+ let resolve15;
56325
56723
  let reject;
56326
56724
  const promise3 = new Promise((res, rej) => {
56327
- resolve14 = res;
56725
+ resolve15 = res;
56328
56726
  reject = rej;
56329
56727
  });
56330
56728
  return {
56331
56729
  promise: promise3,
56332
- resolve: resolve14,
56730
+ resolve: resolve15,
56333
56731
  reject
56334
56732
  };
56335
56733
  }
@@ -57305,8 +57703,8 @@ var import_objectSpread2$13, jsonContentTypeHandler, formDataContentTypeHandler,
57305
57703
  status: "fulfilled",
57306
57704
  value
57307
57705
  };
57308
- subscribers === null || subscribers === undefined || subscribers.forEach(({ resolve: resolve14 }) => {
57309
- resolve14(value);
57706
+ subscribers === null || subscribers === undefined || subscribers.forEach(({ resolve: resolve15 }) => {
57707
+ resolve15(value);
57310
57708
  });
57311
57709
  });
57312
57710
  if ("catch" in thenReturn)
@@ -58190,8 +58588,8 @@ function startBridgeServerTRPC(options) {
58190
58588
  }
58191
58589
  const iterable = isObservable(result) ? observableToAsyncIterable(result, abortController.signal) : result;
58192
58590
  const iterator = iterable[Symbol.asyncIterator]();
58193
- const abortPromise = new Promise((resolve14) => {
58194
- abortController.signal.addEventListener("abort", () => resolve14("abort"), {
58591
+ const abortPromise = new Promise((resolve15) => {
58592
+ abortController.signal.addEventListener("abort", () => resolve15("abort"), {
58195
58593
  once: true
58196
58594
  });
58197
58595
  });
@@ -58553,6 +58951,8 @@ async function startPairingRuntime(options) {
58553
58951
  }
58554
58952
  const relayConnection = connectToRelay(relayUrl, identity2, bridge, {
58555
58953
  secure: true,
58954
+ publicRelayUrl: options?.advertisedRelayUrl ?? undefined,
58955
+ fallbackRelayUrls: options?.fallbackRelayUrls,
58556
58956
  events: options?.relayEvents
58557
58957
  });
58558
58958
  await autoStartConfiguredSessions(bridge, config2.sessions);
@@ -58632,6 +59032,7 @@ function startRelay(port, options = {}) {
58632
59032
  const roomByBridgeKey = new Map;
58633
59033
  const server = Bun.serve({
58634
59034
  port,
59035
+ hostname: "0.0.0.0",
58635
59036
  ...options.tls ? {
58636
59037
  tls: {
58637
59038
  cert: Bun.file(options.tls.cert),
@@ -58852,7 +59253,7 @@ var BRIDGE_ABSENCE_GRACE_MS = 30000, ROOM_IDLE_TIMEOUT_MS = 60000;
58852
59253
  // ../../apps/desktop/src/core/pairing/runtime/relay-runtime.ts
58853
59254
  import { execSync as execSync4 } from "child_process";
58854
59255
  import { existsSync as existsSync19, mkdirSync as mkdirSync11, readdirSync as readdirSync4 } from "fs";
58855
- import { homedir as homedir22 } from "os";
59256
+ import { homedir as homedir22, networkInterfaces } from "os";
58856
59257
  import { join as join27 } from "path";
58857
59258
  function findStoredCerts() {
58858
59259
  if (!existsSync19(PAIRING_DIR2)) {
@@ -58894,24 +59295,42 @@ function readTailscaleStatus2() {
58894
59295
  return null;
58895
59296
  }
58896
59297
  }
58897
- function resolveRelayEndpointForTailscaleStatus(port, tailscale) {
59298
+ function resolveRelayEndpointForTailscaleStatus(port, tailscale, options = {}) {
58898
59299
  const backendState = tailscale?.backendState?.trim().toLowerCase() ?? "";
58899
59300
  const hostname5 = tailscale?.dnsName ?? null;
58900
59301
  const tailscaleRunning = backendState === "running" && tailscale?.online !== false && Boolean(hostname5);
58901
- const tls = resolveTls(tailscaleRunning ? hostname5 : null);
58902
- if (tailscaleRunning && hostname5 && tls) {
59302
+ const tls = tailscaleRunning ? options.tls !== undefined ? options.tls : resolveTls(hostname5) : null;
59303
+ const scheme = tls ? "wss" : "ws";
59304
+ const connectUrl = `${scheme}://127.0.0.1:${port}`;
59305
+ const localAddress = options.localAddress !== undefined ? normalizedOptionalAddress(options.localAddress) : findLocalNetworkAddress();
59306
+ const localRelayUrl = localAddress ? `${scheme}://${localAddress}:${port}` : null;
59307
+ const tailnetRelayUrl = tailscaleRunning && hostname5 ? `${scheme}://${hostname5}:${port}` : null;
59308
+ const fallbackRelayUrls = tailnetRelayUrl && localRelayUrl ? [tailnetRelayUrl] : [];
59309
+ if (localRelayUrl) {
59310
+ return {
59311
+ relayUrl: localRelayUrl,
59312
+ connectUrl,
59313
+ fallbackRelayUrls,
59314
+ options: tls ? { tls } : {}
59315
+ };
59316
+ }
59317
+ if (tailnetRelayUrl && tls) {
58903
59318
  return {
58904
- relayUrl: `wss://${hostname5}:${port}`,
59319
+ relayUrl: tailnetRelayUrl,
59320
+ connectUrl,
59321
+ fallbackRelayUrls: [],
58905
59322
  options: { tls }
58906
59323
  };
58907
59324
  }
58908
- if (tailscaleRunning && hostname5) {
59325
+ if (tailnetRelayUrl) {
58909
59326
  pairingLog.warn("relay", "tailscale is running without TLS; falling back to insecure websocket relay", {
58910
59327
  hostname: hostname5,
58911
59328
  port
58912
59329
  });
58913
59330
  return {
58914
- relayUrl: `ws://${hostname5}:${port}`,
59331
+ relayUrl: tailnetRelayUrl,
59332
+ connectUrl,
59333
+ fallbackRelayUrls: [],
58915
59334
  options: {}
58916
59335
  };
58917
59336
  }
@@ -58924,7 +59343,9 @@ function resolveRelayEndpointForTailscaleStatus(port, tailscale) {
58924
59343
  });
58925
59344
  }
58926
59345
  return {
58927
- relayUrl: `ws://127.0.0.1:${port}`,
59346
+ relayUrl: connectUrl,
59347
+ connectUrl,
59348
+ fallbackRelayUrls: [],
58928
59349
  options: {}
58929
59350
  };
58930
59351
  }
@@ -58977,16 +59398,87 @@ function resolveRelayEndpoint(port) {
58977
59398
  }
58978
59399
  function startManagedRelay(port = 7889) {
58979
59400
  const endpoint = resolveRelayEndpoint(port);
58980
- pairingLog.info("relay", "starting managed relay", { relay: endpoint.relayUrl, port });
59401
+ pairingLog.info("relay", "starting managed relay", {
59402
+ relay: endpoint.relayUrl,
59403
+ connectUrl: endpoint.connectUrl,
59404
+ fallbackRelayUrls: endpoint.fallbackRelayUrls,
59405
+ port
59406
+ });
58981
59407
  const relay = startRelay(port, endpoint.options);
58982
59408
  return {
58983
59409
  relayUrl: endpoint.relayUrl,
59410
+ connectUrl: endpoint.connectUrl,
59411
+ fallbackRelayUrls: endpoint.fallbackRelayUrls,
58984
59412
  stop() {
58985
59413
  pairingLog.info("relay", "stopping managed relay", { relay: endpoint.relayUrl, port });
58986
59414
  relay.stop();
58987
59415
  }
58988
59416
  };
58989
59417
  }
59418
+ function normalizedOptionalAddress(address) {
59419
+ const trimmed = address?.trim();
59420
+ return trimmed ? trimmed : null;
59421
+ }
59422
+ function findLocalNetworkAddress() {
59423
+ const candidates = [];
59424
+ const interfaces = networkInterfaces();
59425
+ for (const [name, entries] of Object.entries(interfaces)) {
59426
+ for (const entry of entries ?? []) {
59427
+ if (entry.internal || entry.family !== "IPv4") {
59428
+ continue;
59429
+ }
59430
+ if (!isPrivateIPv4(entry.address) || isLinkLocalIPv4(entry.address) || isTailscaleIPv4(entry.address)) {
59431
+ continue;
59432
+ }
59433
+ candidates.push({ name, address: entry.address });
59434
+ }
59435
+ }
59436
+ candidates.sort((left, right) => {
59437
+ const leftScore = localInterfaceScore(left.name);
59438
+ const rightScore = localInterfaceScore(right.name);
59439
+ if (leftScore !== rightScore) {
59440
+ return leftScore - rightScore;
59441
+ }
59442
+ return left.address.localeCompare(right.address);
59443
+ });
59444
+ return candidates[0]?.address ?? null;
59445
+ }
59446
+ function localInterfaceScore(name) {
59447
+ if (/^en\d+$/i.test(name)) {
59448
+ return 0;
59449
+ }
59450
+ if (/^bridge\d*$/i.test(name)) {
59451
+ return 2;
59452
+ }
59453
+ return 1;
59454
+ }
59455
+ function isPrivateIPv4(address) {
59456
+ const octets = parseIPv4(address);
59457
+ if (!octets) {
59458
+ return false;
59459
+ }
59460
+ const [first, second] = octets;
59461
+ return first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168;
59462
+ }
59463
+ function isLinkLocalIPv4(address) {
59464
+ const octets = parseIPv4(address);
59465
+ return Boolean(octets && octets[0] === 169 && octets[1] === 254);
59466
+ }
59467
+ function isTailscaleIPv4(address) {
59468
+ const octets = parseIPv4(address);
59469
+ return Boolean(octets && octets[0] === 100 && octets[1] >= 64 && octets[1] <= 127);
59470
+ }
59471
+ function parseIPv4(address) {
59472
+ const octets = address.split(".");
59473
+ if (octets.length !== 4) {
59474
+ return null;
59475
+ }
59476
+ const numbers = octets.map((octet) => Number(octet));
59477
+ if (numbers.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {
59478
+ return null;
59479
+ }
59480
+ return numbers;
59481
+ }
58990
59482
  var PAIRING_DIR2;
58991
59483
  var init_relay_runtime = __esm(() => {
58992
59484
  init_log2();
@@ -59695,12 +60187,19 @@ async function startScoutPairingSession(input) {
59695
60187
  };
59696
60188
  const resolvedRelayUrl = input.forceManagedRelay ? null : input.relayUrl?.trim() || config3.relay;
59697
60189
  try {
59698
- const activeRelayUrl = resolvedRelayUrl ?? (() => {
60190
+ const managedRelay = resolvedRelayUrl ? null : (() => {
59699
60191
  relay = startManagedRelay(config3.port + 1);
59700
- return relay.relayUrl;
60192
+ return relay;
59701
60193
  })();
60194
+ const activeRelayUrl = resolvedRelayUrl ?? managedRelay?.relayUrl;
60195
+ const connectRelayUrl = resolvedRelayUrl ?? managedRelay?.connectUrl ?? managedRelay?.relayUrl;
60196
+ if (!activeRelayUrl || !connectRelayUrl) {
60197
+ throw new Error("Scout pairing relay URL is not configured.");
60198
+ }
59702
60199
  runtime2 = await startPairingRuntime({
59703
- relayUrl: activeRelayUrl,
60200
+ relayUrl: connectRelayUrl,
60201
+ advertisedRelayUrl: activeRelayUrl,
60202
+ fallbackRelayUrls: managedRelay?.fallbackRelayUrls,
59704
60203
  relayEvents: {
59705
60204
  onConnecting() {
59706
60205
  emitOrQueue(createStatusEvent("connecting", `Connecting to ${activeRelayUrl}`));
@@ -59855,8 +60354,8 @@ async function runPairCommand(context, args) {
59855
60354
  process.on("SIGINT", handleSignal);
59856
60355
  process.on("SIGTERM", handleSignal);
59857
60356
  try {
59858
- await new Promise((resolve14) => {
59859
- controller.signal.addEventListener("abort", () => resolve14(), { once: true });
60357
+ await new Promise((resolve15) => {
60358
+ controller.signal.addEventListener("abort", () => resolve15(), { once: true });
59860
60359
  });
59861
60360
  } finally {
59862
60361
  process.off("SIGINT", handleSignal);
@@ -59925,15 +60424,15 @@ __export(exports_server, {
59925
60424
  runServerCommand: () => runServerCommand,
59926
60425
  resolveScoutWebServerEntry: () => resolveScoutWebServerEntry,
59927
60426
  resolveScoutControlPlaneWebServerEntry: () => resolveScoutControlPlaneWebServerEntry,
59928
- resolveBunExecutable: () => resolveBunExecutable3,
60427
+ resolveBunExecutable: () => resolveBunExecutable4,
59929
60428
  renderServerCommandHelp: () => renderServerCommandHelp,
59930
60429
  normalizeServerOpenPath: () => normalizeServerOpenPath
59931
60430
  });
59932
60431
  import { spawn as spawn5 } from "child_process";
59933
- import { accessSync as accessSync2, constants as constants4, existsSync as existsSync20 } from "fs";
60432
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, openSync } from "fs";
59934
60433
  import { homedir as homedir23 } from "os";
59935
- import { dirname as dirname14, join as join28, resolve as resolve14 } from "path";
59936
- import { fileURLToPath as fileURLToPath9 } from "url";
60434
+ import { dirname as dirname15, join as join28, resolve as resolve15 } from "path";
60435
+ import { fileURLToPath as fileURLToPath10 } from "url";
59937
60436
  function renderServerCommandHelp() {
59938
60437
  return [
59939
60438
  "scout server \u2014 Scout web UI (Bun runtime)",
@@ -59948,11 +60447,11 @@ function renderServerCommandHelp() {
59948
60447
  " open Open the Scout web UI (starts server on demand if needed).",
59949
60448
  "",
59950
60449
  "Options:",
59951
- " --port <n> Listen port (default 3200; env OPENSCOUT_WEB_PORT)",
60450
+ " --port <n> Listen port (default 3200; optional override OPENSCOUT_WEB_PORT)",
59952
60451
  " --static Serve built UI from disk",
59953
- " --static-root DIR Static client root (env OPENSCOUT_WEB_STATIC_ROOT)",
59954
- " --vite-url URL Dev proxy target for non-API routes (env OPENSCOUT_WEB_VITE_URL)",
59955
- " --cwd DIR Workspace / setup root (env OPENSCOUT_SETUP_CWD)",
60452
+ " --static-root DIR Static client root (optional override OPENSCOUT_WEB_STATIC_ROOT)",
60453
+ " --vite-url URL Dev proxy target for non-API routes (optional override OPENSCOUT_WEB_VITE_URL)",
60454
+ " --cwd DIR Workspace / setup root (optional override OPENSCOUT_SETUP_CWD)",
59956
60455
  " --path PATH Browser path for `open` (default /)",
59957
60456
  "",
59958
60457
  "Requires `bun` on PATH."
@@ -59963,19 +60462,21 @@ function resolveScoutWebServerEntry() {
59963
60462
  return resolveScoutControlPlaneWebServerEntry();
59964
60463
  }
59965
60464
  function resolveScoutControlPlaneWebServerEntry() {
59966
- const mainDir = dirname14(fileURLToPath9(import.meta.url));
59967
- const bundled = join28(mainDir, "scout-control-plane-web.mjs");
59968
- if (existsSync20(bundled)) {
60465
+ const bundled = resolveBundledEntrypoint(import.meta.url, "scout-control-plane-web.mjs");
60466
+ if (bundled) {
59969
60467
  return bundled;
59970
60468
  }
59971
- const source = fileURLToPath9(new URL("../../../../../packages/web/server/index.ts", import.meta.url).href);
59972
- if (existsSync20(source)) {
60469
+ const repoRoot = resolveOpenScoutRepoRoot({
60470
+ startDirectories: [
60471
+ process.env.OPENSCOUT_SETUP_CWD,
60472
+ process.cwd(),
60473
+ dirname15(fileURLToPath10(import.meta.url))
60474
+ ]
60475
+ });
60476
+ const source = resolveRepoEntrypoint(repoRoot, "packages/web/server/index.ts");
60477
+ if (source) {
59973
60478
  return source;
59974
60479
  }
59975
- const legacySource = fileURLToPath9(new URL("../../server/control-plane-index.ts", import.meta.url).href);
59976
- if (existsSync20(legacySource)) {
59977
- return legacySource;
59978
- }
59979
60480
  throw new ScoutCliError("Could not find Scout web server entry. Rebuild @openscout/scout or run from the OpenScout repository.");
59980
60481
  }
59981
60482
  function parseServerFlags(args) {
@@ -60047,46 +60548,17 @@ function parseServerFlags(args) {
60047
60548
  return { env, openPath };
60048
60549
  }
60049
60550
  function resolveBundledStaticClientRoot(entry, _mode) {
60050
- const entryDir = dirname14(entry);
60551
+ const entryDir = dirname15(entry);
60051
60552
  const clientDirectory = join28(entryDir, "client");
60052
60553
  const indexPath = join28(clientDirectory, "index.html");
60053
60554
  return existsSync20(indexPath) ? clientDirectory : null;
60054
60555
  }
60055
- function isExecutable4(filePath) {
60056
- if (!filePath) {
60057
- return false;
60058
- }
60059
- try {
60060
- accessSync2(filePath, constants4.X_OK);
60061
- return true;
60062
- } catch {
60063
- return false;
60064
- }
60065
- }
60066
- function resolveBunExecutable3(env) {
60067
- const explicitPaths = [
60068
- env.SCOUT_BUN_BIN,
60069
- env.OPENSCOUT_BUN_BIN,
60070
- env.BUN_BIN
60071
- ].filter((candidate) => Boolean(candidate?.trim()));
60072
- for (const candidate of explicitPaths) {
60073
- if (isExecutable4(candidate)) {
60074
- return candidate;
60075
- }
60076
- }
60077
- const pathEntries = (env.PATH ?? "").split(":").filter(Boolean);
60078
- const commonDirectories = [
60079
- join28(homedir23(), ".bun", "bin"),
60080
- "/opt/homebrew/bin",
60081
- "/usr/local/bin"
60082
- ];
60083
- for (const directory of [...pathEntries, ...commonDirectories]) {
60084
- const candidate = join28(directory.replace(/^~(?=$|\/)/, homedir23()), "bun");
60085
- if (isExecutable4(candidate)) {
60086
- return candidate;
60087
- }
60556
+ function resolveBunExecutable4(env) {
60557
+ const bun = resolveBunExecutable2(env);
60558
+ if (bun) {
60559
+ return bun.path;
60088
60560
  }
60089
- return "bun";
60561
+ throw new ScoutCliError("Unable to locate Bun. Install Bun (https://bun.sh) or set OPENSCOUT_BUN_BIN.");
60090
60562
  }
60091
60563
  function buildMergedServerEnv(entry, mode, flagEnv) {
60092
60564
  const bundledStaticClientRoot = resolveBundledStaticClientRoot(entry, mode);
@@ -60156,13 +60628,13 @@ function resolveServerPort(env) {
60156
60628
  }
60157
60629
  return port;
60158
60630
  }
60159
- const fromFile = loadLocalConfig().ports?.web;
60160
- if (fromFile)
60161
- return fromFile;
60162
- return 3200;
60631
+ return resolveWebPort();
60163
60632
  }
60164
60633
  function resolveExpectedCurrentDirectory(env) {
60165
- return resolve14(env.OPENSCOUT_SETUP_CWD?.trim() || process.cwd());
60634
+ return resolveOpenScoutSetupContextRoot({
60635
+ env,
60636
+ fallbackDirectory: process.cwd()
60637
+ });
60166
60638
  }
60167
60639
  function renderModeLabel(mode) {
60168
60640
  return "Scout web";
@@ -60259,12 +60731,19 @@ async function openBrowser(url2) {
60259
60731
  });
60260
60732
  });
60261
60733
  }
60734
+ function resolveScoutWebServerLogPath() {
60735
+ const dir = join28(homedir23(), ".scout", "logs");
60736
+ mkdirSync12(dir, { recursive: true });
60737
+ return join28(dir, "web-server.log");
60738
+ }
60262
60739
  async function spawnDetachedServer(entry, env) {
60263
- const bunExecutable = resolveBunExecutable3(env);
60740
+ const bunExecutable = resolveBunExecutable4(env);
60741
+ const logPath = resolveScoutWebServerLogPath();
60742
+ const logFd = openSync(logPath, "a");
60264
60743
  await new Promise((resolvePromise, rejectPromise) => {
60265
60744
  const child = spawn5(bunExecutable, ["run", entry], {
60266
60745
  detached: true,
60267
- stdio: "ignore",
60746
+ stdio: ["ignore", logFd, logFd],
60268
60747
  env,
60269
60748
  windowsHide: true
60270
60749
  });
@@ -60286,7 +60765,7 @@ async function waitForScoutServer(port, mode, expectedCurrentDirectory) {
60286
60765
  while (Date.now() < deadline) {
60287
60766
  const probe = await probeScoutServer(port);
60288
60767
  if (probe.status === "healthy") {
60289
- const actualCurrentDirectory = resolve14(probe.health.currentDirectory);
60768
+ const actualCurrentDirectory = resolve15(probe.health.currentDirectory);
60290
60769
  if (!isCurrentScoutWebSurface(probe.health.surface)) {
60291
60770
  throw new ScoutCliError(`port ${port} is serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(mode)}.`);
60292
60771
  }
@@ -60300,7 +60779,7 @@ async function waitForScoutServer(port, mode, expectedCurrentDirectory) {
60300
60779
  }
60301
60780
  await new Promise((resolvePromise) => setTimeout(resolvePromise, 250));
60302
60781
  }
60303
- throw new ScoutCliError(`timed out waiting for ${renderModeLabel(mode)} on port ${port}`);
60782
+ throw new ScoutCliError(`timed out waiting for ${renderModeLabel(mode)} on port ${port}; check ${resolveScoutWebServerLogPath()} for the child process output`);
60304
60783
  }
60305
60784
  async function openScoutServer(options) {
60306
60785
  const port = resolveServerPort(options.env);
@@ -60308,7 +60787,7 @@ async function openScoutServer(options) {
60308
60787
  const browserUrl = new URL(normalizeServerOpenPath(options.openPath), `http://127.0.0.1:${port}`).toString();
60309
60788
  const probe = await probeScoutServer(port);
60310
60789
  if (probe.status === "healthy") {
60311
- const actualCurrentDirectory = resolve14(probe.health.currentDirectory);
60790
+ const actualCurrentDirectory = resolve15(probe.health.currentDirectory);
60312
60791
  if (!isCurrentScoutWebSurface(probe.health.surface)) {
60313
60792
  throw new ScoutCliError(`port ${port} is already serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(options.mode)}.`);
60314
60793
  }
@@ -60354,7 +60833,7 @@ async function runServerCommand(context, args) {
60354
60833
  context.output.writeValue(result, renderServerOpenResult);
60355
60834
  return;
60356
60835
  }
60357
- const bunExecutable = resolveBunExecutable3(mergedEnv);
60836
+ const bunExecutable = resolveBunExecutable4(mergedEnv);
60358
60837
  await new Promise((resolvePromise, rejectPromise) => {
60359
60838
  const child = spawn5(bunExecutable, ["run", selection.entry], {
60360
60839
  stdio: "inherit",
@@ -60387,6 +60866,8 @@ async function runServerCommand(context, args) {
60387
60866
  var SERVER_OPEN_TIMEOUT_MS = 15000, SERVER_HEALTH_TIMEOUT_MS = 1500;
60388
60867
  var init_server4 = __esm(() => {
60389
60868
  init_local_config();
60869
+ init_tool_resolution();
60870
+ init_setup();
60390
60871
  init_errors();
60391
60872
  });
60392
60873
 
@@ -60417,9 +60898,10 @@ async function runSpeakCommand(context, args) {
60417
60898
  const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
60418
60899
  const config3 = await loadScoutRelayConfig();
60419
60900
  const voice = getScoutVoiceForChannel(config3, "voice");
60901
+ const body = await resolveMessageBody(options);
60420
60902
  await acquireScoutOnAir(senderId);
60421
60903
  try {
60422
- const clean3 = stripScoutAgentSelectorLabels(options.message);
60904
+ const clean3 = stripScoutAgentSelectorLabels(body);
60423
60905
  if (clean3) {
60424
60906
  await speakScoutText(clean3, voice);
60425
60907
  }
@@ -60428,7 +60910,7 @@ async function runSpeakCommand(context, args) {
60428
60910
  }
60429
60911
  const result = await sendScoutMessage({
60430
60912
  senderId,
60431
- body: options.message,
60913
+ body,
60432
60914
  channel: "voice",
60433
60915
  shouldSpeak: true,
60434
60916
  executionHarness: parseScoutHarness(options.harness),
@@ -60438,16 +60920,17 @@ async function runSpeakCommand(context, args) {
60438
60920
  throw new Error("broker is not reachable");
60439
60921
  }
60440
60922
  if (result.unresolvedTargets.length > 0) {
60441
- throw new Error(formatScoutSendRoutingError(result.unresolvedTargets));
60923
+ throw new Error(formatScoutSendRoutingError(result));
60442
60924
  }
60443
60925
  context.output.writeValue({
60444
- message: options.message,
60926
+ message: body,
60445
60927
  invokedTargets: result.invokedTargets,
60446
60928
  unresolvedTargets: result.unresolvedTargets
60447
60929
  }, renderScoutMessagePostResult);
60448
60930
  }
60449
60931
  var init_speak = __esm(async () => {
60450
60932
  init_context();
60933
+ init_input_file();
60451
60934
  init_options();
60452
60935
  init_broker();
60453
60936
  await __promiseAll([
@@ -60505,8 +60988,8 @@ import { EventEmitter } from "events";
60505
60988
  import { Buffer as Buffer2 } from "buffer";
60506
60989
  import { Buffer as Buffer3 } from "buffer";
60507
60990
  import { EventEmitter as EventEmitter2 } from "events";
60508
- import { resolve as resolve15, dirname as dirname15 } from "path";
60509
- import { fileURLToPath as fileURLToPath10 } from "url";
60991
+ import { resolve as resolve16, dirname as dirname16 } from "path";
60992
+ import { fileURLToPath as fileURLToPath11 } from "url";
60510
60993
  import { resolve as resolve22, isAbsolute as isAbsolute5, parse as parse7 } from "path";
60511
60994
  import { existsSync as existsSync21 } from "fs";
60512
60995
  import { basename as basename11, join as join29 } from "path";
@@ -60516,7 +60999,7 @@ import { EventEmitter as EventEmitter3 } from "events";
60516
60999
  import path22 from "path";
60517
61000
  import { readFile as readFile22, writeFile as writeFile22, mkdir as mkdir22 } from "fs/promises";
60518
61001
  import * as path4 from "path";
60519
- import { mkdir as mkdir9, readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
61002
+ import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
60520
61003
  import * as path3 from "path";
60521
61004
  import { readdir as readdir3 } from "fs/promises";
60522
61005
  import { dlopen, toArrayBuffer as toArrayBuffer4, JSCallback, ptr as ptr4 } from "bun:ffi";
@@ -63641,13 +64124,13 @@ class DebounceController {
63641
64124
  }
63642
64125
  debounce(id, ms, fn) {
63643
64126
  const scopeMap = TIMERS_MAP.get(this.scopeId);
63644
- return new Promise((resolve17, reject) => {
64127
+ return new Promise((resolve18, reject) => {
63645
64128
  if (scopeMap.has(id)) {
63646
64129
  clearTimeout(scopeMap.get(id));
63647
64130
  }
63648
64131
  const timerId = setTimeout(() => {
63649
64132
  try {
63650
- resolve17(fn());
64133
+ resolve18(fn());
63651
64134
  } catch (error48) {
63652
64135
  reject(error48);
63653
64136
  }
@@ -63737,25 +64220,25 @@ function getParsers() {
63737
64220
  filetype: "javascript",
63738
64221
  aliases: ["javascriptreact"],
63739
64222
  queries: {
63740
- highlights: [resolve15(dirname15(fileURLToPath10(import.meta.url)), highlights_default)]
64223
+ highlights: [resolve16(dirname16(fileURLToPath11(import.meta.url)), highlights_default)]
63741
64224
  },
63742
- wasm: resolve15(dirname15(fileURLToPath10(import.meta.url)), tree_sitter_javascript_default)
64225
+ wasm: resolve16(dirname16(fileURLToPath11(import.meta.url)), tree_sitter_javascript_default)
63743
64226
  },
63744
64227
  {
63745
64228
  filetype: "typescript",
63746
64229
  aliases: ["typescriptreact"],
63747
64230
  queries: {
63748
- highlights: [resolve15(dirname15(fileURLToPath10(import.meta.url)), highlights_default2)]
64231
+ highlights: [resolve16(dirname16(fileURLToPath11(import.meta.url)), highlights_default2)]
63749
64232
  },
63750
- wasm: resolve15(dirname15(fileURLToPath10(import.meta.url)), tree_sitter_typescript_default)
64233
+ wasm: resolve16(dirname16(fileURLToPath11(import.meta.url)), tree_sitter_typescript_default)
63751
64234
  },
63752
64235
  {
63753
64236
  filetype: "markdown",
63754
64237
  queries: {
63755
- highlights: [resolve15(dirname15(fileURLToPath10(import.meta.url)), highlights_default3)],
63756
- injections: [resolve15(dirname15(fileURLToPath10(import.meta.url)), injections_default)]
64238
+ highlights: [resolve16(dirname16(fileURLToPath11(import.meta.url)), highlights_default3)],
64239
+ injections: [resolve16(dirname16(fileURLToPath11(import.meta.url)), injections_default)]
63757
64240
  },
63758
- wasm: resolve15(dirname15(fileURLToPath10(import.meta.url)), tree_sitter_markdown_default),
64241
+ wasm: resolve16(dirname16(fileURLToPath11(import.meta.url)), tree_sitter_markdown_default),
63759
64242
  injectionMapping: {
63760
64243
  nodeTypes: {
63761
64244
  inline: "markdown_inline",
@@ -63778,16 +64261,16 @@ function getParsers() {
63778
64261
  {
63779
64262
  filetype: "markdown_inline",
63780
64263
  queries: {
63781
- highlights: [resolve15(dirname15(fileURLToPath10(import.meta.url)), highlights_default4)]
64264
+ highlights: [resolve16(dirname16(fileURLToPath11(import.meta.url)), highlights_default4)]
63782
64265
  },
63783
- wasm: resolve15(dirname15(fileURLToPath10(import.meta.url)), tree_sitter_markdown_inline_default)
64266
+ wasm: resolve16(dirname16(fileURLToPath11(import.meta.url)), tree_sitter_markdown_inline_default)
63784
64267
  },
63785
64268
  {
63786
64269
  filetype: "zig",
63787
64270
  queries: {
63788
- highlights: [resolve15(dirname15(fileURLToPath10(import.meta.url)), highlights_default5)]
64271
+ highlights: [resolve16(dirname16(fileURLToPath11(import.meta.url)), highlights_default5)]
63789
64272
  },
63790
- wasm: resolve15(dirname15(fileURLToPath10(import.meta.url)), tree_sitter_zig_default)
64273
+ wasm: resolve16(dirname16(fileURLToPath11(import.meta.url)), tree_sitter_zig_default)
63791
64274
  }
63792
64275
  ];
63793
64276
  }
@@ -63929,7 +64412,7 @@ class DownloadUtils {
63929
64412
  const cacheFile = path3.join(cacheDir, cacheSubdir, cacheFileName);
63930
64413
  await mkdir9(path3.dirname(cacheFile), { recursive: true });
63931
64414
  try {
63932
- const cachedContent = await readFile10(cacheFile);
64415
+ const cachedContent = await readFile11(cacheFile);
63933
64416
  if (cachedContent.byteLength > 0) {
63934
64417
  console.log(`Loaded from cache: ${cacheFile} (${source})`);
63935
64418
  return { content: cachedContent, filePath: cacheFile };
@@ -63955,7 +64438,7 @@ class DownloadUtils {
63955
64438
  } else {
63956
64439
  try {
63957
64440
  console.log(`Loading from local path: ${source}`);
63958
- const content = await readFile10(source);
64441
+ const content = await readFile11(source);
63959
64442
  return { content, filePath: source };
63960
64443
  } catch (error48) {
63961
64444
  return { error: `Error loading from local path ${source}: ${error48}` };
@@ -63982,7 +64465,7 @@ class DownloadUtils {
63982
64465
  } else {
63983
64466
  try {
63984
64467
  console.log(`Copying from local path: ${source}`);
63985
- const content = await readFile10(source);
64468
+ const content = await readFile11(source);
63986
64469
  await writeFile8(targetPath, Buffer.from(content));
63987
64470
  return { content, filePath: targetPath };
63988
64471
  } catch (error48) {
@@ -93023,14 +93506,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
93023
93506
  prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
93024
93507
  actScopeDepth = prevActScopeDepth;
93025
93508
  }
93026
- function recursivelyFlushAsyncActWork(returnValue, resolve17, reject) {
93509
+ function recursivelyFlushAsyncActWork(returnValue, resolve18, reject) {
93027
93510
  var queue = ReactSharedInternals.actQueue;
93028
93511
  if (queue !== null)
93029
93512
  if (queue.length !== 0)
93030
93513
  try {
93031
93514
  flushActQueue(queue);
93032
93515
  enqueueTask(function() {
93033
- return recursivelyFlushAsyncActWork(returnValue, resolve17, reject);
93516
+ return recursivelyFlushAsyncActWork(returnValue, resolve18, reject);
93034
93517
  });
93035
93518
  return;
93036
93519
  } catch (error48) {
@@ -93038,7 +93521,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
93038
93521
  }
93039
93522
  else
93040
93523
  ReactSharedInternals.actQueue = null;
93041
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve17(returnValue);
93524
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve18(returnValue);
93042
93525
  }
93043
93526
  function flushActQueue(queue) {
93044
93527
  if (!isFlushing) {
@@ -93214,14 +93697,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
93214
93697
  didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = true, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
93215
93698
  });
93216
93699
  return {
93217
- then: function(resolve17, reject) {
93700
+ then: function(resolve18, reject) {
93218
93701
  didAwaitActCall = true;
93219
93702
  thenable.then(function(returnValue) {
93220
93703
  popActScope(prevActQueue, prevActScopeDepth);
93221
93704
  if (prevActScopeDepth === 0) {
93222
93705
  try {
93223
93706
  flushActQueue(queue), enqueueTask(function() {
93224
- return recursivelyFlushAsyncActWork(returnValue, resolve17, reject);
93707
+ return recursivelyFlushAsyncActWork(returnValue, resolve18, reject);
93225
93708
  });
93226
93709
  } catch (error$0) {
93227
93710
  ReactSharedInternals.thrownErrors.push(error$0);
@@ -93232,7 +93715,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
93232
93715
  reject(_thrownError);
93233
93716
  }
93234
93717
  } else
93235
- resolve17(returnValue);
93718
+ resolve18(returnValue);
93236
93719
  }, function(error48) {
93237
93720
  popActScope(prevActQueue, prevActScopeDepth);
93238
93721
  0 < ReactSharedInternals.thrownErrors.length ? (error48 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(error48)) : reject(error48);
@@ -93248,11 +93731,11 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
93248
93731
  if (0 < ReactSharedInternals.thrownErrors.length)
93249
93732
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
93250
93733
  return {
93251
- then: function(resolve17, reject) {
93734
+ then: function(resolve18, reject) {
93252
93735
  didAwaitActCall = true;
93253
93736
  prevActScopeDepth === 0 ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
93254
- return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve17, reject);
93255
- })) : resolve17(returnValue$jscomp$0);
93737
+ return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve18, reject);
93738
+ })) : resolve18(returnValue$jscomp$0);
93256
93739
  }
93257
93740
  };
93258
93741
  };
@@ -95733,8 +96216,8 @@ It can also happen if the client has a browser extension installed which messes
95733
96216
  currentEntangledActionThenable = {
95734
96217
  status: "pending",
95735
96218
  value: undefined,
95736
- then: function(resolve17) {
95737
- entangledListeners.push(resolve17);
96219
+ then: function(resolve18) {
96220
+ entangledListeners.push(resolve18);
95738
96221
  }
95739
96222
  };
95740
96223
  }
@@ -95758,8 +96241,8 @@ It can also happen if the client has a browser extension installed which messes
95758
96241
  status: "pending",
95759
96242
  value: null,
95760
96243
  reason: null,
95761
- then: function(resolve17) {
95762
- listeners.push(resolve17);
96244
+ then: function(resolve18) {
96245
+ listeners.push(resolve18);
95763
96246
  }
95764
96247
  };
95765
96248
  thenable.then(function() {
@@ -121741,14 +122224,14 @@ async function runScoutMonitorApp(options) {
121741
122224
  }
121742
122225
  const renderer = await createCliRenderer();
121743
122226
  let closed = false;
121744
- await new Promise((resolve17) => {
122227
+ await new Promise((resolve18) => {
121745
122228
  const close = () => {
121746
122229
  if (closed) {
121747
122230
  return;
121748
122231
  }
121749
122232
  closed = true;
121750
122233
  renderer.destroy();
121751
- resolve17();
122234
+ resolve18();
121752
122235
  };
121753
122236
  createRoot(renderer).render(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ScoutMonitorApp, {
121754
122237
  currentDirectory: options.currentDirectory,
@@ -121797,7 +122280,7 @@ __export(exports_up, {
121797
122280
  runUpCommand: () => runUpCommand
121798
122281
  });
121799
122282
  import { existsSync as existsSync23 } from "fs";
121800
- import { resolve as resolve17 } from "path";
122283
+ import { resolve as resolve18 } from "path";
121801
122284
  function looksLikePath(value) {
121802
122285
  return value.includes("/") || value.startsWith(".") || value.startsWith("~");
121803
122286
  }
@@ -121859,14 +122342,19 @@ async function runUpCommand(context, args) {
121859
122342
  throw new ScoutCliError("usage: scout up <name|path> [--name <alias>] [--harness <claude|codex>] [--model <model>]");
121860
122343
  }
121861
122344
  let projectPath;
121862
- if (looksLikePath(target) || existsSync23(resolve17(target))) {
121863
- projectPath = resolve17(target);
122345
+ if (looksLikePath(target) || existsSync23(resolve18(target))) {
122346
+ projectPath = resolve18(target);
121864
122347
  } else {
121865
122348
  const resolved = await resolveLocalAgentByName(target);
121866
122349
  if (!resolved) {
121867
- throw new ScoutCliError(`unknown agent "${target}" \u2014 not a registered name or valid path`);
122350
+ const projectMatch = await resolveLocalAgentByName(target, { matchProjectName: true });
122351
+ if (projectMatch) {
122352
+ throw new ScoutCliError(`unknown agent "${target}" \u2014 that matches project "${projectMatch.projectRoot}", ` + `but the registered agent is "${projectMatch.agentId}". ` + `Use \`scout up ${projectMatch.agentId}\` or \`scout up "${projectMatch.projectRoot}"\`.`);
122353
+ }
122354
+ throw new ScoutCliError(`unknown agent "${target}" \u2014 not a registered agent name or valid path`);
121868
122355
  }
121869
122356
  projectPath = resolved.projectRoot;
122357
+ agentName ??= resolved.definitionId;
121870
122358
  }
121871
122359
  const agent = await upScoutAgent({
121872
122360
  projectPath,
@@ -121980,7 +122468,7 @@ var init_whoami = __esm(async () => {
121980
122468
  });
121981
122469
 
121982
122470
  // ../../apps/desktop/src/cli/main.ts
121983
- import { existsSync as existsSync25, readFileSync as readFileSync15, writeFileSync as writeFileSync9, statSync as statSync5, mkdirSync as mkdirSync12 } from "fs";
122471
+ import { existsSync as existsSync25, readFileSync as readFileSync15, writeFileSync as writeFileSync9, statSync as statSync5, mkdirSync as mkdirSync13 } from "fs";
121984
122472
  import { join as join32 } from "path";
121985
122473
  import { spawnSync as spawnSync3 } from "child_process";
121986
122474
  import { homedir as homedir24 } from "os";
@@ -122147,12 +122635,12 @@ function renderScoutHelp(version2 = "0.2.18") {
122147
122635
  "Global flags:",
122148
122636
  ' --json Structured JSON (doctor: NDJSON stream; last object has phase "complete")',
122149
122637
  "",
122150
- "Implicit ask:",
122151
- " scout @agent your request",
122152
- " scout hey @agent can you review this?",
122153
- " scout @agent.harness:codex use the Codex-backed one",
122638
+ "Fast path:",
122639
+ ' scout send "@hudson ready for review" # tell / update in a DM',
122640
+ ' scout ask --to hudson "review the parser" # owned work / reply in a DM',
122641
+ " scout ask --to hudson --prompt-file ./handoff.md",
122154
122642
  "",
122155
- "Operator loop:",
122643
+ "Orientation (only when route or sender is unclear):",
122156
122644
  " scout whoami",
122157
122645
  " scout who",
122158
122646
  " scout latest",
@@ -122165,9 +122653,21 @@ function renderScoutHelp(version2 = "0.2.18") {
122165
122653
  " scout card create # fresh reply-ready return address",
122166
122654
  ' scout broadcast "deploying in 15m" # explicit shared broadcast',
122167
122655
  "",
122656
+ "Implicit ask shortcut:",
122657
+ " scout @agent your request",
122658
+ " scout @agent --prompt-file ./handoff.md",
122659
+ " scout hey @agent can you review this?",
122660
+ " scout @agent.harness:codex use the Codex-backed one",
122661
+ "",
122662
+ "File-backed input:",
122663
+ " ask uses --prompt-file for the primary work prompt",
122664
+ " send/broadcast/speak use --message-file for the message body",
122665
+ " --body-file is accepted as the shared alias",
122666
+ "",
122168
122667
  "One-to-one delegation:",
122169
122668
  ' scout ask --to hudson "review the parser" # DM by default',
122170
122669
  ' scout ask --as premotion.master.mini --to hudson "build the editor"',
122670
+ " scout ask --to hudson --prompt-file ./handoff.md",
122171
122671
  "",
122172
122672
  "Routing:",
122173
122673
  ' scout send "@hudson ready for review" # one target -> DM',
@@ -122195,6 +122695,16 @@ function renderScoutHelp(version2 = "0.2.18") {
122195
122695
 
122196
122696
  // ../../apps/desktop/src/cli/main.ts
122197
122697
  init_options();
122698
+
122699
+ // ../../apps/desktop/src/cli/uptodate.ts
122700
+ function normalizeCliBinaryMtimeMs(value) {
122701
+ return Math.floor(value);
122702
+ }
122703
+ function shouldRestartBrokerForCliMtime(currentMtimeMs, persistedMtimeMs) {
122704
+ return normalizeCliBinaryMtimeMs(currentMtimeMs) > normalizeCliBinaryMtimeMs(persistedMtimeMs);
122705
+ }
122706
+
122707
+ // ../../apps/desktop/src/cli/main.ts
122198
122708
  init_product();
122199
122709
  async function main3() {
122200
122710
  const input = parseScoutArgv(process.argv.slice(2));
@@ -122251,22 +122761,22 @@ function getScoutBinPath() {
122251
122761
  }
122252
122762
  async function ensureBrokerUptodate() {
122253
122763
  try {
122254
- const mtime = statSync5(getScoutBinPath()).mtimeMs;
122764
+ const mtime = normalizeCliBinaryMtimeMs(statSync5(getScoutBinPath()).mtimeMs);
122255
122765
  const checkpointDir = join32(homedir24(), ".scout");
122256
122766
  const mtimePath = join32(checkpointDir, "cli-mtime");
122257
122767
  const lastMtime = existsSync25(mtimePath) ? Number(readFileSync15(mtimePath, "utf8").trim()) : 0;
122258
- if (mtime > lastMtime) {
122768
+ if (shouldRestartBrokerForCliMtime(mtime, lastMtime)) {
122259
122769
  const uid = process.getuid();
122260
122770
  const plistPath = join32(homedir24(), "Library", "LaunchAgents", "dev.openscout.broker.plist");
122261
122771
  spawnSync3("launchctl", ["bootout", `gui/${uid}/dev.openscout.broker`], { stdio: "ignore" });
122262
- await new Promise((resolve18) => setTimeout(resolve18, 1500));
122772
+ await new Promise((resolve19) => setTimeout(resolve19, 1500));
122263
122773
  spawnSync3("launchctl", ["bootstrap", `gui/${uid}`, plistPath], { stdio: "ignore" });
122264
- await new Promise((resolve18) => setTimeout(resolve18, 2000));
122774
+ await new Promise((resolve19) => setTimeout(resolve19, 2000));
122265
122775
  }
122266
122776
  if (!existsSync25(checkpointDir)) {
122267
- mkdirSync12(checkpointDir, { recursive: true });
122777
+ mkdirSync13(checkpointDir, { recursive: true });
122268
122778
  }
122269
- writeFileSync9(mtimePath, String(Math.floor(mtime)), { encoding: "utf8", flag: "w" });
122779
+ writeFileSync9(mtimePath, String(mtime), { encoding: "utf8", flag: "w" });
122270
122780
  } catch {}
122271
122781
  }
122272
122782
  try {