@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.
@@ -56,6 +56,9 @@ function resolvedPairingConfig() {
56
56
  };
57
57
  }
58
58
 
59
+ // ../../apps/desktop/src/core/pairing/supervisor.ts
60
+ import { spawn as spawn4 } from "child_process";
61
+
59
62
  // ../../node_modules/.bun/uqr@0.1.3/node_modules/uqr/dist/index.mjs
60
63
  var QrCodeDataType = /* @__PURE__ */ ((QrCodeDataType2) => {
61
64
  QrCodeDataType2[QrCodeDataType2["Border"] = -1] = "Border";
@@ -685,7 +688,7 @@ function renderQRCode(payload) {
685
688
  // ../../apps/desktop/src/core/pairing/runtime/relay-runtime.ts
686
689
  import { execSync } from "child_process";
687
690
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, readdirSync } from "fs";
688
- import { homedir as homedir2 } from "os";
691
+ import { homedir as homedir2, networkInterfaces } from "os";
689
692
  import { join } from "path";
690
693
 
691
694
  // ../../apps/desktop/src/core/pairing/runtime/log.ts
@@ -716,6 +719,7 @@ function startRelay(port, options = {}) {
716
719
  const roomByBridgeKey = new Map;
717
720
  const server = Bun.serve({
718
721
  port,
722
+ hostname: "0.0.0.0",
719
723
  ...options.tls ? {
720
724
  tls: {
721
725
  cert: Bun.file(options.tls.cert),
@@ -974,24 +978,42 @@ function readTailscaleStatus() {
974
978
  return null;
975
979
  }
976
980
  }
977
- function resolveRelayEndpointForTailscaleStatus(port, tailscale) {
981
+ function resolveRelayEndpointForTailscaleStatus(port, tailscale, options = {}) {
978
982
  const backendState = tailscale?.backendState?.trim().toLowerCase() ?? "";
979
983
  const hostname = tailscale?.dnsName ?? null;
980
984
  const tailscaleRunning = backendState === "running" && tailscale?.online !== false && Boolean(hostname);
981
- const tls = resolveTls(tailscaleRunning ? hostname : null);
982
- if (tailscaleRunning && hostname && tls) {
985
+ const tls = tailscaleRunning ? options.tls !== undefined ? options.tls : resolveTls(hostname) : null;
986
+ const scheme = tls ? "wss" : "ws";
987
+ const connectUrl = `${scheme}://127.0.0.1:${port}`;
988
+ const localAddress = options.localAddress !== undefined ? normalizedOptionalAddress(options.localAddress) : findLocalNetworkAddress();
989
+ const localRelayUrl = localAddress ? `${scheme}://${localAddress}:${port}` : null;
990
+ const tailnetRelayUrl = tailscaleRunning && hostname ? `${scheme}://${hostname}:${port}` : null;
991
+ const fallbackRelayUrls = tailnetRelayUrl && localRelayUrl ? [tailnetRelayUrl] : [];
992
+ if (localRelayUrl) {
983
993
  return {
984
- relayUrl: `wss://${hostname}:${port}`,
994
+ relayUrl: localRelayUrl,
995
+ connectUrl,
996
+ fallbackRelayUrls,
997
+ options: tls ? { tls } : {}
998
+ };
999
+ }
1000
+ if (tailnetRelayUrl && tls) {
1001
+ return {
1002
+ relayUrl: tailnetRelayUrl,
1003
+ connectUrl,
1004
+ fallbackRelayUrls: [],
985
1005
  options: { tls }
986
1006
  };
987
1007
  }
988
- if (tailscaleRunning && hostname) {
1008
+ if (tailnetRelayUrl) {
989
1009
  pairingLog.warn("relay", "tailscale is running without TLS; falling back to insecure websocket relay", {
990
1010
  hostname,
991
1011
  port
992
1012
  });
993
1013
  return {
994
- relayUrl: `ws://${hostname}:${port}`,
1014
+ relayUrl: tailnetRelayUrl,
1015
+ connectUrl,
1016
+ fallbackRelayUrls: [],
995
1017
  options: {}
996
1018
  };
997
1019
  }
@@ -1004,7 +1026,9 @@ function resolveRelayEndpointForTailscaleStatus(port, tailscale) {
1004
1026
  });
1005
1027
  }
1006
1028
  return {
1007
- relayUrl: `ws://127.0.0.1:${port}`,
1029
+ relayUrl: connectUrl,
1030
+ connectUrl,
1031
+ fallbackRelayUrls: [],
1008
1032
  options: {}
1009
1033
  };
1010
1034
  }
@@ -1057,16 +1081,87 @@ function resolveRelayEndpoint(port) {
1057
1081
  }
1058
1082
  function startManagedRelay(port = 7889) {
1059
1083
  const endpoint = resolveRelayEndpoint(port);
1060
- pairingLog.info("relay", "starting managed relay", { relay: endpoint.relayUrl, port });
1084
+ pairingLog.info("relay", "starting managed relay", {
1085
+ relay: endpoint.relayUrl,
1086
+ connectUrl: endpoint.connectUrl,
1087
+ fallbackRelayUrls: endpoint.fallbackRelayUrls,
1088
+ port
1089
+ });
1061
1090
  const relay = startRelay(port, endpoint.options);
1062
1091
  return {
1063
1092
  relayUrl: endpoint.relayUrl,
1093
+ connectUrl: endpoint.connectUrl,
1094
+ fallbackRelayUrls: endpoint.fallbackRelayUrls,
1064
1095
  stop() {
1065
1096
  pairingLog.info("relay", "stopping managed relay", { relay: endpoint.relayUrl, port });
1066
1097
  relay.stop();
1067
1098
  }
1068
1099
  };
1069
1100
  }
1101
+ function normalizedOptionalAddress(address) {
1102
+ const trimmed = address?.trim();
1103
+ return trimmed ? trimmed : null;
1104
+ }
1105
+ function findLocalNetworkAddress() {
1106
+ const candidates = [];
1107
+ const interfaces = networkInterfaces();
1108
+ for (const [name, entries] of Object.entries(interfaces)) {
1109
+ for (const entry of entries ?? []) {
1110
+ if (entry.internal || entry.family !== "IPv4") {
1111
+ continue;
1112
+ }
1113
+ if (!isPrivateIPv4(entry.address) || isLinkLocalIPv4(entry.address) || isTailscaleIPv4(entry.address)) {
1114
+ continue;
1115
+ }
1116
+ candidates.push({ name, address: entry.address });
1117
+ }
1118
+ }
1119
+ candidates.sort((left, right) => {
1120
+ const leftScore = localInterfaceScore(left.name);
1121
+ const rightScore = localInterfaceScore(right.name);
1122
+ if (leftScore !== rightScore) {
1123
+ return leftScore - rightScore;
1124
+ }
1125
+ return left.address.localeCompare(right.address);
1126
+ });
1127
+ return candidates[0]?.address ?? null;
1128
+ }
1129
+ function localInterfaceScore(name) {
1130
+ if (/^en\d+$/i.test(name)) {
1131
+ return 0;
1132
+ }
1133
+ if (/^bridge\d*$/i.test(name)) {
1134
+ return 2;
1135
+ }
1136
+ return 1;
1137
+ }
1138
+ function isPrivateIPv4(address) {
1139
+ const octets = parseIPv4(address);
1140
+ if (!octets) {
1141
+ return false;
1142
+ }
1143
+ const [first, second] = octets;
1144
+ return first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168;
1145
+ }
1146
+ function isLinkLocalIPv4(address) {
1147
+ const octets = parseIPv4(address);
1148
+ return Boolean(octets && octets[0] === 169 && octets[1] === 254);
1149
+ }
1150
+ function isTailscaleIPv4(address) {
1151
+ const octets = parseIPv4(address);
1152
+ return Boolean(octets && octets[0] === 100 && octets[1] >= 64 && octets[1] <= 127);
1153
+ }
1154
+ function parseIPv4(address) {
1155
+ const octets = address.split(".");
1156
+ if (octets.length !== 4) {
1157
+ return null;
1158
+ }
1159
+ const numbers = octets.map((octet) => Number(octet));
1160
+ if (numbers.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {
1161
+ return null;
1162
+ }
1163
+ return numbers;
1164
+ }
1070
1165
 
1071
1166
  // ../../apps/desktop/src/core/pairing/runtime/runtime-state.ts
1072
1167
  import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
@@ -3152,15 +3247,30 @@ function isTrustedPeer(publicKeyHex) {
3152
3247
  }
3153
3248
  var QR_VERSION = 1;
3154
3249
  var QR_EXPIRY_MS = 5 * 60 * 1000;
3155
- function createQRPayload(bridgePublicKey, relayUrl) {
3250
+ function createQRPayload(bridgePublicKey, relayUrl, fallbackRelayUrls = []) {
3251
+ const fallbackRelays = normalizedRelayUrls(fallbackRelayUrls, relayUrl);
3156
3252
  return {
3157
3253
  v: QR_VERSION,
3158
3254
  relay: relayUrl,
3255
+ ...fallbackRelays.length > 0 ? { fallbackRelays } : {},
3159
3256
  room: crypto.randomUUID(),
3160
3257
  publicKey: bytesToHex2(bridgePublicKey),
3161
3258
  expiresAt: Date.now() + QR_EXPIRY_MS
3162
3259
  };
3163
3260
  }
3261
+ function normalizedRelayUrls(urls, primaryRelayUrl) {
3262
+ const seen = new Set([primaryRelayUrl.trim()]);
3263
+ const normalized = [];
3264
+ for (const url of urls) {
3265
+ const trimmed = url.trim();
3266
+ if (!trimmed || seen.has(trimmed)) {
3267
+ continue;
3268
+ }
3269
+ seen.add(trimmed);
3270
+ normalized.push(trimmed);
3271
+ }
3272
+ return normalized;
3273
+ }
3164
3274
  function bytesToHex2(bytes) {
3165
3275
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
3166
3276
  }
@@ -3394,7 +3504,7 @@ function writeJsonAtomically(filePath, value) {
3394
3504
  }
3395
3505
 
3396
3506
  // ../../apps/desktop/src/core/pairing/runtime/runtime.ts
3397
- import { homedir as homedir18 } from "os";
3507
+ import { homedir as homedir19 } from "os";
3398
3508
  // ../agent-sessions/src/protocol/adapter.ts
3399
3509
  class BaseAdapter {
3400
3510
  config;
@@ -4026,6 +4136,15 @@ function stringifyUnknown(value) {
4026
4136
  return String(value);
4027
4137
  }
4028
4138
  }
4139
+ function isRecord(value) {
4140
+ return !!value && typeof value === "object" && !Array.isArray(value);
4141
+ }
4142
+ function maybeString(value) {
4143
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
4144
+ }
4145
+ function maybeNumber(value) {
4146
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
4147
+ }
4029
4148
  function renderToolResultContent(content) {
4030
4149
  if (typeof content === "string") {
4031
4150
  return content;
@@ -4081,6 +4200,7 @@ class ClaudeCodeHistoryParser {
4081
4200
  blockById = new Map;
4082
4201
  activeStreamBlocks = new Map;
4083
4202
  sawStreamTextThisTurn = false;
4203
+ assistantUsageByMessageId = new Map;
4084
4204
  constructor(session, baseTimestampMs) {
4085
4205
  this.session = session;
4086
4206
  this.baseTimestampMs = baseTimestampMs;
@@ -4090,6 +4210,7 @@ class ClaudeCodeHistoryParser {
4090
4210
  let parsedLineCount = 0;
4091
4211
  let skippedLineCount = 0;
4092
4212
  let lineCount = 0;
4213
+ let lastCapturedAt = this.baseTimestampMs;
4093
4214
  for (let index = 0;index < lines.length; index += 1) {
4094
4215
  const rawLine = lines[index];
4095
4216
  const trimmed = rawLine.trim();
@@ -4110,12 +4231,19 @@ class ClaudeCodeHistoryParser {
4110
4231
  continue;
4111
4232
  }
4112
4233
  const capturedAt = extractRecordTimestamp(record) ?? this.baseTimestampMs + index;
4234
+ lastCapturedAt = capturedAt;
4113
4235
  if (this.handleRecord(record, capturedAt)) {
4114
4236
  parsedLineCount += 1;
4115
4237
  } else {
4116
4238
  skippedLineCount += 1;
4117
4239
  }
4118
4240
  }
4241
+ if (this.persistObserveUsageMetadata()) {
4242
+ this.emitEvent(lastCapturedAt, {
4243
+ event: "session:update",
4244
+ session: { ...this.session }
4245
+ });
4246
+ }
4119
4247
  return {
4120
4248
  events: this.events,
4121
4249
  lineCount,
@@ -4124,6 +4252,7 @@ class ClaudeCodeHistoryParser {
4124
4252
  };
4125
4253
  }
4126
4254
  handleRecord(record, capturedAt) {
4255
+ this.captureRecordMetadata(record);
4127
4256
  const type = typeof record.type === "string" ? record.type : null;
4128
4257
  if (!type) {
4129
4258
  return false;
@@ -4133,7 +4262,7 @@ class ClaudeCodeHistoryParser {
4133
4262
  this.handleSystem(record, capturedAt);
4134
4263
  return true;
4135
4264
  case "user":
4136
- this.startTurn(capturedAt);
4265
+ this.handleUser(record, capturedAt);
4137
4266
  return true;
4138
4267
  case "assistant":
4139
4268
  this.handleAssistant(record, capturedAt);
@@ -4157,6 +4286,130 @@ class ClaudeCodeHistoryParser {
4157
4286
  return false;
4158
4287
  }
4159
4288
  }
4289
+ captureRecordMetadata(record) {
4290
+ const runtime = this.ensureObserveMetaRecord("observeRuntime");
4291
+ this.assignObserveString(runtime, "entrypoint", record.entrypoint);
4292
+ this.assignObserveString(runtime, "cliVersion", record.version);
4293
+ this.assignObserveString(runtime, "gitBranch", record.gitBranch);
4294
+ this.assignObserveString(runtime, "permissionMode", record.permissionMode);
4295
+ this.assignObserveString(runtime, "userType", record.userType);
4296
+ const recordCwd = maybeString(record.cwd);
4297
+ if (recordCwd && !this.session.cwd) {
4298
+ this.session.cwd = recordCwd;
4299
+ }
4300
+ const message = isRecord(record.message) ? record.message : null;
4301
+ const model = maybeString(message?.model);
4302
+ if (model && !this.session.model) {
4303
+ this.session.model = model;
4304
+ }
4305
+ const usage = this.readClaudeUsageEntry(record);
4306
+ if (!usage) {
4307
+ return;
4308
+ }
4309
+ const messageId = maybeString(message?.id) ?? maybeString(record.requestId) ?? maybeString(record.uuid);
4310
+ if (!messageId) {
4311
+ return;
4312
+ }
4313
+ this.assistantUsageByMessageId.set(messageId, usage);
4314
+ }
4315
+ readClaudeUsageEntry(record) {
4316
+ const type = maybeString(record.type);
4317
+ const message = isRecord(record.message) ? record.message : null;
4318
+ const role = maybeString(message?.role);
4319
+ if (type !== "assistant" && role !== "assistant") {
4320
+ return null;
4321
+ }
4322
+ const usage = isRecord(message?.usage) ? message.usage : null;
4323
+ const serverToolUse = isRecord(usage?.server_tool_use) ? usage.server_tool_use : null;
4324
+ const entry = {
4325
+ inputTokens: maybeNumber(usage?.input_tokens) ?? 0,
4326
+ outputTokens: maybeNumber(usage?.output_tokens) ?? 0,
4327
+ cacheReadInputTokens: maybeNumber(usage?.cache_read_input_tokens) ?? 0,
4328
+ cacheCreationInputTokens: maybeNumber(usage?.cache_creation_input_tokens) ?? 0,
4329
+ webSearchRequests: maybeNumber(serverToolUse?.web_search_requests) ?? 0,
4330
+ webFetchRequests: maybeNumber(serverToolUse?.web_fetch_requests) ?? 0,
4331
+ ...maybeString(message?.service_tier) ? { serviceTier: maybeString(message?.service_tier) } : {},
4332
+ ...maybeString(message?.speed) ? { speed: maybeString(message?.speed) } : {}
4333
+ };
4334
+ 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);
4335
+ return hasUsage ? entry : null;
4336
+ }
4337
+ persistObserveUsageMetadata() {
4338
+ if (this.assistantUsageByMessageId.size === 0) {
4339
+ return false;
4340
+ }
4341
+ const usage = this.ensureObserveMetaRecord("observeUsage");
4342
+ let inputTokens = 0;
4343
+ let outputTokens = 0;
4344
+ let cacheReadInputTokens = 0;
4345
+ let cacheCreationInputTokens = 0;
4346
+ let webSearchRequests = 0;
4347
+ let webFetchRequests = 0;
4348
+ let serviceTier;
4349
+ let speed;
4350
+ for (const entry of this.assistantUsageByMessageId.values()) {
4351
+ inputTokens += entry.inputTokens;
4352
+ outputTokens += entry.outputTokens;
4353
+ cacheReadInputTokens += entry.cacheReadInputTokens;
4354
+ cacheCreationInputTokens += entry.cacheCreationInputTokens;
4355
+ webSearchRequests += entry.webSearchRequests;
4356
+ webFetchRequests += entry.webFetchRequests;
4357
+ if (entry.serviceTier) {
4358
+ serviceTier = entry.serviceTier;
4359
+ }
4360
+ if (entry.speed) {
4361
+ speed = entry.speed;
4362
+ }
4363
+ }
4364
+ let changed = false;
4365
+ const assignNumber = (key, value) => {
4366
+ if (usage[key] !== value) {
4367
+ usage[key] = value;
4368
+ changed = true;
4369
+ }
4370
+ };
4371
+ const assignString = (key, value) => {
4372
+ if (usage[key] !== value) {
4373
+ usage[key] = value;
4374
+ changed = true;
4375
+ }
4376
+ };
4377
+ assignNumber("assistantMessages", this.assistantUsageByMessageId.size);
4378
+ if (inputTokens > 0)
4379
+ assignNumber("inputTokens", inputTokens);
4380
+ if (outputTokens > 0)
4381
+ assignNumber("outputTokens", outputTokens);
4382
+ if (cacheReadInputTokens > 0)
4383
+ assignNumber("cacheReadInputTokens", cacheReadInputTokens);
4384
+ if (cacheCreationInputTokens > 0)
4385
+ assignNumber("cacheCreationInputTokens", cacheCreationInputTokens);
4386
+ if (webSearchRequests > 0)
4387
+ assignNumber("webSearchRequests", webSearchRequests);
4388
+ if (webFetchRequests > 0)
4389
+ assignNumber("webFetchRequests", webFetchRequests);
4390
+ if (serviceTier)
4391
+ assignString("serviceTier", serviceTier);
4392
+ if (speed)
4393
+ assignString("speed", speed);
4394
+ return changed;
4395
+ }
4396
+ ensureObserveMetaRecord(key) {
4397
+ const providerMeta = isRecord(this.session.providerMeta) ? this.session.providerMeta : {};
4398
+ this.session.providerMeta = providerMeta;
4399
+ const existing = providerMeta[key];
4400
+ if (isRecord(existing)) {
4401
+ return existing;
4402
+ }
4403
+ const next = {};
4404
+ providerMeta[key] = next;
4405
+ return next;
4406
+ }
4407
+ assignObserveString(target, key, value) {
4408
+ const next = maybeString(value);
4409
+ if (next) {
4410
+ target[key] = next;
4411
+ }
4412
+ }
4160
4413
  handleSystem(record, capturedAt) {
4161
4414
  if (record.subtype !== "init") {
4162
4415
  return;
@@ -4186,19 +4439,41 @@ class ClaudeCodeHistoryParser {
4186
4439
  });
4187
4440
  }
4188
4441
  }
4442
+ handleUser(record, capturedAt) {
4443
+ const message = record.message && typeof record.message === "object" ? record.message : null;
4444
+ const content = message?.content;
4445
+ if (Array.isArray(content) && content.length > 0) {
4446
+ const toolResults = content.filter((entry) => {
4447
+ return !!entry && typeof entry === "object" && !Array.isArray(entry) && entry.type === "tool_result";
4448
+ });
4449
+ if (toolResults.length === content.length) {
4450
+ for (const toolResult of toolResults) {
4451
+ this.handleToolResult({
4452
+ ...toolResult,
4453
+ tool_use_id: typeof toolResult.tool_use_id === "string" ? toolResult.tool_use_id : toolResult.id,
4454
+ is_error: toolResult.is_error === true
4455
+ }, capturedAt);
4456
+ }
4457
+ return;
4458
+ }
4459
+ }
4460
+ this.startTurn(capturedAt);
4461
+ }
4189
4462
  handleAssistant(record, capturedAt) {
4190
4463
  const turn = this.ensureTurn(capturedAt);
4191
- if (this.sawStreamTextThisTurn) {
4192
- return;
4193
- }
4194
4464
  const content = record.message && typeof record.message === "object" ? record.message.content : record.content;
4195
4465
  if (!Array.isArray(content)) {
4466
+ this.maybeEndTurnFromAssistant(record, capturedAt);
4196
4467
  return;
4197
4468
  }
4469
+ const skipTextBlocks = this.sawStreamTextThisTurn;
4198
4470
  for (const part of content) {
4199
4471
  const contentPart = part;
4200
4472
  const contentType = typeof contentPart.type === "string" ? contentPart.type : "";
4201
4473
  if (contentType === "thinking" || contentType === "reasoning") {
4474
+ if (skipTextBlocks) {
4475
+ continue;
4476
+ }
4202
4477
  const block = this.startBlock(turn, capturedAt, {
4203
4478
  type: "reasoning",
4204
4479
  text: typeof contentPart.thinking === "string" ? contentPart.thinking : typeof contentPart.text === "string" ? contentPart.text : "",
@@ -4206,14 +4481,20 @@ class ClaudeCodeHistoryParser {
4206
4481
  });
4207
4482
  this.emitBlockEnd(capturedAt, turn, block, "completed");
4208
4483
  } else if (contentType === "text") {
4484
+ if (skipTextBlocks) {
4485
+ continue;
4486
+ }
4209
4487
  const block = this.startBlock(turn, capturedAt, {
4210
4488
  type: "text",
4211
4489
  text: typeof contentPart.text === "string" ? contentPart.text : "",
4212
4490
  status: "completed"
4213
4491
  });
4214
4492
  this.emitBlockEnd(capturedAt, turn, block, "completed");
4493
+ } else if (contentType === "tool_use") {
4494
+ this.handleToolUse(contentPart, capturedAt);
4215
4495
  }
4216
4496
  }
4497
+ this.maybeEndTurnFromAssistant(record, capturedAt);
4217
4498
  }
4218
4499
  handleStreamEvent(record, capturedAt) {
4219
4500
  const turn = this.ensureTurn(capturedAt);
@@ -4435,6 +4716,12 @@ class ClaudeCodeHistoryParser {
4435
4716
  this.emitError(turn, capturedAt, message);
4436
4717
  this.endTurn("failed", capturedAt);
4437
4718
  }
4719
+ maybeEndTurnFromAssistant(record, capturedAt) {
4720
+ const stopReason = record.message && typeof record.message === "object" ? record.message.stop_reason : record.stop_reason;
4721
+ if (stopReason === "end_turn") {
4722
+ this.endTurn("completed", capturedAt);
4723
+ }
4724
+ }
4438
4725
  startTurn(capturedAt) {
4439
4726
  if (this.currentTurn) {
4440
4727
  this.endTurn("stopped", capturedAt);
@@ -8539,11 +8826,11 @@ function formatTimestamp(value) {
8539
8826
  // ../../apps/desktop/src/core/pairing/runtime/bridge/server.ts
8540
8827
  import { readdirSync as readdirSync3, readFileSync as readFileSync9, realpathSync, statSync as statSync3 } from "fs";
8541
8828
  import { execSync as execSync3 } from "child_process";
8542
- import { basename as basename6, isAbsolute as isAbsolute3, join as join17, relative as relative2 } from "path";
8543
- import { homedir as homedir15 } from "os";
8829
+ import { basename as basename7, isAbsolute as isAbsolute3, join as join18, relative as relative2 } from "path";
8830
+ import { homedir as homedir16 } from "os";
8544
8831
 
8545
8832
  // ../../apps/desktop/src/core/mobile/service.ts
8546
- import { basename as basename5, resolve as resolve6 } from "path";
8833
+ import { basename as basename6, resolve as resolve7 } from "path";
8547
8834
 
8548
8835
  // ../runtime/src/harness-catalog.ts
8549
8836
  import { execFileSync } from "child_process";
@@ -10154,41 +10441,6 @@ function diagnoseAgentIdentity(identity, candidates) {
10154
10441
  var parseAgentSelector = parseAgentIdentity;
10155
10442
  var formatAgentSelector = formatAgentIdentity;
10156
10443
  var resolveAgentSelector = resolveAgentIdentity;
10157
- // ../protocol/dist/scout-agent-card.js
10158
- function buildScoutReturnAddress(input) {
10159
- const next = {
10160
- actorId: input.actorId,
10161
- handle: input.handle.trim()
10162
- };
10163
- if (input.displayName?.trim()) {
10164
- next.displayName = input.displayName.trim();
10165
- }
10166
- if (input.selector?.trim()) {
10167
- next.selector = input.selector.trim();
10168
- }
10169
- if (input.defaultSelector?.trim()) {
10170
- next.defaultSelector = input.defaultSelector.trim();
10171
- }
10172
- if (input.conversationId?.trim()) {
10173
- next.conversationId = input.conversationId.trim();
10174
- }
10175
- if (input.replyToMessageId?.trim()) {
10176
- next.replyToMessageId = input.replyToMessageId.trim();
10177
- }
10178
- if (input.nodeId?.trim()) {
10179
- next.nodeId = input.nodeId.trim();
10180
- }
10181
- if (input.projectRoot?.trim()) {
10182
- next.projectRoot = input.projectRoot.trim();
10183
- }
10184
- if (input.sessionId?.trim()) {
10185
- next.sessionId = input.sessionId.trim();
10186
- }
10187
- if (input.metadata && Object.keys(input.metadata).length > 0) {
10188
- next.metadata = input.metadata;
10189
- }
10190
- return next;
10191
- }
10192
10444
  // ../runtime/src/user-project-hints.ts
10193
10445
  import { readdir, readFile as readFile3, stat } from "fs/promises";
10194
10446
  import { homedir as homedir11 } from "os";
@@ -12239,9 +12491,9 @@ async function ensureRelayAgentConfigured(value, options = {}) {
12239
12491
 
12240
12492
  // ../runtime/src/local-agents.ts
12241
12493
  import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
12242
- import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
12494
+ import { existsSync as existsSync14, readFileSync as readFileSync8 } from "fs";
12243
12495
  import { mkdir as mkdir6, rm as rm5, stat as stat3, writeFile as writeFile6 } from "fs/promises";
12244
- import { basename as basename4, dirname as dirname6, join as join15, resolve as resolve5 } from "path";
12496
+ import { basename as basename5, dirname as dirname7, join as join16, resolve as resolve6 } from "path";
12245
12497
  import { fileURLToPath as fileURLToPath5 } from "url";
12246
12498
 
12247
12499
  // ../runtime/src/claude-stream-json.ts
@@ -13585,10 +13837,116 @@ function buildCollaborationContractPrompt(agentId) {
13585
13837
 
13586
13838
  // ../runtime/src/broker-service.ts
13587
13839
  import { spawnSync } from "child_process";
13588
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
13840
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
13841
+ import { homedir as homedir14 } from "os";
13842
+ import { dirname as dirname6, join as join15, resolve as resolve5 } from "path";
13843
+ import { fileURLToPath as fileURLToPath4 } from "url";
13844
+
13845
+ // ../runtime/src/tool-resolution.ts
13846
+ import { accessSync as accessSync2, constants as constants4, existsSync as existsSync12 } from "fs";
13589
13847
  import { homedir as homedir13 } from "os";
13590
13848
  import { basename as basename3, dirname as dirname5, join as join14, resolve as resolve4 } from "path";
13591
- import { fileURLToPath as fileURLToPath4 } from "url";
13849
+ var DEFAULT_COMMON_EXECUTABLE_DIRECTORIES = [
13850
+ join14(homedir13(), ".bun", "bin"),
13851
+ "/opt/homebrew/bin",
13852
+ "/usr/local/bin"
13853
+ ];
13854
+ function expandHomePath3(value, env = process.env) {
13855
+ const home = env.HOME ?? homedir13();
13856
+ if (value === "~") {
13857
+ return home;
13858
+ }
13859
+ if (value.startsWith("~/")) {
13860
+ return join14(home, value.slice(2));
13861
+ }
13862
+ return value;
13863
+ }
13864
+ function isExecutablePath(candidate) {
13865
+ if (!candidate) {
13866
+ return false;
13867
+ }
13868
+ try {
13869
+ accessSync2(candidate, constants4.X_OK);
13870
+ return true;
13871
+ } catch {
13872
+ return false;
13873
+ }
13874
+ }
13875
+ function splitPathEntries(env = process.env) {
13876
+ const separator = process.platform === "win32" ? ";" : ":";
13877
+ return (env.PATH ?? "").split(separator).filter(Boolean);
13878
+ }
13879
+ function dedupeDirectories(values, env) {
13880
+ const seen = new Set;
13881
+ const resolvedEntries = [];
13882
+ for (const value of values) {
13883
+ const normalized = resolve4(expandHomePath3(value, env));
13884
+ if (seen.has(normalized)) {
13885
+ continue;
13886
+ }
13887
+ seen.add(normalized);
13888
+ resolvedEntries.push(normalized);
13889
+ }
13890
+ return resolvedEntries;
13891
+ }
13892
+ function resolveExecutableFromSearch(options) {
13893
+ const env = options.env ?? process.env;
13894
+ const envKeys = options.envKeys ?? [];
13895
+ for (const envKey of envKeys) {
13896
+ const explicit = env[envKey]?.trim();
13897
+ if (!explicit) {
13898
+ continue;
13899
+ }
13900
+ const expanded = expandHomePath3(explicit, env);
13901
+ if (isExecutablePath(expanded)) {
13902
+ return { path: resolve4(expanded), source: "env" };
13903
+ }
13904
+ const foundOnPath = findExecutableOnSearchPath(explicit, env);
13905
+ if (foundOnPath) {
13906
+ return { path: foundOnPath.path, source: "env" };
13907
+ }
13908
+ }
13909
+ const commonDirectories = dedupeDirectories([...options.commonDirectories ?? DEFAULT_COMMON_EXECUTABLE_DIRECTORIES], env);
13910
+ const searchDirectories = dedupeDirectories([
13911
+ ...splitPathEntries(env),
13912
+ ...options.extraDirectories ?? [],
13913
+ ...commonDirectories
13914
+ ], env);
13915
+ for (const directory of searchDirectories) {
13916
+ for (const name of options.names) {
13917
+ const candidate = join14(directory, name);
13918
+ if (isExecutablePath(candidate)) {
13919
+ return {
13920
+ path: candidate,
13921
+ source: commonDirectories.includes(directory) ? "common-path" : "path"
13922
+ };
13923
+ }
13924
+ }
13925
+ }
13926
+ return null;
13927
+ }
13928
+ function resolveBunExecutable2(env = process.env) {
13929
+ return resolveExecutableFromSearch({
13930
+ env,
13931
+ envKeys: ["OPENSCOUT_BUN_BIN", "SCOUT_BUN_BIN", "BUN_BIN"],
13932
+ names: ["bun"]
13933
+ });
13934
+ }
13935
+ function findExecutableOnSearchPath(name, env) {
13936
+ const searchDirectories = dedupeDirectories([
13937
+ ...splitPathEntries(env),
13938
+ ...DEFAULT_COMMON_EXECUTABLE_DIRECTORIES
13939
+ ], env);
13940
+ for (const directory of searchDirectories) {
13941
+ const candidate = join14(directory, name);
13942
+ if (isExecutablePath(candidate)) {
13943
+ return { path: candidate, source: "path" };
13944
+ }
13945
+ }
13946
+ return null;
13947
+ }
13948
+
13949
+ // ../runtime/src/broker-service.ts
13592
13950
  function isTmpPath(p) {
13593
13951
  return /^\/(?:private\/)?tmp\//.test(p);
13594
13952
  }
@@ -13627,17 +13985,17 @@ function runtimePackageDir() {
13627
13985
  const fromCwd = findWorkspaceRuntimeDir(process.cwd());
13628
13986
  if (fromCwd)
13629
13987
  return fromCwd;
13630
- const moduleDir = dirname5(fileURLToPath4(import.meta.url));
13631
- return resolve4(moduleDir, "..");
13988
+ const moduleDir = dirname6(fileURLToPath4(import.meta.url));
13989
+ return resolve5(moduleDir, "..");
13632
13990
  }
13633
13991
  function isInstalledRuntimePackageDir(candidate) {
13634
- return existsSync12(join14(candidate, "package.json")) && existsSync12(join14(candidate, "bin", "openscout-runtime.mjs"));
13992
+ return existsSync13(join15(candidate, "package.json")) && existsSync13(join15(candidate, "bin", "openscout-runtime.mjs"));
13635
13993
  }
13636
13994
  function findGlobalRuntimeDir() {
13637
13995
  const candidates = [
13638
- join14(homedir13(), ".bun", "node_modules", "@openscout", "runtime"),
13639
- join14(homedir13(), ".bun", "install", "global", "node_modules", "@openscout", "runtime"),
13640
- join14(homedir13(), ".bun", "install", "global", "node_modules", "@openscout", "scout", "node_modules", "@openscout", "runtime")
13996
+ join15(homedir14(), ".bun", "node_modules", "@openscout", "runtime"),
13997
+ join15(homedir14(), ".bun", "install", "global", "node_modules", "@openscout", "runtime"),
13998
+ join15(homedir14(), ".bun", "install", "global", "node_modules", "@openscout", "scout", "node_modules", "@openscout", "runtime")
13641
13999
  ];
13642
14000
  for (const c of candidates) {
13643
14001
  if (isInstalledRuntimePackageDir(c))
@@ -13647,11 +14005,11 @@ function findGlobalRuntimeDir() {
13647
14005
  const result = spawnSync("which", ["scout"], { encoding: "utf8", timeout: 3000 });
13648
14006
  const scoutBin = result.stdout?.trim();
13649
14007
  if (scoutBin) {
13650
- const scoutPkg = resolve4(scoutBin, "..", "..");
13651
- const nested = join14(scoutPkg, "node_modules", "@openscout", "runtime");
14008
+ const scoutPkg = resolve5(scoutBin, "..", "..");
14009
+ const nested = join15(scoutPkg, "node_modules", "@openscout", "runtime");
13652
14010
  if (isInstalledRuntimePackageDir(nested))
13653
14011
  return nested;
13654
- const sibling = resolve4(scoutPkg, "..", "runtime");
14012
+ const sibling = resolve5(scoutPkg, "..", "runtime");
13655
14013
  if (isInstalledRuntimePackageDir(sibling))
13656
14014
  return sibling;
13657
14015
  }
@@ -13659,38 +14017,24 @@ function findGlobalRuntimeDir() {
13659
14017
  return null;
13660
14018
  }
13661
14019
  function findWorkspaceRuntimeDir(startDir) {
13662
- let current = resolve4(startDir);
14020
+ let current = resolve5(startDir);
13663
14021
  while (true) {
13664
- const candidate = join14(current, "packages", "runtime");
13665
- if (existsSync12(join14(candidate, "package.json")) && existsSync12(join14(candidate, "src"))) {
14022
+ const candidate = join15(current, "packages", "runtime");
14023
+ if (existsSync13(join15(candidate, "package.json")) && existsSync13(join15(candidate, "src"))) {
13666
14024
  return candidate;
13667
14025
  }
13668
- const parent = dirname5(current);
14026
+ const parent = dirname6(current);
13669
14027
  if (parent === current)
13670
14028
  return null;
13671
14029
  current = parent;
13672
14030
  }
13673
14031
  }
13674
- function resolveBunExecutable2() {
13675
- const explicit = process.env.OPENSCOUT_BUN_BIN ?? process.env.BUN_BIN;
13676
- if (explicit && explicit.trim().length > 0) {
13677
- return explicit;
14032
+ function resolveBunExecutable3() {
14033
+ const bun = resolveBunExecutable2(process.env);
14034
+ if (bun) {
14035
+ return bun.path;
13678
14036
  }
13679
- if (basename3(process.execPath).startsWith("bun") && existsSync12(process.execPath)) {
13680
- return process.execPath;
13681
- }
13682
- const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
13683
- for (const entry of pathEntries) {
13684
- const candidate = join14(entry, "bun");
13685
- if (existsSync12(candidate)) {
13686
- return candidate;
13687
- }
13688
- }
13689
- const homeBun = join14(homedir13(), ".bun", "bin", "bun");
13690
- if (existsSync12(homeBun)) {
13691
- return homeBun;
13692
- }
13693
- return "bun";
14037
+ throw new Error("Unable to locate Bun for broker service management. Install Bun or set OPENSCOUT_BUN_BIN.");
13694
14038
  }
13695
14039
  function resolveBrokerServiceMode() {
13696
14040
  const explicit = (process.env.OPENSCOUT_BROKER_SERVICE_MODE ?? "").trim().toLowerCase();
@@ -13729,15 +14073,15 @@ function resolveBrokerServiceConfig() {
13729
14073
  const label = resolveBrokerServiceLabel(mode);
13730
14074
  const uid = typeof process.getuid === "function" ? process.getuid() : Number.parseInt(process.env.UID ?? "0", 10);
13731
14075
  const supportPaths = resolveOpenScoutSupportPaths();
13732
- const defaultSupportDir = join14(homedir13(), "Library", "Application Support", "OpenScout");
14076
+ const defaultSupportDir = join15(homedir14(), "Library", "Application Support", "OpenScout");
13733
14077
  const supportDirectory = isTmpPath(supportPaths.supportDirectory) ? defaultSupportDir : supportPaths.supportDirectory;
13734
- const logsDirectory = join14(supportDirectory, "logs", "broker");
13735
- const controlHome = isTmpPath(supportPaths.controlHome) ? join14(homedir13(), ".openscout", "control-plane") : supportPaths.controlHome;
14078
+ const logsDirectory = join15(supportDirectory, "logs", "broker");
14079
+ const controlHome = isTmpPath(supportPaths.controlHome) ? join15(homedir14(), ".openscout", "control-plane") : supportPaths.controlHome;
13736
14080
  const advertiseScope = resolveAdvertiseScope();
13737
14081
  const brokerHost = resolveBrokerHost(advertiseScope);
13738
14082
  const brokerPort = Number.parseInt(process.env.OPENSCOUT_BROKER_PORT ?? String(DEFAULT_BROKER_PORT), 10);
13739
14083
  const brokerUrl = process.env.OPENSCOUT_BROKER_URL ?? buildDefaultBrokerUrl(brokerHost, brokerPort);
13740
- const launchAgentPath = join14(homedir13(), "Library", "LaunchAgents", `${label}.plist`);
14084
+ const launchAgentPath = join15(homedir14(), "Library", "LaunchAgents", `${label}.plist`);
13741
14085
  return {
13742
14086
  label,
13743
14087
  mode,
@@ -13747,11 +14091,11 @@ function resolveBrokerServiceConfig() {
13747
14091
  launchAgentPath,
13748
14092
  supportDirectory,
13749
14093
  logsDirectory,
13750
- stdoutLogPath: join14(logsDirectory, "stdout.log"),
13751
- stderrLogPath: join14(logsDirectory, "stderr.log"),
14094
+ stdoutLogPath: join15(logsDirectory, "stdout.log"),
14095
+ stderrLogPath: join15(logsDirectory, "stderr.log"),
13752
14096
  controlHome,
13753
14097
  runtimePackageDir: runtimePackageDir(),
13754
- bunExecutable: resolveBunExecutable2(),
14098
+ bunExecutable: resolveBunExecutable3(),
13755
14099
  brokerHost,
13756
14100
  brokerPort,
13757
14101
  brokerUrl,
@@ -13768,7 +14112,7 @@ function renderLaunchAgentPlist(config) {
13768
14112
  OPENSCOUT_BROKER_SERVICE_MODE: config.mode,
13769
14113
  OPENSCOUT_BROKER_SERVICE_LABEL: config.label,
13770
14114
  OPENSCOUT_ADVERTISE_SCOPE: config.advertiseScope,
13771
- HOME: homedir13(),
14115
+ HOME: homedir14(),
13772
14116
  PATH: launchPath,
13773
14117
  ...collectOptionalEnvVars([
13774
14118
  "OPENSCOUT_MESH_ID",
@@ -13794,7 +14138,7 @@ function renderLaunchAgentPlist(config) {
13794
14138
  <key>ProgramArguments</key>
13795
14139
  <array>
13796
14140
  <string>${xmlEscape(config.bunExecutable)}</string>
13797
- <string>${xmlEscape(join14(config.runtimePackageDir, "bin", "openscout-runtime.mjs"))}</string>
14141
+ <string>${xmlEscape(join15(config.runtimePackageDir, "bin", "openscout-runtime.mjs"))}</string>
13798
14142
  <string>broker</string>
13799
14143
  </array>
13800
14144
  <key>WorkingDirectory</key>
@@ -13829,7 +14173,7 @@ function collectOptionalEnvVars(keys) {
13829
14173
  }
13830
14174
  function resolveLaunchAgentPATH() {
13831
14175
  const entries = [
13832
- join14(homedir13(), ".bun", "bin"),
14176
+ join15(homedir14(), ".bun", "bin"),
13833
14177
  ...(process.env.PATH ?? "").split(":").filter(Boolean),
13834
14178
  "/opt/homebrew/bin",
13835
14179
  "/usr/local/bin",
@@ -13844,7 +14188,7 @@ function xmlEscape(value) {
13844
14188
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
13845
14189
  }
13846
14190
  function ensureParentDirectory(filePath) {
13847
- mkdirSync8(dirname5(filePath), { recursive: true });
14191
+ mkdirSync8(dirname6(filePath), { recursive: true });
13848
14192
  }
13849
14193
  function ensureServiceDirectories(config) {
13850
14194
  ensureOpenScoutCleanSlateSync();
@@ -13877,7 +14221,7 @@ function launchctlPath() {
13877
14221
  return "/bin/launchctl";
13878
14222
  }
13879
14223
  function readLogLines(path2) {
13880
- if (!existsSync12(path2)) {
14224
+ if (!existsSync13(path2)) {
13881
14225
  return [];
13882
14226
  }
13883
14227
  return readFileSync7(path2, "utf8").split(`
@@ -13977,7 +14321,7 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
13977
14321
  ensureServiceDirectories(config);
13978
14322
  const launchctl = inspectLaunchctl(config);
13979
14323
  const health = await fetchHealthSnapshot(config);
13980
- const installed = existsSync12(config.launchAgentPath);
14324
+ const installed = existsSync13(config.launchAgentPath);
13981
14325
  const lastLogLine = health.reachable ? readLastLogLine([config.stdoutLogPath, config.stderrLogPath]) : readLastLogLine([config.stderrLogPath, config.stdoutLogPath]);
13982
14326
  return {
13983
14327
  label: config.label,
@@ -14020,7 +14364,7 @@ async function startBrokerService(config = resolveBrokerServiceConfig()) {
14020
14364
  throw new Error(status.lastLogLine ?? status.health.error ?? "Broker service did not become healthy.");
14021
14365
  }
14022
14366
  function sleep(ms) {
14023
- return new Promise((resolve5) => setTimeout(resolve5, ms));
14367
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
14024
14368
  }
14025
14369
  async function stopBrokerService(config = resolveBrokerServiceConfig()) {
14026
14370
  runCommand(launchctlPath(), ["bootout", config.serviceTarget], { allowFailure: true });
@@ -14039,7 +14383,7 @@ async function restartBrokerService(config = resolveBrokerServiceConfig()) {
14039
14383
  }
14040
14384
  async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
14041
14385
  await stopBrokerService(config);
14042
- if (existsSync12(config.launchAgentPath)) {
14386
+ if (existsSync13(config.launchAgentPath)) {
14043
14387
  rmSync2(config.launchAgentPath, { force: true });
14044
14388
  }
14045
14389
  return brokerServiceStatus(config);
@@ -14109,8 +14453,8 @@ var LOCAL_AGENT_SYSTEM_PROMPT_TEMPLATE_HINT = [
14109
14453
  ].join("");
14110
14454
 
14111
14455
  // ../runtime/src/local-agents.ts
14112
- var MODULE_DIRECTORY = dirname6(fileURLToPath5(import.meta.url));
14113
- var OPENSCOUT_REPO_ROOT = resolve5(MODULE_DIRECTORY, "..", "..", "..");
14456
+ var MODULE_DIRECTORY = dirname7(fileURLToPath5(import.meta.url));
14457
+ var OPENSCOUT_REPO_ROOT = resolve6(MODULE_DIRECTORY, "..", "..", "..");
14114
14458
  var DEFAULT_LOCAL_AGENT_CAPABILITIES = ["chat", "invoke", "deliver"];
14115
14459
  var DEFAULT_LOCAL_AGENT_HARNESS = "claude";
14116
14460
  function resolveRelayHub() {
@@ -14118,18 +14462,18 @@ function resolveRelayHub() {
14118
14462
  }
14119
14463
  function resolveProjectsRoot(projectPath) {
14120
14464
  if (process.env.OPENSCOUT_PROJECTS_ROOT?.trim()) {
14121
- return resolve5(process.env.OPENSCOUT_PROJECTS_ROOT.trim());
14465
+ return resolve6(process.env.OPENSCOUT_PROJECTS_ROOT.trim());
14122
14466
  }
14123
14467
  try {
14124
14468
  const supportPaths = resolveOpenScoutSupportPaths();
14125
- if (!existsSync13(supportPaths.settingsPath)) {
14126
- return dirname6(projectPath);
14469
+ if (!existsSync14(supportPaths.settingsPath)) {
14470
+ return dirname7(projectPath);
14127
14471
  }
14128
14472
  const raw = JSON.parse(readFileSync8(supportPaths.settingsPath, "utf8"));
14129
14473
  const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
14130
- return workspaceRoot ? resolve5(workspaceRoot) : dirname6(projectPath);
14474
+ return workspaceRoot ? resolve6(workspaceRoot) : dirname7(projectPath);
14131
14475
  } catch {
14132
- return dirname6(projectPath);
14476
+ return dirname7(projectPath);
14133
14477
  }
14134
14478
  }
14135
14479
  function resolveBrokerUrl() {
@@ -14139,7 +14483,7 @@ function nowSeconds2() {
14139
14483
  return Math.floor(Date.now() / 1000);
14140
14484
  }
14141
14485
  function scoutCliPath() {
14142
- return join15(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
14486
+ return join16(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
14143
14487
  }
14144
14488
  function legacyNodeBrokerRelayCommand() {
14145
14489
  return `node ${JSON.stringify(scoutCliPath())}`;
@@ -14152,13 +14496,13 @@ function titleCaseLocalAgentName(value) {
14152
14496
  }
14153
14497
  function resolveScoutSkillPath() {
14154
14498
  const candidatePaths = [
14155
- join15(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
14156
- join15(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
14157
- join15(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
14158
- join15(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
14499
+ join16(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
14500
+ join16(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
14501
+ join16(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
14502
+ join16(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
14159
14503
  ];
14160
14504
  for (const path2 of candidatePaths) {
14161
- if (existsSync13(path2)) {
14505
+ if (existsSync14(path2)) {
14162
14506
  return path2;
14163
14507
  }
14164
14508
  }
@@ -14209,7 +14553,7 @@ function buildLocalAgentCollaborationPrompt(context) {
14209
14553
  function buildLocalAgentTmuxProtocolPrompt(context) {
14210
14554
  return [
14211
14555
  "Relay protocol:",
14212
- ` - Read recent context with: ${context.relayCommand} read --as ${context.agentId}`,
14556
+ ` - Read recent context when needed with: ${context.relayCommand} latest --agent ${context.agentId} --limit 20`,
14213
14557
  ` - Tell one agent with: ${context.relayCommand} send --as ${context.agentId} "@<agent> your message"`,
14214
14558
  ` - Ask one agent and stay attached with: ${context.relayCommand} ask --to <agent> --as ${context.agentId} "your request"`,
14215
14559
  "",
@@ -14221,9 +14565,10 @@ function buildLocalAgentTmuxProtocolPrompt(context) {
14221
14565
  " - If you need multiple agents, use separate DMs or an explicit channel; do not guess a venue from multiple mentions",
14222
14566
  " - Do not use channel.shared for ordinary delegation or follow-up; reserve it for explicit group updates or broadcasts",
14223
14567
  " - If a short @handle may be ambiguous, resolve the exact target before sending; do not guess and do not fall back to shared",
14568
+ " - 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",
14224
14569
  " - Use send for tells and status; use ask when the meaning is 'do this and get back to me'",
14225
14570
  " - When replying to an [ask:<id>] request, include the same [ask:<id>] tag in your reply",
14226
- " - Use the broker-backed relay read command above to inspect recent context before responding",
14571
+ " - Use the broker-backed latest command above only when recent context is needed before responding",
14227
14572
  ` - Follow the scout skill at ${context.scoutSkill} for agent-to-agent communication`
14228
14573
  ].join(`
14229
14574
  `);
@@ -14234,7 +14579,7 @@ function buildLocalAgentDirectProtocolPrompt(context) {
14234
14579
  " - You are invoked directly by the OpenScout broker",
14235
14580
  " - Return your final answer in the assistant message for the current turn",
14236
14581
  " - Do not shell out to send the final answer through relay yourself",
14237
- ` - If you need recent relay context, inspect it with: ${context.relayCommand} read --as ${context.agentId}`,
14582
+ ` - If you need recent broker context, inspect it with: ${context.relayCommand} latest --agent ${context.agentId} --limit 20`,
14238
14583
  ` - If you need to tell one agent something, use: ${context.relayCommand} send --as ${context.agentId} "@<agent> your message"`,
14239
14584
  ` - If you need another agent to do work, use: ${context.relayCommand} ask --to <agent> --as ${context.agentId} "your request"`,
14240
14585
  " - Default Scout loop: resolve identity, resolve one target, choose DM vs explicit channel, keep follow-up in that same venue",
@@ -14242,6 +14587,7 @@ function buildLocalAgentDirectProtocolPrompt(context) {
14242
14587
  " - If you need multiple agents, use separate DMs or an explicit channel; do not guess a venue from multiple mentions",
14243
14588
  " - Do not use channel.shared for ordinary delegation or follow-up; reserve it for explicit group updates or broadcasts",
14244
14589
  " - If a short @handle may be ambiguous, resolve the exact target before sending; do not guess and do not fall back to shared",
14590
+ " - 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",
14245
14591
  " - Use send for tells and status; use ask when the meaning is 'do this and get back to me'",
14246
14592
  ` - Follow the scout skill at ${context.scoutSkill} for agent-to-agent communication`
14247
14593
  ].join(`
@@ -14304,7 +14650,7 @@ function buildLegacySimpleRelayPrompt(agentId, projectName, projectPath, relayCo
14304
14650
  "A primary agent may call into you for context, execution, follow-through, and handoff.",
14305
14651
  "",
14306
14652
  `You have full access to the codebase at ${projectPath}.`,
14307
- `There is a structured relay event stream at ${join15(relayHub, "channel.jsonl")} shared by all agents.`,
14653
+ `There is a structured relay event stream at ${join16(relayHub, "channel.jsonl")} shared by all agents.`,
14308
14654
  "",
14309
14655
  "Your job:",
14310
14656
  ` - Respond to @${agentId} mentions from other agents`,
@@ -14408,17 +14754,17 @@ async function writeLocalAgentRegistry(registry) {
14408
14754
  }
14409
14755
  await writeRelayAgentOverrides(nextOverrides);
14410
14756
  }
14411
- function expandHomePath3(value) {
14757
+ function expandHomePath4(value) {
14412
14758
  if (value === "~") {
14413
14759
  return process.env.HOME ?? process.cwd();
14414
14760
  }
14415
14761
  if (value.startsWith("~/")) {
14416
- return join15(process.env.HOME ?? process.cwd(), value.slice(2));
14762
+ return join16(process.env.HOME ?? process.cwd(), value.slice(2));
14417
14763
  }
14418
14764
  return value;
14419
14765
  }
14420
14766
  function normalizeProjectPath(value) {
14421
- return resolve5(expandHomePath3(value.trim() || "."));
14767
+ return resolve6(expandHomePath4(value.trim() || "."));
14422
14768
  }
14423
14769
  function normalizeTmuxSessionName2(value, agentId) {
14424
14770
  const fallback = `relay-${agentId}`;
@@ -14681,7 +15027,7 @@ function recordForHarness(record, harnessOverride) {
14681
15027
  function normalizeLocalAgentRecord(agentId, record) {
14682
15028
  const cwd = normalizeProjectPath(record.cwd || process.cwd());
14683
15029
  const projectRoot = normalizeProjectPath(record.projectRoot || cwd);
14684
- const project = record.project?.trim() || basename4(projectRoot);
15030
+ const project = record.project?.trim() || basename5(projectRoot);
14685
15031
  const definitionId = record.definitionId?.trim() || agentId;
14686
15032
  const defaultHarness = activeLocalHarness(record);
14687
15033
  const harnessProfiles = normalizeLocalHarnessProfiles(agentId, {
@@ -14746,7 +15092,7 @@ function localAgentRecordFromResolvedConfig(config) {
14746
15092
  function localAgentRecordFromRelayAgentOverride(agentId, override) {
14747
15093
  return normalizeLocalAgentRecord(agentId, {
14748
15094
  definitionId: override.definitionId ?? agentId,
14749
- project: override.projectName ?? basename4(override.projectRoot || override.runtime?.cwd || agentId),
15095
+ project: override.projectName ?? basename5(override.projectRoot || override.runtime?.cwd || agentId),
14750
15096
  projectRoot: override.projectRoot ?? override.runtime?.cwd,
14751
15097
  tmuxSession: override.runtime?.sessionId ?? `relay-${agentId}`,
14752
15098
  cwd: override.runtime?.cwd ?? override.projectRoot,
@@ -14842,7 +15188,7 @@ function buildLocalAgentBootstrapPrompt(_harness, _systemPrompt, initialMessage)
14842
15188
  function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile, workerScript) {
14843
15189
  const extraArgs = shellQuoteArguments(normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs));
14844
15190
  if (normalizeLocalAgentHarness(record.harness) === "codex") {
14845
- return `exec bash ${JSON.stringify(workerScript ?? join15(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
15191
+ return `exec bash ${JSON.stringify(workerScript ?? join16(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
14846
15192
  }
14847
15193
  return [
14848
15194
  "claude",
@@ -14888,7 +15234,7 @@ async function ensureLocalAgentOnline(agentName, record) {
14888
15234
  return normalizedRecord;
14889
15235
  }
14890
15236
  const projectPath = normalizedRecord.cwd;
14891
- const projectName = normalizedRecord.project || basename4(projectPath);
15237
+ const projectName = normalizedRecord.project || basename5(projectPath);
14892
15238
  const systemPromptTemplate = normalizedRecord.systemPrompt || buildLocalAgentSystemPromptTemplate();
14893
15239
  const systemPrompt = renderLocalAgentSystemPromptTemplate(systemPromptTemplate, buildLocalAgentTemplateContext(agentName, projectName, projectPath), { transport: normalizedRecord.transport });
14894
15240
  const agentRuntimeDir = relayAgentRuntimeDirectory(agentName);
@@ -14896,7 +15242,7 @@ async function ensureLocalAgentOnline(agentName, record) {
14896
15242
  await mkdir6(agentRuntimeDir, { recursive: true });
14897
15243
  await mkdir6(logsDir, { recursive: true });
14898
15244
  if (normalizedRecord.transport === "codex_app_server") {
14899
- await writeFile6(join15(agentRuntimeDir, "prompt.txt"), systemPrompt);
15245
+ await writeFile6(join16(agentRuntimeDir, "prompt.txt"), systemPrompt);
14900
15246
  await ensureCodexAppServerAgentOnline(buildCodexAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
14901
15247
  const registry2 = await readLocalAgentRegistry();
14902
15248
  registry2[agentName] = {
@@ -14908,7 +15254,7 @@ async function ensureLocalAgentOnline(agentName, record) {
14908
15254
  return registry2[agentName];
14909
15255
  }
14910
15256
  if (normalizedRecord.transport === "claude_stream_json") {
14911
- await writeFile6(join15(agentRuntimeDir, "prompt.txt"), systemPrompt);
15257
+ await writeFile6(join16(agentRuntimeDir, "prompt.txt"), systemPrompt);
14912
15258
  await ensureClaudeStreamJsonAgentOnline(buildClaudeAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
14913
15259
  const registry2 = await readLocalAgentRegistry();
14914
15260
  registry2[agentName] = {
@@ -14921,17 +15267,17 @@ async function ensureLocalAgentOnline(agentName, record) {
14921
15267
  }
14922
15268
  const initialMessage = buildLocalAgentInitialMessage(projectName, agentName);
14923
15269
  const bootstrapPrompt = buildLocalAgentBootstrapPrompt(normalizeLocalAgentHarness(normalizedRecord.harness), systemPrompt, initialMessage);
14924
- const queueDirectory = join15(agentRuntimeDir, "queue");
14925
- const processingDirectory = join15(queueDirectory, "processing");
14926
- const processedDirectory = join15(queueDirectory, "processed");
14927
- const promptFile = join15(agentRuntimeDir, "prompt.txt");
14928
- const initialFile = join15(agentRuntimeDir, "initial.txt");
14929
- const launchScript = join15(agentRuntimeDir, "launch.sh");
14930
- const workerScript = join15(agentRuntimeDir, "codex-worker.sh");
14931
- const codexSessionIdFile = join15(agentRuntimeDir, "codex-session-id.txt");
14932
- const stateFile = join15(agentRuntimeDir, "state.json");
14933
- const stdoutLogFile = join15(logsDir, "stdout.log");
14934
- const stderrLogFile = join15(logsDir, "stderr.log");
15270
+ const queueDirectory = join16(agentRuntimeDir, "queue");
15271
+ const processingDirectory = join16(queueDirectory, "processing");
15272
+ const processedDirectory = join16(queueDirectory, "processed");
15273
+ const promptFile = join16(agentRuntimeDir, "prompt.txt");
15274
+ const initialFile = join16(agentRuntimeDir, "initial.txt");
15275
+ const launchScript = join16(agentRuntimeDir, "launch.sh");
15276
+ const workerScript = join16(agentRuntimeDir, "codex-worker.sh");
15277
+ const codexSessionIdFile = join16(agentRuntimeDir, "codex-session-id.txt");
15278
+ const stateFile = join16(agentRuntimeDir, "state.json");
15279
+ const stdoutLogFile = join16(logsDir, "stdout.log");
15280
+ const stderrLogFile = join16(logsDir, "stderr.log");
14935
15281
  await writeFile6(promptFile, systemPrompt);
14936
15282
  await writeFile6(initialFile, bootstrapPrompt);
14937
15283
  await writeFile6(stderrLogFile, "");
@@ -14993,10 +15339,10 @@ async function ensureLocalAgentOnline(agentName, record) {
14993
15339
  if (isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
14994
15340
  break;
14995
15341
  }
14996
- await new Promise((resolve6) => setTimeout(resolve6, 100));
15342
+ await new Promise((resolve7) => setTimeout(resolve7, 100));
14997
15343
  }
14998
15344
  if (!isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
14999
- const stderrTail = existsSync13(stderrLogFile) ? readFileSync8(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
15345
+ const stderrTail = existsSync14(stderrLogFile) ? readFileSync8(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
15000
15346
  `).trim() : "";
15001
15347
  throw new Error(stderrTail ? `Relay agent ${agentName} failed to stay online:
15002
15348
  ${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
@@ -15051,6 +15397,7 @@ async function startLocalAgent(input) {
15051
15397
  const preferredHarness = input.harness ? normalizeLocalAgentHarness(input.harness) : undefined;
15052
15398
  const currentDirectory = input.currentDirectory ?? projectPath;
15053
15399
  const effectiveCwd = input.cwdOverride ? normalizeProjectPath(input.cwdOverride) : undefined;
15400
+ const shouldEnsureOnline = input.ensureOnline !== false;
15054
15401
  const requestedDefinitionId = input.agentName?.trim() ? normalizeAgentSelectorSegment(input.agentName.trim()) : "";
15055
15402
  if (input.agentName?.trim() && !requestedDefinitionId) {
15056
15403
  throw new Error(`Invalid agent name "${input.agentName}".`);
@@ -15098,7 +15445,7 @@ async function startLocalAgent(input) {
15098
15445
  }
15099
15446
  if (!matchingOverride) {
15100
15447
  const configDefinitionId = coldProjectConfig?.agent?.id?.trim() ? normalizeAgentSelectorSegment(coldProjectConfig.agent.id.trim()) : "";
15101
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
15448
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
15102
15449
  const configDisplayName = coldProjectConfig?.agent?.displayName?.trim() || "";
15103
15450
  const effectiveDisplayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
15104
15451
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
@@ -15115,7 +15462,7 @@ async function startLocalAgent(input) {
15115
15462
  agentId: instance.id,
15116
15463
  definitionId,
15117
15464
  displayName: effectiveDisplayName,
15118
- projectName: basename4(projectRoot),
15465
+ projectName: basename5(projectRoot),
15119
15466
  projectRoot,
15120
15467
  projectConfigPath: coldProjectConfigPath,
15121
15468
  source: "manual",
@@ -15155,7 +15502,7 @@ async function startLocalAgent(input) {
15155
15502
  agentId: instance.id,
15156
15503
  definitionId: requestedDefinitionId,
15157
15504
  displayName: input.displayName || titleCaseLocalAgentName(requestedDefinitionId),
15158
- projectName: matchingOverride.projectName ?? basename4(matchingProjectRoot),
15505
+ projectName: matchingOverride.projectName ?? basename5(matchingProjectRoot),
15159
15506
  projectRoot: matchingProjectRoot,
15160
15507
  projectConfigPath: matchingOverride.projectConfigPath ?? null,
15161
15508
  source: "manual",
@@ -15200,12 +15547,14 @@ async function startLocalAgent(input) {
15200
15547
  }
15201
15548
  targetAgentId = matchingAgentId;
15202
15549
  }
15203
- await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
15204
- includeDiscovered: false,
15205
- currentDirectory,
15206
- ensureCurrentProjectConfig: false,
15207
- harness: preferredHarness
15208
- });
15550
+ if (shouldEnsureOnline) {
15551
+ await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
15552
+ includeDiscovered: false,
15553
+ currentDirectory,
15554
+ ensureCurrentProjectConfig: false,
15555
+ harness: preferredHarness
15556
+ });
15557
+ }
15209
15558
  const finalOverrides = await readRelayAgentOverrides();
15210
15559
  const finalOverride = finalOverrides[targetAgentId];
15211
15560
  if (!finalOverride) {
@@ -15271,8 +15620,8 @@ async function interruptLocalAgent(agentId) {
15271
15620
  function readPersistedClaudeSessionId(agentName) {
15272
15621
  try {
15273
15622
  const runtimeDir = relayAgentRuntimeDirectory(agentName);
15274
- const catalogPath = join15(runtimeDir, "session-catalog.json");
15275
- if (existsSync13(catalogPath)) {
15623
+ const catalogPath = join16(runtimeDir, "session-catalog.json");
15624
+ if (existsSync14(catalogPath)) {
15276
15625
  const raw = readFileSync8(catalogPath, "utf8").trim();
15277
15626
  if (raw) {
15278
15627
  const catalog = JSON.parse(raw);
@@ -15281,8 +15630,8 @@ function readPersistedClaudeSessionId(agentName) {
15281
15630
  }
15282
15631
  }
15283
15632
  }
15284
- const legacyPath = join15(runtimeDir, "claude-session-id.txt");
15285
- if (existsSync13(legacyPath)) {
15633
+ const legacyPath = join16(runtimeDir, "claude-session-id.txt");
15634
+ if (existsSync14(legacyPath)) {
15286
15635
  const value = readFileSync8(legacyPath, "utf8").trim();
15287
15636
  return value || null;
15288
15637
  }
@@ -15476,6 +15825,7 @@ var scoutBrokerPaths = {
15476
15825
  endpoints: "/v1/endpoints",
15477
15826
  conversations: "/v1/conversations",
15478
15827
  invocations: "/v1/invocations",
15828
+ deliver: "/v1/deliver",
15479
15829
  activity: "/v1/activity",
15480
15830
  collaborationRecords: "/v1/collaboration/records",
15481
15831
  collaborationEvents: "/v1/collaboration/events"
@@ -15505,29 +15855,6 @@ function titleCaseName(value) {
15505
15855
  function displayNameForBrokerActor(snapshot, actorId) {
15506
15856
  return snapshot.agents[actorId]?.displayName ?? snapshot.actors[actorId]?.displayName ?? titleCaseName(metadataString2(snapshot.agents[actorId]?.metadata, "definitionId") || actorId);
15507
15857
  }
15508
- function firstEndpointForActor(snapshot, actorId) {
15509
- return Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === actorId).sort((lhs, rhs) => lhs.id.localeCompare(rhs.id))[0];
15510
- }
15511
- function buildScoutReturnAddress2(snapshot, actorId, options = {}) {
15512
- const agent = snapshot.agents[actorId];
15513
- const actor = snapshot.actors[actorId];
15514
- const endpoint = firstEndpointForActor(snapshot, actorId);
15515
- const selector = agent?.selector?.trim() || metadataString2(agent?.metadata, "selector") || metadataString2(actor?.metadata, "selector");
15516
- const defaultSelector = agent?.defaultSelector?.trim() || metadataString2(agent?.metadata, "defaultSelector") || metadataString2(actor?.metadata, "defaultSelector");
15517
- const projectRoot = endpoint?.projectRoot ?? endpoint?.cwd ?? metadataString2(agent?.metadata, "projectRoot") ?? metadataString2(actor?.metadata, "projectRoot");
15518
- return buildScoutReturnAddress({
15519
- actorId,
15520
- handle: agent?.handle?.trim() || actor?.handle?.trim() || actorId,
15521
- displayName: agent?.displayName || actor?.displayName,
15522
- selector,
15523
- defaultSelector,
15524
- conversationId: options.conversationId,
15525
- replyToMessageId: options.replyToMessageId,
15526
- nodeId: endpoint?.nodeId || agent?.authorityNodeId || agent?.homeNodeId,
15527
- projectRoot,
15528
- sessionId: endpoint?.sessionId
15529
- });
15530
- }
15531
15858
  function metadataString2(metadata, key) {
15532
15859
  const value = metadata?.[key];
15533
15860
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
@@ -15830,74 +16157,42 @@ async function openScoutPeerSession(input) {
15830
16157
  };
15831
16158
  }
15832
16159
  async function sendScoutDirectMessage(input) {
15833
- const currentDirectory = input.currentDirectory ?? process.cwd();
15834
- const directSession = await openScoutPeerSession({
15835
- sourceId: OPERATOR_ID,
15836
- targetId: input.agentId,
15837
- currentDirectory
15838
- });
15839
16160
  const broker = await requireScoutBrokerContext();
15840
16161
  const createdAt = Date.now();
15841
- const messageId = `msg-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
15842
16162
  const source = input.source?.trim() || "scout-mobile";
15843
- const targetAgentId = directSession.targetId;
15844
- const targetAgent = broker.snapshot.agents[targetAgentId] ?? ("agent" in directSession ? directSession.agent : undefined);
15845
- const targetLabel = `@${targetAgent?.handle?.trim() || targetAgent?.displayName?.trim() || targetAgentId}`;
15846
- const returnAddress = buildScoutReturnAddress2(broker.snapshot, OPERATOR_ID, {
15847
- conversationId: directSession.conversation.id,
15848
- replyToMessageId: messageId
15849
- });
15850
- await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.messages, {
15851
- id: messageId,
15852
- conversationId: directSession.conversation.id,
15853
- replyToMessageId: input.replyToMessageId ?? undefined,
15854
- actorId: OPERATOR_ID,
15855
- originNodeId: broker.node.id,
15856
- class: "agent",
16163
+ const delivery = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.deliver, {
16164
+ id: `deliver-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
16165
+ requesterId: OPERATOR_ID,
16166
+ requesterNodeId: broker.node.id,
16167
+ targetAgentId: input.agentId,
15857
16168
  body: input.body.trim(),
15858
- mentions: [{ actorId: targetAgentId, label: targetLabel }],
15859
- audience: {
15860
- notify: [targetAgentId],
15861
- reason: "direct_message"
15862
- },
15863
- visibility: "private",
15864
- policy: "durable",
16169
+ intent: "consult",
16170
+ replyToMessageId: input.replyToMessageId ?? undefined,
16171
+ execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
16172
+ ensureAwake: true,
15865
16173
  createdAt,
15866
- metadata: {
16174
+ messageMetadata: {
15867
16175
  source,
15868
16176
  destinationKind: "direct",
15869
- destinationId: targetAgentId,
16177
+ destinationId: input.agentId,
15870
16178
  referenceMessageIds: input.referenceMessageIds ?? [],
15871
16179
  clientMessageId: input.clientMessageId ?? null,
15872
- returnAddress,
15873
16180
  ...input.deviceId ? { deviceId: input.deviceId } : {}
15874
- }
15875
- });
15876
- const invocationResponse = await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.invocations, {
15877
- id: `inv-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
15878
- requesterId: OPERATOR_ID,
15879
- requesterNodeId: broker.node.id,
15880
- targetAgentId,
15881
- action: "consult",
15882
- task: input.body.trim(),
15883
- conversationId: directSession.conversation.id,
15884
- messageId,
15885
- execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
15886
- ensureAwake: true,
15887
- stream: false,
15888
- createdAt,
15889
- metadata: {
16181
+ },
16182
+ invocationMetadata: {
15890
16183
  source,
15891
16184
  destinationKind: "direct",
15892
- destinationId: targetAgentId,
15893
- returnAddress,
16185
+ destinationId: input.agentId,
15894
16186
  ...input.deviceId ? { deviceId: input.deviceId } : {}
15895
16187
  }
15896
16188
  });
16189
+ if (delivery.kind !== "delivery") {
16190
+ throw new Error(delivery.kind === "question" ? delivery.question.detail : delivery.rejection.detail);
16191
+ }
15897
16192
  return {
15898
- conversationId: directSession.conversation.id,
15899
- messageId,
15900
- flight: invocationResponse.flight
16193
+ conversationId: delivery.conversation.id,
16194
+ messageId: delivery.message.id,
16195
+ flight: delivery.flight
15901
16196
  };
15902
16197
  }
15903
16198
  async function loadScoutActivityItems(options = {}) {
@@ -15924,11 +16219,11 @@ async function upScoutAgent(input) {
15924
16219
 
15925
16220
  // ../../apps/desktop/src/server/db-queries.ts
15926
16221
  import { Database } from "bun:sqlite";
15927
- import { homedir as homedir14 } from "os";
15928
- import { join as join16 } from "path";
16222
+ import { homedir as homedir15 } from "os";
16223
+ import { join as join17 } from "path";
15929
16224
  function resolveDbPath() {
15930
- const controlHome = process.env.OPENSCOUT_CONTROL_HOME ?? join16(homedir14(), ".openscout", "control-plane");
15931
- return join16(controlHome, "control-plane.sqlite");
16225
+ const controlHome = process.env.OPENSCOUT_CONTROL_HOME ?? join17(homedir15(), ".openscout", "control-plane");
16226
+ return join17(controlHome, "control-plane.sqlite");
15932
16227
  }
15933
16228
  var _db = null;
15934
16229
  function db() {
@@ -15939,7 +16234,7 @@ function db() {
15939
16234
  }
15940
16235
  return _db;
15941
16236
  }
15942
- var HOME = homedir14();
16237
+ var HOME = homedir15();
15943
16238
  function compact(p) {
15944
16239
  if (!p)
15945
16240
  return null;
@@ -16595,8 +16890,8 @@ async function createScoutSession(input, currentDirectory, deviceId) {
16595
16890
  if (!rawWorkspaceId) {
16596
16891
  throw new Error(`Invalid workspaceId.`);
16597
16892
  }
16598
- const workspaceRoot = resolve6(rawWorkspaceId);
16599
- const projectName = basename5(workspaceRoot) || workspaceRoot;
16893
+ const workspaceRoot = resolve7(rawWorkspaceId);
16894
+ const projectName = basename6(workspaceRoot) || workspaceRoot;
16600
16895
  const workspace = {
16601
16896
  id: workspaceRoot,
16602
16897
  title: projectName,
@@ -16705,17 +17000,17 @@ async function deriveNewAgentName(projectName, branch, harness) {
16705
17000
  }
16706
17001
  async function createGitWorktree(projectRoot, agentName) {
16707
17002
  const { execSync: execSync3 } = await import("child_process");
16708
- const { join: join17 } = await import("path");
16709
- const { mkdirSync: mkdirSync9, existsSync: existsSync14 } = await import("fs");
17003
+ const { join: join18 } = await import("path");
17004
+ const { mkdirSync: mkdirSync9, existsSync: existsSync15 } = await import("fs");
16710
17005
  try {
16711
17006
  execSync3("git rev-parse --git-dir", { cwd: projectRoot, stdio: "pipe" });
16712
17007
  } catch {
16713
17008
  return null;
16714
17009
  }
16715
17010
  const branchName = `scout/${agentName}`;
16716
- const worktreeDir = join17(projectRoot, ".scout-worktrees");
16717
- const worktreePath = join17(worktreeDir, agentName);
16718
- if (existsSync14(worktreePath)) {
17011
+ const worktreeDir = join18(projectRoot, ".scout-worktrees");
17012
+ const worktreePath = join18(worktreeDir, agentName);
17013
+ if (existsSync15(worktreePath)) {
16719
17014
  return { path: worktreePath, branch: branchName };
16720
17015
  }
16721
17016
  mkdirSync9(worktreeDir, { recursive: true });
@@ -16919,9 +17214,9 @@ async function handleRPCInner(bridge, req, deviceId) {
16919
17214
  }
16920
17215
  case "session/resume": {
16921
17216
  const p = req.params;
16922
- const sessionFilename = basename6(p.sessionPath, ".jsonl");
17217
+ const sessionFilename = basename7(p.sessionPath, ".jsonl");
16923
17218
  const parentDir = p.sessionPath.substring(0, p.sessionPath.lastIndexOf("/"));
16924
- const dirName = basename6(parentDir);
17219
+ const dirName = basename7(parentDir);
16925
17220
  let cwd;
16926
17221
  if (dirName.startsWith("-")) {
16927
17222
  const candidate = "/" + dirName.slice(1).replace(/-/g, "/");
@@ -16988,7 +17283,7 @@ async function handleRPCInner(bridge, req, deviceId) {
16988
17283
  return { id: req.id, error: { code: -32000, message: "Workspace target is not a directory" } };
16989
17284
  }
16990
17285
  const adapterType = p.adapter ?? "claude-code";
16991
- const name = p.name ?? basename6(projectPath);
17286
+ const name = p.name ?? basename7(projectPath);
16992
17287
  const session = await bridge.createSession(adapterType, {
16993
17288
  name,
16994
17289
  cwd: projectPath
@@ -17228,13 +17523,13 @@ function resolveMobileCurrentDirectory() {
17228
17523
  }
17229
17524
  }
17230
17525
  function resolveWorkspaceRoot(root) {
17231
- const expandedRoot = root.replace(/^~/, homedir15());
17526
+ const expandedRoot = root.replace(/^~/, homedir16());
17232
17527
  return realpathSync(expandedRoot);
17233
17528
  }
17234
17529
  function resolveWorkspacePath(root, requestedPath) {
17235
17530
  const normalizedRoot = resolveWorkspaceRoot(root);
17236
- const expandedPath = requestedPath?.replace(/^~/, homedir15());
17237
- const candidate = expandedPath ? isAbsolute3(expandedPath) ? expandedPath : join17(normalizedRoot, expandedPath) : normalizedRoot;
17531
+ const expandedPath = requestedPath?.replace(/^~/, homedir16());
17532
+ const candidate = expandedPath ? isAbsolute3(expandedPath) ? expandedPath : join18(normalizedRoot, expandedPath) : normalizedRoot;
17238
17533
  const resolvedCandidate = realpathSync(candidate);
17239
17534
  const rel = relative2(normalizedRoot, resolvedCandidate);
17240
17535
  if (rel === "" || !rel.startsWith("..") && !isAbsolute3(rel)) {
@@ -17264,7 +17559,7 @@ function listDirectories(dirPath) {
17264
17559
  continue;
17265
17560
  if (name === "node_modules" || name === ".build" || name === "target")
17266
17561
  continue;
17267
- const fullPath = join17(dirPath, name);
17562
+ const fullPath = join18(dirPath, name);
17268
17563
  try {
17269
17564
  const stat4 = statSync3(fullPath);
17270
17565
  if (!stat4.isDirectory())
@@ -17287,7 +17582,7 @@ function listDirectories(dirPath) {
17287
17582
  return entries.sort((a, b) => a.name.localeCompare(b.name));
17288
17583
  }
17289
17584
  async function discoverSessionFiles(maxAgeDays, limit) {
17290
- const home = homedir15();
17585
+ const home = homedir16();
17291
17586
  const results = [];
17292
17587
  const searchPaths = [
17293
17588
  { pattern: `${home}/.claude/projects`, agent: "claude-code" },
@@ -31829,7 +32124,7 @@ import { Database as Database2 } from "bun:sqlite";
31829
32124
  import { mkdirSync as mkdirSync9, readFileSync as readFileSync10 } from "fs";
31830
32125
  import { connect as connectHttp2 } from "http2";
31831
32126
  import { createPrivateKey, sign as signWithKey } from "crypto";
31832
- import { dirname as dirname7, join as join18 } from "path";
32127
+ import { dirname as dirname8, join as join19 } from "path";
31833
32128
  var MOBILE_PUSH_SCHEMA = `
31834
32129
  CREATE TABLE IF NOT EXISTS mobile_push_registrations (
31835
32130
  id TEXT PRIMARY KEY,
@@ -31857,7 +32152,7 @@ var dbHandle = null;
31857
32152
  var dbPath = null;
31858
32153
  var cachedApnsJwt = null;
31859
32154
  function resolveControlPlaneDbPath() {
31860
- return join18(resolveOpenScoutSupportPaths().controlHome, "control-plane.sqlite");
32155
+ return join19(resolveOpenScoutSupportPaths().controlHome, "control-plane.sqlite");
31861
32156
  }
31862
32157
  function ensureMobilePushSchema(database) {
31863
32158
  database.exec("PRAGMA busy_timeout = 5000;");
@@ -31871,7 +32166,7 @@ function writeDb() {
31871
32166
  dbHandle = null;
31872
32167
  }
31873
32168
  if (!dbHandle) {
31874
- mkdirSync9(dirname7(nextPath), { recursive: true });
32169
+ mkdirSync9(dirname8(nextPath), { recursive: true });
31875
32170
  dbHandle = new Database2(nextPath, { create: true });
31876
32171
  dbPath = nextPath;
31877
32172
  ensureMobilePushSchema(dbHandle);
@@ -32087,7 +32382,7 @@ async function sendApnsAlertToRegistration(registration, alert) {
32087
32382
  },
32088
32383
  ...alert.payload ? { scout: alert.payload } : {}
32089
32384
  });
32090
- return new Promise((resolve7, reject) => {
32385
+ return new Promise((resolve8, reject) => {
32091
32386
  const client = connectHttp2(authority);
32092
32387
  client.once("error", reject);
32093
32388
  const request = client.request({
@@ -32125,7 +32420,7 @@ async function sendApnsAlertToRegistration(registration, alert) {
32125
32420
  }
32126
32421
  }
32127
32422
  if (status === 200) {
32128
- resolve7({
32423
+ resolve8({
32129
32424
  delivered: true,
32130
32425
  skipped: false,
32131
32426
  status,
@@ -32137,7 +32432,7 @@ async function sendApnsAlertToRegistration(registration, alert) {
32137
32432
  if (reason === "BadDeviceToken" || reason === "Unregistered") {
32138
32433
  deleteMobilePushRegistrationByToken(registration.pushToken);
32139
32434
  }
32140
- resolve7({
32435
+ resolve8({
32141
32436
  delivered: false,
32142
32437
  skipped: false,
32143
32438
  status,
@@ -32200,8 +32495,8 @@ async function broadcastApnsAlertToActiveMobileDevices(alert) {
32200
32495
  // ../../apps/desktop/src/core/pairing/runtime/bridge/router.ts
32201
32496
  import { readFileSync as readFileSync11, readdirSync as readdirSync4, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
32202
32497
  import { execSync as execSync4 } from "child_process";
32203
- import { basename as basename7, isAbsolute as isAbsolute4, join as join19, relative as relative3 } from "path";
32204
- import { homedir as homedir16 } from "os";
32498
+ import { basename as basename8, isAbsolute as isAbsolute4, join as join20, relative as relative3 } from "path";
32499
+ import { homedir as homedir17 } from "os";
32205
32500
  var t = initTRPC.context().create();
32206
32501
  var logged = t.middleware(async ({ path: path2, type, next }) => {
32207
32502
  const start = Date.now();
@@ -32228,13 +32523,13 @@ function resolveMobileCurrentDirectory2() {
32228
32523
  }
32229
32524
  }
32230
32525
  function resolveWorkspaceRoot2(root) {
32231
- const expandedRoot = root.replace(/^~/, homedir16());
32526
+ const expandedRoot = root.replace(/^~/, homedir17());
32232
32527
  return realpathSync2(expandedRoot);
32233
32528
  }
32234
32529
  function resolveWorkspacePath2(root, requestedPath) {
32235
32530
  const normalizedRoot = resolveWorkspaceRoot2(root);
32236
- const expandedPath = requestedPath?.replace(/^~/, homedir16());
32237
- const candidate = expandedPath ? isAbsolute4(expandedPath) ? expandedPath : join19(normalizedRoot, expandedPath) : normalizedRoot;
32531
+ const expandedPath = requestedPath?.replace(/^~/, homedir17());
32532
+ const candidate = expandedPath ? isAbsolute4(expandedPath) ? expandedPath : join20(normalizedRoot, expandedPath) : normalizedRoot;
32238
32533
  const resolvedCandidate = realpathSync2(candidate);
32239
32534
  const rel = relative3(normalizedRoot, resolvedCandidate);
32240
32535
  if (rel === "" || !rel.startsWith("..") && !isAbsolute4(rel)) {
@@ -32264,7 +32559,7 @@ function listDirectories2(dirPath) {
32264
32559
  continue;
32265
32560
  if (name === "node_modules" || name === ".build" || name === "target")
32266
32561
  continue;
32267
- const fullPath = join19(dirPath, name);
32562
+ const fullPath = join20(dirPath, name);
32268
32563
  try {
32269
32564
  const stat4 = statSync4(fullPath);
32270
32565
  if (!stat4.isDirectory())
@@ -32303,7 +32598,7 @@ function detectAgent2(filePath) {
32303
32598
  return "unknown";
32304
32599
  }
32305
32600
  async function discoverSessionFiles2(maxAgeDays, limit) {
32306
- const home = homedir16();
32601
+ const home = homedir17();
32307
32602
  const results = [];
32308
32603
  const searchPaths = [
32309
32604
  { pattern: `${home}/.claude/projects`, agent: "claude-code" },
@@ -32385,23 +32680,23 @@ function bridgeEventIterable(bridge, signal) {
32385
32680
  return {
32386
32681
  [Symbol.asyncIterator]() {
32387
32682
  const buffer = [];
32388
- let resolve7 = null;
32683
+ let resolve8 = null;
32389
32684
  let done = false;
32390
32685
  const unsub = bridge.onEvent((event) => {
32391
32686
  if (done)
32392
32687
  return;
32393
32688
  buffer.push(event);
32394
- if (resolve7) {
32395
- resolve7();
32396
- resolve7 = null;
32689
+ if (resolve8) {
32690
+ resolve8();
32691
+ resolve8 = null;
32397
32692
  }
32398
32693
  });
32399
32694
  const cleanup = () => {
32400
32695
  done = true;
32401
32696
  unsub();
32402
- if (resolve7) {
32403
- resolve7();
32404
- resolve7 = null;
32697
+ if (resolve8) {
32698
+ resolve8();
32699
+ resolve8 = null;
32405
32700
  }
32406
32701
  };
32407
32702
  signal?.addEventListener("abort", cleanup, { once: true });
@@ -32414,7 +32709,7 @@ function bridgeEventIterable(bridge, signal) {
32414
32709
  return { done: false, value: buffer.shift() };
32415
32710
  }
32416
32711
  await new Promise((r) => {
32417
- resolve7 = r;
32712
+ resolve8 = r;
32418
32713
  });
32419
32714
  }
32420
32715
  },
@@ -32543,9 +32838,9 @@ var sessionRouter = t.router({
32543
32838
  adapterType: exports_external.string().optional(),
32544
32839
  name: exports_external.string().optional()
32545
32840
  })).mutation(async ({ input, ctx }) => {
32546
- const sessionFilename = basename7(input.sessionPath, ".jsonl");
32841
+ const sessionFilename = basename8(input.sessionPath, ".jsonl");
32547
32842
  const parentDir = input.sessionPath.substring(0, input.sessionPath.lastIndexOf("/"));
32548
- const dirName = basename7(parentDir);
32843
+ const dirName = basename8(parentDir);
32549
32844
  let cwd;
32550
32845
  if (dirName.startsWith("-")) {
32551
32846
  const candidate = "/" + dirName.slice(1).replace(/-/g, "/");
@@ -32845,7 +33140,7 @@ var workspaceRouter = t.router({
32845
33140
  });
32846
33141
  }
32847
33142
  const adapterType = input.adapter ?? "claude-code";
32848
- const name = input.name ?? basename7(projectPath);
33143
+ const name = input.name ?? basename8(projectPath);
32849
33144
  return ctx.bridge.createSession(adapterType, {
32850
33145
  name,
32851
33146
  cwd: projectPath
@@ -33092,7 +33387,8 @@ var BACKOFF_MULTIPLIER = 2;
33092
33387
  var LEGACY_CLIENT_ID = "__legacy__";
33093
33388
  function connectToRelay(relayUrl, identity2, bridge, options = {}) {
33094
33389
  const { secure = true, events: events2 } = options;
33095
- const qrPayload = createQRPayload(identity2.publicKey, relayUrl);
33390
+ const publicRelayUrl = options.publicRelayUrl?.trim() || relayUrl;
33391
+ const qrPayload = createQRPayload(identity2.publicKey, publicRelayUrl, options.fallbackRelayUrls);
33096
33392
  let ws = null;
33097
33393
  let eventUnsub = null;
33098
33394
  let stopped = false;
@@ -33106,12 +33402,12 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
33106
33402
  const bridgeKeyHex = bytesToHex2(identity2.publicKey);
33107
33403
  const url2 = buildRelayUrl(relayUrl, qrPayload.room, bridgeKeyHex);
33108
33404
  console.log(`[relay-client] connecting to relay (room: ${qrPayload.room})`);
33109
- events2?.onConnecting?.({ relayUrl, room: qrPayload.room });
33405
+ events2?.onConnecting?.({ relayUrl: publicRelayUrl, room: qrPayload.room });
33110
33406
  ws = new WebSocket(url2, relayWebSocketOptions(relayUrl));
33111
33407
  ws.addEventListener("open", () => {
33112
33408
  console.log(`[relay-client] connected to relay (room: ${qrPayload.room})`);
33113
33409
  backoff = INITIAL_BACKOFF_MS;
33114
- events2?.onConnected?.({ relayUrl, room: qrPayload.room });
33410
+ events2?.onConnected?.({ relayUrl: publicRelayUrl, room: qrPayload.room });
33115
33411
  });
33116
33412
  ws.addEventListener("message", (event) => {
33117
33413
  handleRelaySocketMessage(event.data);
@@ -33119,7 +33415,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
33119
33415
  ws.addEventListener("close", (event) => {
33120
33416
  console.log(`[relay-client] disconnected (code: ${event.code}, reason: ${event.reason})`);
33121
33417
  events2?.onClosed?.({
33122
- relayUrl,
33418
+ relayUrl: publicRelayUrl,
33123
33419
  room: qrPayload.room,
33124
33420
  code: event.code,
33125
33421
  reason: event.reason
@@ -33130,7 +33426,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
33130
33426
  ws.addEventListener("error", () => {
33131
33427
  const error48 = new Error("Relay websocket error");
33132
33428
  console.error("[relay-client] connection error");
33133
- events2?.onError?.({ relayUrl, room: qrPayload.room, error: error48 });
33429
+ events2?.onError?.({ relayUrl: publicRelayUrl, room: qrPayload.room, error: error48 });
33134
33430
  });
33135
33431
  }
33136
33432
  function handleRelaySocketMessage(raw) {
@@ -33184,7 +33480,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
33184
33480
  replaceOlderPeerForSameDevice(peer);
33185
33481
  ensureBridgeEventSubscription();
33186
33482
  console.log(`[relay-client] secure handshake complete (peer: ${pubHex.slice(0, 12)}..., device: ${peer.deviceId}, client: ${clientId})`);
33187
- events2?.onPaired?.({ relayUrl, room: qrPayload.room, remotePublicKey });
33483
+ events2?.onPaired?.({ relayUrl: publicRelayUrl, room: qrPayload.room, remotePublicKey });
33188
33484
  sendExistingSessions((json2) => {
33189
33485
  if (peer.transport.isReady()) {
33190
33486
  peer.transport.send(json2);
@@ -33197,7 +33493,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
33197
33493
  onError: (err) => {
33198
33494
  console.error("[relay-client] secure transport error:", err.message);
33199
33495
  log.error("trns:cry", `decrypt failed for ${clientId} \u2014 resetting handshake: ${err.message}`);
33200
- events2?.onError?.({ relayUrl, room: qrPayload.room, error: err });
33496
+ events2?.onError?.({ relayUrl: publicRelayUrl, room: qrPayload.room, error: err });
33201
33497
  sendRelayClose(clientId, 4002, "Transport reset");
33202
33498
  teardownSecurePeer(clientId);
33203
33499
  },
@@ -33396,7 +33692,7 @@ function connectToRelay(relayUrl, identity2, bridge, options = {}) {
33396
33692
  if (stopped)
33397
33693
  return;
33398
33694
  console.log(`[relay-client] reconnecting in ${backoff}ms...`);
33399
- events2?.onReconnectScheduled?.({ relayUrl, room: qrPayload.room, delayMs: backoff });
33695
+ events2?.onReconnectScheduled?.({ relayUrl: publicRelayUrl, room: qrPayload.room, delayMs: backoff });
33400
33696
  reconnectTimer = setTimeout(() => {
33401
33697
  reconnectTimer = null;
33402
33698
  connect();
@@ -35392,8 +35688,8 @@ var Unpromise = class Unpromise2 {
35392
35688
  status: "fulfilled",
35393
35689
  value
35394
35690
  };
35395
- subscribers === null || subscribers === undefined || subscribers.forEach(({ resolve: resolve7 }) => {
35396
- resolve7(value);
35691
+ subscribers === null || subscribers === undefined || subscribers.forEach(({ resolve: resolve8 }) => {
35692
+ resolve8(value);
35397
35693
  });
35398
35694
  });
35399
35695
  if ("catch" in thenReturn)
@@ -35501,15 +35797,15 @@ function resolveSelfTuple(promise2) {
35501
35797
  return Unpromise.proxy(promise2).then(() => [promise2]);
35502
35798
  }
35503
35799
  function withResolvers() {
35504
- let resolve7;
35800
+ let resolve8;
35505
35801
  let reject;
35506
35802
  const promise2 = new Promise((_resolve, _reject) => {
35507
- resolve7 = _resolve;
35803
+ resolve8 = _resolve;
35508
35804
  reject = _reject;
35509
35805
  });
35510
35806
  return {
35511
35807
  promise: promise2,
35512
- resolve: resolve7,
35808
+ resolve: resolve8,
35513
35809
  reject
35514
35810
  };
35515
35811
  }
@@ -35555,8 +35851,8 @@ function timerResource(ms) {
35555
35851
  return makeResource({ start() {
35556
35852
  if (timer)
35557
35853
  throw new Error("Timer already started");
35558
- const promise2 = new Promise((resolve7) => {
35559
- timer = setTimeout(() => resolve7(disposablePromiseTimerResult), ms);
35854
+ const promise2 = new Promise((resolve8) => {
35855
+ timer = setTimeout(() => resolve8(disposablePromiseTimerResult), ms);
35560
35856
  });
35561
35857
  return promise2;
35562
35858
  } }, () => {
@@ -35759,15 +36055,15 @@ function _takeWithGrace() {
35759
36055
  return _takeWithGrace.apply(this, arguments);
35760
36056
  }
35761
36057
  function createDeferred() {
35762
- let resolve7;
36058
+ let resolve8;
35763
36059
  let reject;
35764
36060
  const promise2 = new Promise((res, rej) => {
35765
- resolve7 = res;
36061
+ resolve8 = res;
35766
36062
  reject = rej;
35767
36063
  });
35768
36064
  return {
35769
36065
  promise: promise2,
35770
- resolve: resolve7,
36066
+ resolve: resolve8,
35771
36067
  reject
35772
36068
  };
35773
36069
  }
@@ -36998,14 +37294,14 @@ function parseTRPCMessage(obj, transformer) {
36998
37294
  }
36999
37295
  // ../../apps/desktop/src/core/pairing/runtime/bridge/server-trpc.ts
37000
37296
  import { realpathSync as realpathSync3 } from "fs";
37001
- import { homedir as homedir17 } from "os";
37297
+ import { homedir as homedir18 } from "os";
37002
37298
  function resolveCurrentDirectory() {
37003
37299
  try {
37004
37300
  const config2 = resolveConfig();
37005
37301
  const configuredRoot = config2.workspace?.root;
37006
37302
  if (!configuredRoot)
37007
37303
  return process.cwd();
37008
- const expanded = configuredRoot.replace(/^~/, homedir17());
37304
+ const expanded = configuredRoot.replace(/^~/, homedir18());
37009
37305
  return realpathSync3(expanded);
37010
37306
  } catch {
37011
37307
  return process.cwd();
@@ -37152,8 +37448,8 @@ function startBridgeServerTRPC(options) {
37152
37448
  }
37153
37449
  const iterable = isObservable(result) ? observableToAsyncIterable(result, abortController.signal) : result;
37154
37450
  const iterator = iterable[Symbol.asyncIterator]();
37155
- const abortPromise = new Promise((resolve7) => {
37156
- abortController.signal.addEventListener("abort", () => resolve7("abort"), {
37451
+ const abortPromise = new Promise((resolve8) => {
37452
+ abortController.signal.addEventListener("abort", () => resolve8("abort"), {
37157
37453
  once: true
37158
37454
  });
37159
37455
  });
@@ -37498,6 +37794,8 @@ async function startPairingRuntime(options) {
37498
37794
  }
37499
37795
  const relayConnection = connectToRelay(relayUrl, identity2, bridge, {
37500
37796
  secure: true,
37797
+ publicRelayUrl: options?.advertisedRelayUrl ?? undefined,
37798
+ fallbackRelayUrls: options?.fallbackRelayUrls,
37501
37799
  events: options?.relayEvents
37502
37800
  });
37503
37801
  await autoStartConfiguredSessions(bridge, config2.sessions);
@@ -37525,7 +37823,7 @@ async function autoStartConfiguredSessions(bridge, sessions3) {
37525
37823
  try {
37526
37824
  const session = await bridge.createSession(entry.adapter, {
37527
37825
  name: entry.name,
37528
- cwd: entry.cwd?.replace(/^~/, homedir18()),
37826
+ cwd: entry.cwd?.replace(/^~/, homedir19()),
37529
37827
  options: entry.options
37530
37828
  });
37531
37829
  console.log(`[bridge] session started: ${session.name} (${entry.adapter})`);
@@ -37539,6 +37837,7 @@ async function autoStartConfiguredSessions(bridge, sessions3) {
37539
37837
  // ../../apps/desktop/src/core/pairing/supervisor.ts
37540
37838
  var SCOUT_PAIR_REFRESH_LEEWAY_MS = 30000;
37541
37839
  var SCOUT_PAIR_RESTART_DELAY_MS = 2000;
37840
+ var BONJOUR_SERVICE_TYPE = "_scout-pair._tcp";
37542
37841
  async function runScoutPairingSupervisor() {
37543
37842
  clearStalePairingRuntimeFiles();
37544
37843
  const existingPid = readPairingRuntimePid();
@@ -37560,7 +37859,8 @@ async function runScoutPairingSupervisor() {
37560
37859
  refreshTimer: null,
37561
37860
  intentionalStop: false,
37562
37861
  runtime: null,
37563
- relay: null
37862
+ relay: null,
37863
+ bonjour: null
37564
37864
  };
37565
37865
  const shutdown = async () => {
37566
37866
  state.intentionalStop = true;
@@ -37588,6 +37888,8 @@ async function startSupervisorRuntime(state) {
37588
37888
  const config2 = resolvedPairingConfig();
37589
37889
  const relayPort = config2.port + 1;
37590
37890
  const resolvedRelayUrl = config2.relay?.trim() || null;
37891
+ const identity2 = loadOrCreateIdentity();
37892
+ const publicKeyHex = bytesToHex2(identity2.publicKey);
37591
37893
  writeCurrent(state, {
37592
37894
  status: "starting",
37593
37895
  statusLabel: "Starting",
@@ -37599,12 +37901,24 @@ async function startSupervisorRuntime(state) {
37599
37901
  });
37600
37902
  const emitStatus = createStatusWriter(state);
37601
37903
  try {
37602
- const activeRelayUrl = resolvedRelayUrl ?? (() => {
37904
+ const managedRelay = resolvedRelayUrl ? null : (() => {
37603
37905
  state.relay = startManagedRelay(relayPort);
37604
- return state.relay.relayUrl;
37906
+ return state.relay;
37605
37907
  })();
37606
- state.runtime = await startPairingRuntime({
37908
+ const activeRelayUrl = resolvedRelayUrl ?? managedRelay?.relayUrl;
37909
+ const connectRelayUrl = resolvedRelayUrl ?? managedRelay?.connectUrl ?? managedRelay?.relayUrl;
37910
+ if (!activeRelayUrl || !connectRelayUrl) {
37911
+ throw new Error("Scout pairing relay URL is not configured.");
37912
+ }
37913
+ state.bonjour = managedRelay ? startBonjourRelayAdvertisement({
37914
+ port: relayPort,
37607
37915
  relayUrl: activeRelayUrl,
37916
+ publicKeyHex
37917
+ }) : null;
37918
+ state.runtime = await startPairingRuntime({
37919
+ relayUrl: connectRelayUrl,
37920
+ advertisedRelayUrl: activeRelayUrl,
37921
+ fallbackRelayUrls: managedRelay?.fallbackRelayUrls,
37608
37922
  relayEvents: {
37609
37923
  onConnecting() {
37610
37924
  emitStatus("connecting", `Connecting to ${activeRelayUrl}`);
@@ -37627,7 +37941,6 @@ async function startSupervisorRuntime(state) {
37627
37941
  if (!payload) {
37628
37942
  throw new Error("Scout pairing runtime did not produce a QR payload.");
37629
37943
  }
37630
- const identity2 = loadOrCreateIdentity();
37631
37944
  writeCurrent(state, {
37632
37945
  status: "connecting",
37633
37946
  statusLabel: "Pairing Ready",
@@ -37644,7 +37957,7 @@ async function startSupervisorRuntime(state) {
37644
37957
  },
37645
37958
  childPid: null
37646
37959
  }, {
37647
- identityFingerprint: bytesToHex2(identity2.publicKey).slice(0, 16),
37960
+ identityFingerprint: publicKeyHex.slice(0, 16),
37648
37961
  trustedPeerCount: trustedPeerCount()
37649
37962
  });
37650
37963
  schedulePairingRefresh(state, payload);
@@ -37718,6 +38031,9 @@ function clearRefreshTimer(state) {
37718
38031
  }
37719
38032
  }
37720
38033
  async function stopSupervisorRuntime(state) {
38034
+ try {
38035
+ state.bonjour?.stop();
38036
+ } catch {}
37721
38037
  try {
37722
38038
  await state.runtime?.stop();
37723
38039
  } catch {}
@@ -37726,6 +38042,57 @@ async function stopSupervisorRuntime(state) {
37726
38042
  } catch {}
37727
38043
  state.runtime = null;
37728
38044
  state.relay = null;
38045
+ state.bonjour = null;
38046
+ }
38047
+ function startBonjourRelayAdvertisement(input) {
38048
+ if (process.platform !== "darwin") {
38049
+ return null;
38050
+ }
38051
+ const scheme = relayScheme(input.relayUrl);
38052
+ const fingerprint = input.publicKeyHex.slice(0, 16);
38053
+ const serviceName = `OpenScout ${fingerprint}`;
38054
+ const args = [
38055
+ "-R",
38056
+ serviceName,
38057
+ BONJOUR_SERVICE_TYPE,
38058
+ "local.",
38059
+ String(input.port),
38060
+ "v=1",
38061
+ `pk=${input.publicKeyHex}`,
38062
+ `fp=${fingerprint}`,
38063
+ `scheme=${scheme}`
38064
+ ];
38065
+ let processRef = null;
38066
+ try {
38067
+ processRef = spawn4("/usr/bin/dns-sd", args, { stdio: "ignore" });
38068
+ processRef.on("error", (error48) => {
38069
+ console.warn(`[pairing] bonjour advertisement failed: ${error48.message}`);
38070
+ });
38071
+ processRef.on("exit", (code, signal) => {
38072
+ if (code !== 0 && signal !== "SIGTERM") {
38073
+ console.warn(`[pairing] bonjour advertisement exited (code=${code ?? "null"}, signal=${signal ?? "null"})`);
38074
+ }
38075
+ });
38076
+ console.log(`[pairing] bonjour advertising ${serviceName} on ${BONJOUR_SERVICE_TYPE} port ${input.port}`);
38077
+ } catch (error48) {
38078
+ const detail = error48 instanceof Error ? error48.message : String(error48);
38079
+ console.warn(`[pairing] bonjour advertisement unavailable: ${detail}`);
38080
+ return null;
38081
+ }
38082
+ return {
38083
+ stop() {
38084
+ processRef?.kill("SIGTERM");
38085
+ processRef = null;
38086
+ }
38087
+ };
38088
+ }
38089
+ function relayScheme(relayUrl) {
38090
+ try {
38091
+ const protocol = new URL(relayUrl).protocol;
38092
+ return protocol === "wss:" ? "wss" : "ws";
38093
+ } catch {
38094
+ return relayUrl.startsWith("wss://") ? "wss" : "ws";
38095
+ }
37729
38096
  }
37730
38097
  function writeCurrent(state, patch, overrides = {}) {
37731
38098
  state.current = writePairingRuntimeSnapshot({