@openscout/scout 0.2.68 → 0.2.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.mjs CHANGED
@@ -815,6 +815,17 @@ function assertValidUnblockRequestEvent(event, record) {
815
815
  throw new Error(errors.join("; "));
816
816
  }
817
817
  }
818
+ // ../protocol/src/lifecycle.ts
819
+ var TERMINAL_INVOCATION_STATES;
820
+ var init_lifecycle = __esm(() => {
821
+ TERMINAL_INVOCATION_STATES = new Set([
822
+ "completed",
823
+ "failed",
824
+ "cancelled",
825
+ "expired"
826
+ ]);
827
+ });
828
+
818
829
  // ../protocol/src/permission-policy.ts
819
830
  function normalizeScoutPermissionProfile(value) {
820
831
  const normalized = value?.trim().replaceAll("-", "_");
@@ -901,6 +912,9 @@ function parsePrefixedRouteTarget(prefix, rawValue) {
901
912
  if (prefix === "id" || prefix === "agent-id" || prefix === "agent_id") {
902
913
  return { kind: "agent_id", agentId: value, value: `id:${value}` };
903
914
  }
915
+ if (prefix === "session" || prefix === "session-id" || prefix === "session_id" || prefix === "s") {
916
+ return { kind: "session_id", sessionId: value, value: `session:${value}` };
917
+ }
904
918
  if (prefix === "channel" || prefix === "chan" || prefix === "ch") {
905
919
  return { kind: "channel", channel: value, value: `channel:${value}` };
906
920
  }
@@ -1003,15 +1017,20 @@ var init_inbox = __esm(() => {
1003
1017
  "deferred"
1004
1018
  ];
1005
1019
  });
1020
+ // ../protocol/src/cursor-transport.ts
1021
+ var init_cursor_transport = () => {};
1022
+
1006
1023
  // ../protocol/src/index.ts
1007
1024
  var init_src = __esm(() => {
1008
1025
  init_agent_identity();
1009
1026
  init_agent_address();
1010
1027
  init_agent_selectors();
1028
+ init_lifecycle();
1011
1029
  init_agent_runs();
1012
1030
  init_scout_composer();
1013
1031
  init_inbox();
1014
1032
  init_permission_policy();
1033
+ init_cursor_transport();
1015
1034
  });
1016
1035
 
1017
1036
  // ../../apps/desktop/src/cli/options.ts
@@ -1140,6 +1159,15 @@ function parseComposerRoutedBody(input, commandName, currentDirectory) {
1140
1159
  message: parsed.body
1141
1160
  };
1142
1161
  }
1162
+ if (target.kind === "session_id") {
1163
+ if (commandName === "send") {
1164
+ throw new ScoutCliError("send route operator must target an agent label, ref, channel, or broadcast");
1165
+ }
1166
+ return {
1167
+ targetLabel: `session:${target.sessionId}`,
1168
+ message: parsed.body
1169
+ };
1170
+ }
1143
1171
  if (target.kind === "project_path") {
1144
1172
  if (commandName === "send") {
1145
1173
  throw new ScoutCliError("send route operator must target an agent label, ref, channel, or broadcast");
@@ -1627,6 +1655,7 @@ function parseChannelCommandOptions(args, defaultCurrentDirectory) {
1627
1655
  const parsed = parseContextRootPrefix(args, defaultCurrentDirectory);
1628
1656
  let channel;
1629
1657
  let latest;
1658
+ let markRead = false;
1630
1659
  for (let index = 0;index < parsed.args.length; index += 1) {
1631
1660
  const current = parsed.args[index] ?? "";
1632
1661
  if (current === "--channel" || current.startsWith("--channel=")) {
@@ -1645,20 +1674,28 @@ function parseChannelCommandOptions(args, defaultCurrentDirectory) {
1645
1674
  index = value.nextIndex;
1646
1675
  continue;
1647
1676
  }
1677
+ if (current === "--mark-read" || current === "--read" || current === "--clear") {
1678
+ markRead = true;
1679
+ continue;
1680
+ }
1648
1681
  if (!current.startsWith("-") && !channel) {
1649
1682
  channel = current;
1650
1683
  continue;
1651
1684
  }
1652
1685
  unexpectedArgs("channel", args);
1653
1686
  }
1654
- if (channel && !latest) {
1655
- throw new ScoutCliError("channel name is only valid with --latest");
1687
+ if (latest && markRead) {
1688
+ throw new ScoutCliError("provide either --latest or --mark-read, not both");
1689
+ }
1690
+ if (channel && !latest && !markRead) {
1691
+ throw new ScoutCliError("channel name is only valid with --latest or --mark-read");
1656
1692
  }
1657
1693
  return {
1658
1694
  currentDirectory: parsed.currentDirectory,
1659
1695
  args: parsed.args,
1660
1696
  channel,
1661
- latest
1697
+ latest,
1698
+ markRead
1662
1699
  };
1663
1700
  }
1664
1701
  function parseInboxCommandOptions(args, defaultCurrentDirectory) {
@@ -1805,6 +1842,7 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
1805
1842
  let permissionProfile;
1806
1843
  let requesterId = null;
1807
1844
  let noInput = false;
1845
+ let oneTimeUse = false;
1808
1846
  for (let index = 0;index < parsed.args.length; index += 1) {
1809
1847
  const current = parsed.args[index] ?? "";
1810
1848
  if (current === "--name" || current.startsWith("--name=")) {
@@ -1868,6 +1906,10 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
1868
1906
  noInput = true;
1869
1907
  continue;
1870
1908
  }
1909
+ if (current === "--one-time") {
1910
+ oneTimeUse = true;
1911
+ continue;
1912
+ }
1871
1913
  if (current.startsWith("--")) {
1872
1914
  unexpectedArgs("card create", args);
1873
1915
  }
@@ -1887,7 +1929,8 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
1887
1929
  reasoningEffort,
1888
1930
  permissionProfile,
1889
1931
  requesterId,
1890
- noInput
1932
+ noInput,
1933
+ oneTimeUse
1891
1934
  };
1892
1935
  }
1893
1936
  function parseTuiCommandOptions(args, defaultCurrentDirectory) {
@@ -6209,6 +6252,15 @@ async function maybeReadJsonFromActiveScoutBrokerService(baseUrl, path) {
6209
6252
  includeAcknowledged: url.searchParams.get("includeAcknowledged") === "1" || url.searchParams.get("includeAcknowledged") === "true"
6210
6253
  }));
6211
6254
  }
6255
+ const invocationLifecycleMatch = url.pathname.match(/^\/v1\/invocations\/([^/]+)\/lifecycle$/);
6256
+ if (invocationLifecycleMatch) {
6257
+ if (!service.readInvocationLifecycle) {
6258
+ return unhandled();
6259
+ }
6260
+ return handled(await service.readInvocationLifecycle({
6261
+ invocationId: decodeURIComponent(invocationLifecycleMatch[1] ?? "")
6262
+ }));
6263
+ }
6212
6264
  if (url.pathname === "/v1/activity") {
6213
6265
  if (!service.readActivity) {
6214
6266
  return unhandled();
@@ -7089,6 +7141,17 @@ async function fetchHealthSnapshot(config) {
7089
7141
  nodes: payload.counts.nodes ?? 0,
7090
7142
  actors: payload.counts.actors ?? 0,
7091
7143
  agents: payload.counts.agents ?? 0,
7144
+ agentRecords: payload.counts.agentRecords,
7145
+ rawAgentRecords: payload.counts.rawAgentRecords,
7146
+ configuredAgents: payload.counts.configuredAgents,
7147
+ scoutManagedAgents: payload.counts.scoutManagedAgents,
7148
+ currentAgentRegistrations: payload.counts.currentAgentRegistrations,
7149
+ localAgentRegistrations: payload.counts.localAgentRegistrations,
7150
+ remoteAgentRegistrations: payload.counts.remoteAgentRegistrations,
7151
+ staleAgentRegistrations: payload.counts.staleAgentRegistrations,
7152
+ retiredAgentRegistrations: payload.counts.retiredAgentRegistrations,
7153
+ oneTimeAgentCards: payload.counts.oneTimeAgentCards,
7154
+ persistentAgentCards: payload.counts.persistentAgentCards,
7092
7155
  conversations: payload.counts.conversations ?? 0,
7093
7156
  messages: payload.counts.messages ?? 0,
7094
7157
  flights: payload.counts.flights ?? 0,
@@ -7261,6 +7324,9 @@ var init_broker_process_manager = __esm(() => {
7261
7324
  }
7262
7325
  });
7263
7326
 
7327
+ // ../runtime/src/dispatch-stalled.ts
7328
+ var init_dispatch_stalled = () => {};
7329
+
7264
7330
  // ../runtime/src/managed-agent-environment.ts
7265
7331
  function managedAgentEnvironmentEntries(options) {
7266
7332
  const agentName = options.agentName.trim();
@@ -13525,6 +13591,15 @@ function readCodexAppServerReasoningEffortFromLaunchArgs(launchArgs) {
13525
13591
  }
13526
13592
  return null;
13527
13593
  }
13594
+ function codexAppServerExitMessage(input) {
13595
+ if (input.exitKind === "proactive_shutdown") {
13596
+ return `Codex app-server session for ${input.agentName} was stopped by OpenScout` + (input.reason ? `: ${input.reason}` : "") + ".";
13597
+ }
13598
+ if (input.exitKind === "external_sigterm") {
13599
+ return `Codex app-server for ${input.agentName} was interrupted by SIGTERM.`;
13600
+ }
13601
+ return `Codex app-server exited for ${input.agentName}` + (input.exitCode !== null ? ` with code ${input.exitCode}` : "") + (input.signal ? ` (${input.signal})` : "");
13602
+ }
13528
13603
  function resolveRequesterTimeoutMs2(timeoutMs) {
13529
13604
  if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
13530
13605
  return timeoutMs;
@@ -13696,6 +13771,7 @@ class CodexAppServerSession {
13696
13771
  activeTurn = null;
13697
13772
  threadId = null;
13698
13773
  threadPath = null;
13774
+ proactiveShutdown = null;
13699
13775
  lastConfigSignature;
13700
13776
  constructor(options) {
13701
13777
  this.options = options;
@@ -13826,13 +13902,23 @@ class CodexAppServerSession {
13826
13902
  });
13827
13903
  }
13828
13904
  async shutdown(options = {}) {
13905
+ this.proactiveShutdown = {
13906
+ reason: options.reason ?? (options.resetThread ? "OpenScout reset the app-server session" : "OpenScout stopped the app-server session")
13907
+ };
13908
+ const stoppedError = new CodexAppServerExitError({
13909
+ agentName: this.options.agentName,
13910
+ exitKind: "proactive_shutdown",
13911
+ exitCode: null,
13912
+ signal: null,
13913
+ reason: this.proactiveShutdown.reason
13914
+ });
13829
13915
  const activeTurn = this.activeTurn;
13830
13916
  this.activeTurn = null;
13831
13917
  if (activeTurn) {
13832
- activeTurn.reject(new Error(`Codex app-server session for ${this.options.agentName} was shut down.`));
13918
+ activeTurn.reject(stoppedError);
13833
13919
  }
13834
13920
  for (const pending of this.pendingRequests.values()) {
13835
- pending.reject(new Error(`Codex app-server session for ${this.options.agentName} was shut down.`));
13921
+ pending.reject(stoppedError);
13836
13922
  }
13837
13923
  this.pendingRequests.clear();
13838
13924
  const child = this.process;
@@ -13842,7 +13928,7 @@ class CodexAppServerSession {
13842
13928
  if (child && child.exitCode === null && !child.killed) {
13843
13929
  child.kill("SIGTERM");
13844
13930
  await new Promise((resolve8) => setTimeout(resolve8, 250));
13845
- if (child.exitCode === null && !child.killed) {
13931
+ if (child.exitCode === null && child.signalCode === null) {
13846
13932
  child.kill("SIGKILL");
13847
13933
  }
13848
13934
  }
@@ -13893,6 +13979,7 @@ class CodexAppServerSession {
13893
13979
  await mkdir5(this.options.runtimeDirectory, { recursive: true });
13894
13980
  await mkdir5(this.options.logsDirectory, { recursive: true });
13895
13981
  await writeFile5(join14(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
13982
+ this.proactiveShutdown = null;
13896
13983
  const codexExecutable = resolveCodexExecutable();
13897
13984
  const launchArgs = normalizeCodexAppServerLaunchArgs(this.options.launchArgs);
13898
13985
  const env = buildManagedAgentEnvironment({
@@ -13932,7 +14019,16 @@ class CodexAppServerSession {
13932
14019
  this.failSession(new Error(`Codex app-server failed for ${this.options.agentName}: ${errorMessage4(error)}`));
13933
14020
  });
13934
14021
  child.once("exit", (code, signal) => {
13935
- this.failSession(new Error(`Codex app-server exited for ${this.options.agentName}` + (code !== null ? ` with code ${code}` : "") + (signal ? ` (${signal})` : "")));
14022
+ const proactiveShutdown = this.proactiveShutdown;
14023
+ if (proactiveShutdown) {
14024
+ this.handleProactiveProcessExit(child, proactiveShutdown, code, signal);
14025
+ return;
14026
+ }
14027
+ this.failSession(new CodexAppServerExitError({
14028
+ agentName: this.options.agentName,
14029
+ exitCode: code,
14030
+ signal
14031
+ }));
13936
14032
  });
13937
14033
  await this.request("initialize", {
13938
14034
  clientInfo: {
@@ -14267,6 +14363,23 @@ class CodexAppServerSession {
14267
14363
  }
14268
14364
  this.pendingRequests.clear();
14269
14365
  appendFile3(this.stderrLogPath, `[openscout] ${error.message}
14366
+ `).catch(() => {
14367
+ return;
14368
+ });
14369
+ this.persistState();
14370
+ }
14371
+ handleProactiveProcessExit(child, shutdown, code, signal) {
14372
+ if (this.process === child) {
14373
+ this.process = null;
14374
+ }
14375
+ this.proactiveShutdown = null;
14376
+ this.starting = null;
14377
+ const exitDetail = [
14378
+ code !== null ? `code ${code}` : null,
14379
+ signal
14380
+ ].filter(Boolean).join(", ");
14381
+ const detail = exitDetail ? ` (${exitDetail})` : "";
14382
+ appendFile3(this.stderrLogPath, `[openscout] Codex app-server stopped for ${this.options.agentName}: ${shutdown.reason}${detail}
14270
14383
  `).catch(() => {
14271
14384
  return;
14272
14385
  });
@@ -14337,7 +14450,10 @@ function getOrCreateSession2(options) {
14337
14450
  if (existing.matches(options)) {
14338
14451
  return existing;
14339
14452
  }
14340
- existing.shutdown({ resetThread: true });
14453
+ existing.shutdown({
14454
+ resetThread: true,
14455
+ reason: "OpenScout replaced the app-server session after its launch options changed"
14456
+ });
14341
14457
  sessions2.delete(key);
14342
14458
  }
14343
14459
  const session = new CodexAppServerSession(options);
@@ -14368,13 +14484,36 @@ async function shutdownCodexAppServerAgent(options, shutdownOptions = {}) {
14368
14484
  sessions2.delete(key);
14369
14485
  await session.shutdown(shutdownOptions);
14370
14486
  }
14371
- var SESSION_CATALOG_FILENAME2 = "session-catalog.json", SESSION_CATALOG_MAX_ENTRIES2 = 64, CODEX_ROLLOUT_STALE_ACTIVE_TURN_MS, sessions2;
14487
+ var CodexAppServerExitError, SESSION_CATALOG_FILENAME2 = "session-catalog.json", SESSION_CATALOG_MAX_ENTRIES2 = 64, CODEX_ROLLOUT_STALE_ACTIVE_TURN_MS, sessions2;
14372
14488
  var init_codex_app_server = __esm(() => {
14373
14489
  init_src2();
14374
14490
  init_topology();
14375
14491
  init_codex_executable();
14376
14492
  init_requester_timeout();
14377
14493
  init_codex_executable();
14494
+ CodexAppServerExitError = class CodexAppServerExitError extends Error {
14495
+ code = "CODEX_APP_SERVER_EXIT";
14496
+ exitKind;
14497
+ agentName;
14498
+ exitCode;
14499
+ signal;
14500
+ reason;
14501
+ noteworthy;
14502
+ constructor(input) {
14503
+ const exitKind = input.exitKind ?? (input.signal === "SIGTERM" ? "external_sigterm" : "unexpected_exit");
14504
+ super(codexAppServerExitMessage({
14505
+ ...input,
14506
+ exitKind
14507
+ }));
14508
+ this.name = "CodexAppServerExitError";
14509
+ this.exitKind = exitKind;
14510
+ this.agentName = input.agentName;
14511
+ this.exitCode = input.exitCode;
14512
+ this.signal = input.signal;
14513
+ this.reason = input.reason ?? null;
14514
+ this.noteworthy = exitKind !== "unexpected_exit";
14515
+ }
14516
+ };
14378
14517
  CODEX_ROLLOUT_STALE_ACTIVE_TURN_MS = 10 * 60 * 1000;
14379
14518
  sessions2 = new Map;
14380
14519
  });
@@ -14470,11 +14609,47 @@ var init_permission_policy2 = __esm(() => {
14470
14609
  init_src();
14471
14610
  });
14472
14611
 
14612
+ // ../runtime/src/user-config.ts
14613
+ import { existsSync as existsSync11, readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
14614
+ import { homedir as homedir12 } from "os";
14615
+ import { dirname as dirname8, join as join15 } from "path";
14616
+ function userConfigPath() {
14617
+ return join15(process.env.OPENSCOUT_HOME ?? join15(homedir12(), ".openscout"), "user.json");
14618
+ }
14619
+ function loadUserConfig() {
14620
+ const configPath = userConfigPath();
14621
+ if (!existsSync11(configPath))
14622
+ return {};
14623
+ try {
14624
+ return JSON.parse(readFileSync6(configPath, "utf8"));
14625
+ } catch {
14626
+ return {};
14627
+ }
14628
+ }
14629
+ function saveUserConfig(config) {
14630
+ const configPath = userConfigPath();
14631
+ mkdirSync3(dirname8(configPath), { recursive: true });
14632
+ writeFileSync3(configPath, JSON.stringify(config, null, 2) + `
14633
+ `, "utf8");
14634
+ }
14635
+ function resolveOperatorName() {
14636
+ const config = loadUserConfig();
14637
+ return config.name?.trim() || process.env.OPENSCOUT_OPERATOR_NAME?.trim() || process.env.USER?.trim() || "operator";
14638
+ }
14639
+ function normalizeHandle(value) {
14640
+ return value?.trim().replace(/^@+/, "") ?? "";
14641
+ }
14642
+ function resolveOperatorHandle() {
14643
+ const config = loadUserConfig();
14644
+ return normalizeHandle(config.handle) || normalizeHandle(process.env.OPENSCOUT_OPERATOR_HANDLE) || normalizeHandle(config.name) || normalizeHandle(process.env.OPENSCOUT_OPERATOR_NAME) || normalizeHandle(process.env.USER) || "operator";
14645
+ }
14646
+ var init_user_config = () => {};
14647
+
14473
14648
  // ../runtime/src/local-agents.ts
14474
14649
  import { execFileSync as execFileSync4, execSync } from "child_process";
14475
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
14650
+ import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
14476
14651
  import { mkdir as mkdir6, rm as rm5, stat as stat3, writeFile as writeFile6 } from "fs/promises";
14477
- import { basename as basename7, dirname as dirname8, join as join15, resolve as resolve8 } from "path";
14652
+ import { basename as basename7, dirname as dirname9, join as join16, resolve as resolve8 } from "path";
14478
14653
  import { fileURLToPath as fileURLToPath6 } from "url";
14479
14654
  function resolveRelayHub() {
14480
14655
  return resolveOpenScoutSupportPaths().relayHubDirectory;
@@ -14485,14 +14660,14 @@ function resolveProjectsRoot(projectPath) {
14485
14660
  }
14486
14661
  try {
14487
14662
  const supportPaths = resolveOpenScoutSupportPaths();
14488
- if (!existsSync11(supportPaths.settingsPath)) {
14489
- return dirname8(projectPath);
14663
+ if (!existsSync12(supportPaths.settingsPath)) {
14664
+ return dirname9(projectPath);
14490
14665
  }
14491
- const raw = JSON.parse(readFileSync6(supportPaths.settingsPath, "utf8"));
14666
+ const raw = JSON.parse(readFileSync7(supportPaths.settingsPath, "utf8"));
14492
14667
  const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
14493
- return workspaceRoot ? resolve8(workspaceRoot) : dirname8(projectPath);
14668
+ return workspaceRoot ? resolve8(workspaceRoot) : dirname9(projectPath);
14494
14669
  } catch {
14495
- return dirname8(projectPath);
14670
+ return dirname9(projectPath);
14496
14671
  }
14497
14672
  }
14498
14673
  function resolveBrokerUrl() {
@@ -14502,7 +14677,7 @@ function nowSeconds2() {
14502
14677
  return Math.floor(Date.now() / 1000);
14503
14678
  }
14504
14679
  function scoutCliPath() {
14505
- return join15(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
14680
+ return join16(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
14506
14681
  }
14507
14682
  function legacyNodeBrokerRelayCommand() {
14508
14683
  return `node ${JSON.stringify(scoutCliPath())}`;
@@ -14515,13 +14690,13 @@ function titleCaseLocalAgentName(value) {
14515
14690
  }
14516
14691
  function resolveScoutSkillPath() {
14517
14692
  const candidatePaths = [
14518
- join15(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
14519
- join15(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
14520
- join15(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
14521
- join15(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
14693
+ join16(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
14694
+ join16(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
14695
+ join16(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
14696
+ join16(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
14522
14697
  ];
14523
14698
  for (const path of candidatePaths) {
14524
- if (existsSync11(path)) {
14699
+ if (existsSync12(path)) {
14525
14700
  return path;
14526
14701
  }
14527
14702
  }
@@ -14629,6 +14804,74 @@ function buildLocalAgentSystemPromptTemplate() {
14629
14804
  ].join(`
14630
14805
  `);
14631
14806
  }
14807
+ function normalizeOperatorHandleSegment(value) {
14808
+ return normalizeAgentSelectorSegment(value?.trim().replace(/^@+/, "") ?? "") || "operator";
14809
+ }
14810
+ function resolveOperatorAugmentAgentName() {
14811
+ return `${normalizeOperatorHandleSegment(resolveOperatorHandle())}-ai`;
14812
+ }
14813
+ function operatorAugmentAgentNameCandidates() {
14814
+ return new Set([
14815
+ resolveOperatorAugmentAgentName(),
14816
+ "operator-ai"
14817
+ ]);
14818
+ }
14819
+ function buildOperatorAugmentSystemPromptTemplate(input = {}) {
14820
+ const operatorHandle = normalizeOperatorHandleSegment(input.operatorHandle ?? resolveOperatorHandle());
14821
+ const augmentHandle = normalizeOperatorHandleSegment(input.augmentHandle ?? `${operatorHandle}-ai`);
14822
+ const humanLabel = `@${operatorHandle}`;
14823
+ const augmentLabel = `@${augmentHandle}`;
14824
+ return [
14825
+ "{{base_prompt}}",
14826
+ "",
14827
+ `You are the augmented counterpart to the human Scout operator ${humanLabel}.`,
14828
+ `${humanLabel} is the human. ${augmentLabel} is the AI-augmented looper with a human in the loop.`,
14829
+ `Do not impersonate ${humanLabel}. Speak and act as ${augmentLabel}, and involve ${humanLabel} only when human judgment or approval is the real next dependency.`,
14830
+ "",
14831
+ "Operating loop:",
14832
+ " - Keep long-running conversations in the same DM or invocation thread whenever possible.",
14833
+ " - Maintain continuity across turns: track the goal, decisions made, open questions, blockers, and promised follow-ups.",
14834
+ " - For efforts that span many turns, many files, or more than one working session, create or update a durable note/checkpoint when you have write access, then point to it briefly.",
14835
+ " - Prefer continuing from a concise recap over resetting context. If context is aging, summarize the useful state and keep moving.",
14836
+ ` - Be explicit about the next responsible owner: ${augmentLabel}, ${humanLabel}, or another named agent.`,
14837
+ "",
14838
+ `When to invoke ${humanLabel}:`,
14839
+ " - Approval is needed for destructive, irreversible, public, financial, security-sensitive, credential, privacy, or cross-project priority decisions.",
14840
+ ` - The task depends on taste, intent, product direction, personal context, or a choice only ${humanLabel} can make.`,
14841
+ " - You are blocked after a reasonable local attempt and the unblock request can be stated as a short concrete question.",
14842
+ " - A result is materially uncertain and acting without human input could waste meaningful time or create cleanup work.",
14843
+ " - You need permission to interrupt, wake, or redirect other people or agents outside the current work venue.",
14844
+ "",
14845
+ `How to invoke ${humanLabel}:`,
14846
+ " - Keep the ask concise: context, the decision needed, the default you recommend, and the consequence of no answer.",
14847
+ " - Use the same DM/thread when it exists. Do not broadcast human-loop requests.",
14848
+ ` - If a reply is required before work can proceed, say that ${humanLabel} owns the next move and stop claiming progress.`,
14849
+ " - If work can continue safely, state the assumption and continue without waiting.",
14850
+ "",
14851
+ `Do not invoke ${humanLabel} for:`,
14852
+ " - Routine status updates, obvious implementation details, command outputs, local orientation, or reversible low-risk cleanup.",
14853
+ " - Ambiguity that you can resolve by reading the repo, checking Scout state, or asking the correct specialist agent.",
14854
+ "",
14855
+ "{{project_context}}",
14856
+ "",
14857
+ "{{collaboration_prompt}}",
14858
+ "",
14859
+ "{{protocol_prompt}}"
14860
+ ].join(`
14861
+ `);
14862
+ }
14863
+ function operatorAugmentDefaultsForDefinitionId(definitionId) {
14864
+ const normalizedDefinitionId = normalizeAgentSelectorSegment(definitionId);
14865
+ if (!normalizedDefinitionId || !operatorAugmentAgentNameCandidates().has(normalizedDefinitionId)) {
14866
+ return null;
14867
+ }
14868
+ return {
14869
+ displayName: titleCaseLocalAgentName(normalizedDefinitionId),
14870
+ systemPrompt: buildOperatorAugmentSystemPromptTemplate({
14871
+ augmentHandle: normalizedDefinitionId
14872
+ })
14873
+ };
14874
+ }
14632
14875
  function renderLocalAgentSystemPromptTemplate(template, context, options = {}) {
14633
14876
  const basePrompt = buildLocalAgentBasePrompt(context);
14634
14877
  const projectContext = buildLocalAgentProjectContextPrompt(context);
@@ -14783,13 +15026,20 @@ function expandHomePath4(value) {
14783
15026
  return process.env.HOME ?? process.cwd();
14784
15027
  }
14785
15028
  if (value.startsWith("~/")) {
14786
- return join15(process.env.HOME ?? process.cwd(), value.slice(2));
15029
+ return join16(process.env.HOME ?? process.cwd(), value.slice(2));
14787
15030
  }
14788
15031
  return value;
14789
15032
  }
14790
15033
  function normalizeProjectPath(value) {
14791
15034
  return resolve8(expandHomePath4(value.trim() || "."));
14792
15035
  }
15036
+ function compactHomePath(value) {
15037
+ const home = process.env.HOME;
15038
+ if (!home) {
15039
+ return value;
15040
+ }
15041
+ return value.startsWith(home) ? value.replace(home, "~") : value;
15042
+ }
14793
15043
  function normalizeTmuxSessionName2(value, agentId) {
14794
15044
  const fallback = `relay-${agentId}`;
14795
15045
  const trimmed = value?.trim() ?? "";
@@ -15043,6 +15293,21 @@ function applyRequestedRuntimeOptionsToLaunchArgs(harness, launchArgs, options)
15043
15293
  ...buildLaunchArgsForRequestedReasoningEffort(harness, requestedReasoningEffort)
15044
15294
  ];
15045
15295
  }
15296
+ function setLaunchModelForHarness(harness, launchArgs, model) {
15297
+ const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
15298
+ if (model === undefined) {
15299
+ return normalized;
15300
+ }
15301
+ const stripped = stripLaunchModelForHarness(harness, normalized);
15302
+ const requestedModel = normalizeRequestedModel(model ?? undefined);
15303
+ if (!requestedModel) {
15304
+ return stripped;
15305
+ }
15306
+ return [
15307
+ ...stripped,
15308
+ ...buildLaunchArgsForRequestedModel(harness, requestedModel)
15309
+ ];
15310
+ }
15046
15311
  function defaultHarnessForOverride(override, fallback = "claude") {
15047
15312
  return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
15048
15313
  }
@@ -15163,6 +15428,31 @@ function recordForHarness(record, harnessOverride) {
15163
15428
  permissionProfile: profile.permissionProfile
15164
15429
  };
15165
15430
  }
15431
+ function normalizeCardTimestamp(value) {
15432
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
15433
+ }
15434
+ function normalizeLocalAgentCardLifecycle(value, now = Date.now()) {
15435
+ if (!value) {
15436
+ return;
15437
+ }
15438
+ const kind = value.kind === "one_time" ? "one_time" : value.kind === "persistent" ? "persistent" : undefined;
15439
+ if (!kind) {
15440
+ return;
15441
+ }
15442
+ const createdAt = normalizeCardTimestamp(value.createdAt) ?? now;
15443
+ const expiresAt = normalizeCardTimestamp(value.expiresAt);
15444
+ const maxUses = typeof value.maxUses === "number" && Number.isFinite(value.maxUses) && value.maxUses > 0 ? Math.floor(value.maxUses) : undefined;
15445
+ const createdById = value.createdById?.trim();
15446
+ const inboxConversationId = value.inboxConversationId?.trim();
15447
+ return {
15448
+ kind,
15449
+ createdAt,
15450
+ ...createdById ? { createdById } : {},
15451
+ ...expiresAt ? { expiresAt } : {},
15452
+ ...maxUses ? { maxUses } : {},
15453
+ ...inboxConversationId ? { inboxConversationId } : {}
15454
+ };
15455
+ }
15166
15456
  function normalizeLocalAgentRecord(agentId, record) {
15167
15457
  const cwd = normalizeProjectPath(record.cwd || process.cwd());
15168
15458
  const projectRoot = normalizeProjectPath(record.projectRoot || cwd);
@@ -15192,7 +15482,8 @@ function normalizeLocalAgentRecord(agentId, record) {
15192
15482
  capabilities: normalizeLocalAgentCapabilities(record.capabilities),
15193
15483
  launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs),
15194
15484
  permissionProfile: activeProfile?.permissionProfile ?? record.permissionProfile,
15195
- registrationSource: record.registrationSource
15485
+ registrationSource: record.registrationSource,
15486
+ card: normalizeLocalAgentCardLifecycle(record.card)
15196
15487
  };
15197
15488
  }
15198
15489
  function localAgentStatusFromRecord(agentId, record, source) {
@@ -15246,7 +15537,8 @@ function localAgentRecordFromRelayAgentOverride(agentId, override) {
15246
15537
  harnessProfiles: override.harnessProfiles,
15247
15538
  transport: normalizeLocalAgentTransport(override.runtime?.transport, normalizeLocalAgentHarness(override.runtime?.harness)),
15248
15539
  capabilities: override.capabilities,
15249
- launchArgs: override.launchArgs
15540
+ launchArgs: override.launchArgs,
15541
+ card: override.card
15250
15542
  });
15251
15543
  }
15252
15544
  function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
@@ -15267,6 +15559,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
15267
15559
  capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
15268
15560
  defaultHarness: normalizeManagedHarness2(normalizedRecord.defaultHarness, "claude"),
15269
15561
  harnessProfiles: normalizedRecord.harnessProfiles,
15562
+ ...normalizedRecord.card ? { card: normalizedRecord.card } : {},
15270
15563
  runtime: {
15271
15564
  cwd: normalizeProjectPath(normalizedRecord.cwd),
15272
15565
  harness: normalizeLocalAgentHarness(normalizedRecord.harness),
@@ -15276,6 +15569,245 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
15276
15569
  }
15277
15570
  };
15278
15571
  }
15572
+ function buildLocalAgentConfigState(agentId, record) {
15573
+ const harness = normalizeLocalAgentHarness(record.harness);
15574
+ const launchArgs = normalizeLaunchArgsForHarness(harness, record.launchArgs);
15575
+ return {
15576
+ agentId,
15577
+ editable: true,
15578
+ model: readLaunchModelForHarness(harness, launchArgs) ?? null,
15579
+ permissionProfile: record.permissionProfile ?? null,
15580
+ systemPrompt: record.systemPrompt || buildLocalAgentSystemPromptTemplate(),
15581
+ runtime: {
15582
+ cwd: compactHomePath(record.cwd),
15583
+ harness,
15584
+ transport: normalizeLocalAgentTransport(record.transport, harness),
15585
+ sessionId: record.tmuxSession,
15586
+ wakePolicy: "on_demand"
15587
+ },
15588
+ launchArgs,
15589
+ capabilities: normalizeLocalAgentCapabilities(record.capabilities),
15590
+ applyMode: "restart",
15591
+ templateHint: LOCAL_AGENT_SYSTEM_PROMPT_TEMPLATE_HINT
15592
+ };
15593
+ }
15594
+ async function assertDirectoryExists(directory) {
15595
+ const stats = await stat3(directory);
15596
+ if (!stats.isDirectory()) {
15597
+ throw new Error(`${directory} is not a directory.`);
15598
+ }
15599
+ }
15600
+ async function resolveConfiguredLocalAgentRecord(agentId) {
15601
+ const overrides = await readRelayAgentOverrides();
15602
+ const override = overrides[agentId];
15603
+ if (override) {
15604
+ return localAgentRecordFromRelayAgentOverride(agentId, override);
15605
+ }
15606
+ const registry = await readLocalAgentRegistry();
15607
+ const record = registry[agentId];
15608
+ return record ? normalizeLocalAgentRecord(agentId, record) : null;
15609
+ }
15610
+ async function getLocalAgentConfig(agentId) {
15611
+ const record = await resolveConfiguredLocalAgentRecord(agentId);
15612
+ if (!record) {
15613
+ return null;
15614
+ }
15615
+ return buildLocalAgentConfigState(agentId, record);
15616
+ }
15617
+ function defaultLocalAgentSessionId(agentId, harness) {
15618
+ return normalizeTmuxSessionName2(undefined, `${agentId}-${harness}`);
15619
+ }
15620
+ async function updateLocalAgentCard(agentId, input) {
15621
+ const existing = await getLocalAgentConfig(agentId);
15622
+ if (!existing) {
15623
+ return null;
15624
+ }
15625
+ const nextHarness = input.harness ? normalizeLocalAgentHarness(input.harness) : existing.runtime.harness;
15626
+ const harnessChanged = nextHarness !== existing.runtime.harness;
15627
+ const model = input.model === undefined ? harnessChanged ? null : undefined : input.model;
15628
+ return updateLocalAgentConfig(agentId, {
15629
+ runtime: {
15630
+ cwd: existing.runtime.cwd,
15631
+ harness: nextHarness,
15632
+ transport: harnessChanged ? normalizeLocalAgentTransport(undefined, nextHarness) : existing.runtime.transport,
15633
+ sessionId: harnessChanged ? defaultLocalAgentSessionId(agentId, nextHarness) : existing.runtime.sessionId
15634
+ },
15635
+ systemPrompt: existing.systemPrompt,
15636
+ launchArgs: harnessChanged ? [] : existing.launchArgs,
15637
+ model,
15638
+ reasoningEffort: input.reasoningEffort,
15639
+ permissionProfile: input.permissionProfile,
15640
+ capabilities: existing.capabilities
15641
+ });
15642
+ }
15643
+ async function updateLocalAgentConfig(agentId, input) {
15644
+ const [registry, overrides] = await Promise.all([
15645
+ readLocalAgentRegistry(),
15646
+ readRelayAgentOverrides()
15647
+ ]);
15648
+ const record = registry[agentId] ?? (overrides[agentId] ? localAgentRecordFromRelayAgentOverride(agentId, overrides[agentId]) : undefined);
15649
+ if (!record) {
15650
+ return null;
15651
+ }
15652
+ const cwd = normalizeProjectPath(input.runtime.cwd || record.cwd);
15653
+ await assertDirectoryExists(cwd);
15654
+ const nextHarness = normalizeLocalAgentHarness(input.runtime.harness);
15655
+ const nextTransport = normalizeLocalAgentTransport(input.runtime.transport ?? record.transport, nextHarness);
15656
+ let nextLaunchArgs = setLaunchModelForHarness(nextHarness, input.launchArgs, input.model);
15657
+ if (input.reasoningEffort !== undefined) {
15658
+ const stripped = stripLaunchReasoningEffortForHarness(nextHarness, nextLaunchArgs);
15659
+ const requestedReasoningEffort = normalizeRequestedReasoningEffort(input.reasoningEffort ?? undefined);
15660
+ nextLaunchArgs = requestedReasoningEffort ? [
15661
+ ...stripped,
15662
+ ...buildLaunchArgsForRequestedReasoningEffort(nextHarness, requestedReasoningEffort)
15663
+ ] : stripped;
15664
+ }
15665
+ const nextPermissionProfile = input.permissionProfile === undefined ? record.permissionProfile : input.permissionProfile === null ? undefined : parseScoutPermissionProfile(input.permissionProfile);
15666
+ const nextRecord = normalizeLocalAgentRecord(agentId, {
15667
+ ...record,
15668
+ cwd,
15669
+ tmuxSession: input.runtime.sessionId,
15670
+ harness: nextHarness,
15671
+ defaultHarness: nextHarness,
15672
+ harnessProfiles: {
15673
+ ...record.harnessProfiles ?? {},
15674
+ [normalizeManagedHarness2(input.runtime.harness, "claude")]: {
15675
+ cwd,
15676
+ transport: nextTransport,
15677
+ sessionId: normalizeTmuxSessionName2(input.runtime.sessionId, `${agentId}-${normalizeManagedHarness2(input.runtime.harness, "claude")}`),
15678
+ launchArgs: nextLaunchArgs,
15679
+ ...nextPermissionProfile ? { permissionProfile: nextPermissionProfile } : {}
15680
+ }
15681
+ },
15682
+ transport: nextTransport,
15683
+ systemPrompt: input.systemPrompt.trim() || undefined,
15684
+ launchArgs: nextLaunchArgs,
15685
+ permissionProfile: nextPermissionProfile,
15686
+ capabilities: input.capabilities
15687
+ });
15688
+ registry[agentId] = nextRecord;
15689
+ await writeLocalAgentRegistry(registry);
15690
+ return buildLocalAgentConfigState(agentId, nextRecord);
15691
+ }
15692
+ async function updateLocalAgentCardLifecycle(agentId, input) {
15693
+ const overrides = await readRelayAgentOverrides();
15694
+ const override = overrides[agentId];
15695
+ if (!override) {
15696
+ return null;
15697
+ }
15698
+ const lifecycle2 = normalizeLocalAgentCardLifecycle({
15699
+ ...override.card ?? {},
15700
+ ...input
15701
+ });
15702
+ if (!lifecycle2) {
15703
+ return null;
15704
+ }
15705
+ overrides[agentId] = {
15706
+ ...override,
15707
+ card: lifecycle2
15708
+ };
15709
+ await writeRelayAgentOverrides(overrides);
15710
+ return lifecycle2;
15711
+ }
15712
+ function oneTimeCardCreatedAt(lifecycle2) {
15713
+ return normalizeCardTimestamp(lifecycle2.createdAt) ?? 0;
15714
+ }
15715
+ function shouldPruneOneTimeCard(lifecycle2, now, maxAgeMs) {
15716
+ if (lifecycle2.kind !== "one_time") {
15717
+ return false;
15718
+ }
15719
+ if (lifecycle2.expiresAt !== undefined && lifecycle2.expiresAt <= now) {
15720
+ return true;
15721
+ }
15722
+ const createdAt = oneTimeCardCreatedAt(lifecycle2);
15723
+ return createdAt > 0 && createdAt + maxAgeMs <= now;
15724
+ }
15725
+ function oneTimeCardMatchesScope(agentId, override, input) {
15726
+ if (BUILT_IN_AGENT_DEFINITION_IDS.has(agentId)) {
15727
+ return false;
15728
+ }
15729
+ const lifecycle2 = normalizeLocalAgentCardLifecycle(override.card);
15730
+ if (lifecycle2?.kind !== "one_time") {
15731
+ return false;
15732
+ }
15733
+ if (input.createdById?.trim() && lifecycle2.createdById !== input.createdById.trim()) {
15734
+ return false;
15735
+ }
15736
+ if (input.projectRoot?.trim()) {
15737
+ const scopedRoot = normalizeProjectPath(input.projectRoot);
15738
+ const cardRoot = normalizeProjectPath(override.projectRoot || override.runtime?.cwd || ".");
15739
+ if (cardRoot !== scopedRoot) {
15740
+ return false;
15741
+ }
15742
+ }
15743
+ return true;
15744
+ }
15745
+ async function retireLocalAgentOverrides(overrides, agentIds) {
15746
+ const retired = [];
15747
+ for (const agentId of agentIds) {
15748
+ const override = overrides[agentId];
15749
+ if (!override) {
15750
+ continue;
15751
+ }
15752
+ const record = localAgentRecordFromRelayAgentOverride(agentId, override);
15753
+ const status = localAgentStatusFromRecord(agentId, record, localAgentStatusSource(agentId, overrides));
15754
+ await stopLocalAgent(agentId).catch(() => null);
15755
+ retired.push({
15756
+ ...status,
15757
+ isOnline: false
15758
+ });
15759
+ delete overrides[agentId];
15760
+ }
15761
+ if (retired.length > 0) {
15762
+ await writeRelayAgentOverrides(overrides);
15763
+ }
15764
+ return retired;
15765
+ }
15766
+ async function pruneOneTimeLocalAgentCards(input = {}) {
15767
+ const now = input.now ?? Date.now();
15768
+ const maxAgeMs = Math.max(0, input.maxAgeMs ?? DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_TTL_MS);
15769
+ const maxCount = Math.max(0, input.maxCount ?? DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_RETAIN);
15770
+ const excluded = new Set(input.excludeAgentIds ?? []);
15771
+ const overrides = await readRelayAgentOverrides();
15772
+ const candidates = Object.entries(overrides).filter(([agentId, override]) => !excluded.has(agentId) && oneTimeCardMatchesScope(agentId, override, input)).map(([agentId, override]) => ({
15773
+ agentId,
15774
+ override,
15775
+ lifecycle: normalizeLocalAgentCardLifecycle(override.card)
15776
+ })).sort((left, right) => oneTimeCardCreatedAt(right.lifecycle) - oneTimeCardCreatedAt(left.lifecycle) || left.agentId.localeCompare(right.agentId));
15777
+ const retireIds = new Set;
15778
+ for (const candidate of candidates) {
15779
+ if (shouldPruneOneTimeCard(candidate.lifecycle, now, maxAgeMs)) {
15780
+ retireIds.add(candidate.agentId);
15781
+ }
15782
+ }
15783
+ const retained = candidates.filter((candidate) => !retireIds.has(candidate.agentId));
15784
+ for (const candidate of retained.slice(maxCount)) {
15785
+ retireIds.add(candidate.agentId);
15786
+ }
15787
+ const retired = await retireLocalAgentOverrides({ ...overrides }, retireIds);
15788
+ return {
15789
+ inspected: candidates.length,
15790
+ remaining: Math.max(0, candidates.length - retired.length),
15791
+ retired
15792
+ };
15793
+ }
15794
+ async function retireLocalAgent(agentId) {
15795
+ const overrides = await readRelayAgentOverrides();
15796
+ const override = overrides[agentId];
15797
+ if (!override) {
15798
+ return null;
15799
+ }
15800
+ const record = localAgentRecordFromRelayAgentOverride(agentId, override);
15801
+ const status = localAgentStatusFromRecord(agentId, record, localAgentStatusSource(agentId, overrides));
15802
+ await stopLocalAgent(agentId).catch(() => null);
15803
+ const nextOverrides = { ...overrides };
15804
+ delete nextOverrides[agentId];
15805
+ await writeRelayAgentOverrides(nextOverrides);
15806
+ return {
15807
+ ...status,
15808
+ isOnline: false
15809
+ };
15810
+ }
15279
15811
  function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
15280
15812
  const permissionPosture = compileCodexPermissionProfile(record.permissionProfile);
15281
15813
  return {
@@ -15326,6 +15858,90 @@ function buildLocalAgentSystemPrompt(agentName, projectName, projectPath, option
15326
15858
  function buildLocalAgentInitialMessage(projectName, agentName) {
15327
15859
  return `You are now online as the ${agentName} relay agent for ${projectName}. Announce yourself on the relay with: ${brokerRelayCommand()} send --as ${agentName} "relay agent online \u2014 ready to assist with ${projectName}"`;
15328
15860
  }
15861
+ function tmuxDispatchSleep(ms) {
15862
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
15863
+ }
15864
+ function captureTmuxPaneTail(sessionName, lines) {
15865
+ try {
15866
+ return execFileSync4("tmux", ["capture-pane", "-p", "-t", sessionName, "-S", `-${lines}`, "-E", "-"], { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" });
15867
+ } catch {
15868
+ return "";
15869
+ }
15870
+ }
15871
+ async function waitForTmuxHarnessReady(sessionName, harness) {
15872
+ if (harness !== "claude") {
15873
+ return;
15874
+ }
15875
+ const deadline = Date.now() + TMUX_READY_TIMEOUT_MS;
15876
+ let paneTail = "";
15877
+ while (Date.now() < deadline) {
15878
+ if (!isLocalAgentSessionAlive(sessionName)) {
15879
+ throw new Error(`tmux session ${sessionName} exited before Claude Code was ready.`);
15880
+ }
15881
+ paneTail = captureTmuxPaneTail(sessionName, TMUX_READY_TAIL_LINES);
15882
+ if (tmuxPaneTailShowsReadyComposer(paneTail)) {
15883
+ return;
15884
+ }
15885
+ await tmuxDispatchSleep(TMUX_READY_POLL_MS);
15886
+ }
15887
+ const tail = stripTerminalControlSequences(paneTail).trim().split(/\r?\n/).slice(-20).join(`
15888
+ `).trim();
15889
+ throw new Error(`tmux session ${sessionName} did not show a ready Claude Code composer within ${TMUX_READY_TIMEOUT_MS}ms.` + (tail ? `
15890
+ Recent pane tail:
15891
+ ${tail}` : ""));
15892
+ }
15893
+ function tmuxPaneTailShowsReadyComposer(paneTail) {
15894
+ const cleanedTail = stripTerminalControlSequences(paneTail);
15895
+ const lines = cleanedTail.split(/\r?\n/);
15896
+ const anchor = findActiveTmuxComposerAnchor(lines);
15897
+ if (!anchor) {
15898
+ return false;
15899
+ }
15900
+ const afterComposerLines = [];
15901
+ for (const line of lines.slice(anchor.index + 1)) {
15902
+ if (isTmuxComposerBoundary(line)) {
15903
+ break;
15904
+ }
15905
+ afterComposerLines.push(line);
15906
+ }
15907
+ return !tmuxPaneTailShowsHarnessActivity(afterComposerLines.join(`
15908
+ `));
15909
+ }
15910
+ function stripTerminalControlSequences(value) {
15911
+ return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
15912
+ }
15913
+ function findActiveTmuxComposerAnchor(lines) {
15914
+ const inlineComposerIndex = findLastIndex(lines, (line) => /^\s*[\u276F\u203A]\s*/.test(line));
15915
+ const boxedComposerIndex = findLastIndex(lines, (line) => /^\s*[\u2502\u2503]\s*[>\u276F]\s*/.test(line));
15916
+ if (inlineComposerIndex < 0 && boxedComposerIndex < 0) {
15917
+ return null;
15918
+ }
15919
+ if (inlineComposerIndex > boxedComposerIndex) {
15920
+ return { index: inlineComposerIndex, kind: "inline" };
15921
+ }
15922
+ return { index: boxedComposerIndex, kind: "boxed" };
15923
+ }
15924
+ function findLastIndex(items, predicate) {
15925
+ for (let index = items.length - 1;index >= 0; index -= 1) {
15926
+ if (predicate(items[index])) {
15927
+ return index;
15928
+ }
15929
+ }
15930
+ return -1;
15931
+ }
15932
+ function isTmuxComposerBoundary(line) {
15933
+ const trimmed = line.trim();
15934
+ if (!trimmed) {
15935
+ return false;
15936
+ }
15937
+ if (/^[\u2500\u2501\u2550\u256D\u256E\u2570\u256F\u250C\u2510\u2514\u2518\u2554\u2557\u255A\u255D\u255F\u2562\u2560\u2563\u256A\u256B\u256C\u2569\u2566\u2564\u2567\u254C\u254D\u254E\u254F\s]+$/.test(trimmed)) {
15938
+ return true;
15939
+ }
15940
+ return /^--\s*(?:INSERT|NORMAL)\s*--/.test(trimmed) || /^(?:Opus|Sonnet|Haiku|Claude|Codex|GPT)\b/.test(trimmed);
15941
+ }
15942
+ function tmuxPaneTailShowsHarnessActivity(paneTail) {
15943
+ return /(?:^|\n)\s*(?:[\u23FA\u25CF\u273D\u2722\u273B\u23BF]|Bash\(|Read\(|Edit\(|Write\(|Grep\(|Glob\(|TodoWrite\()/.test(paneTail);
15944
+ }
15329
15945
  function shellQuoteArguments(args) {
15330
15946
  return args.map((arg) => JSON.stringify(arg)).join(" ");
15331
15947
  }
@@ -15336,10 +15952,10 @@ function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile
15336
15952
  const harness = normalizeLocalAgentHarness(record.harness);
15337
15953
  const extraArgs = shellQuoteArguments(harness === "claude" ? normalizeClaudeRuntimeLaunchArgs(record.launchArgs) : normalizeLaunchArgsForHarness(harness, record.launchArgs));
15338
15954
  if (harness === "codex") {
15339
- return `exec bash ${JSON.stringify(workerScript ?? join15(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
15955
+ return `exec bash ${JSON.stringify(workerScript ?? join16(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
15340
15956
  }
15341
15957
  if (harness === "pi") {
15342
- const sessionDir = join15(relayAgentRuntimeDirectory(agentName), "pi-sessions");
15958
+ const sessionDir = join16(relayAgentRuntimeDirectory(agentName), "pi-sessions");
15343
15959
  return [
15344
15960
  "pi",
15345
15961
  `--append-system-prompt "$(cat ${JSON.stringify(promptFile)})"`,
@@ -15409,7 +16025,7 @@ async function ensureLocalAgentOnline(agentName, record) {
15409
16025
  await mkdir6(agentRuntimeDir, { recursive: true });
15410
16026
  await mkdir6(logsDir, { recursive: true });
15411
16027
  if (normalizedRecord.transport === "codex_app_server") {
15412
- await writeFile6(join15(agentRuntimeDir, "prompt.txt"), systemPrompt);
16028
+ await writeFile6(join16(agentRuntimeDir, "prompt.txt"), systemPrompt);
15413
16029
  await ensureCodexAppServerAgentOnline(buildCodexAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
15414
16030
  const registry2 = await readLocalAgentRegistry();
15415
16031
  registry2[agentName] = {
@@ -15421,7 +16037,7 @@ async function ensureLocalAgentOnline(agentName, record) {
15421
16037
  return registry2[agentName];
15422
16038
  }
15423
16039
  if (normalizedRecord.transport === "claude_stream_json") {
15424
- await writeFile6(join15(agentRuntimeDir, "prompt.txt"), systemPrompt);
16040
+ await writeFile6(join16(agentRuntimeDir, "prompt.txt"), systemPrompt);
15425
16041
  await ensureClaudeStreamJsonAgentOnline(buildClaudeAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
15426
16042
  const registry2 = await readLocalAgentRegistry();
15427
16043
  registry2[agentName] = {
@@ -15434,17 +16050,17 @@ async function ensureLocalAgentOnline(agentName, record) {
15434
16050
  }
15435
16051
  const initialMessage = buildLocalAgentInitialMessage(projectName, agentName);
15436
16052
  const bootstrapPrompt = buildLocalAgentBootstrapPrompt(normalizeLocalAgentHarness(normalizedRecord.harness), systemPrompt, initialMessage);
15437
- const queueDirectory = join15(agentRuntimeDir, "queue");
15438
- const processingDirectory = join15(queueDirectory, "processing");
15439
- const processedDirectory = join15(queueDirectory, "processed");
15440
- const promptFile = join15(agentRuntimeDir, "prompt.txt");
15441
- const initialFile = join15(agentRuntimeDir, "initial.txt");
15442
- const launchScript = join15(agentRuntimeDir, "launch.sh");
15443
- const workerScript = join15(agentRuntimeDir, "codex-worker.sh");
15444
- const codexSessionIdFile = join15(agentRuntimeDir, "codex-session-id.txt");
15445
- const stateFile = join15(agentRuntimeDir, "state.json");
15446
- const stdoutLogFile = join15(logsDir, "stdout.log");
15447
- const stderrLogFile = join15(logsDir, "stderr.log");
16053
+ const queueDirectory = join16(agentRuntimeDir, "queue");
16054
+ const processingDirectory = join16(queueDirectory, "processing");
16055
+ const processedDirectory = join16(queueDirectory, "processed");
16056
+ const promptFile = join16(agentRuntimeDir, "prompt.txt");
16057
+ const initialFile = join16(agentRuntimeDir, "initial.txt");
16058
+ const launchScript = join16(agentRuntimeDir, "launch.sh");
16059
+ const workerScript = join16(agentRuntimeDir, "codex-worker.sh");
16060
+ const codexSessionIdFile = join16(agentRuntimeDir, "codex-session-id.txt");
16061
+ const stateFile = join16(agentRuntimeDir, "state.json");
16062
+ const stdoutLogFile = join16(logsDir, "stdout.log");
16063
+ const stderrLogFile = join16(logsDir, "stderr.log");
15448
16064
  await writeFile6(promptFile, systemPrompt);
15449
16065
  await writeFile6(initialFile, bootstrapPrompt);
15450
16066
  await writeFile6(stderrLogFile, "");
@@ -15509,11 +16125,17 @@ async function ensureLocalAgentOnline(agentName, record) {
15509
16125
  await new Promise((resolve9) => setTimeout(resolve9, 100));
15510
16126
  }
15511
16127
  if (!isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
15512
- const stderrTail = existsSync11(stderrLogFile) ? readFileSync6(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
16128
+ const stderrTail = existsSync12(stderrLogFile) ? readFileSync7(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
15513
16129
  `).trim() : "";
15514
16130
  throw new Error(stderrTail ? `Relay agent ${agentName} failed to stay online:
15515
16131
  ${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
15516
16132
  }
16133
+ try {
16134
+ await waitForTmuxHarnessReady(normalizedRecord.tmuxSession, normalizeLocalAgentHarness(normalizedRecord.harness));
16135
+ } catch (error) {
16136
+ killAgentSession(normalizedRecord.tmuxSession);
16137
+ throw error;
16138
+ }
15517
16139
  const registry = await readLocalAgentRegistry();
15518
16140
  registry[agentName] = {
15519
16141
  ...normalizedRecord,
@@ -15695,6 +16317,7 @@ async function startLocalAgent(input) {
15695
16317
  const effectiveCwd = input.cwdOverride ? normalizeProjectPath(input.cwdOverride) : undefined;
15696
16318
  const shouldEnsureOnline = input.ensureOnline !== false;
15697
16319
  const requestedPermissionProfile = parseScoutPermissionProfile(input.permissionProfile);
16320
+ const cardLifecycle = normalizeLocalAgentCardLifecycle(input.card);
15698
16321
  const requestedDefinitionId = input.agentName?.trim() ? normalizeAgentSelectorSegment(input.agentName.trim()) : "";
15699
16322
  if (input.agentName?.trim() && !requestedDefinitionId) {
15700
16323
  throw new Error(`Invalid agent name "${input.agentName}".`);
@@ -15744,7 +16367,8 @@ async function startLocalAgent(input) {
15744
16367
  const configDefinitionId = coldProjectConfig?.agent?.id?.trim() ? normalizeAgentSelectorSegment(coldProjectConfig.agent.id.trim()) : "";
15745
16368
  const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename7(projectRoot)) || "agent";
15746
16369
  const configDisplayName = coldProjectConfig?.agent?.displayName?.trim() || "";
15747
- const effectiveDisplayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
16370
+ const operatorAugmentDefaults = operatorAugmentDefaultsForDefinitionId(definitionId);
16371
+ const effectiveDisplayName = input.displayName || operatorAugmentDefaults?.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
15748
16372
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
15749
16373
  const configDefaultHarness = coldProjectConfig?.agent?.runtime?.defaultHarness;
15750
16374
  const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
@@ -15767,8 +16391,10 @@ async function startLocalAgent(input) {
15767
16391
  projectConfigPath: coldProjectConfigPath,
15768
16392
  source: "manual",
15769
16393
  startedAt: nowSeconds2(),
16394
+ ...operatorAugmentDefaults ? { systemPrompt: operatorAugmentDefaults.systemPrompt } : {},
15770
16395
  launchArgs,
15771
16396
  defaultHarness: effectiveHarness,
16397
+ ...cardLifecycle ? { card: cardLifecycle } : {},
15772
16398
  harnessProfiles: {
15773
16399
  [effectiveHarness]: {
15774
16400
  cwd: effectiveCwd ?? projectRoot,
@@ -15804,18 +16430,21 @@ async function startLocalAgent(input) {
15804
16430
  reasoningEffort: input.reasoningEffort
15805
16431
  });
15806
16432
  const nextTransport = normalizeLocalAgentTransport(preferredHarness ? undefined : matchingOverride.runtime?.transport, nextHarness);
16433
+ const operatorAugmentDefaults = operatorAugmentDefaultsForDefinitionId(requestedDefinitionId);
15807
16434
  overrides[instance.id] = {
15808
16435
  agentId: instance.id,
15809
16436
  definitionId: requestedDefinitionId,
15810
- displayName: input.displayName || titleCaseLocalAgentName(requestedDefinitionId),
16437
+ displayName: input.displayName || operatorAugmentDefaults?.displayName || titleCaseLocalAgentName(requestedDefinitionId),
15811
16438
  projectName: matchingOverride.projectName ?? basename7(matchingProjectRoot),
15812
16439
  projectRoot: matchingProjectRoot,
15813
16440
  projectConfigPath: null,
15814
16441
  source: "manual",
15815
16442
  startedAt: matchingOverride.startedAt ?? nowSeconds2(),
16443
+ ...operatorAugmentDefaults ? { systemPrompt: operatorAugmentDefaults.systemPrompt } : {},
15816
16444
  launchArgs: nextHarness === nextDefaultHarness ? nextLaunchArgs : matchingOverride.launchArgs,
15817
16445
  capabilities: matchingOverride.capabilities,
15818
16446
  defaultHarness: nextDefaultHarness,
16447
+ ...cardLifecycle ? { card: cardLifecycle } : {},
15819
16448
  harnessProfiles: {
15820
16449
  ...matchingOverride.harnessProfiles ?? {},
15821
16450
  [nextHarness]: {
@@ -15837,7 +16466,9 @@ async function startLocalAgent(input) {
15837
16466
  await writeRelayAgentOverrides(overrides);
15838
16467
  targetAgentId = instance.id;
15839
16468
  } else {
15840
- if (input.model || input.reasoningEffort || requestedPermissionProfile) {
16469
+ const operatorAugmentDefaults = operatorAugmentDefaultsForDefinitionId(matchingOverride.definitionId ?? requestedDefinitionId);
16470
+ const shouldApplyOperatorAugmentPrompt = Boolean(operatorAugmentDefaults && !matchingOverride.systemPrompt?.trim());
16471
+ if (input.model || input.reasoningEffort || requestedPermissionProfile || cardLifecycle || shouldApplyOperatorAugmentPrompt) {
15841
16472
  const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
15842
16473
  const nextLaunchArgs = applyRequestedRuntimeOptionsToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), {
15843
16474
  model: input.model,
@@ -15845,6 +16476,8 @@ async function startLocalAgent(input) {
15845
16476
  });
15846
16477
  overrides[matchingAgentId] = {
15847
16478
  ...matchingOverride,
16479
+ ...cardLifecycle ? { card: cardLifecycle } : {},
16480
+ ...shouldApplyOperatorAugmentPrompt ? { systemPrompt: operatorAugmentDefaults.systemPrompt } : {},
15848
16481
  launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
15849
16482
  harnessProfiles: {
15850
16483
  ...matchingOverride.harnessProfiles ?? {},
@@ -15949,9 +16582,9 @@ async function restartAllLocalAgents(options = {}) {
15949
16582
  function readPersistedClaudeSessionId(agentName) {
15950
16583
  try {
15951
16584
  const runtimeDir = relayAgentRuntimeDirectory(agentName);
15952
- const catalogPath = join15(runtimeDir, "session-catalog.json");
15953
- if (existsSync11(catalogPath)) {
15954
- const raw = readFileSync6(catalogPath, "utf8").trim();
16585
+ const catalogPath = join16(runtimeDir, "session-catalog.json");
16586
+ if (existsSync12(catalogPath)) {
16587
+ const raw = readFileSync7(catalogPath, "utf8").trim();
15955
16588
  if (raw) {
15956
16589
  const catalog = JSON.parse(raw);
15957
16590
  if (typeof catalog.activeSessionId === "string" && catalog.activeSessionId.trim()) {
@@ -15959,9 +16592,9 @@ function readPersistedClaudeSessionId(agentName) {
15959
16592
  }
15960
16593
  }
15961
16594
  }
15962
- const legacyPath = join15(runtimeDir, "claude-session-id.txt");
15963
- if (existsSync11(legacyPath)) {
15964
- const value = readFileSync6(legacyPath, "utf8").trim();
16595
+ const legacyPath = join16(runtimeDir, "claude-session-id.txt");
16596
+ if (existsSync12(legacyPath)) {
16597
+ const value = readFileSync7(legacyPath, "utf8").trim();
15965
16598
  return value || null;
15966
16599
  }
15967
16600
  return null;
@@ -15973,6 +16606,7 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
15973
16606
  const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
15974
16607
  const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
15975
16608
  const codexPermissionPosture = normalizeLocalAgentHarness(normalizedRecord.harness) === "codex" ? compileCodexPermissionProfile(normalizedRecord.permissionProfile) : null;
16609
+ const cardLifecycle = normalizeLocalAgentCardLifecycle(normalizedRecord.card);
15976
16610
  const definitionId = normalizedRecord.definitionId ?? agentId;
15977
16611
  const displayName = titleCaseLocalAgentName(definitionId);
15978
16612
  const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
@@ -16000,6 +16634,7 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
16000
16634
  workspaceQualifier: instance.workspaceQualifier,
16001
16635
  branch: instance.branch,
16002
16636
  ...configuredModel ? { model: configuredModel } : {},
16637
+ ...cardLifecycle ? { cardLifecycle } : {},
16003
16638
  ...codexPermissionPosture ? {
16004
16639
  permissionProfile: codexPermissionPosture.profile,
16005
16640
  approvalPolicy: codexPermissionPosture.approvalPolicy,
@@ -16035,6 +16670,7 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
16035
16670
  workspaceQualifier: instance.workspaceQualifier,
16036
16671
  branch: instance.branch,
16037
16672
  ...configuredModel ? { model: configuredModel } : {},
16673
+ ...cardLifecycle ? { cardLifecycle } : {},
16038
16674
  ...codexPermissionPosture ? {
16039
16675
  permissionProfile: codexPermissionPosture.profile,
16040
16676
  approvalPolicy: codexPermissionPosture.approvalPolicy,
@@ -16075,6 +16711,7 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
16075
16711
  workspaceQualifier: instance.workspaceQualifier,
16076
16712
  branch: instance.branch,
16077
16713
  ...configuredModel ? { model: configuredModel } : {},
16714
+ ...cardLifecycle ? { cardLifecycle } : {},
16078
16715
  ...codexPermissionPosture ? {
16079
16716
  permissionProfile: codexPermissionPosture.profile,
16080
16717
  approvalPolicy: codexPermissionPosture.approvalPolicy,
@@ -16161,9 +16798,10 @@ async function ensureLocalAgentBindingOnline(agentId, nodeId, options = {}) {
16161
16798
  const onlineRecord = await ensureLocalAgentOnline(agentId, recordForHarness(localAgentRecordFromRelayAgentOverride(agentId, override), options.harness));
16162
16799
  return buildLocalAgentBinding(agentId, onlineRecord, isLocalAgentRecordOnline(agentId, onlineRecord), nodeId, "relay-agent-registry");
16163
16800
  }
16164
- var MODULE_DIRECTORY, OPENSCOUT_REPO_ROOT, DEFAULT_LOCAL_AGENT_CAPABILITIES, DEFAULT_LOCAL_AGENT_HARNESS = "claude", SUPPORTED_LOCAL_AGENT_HARNESSES, SUPPORTED_SCOUT_HARNESSES, DEFAULT_CLAUDE_SCOUT_ALLOWED_TOOLS;
16801
+ var MODULE_DIRECTORY, OPENSCOUT_REPO_ROOT, DEFAULT_LOCAL_AGENT_CAPABILITIES, DEFAULT_LOCAL_AGENT_HARNESS = "claude", DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_TTL_MS, DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_RETAIN = 24, SUPPORTED_LOCAL_AGENT_HARNESSES, SUPPORTED_SCOUT_HARNESSES, DEFAULT_CLAUDE_SCOUT_ALLOWED_TOOLS, TMUX_READY_TIMEOUT_MS = 20000, TMUX_READY_POLL_MS = 250, TMUX_READY_TAIL_LINES = 80;
16165
16802
  var init_local_agents = __esm(() => {
16166
16803
  init_src();
16804
+ init_dispatch_stalled();
16167
16805
  init_claude_stream_json();
16168
16806
  init_codex_app_server();
16169
16807
  init_setup();
@@ -16173,9 +16811,11 @@ var init_local_agents = __esm(() => {
16173
16811
  init_local_agent_template();
16174
16812
  init_permission_policy2();
16175
16813
  init_requester_timeout();
16176
- MODULE_DIRECTORY = dirname8(fileURLToPath6(import.meta.url));
16814
+ init_user_config();
16815
+ MODULE_DIRECTORY = dirname9(fileURLToPath6(import.meta.url));
16177
16816
  OPENSCOUT_REPO_ROOT = resolve8(MODULE_DIRECTORY, "..", "..", "..");
16178
16817
  DEFAULT_LOCAL_AGENT_CAPABILITIES = ["chat", "invoke", "deliver"];
16818
+ DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_TTL_MS = 24 * 60 * 60 * 1000;
16179
16819
  SUPPORTED_LOCAL_AGENT_HARNESSES = ["claude", "codex", "pi"];
16180
16820
  SUPPORTED_SCOUT_HARNESSES = [
16181
16821
  ...SUPPORTED_LOCAL_AGENT_HARNESSES,
@@ -16190,8 +16830,8 @@ var init_local_agents = __esm(() => {
16190
16830
  "mcp__scout__agents_search",
16191
16831
  "mcp__scout__agents_resolve",
16192
16832
  "mcp__scout__messages_reply",
16833
+ "mcp__scout__ask",
16193
16834
  "mcp__scout__messages_send",
16194
- "mcp__scout__invocations_ask",
16195
16835
  "mcp__scout__invocations_get",
16196
16836
  "mcp__scout__invocations_wait",
16197
16837
  "mcp__scout__work_update",
@@ -16214,6 +16854,9 @@ function scoutBrokerInvocationPath(invocationId) {
16214
16854
  function scoutBrokerInvocationStreamPath(invocationId) {
16215
16855
  return `${scoutBrokerInvocationPath(invocationId)}/stream`;
16216
16856
  }
16857
+ function scoutBrokerInvocationLifecyclePath(invocationId) {
16858
+ return `${scoutBrokerInvocationPath(invocationId)}/lifecycle`;
16859
+ }
16217
16860
  var scoutBrokerPaths, openAiAudioSpeechUrl = "https://api.openai.com/v1/audio/speech";
16218
16861
  var init_paths = __esm(() => {
16219
16862
  scoutBrokerPaths = {
@@ -16256,35 +16899,6 @@ function buildScoutAskMetadata(input) {
16256
16899
  };
16257
16900
  }
16258
16901
 
16259
- // ../runtime/src/user-config.ts
16260
- import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
16261
- import { homedir as homedir12 } from "os";
16262
- import { dirname as dirname9, join as join16 } from "path";
16263
- function userConfigPath() {
16264
- return join16(process.env.OPENSCOUT_HOME ?? join16(homedir12(), ".openscout"), "user.json");
16265
- }
16266
- function loadUserConfig() {
16267
- const configPath = userConfigPath();
16268
- if (!existsSync12(configPath))
16269
- return {};
16270
- try {
16271
- return JSON.parse(readFileSync7(configPath, "utf8"));
16272
- } catch {
16273
- return {};
16274
- }
16275
- }
16276
- function saveUserConfig(config) {
16277
- const configPath = userConfigPath();
16278
- mkdirSync3(dirname9(configPath), { recursive: true });
16279
- writeFileSync3(configPath, JSON.stringify(config, null, 2) + `
16280
- `, "utf8");
16281
- }
16282
- function resolveOperatorName() {
16283
- const config = loadUserConfig();
16284
- return config.name?.trim() || process.env.OPENSCOUT_OPERATOR_NAME?.trim() || process.env.USER?.trim() || "operator";
16285
- }
16286
- var init_user_config = () => {};
16287
-
16288
16902
  // ../../apps/desktop/src/core/broker/sender.ts
16289
16903
  import { basename as basename8 } from "path";
16290
16904
  function resolveScoutAgentName(agentName) {
@@ -16449,7 +17063,7 @@ function buildScoutReturnAddress2(snapshot, actorId, options = {}) {
16449
17063
  replyToMessageId: options.replyToMessageId,
16450
17064
  nodeId: endpoint?.nodeId || agent?.authorityNodeId || agent?.homeNodeId,
16451
17065
  projectRoot,
16452
- sessionId: endpoint?.sessionId
17066
+ sessionId: options.sessionId ?? endpoint?.sessionId
16453
17067
  });
16454
17068
  }
16455
17069
  function sanitizeConversationSegment(value) {
@@ -16562,11 +17176,7 @@ function isSupersededBrokerAgent(snapshot, agentId) {
16562
17176
  if (!agent) {
16563
17177
  return false;
16564
17178
  }
16565
- if (!metadataBoolean(agent.metadata, "staleLocalRegistration")) {
16566
- return false;
16567
- }
16568
- const replacementAgentId = metadataString2(agent.metadata, "replacedByAgentId");
16569
- return Boolean(replacementAgentId && snapshot.agents[replacementAgentId]);
17179
+ return metadataBoolean(agent.metadata, "retiredFromFleet") || metadataBoolean(agent.metadata, "staleLocalRegistration");
16570
17180
  }
16571
17181
  function isBuiltInBrokerAgent(snapshot, agentId) {
16572
17182
  const agent = snapshot.agents[agentId];
@@ -16677,6 +17287,17 @@ async function readScoutBrokerHealth(baseUrl = resolveScoutBrokerUrl()) {
16677
17287
  nodes: health.counts.nodes ?? 0,
16678
17288
  actors: health.counts.actors ?? 0,
16679
17289
  agents: health.counts.agents ?? 0,
17290
+ agentRecords: health.counts.agentRecords,
17291
+ rawAgentRecords: health.counts.rawAgentRecords,
17292
+ configuredAgents: health.counts.configuredAgents,
17293
+ scoutManagedAgents: health.counts.scoutManagedAgents,
17294
+ currentAgentRegistrations: health.counts.currentAgentRegistrations,
17295
+ localAgentRegistrations: health.counts.localAgentRegistrations,
17296
+ remoteAgentRegistrations: health.counts.remoteAgentRegistrations,
17297
+ staleAgentRegistrations: health.counts.staleAgentRegistrations,
17298
+ retiredAgentRegistrations: health.counts.retiredAgentRegistrations,
17299
+ oneTimeAgentCards: health.counts.oneTimeAgentCards,
17300
+ persistentAgentCards: health.counts.persistentAgentCards,
16680
17301
  conversations: health.counts.conversations ?? 0,
16681
17302
  messages: health.counts.messages ?? 0,
16682
17303
  flights: health.counts.flights ?? 0,
@@ -17134,7 +17755,7 @@ async function resolveMentionTargets(snapshot, text, currentDirectory) {
17134
17755
  const ambiguous = [];
17135
17756
  const candidateMap = new Map;
17136
17757
  const endpointBackedAgentIds = [
17137
- ...new Set(Object.values(snapshot.endpoints).map((endpoint) => endpoint.agentId).filter((agentId) => agentId && agentId !== OPERATOR_ID))
17758
+ ...new Set(Object.values(snapshot.endpoints).map((endpoint) => endpoint.agentId).filter((agentId) => agentId && agentId !== OPERATOR_ID && !isSupersededBrokerAgent(snapshot, agentId)))
17138
17759
  ];
17139
17760
  for (const agent of Object.values(snapshot.agents)) {
17140
17761
  if (isSupersededBrokerAgent(snapshot, agent.id)) {
@@ -17306,6 +17927,48 @@ async function registerScoutLocalAgentBinding(input) {
17306
17927
  brokerRegistered: Boolean(broker)
17307
17928
  };
17308
17929
  }
17930
+ async function retireScoutLocalAgentBinding(input) {
17931
+ const broker = input.broker ?? await loadScoutBrokerContext();
17932
+ if (!broker) {
17933
+ return false;
17934
+ }
17935
+ const retiredAt = Date.now();
17936
+ let retired = false;
17937
+ const agent = broker.snapshot.agents[input.agentId];
17938
+ if (agent) {
17939
+ const nextAgent = {
17940
+ ...agent,
17941
+ metadata: {
17942
+ ...agent.metadata ?? {},
17943
+ retiredFromFleet: true,
17944
+ retiredAt
17945
+ }
17946
+ };
17947
+ await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.agents, nextAgent);
17948
+ broker.snapshot.agents[input.agentId] = nextAgent;
17949
+ retired = true;
17950
+ }
17951
+ for (const endpoint of Object.values(broker.snapshot.endpoints)) {
17952
+ if (endpoint.agentId !== input.agentId) {
17953
+ continue;
17954
+ }
17955
+ const nextEndpoint = {
17956
+ ...endpoint,
17957
+ state: "offline",
17958
+ metadata: {
17959
+ ...endpoint.metadata ?? {},
17960
+ retiredFromFleet: true,
17961
+ retiredAt,
17962
+ lastError: "local agent card retired",
17963
+ lastFailedAt: retiredAt
17964
+ }
17965
+ };
17966
+ await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.endpoints, nextEndpoint);
17967
+ broker.snapshot.endpoints[endpoint.id] = nextEndpoint;
17968
+ retired = true;
17969
+ }
17970
+ return retired;
17971
+ }
17309
17972
  async function resolveConversationActorId(baseUrl, snapshot, nodeId, actorId, currentDirectory, displayName) {
17310
17973
  const normalized = actorId.trim() || OPERATOR_ID;
17311
17974
  if (snapshot.agents[normalized] || snapshot.actors[normalized]) {
@@ -18123,6 +18786,11 @@ async function attachScoutManagedLocalSession(input) {
18123
18786
  async function sendScoutDirectMessage(input) {
18124
18787
  const broker = await requireScoutBrokerContext();
18125
18788
  const source = input.source?.trim() || "scout-mobile";
18789
+ const targetEndpoints = Object.values(broker.snapshot.endpoints ?? {}).filter((endpoint) => endpoint.agentId === input.agentId);
18790
+ const targetIsStale = isSupersededBrokerAgent(broker.snapshot, input.agentId) || targetEndpoints.length > 0 && targetEndpoints.every((endpoint) => metadataBoolean(endpoint.metadata, "retiredFromFleet") || metadataBoolean(endpoint.metadata, "staleLocalRegistration"));
18791
+ if (targetIsStale) {
18792
+ throw new Error(`${displayNameForBrokerActor(broker.snapshot, input.agentId)} is a stale local registration. Start the current project session from Workspaces before sending.`);
18793
+ }
18126
18794
  const delivery = await brokerPostDeliver(broker.baseUrl, {
18127
18795
  caller: {
18128
18796
  actorId: OPERATOR_ID,
@@ -18185,6 +18853,41 @@ async function askScoutAgentById(input) {
18185
18853
  workspace: input.workspace,
18186
18854
  senderContext: input.senderContext,
18187
18855
  labels: input.labels,
18856
+ replyToSessionId: input.replyToSessionId,
18857
+ currentDirectory: input.currentDirectory,
18858
+ source: input.source ?? "scout-mcp"
18859
+ });
18860
+ return {
18861
+ usedBroker: result.usedBroker,
18862
+ flight: result.flight,
18863
+ conversationId: result.conversationId,
18864
+ messageId: result.messageId,
18865
+ workItem: result.workItem,
18866
+ unresolvedTargetId: result.unresolvedTarget,
18867
+ targetDiagnostic: result.targetDiagnostic
18868
+ };
18869
+ }
18870
+ async function askScoutSessionById(input) {
18871
+ const targetSessionId = input.targetSessionId.trim();
18872
+ const result = await deliverScoutAsk({
18873
+ senderId: input.senderId,
18874
+ target: {
18875
+ kind: "session_id",
18876
+ sessionId: targetSessionId
18877
+ },
18878
+ targetLabel: `session:${targetSessionId}`,
18879
+ targetSessionId,
18880
+ body: input.body,
18881
+ workItem: input.workItem,
18882
+ channel: input.channel,
18883
+ shouldSpeak: input.shouldSpeak,
18884
+ createdAtMs: input.createdAtMs,
18885
+ executionHarness: input.executionHarness,
18886
+ executionSession: "existing",
18887
+ workspace: input.workspace,
18888
+ senderContext: input.senderContext,
18889
+ labels: input.labels,
18890
+ replyToSessionId: input.replyToSessionId,
18188
18891
  currentDirectory: input.currentDirectory,
18189
18892
  source: input.source ?? "scout-mcp"
18190
18893
  });
@@ -18204,6 +18907,8 @@ function renderedScoutAskTarget(target) {
18204
18907
  return target.agentId.trim();
18205
18908
  case "agent_label":
18206
18909
  return target.label.trim();
18910
+ case "session_id":
18911
+ return `session:${target.sessionId.trim()}`;
18207
18912
  case "binding_ref":
18208
18913
  return `ref:${target.ref.trim()}`;
18209
18914
  case "project_path":
@@ -18217,8 +18922,11 @@ function renderedScoutAskTarget(target) {
18217
18922
  function directAgentIdForAskTarget(target) {
18218
18923
  return target.kind === "agent_id" ? target.agentId.trim() : undefined;
18219
18924
  }
18925
+ function directSessionIdForAskTarget(target) {
18926
+ return target.kind === "session_id" ? target.sessionId.trim() : undefined;
18927
+ }
18220
18928
  function defaultAskExecutionSession(target) {
18221
- return target.kind === "binding_ref" ? "existing" : "new";
18929
+ return target.kind === "binding_ref" || target.kind === "session_id" ? "existing" : "new";
18222
18930
  }
18223
18931
  async function deliverScoutAsk(input) {
18224
18932
  const broker = await loadScoutBrokerContext();
@@ -18235,6 +18943,7 @@ async function deliverScoutAsk(input) {
18235
18943
  return { usedBroker: true, unresolvedTarget: renderedTarget };
18236
18944
  }
18237
18945
  const labels = normalizeWorkItemLabels(input.labels);
18946
+ const targetSessionId = directSessionIdForAskTarget(input.target) ?? input.targetSessionId?.trim();
18238
18947
  const workRecordId = input.workItem ? buildScoutEntityId("work", createdAtMs) : undefined;
18239
18948
  const deliveryWorkItem = input.workItem && workRecordId ? { id: workRecordId, ...input.workItem } : undefined;
18240
18949
  const askMetadata = buildScoutAskMetadata({
@@ -18244,6 +18953,12 @@ async function deliverScoutAsk(input) {
18244
18953
  workspace: input.workspace,
18245
18954
  labels
18246
18955
  });
18956
+ const replyToSessionId = input.replyToSessionId?.trim();
18957
+ const deliveryMetadata = targetSessionId || replyToSessionId ? {
18958
+ ...askMetadata,
18959
+ ...targetSessionId ? { targetSessionId } : {},
18960
+ ...replyToSessionId ? { replyToSessionId } : {}
18961
+ } : askMetadata;
18247
18962
  const delivery = await brokerPostDeliver(broker.baseUrl, {
18248
18963
  caller: {
18249
18964
  actorId: senderId,
@@ -18253,7 +18968,9 @@ async function deliverScoutAsk(input) {
18253
18968
  },
18254
18969
  target: input.target,
18255
18970
  ...targetAgentId ? { targetAgentId } : {},
18971
+ ...targetSessionId ? { targetSessionId } : {},
18256
18972
  targetLabel: renderedTarget,
18973
+ ...replyToSessionId ? { replyToSessionId } : {},
18257
18974
  body: input.body.trim(),
18258
18975
  intent: "consult",
18259
18976
  channel: input.channel,
@@ -18261,12 +18978,13 @@ async function deliverScoutAsk(input) {
18261
18978
  ...deliveryWorkItem ? { collaborationRecordId: workRecordId, workItem: deliveryWorkItem } : {},
18262
18979
  execution: {
18263
18980
  ...input.executionHarness ? { harness: input.executionHarness } : {},
18981
+ ...targetSessionId ? { targetSessionId } : {},
18264
18982
  session: input.executionSession ?? defaultAskExecutionSession(input.target)
18265
18983
  },
18266
18984
  ensureAwake: true,
18267
18985
  ...labels ? { labels } : {},
18268
- messageMetadata: askMetadata,
18269
- invocationMetadata: askMetadata
18986
+ messageMetadata: deliveryMetadata,
18987
+ invocationMetadata: deliveryMetadata
18270
18988
  });
18271
18989
  if (delivery.kind !== "delivery") {
18272
18990
  return {
@@ -18311,6 +19029,8 @@ async function askScoutQuestion(input) {
18311
19029
  workspace: input.workspace,
18312
19030
  senderContext: input.senderContext,
18313
19031
  labels: input.labels,
19032
+ targetSessionId: input.targetSessionId,
19033
+ replyToSessionId: input.replyToSessionId,
18314
19034
  currentDirectory: input.currentDirectory,
18315
19035
  source: input.source ?? "scout-cli"
18316
19036
  });
@@ -18329,6 +19049,16 @@ async function loadScoutInvocationSnapshot(baseUrl, invocationId) {
18329
19049
  }
18330
19050
  return snapshot;
18331
19051
  }
19052
+ async function loadScoutInvocationLifecycle(baseUrl, invocationId) {
19053
+ try {
19054
+ return await brokerReadJson(baseUrl, scoutBrokerInvocationLifecyclePath(invocationId));
19055
+ } catch (error) {
19056
+ if (error instanceof Error && error.message.includes(`${scoutBrokerInvocationLifecyclePath(invocationId)} returned 404`)) {
19057
+ return null;
19058
+ }
19059
+ throw error;
19060
+ }
19061
+ }
18332
19062
  async function resolveScoutWaitReference(baseUrl, input) {
18333
19063
  const original = input.trim();
18334
19064
  const normalized = normalizeScoutWaitRef(original);
@@ -18662,6 +19392,18 @@ async function readScoutBrokerFeed(options) {
18662
19392
  return null;
18663
19393
  }
18664
19394
  }
19395
+ async function markScoutConversationRead(options = {}) {
19396
+ const broker = await requireScoutBrokerContext(options.baseUrl);
19397
+ const conversationId = options.conversationId ?? scoutConversationIdForChannel(options.channel);
19398
+ return brokerPostJson(broker.baseUrl, `/v1/conversations/${encodeURIComponent(conversationId)}/read-cursors`, {
19399
+ actorId: options.actorId,
19400
+ readerNodeId: options.readerNodeId ?? broker.node.id,
19401
+ lastReadMessageId: options.lastReadMessageId,
19402
+ lastReadSeq: options.lastReadSeq,
19403
+ lastReadAt: options.lastReadAt,
19404
+ metadata: options.metadata
19405
+ });
19406
+ }
18665
19407
  async function loadScoutActivityItems(options = {}) {
18666
19408
  const search = new URLSearchParams;
18667
19409
  if (options.agentId)
@@ -18793,9 +19535,20 @@ async function loadConfiguredAgentIds() {
18793
19535
  return new Set;
18794
19536
  }
18795
19537
  }
19538
+ async function loadDiscoveredAgentMap(currentDirectory) {
19539
+ try {
19540
+ const setup = await loadResolvedRelayAgents({ currentDirectory });
19541
+ return new Map(setup.discoveredAgents.map((agent) => [agent.agentId, agent]));
19542
+ } catch {
19543
+ return new Map;
19544
+ }
19545
+ }
18796
19546
  async function listScoutAgents(options = {}) {
18797
19547
  const broker = await requireScoutBrokerContext();
18798
- const configuredAgentIds = await loadConfiguredAgentIds();
19548
+ const [configuredAgentIds, discoveredAgents] = await Promise.all([
19549
+ loadConfiguredAgentIds(),
19550
+ loadDiscoveredAgentMap(options.currentDirectory ?? process.cwd())
19551
+ ]);
18799
19552
  const endpointsByAgent = new Map;
18800
19553
  const messageStats = new Map;
18801
19554
  for (const endpoint of Object.values(broker.snapshot.endpoints ?? {})) {
@@ -18832,12 +19585,13 @@ async function listScoutAgents(options = {}) {
18832
19585
  ...Object.keys(broker.snapshot.agents ?? {}),
18833
19586
  ...Array.from(endpointsByAgent.keys()),
18834
19587
  ...Array.from(messageStats.keys()),
18835
- ...Array.from(configuredAgentIds.values())
19588
+ ...Array.from(configuredAgentIds.values()),
19589
+ ...Array.from(discoveredAgents.keys())
18836
19590
  ])
18837
19591
  ].filter((agentId) => agentId && agentId !== OPERATOR_ID).filter((agentId) => !isBuiltInBrokerAgent(broker.snapshot, agentId)).filter((agentId) => !isSupersededBrokerAgent(broker.snapshot, agentId)).map((agentId) => {
18838
19592
  const endpoints = endpointsByAgent.get(agentId) ?? [];
18839
19593
  const brokerMessages = messageStats.get(agentId);
18840
- const registrationKind = configuredAgentIds.has(agentId) ? "configured" : "broker";
19594
+ const registrationKind = discoveredAgents.get(agentId)?.registrationKind ?? (configuredAgentIds.has(agentId) ? "configured" : "broker");
18841
19595
  const state = whoEntryState(endpoints, registrationKind);
18842
19596
  const lastSeen = maxDefined([
18843
19597
  brokerMessages?.lastSeen,
@@ -19099,6 +19853,8 @@ function renderedAskTarget(target) {
19099
19853
  return target.agentId;
19100
19854
  case "agent_label":
19101
19855
  return target.label;
19856
+ case "session_id":
19857
+ return `session:${target.sessionId}`;
19102
19858
  case "binding_ref":
19103
19859
  return `ref:${target.ref}`;
19104
19860
  case "project_path":
@@ -19120,6 +19876,9 @@ function askTargetFor(to) {
19120
19876
  if (parsed.kind === "binding_ref") {
19121
19877
  return { kind: "binding_ref", ref: parsed.ref };
19122
19878
  }
19879
+ if (parsed.kind === "session_id") {
19880
+ return { kind: "session_id", sessionId: parsed.sessionId };
19881
+ }
19123
19882
  if (parsed.kind === "agent_label") {
19124
19883
  return { kind: "agent_label", label: parsed.label };
19125
19884
  }
@@ -19200,6 +19959,7 @@ var scoutAskHandler = async (command) => {
19200
19959
  body: command.body,
19201
19960
  workItem: command.workItem,
19202
19961
  labels: command.labels,
19962
+ replyToSessionId: command.replyToSessionId,
19203
19963
  channel: command.channel,
19204
19964
  shouldSpeak: command.shouldSpeak,
19205
19965
  executionHarness: command.harness,
@@ -19239,11 +19999,14 @@ function renderAskCommandHelp() {
19239
19999
  " one target + no channel -> DM",
19240
20000
  " --channel <name> -> named group thread",
19241
20001
  " short @name -> must resolve to exactly one routable agent",
20002
+ " --project <path> -> ask by repo/workspace path; Scout resolves the concrete agent/session",
20003
+ " '>> project:<path> ...' -> composer route form for the same project-path ask",
19242
20004
  "",
19243
20005
  'Use ask when the meaning is "do this and get back to me."',
19244
20006
  "The command creates durable broker work; the target should acknowledge quickly in the same DM or channel.",
19245
20007
  `Default inline mode returns once the target has acknowledged, completed immediately, or stays unacknowledged for ${DEFAULT_ASK_ACK_TIMEOUT_SECONDS}s.`,
19246
20008
  "Use the flight id, conversation, notify mode, or an explicit wait to follow the final completion.",
20009
+ "Use --project when you know the project path but do not want to look up or pin an agent id first.",
19247
20010
  "",
19248
20011
  "Input:",
19249
20012
  " inline message -> primary prompt body",
@@ -19254,6 +20017,8 @@ function renderAskCommandHelp() {
19254
20017
  ' scout ask --to hudson "review the parser"',
19255
20018
  ' scout ask --ref 7f3a9c21 "continue from that result"',
19256
20019
  ' scout ask --project ../talkie "how did you handle auth?"',
20020
+ ' scout ask --project ../talkie --harness codex "review this from a fresh Codex session"',
20021
+ " scout ask '>> project:../talkie compare auth against this branch'",
19257
20022
  " scout ask --to hudson --prompt-file ./handoff.md",
19258
20023
  ' scout ask --as premotion.master.mini --to hudson "build the editor"',
19259
20024
  ' scout ask --to hudson --reply-mode notify "take the next pass and report back"',
@@ -19862,6 +20627,7 @@ function buildScoutAgentCard(binding, options = {}) {
19862
20627
  const supportedInterfaces = metadataRecordArray(binding.agent.metadata, "supportedInterfaces");
19863
20628
  const securitySchemes = metadataRecord2(binding.agent.metadata, "securitySchemes");
19864
20629
  const securityRequirements = metadataStringMatrix(binding.agent.metadata, "securityRequirements");
20630
+ const lifecycle2 = options.lifecycle ?? metadataRecord2(binding.agent.metadata, "cardLifecycle");
19865
20631
  return {
19866
20632
  id: binding.agent.id,
19867
20633
  agentId: binding.agent.id,
@@ -19891,6 +20657,7 @@ function buildScoutAgentCard(binding, options = {}) {
19891
20657
  ...options.createdById?.trim() ? { createdById: options.createdById.trim() } : {},
19892
20658
  brokerRegistered: options.brokerRegistered ?? false,
19893
20659
  ...options.inboxConversationId?.trim() ? { inboxConversationId: options.inboxConversationId.trim() } : {},
20660
+ ...lifecycle2 ? { lifecycle: lifecycle2 } : {},
19894
20661
  returnAddress: buildScoutReturnAddress({
19895
20662
  actorId: binding.agent.id,
19896
20663
  handle,
@@ -19908,7 +20675,8 @@ function buildScoutAgentCard(binding, options = {}) {
19908
20675
  actorId: binding.actor.id,
19909
20676
  endpointId: binding.endpoint.id,
19910
20677
  wakePolicy: binding.agent.wakePolicy,
19911
- ...model ? { model } : {}
20678
+ ...model ? { model } : {},
20679
+ ...lifecycle2 ? { cardLifecycle: lifecycle2 } : {}
19912
20680
  }
19913
20681
  };
19914
20682
  }
@@ -19917,13 +20685,24 @@ var init_scout_agent_cards = __esm(() => {
19917
20685
  });
19918
20686
 
19919
20687
  // ../runtime/src/control-plane-agents.ts
20688
+ import { randomUUID as randomUUID2 } from "crypto";
20689
+ import { basename as basename11 } from "path";
20690
+ function oneTimeAgentName(input) {
20691
+ const base = normalizeAgentSelectorSegment(input.agentName || basename11(input.projectPath)) || "agent";
20692
+ return `${base}-card-${randomUUID2().slice(0, 8)}`;
20693
+ }
19920
20694
  function createScoutAgentService(deps) {
19921
20695
  const localAgents = {
19922
20696
  listLocalAgents: deps.localAgents?.listLocalAgents ?? listLocalAgents,
20697
+ retireLocalAgent: deps.localAgents?.retireLocalAgent ?? retireLocalAgent,
19923
20698
  restartAllLocalAgents: deps.localAgents?.restartAllLocalAgents ?? restartAllLocalAgents,
20699
+ restartLocalAgent: deps.localAgents?.restartLocalAgent ?? restartLocalAgent,
19924
20700
  startLocalAgent: deps.localAgents?.startLocalAgent ?? startLocalAgent,
19925
20701
  stopAllLocalAgents: deps.localAgents?.stopAllLocalAgents ?? stopAllLocalAgents,
19926
20702
  stopLocalAgent: deps.localAgents?.stopLocalAgent ?? stopLocalAgent,
20703
+ updateLocalAgentCard: deps.localAgents?.updateLocalAgentCard ?? updateLocalAgentCard,
20704
+ updateLocalAgentCardLifecycle: deps.localAgents?.updateLocalAgentCardLifecycle ?? updateLocalAgentCardLifecycle,
20705
+ pruneOneTimeLocalAgentCards: deps.localAgents?.pruneOneTimeLocalAgentCards ?? pruneOneTimeLocalAgentCards,
19927
20706
  inferLocalAgentBinding: deps.localAgents?.inferLocalAgentBinding ?? inferLocalAgentBinding
19928
20707
  };
19929
20708
  return {
@@ -19940,22 +20719,62 @@ function createScoutAgentService(deps) {
19940
20719
  async downScoutAgent(agentId) {
19941
20720
  return localAgents.stopLocalAgent(agentId);
19942
20721
  },
20722
+ async retireScoutAgentCard(agentId) {
20723
+ const broker = await deps.loadScoutBrokerContext().catch(() => null);
20724
+ const retired = await localAgents.retireLocalAgent(agentId);
20725
+ if (retired) {
20726
+ await deps.retireScoutLocalAgentBinding?.({ agentId, broker }).catch(() => false);
20727
+ }
20728
+ return retired;
20729
+ },
20730
+ async updateScoutAgentCard(agentId, input) {
20731
+ const config = await localAgents.updateLocalAgentCard(agentId, input);
20732
+ if (!config) {
20733
+ return null;
20734
+ }
20735
+ if (input.restart === true) {
20736
+ await localAgents.restartLocalAgent(agentId);
20737
+ }
20738
+ await deps.registerScoutLocalAgentBinding({ agentId }).catch(() => {});
20739
+ return config;
20740
+ },
19943
20741
  async downAllScoutAgents(input = {}) {
19944
20742
  return localAgents.stopAllLocalAgents(input);
19945
20743
  },
19946
20744
  async restartScoutAgents(input = {}) {
19947
20745
  return localAgents.restartAllLocalAgents(input);
19948
20746
  },
20747
+ async cleanupScoutAgentCards(input = {}) {
20748
+ const broker = await deps.loadScoutBrokerContext().catch(() => null);
20749
+ const result = await localAgents.pruneOneTimeLocalAgentCards(input);
20750
+ await Promise.all(result.retired.map((status) => deps.retireScoutLocalAgentBinding?.({ agentId: status.agentId, broker }).catch(() => false)));
20751
+ return result;
20752
+ },
19949
20753
  async createScoutAgentCard(input) {
20754
+ const createdAt = Date.now();
20755
+ const oneTimeUse = input.oneTimeUse === true;
20756
+ const createdById = input.createdById?.trim() || undefined;
20757
+ const agentName = oneTimeUse ? oneTimeAgentName({
20758
+ agentName: input.agentName,
20759
+ projectPath: input.projectPath
20760
+ }) : input.agentName;
20761
+ const lifecycle2 = oneTimeUse ? {
20762
+ kind: "one_time",
20763
+ createdAt,
20764
+ ...createdById ? { createdById } : {},
20765
+ expiresAt: createdAt + Math.max(1, input.ttlMs ?? DEFAULT_ONE_TIME_SCOUT_AGENT_CARD_TTL_MS),
20766
+ maxUses: 1
20767
+ } : undefined;
19950
20768
  const status = await localAgents.startLocalAgent({
19951
20769
  projectPath: input.projectPath,
19952
- agentName: input.agentName,
20770
+ agentName,
19953
20771
  displayName: input.displayName,
19954
20772
  harness: input.harness,
19955
20773
  model: input.model,
19956
20774
  reasoningEffort: input.reasoningEffort,
19957
20775
  permissionProfile: input.permissionProfile,
19958
20776
  currentDirectory: input.currentDirectory,
20777
+ ...lifecycle2 ? { card: lifecycle2 } : {},
19959
20778
  ensureOnline: false
19960
20779
  });
19961
20780
  const currentDirectory = input.currentDirectory ?? input.projectPath;
@@ -19969,48 +20788,70 @@ function createScoutAgentService(deps) {
19969
20788
  throw new Error(`Agent ${status.agentId} did not expose an addressable binding.`);
19970
20789
  }
19971
20790
  let inboxConversationId;
19972
- let createdById = input.createdById?.trim() || undefined;
19973
- if (broker && createdById && createdById !== binding.agent.id) {
20791
+ let resolvedCreatedById = createdById;
20792
+ if (broker && resolvedCreatedById && resolvedCreatedById !== binding.agent.id) {
19974
20793
  const session = await deps.openScoutPeerSession({
19975
- sourceId: createdById,
20794
+ sourceId: resolvedCreatedById,
19976
20795
  targetId: binding.agent.id,
19977
20796
  currentDirectory
19978
20797
  });
19979
20798
  inboxConversationId = session.conversation.id;
19980
- createdById = session.sourceId;
20799
+ resolvedCreatedById = session.sourceId;
20800
+ }
20801
+ const finalLifecycle = lifecycle2 ? {
20802
+ ...lifecycle2,
20803
+ ...resolvedCreatedById ? { createdById: resolvedCreatedById } : {},
20804
+ ...inboxConversationId ? { inboxConversationId } : {}
20805
+ } : undefined;
20806
+ if (finalLifecycle) {
20807
+ await localAgents.updateLocalAgentCardLifecycle(status.agentId, finalLifecycle).catch(() => null);
20808
+ const cleaned = await localAgents.pruneOneTimeLocalAgentCards({
20809
+ createdById: finalLifecycle.createdById,
20810
+ projectRoot: binding.endpoint.projectRoot ?? input.projectPath,
20811
+ excludeAgentIds: [status.agentId]
20812
+ }).catch(() => null);
20813
+ if (cleaned?.retired.length) {
20814
+ await Promise.all(cleaned.retired.map((retired) => deps.retireScoutLocalAgentBinding?.({ agentId: retired.agentId, broker }).catch(() => false)));
20815
+ }
19981
20816
  }
19982
20817
  return buildScoutAgentCard(binding, {
19983
20818
  currentDirectory,
19984
- createdById,
20819
+ createdById: resolvedCreatedById,
19985
20820
  brokerRegistered: syncResult?.brokerRegistered ?? false,
19986
- inboxConversationId
20821
+ inboxConversationId,
20822
+ lifecycle: finalLifecycle
19987
20823
  });
19988
20824
  }
19989
20825
  };
19990
20826
  }
20827
+ var DEFAULT_ONE_TIME_SCOUT_AGENT_CARD_TTL_MS;
19991
20828
  var init_control_plane_agents = __esm(() => {
20829
+ init_src();
19992
20830
  init_local_agents();
19993
20831
  init_scout_agent_cards();
20832
+ DEFAULT_ONE_TIME_SCOUT_AGENT_CARD_TTL_MS = 24 * 60 * 60 * 1000;
19994
20833
  });
19995
20834
 
19996
20835
  // ../../apps/desktop/src/core/agents/service.ts
19997
- var scoutAgentService, createScoutAgentCard, downAllScoutAgents, downScoutAgent, loadScoutAgentStatuses, restartScoutAgents, upScoutAgent;
20836
+ var scoutAgentService, cleanupScoutAgentCards, createScoutAgentCard, downAllScoutAgents, downScoutAgent, loadScoutAgentStatuses, retireScoutAgentCard, restartScoutAgents, updateScoutAgentCard, upScoutAgent;
19998
20837
  var init_service2 = __esm(() => {
19999
20838
  init_control_plane_agents();
20000
20839
  init_service();
20001
20840
  scoutAgentService = createScoutAgentService({
20002
20841
  loadScoutBrokerContext,
20003
20842
  openScoutPeerSession,
20004
- registerScoutLocalAgentBinding
20005
- });
20006
- ({
20007
- createScoutAgentCard,
20008
- downAllScoutAgents,
20009
- downScoutAgent,
20010
- loadScoutAgentStatuses,
20011
- restartScoutAgents,
20012
- upScoutAgent
20013
- } = scoutAgentService);
20843
+ registerScoutLocalAgentBinding,
20844
+ retireScoutLocalAgentBinding
20845
+ });
20846
+ cleanupScoutAgentCards = scoutAgentService.cleanupScoutAgentCards;
20847
+ createScoutAgentCard = scoutAgentService.createScoutAgentCard;
20848
+ downAllScoutAgents = scoutAgentService.downAllScoutAgents;
20849
+ downScoutAgent = scoutAgentService.downScoutAgent;
20850
+ loadScoutAgentStatuses = scoutAgentService.loadScoutAgentStatuses;
20851
+ retireScoutAgentCard = scoutAgentService.retireScoutAgentCard;
20852
+ restartScoutAgents = scoutAgentService.restartScoutAgents;
20853
+ updateScoutAgentCard = scoutAgentService.updateScoutAgentCard;
20854
+ upScoutAgent = scoutAgentService.upScoutAgent;
20014
20855
  });
20015
20856
 
20016
20857
  // ../../apps/desktop/src/ui/terminal/cards.ts
@@ -20078,16 +20919,129 @@ __export(exports_card, {
20078
20919
  import { createInterface } from "readline";
20079
20920
  function renderCardCommandHelp() {
20080
20921
  return [
20081
- "Usage: scout card create [path] [--name <alias>] [--display-name <name>] [--harness <claude|codex|pi>] [--model <model>] [--reasoning-effort <effort>] [--permission-profile <profile>] [--as <requester>] [--no-input] [--path <path>]",
20922
+ "Usage:",
20923
+ " scout card create [path] [--name <alias>] [--display-name <name>] [--harness <claude|codex|pi>] [--model <model>] [--reasoning-effort <effort>] [--permission-profile <profile>] [--as <requester>] [--one-time] [--no-input] [--path <path>]",
20924
+ ` scout card update <agent> [--harness <claude|codex|pi>] [--model <model>|--clear-model] [--reasoning-effort <effort>|--clear-reasoning-effort] [--permission-profile <${formatScoutPermissionProfiles()}>|--clear-permission-profile] [--restart]`,
20925
+ " scout card cleanup [--all]",
20926
+ " scout card retire <agent>",
20082
20927
  "",
20083
- "Create a dedicated Scout agent card with a reply-ready return address.",
20928
+ "Create, update, or retire a dedicated Scout agent card with a reply-ready return address.",
20084
20929
  "",
20085
20930
  "Use this when another agent should get back to you on a fresh project-scoped inbox,",
20086
20931
  "or when a worktree needs its own obvious handle instead of reusing a shared project agent.",
20087
20932
  "",
20088
20933
  "Examples:",
20089
20934
  " scout card create",
20090
- " scout card create ~/dev/openscout-worktrees/shell-fix --name shellfix --harness claude --model claude-sonnet-4-6"
20935
+ " scout card create ~/dev/openscout-worktrees/shell-fix --name shellfix --harness claude --model claude-sonnet-4-6",
20936
+ " scout card create --one-time --name review",
20937
+ " scout card cleanup",
20938
+ " scout card update talkie-drift-investigator --harness claude --model claude-opus-4-7",
20939
+ " scout card retire talkie-drift-investigator"
20940
+ ].join(`
20941
+ `);
20942
+ }
20943
+ function readRequiredFlagValue(args, index, flag) {
20944
+ const current = args[index] ?? "";
20945
+ if (current.startsWith(`${flag}=`)) {
20946
+ return { value: current.slice(flag.length + 1), index };
20947
+ }
20948
+ const value = args[index + 1];
20949
+ if (!value) {
20950
+ throw new ScoutCliError(`missing value for ${flag}`);
20951
+ }
20952
+ return { value, index: index + 1 };
20953
+ }
20954
+ function parseCardUpdateOptions(args) {
20955
+ const target = args[0]?.trim();
20956
+ if (!target) {
20957
+ throw new ScoutCliError(renderCardCommandHelp());
20958
+ }
20959
+ const options = { restart: false };
20960
+ for (let index = 1;index < args.length; index += 1) {
20961
+ const current = args[index] ?? "";
20962
+ if (current === "--harness" || current.startsWith("--harness=")) {
20963
+ const parsed = readRequiredFlagValue(args, index, "--harness");
20964
+ options.harness = parsed.value;
20965
+ index = parsed.index;
20966
+ continue;
20967
+ }
20968
+ if (current === "--model" || current.startsWith("--model=")) {
20969
+ const parsed = readRequiredFlagValue(args, index, "--model");
20970
+ options.model = parsed.value;
20971
+ index = parsed.index;
20972
+ continue;
20973
+ }
20974
+ if (current === "--clear-model") {
20975
+ options.model = null;
20976
+ continue;
20977
+ }
20978
+ if (current === "--reasoning-effort" || current === "--effort" || current.startsWith("--reasoning-effort=") || current.startsWith("--effort=")) {
20979
+ const flag = current.startsWith("--effort") ? "--effort" : "--reasoning-effort";
20980
+ const parsed = readRequiredFlagValue(args, index, flag);
20981
+ options.reasoningEffort = parsed.value;
20982
+ index = parsed.index;
20983
+ continue;
20984
+ }
20985
+ if (current === "--clear-reasoning-effort") {
20986
+ options.reasoningEffort = null;
20987
+ continue;
20988
+ }
20989
+ if (current === "--permission-profile" || current.startsWith("--permission-profile=")) {
20990
+ const parsed = readRequiredFlagValue(args, index, "--permission-profile");
20991
+ options.permissionProfile = parsed.value;
20992
+ index = parsed.index;
20993
+ continue;
20994
+ }
20995
+ if (current === "--clear-permission-profile") {
20996
+ options.permissionProfile = null;
20997
+ continue;
20998
+ }
20999
+ if (current === "--restart") {
21000
+ options.restart = true;
21001
+ continue;
21002
+ }
21003
+ if (current.startsWith("--")) {
21004
+ throw new ScoutCliError(`unexpected argument for card update: ${current}`);
21005
+ }
21006
+ throw new ScoutCliError(`unexpected arguments for card update: ${args.join(" ")}`);
21007
+ }
21008
+ return { target, options };
21009
+ }
21010
+ async function resolveCardAgentId(target) {
21011
+ const resolved = await resolveLocalAgentByName(target) ?? await resolveLocalAgentByName(target, { matchProjectName: true });
21012
+ if (!resolved) {
21013
+ throw new ScoutCliError(`unknown Scout card "${target}"`);
21014
+ }
21015
+ return resolved.agentId;
21016
+ }
21017
+ function renderCardUpdateResult(value) {
21018
+ return [
21019
+ `Updated ${value.agentId}`,
21020
+ `Harness: ${value.config.runtime.harness}`,
21021
+ `Transport: ${value.config.runtime.transport}`,
21022
+ `Session: ${value.config.runtime.sessionId}`,
21023
+ `Model: ${value.config.model ?? "default"}`,
21024
+ `Permission: ${value.config.permissionProfile ?? "default"}`,
21025
+ `Restarted: ${value.restarted ? "yes" : "no"}`
21026
+ ].join(`
21027
+ `);
21028
+ }
21029
+ function renderCardRetireResult(value) {
21030
+ if (!value) {
21031
+ return "Agent not found.";
21032
+ }
21033
+ return `Retired ${value.agentId}`;
21034
+ }
21035
+ function renderCardCleanupResult(value) {
21036
+ const count = value.retired.length;
21037
+ if (count === 0) {
21038
+ return `No one-time cards retired. Inspected ${value.inspected}.`;
21039
+ }
21040
+ const agents = value.retired.map((agent) => `- ${agent.agentId}`).join(`
21041
+ `);
21042
+ return [
21043
+ `Retired ${count} one-time card${count === 1 ? "" : "s"}.`,
21044
+ agents
20091
21045
  ].join(`
20092
21046
  `);
20093
21047
  }
@@ -20106,6 +21060,60 @@ async function runCardCommand(context, args) {
20106
21060
  context.output.writeText(renderCardCommandHelp());
20107
21061
  return;
20108
21062
  }
21063
+ if (subcommand === "update") {
21064
+ if (args.slice(1).some((arg) => HELP_FLAGS4.has(arg))) {
21065
+ context.output.writeText(renderCardCommandHelp());
21066
+ return;
21067
+ }
21068
+ const { target, options: options2 } = parseCardUpdateOptions(args.slice(1));
21069
+ const agentId = await resolveCardAgentId(target);
21070
+ const config = await updateScoutAgentCard(agentId, {
21071
+ harness: parseScoutLocalHarness(options2.harness),
21072
+ model: options2.model,
21073
+ reasoningEffort: options2.reasoningEffort,
21074
+ permissionProfile: options2.permissionProfile,
21075
+ restart: options2.restart
21076
+ });
21077
+ if (!config) {
21078
+ throw new ScoutCliError(`unknown Scout card "${target}"`);
21079
+ }
21080
+ context.output.writeValue({ agentId, config, restarted: options2.restart }, renderCardUpdateResult);
21081
+ return;
21082
+ }
21083
+ if (subcommand === "retire" || subcommand === "delete" || subcommand === "remove") {
21084
+ if (args.slice(1).some((arg) => HELP_FLAGS4.has(arg))) {
21085
+ context.output.writeText(renderCardCommandHelp());
21086
+ return;
21087
+ }
21088
+ const target = args[1]?.trim();
21089
+ if (!target || args.length > 2) {
21090
+ throw new ScoutCliError(renderCardCommandHelp());
21091
+ }
21092
+ const agentId = await resolveCardAgentId(target);
21093
+ const retired = await retireScoutAgentCard(agentId);
21094
+ context.output.writeValue(retired, renderCardRetireResult);
21095
+ return;
21096
+ }
21097
+ if (subcommand === "cleanup" || subcommand === "prune") {
21098
+ if (args.slice(1).some((arg) => HELP_FLAGS4.has(arg))) {
21099
+ context.output.writeText(renderCardCommandHelp());
21100
+ return;
21101
+ }
21102
+ let all = false;
21103
+ for (const arg of args.slice(1)) {
21104
+ if (arg === "--all") {
21105
+ all = true;
21106
+ continue;
21107
+ }
21108
+ throw new ScoutCliError(`unexpected argument for card cleanup: ${arg}`);
21109
+ }
21110
+ const cleaned = await cleanupScoutAgentCards({
21111
+ currentDirectory: defaultScoutContextDirectory(context),
21112
+ maxCount: all ? 0 : undefined
21113
+ });
21114
+ context.output.writeValue(cleaned, renderCardCleanupResult);
21115
+ return;
21116
+ }
20109
21117
  if (subcommand !== "create") {
20110
21118
  throw new ScoutCliError(renderCardCommandHelp());
20111
21119
  }
@@ -20155,12 +21163,14 @@ async function runCardCommand(context, args) {
20155
21163
  reasoningEffort: options.reasoningEffort,
20156
21164
  permissionProfile: options.permissionProfile,
20157
21165
  currentDirectory: options.currentDirectory,
20158
- createdById: resolveScoutAgentName(options.requesterId)
21166
+ createdById: resolveScoutAgentName(options.requesterId),
21167
+ oneTimeUse: options.oneTimeUse
20159
21168
  });
20160
21169
  context.output.writeValue(card, renderScoutAgentCard);
20161
21170
  }
20162
21171
  var HELP_FLAGS4;
20163
21172
  var init_card = __esm(() => {
21173
+ init_src();
20164
21174
  init_local_agents();
20165
21175
  init_context();
20166
21176
  init_errors();
@@ -48656,17 +49666,29 @@ __export(exports_channel, {
48656
49666
  function newestMessagesFirst(messages2) {
48657
49667
  return [...messages2].sort((left, right) => right.createdAt - left.createdAt);
48658
49668
  }
49669
+ function renderChannelMarkReadReport(report) {
49670
+ const message = report.lastReadMessageId ? ` through ${report.lastReadMessageId}` : "";
49671
+ return [
49672
+ `Marked ${report.conversationId} read for ${report.actorId}${message}.`,
49673
+ `Acknowledged deliveries: ${report.acknowledgedDeliveries}`
49674
+ ].join(`
49675
+ `);
49676
+ }
48659
49677
  function renderChannelCommandHelp() {
48660
49678
  return [
48661
49679
  "Usage: scout channel [--context-root <path>]",
48662
49680
  " scout channel [<name>] --latest <count> [--json]",
49681
+ " scout channel [<name>] --mark-read [--json]",
48663
49682
  "",
48664
49683
  "Run a Scout channel server over stdio.",
48665
49684
  "Read recent channel messages with --latest and exit.",
49685
+ "Mark a channel read with --mark-read (aliases: --read, --clear).",
48666
49686
  "",
48667
49687
  "Examples:",
48668
49688
  " scout channel --latest 10 --json",
48669
49689
  " scout channel homepage-polish --latest 10 --json",
49690
+ " scout channel shared --mark-read",
49691
+ " scout channel triage --clear",
48670
49692
  "",
48671
49693
  "This command is intended to be launched by Claude Code as a channel.",
48672
49694
  "It subscribes to the Scout broker event stream and pushes incoming",
@@ -48683,7 +49705,7 @@ function renderChannelCommandHelp() {
48683
49705
  "",
48684
49706
  "Do not name this server scout if you also use the full Scout MCP server.",
48685
49707
  "The channel server only exposes scout_send/scout_reply; scout mcp exposes",
48686
- "agents_start, invocations_ask, and the rest of the coordination tools."
49708
+ "ask, agents_start, invocations_get, invocations_wait, and the rest of the coordination tools."
48687
49709
  ].join(`
48688
49710
  `);
48689
49711
  }
@@ -48701,6 +49723,22 @@ async function runChannelCommand(context, args) {
48701
49723
  context.output.writeValue(newestMessagesFirst(messages2), renderScoutMessageList);
48702
49724
  return;
48703
49725
  }
49726
+ if (options.markRead) {
49727
+ const actorId = await resolveScoutSenderId(null, options.currentDirectory, context.env);
49728
+ const result = await markScoutConversationRead({
49729
+ channel: options.channel,
49730
+ actorId,
49731
+ metadata: { source: "scout-cli", action: "channel.mark-read" }
49732
+ });
49733
+ const report = {
49734
+ conversationId: scoutConversationIdForChannel(options.channel),
49735
+ actorId,
49736
+ lastReadMessageId: result.cursor.lastReadMessageId ?? null,
49737
+ acknowledgedDeliveries: result.acknowledgedDeliveries
49738
+ };
49739
+ context.output.writeValue(report, renderChannelMarkReadReport);
49740
+ return;
49741
+ }
48704
49742
  await runScoutChannelServer({
48705
49743
  defaultCurrentDirectory: options.currentDirectory,
48706
49744
  env: context.env
@@ -48721,34 +49759,39 @@ __export(exports_config, {
48721
49759
  });
48722
49760
  async function runConfigCommand(_context, args) {
48723
49761
  const [subcommand, key, ...rest] = args;
48724
- if (subcommand === "set" && key === "name" && rest.length > 0) {
48725
- const name = rest.join(" ").trim();
49762
+ if (subcommand === "set" && (key === "name" || key === "handle") && rest.length > 0) {
49763
+ const value = rest.join(" ").trim();
48726
49764
  const config2 = loadUserConfig();
48727
- config2.name = name;
49765
+ config2[key] = value;
48728
49766
  saveUserConfig(config2);
48729
- console.log(`Name set to: ${name}`);
49767
+ console.log(`${key === "name" ? "Name" : "Handle"} set to: ${value}`);
48730
49768
  return;
48731
49769
  }
48732
- if (subcommand === "get" && key === "name") {
48733
- console.log(resolveOperatorName());
49770
+ if (subcommand === "get" && (key === "name" || key === "handle")) {
49771
+ console.log(key === "name" ? resolveOperatorName() : resolveOperatorHandle());
48734
49772
  return;
48735
49773
  }
48736
- if (subcommand === "set" && key === "name") {
49774
+ if (subcommand === "set" && (key === "name" || key === "handle")) {
48737
49775
  const config2 = loadUserConfig();
48738
- delete config2.name;
49776
+ delete config2[key];
48739
49777
  saveUserConfig(config2);
48740
- console.log(`Name reset to default: ${resolveOperatorName()}`);
49778
+ const fallback = key === "name" ? resolveOperatorName() : resolveOperatorHandle();
49779
+ console.log(`${key === "name" ? "Name" : "Handle"} reset to default: ${fallback}`);
48741
49780
  return;
48742
49781
  }
48743
49782
  if (!subcommand || subcommand === "show") {
48744
49783
  const config2 = loadUserConfig();
48745
49784
  const name = resolveOperatorName();
49785
+ const handle = resolveOperatorHandle();
48746
49786
  console.log(`name: ${name}${config2.name ? "" : " (default)"}`);
49787
+ console.log(`handle: @${handle}${config2.handle ? "" : " (default)"}`);
48747
49788
  return;
48748
49789
  }
48749
49790
  console.error("usage: scout config [show]");
48750
49791
  console.error(" scout config set name <value>");
49792
+ console.error(" scout config set handle <value>");
48751
49793
  console.error(" scout config get name");
49794
+ console.error(" scout config get handle");
48752
49795
  process.exit(1);
48753
49796
  }
48754
49797
  var init_config = __esm(() => {
@@ -52087,7 +53130,7 @@ var init_mcp = __esm(() => {
52087
53130
 
52088
53131
  // ../../apps/desktop/src/core/mcp/scout-mcp.ts
52089
53132
  import { readFileSync as readFileSync10 } from "fs";
52090
- import { basename as basename11, resolve as resolve13 } from "path";
53133
+ import { basename as basename12, resolve as resolve13 } from "path";
52091
53134
  function createAgentPickerFieldMeta(input) {
52092
53135
  return {
52093
53136
  kind: "agent-picker",
@@ -52245,7 +53288,7 @@ function renderMcpAskSummary(result) {
52245
53288
  ].filter(Boolean);
52246
53289
  const detailText = details.length > 0 ? `; ${details.join(", ")}` : "";
52247
53290
  const followText = result.followUrl ? ` Follow: ${result.followUrl}` : "";
52248
- if (result.waitStatus === "timeout" && result.flightId) {
53291
+ if (result.waitStatus === "pending" && result.flightId) {
52249
53292
  const state = result.flight?.state ? ` ${result.flight.state}` : "";
52250
53293
  return `Ask dispatch is still${state}; use invocations_wait with flightId=${result.flightId}.${followText}`;
52251
53294
  }
@@ -52266,7 +53309,8 @@ function renderMcpAskPrimitiveSummary(receipt) {
52266
53309
  const target = receipt.ids.targetAgentId ? ` to ${receipt.ids.targetAgentId}` : "";
52267
53310
  const flight = receipt.ids.flightId ? `; flight ${receipt.ids.flightId}` : "";
52268
53311
  const work = receipt.ids.workId ? `; work ${receipt.ids.workId}` : "";
52269
- return `Ask ${receipt.state}${target}${flight}${work}.`;
53312
+ const delivery = receipt.delivery === "mcp_notification" ? receipt.notification?.status === "scheduled" ? " Reply will be delivered by MCP notification." : receipt.ids.flightId ? ` MCP notification was not scheduled; use invocations_wait with flightId=${receipt.ids.flightId}.` : " MCP notification was not scheduled." : "";
53313
+ return `Ask ${receipt.state}${target}${flight}${work}.${delivery}`;
52270
53314
  }
52271
53315
  if (receipt.next) {
52272
53316
  return `Ask was not sent: ${receipt.next.reason}`;
@@ -52282,6 +53326,9 @@ function resolveAskReplyMode(input) {
52282
53326
  }
52283
53327
  return input.awaitReply ? "inline" : "none";
52284
53328
  }
53329
+ function resolveMcpReplyToSessionId(explicitSessionId, env) {
53330
+ return explicitSessionId?.trim() || env.CODEX_THREAD_ID?.trim() || undefined;
53331
+ }
52285
53332
  function workUrlFor(workItem, env) {
52286
53333
  return workItem ? buildScoutPath2(resolveScoutWebOrigin2(env), `/work/${encodeURIComponent(workItem.id)}`) : null;
52287
53334
  }
@@ -52326,7 +53373,7 @@ function buildScoutFollowArtifacts(input, env) {
52326
53373
  invocationId: input.flight?.invocationId ?? null,
52327
53374
  conversationId: input.conversationId,
52328
53375
  workId: input.workItem?.id ?? null,
52329
- sessionId: null,
53376
+ sessionId: input.targetSessionId ?? null,
52330
53377
  targetAgentId: input.targetAgentId ?? input.flight?.targetAgentId ?? null
52331
53378
  };
52332
53379
  const origin = resolveScoutWebOrigin2(env);
@@ -52347,6 +53394,12 @@ function buildScoutFollowArtifacts(input, env) {
52347
53394
  function isTerminalFlightState2(state) {
52348
53395
  return state === "completed" || state === "failed" || state === "cancelled";
52349
53396
  }
53397
+ async function loadInvocationLifecycleForFlight(input) {
53398
+ if (!input.flight?.invocationId || !input.deps.getInvocationLifecycle) {
53399
+ return null;
53400
+ }
53401
+ return await input.deps.getInvocationLifecycle(input.brokerUrl, input.flight.invocationId);
53402
+ }
52350
53403
  function isAcknowledgedFlightState(state) {
52351
53404
  return state === "running" || state === "waiting";
52352
53405
  }
@@ -52367,7 +53420,7 @@ async function waitForFlightForMcp(input) {
52367
53420
  return { flight: latestFlight, waitStatus: "acknowledged" };
52368
53421
  }
52369
53422
  if (deadline !== null && Date.now() > deadline) {
52370
- return { flight: latestFlight, waitStatus: "timeout" };
53423
+ return { flight: latestFlight, waitStatus: "pending" };
52371
53424
  }
52372
53425
  latestFlight = await input.deps.getFlight(input.brokerUrl, input.flight.id) ?? latestFlight;
52373
53426
  await new Promise((resolve14) => setTimeout(resolve14, 1000));
@@ -52378,9 +53431,9 @@ function buildInvocationLookupContent(input) {
52378
53431
  flight: input.flight,
52379
53432
  conversationId: null,
52380
53433
  workItem: null,
52381
- targetAgentId: input.flight?.targetAgentId ?? null
53434
+ targetAgentId: input.lifecycle?.targetAgentId ?? input.flight?.targetAgentId ?? null
52382
53435
  }, input.env);
52383
- const terminal = isTerminalFlightState2(input.flight?.state);
53436
+ const terminal = isTerminalFlightState2(input.flight?.state) || Boolean(input.lifecycle?.terminal);
52384
53437
  return {
52385
53438
  currentDirectory: input.currentDirectory,
52386
53439
  flightId: input.flightId,
@@ -52388,8 +53441,9 @@ function buildInvocationLookupContent(input) {
52388
53441
  waitStatus: input.waitStatus,
52389
53442
  terminal,
52390
53443
  flight: input.flight,
52391
- output: input.flight?.output ?? input.flight?.summary ?? null,
52392
- error: input.flight?.error ?? null,
53444
+ lifecycle: input.lifecycle ?? null,
53445
+ output: input.flight?.output ?? input.flight?.summary ?? input.lifecycle?.terminal?.summary ?? null,
53446
+ error: input.flight?.error ?? input.lifecycle?.terminal?.errorClass ?? null,
52393
53447
  ids: followArtifacts.ids,
52394
53448
  links: followArtifacts.links,
52395
53449
  followUrl: followArtifacts.followUrl
@@ -52406,7 +53460,7 @@ function renderInvocationLookupSummary(result) {
52406
53460
  return result.error;
52407
53461
  }
52408
53462
  const followText = result.followUrl ? ` Follow: ${result.followUrl}` : "";
52409
- if (result.waitStatus === "timeout") {
53463
+ if (result.waitStatus === "pending") {
52410
53464
  return `Flight ${result.flightId} is still ${result.flight.state}.${followText}`;
52411
53465
  }
52412
53466
  return `Flight ${result.flightId} is ${result.flight.state}.${followText}`;
@@ -52457,7 +53511,7 @@ function sendScoutReplyNotification(server, params) {
52457
53511
  function scheduleScoutReplyNotification(input) {
52458
53512
  (async () => {
52459
53513
  try {
52460
- const completedFlight = await input.deps.waitForFlight(input.brokerUrl, input.flight.id, { timeoutSeconds: input.timeoutSeconds });
53514
+ const completedFlight = await input.deps.waitForFlight(input.brokerUrl, input.flight.id);
52461
53515
  await sendScoutReplyNotification(input.server, {
52462
53516
  ...input.context,
52463
53517
  status: "completed",
@@ -52619,7 +53673,7 @@ function isCanonicalOpenScoutProjectRoot(projectRoot) {
52619
53673
  if (!projectRoot) {
52620
53674
  return false;
52621
53675
  }
52622
- return normalizeSearchValue(basename11(resolve13(projectRoot))) === "openscout";
53676
+ return normalizeSearchValue(basename12(resolve13(projectRoot))) === "openscout";
52623
53677
  }
52624
53678
  function isSameProjectRoot(left, right) {
52625
53679
  if (!left || !right) {
@@ -52765,7 +53819,8 @@ function decorateAgentLabels(entries) {
52765
53819
  workspace: entry.workspace,
52766
53820
  node: entry.node,
52767
53821
  projectRoot: entry.projectRoot,
52768
- transport: entry.transport
53822
+ transport: entry.transport,
53823
+ sessionId: entry.sessionId
52769
53824
  };
52770
53825
  });
52771
53826
  }
@@ -52826,7 +53881,7 @@ function scoreAgentCandidate(candidate, query) {
52826
53881
  candidate.model,
52827
53882
  candidate.workspace,
52828
53883
  candidate.node,
52829
- candidate.projectRoot ? basename11(candidate.projectRoot) : null
53884
+ candidate.projectRoot ? basename12(candidate.projectRoot) : null
52830
53885
  ];
52831
53886
  let best = -1;
52832
53887
  for (const value of haystacks) {
@@ -52883,6 +53938,7 @@ async function loadScoutAgentDirectory(currentDirectory) {
52883
53938
  node: entry.node ?? existing.node,
52884
53939
  projectRoot: entry.projectRoot ?? existing.projectRoot,
52885
53940
  transport: entry.transport ?? existing.transport,
53941
+ sessionId: entry.sessionId ?? existing.sessionId,
52886
53942
  state: rankState(entry.state) >= rankState(existing.state) ? entry.state : existing.state,
52887
53943
  registrationKind: entry.registrationKind === "broker" ? entry.registrationKind : existing.registrationKind,
52888
53944
  routable: entry.routable || existing.routable
@@ -52906,7 +53962,8 @@ async function loadScoutAgentDirectory(currentDirectory) {
52906
53962
  workspace: discovered.instance.workspaceQualifier || null,
52907
53963
  node: discovered.instance.nodeQualifier || null,
52908
53964
  projectRoot: discovered.projectRoot,
52909
- transport: discovered.runtime.transport ?? null
53965
+ transport: discovered.runtime.transport ?? null,
53966
+ sessionId: null
52910
53967
  });
52911
53968
  }
52912
53969
  for (const agent of Object.values(broker.snapshot.agents ?? {})) {
@@ -52933,7 +53990,8 @@ async function loadScoutAgentDirectory(currentDirectory) {
52933
53990
  workspace: normalizedStringOrNull(agent.workspaceQualifier),
52934
53991
  node: normalizedStringOrNull(agent.nodeQualifier),
52935
53992
  projectRoot: normalizedStringOrNull(preferredEndpoint?.projectRoot ?? preferredEndpoint?.cwd),
52936
- transport: normalizedStringOrNull(preferredEndpoint?.transport)
53993
+ transport: normalizedStringOrNull(preferredEndpoint?.transport),
53994
+ sessionId: normalizedStringOrNull(preferredEndpoint?.sessionId)
52937
53995
  });
52938
53996
  }
52939
53997
  return [...directory.values()].sort((left, right) => {
@@ -53038,7 +54096,9 @@ function defaultScoutMcpDependencies(env) {
53038
54096
  reasoningEffort,
53039
54097
  permissionProfile,
53040
54098
  currentDirectory,
53041
- createdById
54099
+ createdById,
54100
+ oneTimeUse,
54101
+ ttlMs
53042
54102
  }) => createScoutAgentCard({
53043
54103
  projectPath,
53044
54104
  agentName,
@@ -53048,7 +54108,9 @@ function defaultScoutMcpDependencies(env) {
53048
54108
  reasoningEffort,
53049
54109
  permissionProfile,
53050
54110
  currentDirectory,
53051
- createdById
54111
+ createdById,
54112
+ oneTimeUse,
54113
+ ttlMs
53052
54114
  }),
53053
54115
  startAgent: ({
53054
54116
  projectPath,
@@ -53146,6 +54208,7 @@ function defaultScoutMcpDependencies(env) {
53146
54208
  channel,
53147
54209
  shouldSpeak,
53148
54210
  labels,
54211
+ replyToSessionId,
53149
54212
  currentDirectory,
53150
54213
  source
53151
54214
  }) => askScoutQuestion({
@@ -53156,6 +54219,7 @@ function defaultScoutMcpDependencies(env) {
53156
54219
  channel,
53157
54220
  shouldSpeak,
53158
54221
  labels,
54222
+ replyToSessionId,
53159
54223
  currentDirectory,
53160
54224
  source
53161
54225
  }),
@@ -53167,6 +54231,7 @@ function defaultScoutMcpDependencies(env) {
53167
54231
  channel,
53168
54232
  shouldSpeak,
53169
54233
  labels,
54234
+ replyToSessionId,
53170
54235
  currentDirectory,
53171
54236
  source
53172
54237
  }) => askScoutAgentById({
@@ -53177,12 +54242,37 @@ function defaultScoutMcpDependencies(env) {
53177
54242
  channel,
53178
54243
  shouldSpeak,
53179
54244
  labels,
54245
+ replyToSessionId,
54246
+ currentDirectory,
54247
+ source
54248
+ }),
54249
+ askSessionById: ({
54250
+ senderId,
54251
+ targetSessionId,
54252
+ body,
54253
+ workItem,
54254
+ channel,
54255
+ shouldSpeak,
54256
+ labels,
54257
+ replyToSessionId,
54258
+ currentDirectory,
54259
+ source
54260
+ }) => askScoutSessionById({
54261
+ senderId,
54262
+ targetSessionId,
54263
+ body,
54264
+ workItem,
54265
+ channel,
54266
+ shouldSpeak,
54267
+ labels,
54268
+ replyToSessionId,
53180
54269
  currentDirectory,
53181
54270
  source
53182
54271
  }),
53183
54272
  updateWorkItem: (input) => updateScoutWorkItem(input),
53184
54273
  waitForFlight: (baseUrl, flightId, options) => waitForScoutFlight(baseUrl, flightId, options),
53185
54274
  getFlight: (baseUrl, flightId) => loadScoutFlight(baseUrl, flightId),
54275
+ getInvocationLifecycle: (baseUrl, invocationId) => loadScoutInvocationLifecycle(baseUrl, invocationId),
53186
54276
  readLabelBrief: (label, baseUrl) => readScoutLabelBrief(label, baseUrl),
53187
54277
  readLabelFeed: (label, baseUrl, options) => readScoutLabelFeed(label, options, baseUrl)
53188
54278
  };
@@ -53413,7 +54503,7 @@ function createScoutMcpServer(options) {
53413
54503
  });
53414
54504
  server.registerTool("messages_reply", {
53415
54505
  title: "Reply to Scout Message",
53416
- description: "Reply to the active inbound Scout broker ask. If conversationId and replyToMessageId are omitted, this uses the active ScoutReplyContext. If there is no active context, use messages_send for a new message or pass both ids explicitly.",
54506
+ description: "Reply in an existing Scout conversation/thread. This is a normal threaded conversation message, not a fresh ask or owned-work lifecycle. If conversationId and replyToMessageId are omitted, this uses the current ScoutReplyContext. If there is no active context, use messages_send for a new message or ask for a new request.",
53417
54507
  inputSchema: object2({
53418
54508
  body: string2().min(1),
53419
54509
  currentDirectory: string2().optional(),
@@ -53454,7 +54544,7 @@ function createScoutMcpServer(options) {
53454
54544
  routingError: "missing_reply_context"
53455
54545
  };
53456
54546
  return {
53457
- content: createPlainTextContent("No active Scout broker reply context. Use messages_send for a new message, or pass conversationId and replyToMessageId explicitly."),
54547
+ content: createPlainTextContent("No active Scout broker reply context. Use messages_send for a new message, use ask for a new request, or pass conversationId and replyToMessageId explicitly."),
53458
54548
  structuredContent: structuredContent2
53459
54549
  };
53460
54550
  }
@@ -53527,7 +54617,7 @@ function createScoutMcpServer(options) {
53527
54617
  });
53528
54618
  server.registerTool("card_create", {
53529
54619
  title: "Create Scout Agent Card",
53530
- description: "Create a dedicated Scout agent card with a reply-ready return address. Use this when another agent should get back to you on a fresh project-scoped inbox or worktree-scoped alias. One target stays private by default; group coordination still requires an explicit channel elsewhere.",
54620
+ description: "Create a Scout agent card with a reply-ready return address. Agent-created cards default to one-time use so short-lived review/probe identities do not crowd the system; pass oneTimeUse=false for a persistent card. One target stays private by default; group coordination still requires an explicit channel elsewhere.",
53531
54621
  inputSchema: object2({
53532
54622
  projectPath: string2().optional(),
53533
54623
  currentDirectory: string2().optional(),
@@ -53537,7 +54627,9 @@ function createScoutMcpServer(options) {
53537
54627
  harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional(),
53538
54628
  model: string2().optional(),
53539
54629
  reasoningEffort: string2().optional(),
53540
- permissionProfile: string2().optional()
54630
+ permissionProfile: string2().optional(),
54631
+ oneTimeUse: boolean2().optional(),
54632
+ ttlSeconds: number2().positive().optional()
53541
54633
  }),
53542
54634
  outputSchema: cardCreateResultSchema,
53543
54635
  annotations: {
@@ -53555,7 +54647,9 @@ function createScoutMcpServer(options) {
53555
54647
  harness,
53556
54648
  model,
53557
54649
  reasoningEffort,
53558
- permissionProfile
54650
+ permissionProfile,
54651
+ oneTimeUse,
54652
+ ttlSeconds
53559
54653
  }) => {
53560
54654
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
53561
54655
  const resolvedSenderId = await resolveMcpSenderId(deps, senderId, resolvedCurrentDirectory, env);
@@ -53568,7 +54662,9 @@ function createScoutMcpServer(options) {
53568
54662
  reasoningEffort: reasoningEffort?.trim() || undefined,
53569
54663
  permissionProfile: permissionProfile?.trim() || undefined,
53570
54664
  currentDirectory: resolvedCurrentDirectory,
53571
- createdById: resolvedSenderId
54665
+ createdById: resolvedSenderId,
54666
+ oneTimeUse: oneTimeUse ?? true,
54667
+ ttlMs: ttlSeconds === undefined ? undefined : Math.round(ttlSeconds * 1000)
53572
54668
  });
53573
54669
  const structuredContent = {
53574
54670
  currentDirectory: resolvedCurrentDirectory,
@@ -53679,7 +54775,7 @@ function createScoutMcpServer(options) {
53679
54775
  title: "Resolve Scout Agent",
53680
54776
  description: "Resolve one exact Scout agent handle or return ambiguity details. Use this when a short handle may be ambiguous; explicit target sends can let the broker resolve in one call.",
53681
54777
  inputSchema: object2({
53682
- label: string2().min(1).describe("Scout agent handle or selector, such as @talkie or @talkie#codex?5.5"),
54778
+ label: string2().min(1).describe("Scout agent handle or selector, such as @talkie. Use harness/model/profile qualifiers only when you need a specific instance."),
53683
54779
  currentDirectory: string2().optional()
53684
54780
  }),
53685
54781
  outputSchema: resolveResultSchema,
@@ -53710,17 +54806,25 @@ function createScoutMcpServer(options) {
53710
54806
  });
53711
54807
  server.registerTool("ask", {
53712
54808
  title: "Ask",
53713
- description: "Ask another agent to answer, review, try, build, compare, or give feedback. This is the normal agent-to-agent ask primitive: pass who to ask in `to`, or pass `projectPath` when you know the project root and want Scout to choose the concrete agent. Scout resolves, routes, wakes when possible, and returns a compact receipt. Use discovery tools only when you need broker help rather than agent work.",
54809
+ description: "Ask another agent to answer, review, try, build, compare, or give feedback. This is the single broker front door for requested work: pass who to ask in `to`, pass `projectPath` when you know the project root and want Scout to choose/create the concrete instance, or pass `targetSessionId` to keep a sticky session. Ask may create message, invocation, flight, delivery, and work records as side effects; use invocations_get/invocations_wait to observe flight records. Use discovery tools only when you need broker help rather than agent work.",
53714
54810
  inputSchema: object2({
53715
54811
  to: string2().min(1).optional().describe("Agent id, label, sibling, specialist, or recent collaborator."),
53716
- projectPath: string2().min(1).optional().describe("Project root to ask; Scout resolves the owning agent."),
54812
+ targetSessionId: targetSessionIdInputSchema,
54813
+ projectPath: projectPathInputSchema,
53717
54814
  body: string2().min(1),
53718
54815
  currentDirectory: string2().optional(),
53719
54816
  senderId: string2().optional(),
54817
+ replyToSessionId: string2().optional().describe("Optional requester session that should receive the eventual reply. When omitted, Codex MCP uses the current CODEX_THREAD_ID when available."),
54818
+ labels: array(string2()).optional(),
54819
+ workItem: workItemInputSchema.optional(),
54820
+ channel: string2().optional(),
54821
+ shouldSpeak: boolean2().optional(),
54822
+ replyMode: _enum2(REPLY_MODE_VALUES).describe("Reply delivery mode: 'none' returns durable ids only, 'inline' waits briefly, and 'notify' returns quickly then emits notifications/scout/reply later.").optional(),
54823
+ timeoutSeconds: number2().int().min(1).optional().describe("Caller wait budget in seconds for inline waits only; it never cancels or fails the broker ask."),
53720
54824
  harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional(),
53721
54825
  workspace: _enum2(["same", "new_worktree"]).optional(),
53722
54826
  session: _enum2(["reuse", "new"]).optional(),
53723
- wait: boolean2().optional()
54827
+ wait: boolean2().optional().describe("Compatibility alias for replyMode='inline'.")
53724
54828
  }),
53725
54829
  outputSchema: askReceiptSchema,
53726
54830
  annotations: {
@@ -53738,10 +54842,18 @@ function createScoutMcpServer(options) {
53738
54842
  })
53739
54843
  }, async ({
53740
54844
  to,
54845
+ targetSessionId,
53741
54846
  projectPath,
53742
54847
  body,
53743
54848
  currentDirectory,
53744
54849
  senderId,
54850
+ replyToSessionId,
54851
+ labels,
54852
+ workItem,
54853
+ channel,
54854
+ shouldSpeak,
54855
+ replyMode,
54856
+ timeoutSeconds,
53745
54857
  harness,
53746
54858
  workspace,
53747
54859
  session,
@@ -53749,8 +54861,14 @@ function createScoutMcpServer(options) {
53749
54861
  }) => {
53750
54862
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
53751
54863
  const resolvedSenderId = await resolveMcpSenderId(deps, senderId, resolvedCurrentDirectory, env);
53752
- const targetTo = to?.trim();
53753
- const targetProjectPath = projectPath?.trim();
54864
+ const resolvedReplyToSessionId = resolveMcpReplyToSessionId(replyToSessionId, env);
54865
+ const resolvedReplyMode = resolveAskReplyMode({
54866
+ awaitReply: wait,
54867
+ replyMode
54868
+ });
54869
+ const targetSession = targetSessionId?.trim();
54870
+ const targetTo = targetSession ? `session:${targetSession}` : to?.trim();
54871
+ const targetProjectPath = projectPath?.trim() ? resolve13(resolvedCurrentDirectory, projectPath.trim()) : undefined;
53754
54872
  let structuredContent = targetTo && targetProjectPath ? {
53755
54873
  ok: false,
53756
54874
  state: "failed",
@@ -53763,15 +54881,20 @@ function createScoutMcpServer(options) {
53763
54881
  senderId: resolvedSenderId,
53764
54882
  ...targetProjectPath ? { projectPath: targetProjectPath } : { to: targetTo ?? "" },
53765
54883
  body,
53766
- harness,
53767
- workspace,
53768
- session,
54884
+ ...harness ? { harness } : {},
54885
+ ...workspace ? { workspace } : {},
54886
+ ...session ? { session } : {},
54887
+ ...workItem ? { workItem } : {},
54888
+ ...labels ? { labels } : {},
54889
+ ...channel ? { channel } : {},
54890
+ ...shouldSpeak !== undefined ? { shouldSpeak } : {},
54891
+ ...resolvedReplyToSessionId ? { replyToSessionId: resolvedReplyToSessionId } : {},
53769
54892
  currentDirectory: resolvedCurrentDirectory,
53770
54893
  source: "scout-mcp"
53771
54894
  });
53772
- if (wait && structuredContent.ids.flightId) {
54895
+ if (resolvedReplyMode === "inline" && structuredContent.ids.flightId) {
53773
54896
  try {
53774
- const flight = await deps.waitForFlight(deps.resolveBrokerUrl(), structuredContent.ids.flightId);
54897
+ const flight = await deps.waitForFlight(deps.resolveBrokerUrl(), structuredContent.ids.flightId, timeoutSeconds ? { timeoutSeconds } : undefined);
53775
54898
  structuredContent = {
53776
54899
  ...structuredContent,
53777
54900
  state: flight.state === "completed" ? "completed" : flight.state === "failed" || flight.state === "cancelled" ? "failed" : "queued",
@@ -53784,6 +54907,58 @@ function createScoutMcpServer(options) {
53784
54907
  }
53785
54908
  };
53786
54909
  } catch {}
54910
+ } else if (resolvedReplyMode === "notify" && structuredContent.ids.flightId) {
54911
+ const flight = await deps.getFlight(deps.resolveBrokerUrl(), structuredContent.ids.flightId).catch(() => null);
54912
+ if (flight) {
54913
+ const followArtifacts = buildScoutFollowArtifacts({
54914
+ flight,
54915
+ conversationId: structuredContent.ids.conversationId ?? null,
54916
+ workItem: null,
54917
+ targetAgentId: structuredContent.ids.targetAgentId ?? flight.targetAgentId ?? null
54918
+ }, env);
54919
+ scheduleScoutReplyNotification({
54920
+ server,
54921
+ deps,
54922
+ brokerUrl: deps.resolveBrokerUrl(),
54923
+ flight,
54924
+ context: {
54925
+ currentDirectory: resolvedCurrentDirectory,
54926
+ senderId: resolvedSenderId,
54927
+ targetAgentId: structuredContent.ids.targetAgentId ?? flight.targetAgentId ?? null,
54928
+ targetLabel: targetTo ?? targetProjectPath ?? null,
54929
+ conversationId: structuredContent.ids.conversationId ?? null,
54930
+ messageId: structuredContent.ids.messageId ?? null,
54931
+ bindingRef: structuredContent.ids.bindingRef ? `ref:${structuredContent.ids.bindingRef}` : null,
54932
+ flightId: flight.id,
54933
+ workItem: null,
54934
+ workId: structuredContent.ids.workId ?? null,
54935
+ workUrl: null,
54936
+ ids: followArtifacts.ids,
54937
+ links: followArtifacts.links,
54938
+ followUrl: followArtifacts.followUrl
54939
+ }
54940
+ });
54941
+ structuredContent = {
54942
+ ...structuredContent,
54943
+ delivery: "mcp_notification",
54944
+ notification: {
54945
+ method: "notifications/scout/reply",
54946
+ status: "scheduled"
54947
+ }
54948
+ };
54949
+ }
54950
+ }
54951
+ if (structuredContent.ok && !structuredContent.delivery) {
54952
+ structuredContent = {
54953
+ ...structuredContent,
54954
+ delivery: resolvedReplyMode === "notify" ? "mcp_notification" : resolvedReplyMode === "inline" ? "inline" : "none",
54955
+ ...resolvedReplyMode === "notify" ? {
54956
+ notification: {
54957
+ method: "notifications/scout/reply",
54958
+ status: "not_scheduled"
54959
+ }
54960
+ } : {}
54961
+ };
53787
54962
  }
53788
54963
  return {
53789
54964
  content: createPlainTextContent(renderMcpAskPrimitiveSummary(structuredContent)),
@@ -53792,7 +54967,7 @@ function createScoutMcpServer(options) {
53792
54967
  });
53793
54968
  server.registerTool("messages_send", {
53794
54969
  title: "Send Scout Message",
53795
- description: "Post a broker-backed Scout tell/update. Use this for heads-up, replies, and status. Pass targets as fields: one explicit target without a channel becomes a DM, group delivery requires an explicit channel, and the body remains payload text. Targeted DMs are dispatched by the broker when the target can be reached; callers should not preflight wake/session mechanics. Use targetAgentId when agents_start returned exactTargetAgentId; this bypasses label resolution. Use channel='shared' only for shared updates. Pass targetLabel for the single-call broker-resolved path; mentionAgentIds remains available for exact-id compatibility. If a requested new or precise target is unresolved or mismatched, call agents_start and retry with the returned exactTargetAgentId instead of substituting a different agent. For agent-to-agent work, use ask instead.",
54970
+ description: "Post a broker-backed Scout message/update/reply. Use this for heads-up, threaded conversation, and status when no new owned-work lifecycle is needed. Pass targets as fields: one explicit target without a channel becomes a DM, group delivery requires an explicit channel, and the body remains payload text. Targeted DMs are dispatched by the broker when the target can be reached; callers should not preflight wake/session mechanics. Use targetAgentId when agents_start returned exactTargetAgentId; this bypasses label resolution. Use channel='shared' only for shared updates. Pass targetLabel for the single-call broker-resolved path; mentionAgentIds remains available for exact-id compatibility. If a requested new or precise target is unresolved or mismatched, call agents_start and retry with the returned exactTargetAgentId instead of substituting a different agent. For new agent-to-agent work, use ask instead.",
53796
54971
  inputSchema: object2({
53797
54972
  body: string2().min(1),
53798
54973
  currentDirectory: string2().optional(),
@@ -54048,272 +55223,380 @@ function createScoutMcpServer(options) {
54048
55223
  structuredContent
54049
55224
  };
54050
55225
  });
54051
- server.registerTool("invocations_ask", {
54052
- title: "Create Scout Invocation",
54053
- description: "Low-level broker-backed invocation handoff. For normal agent-to-agent work, use ask. Use this only when you need exact invocation controls such as targetAgentId, workItem, or replyMode. Pass targetLabel or targetAgentId as routing fields; one target without a channel becomes a DM and the body remains payload text. Use targetAgentId when agents_start returned exactTargetAgentId; this bypasses label resolution. replyMode='inline' returns once the invocation is acknowledged or already complete; use invocations_wait for a longer follow-up poll. replyMode='notify' returns immediately and emits notifications/scout/reply later; replyMode='none' returns the durable receipt only. awaitReply is kept as a boolean alias for replyMode='inline'.",
54054
- inputSchema: object2({
54055
- body: string2().min(1),
54056
- currentDirectory: string2().optional(),
54057
- senderId: string2().optional(),
54058
- targetAgentId: targetAgentIdInputSchema,
54059
- targetLabel: targetLabelInputSchema,
54060
- labels: array(string2()).optional(),
54061
- workItem: workItemInputSchema.optional(),
54062
- channel: string2().optional(),
54063
- shouldSpeak: boolean2().optional(),
54064
- awaitReply: boolean2().describe("Compatibility alias for replyMode='inline'.").optional(),
54065
- replyMode: _enum2(REPLY_MODE_VALUES).describe("Reply delivery mode: 'inline' returns a quick acknowledgement or immediate completion, 'notify' returns immediately then emits notifications/scout/reply, and 'none' returns durable ids only. Inline acknowledgement waits default to 30 seconds unless timeoutSeconds is set.").optional(),
54066
- timeoutSeconds: number2().int().min(1).optional()
54067
- }).refine((value) => Boolean(value.targetAgentId?.trim() || value.targetLabel?.trim()), {
54068
- message: "Provide either targetAgentId or targetLabel.",
54069
- path: ["targetAgentId"]
54070
- }),
54071
- outputSchema: askResultSchema,
54072
- annotations: {
54073
- readOnlyHint: false,
54074
- idempotentHint: false,
54075
- destructiveHint: false,
54076
- openWorldHint: false
54077
- },
54078
- _meta: createToolUiMeta({
54079
- targetAgentId: createAgentPickerFieldMeta({
54080
- selection: "single",
54081
- valueField: "agentId"
55226
+ if (env.OPENSCOUT_EXPOSE_DEPRECATED_INVOCATIONS_ASK === "1") {
55227
+ server.registerTool("invocations_ask", {
55228
+ title: "Deprecated Scout Invocation Ask",
55229
+ description: "DEPRECATED compatibility surface. Do not use this as an agent front door; use ask to talk to the broker. Ask creates broker invocation and flight records as side effects, then use invocations_get or invocations_wait to observe those records. This tool is hidden unless OPENSCOUT_EXPOSE_DEPRECATED_INVOCATIONS_ASK=1 is set for an older client.",
55230
+ inputSchema: object2({
55231
+ body: string2().min(1),
55232
+ currentDirectory: string2().optional(),
55233
+ senderId: string2().optional(),
55234
+ targetSessionId: targetSessionIdInputSchema,
55235
+ targetAgentId: targetAgentIdInputSchema,
55236
+ targetLabel: targetLabelInputSchema,
55237
+ replyToSessionId: string2().describe("Optional requester session that should receive the eventual reply. When omitted, Codex MCP uses the current CODEX_THREAD_ID when available.").optional(),
55238
+ labels: array(string2()).optional(),
55239
+ workItem: workItemInputSchema.optional(),
55240
+ channel: string2().optional(),
55241
+ shouldSpeak: boolean2().optional(),
55242
+ awaitReply: boolean2().describe("Compatibility alias for replyMode='inline'.").optional(),
55243
+ replyMode: _enum2(REPLY_MODE_VALUES).describe("Reply delivery mode: 'inline' returns a quick acknowledgement or immediate completion, 'notify' returns immediately then emits notifications/scout/reply, and 'none' returns durable ids only. Inline acknowledgement waits use timeoutSeconds only as a caller wait budget.").optional(),
55244
+ timeoutSeconds: number2().int().min(1).optional().describe("Caller wait budget in seconds for inline waits only; it never cancels or fails the broker ask.")
55245
+ }).refine((value) => Boolean(value.targetSessionId?.trim() || value.targetAgentId?.trim() || value.targetLabel?.trim()), {
55246
+ message: "Provide targetSessionId, targetAgentId, or targetLabel.",
55247
+ path: ["targetSessionId"]
54082
55248
  }),
54083
- targetLabel: createAgentPickerFieldMeta({
54084
- selection: "single",
54085
- valueField: "label",
54086
- resolveTool: "agents_resolve"
55249
+ outputSchema: askResultSchema,
55250
+ annotations: {
55251
+ readOnlyHint: false,
55252
+ idempotentHint: false,
55253
+ destructiveHint: false,
55254
+ openWorldHint: false
55255
+ },
55256
+ _meta: createToolUiMeta({
55257
+ targetSessionId: createAgentPickerFieldMeta({
55258
+ selection: "single",
55259
+ valueField: "sessionId"
55260
+ }),
55261
+ targetAgentId: createAgentPickerFieldMeta({
55262
+ selection: "single",
55263
+ valueField: "agentId"
55264
+ }),
55265
+ targetLabel: createAgentPickerFieldMeta({
55266
+ selection: "single",
55267
+ valueField: "label",
55268
+ resolveTool: "agents_resolve"
55269
+ })
54087
55270
  })
54088
- })
54089
- }, async ({
54090
- body,
54091
- currentDirectory,
54092
- senderId,
54093
- targetAgentId,
54094
- targetLabel,
54095
- labels,
54096
- workItem,
54097
- channel,
54098
- shouldSpeak,
54099
- awaitReply,
54100
- replyMode,
54101
- timeoutSeconds
54102
- }) => {
54103
- const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
54104
- const resolvedSenderId = await resolveMcpSenderId(deps, senderId, resolvedCurrentDirectory, env);
54105
- const resolvedReplyMode = resolveAskReplyMode({ awaitReply, replyMode });
54106
- const shouldAwait = resolvedReplyMode === "inline";
54107
- if (targetAgentId?.trim()) {
54108
- const result2 = await deps.askAgentById({
55271
+ }, async ({
55272
+ body,
55273
+ currentDirectory,
55274
+ senderId,
55275
+ targetSessionId,
55276
+ targetAgentId,
55277
+ targetLabel,
55278
+ replyToSessionId,
55279
+ labels,
55280
+ workItem,
55281
+ channel,
55282
+ shouldSpeak,
55283
+ awaitReply,
55284
+ replyMode,
55285
+ timeoutSeconds
55286
+ }) => {
55287
+ const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
55288
+ const resolvedSenderId = await resolveMcpSenderId(deps, senderId, resolvedCurrentDirectory, env);
55289
+ const resolvedReplyMode = resolveAskReplyMode({ awaitReply, replyMode });
55290
+ const shouldAwait = resolvedReplyMode === "inline";
55291
+ const resolvedReplyToSessionId = resolveMcpReplyToSessionId(replyToSessionId, env);
55292
+ if (targetSessionId?.trim()) {
55293
+ const trimmedTargetSessionId = targetSessionId.trim();
55294
+ const result2 = await deps.askSessionById({
55295
+ senderId: resolvedSenderId,
55296
+ targetSessionId: trimmedTargetSessionId,
55297
+ body,
55298
+ workItem,
55299
+ channel,
55300
+ shouldSpeak,
55301
+ labels,
55302
+ replyToSessionId: resolvedReplyToSessionId,
55303
+ currentDirectory: resolvedCurrentDirectory,
55304
+ source: "scout-mcp"
55305
+ });
55306
+ const waitResult2 = shouldAwait ? await waitForFlightForMcp({
55307
+ deps,
55308
+ brokerUrl: deps.resolveBrokerUrl(),
55309
+ flight: result2.flight ?? null,
55310
+ timeoutSeconds
55311
+ }) : { flight: null, waitStatus: "not_requested" };
55312
+ const completedFlight2 = waitResult2.flight;
55313
+ const trackedWorkItem2 = result2.workItem ?? null;
55314
+ const notificationScheduled2 = resolvedReplyMode === "notify" && Boolean(result2.flight);
55315
+ const followArtifacts2 = buildScoutFollowArtifacts({
55316
+ flight: completedFlight2 ?? result2.flight ?? null,
55317
+ conversationId: result2.conversationId ?? null,
55318
+ workItem: trackedWorkItem2,
55319
+ targetSessionId: trimmedTargetSessionId,
55320
+ targetAgentId: result2.flight?.targetAgentId ?? null
55321
+ }, env);
55322
+ if (resolvedReplyMode === "notify" && result2.flight) {
55323
+ scheduleScoutReplyNotification({
55324
+ server,
55325
+ deps,
55326
+ brokerUrl: deps.resolveBrokerUrl(),
55327
+ flight: result2.flight,
55328
+ context: {
55329
+ currentDirectory: resolvedCurrentDirectory,
55330
+ senderId: resolvedSenderId,
55331
+ targetAgentId: result2.flight.targetAgentId ?? null,
55332
+ targetLabel: null,
55333
+ conversationId: result2.conversationId ?? null,
55334
+ messageId: result2.messageId ?? null,
55335
+ flightId: result2.flight.id,
55336
+ workItem: trackedWorkItem2,
55337
+ workId: trackedWorkItem2?.id ?? null,
55338
+ workUrl: workUrlFor(trackedWorkItem2, env),
55339
+ ids: followArtifacts2.ids,
55340
+ links: followArtifacts2.links,
55341
+ followUrl: followArtifacts2.followUrl
55342
+ }
55343
+ });
55344
+ }
55345
+ const unresolvedTargetId = result2.unresolvedTargetId ?? null;
55346
+ const structuredContent2 = {
55347
+ currentDirectory: resolvedCurrentDirectory,
55348
+ senderId: resolvedSenderId,
55349
+ targetAgentId: result2.flight?.targetAgentId ?? null,
55350
+ targetSessionId: trimmedTargetSessionId,
55351
+ targetLabel: null,
55352
+ replyToSessionId: resolvedReplyToSessionId ?? null,
55353
+ usedBroker: result2.usedBroker,
55354
+ awaited: shouldAwait,
55355
+ waitStatus: waitResult2.waitStatus,
55356
+ replyMode: resolvedReplyMode,
55357
+ delivery: notificationScheduled2 ? "mcp_notification" : shouldAwait ? "inline" : "none",
55358
+ notification: resolvedReplyMode === "notify" ? {
55359
+ method: "notifications/scout/reply",
55360
+ status: notificationScheduled2 ? "scheduled" : "not_scheduled"
55361
+ } : null,
55362
+ conversationId: result2.conversationId ?? null,
55363
+ messageId: result2.messageId ?? null,
55364
+ flight: completedFlight2 ?? result2.flight ?? null,
55365
+ flightId: completedFlight2?.id ?? result2.flight?.id ?? null,
55366
+ output: waitResult2.waitStatus === "completed" || waitResult2.waitStatus === "terminal" ? completedFlight2?.output ?? completedFlight2?.summary ?? null : null,
55367
+ unresolvedTargetId,
55368
+ unresolvedTargetLabel: null,
55369
+ workItem: trackedWorkItem2,
55370
+ workId: trackedWorkItem2?.id ?? null,
55371
+ workUrl: workUrlFor(trackedWorkItem2, env),
55372
+ ids: followArtifacts2.ids,
55373
+ links: followArtifacts2.links,
55374
+ followUrl: followArtifacts2.followUrl,
55375
+ targetDiagnostic: result2.targetDiagnostic ?? buildExactTargetIdsDiagnostic(unresolvedTargetId ? [unresolvedTargetId] : []),
55376
+ startSuggestion: null
55377
+ };
55378
+ return {
55379
+ content: createPlainTextContent(renderMcpAskSummary(structuredContent2)),
55380
+ structuredContent: structuredContent2
55381
+ };
55382
+ }
55383
+ if (targetAgentId?.trim()) {
55384
+ const result2 = await deps.askAgentById({
55385
+ senderId: resolvedSenderId,
55386
+ targetAgentId: targetAgentId.trim(),
55387
+ body,
55388
+ workItem,
55389
+ channel,
55390
+ shouldSpeak,
55391
+ labels,
55392
+ replyToSessionId: resolvedReplyToSessionId,
55393
+ currentDirectory: resolvedCurrentDirectory,
55394
+ source: "scout-mcp"
55395
+ });
55396
+ const waitResult2 = shouldAwait ? await waitForFlightForMcp({
55397
+ deps,
55398
+ brokerUrl: deps.resolveBrokerUrl(),
55399
+ flight: result2.flight ?? null,
55400
+ timeoutSeconds
55401
+ }) : { flight: null, waitStatus: "not_requested" };
55402
+ const completedFlight2 = waitResult2.flight;
55403
+ const trackedWorkItem2 = result2.workItem ?? null;
55404
+ const notificationScheduled2 = resolvedReplyMode === "notify" && Boolean(result2.flight);
55405
+ const followArtifacts2 = buildScoutFollowArtifacts({
55406
+ flight: completedFlight2 ?? result2.flight ?? null,
55407
+ conversationId: result2.conversationId ?? null,
55408
+ workItem: trackedWorkItem2,
55409
+ targetAgentId: targetAgentId.trim()
55410
+ }, env);
55411
+ if (resolvedReplyMode === "notify" && result2.flight) {
55412
+ scheduleScoutReplyNotification({
55413
+ server,
55414
+ deps,
55415
+ brokerUrl: deps.resolveBrokerUrl(),
55416
+ flight: result2.flight,
55417
+ context: {
55418
+ currentDirectory: resolvedCurrentDirectory,
55419
+ senderId: resolvedSenderId,
55420
+ targetAgentId: targetAgentId.trim(),
55421
+ targetLabel: null,
55422
+ conversationId: result2.conversationId ?? null,
55423
+ messageId: result2.messageId ?? null,
55424
+ flightId: result2.flight.id,
55425
+ workItem: trackedWorkItem2,
55426
+ workId: trackedWorkItem2?.id ?? null,
55427
+ workUrl: workUrlFor(trackedWorkItem2, env),
55428
+ ids: followArtifacts2.ids,
55429
+ links: followArtifacts2.links,
55430
+ followUrl: followArtifacts2.followUrl
55431
+ }
55432
+ });
55433
+ }
55434
+ const unresolvedTargetId = result2.unresolvedTargetId ?? null;
55435
+ const structuredContent2 = {
55436
+ currentDirectory: resolvedCurrentDirectory,
55437
+ senderId: resolvedSenderId,
55438
+ targetAgentId: targetAgentId.trim(),
55439
+ targetSessionId: null,
55440
+ targetLabel: null,
55441
+ replyToSessionId: resolvedReplyToSessionId ?? null,
55442
+ usedBroker: result2.usedBroker,
55443
+ awaited: shouldAwait,
55444
+ waitStatus: waitResult2.waitStatus,
55445
+ replyMode: resolvedReplyMode,
55446
+ delivery: notificationScheduled2 ? "mcp_notification" : shouldAwait ? "inline" : "none",
55447
+ notification: resolvedReplyMode === "notify" ? {
55448
+ method: "notifications/scout/reply",
55449
+ status: notificationScheduled2 ? "scheduled" : "not_scheduled"
55450
+ } : null,
55451
+ conversationId: result2.conversationId ?? null,
55452
+ messageId: result2.messageId ?? null,
55453
+ flight: completedFlight2 ?? result2.flight ?? null,
55454
+ flightId: completedFlight2?.id ?? result2.flight?.id ?? null,
55455
+ output: waitResult2.waitStatus === "completed" || waitResult2.waitStatus === "terminal" ? completedFlight2?.output ?? completedFlight2?.summary ?? null : null,
55456
+ unresolvedTargetId,
55457
+ unresolvedTargetLabel: null,
55458
+ workItem: trackedWorkItem2,
55459
+ workId: trackedWorkItem2?.id ?? null,
55460
+ workUrl: workUrlFor(trackedWorkItem2, env),
55461
+ ids: followArtifacts2.ids,
55462
+ links: followArtifacts2.links,
55463
+ followUrl: followArtifacts2.followUrl,
55464
+ targetDiagnostic: result2.targetDiagnostic ?? buildExactTargetIdsDiagnostic(unresolvedTargetId ? [unresolvedTargetId] : []),
55465
+ startSuggestion: null
55466
+ };
55467
+ return {
55468
+ content: createPlainTextContent(renderMcpAskSummary(structuredContent2)),
55469
+ structuredContent: structuredContent2
55470
+ };
55471
+ }
55472
+ const targetCheck = await diagnosePreciseTargetLabel({
55473
+ deps,
55474
+ targetLabel,
55475
+ currentDirectory: resolvedCurrentDirectory
55476
+ });
55477
+ if (targetCheck.blocked) {
55478
+ const structuredContent2 = {
55479
+ currentDirectory: resolvedCurrentDirectory,
55480
+ senderId: resolvedSenderId,
55481
+ targetAgentId: null,
55482
+ targetSessionId: null,
55483
+ targetLabel: targetLabel.trim(),
55484
+ replyToSessionId: resolvedReplyToSessionId ?? null,
55485
+ usedBroker: true,
55486
+ awaited: shouldAwait,
55487
+ waitStatus: "not_requested",
55488
+ replyMode: resolvedReplyMode,
55489
+ delivery: "none",
55490
+ notification: null,
55491
+ conversationId: null,
55492
+ messageId: null,
55493
+ flight: null,
55494
+ flightId: null,
55495
+ output: null,
55496
+ unresolvedTargetId: null,
55497
+ unresolvedTargetLabel: targetLabel.trim(),
55498
+ workItem: null,
55499
+ workId: null,
55500
+ workUrl: null,
55501
+ targetDiagnostic: targetCheck.diagnostic,
55502
+ startSuggestion: targetCheck.startSuggestion
55503
+ };
55504
+ return {
55505
+ content: createPlainTextContent(renderMcpAskSummary(structuredContent2)),
55506
+ structuredContent: structuredContent2
55507
+ };
55508
+ }
55509
+ const result = await deps.askQuestion({
54109
55510
  senderId: resolvedSenderId,
54110
- targetAgentId: targetAgentId.trim(),
55511
+ targetLabel: targetLabel.trim(),
54111
55512
  body,
54112
55513
  workItem,
54113
55514
  channel,
54114
55515
  shouldSpeak,
54115
55516
  labels,
55517
+ replyToSessionId: resolvedReplyToSessionId,
54116
55518
  currentDirectory: resolvedCurrentDirectory,
54117
55519
  source: "scout-mcp"
54118
55520
  });
54119
- const waitResult2 = shouldAwait ? await waitForFlightForMcp({
55521
+ const waitResult = shouldAwait ? await waitForFlightForMcp({
54120
55522
  deps,
54121
55523
  brokerUrl: deps.resolveBrokerUrl(),
54122
- flight: result2.flight ?? null,
55524
+ flight: result.flight ?? null,
54123
55525
  timeoutSeconds
54124
55526
  }) : { flight: null, waitStatus: "not_requested" };
54125
- const completedFlight2 = waitResult2.flight;
54126
- const trackedWorkItem2 = result2.workItem ?? null;
54127
- const notificationScheduled2 = resolvedReplyMode === "notify" && Boolean(result2.flight);
54128
- const followArtifacts2 = buildScoutFollowArtifacts({
54129
- flight: completedFlight2 ?? result2.flight ?? null,
54130
- conversationId: result2.conversationId ?? null,
54131
- workItem: trackedWorkItem2,
54132
- targetAgentId: targetAgentId.trim()
55527
+ const completedFlight = waitResult.flight;
55528
+ const trackedWorkItem = result.workItem ?? null;
55529
+ const notificationScheduled = resolvedReplyMode === "notify" && Boolean(result.flight);
55530
+ const followArtifacts = buildScoutFollowArtifacts({
55531
+ flight: completedFlight ?? result.flight ?? null,
55532
+ conversationId: result.conversationId ?? null,
55533
+ workItem: trackedWorkItem,
55534
+ targetAgentId: result.flight?.targetAgentId ?? null
54133
55535
  }, env);
54134
- if (resolvedReplyMode === "notify" && result2.flight) {
55536
+ if (resolvedReplyMode === "notify" && result.flight) {
54135
55537
  scheduleScoutReplyNotification({
54136
55538
  server,
54137
55539
  deps,
54138
55540
  brokerUrl: deps.resolveBrokerUrl(),
54139
- flight: result2.flight,
54140
- timeoutSeconds,
55541
+ flight: result.flight,
54141
55542
  context: {
54142
55543
  currentDirectory: resolvedCurrentDirectory,
54143
55544
  senderId: resolvedSenderId,
54144
- targetAgentId: targetAgentId.trim(),
54145
- targetLabel: null,
54146
- conversationId: result2.conversationId ?? null,
54147
- messageId: result2.messageId ?? null,
54148
- flightId: result2.flight.id,
54149
- workItem: trackedWorkItem2,
54150
- workId: trackedWorkItem2?.id ?? null,
54151
- workUrl: workUrlFor(trackedWorkItem2, env),
54152
- ids: followArtifacts2.ids,
54153
- links: followArtifacts2.links,
54154
- followUrl: followArtifacts2.followUrl
55545
+ targetAgentId: result.flight.targetAgentId ?? null,
55546
+ targetLabel: targetLabel.trim(),
55547
+ conversationId: result.conversationId ?? null,
55548
+ messageId: result.messageId ?? null,
55549
+ bindingRef: result.bindingRef ? `ref:${result.bindingRef}` : null,
55550
+ flightId: result.flight.id,
55551
+ workItem: trackedWorkItem,
55552
+ workId: trackedWorkItem?.id ?? null,
55553
+ workUrl: workUrlFor(trackedWorkItem, env),
55554
+ ids: followArtifacts.ids,
55555
+ links: followArtifacts.links,
55556
+ followUrl: followArtifacts.followUrl
54155
55557
  }
54156
55558
  });
54157
55559
  }
54158
- const unresolvedTargetId = result2.unresolvedTargetId ?? null;
54159
- const structuredContent2 = {
55560
+ const startSuggestion = result.unresolvedTarget ? await buildStartSuggestionForTarget(result.unresolvedTarget, resolvedCurrentDirectory) : null;
55561
+ const structuredContent = {
54160
55562
  currentDirectory: resolvedCurrentDirectory,
54161
55563
  senderId: resolvedSenderId,
54162
- targetAgentId: targetAgentId.trim(),
54163
- targetLabel: null,
54164
- usedBroker: result2.usedBroker,
55564
+ targetAgentId: result.flight?.targetAgentId ?? null,
55565
+ targetSessionId: null,
55566
+ targetLabel: targetLabel.trim(),
55567
+ replyToSessionId: resolvedReplyToSessionId ?? null,
55568
+ usedBroker: result.usedBroker,
54165
55569
  awaited: shouldAwait,
54166
- waitStatus: waitResult2.waitStatus,
55570
+ waitStatus: waitResult.waitStatus,
54167
55571
  replyMode: resolvedReplyMode,
54168
- delivery: notificationScheduled2 ? "mcp_notification" : shouldAwait ? "inline" : "none",
55572
+ delivery: notificationScheduled ? "mcp_notification" : shouldAwait ? "inline" : "none",
54169
55573
  notification: resolvedReplyMode === "notify" ? {
54170
55574
  method: "notifications/scout/reply",
54171
- status: notificationScheduled2 ? "scheduled" : "not_scheduled"
55575
+ status: notificationScheduled ? "scheduled" : "not_scheduled"
54172
55576
  } : null,
54173
- conversationId: result2.conversationId ?? null,
54174
- messageId: result2.messageId ?? null,
54175
- flight: completedFlight2 ?? result2.flight ?? null,
54176
- flightId: completedFlight2?.id ?? result2.flight?.id ?? null,
54177
- output: waitResult2.waitStatus === "completed" || waitResult2.waitStatus === "terminal" ? completedFlight2?.output ?? completedFlight2?.summary ?? null : null,
54178
- unresolvedTargetId,
54179
- unresolvedTargetLabel: null,
54180
- workItem: trackedWorkItem2,
54181
- workId: trackedWorkItem2?.id ?? null,
54182
- workUrl: workUrlFor(trackedWorkItem2, env),
54183
- ids: followArtifacts2.ids,
54184
- links: followArtifacts2.links,
54185
- followUrl: followArtifacts2.followUrl,
54186
- targetDiagnostic: result2.targetDiagnostic ?? buildExactTargetIdsDiagnostic(unresolvedTargetId ? [unresolvedTargetId] : []),
54187
- startSuggestion: null
54188
- };
54189
- return {
54190
- content: createPlainTextContent(renderMcpAskSummary(structuredContent2)),
54191
- structuredContent: structuredContent2
54192
- };
54193
- }
54194
- const targetCheck = await diagnosePreciseTargetLabel({
54195
- deps,
54196
- targetLabel,
54197
- currentDirectory: resolvedCurrentDirectory
54198
- });
54199
- if (targetCheck.blocked) {
54200
- const structuredContent2 = {
54201
- currentDirectory: resolvedCurrentDirectory,
54202
- senderId: resolvedSenderId,
54203
- targetAgentId: null,
54204
- targetLabel: targetLabel.trim(),
54205
- usedBroker: true,
54206
- awaited: shouldAwait,
54207
- waitStatus: "not_requested",
54208
- replyMode: resolvedReplyMode,
54209
- delivery: "none",
54210
- notification: null,
54211
- conversationId: null,
54212
- messageId: null,
54213
- flight: null,
54214
- flightId: null,
54215
- output: null,
55577
+ conversationId: result.conversationId ?? null,
55578
+ messageId: result.messageId ?? null,
55579
+ bindingRef: result.bindingRef ? `ref:${result.bindingRef}` : null,
55580
+ flight: completedFlight ?? result.flight ?? null,
55581
+ flightId: completedFlight?.id ?? result.flight?.id ?? null,
55582
+ output: waitResult.waitStatus === "completed" || waitResult.waitStatus === "terminal" ? completedFlight?.output ?? completedFlight?.summary ?? null : null,
54216
55583
  unresolvedTargetId: null,
54217
- unresolvedTargetLabel: targetLabel.trim(),
54218
- workItem: null,
54219
- workId: null,
54220
- workUrl: null,
54221
- targetDiagnostic: targetCheck.diagnostic,
54222
- startSuggestion: targetCheck.startSuggestion
55584
+ unresolvedTargetLabel: result.unresolvedTarget ?? null,
55585
+ workItem: trackedWorkItem,
55586
+ workId: trackedWorkItem?.id ?? null,
55587
+ workUrl: workUrlFor(trackedWorkItem, env),
55588
+ ids: followArtifacts.ids,
55589
+ links: followArtifacts.links,
55590
+ followUrl: followArtifacts.followUrl,
55591
+ targetDiagnostic: result.targetDiagnostic ?? null,
55592
+ startSuggestion
54223
55593
  };
54224
55594
  return {
54225
- content: createPlainTextContent(renderMcpAskSummary(structuredContent2)),
54226
- structuredContent: structuredContent2
55595
+ content: createPlainTextContent(renderMcpAskSummary(structuredContent)),
55596
+ structuredContent
54227
55597
  };
54228
- }
54229
- const result = await deps.askQuestion({
54230
- senderId: resolvedSenderId,
54231
- targetLabel: targetLabel.trim(),
54232
- body,
54233
- workItem,
54234
- channel,
54235
- shouldSpeak,
54236
- labels,
54237
- currentDirectory: resolvedCurrentDirectory,
54238
- source: "scout-mcp"
54239
55598
  });
54240
- const waitResult = shouldAwait ? await waitForFlightForMcp({
54241
- deps,
54242
- brokerUrl: deps.resolveBrokerUrl(),
54243
- flight: result.flight ?? null,
54244
- timeoutSeconds
54245
- }) : { flight: null, waitStatus: "not_requested" };
54246
- const completedFlight = waitResult.flight;
54247
- const trackedWorkItem = result.workItem ?? null;
54248
- const notificationScheduled = resolvedReplyMode === "notify" && Boolean(result.flight);
54249
- const followArtifacts = buildScoutFollowArtifacts({
54250
- flight: completedFlight ?? result.flight ?? null,
54251
- conversationId: result.conversationId ?? null,
54252
- workItem: trackedWorkItem,
54253
- targetAgentId: result.flight?.targetAgentId ?? null
54254
- }, env);
54255
- if (resolvedReplyMode === "notify" && result.flight) {
54256
- scheduleScoutReplyNotification({
54257
- server,
54258
- deps,
54259
- brokerUrl: deps.resolveBrokerUrl(),
54260
- flight: result.flight,
54261
- timeoutSeconds,
54262
- context: {
54263
- currentDirectory: resolvedCurrentDirectory,
54264
- senderId: resolvedSenderId,
54265
- targetAgentId: result.flight.targetAgentId ?? null,
54266
- targetLabel: targetLabel.trim(),
54267
- conversationId: result.conversationId ?? null,
54268
- messageId: result.messageId ?? null,
54269
- bindingRef: result.bindingRef ? `ref:${result.bindingRef}` : null,
54270
- flightId: result.flight.id,
54271
- workItem: trackedWorkItem,
54272
- workId: trackedWorkItem?.id ?? null,
54273
- workUrl: workUrlFor(trackedWorkItem, env),
54274
- ids: followArtifacts.ids,
54275
- links: followArtifacts.links,
54276
- followUrl: followArtifacts.followUrl
54277
- }
54278
- });
54279
- }
54280
- const startSuggestion = result.unresolvedTarget ? await buildStartSuggestionForTarget(result.unresolvedTarget, resolvedCurrentDirectory) : null;
54281
- const structuredContent = {
54282
- currentDirectory: resolvedCurrentDirectory,
54283
- senderId: resolvedSenderId,
54284
- targetAgentId: result.flight?.targetAgentId ?? null,
54285
- targetLabel: targetLabel.trim(),
54286
- usedBroker: result.usedBroker,
54287
- awaited: shouldAwait,
54288
- waitStatus: waitResult.waitStatus,
54289
- replyMode: resolvedReplyMode,
54290
- delivery: notificationScheduled ? "mcp_notification" : shouldAwait ? "inline" : "none",
54291
- notification: resolvedReplyMode === "notify" ? {
54292
- method: "notifications/scout/reply",
54293
- status: notificationScheduled ? "scheduled" : "not_scheduled"
54294
- } : null,
54295
- conversationId: result.conversationId ?? null,
54296
- messageId: result.messageId ?? null,
54297
- bindingRef: result.bindingRef ? `ref:${result.bindingRef}` : null,
54298
- flight: completedFlight ?? result.flight ?? null,
54299
- flightId: completedFlight?.id ?? result.flight?.id ?? null,
54300
- output: waitResult.waitStatus === "completed" || waitResult.waitStatus === "terminal" ? completedFlight?.output ?? completedFlight?.summary ?? null : null,
54301
- unresolvedTargetId: null,
54302
- unresolvedTargetLabel: result.unresolvedTarget ?? null,
54303
- workItem: trackedWorkItem,
54304
- workId: trackedWorkItem?.id ?? null,
54305
- workUrl: workUrlFor(trackedWorkItem, env),
54306
- ids: followArtifacts.ids,
54307
- links: followArtifacts.links,
54308
- followUrl: followArtifacts.followUrl,
54309
- targetDiagnostic: result.targetDiagnostic ?? null,
54310
- startSuggestion
54311
- };
54312
- return {
54313
- content: createPlainTextContent(renderMcpAskSummary(structuredContent)),
54314
- structuredContent
54315
- };
54316
- });
55599
+ }
54317
55600
  server.registerTool("invocations_get", {
54318
55601
  title: "Get Scout Ask",
54319
55602
  description: "Fetch the current broker flight state for a previously-created Scout ask or invocation. Use this with a flightId returned by ask to observe long-running work without blocking the original ask call.",
@@ -54331,11 +55614,18 @@ function createScoutMcpServer(options) {
54331
55614
  }, async ({ currentDirectory, flightId }) => {
54332
55615
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
54333
55616
  const trimmedFlightId = flightId.trim();
54334
- const flight = await deps.getFlight(deps.resolveBrokerUrl(), trimmedFlightId);
55617
+ const brokerUrl = deps.resolveBrokerUrl();
55618
+ const flight = await deps.getFlight(brokerUrl, trimmedFlightId);
55619
+ const lifecycle2 = await loadInvocationLifecycleForFlight({
55620
+ deps,
55621
+ brokerUrl,
55622
+ flight
55623
+ });
54335
55624
  const structuredContent = buildInvocationLookupContent({
54336
55625
  currentDirectory: resolvedCurrentDirectory,
54337
55626
  flightId: trimmedFlightId,
54338
55627
  flight,
55628
+ lifecycle: lifecycle2,
54339
55629
  waitStatus: "not_requested",
54340
55630
  env
54341
55631
  });
@@ -54350,7 +55640,7 @@ function createScoutMcpServer(options) {
54350
55640
  inputSchema: object2({
54351
55641
  currentDirectory: string2().optional(),
54352
55642
  flightId: string2().min(1),
54353
- timeoutSeconds: number2().int().min(1).max(300).default(30)
55643
+ timeoutSeconds: number2().int().min(1).max(300).default(30).describe("Caller wait budget in seconds; elapsed time returns the latest state and does not cancel or fail the ask.")
54354
55644
  }),
54355
55645
  outputSchema: invocationLookupResultSchema,
54356
55646
  annotations: {
@@ -54362,23 +55652,30 @@ function createScoutMcpServer(options) {
54362
55652
  }, async ({ currentDirectory, flightId, timeoutSeconds }) => {
54363
55653
  const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
54364
55654
  const trimmedFlightId = flightId.trim();
55655
+ const brokerUrl = deps.resolveBrokerUrl();
54365
55656
  let flight = null;
54366
- let waitStatus = "timeout";
55657
+ let waitStatus = "pending";
54367
55658
  try {
54368
- flight = await deps.waitForFlight(deps.resolveBrokerUrl(), trimmedFlightId, { timeoutSeconds });
55659
+ flight = await deps.waitForFlight(brokerUrl, trimmedFlightId, { timeoutSeconds });
54369
55660
  waitStatus = "completed";
54370
55661
  } catch (error48) {
54371
- flight = await deps.getFlight(deps.resolveBrokerUrl(), trimmedFlightId);
55662
+ flight = await deps.getFlight(brokerUrl, trimmedFlightId);
54372
55663
  if (isTerminalFlightState2(flight?.state)) {
54373
55664
  waitStatus = "terminal";
54374
55665
  } else if (!(error48 instanceof Error) || !error48.message.includes("Timed out waiting for flight")) {
54375
55666
  throw error48;
54376
55667
  }
54377
55668
  }
55669
+ const lifecycle2 = await loadInvocationLifecycleForFlight({
55670
+ deps,
55671
+ brokerUrl,
55672
+ flight
55673
+ });
54378
55674
  const structuredContent = buildInvocationLookupContent({
54379
55675
  currentDirectory: resolvedCurrentDirectory,
54380
55676
  flightId: trimmedFlightId,
54381
55677
  flight,
55678
+ lifecycle: lifecycle2,
54382
55679
  waitStatus,
54383
55680
  env
54384
55681
  });
@@ -54545,7 +55842,7 @@ async function runScoutMcpServer(options) {
54545
55842
  const transport = new StdioServerTransport;
54546
55843
  await server.connect(transport);
54547
55844
  }
54548
- var AGENT_STATE_VALUES, REGISTRATION_KIND_VALUES, RESOLVE_KIND_VALUES, MESSAGE_ROUTE_KIND_VALUES, MESSAGE_ROUTING_ERROR_VALUES, REPLY_MODE_VALUES, REPLY_DELIVERY_VALUES, LOCAL_AGENT_HARNESS_VALUES, DEFAULT_ASK_ACK_TIMEOUT_SECONDS2 = 30, SCOUT_MCP_UI_META_KEY = "openscout/ui", scoutAgentToolIconMeta, scoutAgentAvatarMeta, targetLabelInputSchema, targetAgentIdInputSchema, mentionAgentIdsInputSchema, flightSchema, labelBriefFlightSchema, labelBriefWorkItemSchema, labelBriefSchema, labelFeedEventSchema, labelFeedSchema, trackedWorkItemSchema, workItemInputSchema, waitingOnSchema, progressSchema, workItemUpdateSchema, agentCandidateSchema, scoutReturnAddressSchema, scoutAgentCardSchema, localAgentStatusSchema, whoAmISchema, brokerMessageSchema, messagesInboxResultSchema, messagesChannelResultSchema, brokerFeedRecordSchema, brokerFeedItemSchema, brokerFeedSchema, searchResultSchema, resolveResultSchema, startSuggestionSchema, followIdsSchema, followLinksSchema, sendRoutingAdviceSchema, sendResultSchema, replyContextSchema, currentReplyContextResultSchema, replyResultSchema, cardCreateResultSchema, agentStartResultSchema, currentSessionAttachResultSchema, askResultSchema, askReceiptSchema, invocationLookupResultSchema, workUpdateResultSchema;
55845
+ var AGENT_STATE_VALUES, REGISTRATION_KIND_VALUES, RESOLVE_KIND_VALUES, MESSAGE_ROUTE_KIND_VALUES, MESSAGE_ROUTING_ERROR_VALUES, REPLY_MODE_VALUES, REPLY_DELIVERY_VALUES, LOCAL_AGENT_HARNESS_VALUES, DEFAULT_ASK_ACK_TIMEOUT_SECONDS2 = 30, SCOUT_MCP_UI_META_KEY = "openscout/ui", scoutAgentToolIconMeta, scoutAgentAvatarMeta, targetLabelInputSchema, targetAgentIdInputSchema, targetSessionIdInputSchema, projectPathInputSchema, mentionAgentIdsInputSchema, flightSchema, invocationLifecycleSchema, labelBriefFlightSchema, labelBriefWorkItemSchema, labelBriefSchema, labelFeedEventSchema, labelFeedSchema, trackedWorkItemSchema, workItemInputSchema, waitingOnSchema, progressSchema, workItemUpdateSchema, agentCandidateSchema, scoutReturnAddressSchema, scoutAgentCardLifecycleSchema, scoutAgentCardSchema, localAgentStatusSchema, whoAmISchema, brokerMessageSchema, messagesInboxResultSchema, messagesChannelResultSchema, brokerFeedRecordSchema, brokerFeedItemSchema, brokerFeedSchema, searchResultSchema, resolveResultSchema, startSuggestionSchema, followIdsSchema, followLinksSchema, sendRoutingAdviceSchema, sendResultSchema, replyContextSchema, currentReplyContextResultSchema, replyResultSchema, cardCreateResultSchema, agentStartResultSchema, currentSessionAttachResultSchema, askResultSchema, askReceiptSchema, invocationLookupResultSchema, workUpdateResultSchema;
54549
55846
  var init_scout_mcp = __esm(() => {
54550
55847
  init_mcp();
54551
55848
  init_stdio2();
@@ -54590,8 +55887,10 @@ var init_scout_mcp = __esm(() => {
54590
55887
  colorSeedField: "agentId",
54591
55888
  fallbackGlyph: "@"
54592
55889
  };
54593
- targetLabelInputSchema = string2().describe("Scout agent handle to contact, such as @talkie or @talkie#codex?5.5").optional();
55890
+ targetLabelInputSchema = string2().describe("Scout agent handle to contact, such as @talkie. Treat harness/model/profile as instance constraints, not the base agent identity.").optional();
54594
55891
  targetAgentIdInputSchema = string2().describe("Exact Scout agent id when already known, such as talkie.master.mini").optional();
55892
+ targetSessionIdInputSchema = string2().describe("Exact Scout session id to continue, such as a CODEX_THREAD_ID or attached runtime session id").optional();
55893
+ projectPathInputSchema = string2().min(1).describe("Project root to ask when you do not have a specific agent in mind; Scout resolves or creates the concrete agent instance.").optional();
54595
55894
  mentionAgentIdsInputSchema = array(string2()).describe("Exact Scout agent ids to target directly when you already know them").optional();
54596
55895
  flightSchema = object2({
54597
55896
  id: string2(),
@@ -54607,6 +55906,26 @@ var init_scout_mcp = __esm(() => {
54607
55906
  labels: array(string2()).optional(),
54608
55907
  metadata: record(string2(), unknown()).optional()
54609
55908
  });
55909
+ invocationLifecycleSchema = object2({
55910
+ invocationId: string2(),
55911
+ flightId: string2().optional(),
55912
+ state: string2(),
55913
+ targetAgentId: string2().optional(),
55914
+ targetEndpointId: string2().optional(),
55915
+ peerNodeId: string2().optional(),
55916
+ peerFlightId: string2().optional(),
55917
+ workId: string2().optional(),
55918
+ actionId: string2().optional(),
55919
+ idempotencyKey: string2().optional(),
55920
+ acknowledgedAt: number2().optional(),
55921
+ startedAt: number2().optional(),
55922
+ completedAt: number2().optional(),
55923
+ expiresAt: number2().optional(),
55924
+ lastProgressAt: number2().optional(),
55925
+ terminal: object2({}).catchall(unknown()).optional(),
55926
+ deliveries: array(object2({}).catchall(unknown())).optional(),
55927
+ metadata: record(string2(), unknown()).optional()
55928
+ }).catchall(unknown());
54610
55929
  labelBriefFlightSchema = object2({
54611
55930
  id: string2(),
54612
55931
  invocationId: string2(),
@@ -54762,7 +56081,8 @@ var init_scout_mcp = __esm(() => {
54762
56081
  workspace: string2().nullable(),
54763
56082
  node: string2().nullable(),
54764
56083
  projectRoot: string2().nullable(),
54765
- transport: string2().nullable()
56084
+ transport: string2().nullable(),
56085
+ sessionId: string2().nullable().optional()
54766
56086
  });
54767
56087
  scoutReturnAddressSchema = object2({
54768
56088
  actorId: string2(),
@@ -54777,6 +56097,14 @@ var init_scout_mcp = __esm(() => {
54777
56097
  sessionId: string2().optional(),
54778
56098
  metadata: record(string2(), unknown()).optional()
54779
56099
  });
56100
+ scoutAgentCardLifecycleSchema = object2({
56101
+ kind: _enum2(["persistent", "one_time"]),
56102
+ createdAt: number2().optional(),
56103
+ createdById: string2().optional(),
56104
+ expiresAt: number2().optional(),
56105
+ maxUses: number2().optional(),
56106
+ inboxConversationId: string2().optional()
56107
+ });
54780
56108
  scoutAgentCardSchema = object2({
54781
56109
  id: string2(),
54782
56110
  agentId: string2(),
@@ -54796,6 +56124,7 @@ var init_scout_mcp = __esm(() => {
54796
56124
  createdById: string2().optional(),
54797
56125
  brokerRegistered: boolean2(),
54798
56126
  inboxConversationId: string2().optional(),
56127
+ lifecycle: scoutAgentCardLifecycleSchema.optional(),
54799
56128
  returnAddress: scoutReturnAddressSchema,
54800
56129
  metadata: record(string2(), unknown()).optional()
54801
56130
  });
@@ -55077,10 +56406,12 @@ var init_scout_mcp = __esm(() => {
55077
56406
  currentDirectory: string2(),
55078
56407
  senderId: string2(),
55079
56408
  targetAgentId: string2().nullable(),
56409
+ targetSessionId: string2().nullable().optional(),
55080
56410
  targetLabel: string2().nullable(),
56411
+ replyToSessionId: string2().nullable().optional(),
55081
56412
  usedBroker: boolean2(),
55082
56413
  awaited: boolean2(),
55083
- waitStatus: _enum2(["not_requested", "acknowledged", "completed", "terminal", "timeout"]).optional(),
56414
+ waitStatus: _enum2(["not_requested", "acknowledged", "completed", "terminal", "pending"]).optional(),
55084
56415
  replyMode: _enum2(REPLY_MODE_VALUES).optional(),
55085
56416
  delivery: _enum2(REPLY_DELIVERY_VALUES).optional(),
55086
56417
  notification: object2({
@@ -55115,6 +56446,11 @@ var init_scout_mcp = __esm(() => {
55115
56446
  workId: string2().optional(),
55116
56447
  bindingRef: string2().optional()
55117
56448
  }),
56449
+ delivery: _enum2(REPLY_DELIVERY_VALUES).optional(),
56450
+ notification: object2({
56451
+ method: literal("notifications/scout/reply"),
56452
+ status: _enum2(["scheduled", "not_scheduled"])
56453
+ }).optional(),
55118
56454
  next: object2({
55119
56455
  tool: _enum2(["agents_resolve", "agents_search", "agents_start"]),
55120
56456
  arguments: record(string2(), unknown()),
@@ -55129,9 +56465,10 @@ var init_scout_mcp = __esm(() => {
55129
56465
  currentDirectory: string2(),
55130
56466
  flightId: string2(),
55131
56467
  found: boolean2(),
55132
- waitStatus: _enum2(["not_requested", "completed", "terminal", "timeout"]).optional(),
56468
+ waitStatus: _enum2(["not_requested", "completed", "terminal", "pending"]).optional(),
55133
56469
  terminal: boolean2(),
55134
56470
  flight: flightSchema.nullable(),
56471
+ lifecycle: invocationLifecycleSchema.nullable().optional(),
55135
56472
  output: string2().nullable(),
55136
56473
  error: string2().nullable(),
55137
56474
  ids: followIdsSchema.optional(),
@@ -55489,14 +56826,13 @@ function renderMcpCommandHelp() {
55489
56826
  " messages_channel read recent messages from a named channel",
55490
56827
  " broker_feed native broker messages/status/errors for an agent",
55491
56828
  " session_attach_current",
55492
- " attach the current live Codex session to Scout",
55493
- " card_create fresh reply-ready return address",
55494
- " agents_start start/create a concrete local agent session",
56829
+ " pro integration: attach the current live Codex session",
56830
+ " card_create pro integration: fresh reply-ready return address",
56831
+ " agents_start pro integration: start/create a concrete local agent session",
55495
56832
  " agents_search find likely targets when routing is ambiguous",
55496
56833
  " agents_resolve pin one exact target when needed",
55497
- " ask agent-to-agent work with compact lifecycle receipt",
56834
+ " ask broker front door for agent-to-agent work/replies",
55498
56835
  " messages_send tell / update with explicit target fields or channel",
55499
- " invocations_ask low-level invocation compatibility surface",
55500
56836
  " invocations_get fetch current state for an existing ask flight",
55501
56837
  " invocations_wait bounded wait for an existing ask flight",
55502
56838
  " work_update progress / waiting / review / done for existing work",
@@ -59527,6 +60863,14 @@ function sqlQuoteLiteral(value) {
59527
60863
  function sqlStringList(values) {
59528
60864
  return `(${values.map(sqlQuoteLiteral).join(",")})`;
59529
60865
  }
60866
+ function sqlTimestampMsExpression(valueExpression) {
60867
+ return `CASE
60868
+ WHEN ${valueExpression} IS NULL THEN NULL
60869
+ WHEN CAST(${valueExpression} AS REAL) < ${EPOCH_MILLISECONDS_FLOOR}
60870
+ THEN CAST(CAST(${valueExpression} AS REAL) * 1000 AS INTEGER)
60871
+ ELSE CAST(${valueExpression} AS INTEGER)
60872
+ END`;
60873
+ }
59530
60874
  function queryExecutingAgentIds() {
59531
60875
  return new Set(db().prepare(`SELECT DISTINCT target_agent_id FROM flights
59532
60876
  WHERE state = 'running'`).all().map((row) => row.target_agent_id));
@@ -59598,6 +60942,9 @@ function queryMobileAgents(limit = 50) {
59598
60942
  });
59599
60943
  }
59600
60944
  function queryMobileSessions(limit = 50) {
60945
+ const conversationCreatedAtExpression = sqlTimestampMsExpression("c.created_at");
60946
+ const messageCreatedAtExpression = sqlTimestampMsExpression("created_at");
60947
+ const previewMessageCreatedAtExpression = sqlTimestampMsExpression("created_at");
59601
60948
  const rows = db().prepare(`SELECT
59602
60949
  c.id,
59603
60950
  c.kind,
@@ -59605,12 +60952,12 @@ function queryMobileSessions(limit = 50) {
59605
60952
  c.metadata_json
59606
60953
  FROM conversations c
59607
60954
  WHERE c.kind = 'direct'
59608
- ORDER BY c.created_at DESC
60955
+ ORDER BY ${conversationCreatedAtExpression} DESC
59609
60956
  LIMIT ?`).all(limit);
59610
60957
  const memberStmt = db().prepare(`SELECT actor_id FROM conversation_members WHERE conversation_id = ?`);
59611
- const statsStmt = db().prepare(`SELECT COUNT(*) AS cnt, MAX(created_at) AS last_at FROM messages WHERE conversation_id = ?`);
60958
+ const statsStmt = db().prepare(`SELECT COUNT(*) AS cnt, MAX(${messageCreatedAtExpression}) AS last_at FROM messages WHERE conversation_id = ?`);
59612
60959
  const previewStmt = db().prepare(`SELECT body FROM messages WHERE conversation_id = ? AND actor_id != 'operator'
59613
- ORDER BY created_at DESC LIMIT 1`);
60960
+ ORDER BY ${previewMessageCreatedAtExpression} DESC LIMIT 1`);
59614
60961
  return rows.map((r) => {
59615
60962
  const participants = memberStmt.all(r.id).map((m) => m.actor_id);
59616
60963
  const agentId = participants.find((p) => p !== "operator") ?? null;
@@ -59786,7 +61133,7 @@ var _db = null, HOME, LATEST_AGENT_ENDPOINT_JOIN = `LEFT JOIN agent_endpoints ep
59786
61133
  WHERE ep2.agent_id = a.id
59787
61134
  ORDER BY ep2.updated_at DESC
59788
61135
  LIMIT 1
59789
- )`, ACTIVE_FLIGHT_STATES_SQL, ACTIVE_FLIGHT_MAX_AGE_MS, ACTIVE_WORK_STATES_SQL;
61136
+ )`, ACTIVE_FLIGHT_STATES_SQL, ACTIVE_FLIGHT_MAX_AGE_MS, EPOCH_MILLISECONDS_FLOOR = 1000000000000, ACTIVE_WORK_STATES_SQL;
59790
61137
  var init_db_queries = __esm(() => {
59791
61138
  HOME = homedir23();
59792
61139
  ACTIVE_FLIGHT_STATES_SQL = sqlStringList(["running", "waking", "waiting", "queued"]);
@@ -59795,7 +61142,7 @@ var init_db_queries = __esm(() => {
59795
61142
  });
59796
61143
 
59797
61144
  // ../../apps/desktop/src/core/mobile/service.ts
59798
- import { basename as basename12, resolve as resolve16 } from "path";
61145
+ import { basename as basename13, resolve as resolve16 } from "path";
59799
61146
  function normalizeTimestamp2(value) {
59800
61147
  if (!value)
59801
61148
  return null;
@@ -59810,6 +61157,27 @@ function metadataString4(metadata, key) {
59810
61157
  const value = metadata?.[key];
59811
61158
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
59812
61159
  }
61160
+ function metadataBoolean2(metadata, key) {
61161
+ return metadata?.[key] === true;
61162
+ }
61163
+ function isBrokerRequesterWaitTimeoutStatusMessage(message) {
61164
+ if (message.class !== "status" || metadataString4(message.metadata, "source") !== "broker") {
61165
+ return false;
61166
+ }
61167
+ return message.body.includes("Scout stopped waiting for a synchronous result") || message.body.includes("the requester stopped waiting after");
61168
+ }
61169
+ function isRequesterWaitTimeoutFlight(flight) {
61170
+ return metadataBoolean2(flight.metadata, "requesterTimedOut") || metadataString4(flight.metadata, "timeoutScope") === "requester_wait" || Boolean(flight.summary?.includes("Scout stopped waiting for a synchronous result"));
61171
+ }
61172
+ function isInactiveAgent(agent) {
61173
+ return metadataBoolean2(agent?.metadata, "retiredFromFleet") || metadataBoolean2(agent?.metadata, "staleLocalRegistration");
61174
+ }
61175
+ function isInactiveEndpoint(snapshot, endpoint) {
61176
+ if (!endpoint) {
61177
+ return true;
61178
+ }
61179
+ return metadataBoolean2(endpoint.metadata, "retiredFromFleet") || metadataBoolean2(endpoint.metadata, "staleLocalRegistration") || isInactiveAgent(snapshot.agents[endpoint.agentId]);
61180
+ }
59813
61181
  function titleCaseToken(value) {
59814
61182
  return value.length === 0 ? value : `${value[0].toUpperCase()}${value.slice(1)}`;
59815
61183
  }
@@ -59864,6 +61232,9 @@ async function loadMobileWorkspaceInventory(currentDirectory) {
59864
61232
  function latestMessageByConversation(snapshot) {
59865
61233
  const buckets = new Map;
59866
61234
  for (const message of Object.values(snapshot.messages)) {
61235
+ if (isBrokerRequesterWaitTimeoutStatusMessage(message)) {
61236
+ continue;
61237
+ }
59867
61238
  const next = buckets.get(message.conversationId) ?? [];
59868
61239
  next.push(message);
59869
61240
  buckets.set(message.conversationId, next);
@@ -59882,14 +61253,14 @@ function agentDisplayName(snapshot, agentId) {
59882
61253
  return snapshot.agents[agentId]?.displayName ?? snapshot.actors[agentId]?.displayName ?? agentId;
59883
61254
  }
59884
61255
  function endpointForAgent(snapshot, agentId) {
59885
- return Object.values(snapshot.endpoints).find((endpoint) => endpoint.agentId === agentId) ?? null;
61256
+ return Object.values(snapshot.endpoints).find((endpoint) => endpoint.agentId === agentId && !isInactiveEndpoint(snapshot, endpoint)) ?? null;
59886
61257
  }
59887
61258
  function buildMobileAgentSummary(snapshot, agent) {
59888
61259
  const endpoint = endpointForAgent(snapshot, agent.id);
59889
61260
  const flights = Object.values(snapshot.flights).filter((flight) => flight.targetAgentId === agent.id);
59890
61261
  const hasWorkingFlight = flights.some((flight) => flight.state === "running");
59891
61262
  const lastAuthoredMessageAt = Object.values(snapshot.messages).filter((message) => message.actorId === agent.id).reduce((latest, message) => {
59892
- const createdAt = normalizeTimestamp2(message.createdAt);
61263
+ const createdAt = normalizeTimestampMs(message.createdAt);
59893
61264
  return typeof createdAt === "number" && (!latest || createdAt > latest) ? createdAt : latest;
59894
61265
  }, null);
59895
61266
  const state = hasWorkingFlight ? "working" : endpoint && endpoint.state !== "offline" ? "available" : "offline";
@@ -59916,7 +61287,7 @@ function buildMobileSessionSummaries(snapshot) {
59916
61287
  const latestMessage = messages2.at(-1) ?? null;
59917
61288
  const directAgentId = conversation.kind === "direct" ? conversation.participantIds.find((participantId) => participantId !== "operator") ?? null : null;
59918
61289
  const agent = directAgentId ? snapshot.agents[directAgentId] ?? null : null;
59919
- if (!directAgentId || !agent || messages2.length === 0) {
61290
+ if (!directAgentId || !agent || isInactiveAgent(agent) || messages2.length === 0) {
59920
61291
  return [];
59921
61292
  }
59922
61293
  const endpoint = endpointForAgent(snapshot, directAgentId);
@@ -59934,7 +61305,7 @@ function buildMobileSessionSummaries(snapshot) {
59934
61305
  currentBranch: metadataString4(endpoint?.metadata, "branch") ?? metadataString4(endpoint?.metadata, "workspaceQualifier") ?? metadataString4(agent?.metadata, "branch") ?? metadataString4(agent?.metadata, "workspaceQualifier"),
59935
61306
  preview: latestMessage?.body ?? null,
59936
61307
  messageCount: messages2.length,
59937
- lastMessageAt: normalizeTimestamp2(latestMessage?.createdAt),
61308
+ lastMessageAt: normalizeTimestampMs(latestMessage?.createdAt),
59938
61309
  workspaceRoot: endpoint?.projectRoot ?? endpoint?.cwd ?? null
59939
61310
  }];
59940
61311
  });
@@ -59958,7 +61329,7 @@ function buildMobileSessionSummaries(snapshot) {
59958
61329
  return [...deduped.values()].sort((left, right) => (right.lastMessageAt ?? 0) - (left.lastMessageAt ?? 0));
59959
61330
  }
59960
61331
  function messagesForConversation(snapshot, conversationId) {
59961
- return Object.values(snapshot.messages).filter((message) => message.conversationId === conversationId).sort((left, right) => (normalizeTimestamp2(left.createdAt) ?? 0) - (normalizeTimestamp2(right.createdAt) ?? 0));
61332
+ return Object.values(snapshot.messages).filter((message) => !isBrokerRequesterWaitTimeoutStatusMessage(message)).filter((message) => message.conversationId === conversationId).sort((left, right) => (normalizeTimestamp2(left.createdAt) ?? 0) - (normalizeTimestamp2(right.createdAt) ?? 0));
59962
61333
  }
59963
61334
  function pageMessagesForConversation(snapshot, conversationId, options = {}) {
59964
61335
  const allMessages = messagesForConversation(snapshot, conversationId);
@@ -59995,7 +61366,7 @@ function pageMessagesForConversation(snapshot, conversationId, options = {}) {
59995
61366
  function latestActiveFlightForAgent(snapshot, agentId) {
59996
61367
  if (!agentId)
59997
61368
  return null;
59998
- return Object.values(snapshot.flights).filter((flight) => flight.targetAgentId === agentId && !["completed", "failed", "cancelled"].includes(flight.state)).sort((left, right) => (normalizeTimestamp2(right.startedAt) ?? 0) - (normalizeTimestamp2(left.startedAt) ?? 0))[0] ?? null;
61369
+ return Object.values(snapshot.flights).filter((flight) => flight.targetAgentId === agentId && (flight.state === "running" || flight.state === "waiting") && !isRequesterWaitTimeoutFlight(flight)).sort((left, right) => (normalizeTimestamp2(right.startedAt) ?? 0) - (normalizeTimestamp2(left.startedAt) ?? 0))[0] ?? null;
59999
61370
  }
60000
61371
  async function loadMobileRelayState() {
60001
61372
  const broker = await loadScoutBrokerContext();
@@ -60003,7 +61374,10 @@ async function loadMobileRelayState() {
60003
61374
  return { agents: [], sessions: [] };
60004
61375
  }
60005
61376
  const snapshot = broker.snapshot;
60006
- const agents = Object.values(snapshot.agents).map((agent) => buildMobileAgentSummary(snapshot, agent)).sort((left, right) => (right.lastActiveAt ?? 0) - (left.lastActiveAt ?? 0) || left.title.localeCompare(right.title));
61377
+ const agents = Object.values(snapshot.agents).filter((agent) => !isInactiveAgent(agent)).filter((agent) => {
61378
+ const endpoints = Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === agent.id);
61379
+ return endpoints.length === 0 || endpoints.some((endpoint) => !isInactiveEndpoint(snapshot, endpoint));
61380
+ }).map((agent) => buildMobileAgentSummary(snapshot, agent)).sort((left, right) => (right.lastActiveAt ?? 0) - (left.lastActiveAt ?? 0) || left.title.localeCompare(right.title));
60007
61381
  return {
60008
61382
  agents,
60009
61383
  sessions: buildMobileSessionSummaries(snapshot)
@@ -60110,10 +61484,10 @@ async function getScoutMobileSessionSnapshot(conversationId, options = {}, curre
60110
61484
  const messages2 = messagePage.messages;
60111
61485
  const activeFlight = latestActiveFlightForAgent(snapshot, directAgentId);
60112
61486
  const lastAgentMessageAt = messages2.filter((message) => message.actorId === directAgentId).reduce((latest, message) => {
60113
- const createdAt = normalizeTimestamp2(message.createdAt);
61487
+ const createdAt = normalizeTimestampMs(message.createdAt);
60114
61488
  return typeof createdAt === "number" && (!latest || createdAt > latest) ? createdAt : latest;
60115
61489
  }, null);
60116
- const shouldShowWorkingTurn = Boolean(activeFlight && (normalizeTimestamp2(activeFlight.startedAt) ?? 0) > (lastAgentMessageAt ?? 0));
61490
+ const shouldShowWorkingTurn = Boolean(activeFlight && (normalizeTimestampMs(activeFlight.startedAt) ?? 0) > (lastAgentMessageAt ?? 0));
60117
61491
  const turns = messages2.map((message) => ({
60118
61492
  id: message.id,
60119
61493
  status: "completed",
@@ -60187,7 +61561,7 @@ async function createScoutSession(input, currentDirectory, deviceId) {
60187
61561
  throw new Error(`Invalid workspaceId.`);
60188
61562
  }
60189
61563
  const workspaceRoot = resolve16(rawWorkspaceId);
60190
- const projectName = basename12(workspaceRoot) || workspaceRoot;
61564
+ const projectName = basename13(workspaceRoot) || workspaceRoot;
60191
61565
  const workspace = {
60192
61566
  id: workspaceRoot,
60193
61567
  title: projectName,
@@ -60264,6 +61638,9 @@ async function createScoutSession(input, currentDirectory, deviceId) {
60264
61638
  };
60265
61639
  }
60266
61640
  async function sendScoutMobileMessage(input, currentDirectory, deviceId) {
61641
+ if (input.agentId === SCOUTBOT_AGENT_ID) {
61642
+ return sendScoutbotMobileThreadMessage(input, currentDirectory, deviceId);
61643
+ }
60267
61644
  return sendScoutDirectMessage({
60268
61645
  agentId: input.agentId,
60269
61646
  body: input.body,
@@ -60276,6 +61653,88 @@ async function sendScoutMobileMessage(input, currentDirectory, deviceId) {
60276
61653
  deviceId
60277
61654
  });
60278
61655
  }
61656
+ async function sendScoutbotMobileThreadMessage(input, currentDirectory, deviceId) {
61657
+ const broker = await loadScoutBrokerContext();
61658
+ if (!broker) {
61659
+ throw new Error("Scoutbot is not available.");
61660
+ }
61661
+ const conversationId = broker.snapshot.conversations[SCOUTBOT_LEGACY_CONVERSATION_ID] ? SCOUTBOT_LEGACY_CONVERSATION_ID : SCOUTBOT_DEFAULT_CONVERSATION_ID;
61662
+ const conversation = broker.snapshot.conversations[conversationId] ?? {
61663
+ id: conversationId,
61664
+ kind: "direct",
61665
+ title: conversationId === SCOUTBOT_LEGACY_CONVERSATION_ID ? "Scout" : "Scout \xB7 default",
61666
+ visibility: "private",
61667
+ shareMode: "local",
61668
+ authorityNodeId: broker.node.id,
61669
+ participantIds: ["operator", SCOUTBOT_AGENT_ID].sort(),
61670
+ metadata: {
61671
+ surface: "scoutbot",
61672
+ scoutbotThreadId: SCOUTBOT_DEFAULT_THREAD_ID
61673
+ }
61674
+ };
61675
+ if (!broker.snapshot.conversations[conversationId]) {
61676
+ await postMobileBrokerJson(broker.baseUrl, "/v1/conversations", conversation);
61677
+ }
61678
+ const now = Date.now();
61679
+ const messageId = createMobileBrokerEntityId("msg", now);
61680
+ const transportSessionId = scoutbotTransportSessionId(broker.snapshot);
61681
+ await postMobileBrokerJson(broker.baseUrl, "/v1/messages", {
61682
+ id: messageId,
61683
+ conversationId,
61684
+ actorId: "operator",
61685
+ originNodeId: broker.node.id,
61686
+ class: "agent",
61687
+ body: input.body.trim(),
61688
+ replyToMessageId: input.replyToMessageId ?? undefined,
61689
+ mentions: [{ actorId: SCOUTBOT_AGENT_ID, label: "@scoutbot" }],
61690
+ audience: { notify: [SCOUTBOT_AGENT_ID], reason: "direct_message" },
61691
+ visibility: "private",
61692
+ policy: "durable",
61693
+ createdAt: now,
61694
+ metadata: {
61695
+ source: "scout-mobile",
61696
+ destinationKind: "scoutbot_thread",
61697
+ destinationId: SCOUTBOT_DEFAULT_THREAD_ID,
61698
+ scoutbotThreadId: SCOUTBOT_DEFAULT_THREAD_ID,
61699
+ ...transportSessionId ? { targetSessionId: transportSessionId } : {},
61700
+ referenceMessageIds: input.referenceMessageIds ?? [],
61701
+ clientMessageId: input.clientMessageId ?? null,
61702
+ ...deviceId ? { deviceId } : {},
61703
+ relayMessageId: messageId,
61704
+ returnAddress: {
61705
+ actorId: "operator",
61706
+ conversationId,
61707
+ replyToMessageId: messageId,
61708
+ ...transportSessionId ? { sessionId: transportSessionId } : {}
61709
+ }
61710
+ }
61711
+ });
61712
+ return {
61713
+ conversationId,
61714
+ messageId
61715
+ };
61716
+ }
61717
+ function scoutbotTransportSessionId(snapshot) {
61718
+ const endpoint = Object.values(snapshot.endpoints ?? {}).find((candidate) => candidate.agentId === SCOUTBOT_AGENT_ID && candidate.transport === "codex_app_server" && !isInactiveEndpoint(snapshot, candidate));
61719
+ if (!endpoint)
61720
+ return null;
61721
+ return metadataString4(endpoint.metadata, "threadId") ?? metadataString4(endpoint.metadata, "externalSessionId") ?? endpoint.sessionId?.trim() ?? null;
61722
+ }
61723
+ async function postMobileBrokerJson(baseUrl, path2, body) {
61724
+ const response = await fetch(new URL(path2, baseUrl), {
61725
+ method: "POST",
61726
+ headers: { "content-type": "application/json" },
61727
+ body: JSON.stringify(body)
61728
+ });
61729
+ if (!response.ok) {
61730
+ const text = await response.text().catch(() => "");
61731
+ throw new Error(`Broker ${path2} failed (${response.status}): ${text || response.statusText}`);
61732
+ }
61733
+ return await response.json();
61734
+ }
61735
+ function createMobileBrokerEntityId(prefix, createdAtMs) {
61736
+ return `${prefix}-${createdAtMs.toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
61737
+ }
60279
61738
  async function deriveNewAgentName(projectName, branch, harness) {
60280
61739
  const parts = [projectName.toLowerCase()];
60281
61740
  if (branch) {
@@ -60342,7 +61801,7 @@ async function getScoutMobileActivity(filters = {}) {
60342
61801
  limit: filters.limit ?? 100
60343
61802
  });
60344
61803
  }
60345
- var DEFAULT_MOBILE_RECENT_TURN_LIMIT = 24, DEFAULT_MOBILE_HISTORY_PAGE_LIMIT = 40;
61804
+ var SCOUTBOT_AGENT_ID = "scoutbot", SCOUTBOT_DEFAULT_THREAD_ID = "thr-default", SCOUTBOT_DEFAULT_CONVERSATION_ID = "dm.operator.scoutbot.default", SCOUTBOT_LEGACY_CONVERSATION_ID = "dm.operator.scoutbot", DEFAULT_MOBILE_RECENT_TURN_LIMIT = 24, DEFAULT_MOBILE_HISTORY_PAGE_LIMIT = 40;
60346
61805
  var init_service5 = __esm(() => {
60347
61806
  init_harness_catalog();
60348
61807
  init_setup();
@@ -60354,7 +61813,7 @@ var init_service5 = __esm(() => {
60354
61813
  // ../../apps/desktop/src/core/pairing/runtime/bridge/server.ts
60355
61814
  import { readdirSync as readdirSync4, readFileSync as readFileSync15, realpathSync, statSync as statSync5 } from "fs";
60356
61815
  import { execSync as execSync2 } from "child_process";
60357
- import { basename as basename13, isAbsolute as isAbsolute3, join as join30, relative as relative3 } from "path";
61816
+ import { basename as basename14, isAbsolute as isAbsolute3, join as join30, relative as relative3 } from "path";
60358
61817
  import { homedir as homedir24 } from "os";
60359
61818
  function replaySyncEvents(bridge, sessionId, lastSeq) {
60360
61819
  return bridge.replay(sessionId, lastSeq);
@@ -60527,9 +61986,9 @@ async function handleRPCInner(bridge, req, deviceId) {
60527
61986
  }
60528
61987
  case "session/resume": {
60529
61988
  const p = req.params;
60530
- const sessionFilename = basename13(p.sessionPath, ".jsonl");
61989
+ const sessionFilename = basename14(p.sessionPath, ".jsonl");
60531
61990
  const parentDir = p.sessionPath.substring(0, p.sessionPath.lastIndexOf("/"));
60532
- const dirName = basename13(parentDir);
61991
+ const dirName = basename14(parentDir);
60533
61992
  let cwd;
60534
61993
  if (dirName.startsWith("-")) {
60535
61994
  const candidate = "/" + dirName.slice(1).replace(/-/g, "/");
@@ -60596,7 +62055,7 @@ async function handleRPCInner(bridge, req, deviceId) {
60596
62055
  return { id: req.id, error: { code: -32000, message: "Workspace target is not a directory" } };
60597
62056
  }
60598
62057
  const adapterType = p.adapter ?? "claude-code";
60599
- const name = p.name ?? basename13(projectPath);
62058
+ const name = p.name ?? basename14(projectPath);
60600
62059
  const session = await bridge.createSession(adapterType, {
60601
62060
  name,
60602
62061
  cwd: projectPath
@@ -62066,7 +63525,7 @@ function planMessageDeliveries(input) {
62066
63525
  return [...deliveries2.values()];
62067
63526
  }
62068
63527
 
62069
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/entity.js
63528
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/entity.js
62070
63529
  function is(value, type) {
62071
63530
  if (!value || typeof value !== "object") {
62072
63531
  return false;
@@ -62094,7 +63553,7 @@ var init_entity = __esm(() => {
62094
63553
  hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
62095
63554
  });
62096
63555
 
62097
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column.js
63556
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/column.js
62098
63557
  var Column;
62099
63558
  var init_column = __esm(() => {
62100
63559
  init_entity();
@@ -62148,7 +63607,7 @@ var init_column = __esm(() => {
62148
63607
  };
62149
63608
  });
62150
63609
 
62151
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column-builder.js
63610
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/column-builder.js
62152
63611
  var ColumnBuilder;
62153
63612
  var init_column_builder = __esm(() => {
62154
63613
  init_entity();
@@ -62208,19 +63667,19 @@ var init_column_builder = __esm(() => {
62208
63667
  };
62209
63668
  });
62210
63669
 
62211
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.utils.js
63670
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/table.utils.js
62212
63671
  var TableName;
62213
63672
  var init_table_utils = __esm(() => {
62214
63673
  TableName = Symbol.for("drizzle:Name");
62215
63674
  });
62216
63675
 
62217
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing-utils.js
63676
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/tracing-utils.js
62218
63677
  function iife(fn, ...args) {
62219
63678
  return fn(...args);
62220
63679
  }
62221
63680
  var init_tracing_utils = () => {};
62222
63681
 
62223
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/unique-constraint.js
63682
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/unique-constraint.js
62224
63683
  function uniqueKeyName(table, columns) {
62225
63684
  return `${table[TableName]}_${columns.join("_")}_unique`;
62226
63685
  }
@@ -62228,7 +63687,7 @@ var init_unique_constraint = __esm(() => {
62228
63687
  init_table_utils();
62229
63688
  });
62230
63689
 
62231
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/common.js
63690
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/columns/common.js
62232
63691
  var PgColumn, ExtraConfigColumn;
62233
63692
  var init_common = __esm(() => {
62234
63693
  init_column();
@@ -62282,7 +63741,7 @@ var init_common = __esm(() => {
62282
63741
  };
62283
63742
  });
62284
63743
 
62285
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/enum.js
63744
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/columns/enum.js
62286
63745
  function isPgEnum(obj) {
62287
63746
  return !!obj && typeof obj === "function" && isPgEnumSym in obj && obj[isPgEnumSym] === true;
62288
63747
  }
@@ -62317,7 +63776,7 @@ var init_enum = __esm(() => {
62317
63776
  };
62318
63777
  });
62319
63778
 
62320
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/subquery.js
63779
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/subquery.js
62321
63780
  var Subquery;
62322
63781
  var init_subquery = __esm(() => {
62323
63782
  init_entity();
@@ -62336,11 +63795,11 @@ var init_subquery = __esm(() => {
62336
63795
  };
62337
63796
  });
62338
63797
 
62339
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/version.js
63798
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/version.js
62340
63799
  var version2 = "0.45.2";
62341
63800
  var init_version = () => {};
62342
63801
 
62343
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing.js
63802
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/tracing.js
62344
63803
  var otel, rawTracer, tracer;
62345
63804
  var init_tracing = __esm(() => {
62346
63805
  init_tracing_utils();
@@ -62370,13 +63829,13 @@ var init_tracing = __esm(() => {
62370
63829
  };
62371
63830
  });
62372
63831
 
62373
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/view-common.js
63832
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/view-common.js
62374
63833
  var ViewBaseConfig;
62375
63834
  var init_view_common = __esm(() => {
62376
63835
  ViewBaseConfig = Symbol.for("drizzle:ViewBaseConfig");
62377
63836
  });
62378
63837
 
62379
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.js
63838
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/table.js
62380
63839
  var Schema, Columns, ExtraConfigColumns, OriginalName, BaseName, IsAlias, ExtraConfigBuilder, IsDrizzleTable, Table;
62381
63840
  var init_table = __esm(() => {
62382
63841
  init_entity();
@@ -62418,7 +63877,7 @@ var init_table = __esm(() => {
62418
63877
  };
62419
63878
  });
62420
63879
 
62421
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/sql.js
63880
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/sql.js
62422
63881
  function isSQLWrapper(value) {
62423
63882
  return value !== null && value !== undefined && typeof value.getSQL === "function";
62424
63883
  }
@@ -62782,7 +64241,7 @@ var init_sql = __esm(() => {
62782
64241
  };
62783
64242
  });
62784
64243
 
62785
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/alias.js
64244
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/alias.js
62786
64245
  var ColumnAliasProxyHandler, TableAliasProxyHandler;
62787
64246
  var init_alias = __esm(() => {
62788
64247
  init_column();
@@ -62844,13 +64303,13 @@ var init_alias = __esm(() => {
62844
64303
  };
62845
64304
  });
62846
64305
 
62847
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/errors.js
64306
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/errors.js
62848
64307
  var init_errors5 = () => {};
62849
64308
 
62850
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/logger.js
64309
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/logger.js
62851
64310
  var init_logger = () => {};
62852
64311
 
62853
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/query-promise.js
64312
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/query-promise.js
62854
64313
  var QueryPromise;
62855
64314
  var init_query_promise = __esm(() => {
62856
64315
  init_entity();
@@ -62875,7 +64334,7 @@ var init_query_promise = __esm(() => {
62875
64334
  };
62876
64335
  });
62877
64336
 
62878
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/utils.js
64337
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/utils.js
62879
64338
  function orderSelectedFields(fields, pathPrefix) {
62880
64339
  return Object.entries(fields).reduce((result, [name, field]) => {
62881
64340
  if (typeof name !== "string") {
@@ -62934,41 +64393,41 @@ var init_utils4 = __esm(() => {
62934
64393
  textDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder;
62935
64394
  });
62936
64395
 
62937
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/expressions/conditions.js
64396
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/expressions/conditions.js
62938
64397
  var init_conditions = () => {};
62939
64398
 
62940
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/expressions/select.js
64399
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/expressions/select.js
62941
64400
  var init_select = () => {};
62942
64401
 
62943
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/expressions/index.js
64402
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/expressions/index.js
62944
64403
  var init_expressions = __esm(() => {
62945
64404
  init_conditions();
62946
64405
  init_select();
62947
64406
  });
62948
64407
 
62949
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/relations.js
64408
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/relations.js
62950
64409
  var init_relations = () => {};
62951
64410
 
62952
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/functions/aggregate.js
64411
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/functions/aggregate.js
62953
64412
  var init_aggregate = () => {};
62954
64413
 
62955
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/functions/vector.js
64414
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/functions/vector.js
62956
64415
  var init_vector = () => {};
62957
64416
 
62958
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/functions/index.js
64417
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/functions/index.js
62959
64418
  var init_functions = __esm(() => {
62960
64419
  init_aggregate();
62961
64420
  init_vector();
62962
64421
  });
62963
64422
 
62964
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/index.js
64423
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/index.js
62965
64424
  var init_sql2 = __esm(() => {
62966
64425
  init_expressions();
62967
64426
  init_functions();
62968
64427
  init_sql();
62969
64428
  });
62970
64429
 
62971
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/index.js
64430
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/index.js
62972
64431
  var init_drizzle_orm = __esm(() => {
62973
64432
  init_alias();
62974
64433
  init_column_builder();
@@ -62985,13 +64444,13 @@ var init_drizzle_orm = __esm(() => {
62985
64444
  init_view_common();
62986
64445
  });
62987
64446
 
62988
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/alias.js
64447
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/alias.js
62989
64448
  var init_alias2 = () => {};
62990
64449
 
62991
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/checks.js
64450
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/checks.js
62992
64451
  var init_checks4 = () => {};
62993
64452
 
62994
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
64453
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
62995
64454
  var ForeignKeyBuilder, ForeignKey;
62996
64455
  var init_foreign_keys = __esm(() => {
62997
64456
  init_entity();
@@ -63049,7 +64508,7 @@ var init_foreign_keys = __esm(() => {
63049
64508
  };
63050
64509
  });
63051
64510
 
63052
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/unique-constraint.js
64511
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/unique-constraint.js
63053
64512
  function uniqueKeyName2(table2, columns) {
63054
64513
  return `${table2[TableName]}_${columns.join("_")}_unique`;
63055
64514
  }
@@ -63057,7 +64516,7 @@ var init_unique_constraint2 = __esm(() => {
63057
64516
  init_table_utils();
63058
64517
  });
63059
64518
 
63060
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/common.js
64519
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/common.js
63061
64520
  var SQLiteColumnBuilder, SQLiteColumn;
63062
64521
  var init_common2 = __esm(() => {
63063
64522
  init_column_builder();
@@ -63115,7 +64574,7 @@ var init_common2 = __esm(() => {
63115
64574
  };
63116
64575
  });
63117
64576
 
63118
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/blob.js
64577
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/blob.js
63119
64578
  function blob(a, b) {
63120
64579
  const { name, config: config2 } = getColumnNameAndConfig(a, b);
63121
64580
  if (config2?.mode === "json") {
@@ -63204,7 +64663,7 @@ var init_blob = __esm(() => {
63204
64663
  };
63205
64664
  });
63206
64665
 
63207
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/custom.js
64666
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/custom.js
63208
64667
  function customType(customTypeParams) {
63209
64668
  return (a, b) => {
63210
64669
  const { name, config: config2 } = getColumnNameAndConfig(a, b);
@@ -63250,7 +64709,7 @@ var init_custom = __esm(() => {
63250
64709
  };
63251
64710
  });
63252
64711
 
63253
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/integer.js
64712
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/integer.js
63254
64713
  function integer2(a, b) {
63255
64714
  const { name, config: config2 } = getColumnNameAndConfig(a, b);
63256
64715
  if (config2?.mode === "timestamp" || config2?.mode === "timestamp_ms") {
@@ -63352,7 +64811,7 @@ var init_integer = __esm(() => {
63352
64811
  };
63353
64812
  });
63354
64813
 
63355
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/numeric.js
64814
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/numeric.js
63356
64815
  function numeric(a, b) {
63357
64816
  const { name, config: config2 } = getColumnNameAndConfig(a, b);
63358
64817
  const mode = config2?.mode;
@@ -63423,7 +64882,7 @@ var init_numeric = __esm(() => {
63423
64882
  };
63424
64883
  });
63425
64884
 
63426
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/real.js
64885
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/real.js
63427
64886
  function real(name) {
63428
64887
  return new SQLiteRealBuilder(name ?? "");
63429
64888
  }
@@ -63448,7 +64907,7 @@ var init_real = __esm(() => {
63448
64907
  };
63449
64908
  });
63450
64909
 
63451
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/text.js
64910
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/text.js
63452
64911
  function text(a, b = {}) {
63453
64912
  const { name, config: config2 } = getColumnNameAndConfig(a, b);
63454
64913
  if (config2.mode === "json") {
@@ -63506,7 +64965,7 @@ var init_text = __esm(() => {
63506
64965
  };
63507
64966
  });
63508
64967
 
63509
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/index.js
64968
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/index.js
63510
64969
  var init_columns = __esm(() => {
63511
64970
  init_blob();
63512
64971
  init_common2();
@@ -63517,7 +64976,7 @@ var init_columns = __esm(() => {
63517
64976
  init_text();
63518
64977
  });
63519
64978
 
63520
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/selection-proxy.js
64979
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/selection-proxy.js
63521
64980
  var SelectionProxyHandler;
63522
64981
  var init_selection_proxy = __esm(() => {
63523
64982
  init_alias();
@@ -63578,7 +65037,7 @@ var init_selection_proxy = __esm(() => {
63578
65037
  };
63579
65038
  });
63580
65039
 
63581
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/all.js
65040
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/all.js
63582
65041
  function getSQLiteColumnBuilders() {
63583
65042
  return {
63584
65043
  blob,
@@ -63598,7 +65057,7 @@ var init_all = __esm(() => {
63598
65057
  init_text();
63599
65058
  });
63600
65059
 
63601
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/table.js
65060
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/table.js
63602
65061
  function sqliteTableBase(name, columns, extraConfig, schema, baseName = name) {
63603
65062
  const rawTable = new SQLiteTable(name, schema, baseName);
63604
65063
  const parsedColumns = typeof columns === "function" ? columns(getSQLiteColumnBuilders()) : columns;
@@ -63636,7 +65095,7 @@ var init_table2 = __esm(() => {
63636
65095
  };
63637
65096
  });
63638
65097
 
63639
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/indexes.js
65098
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/indexes.js
63640
65099
  function index(name) {
63641
65100
  return new IndexBuilderOn(name, false);
63642
65101
  }
@@ -63681,10 +65140,10 @@ var init_indexes = __esm(() => {
63681
65140
  };
63682
65141
  });
63683
65142
 
63684
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/primary-keys.js
65143
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/primary-keys.js
63685
65144
  var init_primary_keys = () => {};
63686
65145
 
63687
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/utils.js
65146
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/utils.js
63688
65147
  function extractUsedTable(table2) {
63689
65148
  if (is(table2, SQLiteTable)) {
63690
65149
  return [`${table2[Table.Symbol.BaseName]}`];
@@ -63705,10 +65164,10 @@ var init_utils5 = __esm(() => {
63705
65164
  init_table2();
63706
65165
  });
63707
65166
 
63708
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/delete.js
65167
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/delete.js
63709
65168
  var init_delete = () => {};
63710
65169
 
63711
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/view-base.js
65170
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/view-base.js
63712
65171
  var SQLiteViewBase;
63713
65172
  var init_view_base = __esm(() => {
63714
65173
  init_entity();
@@ -63718,10 +65177,10 @@ var init_view_base = __esm(() => {
63718
65177
  };
63719
65178
  });
63720
65179
 
63721
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/dialect.js
65180
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/dialect.js
63722
65181
  var init_dialect = () => {};
63723
65182
 
63724
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/query-builders/query-builder.js
65183
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/query-builders/query-builder.js
63725
65184
  var TypedQueryBuilder;
63726
65185
  var init_query_builder = __esm(() => {
63727
65186
  init_entity();
@@ -63733,7 +65192,7 @@ var init_query_builder = __esm(() => {
63733
65192
  };
63734
65193
  });
63735
65194
 
63736
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/select.js
65195
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/select.js
63737
65196
  function createSetOperator(type, isAll) {
63738
65197
  return (leftSelect, rightSelect, ...restSelects) => {
63739
65198
  const setOperators = [rightSelect, ...restSelects].map((select2) => ({
@@ -64002,13 +65461,13 @@ var init_select2 = __esm(() => {
64002
65461
  except = createSetOperator("except", false);
64003
65462
  });
64004
65463
 
64005
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/query-builder.js
65464
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/query-builder.js
64006
65465
  var init_query_builder2 = () => {};
64007
65466
 
64008
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/insert.js
65467
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/insert.js
64009
65468
  var init_insert = () => {};
64010
65469
 
64011
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/update.js
65470
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/update.js
64012
65471
  var SQLiteUpdateBase;
64013
65472
  var init_update = __esm(() => {
64014
65473
  init_entity();
@@ -64112,7 +65571,7 @@ var init_update = __esm(() => {
64112
65571
  };
64113
65572
  });
64114
65573
 
64115
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/index.js
65574
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/index.js
64116
65575
  var init_query_builders = __esm(() => {
64117
65576
  init_delete();
64118
65577
  init_insert();
@@ -64121,16 +65580,16 @@ var init_query_builders = __esm(() => {
64121
65580
  init_update();
64122
65581
  });
64123
65582
 
64124
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/db.js
65583
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/db.js
64125
65584
  var init_db = () => {};
64126
65585
 
64127
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/session.js
65586
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/session.js
64128
65587
  var init_session = () => {};
64129
65588
 
64130
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/view.js
65589
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/view.js
64131
65590
  var init_view = () => {};
64132
65591
 
64133
- // ../../node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/index.js
65592
+ // ../../node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/index.js
64134
65593
  var init_sqlite_core = __esm(() => {
64135
65594
  init_alias2();
64136
65595
  init_checks4();
@@ -64149,10 +65608,11 @@ var init_sqlite_core = __esm(() => {
64149
65608
  });
64150
65609
 
64151
65610
  // ../runtime/src/drizzle-schema.ts
64152
- var deliveriesTable, deliveryAttemptsTable, briefingsTable;
65611
+ var epochMsNow, deliveriesTable, deliveryAttemptsTable, briefingsTable;
64153
65612
  var init_drizzle_schema = __esm(() => {
64154
65613
  init_drizzle_orm();
64155
65614
  init_sqlite_core();
65615
+ epochMsNow = sql`(CAST(strftime('%s','now') AS INTEGER) * 1000)`;
64156
65616
  deliveriesTable = sqliteTable("deliveries", {
64157
65617
  id: text("id").primaryKey(),
64158
65618
  messageId: text("message_id"),
@@ -64168,7 +65628,7 @@ var init_drizzle_schema = __esm(() => {
64168
65628
  leaseOwner: text("lease_owner"),
64169
65629
  leaseExpiresAt: integer2("lease_expires_at"),
64170
65630
  metadataJson: text("metadata_json"),
64171
- createdAt: integer2("created_at").notNull().default(sql`(unixepoch())`)
65631
+ createdAt: integer2("created_at").notNull().default(epochMsNow)
64172
65632
  }, (table3) => [
64173
65633
  index("idx_deliveries_status_transport").on(table3.status, table3.transport)
64174
65634
  ]);
@@ -64195,7 +65655,7 @@ var init_drizzle_schema = __esm(() => {
64195
65655
  snapshotJson: text("snapshot_json").notNull(),
64196
65656
  callJson: text("call_json").notNull(),
64197
65657
  markdown: text("markdown"),
64198
- createdAt: integer2("created_at").notNull().default(sql`(unixepoch())`)
65658
+ createdAt: integer2("created_at").notNull().default(epochMsNow)
64199
65659
  }, (table3) => [
64200
65660
  index("idx_briefings_created_at").on(table3.createdAt),
64201
65661
  index("idx_briefings_kind_created_at").on(table3.kind, table3.createdAt)
@@ -64936,6 +66396,17 @@ var init_mesh_forwarding = __esm(() => {
64936
66396
  init_src();
64937
66397
  init_iroh_bridge();
64938
66398
  });
66399
+
66400
+ // ../runtime/src/invocation-lifecycle-read-model.ts
66401
+ var init_invocation_lifecycle_read_model = __esm(() => {
66402
+ init_src();
66403
+ });
66404
+
66405
+ // ../runtime/src/broker-core-service.ts
66406
+ var init_broker_core_service = __esm(() => {
66407
+ init_invocation_lifecycle_read_model();
66408
+ });
66409
+
64939
66410
  // ../runtime/src/scout-broker.ts
64940
66411
  var init_scout_broker = __esm(() => {
64941
66412
  init_src();
@@ -66310,10 +67781,34 @@ function buildScoutVantagePlan(input) {
66310
67781
  manifest: {
66311
67782
  kind: HUDSON_VANTAGE_SETUP_KIND,
66312
67783
  schemaVersion: HUDSON_VANTAGE_SCHEMA_VERSION,
67784
+ workspaceID: workspaceIdFor(input.currentDirectory),
66313
67785
  source: "openscout",
66314
67786
  generatedAt,
66315
67787
  currentDirectory: input.currentDirectory,
66316
67788
  broker: buildBrokerSummary(input.broker?.baseUrl ?? null, brokerNode, brokerSnapshot),
67789
+ presentation: {
67790
+ title: "Scout Vantage",
67791
+ subtitle: "local runtime handoff",
67792
+ badge: "runtime",
67793
+ cobrand: "OpenScout",
67794
+ productName: "Scout",
67795
+ hostName: "Hudson Vantage",
67796
+ theme: "jade",
67797
+ accent: "cyan"
67798
+ },
67799
+ style: {
67800
+ preset: "jade",
67801
+ canvasGridMode: "dots",
67802
+ canvasGridStep: 18,
67803
+ focusPadding: 12
67804
+ },
67805
+ viewport: { fit: true },
67806
+ layout: {
67807
+ canvasTool: "select",
67808
+ navigationFilter: "all",
67809
+ minimapCollapsed: false,
67810
+ inspectorCollapsed: false
67811
+ },
66317
67812
  focus: focusAgentId ? { agentId: focusAgentId } : focusNativeSessionId ? { nativeSessionId: focusNativeSessionId } : null,
66318
67813
  selectedAgentIds,
66319
67814
  selectedNativeSessionIds,
@@ -66520,6 +68015,10 @@ function tmuxTargetKey(target) {
66520
68015
  function stableNodeId(kind, value) {
66521
68016
  return `vantage.${kind}.${slugify2(value)}.${fnv1a(value)}`;
66522
68017
  }
68018
+ function workspaceIdFor(currentDirectory) {
68019
+ const lastSegment = currentDirectory.split(/[\\/]+/g).filter(Boolean).at(-1) ?? "workspace";
68020
+ return `openscout-${slugify2(lastSegment)}`;
68021
+ }
66523
68022
  function slugify2(value) {
66524
68023
  const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
66525
68024
  return slug || "node";
@@ -66571,6 +68070,7 @@ var init_src3 = __esm(() => {
66571
68070
  init_tailscale();
66572
68071
  init_broker_process_manager();
66573
68072
  init_broker_api();
68073
+ init_broker_core_service();
66574
68074
  init_local_agents();
66575
68075
  init_control_plane_agents();
66576
68076
  init_scout_broker();
@@ -69874,7 +71374,7 @@ var init_tail_fanout = __esm(() => {
69874
71374
  // ../../apps/desktop/src/core/pairing/runtime/bridge/router.ts
69875
71375
  import { readFileSync as readFileSync17, readdirSync as readdirSync5, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
69876
71376
  import { execSync as execSync3 } from "child_process";
69877
- import { basename as basename14, isAbsolute as isAbsolute4, join as join32, relative as relative4 } from "path";
71377
+ import { basename as basename15, isAbsolute as isAbsolute4, join as join32, relative as relative4 } from "path";
69878
71378
  import { homedir as homedir25 } from "os";
69879
71379
  function resolveMobileCurrentDirectory2() {
69880
71380
  const config2 = resolveConfig();
@@ -70351,9 +71851,9 @@ var init_router = __esm(() => {
70351
71851
  adapterType: exports_external.string().optional(),
70352
71852
  name: exports_external.string().optional()
70353
71853
  })).mutation(async ({ input, ctx }) => {
70354
- const sessionFilename = basename14(input.sessionPath, ".jsonl");
71854
+ const sessionFilename = basename15(input.sessionPath, ".jsonl");
70355
71855
  const parentDir = input.sessionPath.substring(0, input.sessionPath.lastIndexOf("/"));
70356
- const dirName = basename14(parentDir);
71856
+ const dirName = basename15(parentDir);
70357
71857
  let cwd;
70358
71858
  if (dirName.startsWith("-")) {
70359
71859
  const candidate = "/" + dirName.slice(1).replace(/-/g, "/");
@@ -70653,7 +72153,7 @@ var init_router = __esm(() => {
70653
72153
  });
70654
72154
  }
70655
72155
  const adapterType = input.adapter ?? "claude-code";
70656
- const name = input.name ?? basename14(projectPath);
72156
+ const name = input.name ?? basename15(projectPath);
70657
72157
  return ctx.bridge.createSession(adapterType, {
70658
72158
  name,
70659
72159
  cwd: projectPath
@@ -75005,6 +76505,7 @@ function renderServerCommandHelp() {
75005
76505
  "",
75006
76506
  "Usage:",
75007
76507
  " scout server start [options]",
76508
+ " scout server restart [options]",
75008
76509
  " scout server open [options]",
75009
76510
  " scout server caddyfile [options]",
75010
76511
  " scout server edge [options]",
@@ -75013,6 +76514,7 @@ function renderServerCommandHelp() {
75013
76514
  "",
75014
76515
  "Subcommands:",
75015
76516
  " start Start the Scout web UI server.",
76517
+ " restart Restart the Scout web UI server via the broker supervisor.",
75016
76518
  " open Open the Scout web UI (starts server on demand if needed).",
75017
76519
  " caddyfile Print the local edge Caddyfile for scout.local.",
75018
76520
  " edge Publish local names and run the Caddy edge.",
@@ -75264,6 +76766,14 @@ function parseServerSelection(args) {
75264
76766
  mode: "openscout-web"
75265
76767
  };
75266
76768
  }
76769
+ if (args[0] === "restart") {
76770
+ return {
76771
+ action: "restart",
76772
+ flagArgs: args.slice(1),
76773
+ entry: resolveScoutControlPlaneWebServerEntry(),
76774
+ mode: "openscout-web"
76775
+ };
76776
+ }
75267
76777
  if (args[0] === "open") {
75268
76778
  return {
75269
76779
  action: "open",
@@ -75673,6 +77183,11 @@ async function runServerCommand(context, args) {
75673
77183
  context.output.writeText(renderLocalEdgeTrustResult(ensureScoutLocalEdgeTrust({ env: mergedEnv })));
75674
77184
  return;
75675
77185
  }
77186
+ if (selection.action === "restart") {
77187
+ const result = await restartScoutWebServer();
77188
+ context.output.writeValue(result, renderServerRestartResult);
77189
+ return;
77190
+ }
75676
77191
  const bunExecutable = resolveBunExecutable4(mergedEnv);
75677
77192
  await new Promise((resolvePromise, rejectPromise) => {
75678
77193
  const child = spawn5(bunExecutable, ["run", selection.entry], {
@@ -75703,6 +77218,69 @@ async function runServerCommand(context, args) {
75703
77218
  });
75704
77219
  });
75705
77220
  }
77221
+ async function fetchSupervisorStatus() {
77222
+ const brokerUrl = resolveScoutBrokerUrl();
77223
+ const response = await fetch(new URL("/v1/web/status", brokerUrl));
77224
+ if (!response.ok) {
77225
+ throw new ScoutCliError(`broker supervisor returned HTTP ${response.status}`);
77226
+ }
77227
+ return await response.json();
77228
+ }
77229
+ async function waitForSupervisor(predicate, timeoutMs, intervalMs = 150) {
77230
+ const deadline = Date.now() + timeoutMs;
77231
+ let last = null;
77232
+ while (Date.now() < deadline) {
77233
+ last = await fetchSupervisorStatus().catch(() => last);
77234
+ if (last && predicate(last))
77235
+ return last;
77236
+ await new Promise((r) => setTimeout(r, intervalMs));
77237
+ }
77238
+ if (!last) {
77239
+ throw new ScoutCliError("broker supervisor did not respond within the timeout");
77240
+ }
77241
+ return last;
77242
+ }
77243
+ async function restartScoutWebServer() {
77244
+ const started = Date.now();
77245
+ const before = await fetchSupervisorStatus().catch(() => null);
77246
+ if (!before || !before.pid) {
77247
+ throw new ScoutCliError("Scout web server is not running under the broker supervisor \u2014 start it with `scout server start`.");
77248
+ }
77249
+ const previousPid = before.pid;
77250
+ try {
77251
+ process.kill(previousPid, "SIGTERM");
77252
+ } catch (error48) {
77253
+ const code = error48.code;
77254
+ if (code === "EPERM") {
77255
+ throw new ScoutCliError(`not permitted to signal pid ${previousPid}`);
77256
+ }
77257
+ if (code === "ESRCH") {} else {
77258
+ throw error48;
77259
+ }
77260
+ }
77261
+ const drained = await waitForSupervisor((status) => status.pid === null || status.pid !== previousPid, 12000);
77262
+ const drainMs = Date.now() - started;
77263
+ const respawned = drained.pid && drained.pid !== previousPid ? drained : await waitForSupervisor((status) => status.running && status.pid !== null && status.pid !== previousPid, 15000);
77264
+ return {
77265
+ webUrl: respawned.webUrl,
77266
+ port: respawned.port,
77267
+ previousPid,
77268
+ newPid: respawned.pid,
77269
+ drainMs,
77270
+ totalMs: Date.now() - started
77271
+ };
77272
+ }
77273
+ function renderServerRestartResult(value) {
77274
+ return [
77275
+ `Restarted Scout web server`,
77276
+ ` Previous PID: ${value.previousPid}`,
77277
+ ` New PID: ${value.newPid ?? "(unknown)"}`,
77278
+ ` URL: ${value.webUrl ?? "(unknown)"}`,
77279
+ ` Drain: ${value.drainMs}ms`,
77280
+ ` Total: ${value.totalMs}ms`
77281
+ ].join(`
77282
+ `);
77283
+ }
75706
77284
  var SERVER_OPEN_TIMEOUT_MS = 15000, SERVER_HEALTH_TIMEOUT_MS = 1500;
75707
77285
  var init_server4 = __esm(() => {
75708
77286
  init_local_config();
@@ -75710,6 +77288,7 @@ var init_server4 = __esm(() => {
75710
77288
  init_tool_resolution();
75711
77289
  init_setup();
75712
77290
  init_local_edge_dependencies();
77291
+ init_service();
75713
77292
  init_errors();
75714
77293
  });
75715
77294
 
@@ -75832,7 +77411,7 @@ import { resolve as resolve18, dirname as dirname17 } from "path";
75832
77411
  import { fileURLToPath as fileURLToPath11 } from "url";
75833
77412
  import { resolve as resolve22, isAbsolute as isAbsolute5, parse as parse7 } from "path";
75834
77413
  import { existsSync as existsSync25 } from "fs";
75835
- import { basename as basename15, join as join35 } from "path";
77414
+ import { basename as basename16, join as join35 } from "path";
75836
77415
  import os from "os";
75837
77416
  import path2 from "path";
75838
77417
  import { EventEmitter as EventEmitter3 } from "events";
@@ -79123,7 +80702,7 @@ function getBunfsRootPath() {
79123
80702
  return process.platform === "win32" ? "B:\\~BUN\\root" : "/$bunfs/root";
79124
80703
  }
79125
80704
  function normalizeBunfsPath(fileName) {
79126
- return join35(getBunfsRootPath(), basename15(fileName));
80705
+ return join35(getBunfsRootPath(), basename16(fileName));
79127
80706
  }
79128
80707
  function addDefaultParsers(parsers) {
79129
80708
  for (const parser of parsers) {
@@ -136307,14 +137886,18 @@ function displayName(actorId) {
136307
137886
  return segments[0] || trimmed;
136308
137887
  }
136309
137888
  function formatClock(value) {
136310
- const date6 = new Date(value * 1000);
137889
+ const timestamp = normalizeUnixTimestamp2(value);
137890
+ if (timestamp === null)
137891
+ return "--:--:--";
137892
+ const date6 = new Date(timestamp * 1000);
136311
137893
  return [date6.getHours(), date6.getMinutes(), date6.getSeconds()].map((entry) => String(entry).padStart(2, "0")).join(":");
136312
137894
  }
136313
137895
  function formatRelative(value) {
136314
- if (!value || !Number.isFinite(value)) {
137896
+ const timestamp = normalizeUnixTimestamp2(value);
137897
+ if (timestamp === null) {
136315
137898
  return "not seen";
136316
137899
  }
136317
- const age = Math.max(0, Math.floor(Date.now() / 1000) - value);
137900
+ const age = Math.max(0, Math.floor(Date.now() / 1000) - timestamp);
136318
137901
  if (age < 60)
136319
137902
  return `${age}s ago`;
136320
137903
  if (age < 3600)
@@ -136324,7 +137907,10 @@ function formatRelative(value) {
136324
137907
  return `${Math.floor(age / 86400)}d ago`;
136325
137908
  }
136326
137909
  function formatUptime2(value) {
136327
- const age = Math.max(0, Math.floor(Date.now() / 1000) - value);
137910
+ const timestamp = normalizeUnixTimestamp2(value);
137911
+ if (timestamp === null)
137912
+ return "unknown";
137913
+ const age = Math.max(0, Math.floor(Date.now() / 1000) - timestamp);
136328
137914
  if (age < 60)
136329
137915
  return `${age}s`;
136330
137916
  if (age < 3600)
@@ -136495,15 +138081,15 @@ function ChatPanel({
136495
138081
  }) {
136496
138082
  const bodyWidth = Math.max(24, width - 20);
136497
138083
  const ordered = snapshot.recentMessages.slice().sort((left, right) => {
136498
- const leftTs = left.createdAt;
136499
- const rightTs = right.createdAt;
138084
+ const leftTs = normalizeUnixTimestamp2(left.createdAt) ?? 0;
138085
+ const rightTs = normalizeUnixTimestamp2(right.createdAt) ?? 0;
136500
138086
  if (leftTs !== rightTs)
136501
138087
  return leftTs - rightTs;
136502
138088
  return String(left.id).localeCompare(String(right.id));
136503
138089
  });
136504
138090
  const rendered = ordered.flatMap((message) => {
136505
138091
  const actor = truncate(displayName(message.actorId), 12).padEnd(12, " ");
136506
- const stamp = formatClock(Math.floor(message.createdAt / 1000));
138092
+ const stamp = formatClock(message.createdAt);
136507
138093
  const lines = wrapText(message.body, bodyWidth);
136508
138094
  const color = messageTone(message);
136509
138095
  return lines.map((line, index2) => ({
@@ -137890,7 +139476,7 @@ var SCOUT_COMMANDS = [
137890
139476
  { name: "restart", summary: "Restart configured local agents" },
137891
139477
  { name: "menu", summary: "Launch the OpenScout macOS menu bar app" },
137892
139478
  { name: "vantage", summary: "Build a Hudson Vantage terminal canvas plan" },
137893
- { name: "config", summary: "View or set user config (name, etc.)" },
139479
+ { name: "config", summary: "View or set user config (name, handle, etc.)" },
137894
139480
  { name: "mesh", summary: "Mesh status and diagnostics" },
137895
139481
  { name: "pair", summary: "Pair a companion device via QR" },
137896
139482
  { name: "server", summary: "Run the Scout web UI (Bun; see: scout server start/open)" },
@@ -137930,6 +139516,7 @@ function renderScoutHelp(version3 = "0.2.19") {
137930
139516
  "Fast path:",
137931
139517
  ' scout send --to hudson "ready for review" # tell / update in a DM',
137932
139518
  ' scout ask --to hudson "review the parser" # owned work; wait for ack',
139519
+ ' scout ask --project ../talkie "compare auth" # no agent id needed; route by project path',
137933
139520
  " scout ask --to hudson --prompt-file ./handoff.md",
137934
139521
  " scout wait inv_123 --timeout 600 # follow an existing ask",
137935
139522
  " scout label watch release:0.2.66 --interval 2 # label-scoped activity firehose",
@@ -137972,6 +139559,12 @@ function renderScoutHelp(version3 = "0.2.19") {
137972
139559
  ' scout ask --as premotion.master.mini --to hudson "build the editor"',
137973
139560
  " scout ask --to hudson --prompt-file ./handoff.md",
137974
139561
  "",
139562
+ "Project targets:",
139563
+ ' scout ask --project ../talkie "compare auth" # broker resolves the concrete agent/session',
139564
+ ' scout ask --project ../talkie --harness codex "review this from Codex"',
139565
+ " scout ask '>> project:../talkie compare auth' # composer route form; quote >> in shells",
139566
+ " use --project when you know the repo path but not the current Scout handle",
139567
+ "",
137975
139568
  "Routing:",
137976
139569
  ' scout send --to hudson "ready for review" # one target -> DM',
137977
139570
  ' scout send --channel triage "need two reviewers" # explicit group thread',