@openscout/scout 0.2.46 → 0.2.48

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
@@ -382,6 +382,53 @@ function parseWatchCommandOptions(args, defaultCurrentDirectory) {
382
382
  channel
383
383
  };
384
384
  }
385
+ function parseLatestCommandOptions(args, defaultCurrentDirectory) {
386
+ const parsed = parseContextRootPrefix(args, defaultCurrentDirectory);
387
+ let agentId;
388
+ let actorId;
389
+ let conversationId;
390
+ let limit = 12;
391
+ for (let index = 0;index < parsed.args.length; index += 1) {
392
+ const current = parsed.args[index] ?? "";
393
+ if (current === "--agent" || current.startsWith("--agent=")) {
394
+ const value = parseFlagValue(parsed.args, index, "--agent");
395
+ agentId = value.value;
396
+ index = value.nextIndex;
397
+ continue;
398
+ }
399
+ if (current === "--actor" || current.startsWith("--actor=")) {
400
+ const value = parseFlagValue(parsed.args, index, "--actor");
401
+ actorId = value.value;
402
+ index = value.nextIndex;
403
+ continue;
404
+ }
405
+ if (current === "--conversation" || current.startsWith("--conversation=")) {
406
+ const value = parseFlagValue(parsed.args, index, "--conversation");
407
+ conversationId = value.value;
408
+ index = value.nextIndex;
409
+ continue;
410
+ }
411
+ if (current === "--limit" || current.startsWith("--limit=")) {
412
+ const value = parseFlagValue(parsed.args, index, "--limit");
413
+ const parsedLimit = Number.parseInt(value.value, 10);
414
+ if (!Number.isFinite(parsedLimit) || parsedLimit <= 0) {
415
+ throw new ScoutCliError(`invalid limit: ${value.value}`);
416
+ }
417
+ limit = parsedLimit;
418
+ index = value.nextIndex;
419
+ continue;
420
+ }
421
+ unexpectedArgs("latest", args);
422
+ }
423
+ return {
424
+ currentDirectory: parsed.currentDirectory,
425
+ args: parsed.args,
426
+ agentId,
427
+ actorId,
428
+ conversationId,
429
+ limit
430
+ };
431
+ }
385
432
  function parseEnrollCommandOptions(args, defaultCurrentDirectory) {
386
433
  const parsed = parseContextRootPrefix(args, defaultCurrentDirectory);
387
434
  let agentName = null;
@@ -2635,31 +2682,6 @@ var init_user_project_hints = __esm(() => {
2635
2682
  });
2636
2683
 
2637
2684
  // ../runtime/src/setup.ts
2638
- var exports_setup = {};
2639
- __export(exports_setup, {
2640
- writeRelayAgentOverrides: () => writeRelayAgentOverrides,
2641
- writeProjectConfig: () => writeProjectConfig,
2642
- writeOpenScoutSettings: () => writeOpenScoutSettings,
2643
- resolveRelayAgentConfig: () => resolveRelayAgentConfig,
2644
- readRelayAgentOverrides: () => readRelayAgentOverrides,
2645
- readProjectConfig: () => readProjectConfig,
2646
- readOpenScoutSettings: () => readOpenScoutSettings,
2647
- primaryDirectConversationIdForAgent: () => primaryDirectConversationIdForAgent,
2648
- loadResolvedRelayAgents: () => loadResolvedRelayAgents,
2649
- installScoutSkillToHarnesses: () => installScoutSkillToHarnesses,
2650
- initializeOpenScoutSetup: () => initializeOpenScoutSetup,
2651
- findNearestProjectRoot: () => findNearestProjectRoot,
2652
- ensureScoutRelayAgentConfigured: () => ensureScoutRelayAgentConfigured,
2653
- ensureRelayAgentConfigured: () => ensureRelayAgentConfigured,
2654
- ensureProjectConfigForDirectory: () => ensureProjectConfigForDirectory,
2655
- detectPreferredHarness: () => detectPreferredHarness,
2656
- clearGitBranchCache: () => clearGitBranchCache,
2657
- buildRelayAgentInstance: () => buildRelayAgentInstance,
2658
- SCOUT_PRIMARY_CONVERSATION_ID: () => SCOUT_PRIMARY_CONVERSATION_ID,
2659
- SCOUT_AGENT_ID: () => SCOUT_AGENT_ID,
2660
- MANAGED_AGENT_HARNESSES: () => MANAGED_AGENT_HARNESSES,
2661
- DEFAULT_OPERATOR_NAME: () => DEFAULT_OPERATOR_NAME
2662
- });
2663
2685
  import { execFileSync as execFileSync2 } from "child_process";
2664
2686
  import { existsSync as existsSync3, readFileSync } from "fs";
2665
2687
  import { access, mkdir as mkdir2, readdir as readdir2, readFile as readFile3, realpath, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
@@ -2838,13 +2860,6 @@ function normalizeTelegramConversationId(value) {
2838
2860
  }
2839
2861
  return normalized;
2840
2862
  }
2841
- function primaryDirectConversationIdForAgent(agentId) {
2842
- const normalizedAgentId = normalizeAgentId(agentId);
2843
- if (normalizedAgentId === SCOUT_AGENT_ID) {
2844
- return SCOUT_PRIMARY_CONVERSATION_ID;
2845
- }
2846
- return `dm.operator.${normalizedAgentId}`;
2847
- }
2848
2863
  function resolveNodeQualifier() {
2849
2864
  const fromEnv = process.env.OPENSCOUT_NODE_QUALIFIER?.trim();
2850
2865
  if (fromEnv) {
@@ -2890,13 +2905,6 @@ function detectGitBranch(projectRoot) {
2890
2905
  gitBranchCache.set(projectRoot, { branch, expiresAt: now + GIT_BRANCH_CACHE_TTL_MS });
2891
2906
  return branch;
2892
2907
  }
2893
- function clearGitBranchCache(projectRoot) {
2894
- if (projectRoot) {
2895
- gitBranchCache.delete(projectRoot);
2896
- } else {
2897
- gitBranchCache.clear();
2898
- }
2899
- }
2900
2908
  function resolveWorkspaceQualifier(projectRoot) {
2901
2909
  const normalizedProjectRoot = normalizePath(projectRoot);
2902
2910
  const branch = detectGitBranch(normalizedProjectRoot);
@@ -4383,52 +4391,6 @@ async function ensureRelayAgentConfigured(value, options = {}) {
4383
4391
  registrationKind: "configured"
4384
4392
  };
4385
4393
  }
4386
- async function ensureScoutRelayAgentConfigured(options = {}) {
4387
- const settings = await readOpenScoutSettings({ currentDirectory: options.currentDirectory });
4388
- const overrides = await readRelayAgentOverrides();
4389
- const resolvedProjectRoot = options.projectRoot ? normalizePath(options.projectRoot) : options.currentDirectory ? await findNearestProjectRoot(options.currentDirectory) ?? normalizePath(options.currentDirectory) : normalizePath(process.cwd());
4390
- const qualifiedAgentId = buildRelayAgentInstance(SCOUT_AGENT_ID, resolvedProjectRoot).id;
4391
- const existing = overrides[qualifiedAgentId] ?? overrides[SCOUT_AGENT_ID];
4392
- const nextOverride = {
4393
- ...existing,
4394
- agentId: qualifiedAgentId,
4395
- definitionId: SCOUT_AGENT_ID,
4396
- displayName: "Scout",
4397
- projectName: "OpenScout",
4398
- projectRoot: resolvedProjectRoot,
4399
- projectConfigPath: existing?.projectConfigPath ? normalizePath(existing.projectConfigPath) : null,
4400
- source: "manual",
4401
- startedAt: existing?.startedAt ?? nowSeconds(),
4402
- systemPrompt: existing?.systemPrompt?.trim() || undefined,
4403
- capabilities: normalizeCapabilities(existing?.capabilities),
4404
- defaultHarness: "claude",
4405
- harnessProfiles: buildHarnessProfiles({
4406
- projectRoot: resolvedProjectRoot,
4407
- sessionKey: qualifiedAgentId,
4408
- sessionPrefix: settings.agents.sessionPrefix,
4409
- defaultHarness: "claude",
4410
- profiles: existing?.harnessProfiles,
4411
- runtime: existing?.runtime,
4412
- launchArgs: normalizeLaunchArgs(existing?.launchArgs)
4413
- }),
4414
- runtime: {
4415
- cwd: resolvedProjectRoot,
4416
- harness: "claude",
4417
- transport: "claude_stream_json",
4418
- sessionId: normalizeTmuxSessionName(existing?.runtime?.sessionId, `${SCOUT_AGENT_ID}-claude`, settings.agents.sessionPrefix),
4419
- wakePolicy: "on_demand"
4420
- }
4421
- };
4422
- nextOverride.launchArgs = nextOverride.harnessProfiles?.claude?.launchArgs ?? [];
4423
- if (overrides[SCOUT_AGENT_ID]) {
4424
- delete overrides[SCOUT_AGENT_ID];
4425
- }
4426
- if (JSON.stringify(overrides[qualifiedAgentId]) !== JSON.stringify(nextOverride)) {
4427
- overrides[qualifiedAgentId] = nextOverride;
4428
- await writeRelayAgentOverrides(overrides);
4429
- }
4430
- return nextOverride;
4431
- }
4432
4394
  async function initializeOpenScoutSetup(options = {}) {
4433
4395
  return loadResolvedRelayAgents({
4434
4396
  currentDirectory: options.currentDirectory,
@@ -4592,418 +4554,6 @@ async function readOptionalFile(filePath) {
4592
4554
  return null;
4593
4555
  }
4594
4556
  }
4595
- function parseJsonObject(value) {
4596
- const trimmed = value?.trim();
4597
- if (!trimmed) {
4598
- return {};
4599
- }
4600
- try {
4601
- const parsed = JSON.parse(trimmed);
4602
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
4603
- } catch {
4604
- return {};
4605
- }
4606
- }
4607
- function appendActionOutput(blockState, output) {
4608
- if (!blockState || blockState.block.type !== "action" || !output) {
4609
- return;
4610
- }
4611
- const actionBlock = blockState.block;
4612
- actionBlock.action.output += output;
4613
- }
4614
- function updateBlockCompletion(blockState, completed) {
4615
- if (!blockState) {
4616
- return;
4617
- }
4618
- blockState.status = completed ? "completed" : "streaming";
4619
- blockState.block.status = completed ? "completed" : "streaming";
4620
- }
4621
- function parseQuestionAnswer(value) {
4622
- if (Array.isArray(value)) {
4623
- return value.map((entry) => typeof entry === "string" ? entry.trim() : String(entry).trim()).filter(Boolean);
4624
- }
4625
- if (typeof value === "string") {
4626
- return value.split(",").map((entry) => entry.trim()).filter(Boolean);
4627
- }
4628
- if (value == null) {
4629
- return [];
4630
- }
4631
- return [String(value)];
4632
- }
4633
- function isClaudeSessionGlobalEvent(event) {
4634
- return event.type === "system" && event.subtype === "init";
4635
- }
4636
- function stringifyToolResultContent(value) {
4637
- if (typeof value === "string") {
4638
- return value;
4639
- }
4640
- if (Array.isArray(value)) {
4641
- return value.map((entry) => stringifyToolResultContent(entry)).filter(Boolean).join(`
4642
- `);
4643
- }
4644
- if (value == null) {
4645
- return "";
4646
- }
4647
- try {
4648
- return JSON.stringify(value);
4649
- } catch {
4650
- return String(value);
4651
- }
4652
- }
4653
- function buildClaudeAction(toolName, toolUseId, input) {
4654
- if (toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") {
4655
- return {
4656
- kind: "file_change",
4657
- path: typeof input.file_path === "string" ? input.file_path : typeof input.path === "string" ? input.path : "",
4658
- diff: "",
4659
- status: "running",
4660
- output: ""
4661
- };
4662
- }
4663
- if (toolName === "Bash") {
4664
- return {
4665
- kind: "command",
4666
- command: typeof input.command === "string" ? input.command : "",
4667
- status: "running",
4668
- output: ""
4669
- };
4670
- }
4671
- if (toolName === "Agent") {
4672
- return {
4673
- kind: "subagent",
4674
- agentId: toolUseId,
4675
- agentName: typeof input.description === "string" ? input.description : undefined,
4676
- prompt: typeof input.prompt === "string" ? input.prompt : undefined,
4677
- status: "running",
4678
- output: ""
4679
- };
4680
- }
4681
- return {
4682
- kind: "tool_call",
4683
- toolName,
4684
- toolCallId: toolUseId,
4685
- input,
4686
- status: "running",
4687
- output: ""
4688
- };
4689
- }
4690
- function buildClaudeStreamJsonSessionSnapshot(raw, options, targetClaudeSessionId) {
4691
- const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
4692
- let resolvedSessionId = targetClaudeSessionId?.trim() || null;
4693
- if (!resolvedSessionId) {
4694
- for (const line of lines) {
4695
- try {
4696
- const event = JSON.parse(line);
4697
- const nextSessionId = typeof event.session_id === "string" ? event.session_id : typeof event.sessionId === "string" ? event.sessionId : null;
4698
- if (event.type === "system" && event.subtype === "init" && nextSessionId) {
4699
- resolvedSessionId = nextSessionId;
4700
- } else if (!resolvedSessionId && nextSessionId) {
4701
- resolvedSessionId = nextSessionId;
4702
- }
4703
- } catch {}
4704
- }
4705
- }
4706
- const snapshot = {
4707
- session: {
4708
- id: options.sessionId,
4709
- name: options.agentName,
4710
- adapterType: "claude_stream_json",
4711
- status: "idle",
4712
- cwd: options.cwd,
4713
- providerMeta: resolvedSessionId ? { transportSessionId: resolvedSessionId } : undefined
4714
- },
4715
- turns: []
4716
- };
4717
- let currentTurn = null;
4718
- const blockById = new Map;
4719
- let activeContentBlocks = new Map;
4720
- let pendingToolBlocks = new Map;
4721
- const startTurn = () => {
4722
- const turn = {
4723
- id: `${options.sessionId}:turn:${snapshot.turns.length + 1}`,
4724
- status: "streaming",
4725
- blocks: [],
4726
- startedAt: Date.now(),
4727
- nextBlockIndex: 0,
4728
- nextMessageIndex: 0
4729
- };
4730
- snapshot.turns.push(turn);
4731
- snapshot.currentTurnId = turn.id;
4732
- currentTurn = turn;
4733
- activeContentBlocks = new Map;
4734
- pendingToolBlocks = new Map;
4735
- snapshot.session.status = "active";
4736
- return turn;
4737
- };
4738
- const ensureTurn = () => currentTurn ?? startTurn();
4739
- const startTextualBlock = (type, index, initialText = "") => {
4740
- const turn = ensureTurn();
4741
- const blockId = `${turn.id}:m${turn.nextMessageIndex}:i${index}`;
4742
- const block = type === "text" ? {
4743
- id: blockId,
4744
- turnId: turn.id,
4745
- index: turn.nextBlockIndex++,
4746
- type: "text",
4747
- text: initialText,
4748
- status: "streaming"
4749
- } : {
4750
- id: blockId,
4751
- turnId: turn.id,
4752
- index: turn.nextBlockIndex++,
4753
- type: "reasoning",
4754
- text: initialText,
4755
- status: "streaming"
4756
- };
4757
- const blockState = {
4758
- block,
4759
- status: "streaming"
4760
- };
4761
- turn.blocks.push(blockState);
4762
- blockById.set(blockId, blockState);
4763
- activeContentBlocks.set(index, blockState);
4764
- return blockState;
4765
- };
4766
- const finalizeCurrentTurn = (status) => {
4767
- if (!currentTurn) {
4768
- return;
4769
- }
4770
- for (const blockState of activeContentBlocks.values()) {
4771
- updateBlockCompletion(blockState, true);
4772
- }
4773
- activeContentBlocks.clear();
4774
- currentTurn.status = status;
4775
- currentTurn.endedAt = Date.now();
4776
- currentTurn = null;
4777
- snapshot.currentTurnId = undefined;
4778
- snapshot.session.status = status === "error" ? "error" : "idle";
4779
- };
4780
- for (const line of lines) {
4781
- let event;
4782
- try {
4783
- event = JSON.parse(line);
4784
- } catch {
4785
- continue;
4786
- }
4787
- const eventSessionId = typeof event.session_id === "string" ? event.session_id : typeof event.sessionId === "string" ? event.sessionId : null;
4788
- if (resolvedSessionId) {
4789
- if (eventSessionId && eventSessionId !== resolvedSessionId) {
4790
- continue;
4791
- }
4792
- if (!eventSessionId && !isClaudeSessionGlobalEvent(event)) {
4793
- continue;
4794
- }
4795
- }
4796
- if (event.type === "system" && event.subtype === "init") {
4797
- if (typeof event.model === "string" && event.model.trim()) {
4798
- snapshot.session.model = event.model;
4799
- }
4800
- if (typeof event.cwd === "string" && event.cwd.trim()) {
4801
- snapshot.session.cwd = event.cwd;
4802
- }
4803
- snapshot.session.status = currentTurn ? "active" : "idle";
4804
- continue;
4805
- }
4806
- if (event.type === "stream_event") {
4807
- const streamEvent = event.event;
4808
- const streamEventType = typeof streamEvent?.type === "string" ? streamEvent.type : "";
4809
- if (streamEventType === "message_start") {
4810
- const turn = ensureTurn();
4811
- turn.nextMessageIndex += 1;
4812
- activeContentBlocks.clear();
4813
- pendingToolBlocks.clear();
4814
- snapshot.session.status = "active";
4815
- continue;
4816
- }
4817
- if (streamEventType === "content_block_start") {
4818
- const turn = ensureTurn();
4819
- const blockIndex = typeof streamEvent?.index === "number" ? streamEvent.index : 0;
4820
- const contentBlock = streamEvent?.content_block;
4821
- const contentType = typeof contentBlock?.type === "string" ? contentBlock.type : "";
4822
- if (contentType === "thinking") {
4823
- startTextualBlock("reasoning", blockIndex, typeof contentBlock?.thinking === "string" ? contentBlock.thinking : "");
4824
- } else if (contentType === "text") {
4825
- startTextualBlock("text", blockIndex, typeof contentBlock?.text === "string" ? contentBlock.text : "");
4826
- } else if (contentType === "tool_use") {
4827
- pendingToolBlocks.set(blockIndex, {
4828
- toolUseId: typeof contentBlock?.id === "string" ? contentBlock.id : `${turn.id}:tool:${blockIndex}`,
4829
- toolName: typeof contentBlock?.name === "string" ? contentBlock.name : "unknown",
4830
- input: contentBlock?.input && typeof contentBlock.input === "object" && !Array.isArray(contentBlock.input) ? contentBlock.input : {},
4831
- inputJson: "",
4832
- blockIndex: turn.nextBlockIndex++
4833
- });
4834
- }
4835
- continue;
4836
- }
4837
- if (streamEventType === "content_block_delta") {
4838
- const blockIndex = typeof streamEvent?.index === "number" ? streamEvent.index : 0;
4839
- const delta = streamEvent?.delta;
4840
- const deltaType = typeof delta?.type === "string" ? delta.type : "";
4841
- if (deltaType === "text_delta") {
4842
- const blockState = activeContentBlocks.get(blockIndex);
4843
- if (blockState?.block.type === "text" && typeof delta?.text === "string") {
4844
- blockState.block.text += delta.text;
4845
- }
4846
- continue;
4847
- }
4848
- if (deltaType === "thinking_delta") {
4849
- const blockState = activeContentBlocks.get(blockIndex);
4850
- if (blockState?.block.type === "reasoning" && typeof delta?.thinking === "string") {
4851
- blockState.block.text += delta.thinking;
4852
- }
4853
- continue;
4854
- }
4855
- if (deltaType === "input_json_delta") {
4856
- const pending = pendingToolBlocks.get(blockIndex);
4857
- if (pending && typeof delta?.partial_json === "string") {
4858
- pending.inputJson += delta.partial_json;
4859
- }
4860
- }
4861
- continue;
4862
- }
4863
- if (streamEventType === "content_block_stop") {
4864
- const blockIndex = typeof streamEvent?.index === "number" ? streamEvent.index : 0;
4865
- const blockState = activeContentBlocks.get(blockIndex);
4866
- if (blockState) {
4867
- updateBlockCompletion(blockState, true);
4868
- activeContentBlocks.delete(blockIndex);
4869
- continue;
4870
- }
4871
- const pendingTool = pendingToolBlocks.get(blockIndex);
4872
- if (!pendingTool) {
4873
- continue;
4874
- }
4875
- const turn = ensureTurn();
4876
- const parsedInput = {
4877
- ...pendingTool.input,
4878
- ...parseJsonObject(pendingTool.inputJson)
4879
- };
4880
- if (pendingTool.toolName === "AskUserQuestion") {
4881
- const questions = Array.isArray(parsedInput.questions) ? parsedInput.questions : [];
4882
- const firstQuestion = questions[0] ?? {};
4883
- const block = {
4884
- id: pendingTool.toolUseId,
4885
- turnId: turn.id,
4886
- index: pendingTool.blockIndex,
4887
- type: "question",
4888
- header: typeof firstQuestion.header === "string" ? firstQuestion.header : undefined,
4889
- question: typeof firstQuestion.question === "string" ? firstQuestion.question : "",
4890
- options: Array.isArray(firstQuestion.options) ? firstQuestion.options.map((option) => {
4891
- if (typeof option === "string") {
4892
- return { label: option };
4893
- }
4894
- const optionRecord = option;
4895
- return {
4896
- label: typeof optionRecord.label === "string" ? optionRecord.label : String(option),
4897
- description: typeof optionRecord.description === "string" ? optionRecord.description : undefined
4898
- };
4899
- }) : [],
4900
- multiSelect: Boolean(firstQuestion.multiSelect),
4901
- questionStatus: "awaiting_answer",
4902
- status: "streaming"
4903
- };
4904
- const nextBlockState = { block, status: "streaming" };
4905
- turn.blocks.push(nextBlockState);
4906
- blockById.set(block.id, nextBlockState);
4907
- } else {
4908
- const block = {
4909
- id: pendingTool.toolUseId,
4910
- turnId: turn.id,
4911
- index: pendingTool.blockIndex,
4912
- type: "action",
4913
- action: buildClaudeAction(pendingTool.toolName, pendingTool.toolUseId, parsedInput),
4914
- status: "streaming"
4915
- };
4916
- const nextBlockState = { block, status: "streaming" };
4917
- turn.blocks.push(nextBlockState);
4918
- blockById.set(block.id, nextBlockState);
4919
- }
4920
- pendingToolBlocks.delete(blockIndex);
4921
- }
4922
- continue;
4923
- }
4924
- if (event.type === "user") {
4925
- const message = event.message;
4926
- const content = Array.isArray(message?.content) ? message.content : [];
4927
- for (const entry of content) {
4928
- const result = entry;
4929
- if (result.type !== "tool_result" || typeof result.tool_use_id !== "string") {
4930
- continue;
4931
- }
4932
- const blockState = blockById.get(result.tool_use_id);
4933
- if (!blockState) {
4934
- continue;
4935
- }
4936
- if (blockState.block.type === "question") {
4937
- const questionBlock = blockState.block;
4938
- questionBlock.questionStatus = result.is_error ? "denied" : "answered";
4939
- const answer = parseQuestionAnswer(result.content);
4940
- questionBlock.answer = answer.length > 0 ? answer : undefined;
4941
- updateBlockCompletion(blockState, true);
4942
- continue;
4943
- }
4944
- appendActionOutput(blockState, stringifyToolResultContent(result.content));
4945
- if (blockState.block.type === "action") {
4946
- blockState.block.action.status = result.is_error ? "failed" : "completed";
4947
- }
4948
- updateBlockCompletion(blockState, true);
4949
- }
4950
- continue;
4951
- }
4952
- if (event.type === "result") {
4953
- const permissionDenials = Array.isArray(event.permission_denials) ? event.permission_denials : [];
4954
- for (const denial of permissionDenials) {
4955
- if (denial.tool_name !== "AskUserQuestion") {
4956
- continue;
4957
- }
4958
- const toolUseId = typeof denial.tool_use_id === "string" ? denial.tool_use_id : null;
4959
- const blockState = toolUseId ? blockById.get(toolUseId) : undefined;
4960
- if (blockState?.block.type === "question") {
4961
- blockState.block.questionStatus = "denied";
4962
- updateBlockCompletion(blockState, true);
4963
- continue;
4964
- }
4965
- const input = denial.tool_input && typeof denial.tool_input === "object" && !Array.isArray(denial.tool_input) ? denial.tool_input : {};
4966
- const questions = Array.isArray(input.questions) ? input.questions : [];
4967
- const firstQuestion = questions[0] ?? {};
4968
- const turn = ensureTurn();
4969
- const block = {
4970
- id: toolUseId ?? `${turn.id}:denied:${turn.nextBlockIndex}`,
4971
- turnId: turn.id,
4972
- index: turn.nextBlockIndex++,
4973
- type: "question",
4974
- header: typeof firstQuestion.header === "string" ? firstQuestion.header : undefined,
4975
- question: typeof firstQuestion.question === "string" ? firstQuestion.question : "",
4976
- options: Array.isArray(firstQuestion.options) ? firstQuestion.options.map((option) => {
4977
- if (typeof option === "string") {
4978
- return { label: option };
4979
- }
4980
- const optionRecord = option;
4981
- return {
4982
- label: typeof optionRecord.label === "string" ? optionRecord.label : String(option),
4983
- description: typeof optionRecord.description === "string" ? optionRecord.description : undefined
4984
- };
4985
- }) : [],
4986
- multiSelect: Boolean(firstQuestion.multiSelect),
4987
- questionStatus: "denied",
4988
- status: "completed"
4989
- };
4990
- const deniedBlock = { block, status: "completed" };
4991
- turn.blocks.push(deniedBlock);
4992
- blockById.set(block.id, deniedBlock);
4993
- }
4994
- finalizeCurrentTurn(event.is_error ? "error" : "completed");
4995
- continue;
4996
- }
4997
- if (event.type === "error") {
4998
- finalizeCurrentTurn("error");
4999
- }
5000
- }
5001
- if (!resolvedSessionId && snapshot.turns.length === 0) {
5002
- return null;
5003
- }
5004
- snapshot.session.status = currentTurn ? "active" : snapshot.session.status;
5005
- return snapshot;
5006
- }
5007
4557
 
5008
4558
  class ClaudeStreamJsonSession {
5009
4559
  options;
@@ -5286,35 +4836,10 @@ async function ensureClaudeStreamJsonAgentOnline(options) {
5286
4836
  session.update(options);
5287
4837
  return session.ensureOnline();
5288
4838
  }
5289
- async function invokeClaudeStreamJsonAgent(options) {
5290
- const session = getOrCreateSession(options);
5291
- session.update(options);
5292
- return session.invoke(options.prompt, options.timeoutMs);
5293
- }
5294
4839
  function isClaudeStreamJsonAgentAlive(options) {
5295
4840
  const session = sessions.get(sessionKey(options));
5296
4841
  return Boolean(session?.isAlive());
5297
4842
  }
5298
- async function answerClaudeStreamJsonQuestion(options, input) {
5299
- const session = sessions.get(sessionKey(options));
5300
- if (!session) {
5301
- throw new Error(`Claude stream-json session for ${options.agentName} is not running.`);
5302
- }
5303
- session.update(options);
5304
- await session.answerQuestion(input.blockId, input.answer);
5305
- }
5306
- async function getClaudeStreamJsonAgentSnapshot(options) {
5307
- const stdoutLogPath = join5(options.logsDirectory, "stdout.log");
5308
- const sessionStatePath = join5(options.runtimeDirectory, "claude-session-id.txt");
5309
- const [rawLog, persistedSessionId] = await Promise.all([
5310
- readOptionalFile(stdoutLogPath),
5311
- readOptionalFile(sessionStatePath)
5312
- ]);
5313
- if (!rawLog) {
5314
- return null;
5315
- }
5316
- return buildClaudeStreamJsonSessionSnapshot(rawLog, options, persistedSessionId);
5317
- }
5318
4843
  async function shutdownClaudeStreamJsonAgent(options, shutdownOptions = {}) {
5319
4844
  const key = sessionKey(options);
5320
4845
  const session = sessions.get(key);
@@ -5336,11 +4861,6 @@ var init_claude_stream_json = __esm(() => {
5336
4861
  import { spawn as spawn2 } from "child_process";
5337
4862
  import { access as access2, appendFile as appendFile2, constants, mkdir as mkdir4, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "fs/promises";
5338
4863
  import { delimiter, join as join6 } from "path";
5339
- function isCodexThreadGlobalMessage(message) {
5340
- const result = message.result;
5341
- const thread = result?.thread;
5342
- return typeof thread?.id === "string";
5343
- }
5344
4864
  function sessionKey2(options) {
5345
4865
  return `${options.agentName}:${options.sessionId}`;
5346
4866
  }
@@ -5407,14 +4927,6 @@ function parseJsonLine(line) {
5407
4927
  return null;
5408
4928
  }
5409
4929
  }
5410
- function parseJsonRecord(line) {
5411
- try {
5412
- const value = JSON.parse(line);
5413
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
5414
- } catch {
5415
- return null;
5416
- }
5417
- }
5418
4930
  function errorMessage2(error) {
5419
4931
  if (error instanceof Error) {
5420
4932
  return error.message;
@@ -5453,687 +4965,6 @@ async function readOptionalFile2(filePath) {
5453
4965
  return null;
5454
4966
  }
5455
4967
  }
5456
- async function readOptionalJsonRecord(filePath) {
5457
- const raw = await readOptionalFile2(filePath);
5458
- return raw ? parseJsonRecord(raw) : null;
5459
- }
5460
- function codexThreadStatusToSessionStatus(status) {
5461
- switch (status) {
5462
- case "active":
5463
- return "active";
5464
- case "idle":
5465
- return "idle";
5466
- case "error":
5467
- return "error";
5468
- default:
5469
- return "connecting";
5470
- }
5471
- }
5472
- function codexTurnStatusToTurnStatus(status) {
5473
- switch (status) {
5474
- case "completed":
5475
- return "completed";
5476
- case "interrupted":
5477
- return "interrupted";
5478
- case "failed":
5479
- return "error";
5480
- default:
5481
- return "streaming";
5482
- }
5483
- }
5484
- function stringifyCodexItem(value) {
5485
- if (typeof value === "string") {
5486
- return value;
5487
- }
5488
- if (value == null) {
5489
- return "";
5490
- }
5491
- try {
5492
- return JSON.stringify(value);
5493
- } catch {
5494
- return String(value);
5495
- }
5496
- }
5497
- function parseCodexTimestamp(value) {
5498
- if (typeof value === "number" && Number.isFinite(value)) {
5499
- return value > 1000000000000 ? value : value * 1000;
5500
- }
5501
- if (typeof value === "string") {
5502
- const parsed = Date.parse(value);
5503
- if (Number.isFinite(parsed)) {
5504
- return parsed;
5505
- }
5506
- }
5507
- return;
5508
- }
5509
- function parseCodexMaybeJson(value) {
5510
- if (typeof value !== "string") {
5511
- return value;
5512
- }
5513
- const trimmed = value.trim();
5514
- if (!trimmed) {
5515
- return value;
5516
- }
5517
- try {
5518
- return JSON.parse(trimmed);
5519
- } catch {
5520
- return value;
5521
- }
5522
- }
5523
- function extractCodexReasoningText(item) {
5524
- const summary = Array.isArray(item.summary) ? item.summary : [];
5525
- const content = Array.isArray(item.content) ? item.content : [];
5526
- const summaryText = summary.map((entry) => {
5527
- if (typeof entry === "string") {
5528
- return entry;
5529
- }
5530
- const record = entry;
5531
- if (typeof record.text === "string") {
5532
- return record.text;
5533
- }
5534
- if (typeof record.summary === "string") {
5535
- return record.summary;
5536
- }
5537
- return "";
5538
- }).filter(Boolean).join(`
5539
- `);
5540
- const contentText = content.map((entry) => {
5541
- if (typeof entry === "string") {
5542
- return entry;
5543
- }
5544
- const record = entry;
5545
- return typeof record.text === "string" ? record.text : "";
5546
- }).filter(Boolean).join(`
5547
- `);
5548
- return [summaryText, contentText].filter(Boolean).join(`
5549
-
5550
- `).trim();
5551
- }
5552
- function extractCodexMessageText(item) {
5553
- const content = Array.isArray(item.content) ? item.content : [];
5554
- return content.map((entry) => {
5555
- if (typeof entry === "string") {
5556
- return entry;
5557
- }
5558
- const record = entry;
5559
- return typeof record.text === "string" ? record.text : "";
5560
- }).filter(Boolean).join(`
5561
-
5562
- `).trim();
5563
- }
5564
- function extractCodexUserMessageText(item) {
5565
- const text = extractCodexMessageText(item);
5566
- if (text) {
5567
- return text;
5568
- }
5569
- return typeof item.text === "string" ? item.text.trim() : "";
5570
- }
5571
- function renderCodexActionOutput(item) {
5572
- if (typeof item.text === "string" && item.text.trim()) {
5573
- return item.text;
5574
- }
5575
- if (item.action !== undefined) {
5576
- return stringifyCodexItem(item.action);
5577
- }
5578
- return stringifyCodexItem(item);
5579
- }
5580
- function buildCodexActionBlock(item, turnId, index) {
5581
- return {
5582
- id: typeof item.id === "string" ? item.id : `${turnId}:action:${index}`,
5583
- turnId,
5584
- index,
5585
- type: "action",
5586
- status: "streaming",
5587
- action: {
5588
- kind: "tool_call",
5589
- toolName: typeof item.type === "string" ? item.type : "unknown",
5590
- toolCallId: typeof item.id === "string" ? item.id : `${turnId}:action:${index}`,
5591
- input: item,
5592
- output: "",
5593
- status: "running"
5594
- }
5595
- };
5596
- }
5597
- function buildCodexRolloutActionBlock(item, turnId, index) {
5598
- const itemType = typeof item.type === "string" ? item.type : "tool_call";
5599
- const toolCallId = typeof item.call_id === "string" ? item.call_id : `${turnId}:action:${index}`;
5600
- const toolName = typeof item.name === "string" ? item.name : itemType === "web_search_call" ? "web_search" : itemType;
5601
- const input = itemType === "function_call" ? parseCodexMaybeJson(item.arguments) : itemType === "custom_tool_call" ? item.input : item;
5602
- return {
5603
- id: toolCallId,
5604
- turnId,
5605
- index,
5606
- type: "action",
5607
- status: "streaming",
5608
- action: {
5609
- kind: "tool_call",
5610
- toolName,
5611
- toolCallId,
5612
- input,
5613
- output: "",
5614
- status: "running"
5615
- }
5616
- };
5617
- }
5618
- function finalizeCodexTurnBlocks(turn, status) {
5619
- for (const blockState of turn.blocks) {
5620
- if (blockState.status === "completed") {
5621
- continue;
5622
- }
5623
- blockState.status = "completed";
5624
- blockState.block.status = "completed";
5625
- if (blockState.block.type === "action") {
5626
- blockState.block.action.status = status === "completed" ? "completed" : "failed";
5627
- }
5628
- }
5629
- }
5630
- function setCodexProviderMeta(snapshot, threadId, threadPath) {
5631
- if (!threadId && !threadPath) {
5632
- return;
5633
- }
5634
- snapshot.session.providerMeta = {
5635
- ...snapshot.session.providerMeta ?? {},
5636
- ...threadId ? { threadId } : {},
5637
- ...threadPath ? { threadPath } : {}
5638
- };
5639
- }
5640
- function buildCodexAppServerSessionSnapshot(raw, options, targetThreadId) {
5641
- const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
5642
- let resolvedThreadId = targetThreadId?.trim() || null;
5643
- if (!resolvedThreadId) {
5644
- for (const line of lines) {
5645
- try {
5646
- const message = JSON.parse(line);
5647
- const resultThread = message.result?.thread;
5648
- const paramsThread = message.params?.thread;
5649
- const params = message.params;
5650
- const nextThreadId = typeof resultThread?.id === "string" ? resultThread.id : typeof paramsThread?.id === "string" ? paramsThread.id : typeof params?.threadId === "string" ? params.threadId : null;
5651
- if (nextThreadId) {
5652
- resolvedThreadId = nextThreadId;
5653
- }
5654
- } catch {}
5655
- }
5656
- }
5657
- const snapshot = {
5658
- session: {
5659
- id: options.sessionId,
5660
- name: options.agentName,
5661
- adapterType: "codex_app_server",
5662
- status: resolvedThreadId ? "idle" : "connecting",
5663
- cwd: options.cwd,
5664
- providerMeta: resolvedThreadId ? { threadId: resolvedThreadId } : undefined
5665
- },
5666
- turns: []
5667
- };
5668
- const turnsById = new Map;
5669
- const blocksById = new Map;
5670
- const ensureTurn = (turnId) => {
5671
- const existing = turnsById.get(turnId);
5672
- if (existing) {
5673
- return existing;
5674
- }
5675
- const turn = {
5676
- id: turnId,
5677
- status: "streaming",
5678
- blocks: [],
5679
- startedAt: Date.now(),
5680
- nextBlockIndex: 0
5681
- };
5682
- turnsById.set(turnId, turn);
5683
- snapshot.turns.push(turn);
5684
- snapshot.currentTurnId = turnId;
5685
- snapshot.session.status = "active";
5686
- return turn;
5687
- };
5688
- const completeBlock = (blockState) => {
5689
- if (!blockState) {
5690
- return;
5691
- }
5692
- blockState.status = "completed";
5693
- blockState.block.status = "completed";
5694
- };
5695
- for (const line of lines) {
5696
- let message;
5697
- try {
5698
- message = JSON.parse(line);
5699
- } catch {
5700
- continue;
5701
- }
5702
- const params = message.params;
5703
- const result = message.result;
5704
- const paramsThread = params?.thread;
5705
- const resultThread = result?.thread;
5706
- const lineThreadId = typeof params?.threadId === "string" ? params.threadId : typeof paramsThread?.id === "string" ? paramsThread.id : typeof resultThread?.id === "string" ? resultThread.id : null;
5707
- if (resolvedThreadId) {
5708
- if (lineThreadId && lineThreadId !== resolvedThreadId) {
5709
- continue;
5710
- }
5711
- if (!lineThreadId && !isCodexThreadGlobalMessage(message)) {
5712
- continue;
5713
- }
5714
- }
5715
- const resultModel = typeof result?.model === "string" ? result.model : null;
5716
- if (resultThread && resultModel) {
5717
- snapshot.session.model = resultModel;
5718
- }
5719
- if (typeof resultThread?.path === "string") {
5720
- snapshot.session.providerMeta = {
5721
- ...snapshot.session.providerMeta ?? {},
5722
- threadId: resolvedThreadId ?? resultThread.id,
5723
- threadPath: resultThread.path
5724
- };
5725
- }
5726
- if (typeof resultThread?.cwd === "string") {
5727
- snapshot.session.cwd = resultThread.cwd;
5728
- }
5729
- if (message.method === "thread/started" || message.method === "thread/name/updated") {
5730
- if (typeof paramsThread?.path === "string") {
5731
- snapshot.session.providerMeta = {
5732
- ...snapshot.session.providerMeta ?? {},
5733
- threadId: resolvedThreadId ?? paramsThread.id,
5734
- threadPath: paramsThread.path
5735
- };
5736
- }
5737
- if (typeof paramsThread?.cwd === "string") {
5738
- snapshot.session.cwd = paramsThread.cwd;
5739
- }
5740
- if (typeof paramsThread?.name === "string" && paramsThread.name.trim()) {
5741
- snapshot.session.name = paramsThread.name;
5742
- }
5743
- continue;
5744
- }
5745
- if (message.method === "thread/status/changed") {
5746
- const status = params?.status?.type;
5747
- snapshot.session.status = codexThreadStatusToSessionStatus(typeof status === "string" ? status : undefined);
5748
- continue;
5749
- }
5750
- if (message.method === "turn/started") {
5751
- const turn = params?.turn;
5752
- if (typeof turn?.id === "string") {
5753
- ensureTurn(turn.id);
5754
- }
5755
- continue;
5756
- }
5757
- if (message.method === "item/started") {
5758
- const item = params?.item;
5759
- const turnId = typeof params?.turnId === "string" ? params.turnId : null;
5760
- const itemType = typeof item?.type === "string" ? item.type : "";
5761
- const itemId = typeof item?.id === "string" ? item.id : null;
5762
- if (!turnId || !item || !itemId || !itemType) {
5763
- continue;
5764
- }
5765
- const turn = ensureTurn(turnId);
5766
- if (itemType === "userMessage") {
5767
- const block2 = {
5768
- id: itemId,
5769
- turnId,
5770
- index: turn.nextBlockIndex++,
5771
- type: "text",
5772
- text: extractCodexUserMessageText(item),
5773
- status: "streaming"
5774
- };
5775
- const blockState2 = { block: block2, status: "streaming" };
5776
- turn.blocks.push(blockState2);
5777
- blocksById.set(itemId, blockState2);
5778
- continue;
5779
- }
5780
- if (itemType === "agentMessage") {
5781
- const block2 = {
5782
- id: itemId,
5783
- turnId,
5784
- index: turn.nextBlockIndex++,
5785
- type: "text",
5786
- text: typeof item.text === "string" ? item.text : "",
5787
- status: "streaming"
5788
- };
5789
- const blockState2 = { block: block2, status: "streaming" };
5790
- turn.blocks.push(blockState2);
5791
- blocksById.set(itemId, blockState2);
5792
- continue;
5793
- }
5794
- if (itemType === "reasoning") {
5795
- const text = extractCodexReasoningText(item);
5796
- if (!text) {
5797
- continue;
5798
- }
5799
- const block2 = {
5800
- id: itemId,
5801
- turnId,
5802
- index: turn.nextBlockIndex++,
5803
- type: "reasoning",
5804
- text,
5805
- status: "streaming"
5806
- };
5807
- const blockState2 = { block: block2, status: "streaming" };
5808
- turn.blocks.push(blockState2);
5809
- blocksById.set(itemId, blockState2);
5810
- continue;
5811
- }
5812
- const block = buildCodexActionBlock(item, turnId, turn.nextBlockIndex++);
5813
- const blockState = { block, status: "streaming" };
5814
- turn.blocks.push(blockState);
5815
- blocksById.set(block.id, blockState);
5816
- continue;
5817
- }
5818
- if (message.method === "item/agentMessage/delta") {
5819
- const itemId = typeof params?.itemId === "string" ? params.itemId : null;
5820
- const delta = typeof params?.delta === "string" ? params.delta : "";
5821
- const blockState = itemId ? blocksById.get(itemId) : undefined;
5822
- if (blockState?.block.type === "text") {
5823
- blockState.block.text += delta;
5824
- }
5825
- continue;
5826
- }
5827
- if (message.method === "item/completed") {
5828
- const item = params?.item;
5829
- const turnId = typeof params?.turnId === "string" ? params.turnId : null;
5830
- const itemType = typeof item?.type === "string" ? item.type : "";
5831
- const itemId = typeof item?.id === "string" ? item.id : null;
5832
- if (!turnId || !item || !itemId || !itemType) {
5833
- continue;
5834
- }
5835
- if (itemType === "userMessage") {
5836
- const turn2 = ensureTurn(turnId);
5837
- let blockState2 = blocksById.get(itemId);
5838
- if (!blockState2) {
5839
- const block = {
5840
- id: itemId,
5841
- turnId,
5842
- index: turn2.nextBlockIndex++,
5843
- type: "text",
5844
- text: "",
5845
- status: "streaming"
5846
- };
5847
- blockState2 = { block, status: "streaming" };
5848
- turn2.blocks.push(blockState2);
5849
- blocksById.set(itemId, blockState2);
5850
- }
5851
- if (blockState2.block.type === "text") {
5852
- blockState2.block.text = extractCodexUserMessageText(item);
5853
- }
5854
- completeBlock(blockState2);
5855
- continue;
5856
- }
5857
- if (itemType === "agentMessage") {
5858
- const turn2 = ensureTurn(turnId);
5859
- let blockState2 = blocksById.get(itemId);
5860
- if (!blockState2) {
5861
- const block = {
5862
- id: itemId,
5863
- turnId,
5864
- index: turn2.nextBlockIndex++,
5865
- type: "text",
5866
- text: "",
5867
- status: "streaming"
5868
- };
5869
- blockState2 = { block, status: "streaming" };
5870
- turn2.blocks.push(blockState2);
5871
- blocksById.set(itemId, blockState2);
5872
- }
5873
- if (blockState2.block.type === "text" && typeof item.text === "string" && item.text.length > 0) {
5874
- blockState2.block.text = item.text;
5875
- }
5876
- completeBlock(blockState2);
5877
- continue;
5878
- }
5879
- if (itemType === "reasoning") {
5880
- const text = extractCodexReasoningText(item);
5881
- if (!text) {
5882
- continue;
5883
- }
5884
- const turn2 = ensureTurn(turnId);
5885
- let blockState2 = blocksById.get(itemId);
5886
- if (!blockState2) {
5887
- const block = {
5888
- id: itemId,
5889
- turnId,
5890
- index: turn2.nextBlockIndex++,
5891
- type: "reasoning",
5892
- text,
5893
- status: "streaming"
5894
- };
5895
- blockState2 = { block, status: "streaming" };
5896
- turn2.blocks.push(blockState2);
5897
- blocksById.set(itemId, blockState2);
5898
- }
5899
- if (blockState2.block.type === "reasoning" && text) {
5900
- blockState2.block.text = text;
5901
- }
5902
- completeBlock(blockState2);
5903
- continue;
5904
- }
5905
- const turn = ensureTurn(turnId);
5906
- let blockState = blocksById.get(itemId);
5907
- if (!blockState) {
5908
- const block = buildCodexActionBlock(item, turnId, turn.nextBlockIndex++);
5909
- blockState = { block, status: "streaming" };
5910
- turn.blocks.push(blockState);
5911
- blocksById.set(itemId, blockState);
5912
- }
5913
- if (blockState.block.type === "action") {
5914
- blockState.block.action.output = renderCodexActionOutput(item);
5915
- blockState.block.action.status = "completed";
5916
- }
5917
- completeBlock(blockState);
5918
- continue;
5919
- }
5920
- if (message.method === "turn/completed") {
5921
- const turn = params?.turn;
5922
- if (!turn || typeof turn.id !== "string") {
5923
- continue;
5924
- }
5925
- const turnState = ensureTurn(turn.id);
5926
- turnState.status = codexTurnStatusToTurnStatus(typeof turn.status === "string" ? turn.status : undefined);
5927
- turnState.endedAt = Date.now();
5928
- if (snapshot.currentTurnId === turn.id) {
5929
- snapshot.currentTurnId = undefined;
5930
- }
5931
- snapshot.session.status = turnState.status === "error" ? "error" : "idle";
5932
- continue;
5933
- }
5934
- if (message.method === "error") {
5935
- snapshot.session.status = "error";
5936
- }
5937
- }
5938
- if (!resolvedThreadId && snapshot.turns.length === 0) {
5939
- return null;
5940
- }
5941
- return snapshot;
5942
- }
5943
- function buildCodexRolloutSessionSnapshot(raw, options, targetThreadId, rolloutPath) {
5944
- const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
5945
- let resolvedThreadId = targetThreadId?.trim() || null;
5946
- const snapshot = {
5947
- session: {
5948
- id: options.sessionId,
5949
- name: options.agentName,
5950
- adapterType: "codex_app_server",
5951
- status: resolvedThreadId ? "idle" : "connecting",
5952
- cwd: options.cwd
5953
- },
5954
- turns: []
5955
- };
5956
- setCodexProviderMeta(snapshot, resolvedThreadId, rolloutPath ?? null);
5957
- const turnsById = new Map;
5958
- const blocksByCallId = new Map;
5959
- let currentTurnId = null;
5960
- const ensureTurn = (turnId, startedAt) => {
5961
- const existing = turnsById.get(turnId);
5962
- if (existing) {
5963
- if (startedAt && !existing.startedAt) {
5964
- existing.startedAt = startedAt;
5965
- }
5966
- return existing;
5967
- }
5968
- const turn = {
5969
- id: turnId,
5970
- status: "streaming",
5971
- blocks: [],
5972
- startedAt: startedAt ?? Date.now(),
5973
- nextBlockIndex: 0
5974
- };
5975
- turnsById.set(turnId, turn);
5976
- snapshot.turns.push(turn);
5977
- snapshot.currentTurnId = turnId;
5978
- snapshot.session.status = "active";
5979
- return turn;
5980
- };
5981
- for (const line of lines) {
5982
- const message = parseJsonRecord(line);
5983
- if (!message) {
5984
- continue;
5985
- }
5986
- const timestamp = parseCodexTimestamp(message.timestamp) ?? Date.now();
5987
- const entryType = typeof message.type === "string" ? message.type : "";
5988
- const payload = message.payload && typeof message.payload === "object" && !Array.isArray(message.payload) ? message.payload : undefined;
5989
- if (!payload) {
5990
- continue;
5991
- }
5992
- if (entryType === "session_meta") {
5993
- const sessionThreadId = typeof payload.id === "string" ? payload.id : null;
5994
- if (sessionThreadId) {
5995
- if (resolvedThreadId && sessionThreadId !== resolvedThreadId) {
5996
- return null;
5997
- }
5998
- resolvedThreadId = sessionThreadId;
5999
- }
6000
- if (typeof payload.cwd === "string" && payload.cwd.trim()) {
6001
- snapshot.session.cwd = payload.cwd;
6002
- }
6003
- setCodexProviderMeta(snapshot, resolvedThreadId, rolloutPath ?? null);
6004
- snapshot.session.status = resolvedThreadId ? "idle" : snapshot.session.status;
6005
- continue;
6006
- }
6007
- if (entryType === "turn_context") {
6008
- if (typeof payload.cwd === "string" && payload.cwd.trim()) {
6009
- snapshot.session.cwd = payload.cwd;
6010
- }
6011
- if (typeof payload.model === "string" && payload.model.trim()) {
6012
- snapshot.session.model = payload.model;
6013
- }
6014
- continue;
6015
- }
6016
- if (entryType === "event_msg") {
6017
- const payloadType2 = typeof payload.type === "string" ? payload.type : "";
6018
- if (payloadType2 === "task_started") {
6019
- const turnId = typeof payload.turn_id === "string" ? payload.turn_id : null;
6020
- if (!turnId) {
6021
- continue;
6022
- }
6023
- currentTurnId = turnId;
6024
- ensureTurn(turnId, parseCodexTimestamp(payload.started_at) ?? timestamp);
6025
- snapshot.currentTurnId = turnId;
6026
- snapshot.session.status = "active";
6027
- continue;
6028
- }
6029
- if (payloadType2 === "task_complete" || payloadType2 === "turn_aborted") {
6030
- const turnId = typeof payload.turn_id === "string" ? payload.turn_id : currentTurnId;
6031
- if (!turnId) {
6032
- continue;
6033
- }
6034
- const turn2 = ensureTurn(turnId);
6035
- turn2.status = payloadType2 === "task_complete" ? "completed" : payload.reason === "interrupted" ? "interrupted" : "error";
6036
- turn2.endedAt = parseCodexTimestamp(payload.completed_at) ?? timestamp;
6037
- finalizeCodexTurnBlocks(turn2, turn2.status);
6038
- if (snapshot.currentTurnId === turnId) {
6039
- snapshot.currentTurnId = undefined;
6040
- }
6041
- currentTurnId = currentTurnId === turnId ? null : currentTurnId;
6042
- snapshot.session.status = turn2.status === "error" ? "error" : "idle";
6043
- }
6044
- continue;
6045
- }
6046
- if (entryType !== "response_item" || !currentTurnId) {
6047
- continue;
6048
- }
6049
- const turn = ensureTurn(currentTurnId);
6050
- const payloadType = typeof payload.type === "string" ? payload.type : "";
6051
- if (payloadType === "message" && payload.role === "user") {
6052
- const text = extractCodexMessageText(payload);
6053
- if (!text) {
6054
- continue;
6055
- }
6056
- const block = {
6057
- id: `${turn.id}:text:${turn.nextBlockIndex}`,
6058
- turnId: turn.id,
6059
- index: turn.nextBlockIndex++,
6060
- type: "text",
6061
- text,
6062
- status: "completed"
6063
- };
6064
- turn.blocks.push({ block, status: "completed" });
6065
- continue;
6066
- }
6067
- if (payloadType === "message" && payload.role === "assistant") {
6068
- const text = extractCodexMessageText(payload);
6069
- if (!text) {
6070
- continue;
6071
- }
6072
- const block = {
6073
- id: `${turn.id}:text:${turn.nextBlockIndex}`,
6074
- turnId: turn.id,
6075
- index: turn.nextBlockIndex++,
6076
- type: "text",
6077
- text,
6078
- status: "completed"
6079
- };
6080
- turn.blocks.push({ block, status: "completed" });
6081
- continue;
6082
- }
6083
- if (payloadType === "reasoning") {
6084
- const text = extractCodexReasoningText(payload);
6085
- if (!text) {
6086
- continue;
6087
- }
6088
- const block = {
6089
- id: `${turn.id}:reasoning:${turn.nextBlockIndex}`,
6090
- turnId: turn.id,
6091
- index: turn.nextBlockIndex++,
6092
- type: "reasoning",
6093
- text,
6094
- status: "completed"
6095
- };
6096
- turn.blocks.push({ block, status: "completed" });
6097
- continue;
6098
- }
6099
- if (payloadType === "function_call" || payloadType === "custom_tool_call" || payloadType === "web_search_call") {
6100
- const block = buildCodexRolloutActionBlock(payload, turn.id, turn.nextBlockIndex++);
6101
- const blockState = { block, status: "streaming" };
6102
- turn.blocks.push(blockState);
6103
- blocksByCallId.set(block.id, blockState);
6104
- continue;
6105
- }
6106
- if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
6107
- const callId = typeof payload.call_id === "string" ? payload.call_id : null;
6108
- if (!callId) {
6109
- continue;
6110
- }
6111
- let blockState = blocksByCallId.get(callId);
6112
- if (!blockState) {
6113
- const block = buildCodexRolloutActionBlock({
6114
- type: "tool_call",
6115
- call_id: callId,
6116
- name: "unknown"
6117
- }, turn.id, turn.nextBlockIndex++);
6118
- blockState = { block, status: "streaming" };
6119
- turn.blocks.push(blockState);
6120
- blocksByCallId.set(callId, blockState);
6121
- }
6122
- if (blockState.block.type === "action") {
6123
- blockState.block.action.output = stringifyCodexItem(payload.output);
6124
- blockState.block.action.status = "completed";
6125
- }
6126
- blockState.status = "completed";
6127
- blockState.block.status = "completed";
6128
- }
6129
- }
6130
- if (!resolvedThreadId && snapshot.turns.length === 0) {
6131
- return null;
6132
- }
6133
- setCodexProviderMeta(snapshot, resolvedThreadId, rolloutPath ?? null);
6134
- snapshot.session.status = snapshot.currentTurnId ? "active" : snapshot.session.status;
6135
- return snapshot;
6136
- }
6137
4968
  function isMissingCodexRolloutError(error) {
6138
4969
  const message = errorMessage2(error).toLowerCase();
6139
4970
  return message.includes("no rollout found for thread id");
@@ -6780,48 +5611,10 @@ async function ensureCodexAppServerAgentOnline(options) {
6780
5611
  session.update(options);
6781
5612
  return session.ensureOnline();
6782
5613
  }
6783
- async function invokeCodexAppServerAgent(options) {
6784
- const session = getOrCreateSession2(options);
6785
- session.update(options);
6786
- return session.invoke(options.prompt, options.timeoutMs);
6787
- }
6788
- async function sendCodexAppServerAgent(options) {
6789
- const session = getOrCreateSession2(options);
6790
- session.update(options);
6791
- if (session.hasActiveTurn()) {
6792
- return session.steerAndWait(options.prompt, options.timeoutMs);
6793
- }
6794
- return session.invoke(options.prompt, options.timeoutMs);
6795
- }
6796
5614
  function isCodexAppServerAgentAlive(options) {
6797
5615
  const session = sessions2.get(sessionKey2(options));
6798
5616
  return Boolean(session?.isAlive());
6799
5617
  }
6800
- async function getCodexAppServerAgentSnapshot(options) {
6801
- const stdoutLogPath = join6(options.logsDirectory, "stdout.log");
6802
- const threadIdPath = join6(options.runtimeDirectory, "codex-thread-id.txt");
6803
- const statePath = join6(options.runtimeDirectory, "state.json");
6804
- const [rawLog, persistedThreadId, persistedState] = await Promise.all([
6805
- readOptionalFile2(stdoutLogPath),
6806
- readOptionalFile2(threadIdPath),
6807
- readOptionalJsonRecord(statePath)
6808
- ]);
6809
- const resolvedThreadId = options.threadId ?? persistedThreadId ?? metadataString(persistedState ?? undefined, "threadId") ?? undefined;
6810
- const persistedThreadPath = metadataString(persistedState ?? undefined, "threadPath");
6811
- if (persistedThreadPath) {
6812
- const rawRollout = await readOptionalFile2(persistedThreadPath);
6813
- if (rawRollout) {
6814
- const rolloutSnapshot = buildCodexRolloutSessionSnapshot(rawRollout, options, resolvedThreadId, persistedThreadPath);
6815
- if (rolloutSnapshot) {
6816
- return rolloutSnapshot;
6817
- }
6818
- }
6819
- }
6820
- if (!rawLog) {
6821
- return null;
6822
- }
6823
- return buildCodexAppServerSessionSnapshot(rawLog, options, resolvedThreadId);
6824
- }
6825
5618
  async function shutdownCodexAppServerAgent(options, shutdownOptions = {}) {
6826
5619
  const key = sessionKey2(options);
6827
5620
  const session = sessions2.get(key);
@@ -6841,49 +5634,6 @@ var init_codex_app_server = __esm(() => {
6841
5634
  });
6842
5635
 
6843
5636
  // ../runtime/src/collaboration-contract.ts
6844
- function metadataValue(metadata, key) {
6845
- return metadata?.[key];
6846
- }
6847
- function recordFromInvocation(invocation, key) {
6848
- const contextRecord = metadataValue(invocation.context, key);
6849
- if (typeof contextRecord !== "undefined") {
6850
- return contextRecord;
6851
- }
6852
- const nestedContext = metadataValue(invocation.context, "collaboration");
6853
- if (nestedContext && typeof nestedContext === "object" && !Array.isArray(nestedContext)) {
6854
- const nestedValue = metadataValue(nestedContext, key);
6855
- if (typeof nestedValue !== "undefined") {
6856
- return nestedValue;
6857
- }
6858
- }
6859
- const metadataRecord2 = metadataValue(invocation.metadata, key);
6860
- if (typeof metadataRecord2 !== "undefined") {
6861
- return metadataRecord2;
6862
- }
6863
- const nestedMetadata = metadataValue(invocation.metadata, "collaboration");
6864
- if (nestedMetadata && typeof nestedMetadata === "object" && !Array.isArray(nestedMetadata)) {
6865
- return metadataValue(nestedMetadata, key);
6866
- }
6867
- return;
6868
- }
6869
- function stringFromInvocation(invocation, key) {
6870
- const value = recordFromInvocation(invocation, key);
6871
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
6872
- }
6873
- function waitingOnLabel(value) {
6874
- if (typeof value === "string" && value.trim().length > 0) {
6875
- return value.trim();
6876
- }
6877
- if (!value || typeof value !== "object" || Array.isArray(value)) {
6878
- return;
6879
- }
6880
- const waitingOn = value;
6881
- const kind = typeof waitingOn.kind === "string" ? waitingOn.kind : undefined;
6882
- const label = typeof waitingOn.label === "string" ? waitingOn.label.trim() : "";
6883
- const targetId = typeof waitingOn.targetId === "string" ? waitingOn.targetId.trim() : "";
6884
- const parts = [label || kind, targetId || undefined].filter(Boolean);
6885
- return parts.length > 0 ? parts.join(" \xB7 ") : undefined;
6886
- }
6887
5637
  function buildCollaborationContractPrompt(agentId) {
6888
5638
  return [
6889
5639
  "Collaboration contract:",
@@ -6900,38 +5650,6 @@ function buildCollaborationContractPrompt(agentId) {
6900
5650
  ].join(`
6901
5651
  `);
6902
5652
  }
6903
- function buildInvocationCollaborationContextPrompt(invocation) {
6904
- const recordId = stringFromInvocation(invocation, "recordId") ?? stringFromInvocation(invocation, "collaborationRecordId");
6905
- const kind = stringFromInvocation(invocation, "kind") ?? stringFromInvocation(invocation, "collaborationKind");
6906
- const state = stringFromInvocation(invocation, "state") ?? stringFromInvocation(invocation, "collaborationState");
6907
- const ownerId = stringFromInvocation(invocation, "ownerId");
6908
- const nextMoveOwnerId = stringFromInvocation(invocation, "nextMoveOwnerId");
6909
- const wakeReason = stringFromInvocation(invocation, "wakeReason");
6910
- const targetAgentId = stringFromInvocation(invocation, "targetAgentId");
6911
- const waitingOn = waitingOnLabel(recordFromInvocation(invocation, "waitingOn") ?? recordFromInvocation(invocation, "waiting_on"));
6912
- const acceptanceState = stringFromInvocation(invocation, "acceptanceState");
6913
- const requestedById = stringFromInvocation(invocation, "requestedById");
6914
- const askedById = stringFromInvocation(invocation, "askedById");
6915
- const askedOfId = stringFromInvocation(invocation, "askedOfId");
6916
- const lines = [
6917
- recordId ? ` - Record: ${kind ? `${kind} ` : ""}${recordId}` : undefined,
6918
- state ? ` - State: ${state}` : undefined,
6919
- ownerId ? ` - Owner: ${ownerId}` : undefined,
6920
- nextMoveOwnerId ? ` - Next move owner: ${nextMoveOwnerId}` : undefined,
6921
- wakeReason ? ` - Wake reason: ${wakeReason}` : undefined,
6922
- targetAgentId ? ` - Wake target: ${targetAgentId}` : undefined,
6923
- waitingOn ? ` - Waiting on: ${waitingOn}` : undefined,
6924
- acceptanceState ? ` - Acceptance: ${acceptanceState}` : undefined,
6925
- requestedById ? ` - Requested by: ${requestedById}` : undefined,
6926
- askedById ? ` - Asked by: ${askedById}` : undefined,
6927
- askedOfId ? ` - Asked of: ${askedOfId}` : undefined
6928
- ].filter((value) => Boolean(value));
6929
- if (lines.length === 0) {
6930
- return;
6931
- }
6932
- return ["Collaboration context:", ...lines].join(`
6933
- `);
6934
- }
6935
5653
 
6936
5654
  // ../runtime/src/broker-service.ts
6937
5655
  import { spawnSync } from "child_process";
@@ -7460,43 +6178,6 @@ var init_local_agent_template = __esm(() => {
7460
6178
  });
7461
6179
 
7462
6180
  // ../runtime/src/local-agents.ts
7463
- var exports_local_agents = {};
7464
- __export(exports_local_agents, {
7465
- updateLocalAgentConfig: () => updateLocalAgentConfig,
7466
- stripLocalAgentReplyMetadata: () => stripLocalAgentReplyMetadata,
7467
- stopLocalAgent: () => stopLocalAgent,
7468
- stopAllLocalAgents: () => stopAllLocalAgents,
7469
- startLocalAgent: () => startLocalAgent,
7470
- shutdownLocalSessionEndpoint: () => shutdownLocalSessionEndpoint,
7471
- shouldDisableGeneratedCodexEndpoint: () => shouldDisableGeneratedCodexEndpoint,
7472
- sendTmuxPrompt: () => sendTmuxPrompt,
7473
- restartLocalAgent: () => restartLocalAgent,
7474
- restartAllLocalAgents: () => restartAllLocalAgents,
7475
- resolveLocalAgentIdentity: () => resolveLocalAgentIdentity,
7476
- resolveLocalAgentByName: () => resolveLocalAgentByName,
7477
- renderLocalAgentSystemPromptTemplate: () => renderLocalAgentSystemPromptTemplate,
7478
- loadRegisteredLocalAgentBindings: () => loadRegisteredLocalAgentBindings,
7479
- listLocalAgents: () => listLocalAgents,
7480
- isLocalAgentSessionAlive: () => isLocalAgentSessionAlive,
7481
- isLocalAgentEndpointAlive: () => isLocalAgentEndpointAlive,
7482
- invokeLocalAgentEndpoint: () => invokeLocalAgentEndpoint,
7483
- interruptLocalAgent: () => interruptLocalAgent,
7484
- inferLocalAgentBinding: () => inferLocalAgentBinding,
7485
- getLocalAgentSessionSnapshot: () => getLocalAgentSessionSnapshot,
7486
- getLocalAgentEndpointSessionSnapshot: () => getLocalAgentEndpointSessionSnapshot,
7487
- getLocalAgentConfig: () => getLocalAgentConfig,
7488
- ensureLocalSessionEndpointOnline: () => ensureLocalSessionEndpointOnline,
7489
- ensureLocalAgentBindingOnline: () => ensureLocalAgentBindingOnline,
7490
- buildTmuxLaunchShellCommand: () => buildTmuxLaunchShellCommand,
7491
- buildLocalAgentSystemPromptTemplate: () => buildLocalAgentSystemPromptTemplate,
7492
- buildLocalAgentSystemPrompt: () => buildLocalAgentSystemPrompt,
7493
- buildLocalAgentNudge: () => buildLocalAgentNudge,
7494
- buildLocalAgentDirectInvocationPrompt: () => buildLocalAgentDirectInvocationPrompt,
7495
- buildAttachedSessionInvocationPrompt: () => buildAttachedSessionInvocationPrompt,
7496
- answerLocalAgentSessionQuestion: () => answerLocalAgentSessionQuestion,
7497
- SUPPORTED_LOCAL_AGENT_HARNESSES: () => SUPPORTED_LOCAL_AGENT_HARNESSES
7498
- });
7499
- import { randomUUID as randomUUID2 } from "crypto";
7500
6181
  import { execFileSync as execFileSync3, execSync } from "child_process";
7501
6182
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
7502
6183
  import { mkdir as mkdir5, rm as rm4, stat as stat3, writeFile as writeFile5 } from "fs/promises";
@@ -7527,21 +6208,9 @@ function resolveBrokerUrl() {
7527
6208
  function nowSeconds2() {
7528
6209
  return Math.floor(Date.now() / 1000);
7529
6210
  }
7530
- function normalizeBrokerTimestamp(value) {
7531
- return value > 10000000000 ? Math.floor(value / 1000) : value;
7532
- }
7533
6211
  function brokerRelayCommand() {
7534
6212
  return `node ${JSON.stringify(join8(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs"))}`;
7535
6213
  }
7536
- function sleep2(ms) {
7537
- return new Promise((resolve7) => setTimeout(resolve7, ms));
7538
- }
7539
- function createLocalAgentFlightId() {
7540
- return `f-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
7541
- }
7542
- function escapeRegExp(value) {
7543
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7544
- }
7545
6214
  function titleCaseLocalAgentName(value) {
7546
6215
  return value.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
7547
6216
  }
@@ -7786,13 +6455,6 @@ function expandHomePath3(value) {
7786
6455
  function normalizeProjectPath(value) {
7787
6456
  return resolve6(expandHomePath3(value.trim() || "."));
7788
6457
  }
7789
- function compactHomePath(value) {
7790
- const home = process.env.HOME;
7791
- if (!home) {
7792
- return value;
7793
- }
7794
- return value.startsWith(home) ? value.replace(home, "~") : value;
7795
- }
7796
6458
  function normalizeTmuxSessionName2(value, agentId) {
7797
6459
  const fallback = `relay-${agentId}`;
7798
6460
  const trimmed = value?.trim() ?? "";
@@ -8038,134 +6700,6 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
8038
6700
  }
8039
6701
  };
8040
6702
  }
8041
- function buildLocalAgentConfigState(agentId, record) {
8042
- return {
8043
- agentId,
8044
- editable: true,
8045
- systemPrompt: record.systemPrompt || buildLocalAgentSystemPromptTemplate(),
8046
- runtime: {
8047
- cwd: compactHomePath(record.cwd),
8048
- harness: normalizeLocalAgentHarness(record.harness),
8049
- transport: normalizeLocalAgentTransport(record.transport, normalizeLocalAgentHarness(record.harness)),
8050
- sessionId: record.tmuxSession,
8051
- wakePolicy: "on_demand"
8052
- },
8053
- launchArgs: normalizeLocalAgentLaunchArgs(record.launchArgs),
8054
- capabilities: normalizeLocalAgentCapabilities(record.capabilities),
8055
- applyMode: "restart",
8056
- templateHint: LOCAL_AGENT_SYSTEM_PROMPT_TEMPLATE_HINT
8057
- };
8058
- }
8059
- async function assertDirectoryExists(directory) {
8060
- const stats = await stat3(directory);
8061
- if (!stats.isDirectory()) {
8062
- throw new Error(`${directory} is not a directory.`);
8063
- }
8064
- }
8065
- async function resolveConfiguredLocalAgentRecord(agentId) {
8066
- const overrides = await readRelayAgentOverrides();
8067
- const override = overrides[agentId];
8068
- if (override) {
8069
- return localAgentRecordFromRelayAgentOverride(agentId, override);
8070
- }
8071
- const registry = await readLocalAgentRegistry();
8072
- const record = registry[agentId];
8073
- return record ? normalizeLocalAgentRecord(agentId, record) : null;
8074
- }
8075
- async function getLocalAgentConfig(agentId) {
8076
- const record = await resolveConfiguredLocalAgentRecord(agentId);
8077
- if (!record) {
8078
- return null;
8079
- }
8080
- return buildLocalAgentConfigState(agentId, record);
8081
- }
8082
- async function getLocalAgentSessionSnapshot(agentId) {
8083
- const record = await resolveConfiguredLocalAgentRecord(agentId);
8084
- if (!record) {
8085
- return null;
8086
- }
8087
- if (record.transport === "codex_app_server") {
8088
- return getCodexAppServerAgentSnapshot(buildCodexAgentSessionOptions(agentId, record));
8089
- }
8090
- if (record.transport === "claude_stream_json") {
8091
- return getClaudeStreamJsonAgentSnapshot(buildClaudeAgentSessionOptions(agentId, record));
8092
- }
8093
- return null;
8094
- }
8095
- async function getLocalAgentEndpointSessionSnapshot(endpoint) {
8096
- if (endpoint.transport === "codex_app_server") {
8097
- return getCodexAppServerAgentSnapshot(buildCodexEndpointSessionOptions(endpoint));
8098
- }
8099
- if (endpoint.transport === "claude_stream_json") {
8100
- return getClaudeStreamJsonAgentSnapshot(buildClaudeEndpointSessionOptions(endpoint));
8101
- }
8102
- return null;
8103
- }
8104
- async function ensureLocalSessionEndpointOnline(endpoint) {
8105
- if (endpoint.transport === "codex_app_server") {
8106
- await ensureCodexAppServerAgentOnline(buildCodexEndpointSessionOptions(endpoint));
8107
- return;
8108
- }
8109
- if (endpoint.transport === "claude_stream_json") {
8110
- await ensureClaudeStreamJsonAgentOnline(buildClaudeEndpointSessionOptions(endpoint));
8111
- }
8112
- }
8113
- async function shutdownLocalSessionEndpoint(endpoint) {
8114
- if (endpoint.transport === "codex_app_server") {
8115
- await shutdownCodexAppServerAgent(buildCodexEndpointSessionOptions(endpoint));
8116
- return;
8117
- }
8118
- if (endpoint.transport === "claude_stream_json") {
8119
- await shutdownClaudeStreamJsonAgent(buildClaudeEndpointSessionOptions(endpoint));
8120
- }
8121
- }
8122
- async function answerLocalAgentSessionQuestion(agentId, input) {
8123
- const record = await resolveConfiguredLocalAgentRecord(agentId);
8124
- if (!record) {
8125
- throw new Error(`Agent ${agentId} is not configured.`);
8126
- }
8127
- if (record.transport !== "claude_stream_json") {
8128
- throw new Error(`Agent ${agentId} does not support direct question answers for transport ${record.transport}.`);
8129
- }
8130
- await answerClaudeStreamJsonQuestion(buildClaudeAgentSessionOptions(agentId, record), input);
8131
- }
8132
- async function updateLocalAgentConfig(agentId, input) {
8133
- const [registry, overrides] = await Promise.all([
8134
- readLocalAgentRegistry(),
8135
- readRelayAgentOverrides()
8136
- ]);
8137
- const record = registry[agentId] ?? (overrides[agentId] ? localAgentRecordFromRelayAgentOverride(agentId, overrides[agentId]) : undefined);
8138
- if (!record) {
8139
- return null;
8140
- }
8141
- const cwd = normalizeProjectPath(input.runtime.cwd || record.cwd);
8142
- await assertDirectoryExists(cwd);
8143
- const nextHarness = normalizeLocalAgentHarness(input.runtime.harness);
8144
- const nextTransport = normalizeLocalAgentTransport(input.runtime.transport ?? record.transport, nextHarness);
8145
- const nextRecord = normalizeLocalAgentRecord(agentId, {
8146
- ...record,
8147
- cwd,
8148
- tmuxSession: input.runtime.sessionId,
8149
- harness: nextHarness,
8150
- defaultHarness: nextHarness,
8151
- harnessProfiles: {
8152
- ...record.harnessProfiles ?? {},
8153
- [normalizeManagedHarness2(input.runtime.harness, "claude")]: {
8154
- cwd,
8155
- transport: nextTransport,
8156
- sessionId: normalizeTmuxSessionName2(input.runtime.sessionId, `${agentId}-${normalizeManagedHarness2(input.runtime.harness, "claude")}`),
8157
- launchArgs: input.launchArgs
8158
- }
8159
- },
8160
- transport: nextTransport,
8161
- systemPrompt: input.systemPrompt.trim() || undefined,
8162
- launchArgs: input.launchArgs,
8163
- capabilities: input.capabilities
8164
- });
8165
- registry[agentId] = nextRecord;
8166
- await writeLocalAgentRegistry(registry);
8167
- return buildLocalAgentConfigState(agentId, nextRecord);
8168
- }
8169
6703
  function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
8170
6704
  return {
8171
6705
  agentName,
@@ -8188,72 +6722,6 @@ function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
8188
6722
  launchArgs: normalizeLocalAgentLaunchArgs(record.launchArgs)
8189
6723
  };
8190
6724
  }
8191
- function endpointMetadataString(endpoint, key) {
8192
- const value = endpoint.metadata?.[key];
8193
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
8194
- }
8195
- function endpointInvocationPrompt(endpoint, agentName, invocation) {
8196
- const source = endpointMetadataString(endpoint, "source");
8197
- const externalSource = endpointMetadataString(endpoint, "externalSource");
8198
- const attachedTransport = endpointMetadataString(endpoint, "attachedTransport");
8199
- const sessionBacked = endpoint.metadata?.sessionBacked === true;
8200
- if (invocation.action === "consult" && (sessionBacked || source === "local-session" || externalSource === "local-session" || attachedTransport === "codex_app_server" || attachedTransport === "claude_stream_json")) {
8201
- return invocation.task;
8202
- }
8203
- if (sessionBacked) {
8204
- return buildAttachedSessionInvocationPrompt(invocation);
8205
- }
8206
- return buildLocalAgentDirectInvocationPrompt(agentName, invocation);
8207
- }
8208
- function isSessionBackedEndpoint(endpoint) {
8209
- const source = endpointMetadataString(endpoint, "source");
8210
- const externalSource = endpointMetadataString(endpoint, "externalSource");
8211
- const attachedTransport = endpointMetadataString(endpoint, "attachedTransport");
8212
- return endpoint.metadata?.sessionBacked === true || source === "local-session" || externalSource === "local-session" || attachedTransport === "codex_app_server" || attachedTransport === "claude_stream_json";
8213
- }
8214
- function endpointAgentName(endpoint) {
8215
- return endpointMetadataString(endpoint, "agentName") ?? endpointMetadataString(endpoint, "definitionId") ?? endpoint.agentId;
8216
- }
8217
- function endpointRuntimeInstanceId(endpoint) {
8218
- return endpointMetadataString(endpoint, "runtimeInstanceId") ?? endpointMetadataString(endpoint, "runtimeSessionId") ?? endpoint.sessionId ?? `relay-${endpointAgentName(endpoint)}`;
8219
- }
8220
- function endpointCwd(endpoint) {
8221
- return endpoint.cwd ?? endpoint.projectRoot ?? process.cwd();
8222
- }
8223
- function attachedLocalSessionSystemPrompt(endpoint) {
8224
- const explicit = endpointMetadataString(endpoint, "systemPrompt");
8225
- if (explicit) {
8226
- return explicit;
8227
- }
8228
- return "Resume the existing session without changing its identity or prior context.";
8229
- }
8230
- function buildCodexEndpointSessionOptions(endpoint) {
8231
- const agentName = endpointAgentName(endpoint);
8232
- const threadId = endpointMetadataString(endpoint, "threadId") ?? endpointMetadataString(endpoint, "externalSessionId") ?? endpoint.sessionId ?? undefined;
8233
- return {
8234
- agentName,
8235
- sessionId: endpointRuntimeInstanceId(endpoint),
8236
- cwd: endpointCwd(endpoint),
8237
- systemPrompt: attachedLocalSessionSystemPrompt(endpoint),
8238
- runtimeDirectory: relayAgentRuntimeDirectory(agentName),
8239
- logsDirectory: relayAgentLogsDirectory(agentName),
8240
- launchArgs: [],
8241
- threadId,
8242
- requireExistingThread: Boolean(threadId)
8243
- };
8244
- }
8245
- function buildClaudeEndpointSessionOptions(endpoint) {
8246
- const agentName = endpointAgentName(endpoint);
8247
- return {
8248
- agentName,
8249
- sessionId: endpointRuntimeInstanceId(endpoint),
8250
- cwd: endpointCwd(endpoint),
8251
- systemPrompt: attachedLocalSessionSystemPrompt(endpoint),
8252
- runtimeDirectory: relayAgentRuntimeDirectory(agentName),
8253
- logsDirectory: relayAgentLogsDirectory(agentName),
8254
- launchArgs: []
8255
- };
8256
- }
8257
6725
  function isLocalAgentRecordOnline(agentName, record) {
8258
6726
  const normalizedRecord = normalizeLocalAgentRecord(agentName, record);
8259
6727
  if (normalizedRecord.transport === "codex_app_server") {
@@ -8272,117 +6740,12 @@ function isLocalAgentSessionAlive(sessionName) {
8272
6740
  return false;
8273
6741
  }
8274
6742
  }
8275
- function isLocalAgentEndpointAlive(endpoint) {
8276
- if (endpoint.transport === "pairing_bridge") {
8277
- return endpoint.state !== "offline";
8278
- }
8279
- if (endpoint.transport === "codex_app_server") {
8280
- return isCodexAppServerAgentAlive(buildCodexEndpointSessionOptions(endpoint));
8281
- }
8282
- if (endpoint.transport === "claude_stream_json") {
8283
- return isClaudeStreamJsonAgentAlive(buildClaudeEndpointSessionOptions(endpoint));
8284
- }
8285
- const sessionId = endpoint.sessionId ?? (typeof endpoint.metadata?.tmuxSession === "string" ? String(endpoint.metadata.tmuxSession) : null);
8286
- return sessionId ? isLocalAgentSessionAlive(sessionId) : false;
8287
- }
8288
- async function readBrokerMessagesSince(sinceSeconds) {
8289
- const response = await fetch(new URL("/v1/snapshot", resolveBrokerUrl()));
8290
- if (!response.ok) {
8291
- throw new Error(`Broker snapshot failed: ${response.status} ${response.statusText}`);
8292
- }
8293
- const snapshot = await response.json();
8294
- return Object.values(snapshot.messages).filter((message) => normalizeBrokerTimestamp(message.createdAt) >= sinceSeconds).sort((lhs, rhs) => normalizeBrokerTimestamp(lhs.createdAt) - normalizeBrokerTimestamp(rhs.createdAt));
8295
- }
8296
6743
  function buildLocalAgentSystemPrompt(agentName, projectName, projectPath, options = {}) {
8297
6744
  return renderLocalAgentSystemPromptTemplate(buildLocalAgentSystemPromptTemplate(), buildLocalAgentTemplateContext(agentName, projectName, projectPath), options);
8298
6745
  }
8299
6746
  function buildLocalAgentInitialMessage(projectName, agentName) {
8300
6747
  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}"`;
8301
6748
  }
8302
- function buildLocalAgentDirectInvocationPrompt(agentName, invocation) {
8303
- const contextLines = Object.entries(invocation.context ?? {}).map(([key, value]) => `- ${key}: ${String(value)}`);
8304
- const collaborationContract = buildCollaborationContractPrompt(agentName);
8305
- const collaborationContext = buildInvocationCollaborationContextPrompt(invocation);
8306
- const actionRules = invocation.action === "execute" ? "You may inspect and modify the workspace when needed. End with the concise broker-visible reply for the requester." : "Do not modify files unless the request explicitly requires it. End with the concise broker-visible reply for the requester.";
8307
- return [
8308
- `OpenScout invocation for ${agentName}.`,
8309
- `Requester: ${invocation.requesterId}.`,
8310
- `Action: ${invocation.action}.`,
8311
- invocation.conversationId ? `Conversation: ${invocation.conversationId}.` : undefined,
8312
- invocation.messageId ? `Message: ${invocation.messageId}.` : undefined,
8313
- "",
8314
- actionRules,
8315
- collaborationContract,
8316
- "Return only the reply that should be delivered back through the broker.",
8317
- "",
8318
- collaborationContext,
8319
- contextLines.length > 0 ? `Context:
8320
- ${contextLines.join(`
8321
- `)}` : undefined,
8322
- "Task:",
8323
- invocation.task
8324
- ].filter((value) => Boolean(value && value.length > 0)).join(`
8325
- `);
8326
- }
8327
- function buildAttachedSessionInvocationPrompt(invocation) {
8328
- const contextLines = Object.entries(invocation.context ?? {}).map(([key, value]) => `- ${key}: ${String(value)}`);
8329
- return [
8330
- `Scout message from ${invocation.requesterId}.`,
8331
- invocation.conversationId ? `Conversation: ${invocation.conversationId}.` : undefined,
8332
- invocation.messageId ? `Reply-To Message: ${invocation.messageId}.` : undefined,
8333
- invocation.action !== "consult" ? `Requested action: ${invocation.action}.` : undefined,
8334
- "Treat this as a direct message to the current session and reply normally as yourself in this session.",
8335
- contextLines.length > 0 ? `Context:
8336
- ${contextLines.join(`
8337
- `)}` : undefined,
8338
- "",
8339
- invocation.task
8340
- ].filter((value) => Boolean(value && value.length > 0)).join(`
8341
- `);
8342
- }
8343
- function buildLocalAgentNudge(agentName, invocation, flightId) {
8344
- const relayCommand = brokerRelayCommand();
8345
- const parts = [
8346
- `New broker ask from ${invocation.requesterId}.`,
8347
- `Task: ${invocation.task}`,
8348
- "Follow the OpenScout collaboration contract: answer directly if you can, otherwise make the next owner explicit and avoid broad wakeups."
8349
- ];
8350
- if (invocation.context && Object.keys(invocation.context).length > 0) {
8351
- parts.push(`Context: ${JSON.stringify(invocation.context)}`);
8352
- }
8353
- parts.push(`Read recent context if needed: ${relayCommand} read -n 20 --as ${agentName}.`);
8354
- parts.push(`Reply with: ${relayCommand} send --as ${agentName} "[ask:${flightId}] @${invocation.requesterId} <your response>"`);
8355
- return parts.join(" ");
8356
- }
8357
- function stripLocalAgentReplyMetadata(body, flightId, asker) {
8358
- return body.replace(new RegExp(`\\[ask:${escapeRegExp(flightId)}\\]`, "g"), "").replace(new RegExp(`@${escapeRegExp(asker)}`, "g"), "").trim();
8359
- }
8360
- async function sendLocalAgentPrompt(agentName, record, prompt) {
8361
- if (normalizeLocalAgentHarness(record.harness) === "codex") {
8362
- const promptPipe = join8(relayAgentRuntimeDirectory(agentName), "prompt.pipe");
8363
- await writeFile5(promptPipe, prompt.trim() + "\x00");
8364
- return;
8365
- }
8366
- sendTmuxPrompt(record.tmuxSession, prompt);
8367
- }
8368
- function sendTmuxPrompt(sessionName, prompt) {
8369
- const bufferName = `openscout-prompt-${randomUUID2()}`;
8370
- try {
8371
- execFileSync3("tmux", ["load-buffer", "-b", bufferName, "-"], {
8372
- stdio: "pipe",
8373
- input: prompt
8374
- });
8375
- execFileSync3("tmux", ["paste-buffer", "-d", "-b", bufferName, "-t", sessionName], {
8376
- stdio: "pipe"
8377
- });
8378
- execFileSync3("tmux", ["send-keys", "-t", sessionName, "Enter"], { stdio: "pipe" });
8379
- } catch (error) {
8380
- try {
8381
- execFileSync3("tmux", ["delete-buffer", "-b", bufferName], { stdio: "pipe" });
8382
- } catch {}
8383
- throw error;
8384
- }
8385
- }
8386
6749
  function shellQuoteArguments(args) {
8387
6750
  return args.map((arg) => JSON.stringify(arg)).join(" ");
8388
6751
  }
@@ -9078,110 +7441,6 @@ async function ensureLocalAgentBindingOnline(agentId, nodeId, options = {}) {
9078
7441
  const onlineRecord = await ensureLocalAgentOnline(agentId, recordForHarness(localAgentRecordFromRelayAgentOverride(agentId, override), options.harness));
9079
7442
  return buildLocalAgentBinding(agentId, onlineRecord, isLocalAgentRecordOnline(agentId, onlineRecord), nodeId, "relay-agent-registry");
9080
7443
  }
9081
- async function invokeLocalAgentEndpoint(endpoint, invocation) {
9082
- const agentRuntimeId = endpoint.agentId;
9083
- const definitionId = String(endpoint.metadata?.definitionId ?? endpoint.metadata?.agentName ?? endpoint.agentId);
9084
- const prompt = endpointInvocationPrompt(endpoint, definitionId, invocation);
9085
- const registry = await readLocalAgentRegistry();
9086
- const existing = registry[agentRuntimeId];
9087
- if (!existing && endpoint.transport === "codex_app_server") {
9088
- await ensureLocalSessionEndpointOnline(endpoint);
9089
- const invoke = isSessionBackedEndpoint(endpoint) ? sendCodexAppServerAgent : invokeCodexAppServerAgent;
9090
- const result = await invoke({
9091
- ...buildCodexEndpointSessionOptions(endpoint),
9092
- prompt,
9093
- timeoutMs: invocation.timeoutMs
9094
- });
9095
- return {
9096
- output: result.output
9097
- };
9098
- }
9099
- if (!existing && endpoint.transport === "claude_stream_json") {
9100
- await ensureLocalSessionEndpointOnline(endpoint);
9101
- const result = await invokeClaudeStreamJsonAgent({
9102
- ...buildClaudeEndpointSessionOptions(endpoint),
9103
- prompt,
9104
- timeoutMs: invocation.timeoutMs
9105
- });
9106
- return {
9107
- output: result.output
9108
- };
9109
- }
9110
- const projectRoot = endpoint.projectRoot ?? endpoint.cwd;
9111
- const requestedHarness = invocation.execution?.harness;
9112
- const record = existing ?? (projectRoot ? {
9113
- definitionId,
9114
- project: basename3(projectRoot),
9115
- projectRoot,
9116
- tmuxSession: String(endpoint.metadata?.tmuxSession ?? `relay-${agentRuntimeId}`),
9117
- cwd: projectRoot,
9118
- startedAt: nowSeconds2(),
9119
- harness: normalizeLocalAgentHarness(typeof endpoint.harness === "string" ? endpoint.harness : undefined),
9120
- defaultHarness: normalizeLocalAgentHarness(typeof endpoint.harness === "string" ? endpoint.harness : undefined),
9121
- harnessProfiles: {
9122
- [normalizeManagedHarness2(typeof endpoint.harness === "string" ? endpoint.harness : undefined, "claude")]: {
9123
- cwd: projectRoot,
9124
- sessionId: String(endpoint.sessionId ?? endpoint.metadata?.runtimeInstanceId ?? endpoint.metadata?.runtimeSessionId ?? `relay-${agentRuntimeId}`),
9125
- transport: normalizeLocalAgentTransport(typeof endpoint.transport === "string" ? endpoint.transport : undefined, normalizeLocalAgentHarness(typeof endpoint.harness === "string" ? endpoint.harness : undefined)),
9126
- launchArgs: []
9127
- }
9128
- },
9129
- transport: normalizeLocalAgentTransport(typeof endpoint.transport === "string" ? endpoint.transport : undefined, normalizeLocalAgentHarness(typeof endpoint.harness === "string" ? endpoint.harness : undefined)),
9130
- capabilities: [...DEFAULT_LOCAL_AGENT_CAPABILITIES],
9131
- launchArgs: []
9132
- } : null);
9133
- if (!record) {
9134
- throw new Error(`Agent ${agentRuntimeId} is not registered and has no project root.`);
9135
- }
9136
- const selectedRecord = recordForHarness(record, requestedHarness);
9137
- const onlineRecord = await ensureLocalAgentOnline(agentRuntimeId, selectedRecord);
9138
- if (onlineRecord.transport === "codex_app_server") {
9139
- const result = await invokeCodexAppServerAgent({
9140
- ...buildCodexAgentSessionOptions(agentRuntimeId, onlineRecord, renderLocalAgentSystemPromptTemplate(onlineRecord.systemPrompt || buildLocalAgentSystemPromptTemplate(), buildLocalAgentTemplateContext(definitionId, onlineRecord.project, onlineRecord.cwd), { transport: "codex_app_server" })),
9141
- prompt,
9142
- timeoutMs: invocation.timeoutMs
9143
- });
9144
- return {
9145
- output: result.output
9146
- };
9147
- }
9148
- if (onlineRecord.transport === "claude_stream_json") {
9149
- const result = await invokeClaudeStreamJsonAgent({
9150
- ...buildClaudeAgentSessionOptions(agentRuntimeId, onlineRecord, renderLocalAgentSystemPromptTemplate(onlineRecord.systemPrompt || buildLocalAgentSystemPromptTemplate(), buildLocalAgentTemplateContext(definitionId, onlineRecord.project, onlineRecord.cwd), { transport: "claude_stream_json" })),
9151
- prompt,
9152
- timeoutMs: invocation.timeoutMs
9153
- });
9154
- return {
9155
- output: result.output
9156
- };
9157
- }
9158
- const flightId = createLocalAgentFlightId();
9159
- const askedAt = nowSeconds2();
9160
- const timeoutSeconds = invocation.timeoutMs ? Math.max(30, Math.floor(invocation.timeoutMs / 1000)) : 300;
9161
- await sendLocalAgentPrompt(agentRuntimeId, onlineRecord, buildLocalAgentNudge(definitionId, invocation, flightId));
9162
- const deadline = Date.now() + timeoutSeconds * 1000;
9163
- while (Date.now() <= deadline) {
9164
- const messages2 = await readBrokerMessagesSince(askedAt - 1);
9165
- for (let index = messages2.length - 1;index >= 0; index -= 1) {
9166
- const message = messages2[index];
9167
- if (message.actorId !== agentRuntimeId)
9168
- continue;
9169
- if (!message.body.includes(`[ask:${flightId}]`))
9170
- continue;
9171
- return {
9172
- output: stripLocalAgentReplyMetadata(message.body, flightId, invocation.requesterId)
9173
- };
9174
- }
9175
- await sleep2(500);
9176
- }
9177
- throw new Error(`Timed out after ${timeoutSeconds}s waiting for ${agentRuntimeId}.`);
9178
- }
9179
- function shouldDisableGeneratedCodexEndpoint(endpoint) {
9180
- if (endpoint.transport !== "codex_exec") {
9181
- return false;
9182
- }
9183
- return endpoint.metadata?.source === "scout-app";
9184
- }
9185
7444
  var MODULE_DIRECTORY, OPENSCOUT_REPO_ROOT, DEFAULT_LOCAL_AGENT_CAPABILITIES, DEFAULT_LOCAL_AGENT_HARNESS = "claude", SUPPORTED_LOCAL_AGENT_HARNESSES;
9186
7445
  var init_local_agents = __esm(async () => {
9187
7446
  init_src();
@@ -9279,19 +7538,41 @@ function resolveScoutAgentName(agentName) {
9279
7538
  }
9280
7539
  return resolveOperatorName();
9281
7540
  }
9282
- async function resolveScoutSenderId(agentName, currentDirectory) {
7541
+ function resolveConfiguredSenderIdForProjectRoot(overrides, projectRoot) {
7542
+ for (const [agentId, override] of Object.entries(overrides)) {
7543
+ if (BUILT_IN_AGENT_DEFINITION_IDS.has(agentId)) {
7544
+ continue;
7545
+ }
7546
+ if (!override.projectRoot || override.projectRoot !== projectRoot) {
7547
+ continue;
7548
+ }
7549
+ return agentId;
7550
+ }
7551
+ return null;
7552
+ }
7553
+ async function inferSenderIdForProjectRoot(projectRoot) {
7554
+ const overrides = await readRelayAgentOverrides();
7555
+ const configuredSenderId = resolveConfiguredSenderIdForProjectRoot(overrides, projectRoot);
7556
+ if (configuredSenderId) {
7557
+ return configuredSenderId;
7558
+ }
7559
+ const projectConfig = await readProjectConfig(projectRoot);
7560
+ const configuredDefinitionId = normalizeAgentSelectorSegment(projectConfig?.agent?.id?.trim() ?? "");
7561
+ const definitionId = configuredDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
7562
+ return buildRelayAgentInstance(definitionId, projectRoot).id;
7563
+ }
7564
+ async function resolveScoutSenderId(agentName, currentDirectory, env = process.env) {
9283
7565
  if (agentName?.trim()) {
9284
7566
  return agentName.trim();
9285
7567
  }
9286
- if (process.env.OPENSCOUT_AGENT?.trim()) {
9287
- return process.env.OPENSCOUT_AGENT.trim();
7568
+ if (env.OPENSCOUT_AGENT?.trim()) {
7569
+ return env.OPENSCOUT_AGENT.trim();
7570
+ }
7571
+ const projectRoot = await findNearestProjectRoot(currentDirectory);
7572
+ if (!projectRoot) {
7573
+ return resolveOperatorName();
9288
7574
  }
9289
- const { findNearestProjectRoot: findNearestProjectRoot2 } = await Promise.resolve().then(() => (init_setup(), exports_setup));
9290
- const { resolveLocalAgentByName: resolveLocalAgentByName2 } = await init_local_agents().then(() => exports_local_agents);
9291
- const projectRoot = await findNearestProjectRoot2(currentDirectory) ?? currentDirectory;
9292
- const projectName = basename4(projectRoot);
9293
- const agent = await resolveLocalAgentByName2(projectName);
9294
- return agent?.agentId ?? projectName;
7575
+ return inferSenderIdForProjectRoot(projectRoot);
9295
7576
  }
9296
7577
  function parseScoutHarness(value) {
9297
7578
  const trimmed = value?.trim();
@@ -9865,14 +8146,17 @@ async function sendScoutMessage(input) {
9865
8146
  const senderId = await resolveConversationActorId(broker.baseUrl, broker.snapshot, broker.node.id, input.senderId, currentDirectory);
9866
8147
  const availableTargets = (await Promise.all(mentionResolution.resolved.map(async (target) => await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, target.agentId, currentDirectory) ? target : null))).filter((target) => Boolean(target));
9867
8148
  const validTargets = [...new Set(availableTargets.map((target) => target.agentId).filter((target) => target !== senderId && Boolean(broker.snapshot.agents[target])))].sort();
8149
+ const unresolvedTargets = mentionResolution.resolved.filter((target) => !validTargets.includes(target.agentId)).map((target) => target.label).concat(mentionResolution.unresolved).concat(mentionResolution.ambiguous.map((entry) => entry.label));
8150
+ if (unresolvedTargets.length > 0) {
8151
+ return { usedBroker: true, invokedTargets: [], unresolvedTargets };
8152
+ }
9868
8153
  let conversation;
9869
8154
  if (validTargets.length === 1 && !input.channel) {
9870
8155
  const dm = await ensureBrokerDirectConversationBetween(broker.baseUrl, broker.snapshot, broker.node.id, senderId, validTargets[0]);
9871
8156
  conversation = dm.conversation;
9872
8157
  } else {
9873
- conversation = await ensureBrokerConversation(broker.baseUrl, broker.snapshot, broker.node.id, input.channel, senderId, availableTargets.map((target) => target.agentId));
8158
+ conversation = await ensureBrokerConversation(broker.baseUrl, broker.snapshot, broker.node.id, input.channel, senderId, validTargets);
9874
8159
  }
9875
- const unresolvedTargets = mentionResolution.resolved.filter((target) => !validTargets.includes(target.agentId)).map((target) => target.label).concat(mentionResolution.unresolved).concat(mentionResolution.ambiguous.map((entry) => entry.label));
9876
8160
  const messageId = `m-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
9877
8161
  const speechText = input.shouldSpeak ? stripScoutAgentSelectorLabels(input.body) : "";
9878
8162
  const returnAddress = buildScoutReturnAddress(broker.snapshot, senderId, {
@@ -9899,27 +8183,6 @@ async function sendScoutMessage(input) {
9899
8183
  returnAddress
9900
8184
  }
9901
8185
  });
9902
- for (const targetAgentId of validTargets) {
9903
- await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.invocations, {
9904
- id: `inv-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
9905
- requesterId: senderId,
9906
- requesterNodeId: broker.node.id,
9907
- targetAgentId,
9908
- action: "consult",
9909
- task: input.body,
9910
- conversationId: conversation.id,
9911
- messageId,
9912
- execution: input.executionHarness ? { harness: input.executionHarness } : undefined,
9913
- ensureAwake: true,
9914
- stream: false,
9915
- createdAt: Date.now(),
9916
- metadata: {
9917
- source: "scout-cli",
9918
- relayChannel: input.channel ?? "shared",
9919
- returnAddress
9920
- }
9921
- });
9922
- }
9923
8186
  return { usedBroker: true, invokedTargets: validTargets, unresolvedTargets };
9924
8187
  }
9925
8188
  async function openScoutPeerSession(input) {
@@ -10204,7 +8467,7 @@ async function watchScoutMessages(options) {
10204
8467
  return;
10205
8468
  }
10206
8469
  const message = event.payload?.message;
10207
- if (!message || message.conversationId !== conversationId || message.actorId === options.agentId)
8470
+ if (!message || message.conversationId !== conversationId)
10208
8471
  return;
10209
8472
  options.onMessage(message);
10210
8473
  };
@@ -10501,13 +8764,15 @@ async function runAskCommand(context, args) {
10501
8764
  await runAskWithOptions(context, options);
10502
8765
  }
10503
8766
  async function runAskWithOptions(context, options) {
8767
+ const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
8768
+ const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
10504
8769
  const result = await askScoutQuestion({
10505
- senderId: resolveScoutAgentName(options.agentName),
8770
+ senderId,
10506
8771
  targetLabel: options.targetLabel,
10507
8772
  body: options.message,
10508
8773
  channel: options.channel,
10509
8774
  executionHarness: parseScoutHarness(options.harness),
10510
- currentDirectory: options.currentDirectory
8775
+ currentDirectory
10511
8776
  });
10512
8777
  if (!result.usedBroker) {
10513
8778
  throw new Error("broker is not reachable");
@@ -10597,8 +8862,116 @@ function renderScoutBroadcastResult(result) {
10597
8862
  return lines.join(`
10598
8863
  `);
10599
8864
  }
8865
+ function renderScoutActivityKind(kind) {
8866
+ switch (kind) {
8867
+ case "ask_opened":
8868
+ return "asked";
8869
+ case "ask_working":
8870
+ return "working";
8871
+ case "ask_replied":
8872
+ return "replied";
8873
+ case "ask_failed":
8874
+ return "failed";
8875
+ case "handoff_sent":
8876
+ return "handoff";
8877
+ case "agent_message":
8878
+ return "agent";
8879
+ case "status_message":
8880
+ return "status";
8881
+ case "invocation_recorded":
8882
+ return "invoke";
8883
+ case "flight_updated":
8884
+ return "flight";
8885
+ case "collaboration_event":
8886
+ return "collab";
8887
+ case "message_posted":
8888
+ default:
8889
+ return "message";
8890
+ }
8891
+ }
8892
+ function renderScoutActivityParticipants(item) {
8893
+ const actor = item.actorId?.trim();
8894
+ const counterpart = item.counterpartId?.trim();
8895
+ if (actor && counterpart && actor !== counterpart) {
8896
+ return `${actor} -> ${counterpart}`;
8897
+ }
8898
+ return actor || counterpart || item.agentId?.trim() || null;
8899
+ }
8900
+ function renderScoutActivityItem(item) {
8901
+ const timestamp = normalizeUnixTimestamp2(item.ts) ?? Math.floor(Date.now() / 1000);
8902
+ const label = renderScoutActivityKind(item.kind).padEnd(7, " ");
8903
+ const participants = renderScoutActivityParticipants(item);
8904
+ const title = item.title?.trim() || item.summary?.trim() || item.kind;
8905
+ const summary = item.summary?.trim();
8906
+ const detail = summary && summary !== title ? summary : null;
8907
+ return [
8908
+ formatScoutTimestamp(timestamp),
8909
+ label,
8910
+ participants,
8911
+ title,
8912
+ detail ? `(${detail})` : null
8913
+ ].filter(Boolean).join(" ");
8914
+ }
8915
+ function renderScoutActivityList(items) {
8916
+ if (items.length === 0) {
8917
+ return "No Scout activity yet.";
8918
+ }
8919
+ return items.map(renderScoutActivityItem).join(`
8920
+ `);
8921
+ }
10600
8922
  var init_broker = () => {};
10601
8923
 
8924
+ // ../../apps/desktop/src/cli/commands/send.ts
8925
+ var exports_send = {};
8926
+ __export(exports_send, {
8927
+ runSendCommand: () => runSendCommand,
8928
+ formatScoutSendRoutingError: () => formatScoutSendRoutingError
8929
+ });
8930
+ function renderTargetLabel(label) {
8931
+ const trimmed = label.trim();
8932
+ if (!trimmed) {
8933
+ return "";
8934
+ }
8935
+ return trimmed.startsWith("@") ? trimmed : `@${trimmed}`;
8936
+ }
8937
+ function formatScoutSendRoutingError(unresolvedTargets) {
8938
+ const rendered = unresolvedTargets.map(renderTargetLabel).filter((label) => label.length > 0);
8939
+ if (rendered.length === 1) {
8940
+ return `target ${rendered[0]} is not uniquely routable; nothing was sent.`;
8941
+ }
8942
+ return `targets ${rendered.join(", ")} are not uniquely routable; nothing was sent.`;
8943
+ }
8944
+ async function runSendCommand(context, args) {
8945
+ const options = parseSendCommandOptions(args, defaultScoutContextDirectory(context));
8946
+ const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
8947
+ const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
8948
+ const result = await sendScoutMessage({
8949
+ senderId,
8950
+ body: options.message,
8951
+ channel: options.channel,
8952
+ shouldSpeak: options.shouldSpeak,
8953
+ executionHarness: parseScoutHarness(options.harness),
8954
+ currentDirectory: options.currentDirectory
8955
+ });
8956
+ if (!result.usedBroker) {
8957
+ throw new Error("broker is not reachable");
8958
+ }
8959
+ if (result.unresolvedTargets.length > 0) {
8960
+ throw new Error(formatScoutSendRoutingError(result.unresolvedTargets));
8961
+ }
8962
+ context.output.writeValue({
8963
+ message: options.message,
8964
+ invokedTargets: result.invokedTargets,
8965
+ unresolvedTargets: result.unresolvedTargets
8966
+ }, renderScoutMessagePostResult);
8967
+ }
8968
+ var init_send = __esm(async () => {
8969
+ init_context();
8970
+ init_options();
8971
+ init_broker();
8972
+ await init_service();
8973
+ });
8974
+
10602
8975
  // ../../apps/desktop/src/cli/commands/broadcast.ts
10603
8976
  var exports_broadcast = {};
10604
8977
  __export(exports_broadcast, {
@@ -10606,15 +8979,20 @@ __export(exports_broadcast, {
10606
8979
  });
10607
8980
  async function runBroadcastCommand(context, args) {
10608
8981
  const options = parseSendCommandOptions(args, defaultScoutContextDirectory(context));
8982
+ const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
8983
+ const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
10609
8984
  const result = await sendScoutMessage({
10610
- senderId: resolveScoutAgentName(options.agentName),
8985
+ senderId,
10611
8986
  body: `@all ${options.message}`,
10612
8987
  executionHarness: parseScoutHarness(options.harness),
10613
- currentDirectory: options.currentDirectory
8988
+ currentDirectory
10614
8989
  });
10615
8990
  if (!result.usedBroker) {
10616
8991
  throw new Error("broker is not reachable");
10617
8992
  }
8993
+ if (result.unresolvedTargets.length > 0) {
8994
+ throw new Error(formatScoutSendRoutingError(result.unresolvedTargets));
8995
+ }
10618
8996
  context.output.writeValue({
10619
8997
  message: options.message,
10620
8998
  invokedTargets: result.invokedTargets,
@@ -10625,7 +9003,10 @@ var init_broadcast = __esm(async () => {
10625
9003
  init_context();
10626
9004
  init_options();
10627
9005
  init_broker();
10628
- await init_service();
9006
+ await __promiseAll([
9007
+ init_service(),
9008
+ init_send()
9009
+ ]);
10629
9010
  });
10630
9011
 
10631
9012
  // ../runtime/src/relay-agent-cards.ts
@@ -11718,7 +10099,7 @@ function resolveScoutBinPath(appRoot) {
11718
10099
  }
11719
10100
  return candidates[candidates.length - 1];
11720
10101
  }
11721
- function loadScoutEnvReport(context, currentDirectory) {
10102
+ async function loadScoutEnvReport(context, currentDirectory) {
11722
10103
  const appRoot = resolveScoutAppRoot({
11723
10104
  currentDirectory,
11724
10105
  env: context.env
@@ -11736,6 +10117,8 @@ function loadScoutEnvReport(context, currentDirectory) {
11736
10117
  const binPath = resolveScoutBinPath(appRoot);
11737
10118
  const scoutPath = resolveCommandOnPath("scout", context.env);
11738
10119
  const fallbackCommand = `bun ${binPath}`;
10120
+ const resolvedId = await resolveScoutSenderId(null, currentDirectory, context.env);
10121
+ const projectRoot = await findNearestProjectRoot(currentDirectory);
11739
10122
  return {
11740
10123
  executable: {
11741
10124
  command: scoutPath ? "scout" : fallbackCommand,
@@ -11743,8 +10126,9 @@ function loadScoutEnvReport(context, currentDirectory) {
11743
10126
  scoutPath
11744
10127
  },
11745
10128
  agent: {
11746
- resolvedId: context.env.OPENSCOUT_AGENT?.trim() || "operator",
11747
- envAgent: context.env.OPENSCOUT_AGENT?.trim() || null
10129
+ resolvedId,
10130
+ envAgent: context.env.OPENSCOUT_AGENT?.trim() || null,
10131
+ projectRoot
11748
10132
  },
11749
10133
  context: {
11750
10134
  cwd: context.cwd,
@@ -11771,16 +10155,306 @@ function renderScoutEnvReport(report) {
11771
10155
  }
11772
10156
  async function runEnvCommand(context, args) {
11773
10157
  const options = parseContextRootCommandOptions("env", args, defaultScoutContextDirectory(context));
11774
- const report = loadScoutEnvReport(context, options.currentDirectory);
10158
+ const report = await loadScoutEnvReport(context, options.currentDirectory);
11775
10159
  context.output.writeValue(report, renderScoutEnvReport);
11776
10160
  }
11777
10161
  var init_env = __esm(async () => {
10162
+ init_setup();
11778
10163
  init_context();
11779
10164
  init_options();
11780
10165
  init_paths2();
11781
10166
  await init_service();
11782
10167
  });
11783
10168
 
10169
+ // ../../apps/desktop/src/cli/commands/latest.ts
10170
+ var exports_latest = {};
10171
+ __export(exports_latest, {
10172
+ runLatestCommand: () => runLatestCommand
10173
+ });
10174
+ async function runLatestCommand(context, args) {
10175
+ const options = parseLatestCommandOptions(args, defaultScoutContextDirectory(context));
10176
+ const items = await loadScoutActivityItems({
10177
+ agentId: options.agentId,
10178
+ actorId: options.actorId,
10179
+ conversationId: options.conversationId,
10180
+ limit: options.limit
10181
+ });
10182
+ context.output.writeValue(items, renderScoutActivityList);
10183
+ }
10184
+ var init_latest = __esm(async () => {
10185
+ init_context();
10186
+ init_options();
10187
+ init_broker();
10188
+ await init_service();
10189
+ });
10190
+
10191
+ // ../../apps/desktop/src/cli/commands/menu.ts
10192
+ var exports_menu = {};
10193
+ __export(exports_menu, {
10194
+ runMenuCommand: () => runMenuCommand,
10195
+ renderMenuCommandHelp: () => renderMenuCommandHelp,
10196
+ parseMenuCommand: () => parseMenuCommand
10197
+ });
10198
+ import { spawnSync as spawnSync2 } from "child_process";
10199
+ import { existsSync as existsSync10 } from "fs";
10200
+ import { homedir as homedir7 } from "os";
10201
+ import { dirname as dirname10, join as join15, resolve as resolve8 } from "path";
10202
+ import { fileURLToPath as fileURLToPath7 } from "url";
10203
+ function renderMenuCommandHelp() {
10204
+ return [
10205
+ "scout menu \u2014 macOS menu bar app",
10206
+ "",
10207
+ "Usage:",
10208
+ " scout menu",
10209
+ " scout menu launch",
10210
+ " scout menu status",
10211
+ " scout menu restart",
10212
+ " scout menu quit",
10213
+ " scout menu build",
10214
+ " scout menu dmg",
10215
+ "",
10216
+ "Aliases:",
10217
+ " launch = open = start",
10218
+ " quit = stop",
10219
+ "",
10220
+ "Behavior:",
10221
+ " On macOS, `scout menu` launches the installed OpenScout menu app when available.",
10222
+ " When run inside an OpenScout repo checkout, it prefers `apps/macos/bin/openscout-menu.ts`",
10223
+ " so launch/build/restart reuse the repo helper and auto-build the app bundle if needed.",
10224
+ "",
10225
+ "Examples:",
10226
+ " scout menu",
10227
+ " scout menu status",
10228
+ " scout menu restart",
10229
+ " scout menu build --version 0.2.16"
10230
+ ].join(`
10231
+ `);
10232
+ }
10233
+ function parseMenuCommand(args) {
10234
+ const [first, ...rest] = args;
10235
+ if (!first) {
10236
+ return { action: "launch", passthroughArgs: [] };
10237
+ }
10238
+ if (first.startsWith("-")) {
10239
+ return { action: "launch", passthroughArgs: args };
10240
+ }
10241
+ switch (first) {
10242
+ case "launch":
10243
+ case "open":
10244
+ case "start":
10245
+ return { action: "launch", passthroughArgs: rest };
10246
+ case "restart":
10247
+ return { action: "restart", passthroughArgs: rest };
10248
+ case "quit":
10249
+ case "stop":
10250
+ return { action: "quit", passthroughArgs: rest };
10251
+ case "status":
10252
+ return { action: "status", passthroughArgs: rest };
10253
+ case "build":
10254
+ return { action: "build", passthroughArgs: rest };
10255
+ case "dmg":
10256
+ return { action: "dmg", passthroughArgs: rest };
10257
+ default:
10258
+ throw new ScoutCliError(`unknown subcommand: ${first} (try: scout menu)`);
10259
+ }
10260
+ }
10261
+ function runProcess(command, args, options) {
10262
+ const result = spawnSync2(command, args, {
10263
+ cwd: options.cwd,
10264
+ env: options.env,
10265
+ encoding: "utf8"
10266
+ });
10267
+ if (result.error) {
10268
+ throw new ScoutCliError(`failed to run ${command}: ${result.error.message}`);
10269
+ }
10270
+ const stdout = result.stdout.trim();
10271
+ const stderr = result.stderr.trim();
10272
+ const ok = (result.status ?? 1) === 0;
10273
+ if (!ok && !options.allowFailure) {
10274
+ const detail = stderr || stdout || `${command} ${args.join(" ")} failed`;
10275
+ throw new ScoutCliError(detail);
10276
+ }
10277
+ return {
10278
+ ok,
10279
+ stdout,
10280
+ stderr,
10281
+ status: result.status
10282
+ };
10283
+ }
10284
+ function findRepoMenuHelper(startDirectory) {
10285
+ let current = resolve8(startDirectory);
10286
+ while (true) {
10287
+ const candidate = join15(current, "apps", "macos", "bin", "openscout-menu.ts");
10288
+ if (existsSync10(candidate)) {
10289
+ return candidate;
10290
+ }
10291
+ const parent = dirname10(current);
10292
+ if (parent === current) {
10293
+ break;
10294
+ }
10295
+ current = parent;
10296
+ }
10297
+ const sourceRelativeCandidate = fileURLToPath7(new URL("../../../../macos/bin/openscout-menu.ts", import.meta.url));
10298
+ return existsSync10(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
10299
+ }
10300
+ function resolveRepoBundlePath(helperPath) {
10301
+ return resolve8(dirname10(helperPath), "..", "dist", MENU_BUNDLE_NAME);
10302
+ }
10303
+ function isMenuRunning(env) {
10304
+ return runProcess("pgrep", ["-x", MENU_PROCESS_NAME], { env, allowFailure: true }).ok;
10305
+ }
10306
+ function stopRunningMenu(env) {
10307
+ return runProcess("pkill", ["-x", MENU_PROCESS_NAME], { env, allowFailure: true }).ok;
10308
+ }
10309
+ function resolveInstalledMenuBundlePath(env) {
10310
+ const spotlight = runProcess("mdfind", [`kMDItemCFBundleIdentifier == '${MENU_BUNDLE_ID}'`], { env, allowFailure: true });
10311
+ const indexedPath = spotlight.stdout.split(`
10312
+ `).map((line) => line.trim()).find(Boolean);
10313
+ if (indexedPath) {
10314
+ return indexedPath;
10315
+ }
10316
+ for (const candidate of COMMON_MENU_BUNDLE_PATHS) {
10317
+ if (existsSync10(candidate)) {
10318
+ return candidate;
10319
+ }
10320
+ }
10321
+ return null;
10322
+ }
10323
+ function openInstalledMenuApp(bundlePath, env) {
10324
+ const bundleAttempts = [
10325
+ { command: "open", args: ["-b", MENU_BUNDLE_ID] }
10326
+ ];
10327
+ if (bundlePath) {
10328
+ bundleAttempts.push({ command: "open", args: [bundlePath] });
10329
+ }
10330
+ let lastFailure = "OpenScout Menu is not installed.";
10331
+ for (const attempt of bundleAttempts) {
10332
+ const result = runProcess(attempt.command, attempt.args, { env, allowFailure: true });
10333
+ if (result.ok) {
10334
+ return;
10335
+ }
10336
+ lastFailure = result.stderr || result.stdout || lastFailure;
10337
+ }
10338
+ throw new ScoutCliError(`${lastFailure} Run this command from the OpenScout repo to auto-build the menu app, or install the app first.`);
10339
+ }
10340
+ function renderMenuResult(result) {
10341
+ if (result.action === "status") {
10342
+ const lines2 = [
10343
+ `Installed: ${result.installed ? "yes" : "no"}`,
10344
+ `Running: ${result.running ? "yes" : "no"}`
10345
+ ];
10346
+ if (result.bundlePath) {
10347
+ lines2.splice(1, 0, `Bundle: ${result.bundlePath}`);
10348
+ }
10349
+ if (result.helperPath) {
10350
+ lines2.push(`Helper: ${result.helperPath}`);
10351
+ }
10352
+ return lines2.join(`
10353
+ `);
10354
+ }
10355
+ const lines = [result.message];
10356
+ if (result.bundlePath) {
10357
+ lines.push(`Bundle: ${result.bundlePath}`);
10358
+ }
10359
+ return lines.join(`
10360
+ `);
10361
+ }
10362
+ function renderActionMessage(action) {
10363
+ switch (action) {
10364
+ case "build":
10365
+ return "Built the OpenScout menu app bundle.";
10366
+ case "dmg":
10367
+ return "Built the OpenScout menu app DMG.";
10368
+ case "restart":
10369
+ return "Restarted the OpenScout menu app.";
10370
+ case "quit":
10371
+ return "Stopped the OpenScout menu app.";
10372
+ case "status":
10373
+ return "Checked the OpenScout menu app status.";
10374
+ case "launch":
10375
+ default:
10376
+ return "Opened the OpenScout menu app.";
10377
+ }
10378
+ }
10379
+ function runWithRepoHelper(context, helperPath, command) {
10380
+ runProcess(process.execPath, [helperPath, command.action, ...command.passthroughArgs], {
10381
+ cwd: defaultScoutContextDirectory(context),
10382
+ env: context.env
10383
+ });
10384
+ const bundlePath = resolveRepoBundlePath(helperPath);
10385
+ const running = command.action === "quit" ? false : isMenuRunning(context.env);
10386
+ const installed = existsSync10(bundlePath) || running;
10387
+ return {
10388
+ action: command.action,
10389
+ mode: "repo-helper",
10390
+ bundleId: MENU_BUNDLE_ID,
10391
+ bundlePath,
10392
+ helperPath,
10393
+ installed,
10394
+ running,
10395
+ message: renderActionMessage(command.action)
10396
+ };
10397
+ }
10398
+ function runWithInstalledApp(context, command) {
10399
+ const bundlePath = resolveInstalledMenuBundlePath(context.env);
10400
+ if (command.action === "build" || command.action === "dmg") {
10401
+ throw new ScoutCliError(`scout menu ${command.action} requires an OpenScout repo checkout. Run from the repo root or use bun run macos:${command.action}.`);
10402
+ }
10403
+ switch (command.action) {
10404
+ case "launch":
10405
+ if (!isMenuRunning(context.env)) {
10406
+ openInstalledMenuApp(bundlePath, context.env);
10407
+ }
10408
+ break;
10409
+ case "restart":
10410
+ stopRunningMenu(context.env);
10411
+ openInstalledMenuApp(bundlePath, context.env);
10412
+ break;
10413
+ case "quit":
10414
+ stopRunningMenu(context.env);
10415
+ break;
10416
+ case "status":
10417
+ break;
10418
+ default:
10419
+ break;
10420
+ }
10421
+ const running = command.action === "quit" ? false : isMenuRunning(context.env);
10422
+ const installed = Boolean(bundlePath) || running;
10423
+ return {
10424
+ action: command.action,
10425
+ mode: "installed-app",
10426
+ bundleId: MENU_BUNDLE_ID,
10427
+ bundlePath,
10428
+ helperPath: null,
10429
+ installed,
10430
+ running,
10431
+ message: renderActionMessage(command.action)
10432
+ };
10433
+ }
10434
+ async function runMenuCommand(context, args) {
10435
+ if (HELP_FLAGS.has(args[0] ?? "")) {
10436
+ context.output.writeText(renderMenuCommandHelp());
10437
+ return;
10438
+ }
10439
+ if (process.platform !== "darwin") {
10440
+ throw new ScoutCliError("scout menu is only supported on macOS.");
10441
+ }
10442
+ const command = parseMenuCommand(args);
10443
+ const helperPath = findRepoMenuHelper(defaultScoutContextDirectory(context));
10444
+ const result = helperPath ? runWithRepoHelper(context, helperPath, command) : runWithInstalledApp(context, command);
10445
+ context.output.writeValue(result, renderMenuResult);
10446
+ }
10447
+ var MENU_BUNDLE_ID = "com.openscout.menu", MENU_BUNDLE_NAME = "OpenScoutMenu.app", MENU_PROCESS_NAME = "OpenScoutMenu", HELP_FLAGS, COMMON_MENU_BUNDLE_PATHS;
10448
+ var init_menu = __esm(() => {
10449
+ init_context();
10450
+ init_errors();
10451
+ HELP_FLAGS = new Set(["help", "--help", "-h"]);
10452
+ COMMON_MENU_BUNDLE_PATHS = [
10453
+ join15("/Applications", MENU_BUNDLE_NAME),
10454
+ join15(homedir7(), "Applications", MENU_BUNDLE_NAME)
10455
+ ];
10456
+ });
10457
+
11784
10458
  // ../runtime/src/tailscale.ts
11785
10459
  import { readFile as readFile8 } from "fs/promises";
11786
10460
  import { execFile } from "child_process";
@@ -12243,11 +10917,11 @@ var init_mesh = __esm(async () => {
12243
10917
  });
12244
10918
 
12245
10919
  // ../../apps/desktop/src/core/pairing/runtime/config.ts
12246
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
12247
- import { homedir as homedir7 } from "os";
10920
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
10921
+ import { homedir as homedir8 } from "os";
12248
10922
  import path from "path";
12249
10923
  function pairingPaths() {
12250
- const rootDir = path.join(homedir7(), ".scout/pairing");
10924
+ const rootDir = path.join(homedir8(), ".scout/pairing");
12251
10925
  return {
12252
10926
  rootDir,
12253
10927
  configPath: path.join(rootDir, "config.json"),
@@ -12260,7 +10934,7 @@ function pairingPaths() {
12260
10934
  }
12261
10935
  function loadPairingConfig() {
12262
10936
  const { configPath } = pairingPaths();
12263
- if (!existsSync10(configPath)) {
10937
+ if (!existsSync11(configPath)) {
12264
10938
  return {};
12265
10939
  }
12266
10940
  try {
@@ -14347,11 +13021,11 @@ var init_noise = __esm(() => {
14347
13021
  });
14348
13022
 
14349
13023
  // ../../apps/desktop/src/core/pairing/runtime/security/identity.ts
14350
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
14351
- import { join as join15 } from "path";
14352
- import { homedir as homedir8 } from "os";
13024
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
13025
+ import { join as join16 } from "path";
13026
+ import { homedir as homedir9 } from "os";
14353
13027
  function loadOrCreateIdentity() {
14354
- if (existsSync11(IDENTITY_FILE)) {
13028
+ if (existsSync12(IDENTITY_FILE)) {
14355
13029
  return loadIdentity();
14356
13030
  }
14357
13031
  return createAndSaveIdentity();
@@ -14375,7 +13049,7 @@ function createAndSaveIdentity() {
14375
13049
  return keyPair;
14376
13050
  }
14377
13051
  function loadTrustedPeers() {
14378
- if (!existsSync11(TRUSTED_PEERS_FILE))
13052
+ if (!existsSync12(TRUSTED_PEERS_FILE))
14379
13053
  return new Map;
14380
13054
  const data = JSON.parse(readFileSync7(TRUSTED_PEERS_FILE, "utf8"));
14381
13055
  return new Map(data.map((p) => [p.publicKey, p]));
@@ -14411,9 +13085,9 @@ function hexToBytes2(hex) {
14411
13085
  var PAIRING_DIR, IDENTITY_FILE, TRUSTED_PEERS_FILE, QR_VERSION = 1, QR_EXPIRY_MS;
14412
13086
  var init_identity = __esm(() => {
14413
13087
  init_noise();
14414
- PAIRING_DIR = join15(homedir8(), ".scout/pairing");
14415
- IDENTITY_FILE = join15(PAIRING_DIR, "identity.json");
14416
- TRUSTED_PEERS_FILE = join15(PAIRING_DIR, "trusted-peers.json");
13088
+ PAIRING_DIR = join16(homedir9(), ".scout/pairing");
13089
+ IDENTITY_FILE = join16(PAIRING_DIR, "identity.json");
13090
+ TRUSTED_PEERS_FILE = join16(PAIRING_DIR, "trusted-peers.json");
14417
13091
  QR_EXPIRY_MS = 5 * 60 * 1000;
14418
13092
  });
14419
13093
 
@@ -14558,10 +13232,10 @@ var init_security2 = __esm(() => {
14558
13232
  });
14559
13233
 
14560
13234
  // ../../apps/desktop/src/core/pairing/runtime/runtime-state.ts
14561
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync8, renameSync, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
13235
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync8, renameSync, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
14562
13236
  function readPairingRuntimeSnapshot() {
14563
13237
  const { runtimeStatePath } = pairingPaths();
14564
- if (!existsSync12(runtimeStatePath)) {
13238
+ if (!existsSync13(runtimeStatePath)) {
14565
13239
  return null;
14566
13240
  }
14567
13241
  try {
@@ -15090,17 +13764,17 @@ var init_registry = __esm(() => {
15090
13764
  });
15091
13765
 
15092
13766
  // ../agent-sessions/src/adapters/claude-code.ts
15093
- import { existsSync as existsSync13, readdirSync, statSync as statSync2 } from "fs";
15094
- import { homedir as homedir9 } from "os";
15095
- import { join as join16 } from "path";
13767
+ import { existsSync as existsSync14, readdirSync, statSync as statSync2 } from "fs";
13768
+ import { homedir as homedir10 } from "os";
13769
+ import { join as join17 } from "path";
15096
13770
  function resolveClaudeResumeContext(config) {
15097
13771
  const rawResumeId = config.options?.["resume"];
15098
13772
  const resumeId = typeof rawResumeId === "string" ? rawResumeId.trim().replace(/\.jsonl$/u, "") : "";
15099
13773
  if (!resumeId) {
15100
13774
  return null;
15101
13775
  }
15102
- const projectsRoot = join16(homedir9(), ".claude", "projects");
15103
- if (!existsSync13(projectsRoot)) {
13776
+ const projectsRoot = join17(homedir10(), ".claude", "projects");
13777
+ if (!existsSync14(projectsRoot)) {
15104
13778
  return null;
15105
13779
  }
15106
13780
  let projectSlugs;
@@ -15110,8 +13784,8 @@ function resolveClaudeResumeContext(config) {
15110
13784
  return null;
15111
13785
  }
15112
13786
  for (const slug of projectSlugs) {
15113
- const sessionPath = join16(projectsRoot, slug, `${resumeId}.jsonl`);
15114
- if (!existsSync13(sessionPath)) {
13787
+ const sessionPath = join17(projectsRoot, slug, `${resumeId}.jsonl`);
13788
+ if (!existsSync14(sessionPath)) {
15115
13789
  continue;
15116
13790
  }
15117
13791
  const cwd = decodeClaudeProjectsSlug2(slug);
@@ -15550,11 +14224,11 @@ Referenced files: ${prompt.files.join(", ")}` });
15550
14224
  });
15551
14225
  }
15552
14226
  answerQuestion(answer) {
15553
- const resolve8 = this.pendingAnswers.get(answer.blockId);
15554
- if (!resolve8)
14227
+ const resolve9 = this.pendingAnswers.get(answer.blockId);
14228
+ if (!resolve9)
15555
14229
  return;
15556
14230
  this.pendingAnswers.delete(answer.blockId);
15557
- resolve8(answer.answer);
14231
+ resolve9(answer.answer);
15558
14232
  const turn = this.currentTurn;
15559
14233
  if (turn) {
15560
14234
  this.emit("event", {
@@ -15568,8 +14242,8 @@ Referenced files: ${prompt.files.join(", ")}` });
15568
14242
  }
15569
14243
  }
15570
14244
  async awaitAndSendAnswer(blockId, toolCallId) {
15571
- const answer = await new Promise((resolve8) => {
15572
- this.pendingAnswers.set(blockId, resolve8);
14245
+ const answer = await new Promise((resolve9) => {
14246
+ this.pendingAnswers.set(blockId, resolve9);
15573
14247
  });
15574
14248
  if (!this.process?.stdin || typeof this.process.stdin === "number")
15575
14249
  return;
@@ -15654,8 +14328,8 @@ Referenced files: ${prompt.files.join(", ")}` });
15654
14328
  // ../agent-sessions/src/adapters/codex.ts
15655
14329
  import { spawn as spawn4 } from "child_process";
15656
14330
  import { access as access3, appendFile as appendFile3, constants as constants2, mkdir as mkdir8, readFile as readFile9, rm as rm6, writeFile as writeFile7 } from "fs/promises";
15657
- import { delimiter as delimiter2, join as join17 } from "path";
15658
- import { homedir as homedir10 } from "os";
14331
+ import { delimiter as delimiter2, join as join18 } from "path";
14332
+ import { homedir as homedir11 } from "os";
15659
14333
  function parseJsonLine2(line) {
15660
14334
  try {
15661
14335
  return JSON.parse(line);
@@ -15805,7 +14479,7 @@ async function resolveCodexExecutable2() {
15805
14479
  "/usr/local/bin"
15806
14480
  ].filter(Boolean);
15807
14481
  for (const directory of [...pathEntries, ...commonDirectories]) {
15808
- const candidate = join17(directory, "codex");
14482
+ const candidate = join18(directory, "codex");
15809
14483
  if (await isExecutable2(candidate)) {
15810
14484
  return candidate;
15811
14485
  }
@@ -15909,7 +14583,7 @@ var init_codex = __esm(() => {
15909
14583
  this.pendingRequests.clear();
15910
14584
  if (child && child.exitCode === null && !child.killed) {
15911
14585
  child.kill("SIGTERM");
15912
- await new Promise((resolve8) => setTimeout(resolve8, 250));
14586
+ await new Promise((resolve9) => setTimeout(resolve9, 250));
15913
14587
  if (child.exitCode === null && !child.killed) {
15914
14588
  child.kill("SIGKILL");
15915
14589
  }
@@ -15918,7 +14592,7 @@ var init_codex = __esm(() => {
15918
14592
  await this.persistState();
15919
14593
  }
15920
14594
  get codexOptions() {
15921
- const runtimeRoot = join17(homedir10(), ".scout/pairing", "codex", this.session.id);
14595
+ const runtimeRoot = join18(homedir11(), ".scout/pairing", "codex", this.session.id);
15922
14596
  const configuredThreadId = this.config.options?.["threadId"];
15923
14597
  const requireExistingThread = this.config.options?.["requireExistingThread"];
15924
14598
  const rawLaunchArgs = this.config.options?.["launchArgs"];
@@ -15928,8 +14602,8 @@ var init_codex = __esm(() => {
15928
14602
  sessionId: this.session.id,
15929
14603
  cwd: this.config.cwd ?? process.cwd(),
15930
14604
  systemPrompt: this.systemPrompt,
15931
- runtimeDirectory: join17(runtimeRoot, "runtime"),
15932
- logsDirectory: join17(runtimeRoot, "logs"),
14605
+ runtimeDirectory: join18(runtimeRoot, "runtime"),
14606
+ logsDirectory: join18(runtimeRoot, "logs"),
15933
14607
  launchArgs,
15934
14608
  threadId: typeof configuredThreadId === "string" && configuredThreadId.trim().length > 0 ? configuredThreadId.trim() : undefined,
15935
14609
  requireExistingThread: requireExistingThread ?? Boolean(configuredThreadId)
@@ -15940,16 +14614,16 @@ var init_codex = __esm(() => {
15940
14614
  return typeof raw === "string" && raw.trim().length > 0 ? raw : "You are a helpful agent working through Pairing.";
15941
14615
  }
15942
14616
  get threadIdPath() {
15943
- return join17(this.codexOptions.runtimeDirectory, "codex-thread-id.txt");
14617
+ return join18(this.codexOptions.runtimeDirectory, "codex-thread-id.txt");
15944
14618
  }
15945
14619
  get statePath() {
15946
- return join17(this.codexOptions.runtimeDirectory, "state.json");
14620
+ return join18(this.codexOptions.runtimeDirectory, "state.json");
15947
14621
  }
15948
14622
  get stdoutLogPath() {
15949
- return join17(this.codexOptions.logsDirectory, "stdout.log");
14623
+ return join18(this.codexOptions.logsDirectory, "stdout.log");
15950
14624
  }
15951
14625
  get stderrLogPath() {
15952
- return join17(this.codexOptions.logsDirectory, "stderr.log");
14626
+ return join18(this.codexOptions.logsDirectory, "stderr.log");
15953
14627
  }
15954
14628
  enqueue(task) {
15955
14629
  const next = this.serialized.then(task, task);
@@ -15978,7 +14652,7 @@ var init_codex = __esm(() => {
15978
14652
  const options = this.codexOptions;
15979
14653
  await mkdir8(options.runtimeDirectory, { recursive: true });
15980
14654
  await mkdir8(options.logsDirectory, { recursive: true });
15981
- await writeFile7(join17(options.runtimeDirectory, "prompt.txt"), options.systemPrompt);
14655
+ await writeFile7(join18(options.runtimeDirectory, "prompt.txt"), options.systemPrompt);
15982
14656
  const codexExecutable = await resolveCodexExecutable2();
15983
14657
  const child = spawn4(codexExecutable, ["app-server", ...options.launchArgs], {
15984
14658
  cwd: options.cwd,
@@ -16695,9 +15369,9 @@ var init_codex = __esm(() => {
16695
15369
  }
16696
15370
  async request(method, params) {
16697
15371
  const id = String(this.nextRequestId++);
16698
- return new Promise((resolve8, reject) => {
15372
+ return new Promise((resolve9, reject) => {
16699
15373
  this.pendingRequests.set(id, {
16700
- resolve: (value) => resolve8(value),
15374
+ resolve: (value) => resolve9(value),
16701
15375
  reject
16702
15376
  });
16703
15377
  try {
@@ -18052,8 +16726,8 @@ var init_echo = __esm(() => {
18052
16726
  blockId: actionId,
18053
16727
  approval: { version: 1, description: `Run echo tool with: ${text}`, risk: "low" }
18054
16728
  });
18055
- const decision = await new Promise((resolve8) => {
18056
- this.pendingApprovals.set(this.approvalKey(turnId, actionId), { resolve: resolve8 });
16729
+ const decision = await new Promise((resolve9) => {
16730
+ this.pendingApprovals.set(this.approvalKey(turnId, actionId), { resolve: resolve9 });
18057
16731
  });
18058
16732
  if (this.interrupted) {
18059
16733
  this.emitTurnEnd(sessionId, turnId, "stopped");
@@ -18128,7 +16802,7 @@ var init_echo = __esm(() => {
18128
16802
  delay() {
18129
16803
  if (this.stepDelay <= 0)
18130
16804
  return Promise.resolve();
18131
- return new Promise((resolve8) => setTimeout(resolve8, this.stepDelay));
16805
+ return new Promise((resolve9) => setTimeout(resolve9, this.stepDelay));
18132
16806
  }
18133
16807
  };
18134
16808
  });
@@ -18147,8 +16821,8 @@ var init_src2 = __esm(() => {
18147
16821
 
18148
16822
  // ../../apps/desktop/src/core/pairing/runtime/bridge/log.ts
18149
16823
  import { appendFileSync, mkdirSync as mkdirSync7 } from "fs";
18150
- import { join as join18 } from "path";
18151
- import { homedir as homedir11 } from "os";
16824
+ import { join as join19 } from "path";
16825
+ import { homedir as homedir12 } from "os";
18152
16826
  function write(level, category, message, data) {
18153
16827
  const ts = new Date().toISOString().slice(11, 23);
18154
16828
  const lvl = level.toUpperCase().padEnd(5);
@@ -18164,8 +16838,8 @@ function write(level, category, message, data) {
18164
16838
  }
18165
16839
  var LOG_DIR, LOG_FILE, log;
18166
16840
  var init_log = __esm(() => {
18167
- LOG_DIR = join18(homedir11(), ".scout/pairing");
18168
- LOG_FILE = join18(LOG_DIR, "bridge.log");
16841
+ LOG_DIR = join19(homedir12(), ".scout/pairing");
16842
+ LOG_FILE = join19(LOG_DIR, "bridge.log");
18169
16843
  mkdirSync7(LOG_DIR, { recursive: true });
18170
16844
  log = {
18171
16845
  debug: (cat, msg, data) => write("debug", cat, msg, data),
@@ -18241,11 +16915,11 @@ var init_bridge = __esm(() => {
18241
16915
  });
18242
16916
 
18243
16917
  // ../../apps/desktop/src/core/pairing/runtime/bridge/config.ts
18244
- import { existsSync as existsSync14, readFileSync as readFileSync9 } from "fs";
18245
- import { join as join19 } from "path";
18246
- import { homedir as homedir12 } from "os";
16918
+ import { existsSync as existsSync15, readFileSync as readFileSync9 } from "fs";
16919
+ import { join as join20 } from "path";
16920
+ import { homedir as homedir13 } from "os";
18247
16921
  function loadConfigFile() {
18248
- if (!existsSync14(CONFIG_FILE)) {
16922
+ if (!existsSync15(CONFIG_FILE)) {
18249
16923
  return {};
18250
16924
  }
18251
16925
  try {
@@ -18306,17 +16980,17 @@ var init_config3 = __esm(() => {
18306
16980
  port: 7888,
18307
16981
  secure: true
18308
16982
  };
18309
- CONFIG_DIR = join19(homedir12(), ".scout/pairing");
18310
- CONFIG_FILE = join19(CONFIG_DIR, "config.json");
16983
+ CONFIG_DIR = join20(homedir13(), ".scout/pairing");
16984
+ CONFIG_FILE = join20(CONFIG_DIR, "config.json");
18311
16985
  });
18312
16986
 
18313
16987
  // ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
18314
16988
  import { isAbsolute as isAbsolute2 } from "path";
18315
- import { homedir as homedir13 } from "os";
16989
+ import { homedir as homedir14 } from "os";
18316
16990
  function isAllowedPath(filePath) {
18317
16991
  if (!isAbsolute2(filePath))
18318
16992
  return false;
18319
- const relToHome = filePath.slice(homedir13().length + 1);
16993
+ const relToHome = filePath.slice(homedir14().length + 1);
18320
16994
  if (relToHome.startsWith(".") && !relToHome.startsWith(".claude") && !relToHome.startsWith(".scout/pairing")) {
18321
16995
  return false;
18322
16996
  }
@@ -18375,16 +17049,16 @@ function serveFile(url) {
18375
17049
  }
18376
17050
  var ALLOWED_ROOTS;
18377
17051
  var init_fileserver = __esm(() => {
18378
- ALLOWED_ROOTS = [homedir13(), "/tmp"];
17052
+ ALLOWED_ROOTS = [homedir14(), "/tmp"];
18379
17053
  });
18380
17054
 
18381
17055
  // ../../apps/desktop/src/server/db-queries.ts
18382
17056
  import { Database } from "bun:sqlite";
18383
- import { homedir as homedir14 } from "os";
18384
- import { join as join20 } from "path";
17057
+ import { homedir as homedir15 } from "os";
17058
+ import { join as join21 } from "path";
18385
17059
  function resolveDbPath() {
18386
- const controlHome = process.env.OPENSCOUT_CONTROL_HOME ?? join20(homedir14(), ".openscout", "control-plane");
18387
- return join20(controlHome, "control-plane.sqlite");
17060
+ const controlHome = process.env.OPENSCOUT_CONTROL_HOME ?? join21(homedir15(), ".openscout", "control-plane");
17061
+ return join21(controlHome, "control-plane.sqlite");
18388
17062
  }
18389
17063
  function db() {
18390
17064
  if (!_db) {
@@ -18626,11 +17300,11 @@ function queryMobileAgentDetail(agentId) {
18626
17300
  }
18627
17301
  var _db = null, HOME;
18628
17302
  var init_db_queries = __esm(() => {
18629
- HOME = homedir14();
17303
+ HOME = homedir15();
18630
17304
  });
18631
17305
 
18632
17306
  // ../../apps/desktop/src/core/mobile/service.ts
18633
- import { basename as basename6, resolve as resolve8 } from "path";
17307
+ import { basename as basename6, resolve as resolve9 } from "path";
18634
17308
  function normalizeTimestamp(value) {
18635
17309
  if (!value)
18636
17310
  return null;
@@ -19019,7 +17693,7 @@ async function createScoutSession(input, currentDirectory, deviceId) {
19019
17693
  if (!rawWorkspaceId) {
19020
17694
  throw new Error(`Invalid workspaceId.`);
19021
17695
  }
19022
- const workspaceRoot = resolve8(rawWorkspaceId);
17696
+ const workspaceRoot = resolve9(rawWorkspaceId);
19023
17697
  const projectName = basename6(workspaceRoot) || workspaceRoot;
19024
17698
  const workspace = {
19025
17699
  id: workspaceRoot,
@@ -19129,17 +17803,17 @@ async function deriveNewAgentName(projectName, branch, harness) {
19129
17803
  }
19130
17804
  async function createGitWorktree(projectRoot, agentName) {
19131
17805
  const { execSync: execSync2 } = await import("child_process");
19132
- const { join: join21 } = await import("path");
19133
- const { mkdirSync: mkdirSync8, existsSync: existsSync15 } = await import("fs");
17806
+ const { join: join22 } = await import("path");
17807
+ const { mkdirSync: mkdirSync8, existsSync: existsSync16 } = await import("fs");
19134
17808
  try {
19135
17809
  execSync2("git rev-parse --git-dir", { cwd: projectRoot, stdio: "pipe" });
19136
17810
  } catch {
19137
17811
  return null;
19138
17812
  }
19139
17813
  const branchName = `scout/${agentName}`;
19140
- const worktreeDir = join21(projectRoot, ".scout-worktrees");
19141
- const worktreePath = join21(worktreeDir, agentName);
19142
- if (existsSync15(worktreePath)) {
17814
+ const worktreeDir = join22(projectRoot, ".scout-worktrees");
17815
+ const worktreePath = join22(worktreeDir, agentName);
17816
+ if (existsSync16(worktreePath)) {
19143
17817
  return { path: worktreePath, branch: branchName };
19144
17818
  }
19145
17819
  mkdirSync8(worktreeDir, { recursive: true });
@@ -19184,8 +17858,8 @@ var init_service5 = __esm(async () => {
19184
17858
  // ../../apps/desktop/src/core/pairing/runtime/bridge/server.ts
19185
17859
  import { readdirSync as readdirSync2, readFileSync as readFileSync10, realpathSync, statSync as statSync3 } from "fs";
19186
17860
  import { execSync as execSync2 } from "child_process";
19187
- import { basename as basename7, isAbsolute as isAbsolute3, join as join21, relative as relative2 } from "path";
19188
- import { homedir as homedir15 } from "os";
17861
+ import { basename as basename7, isAbsolute as isAbsolute3, join as join22, relative as relative2 } from "path";
17862
+ import { homedir as homedir16 } from "os";
19189
17863
  function replaySyncEvents(bridge, sessionId, lastSeq) {
19190
17864
  return bridge.replay(sessionId, lastSeq);
19191
17865
  }
@@ -19639,13 +18313,13 @@ function resolveMobileCurrentDirectory() {
19639
18313
  }
19640
18314
  }
19641
18315
  function resolveWorkspaceRoot(root) {
19642
- const expandedRoot = root.replace(/^~/, homedir15());
18316
+ const expandedRoot = root.replace(/^~/, homedir16());
19643
18317
  return realpathSync(expandedRoot);
19644
18318
  }
19645
18319
  function resolveWorkspacePath(root, requestedPath) {
19646
18320
  const normalizedRoot = resolveWorkspaceRoot(root);
19647
- const expandedPath = requestedPath?.replace(/^~/, homedir15());
19648
- const candidate = expandedPath ? isAbsolute3(expandedPath) ? expandedPath : join21(normalizedRoot, expandedPath) : normalizedRoot;
18321
+ const expandedPath = requestedPath?.replace(/^~/, homedir16());
18322
+ const candidate = expandedPath ? isAbsolute3(expandedPath) ? expandedPath : join22(normalizedRoot, expandedPath) : normalizedRoot;
19649
18323
  const resolvedCandidate = realpathSync(candidate);
19650
18324
  const rel = relative2(normalizedRoot, resolvedCandidate);
19651
18325
  if (rel === "" || !rel.startsWith("..") && !isAbsolute3(rel)) {
@@ -19660,7 +18334,7 @@ function listDirectories(dirPath) {
19660
18334
  continue;
19661
18335
  if (name === "node_modules" || name === ".build" || name === "target")
19662
18336
  continue;
19663
- const fullPath = join21(dirPath, name);
18337
+ const fullPath = join22(dirPath, name);
19664
18338
  try {
19665
18339
  const stat4 = statSync3(fullPath);
19666
18340
  if (!stat4.isDirectory())
@@ -19683,7 +18357,7 @@ function listDirectories(dirPath) {
19683
18357
  return entries.sort((a, b) => a.name.localeCompare(b.name));
19684
18358
  }
19685
18359
  async function discoverSessionFiles(maxAgeDays, limit) {
19686
- const home = homedir15();
18360
+ const home = homedir16();
19687
18361
  const results = [];
19688
18362
  const searchPaths = [
19689
18363
  { pattern: `${home}/.claude/projects`, agent: "claude-code" },
@@ -34591,8 +33265,8 @@ var init_zod = __esm(() => {
34591
33265
  // ../../apps/desktop/src/core/pairing/runtime/bridge/router.ts
34592
33266
  import { readFileSync as readFileSync11, readdirSync as readdirSync3, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
34593
33267
  import { execSync as execSync3 } from "child_process";
34594
- import { basename as basename8, isAbsolute as isAbsolute4, join as join22, relative as relative3 } from "path";
34595
- import { homedir as homedir16 } from "os";
33268
+ import { basename as basename8, isAbsolute as isAbsolute4, join as join23, relative as relative3 } from "path";
33269
+ import { homedir as homedir17 } from "os";
34596
33270
  function resolveMobileCurrentDirectory2() {
34597
33271
  const config2 = resolveConfig();
34598
33272
  const configuredRoot = config2.workspace?.root;
@@ -34605,13 +33279,13 @@ function resolveMobileCurrentDirectory2() {
34605
33279
  }
34606
33280
  }
34607
33281
  function resolveWorkspaceRoot2(root) {
34608
- const expandedRoot = root.replace(/^~/, homedir16());
33282
+ const expandedRoot = root.replace(/^~/, homedir17());
34609
33283
  return realpathSync2(expandedRoot);
34610
33284
  }
34611
33285
  function resolveWorkspacePath2(root, requestedPath) {
34612
33286
  const normalizedRoot = resolveWorkspaceRoot2(root);
34613
- const expandedPath = requestedPath?.replace(/^~/, homedir16());
34614
- const candidate = expandedPath ? isAbsolute4(expandedPath) ? expandedPath : join22(normalizedRoot, expandedPath) : normalizedRoot;
33287
+ const expandedPath = requestedPath?.replace(/^~/, homedir17());
33288
+ const candidate = expandedPath ? isAbsolute4(expandedPath) ? expandedPath : join23(normalizedRoot, expandedPath) : normalizedRoot;
34615
33289
  const resolvedCandidate = realpathSync2(candidate);
34616
33290
  const rel = relative3(normalizedRoot, resolvedCandidate);
34617
33291
  if (rel === "" || !rel.startsWith("..") && !isAbsolute4(rel)) {
@@ -34626,7 +33300,7 @@ function listDirectories2(dirPath) {
34626
33300
  continue;
34627
33301
  if (name === "node_modules" || name === ".build" || name === "target")
34628
33302
  continue;
34629
- const fullPath = join22(dirPath, name);
33303
+ const fullPath = join23(dirPath, name);
34630
33304
  try {
34631
33305
  const stat4 = statSync4(fullPath);
34632
33306
  if (!stat4.isDirectory())
@@ -34665,7 +33339,7 @@ function detectAgent2(filePath) {
34665
33339
  return "unknown";
34666
33340
  }
34667
33341
  async function discoverSessionFiles2(maxAgeDays, limit) {
34668
- const home = homedir16();
33342
+ const home = homedir17();
34669
33343
  const results = [];
34670
33344
  const searchPaths = [
34671
33345
  { pattern: `${home}/.claude/projects`, agent: "claude-code" },
@@ -34747,23 +33421,23 @@ function bridgeEventIterable(bridge, signal) {
34747
33421
  return {
34748
33422
  [Symbol.asyncIterator]() {
34749
33423
  const buffer = [];
34750
- let resolve9 = null;
33424
+ let resolve10 = null;
34751
33425
  let done = false;
34752
33426
  const unsub = bridge.onEvent((event) => {
34753
33427
  if (done)
34754
33428
  return;
34755
33429
  buffer.push(event);
34756
- if (resolve9) {
34757
- resolve9();
34758
- resolve9 = null;
33430
+ if (resolve10) {
33431
+ resolve10();
33432
+ resolve10 = null;
34759
33433
  }
34760
33434
  });
34761
33435
  const cleanup = () => {
34762
33436
  done = true;
34763
33437
  unsub();
34764
- if (resolve9) {
34765
- resolve9();
34766
- resolve9 = null;
33438
+ if (resolve10) {
33439
+ resolve10();
33440
+ resolve10 = null;
34767
33441
  }
34768
33442
  };
34769
33443
  signal?.addEventListener("abort", cleanup, { once: true });
@@ -34776,7 +33450,7 @@ function bridgeEventIterable(bridge, signal) {
34776
33450
  return { done: false, value: buffer.shift() };
34777
33451
  }
34778
33452
  await new Promise((r) => {
34779
- resolve9 = r;
33453
+ resolve10 = r;
34780
33454
  });
34781
33455
  }
34782
33456
  },
@@ -37494,15 +36168,15 @@ function resolveSelfTuple(promise2) {
37494
36168
  return Unpromise.proxy(promise2).then(() => [promise2]);
37495
36169
  }
37496
36170
  function withResolvers() {
37497
- let resolve9;
36171
+ let resolve10;
37498
36172
  let reject;
37499
36173
  const promise2 = new Promise((_resolve, _reject) => {
37500
- resolve9 = _resolve;
36174
+ resolve10 = _resolve;
37501
36175
  reject = _reject;
37502
36176
  });
37503
36177
  return {
37504
36178
  promise: promise2,
37505
- resolve: resolve9,
36179
+ resolve: resolve10,
37506
36180
  reject
37507
36181
  };
37508
36182
  }
@@ -37541,8 +36215,8 @@ function timerResource(ms) {
37541
36215
  return makeResource({ start() {
37542
36216
  if (timer)
37543
36217
  throw new Error("Timer already started");
37544
- const promise2 = new Promise((resolve9) => {
37545
- timer = setTimeout(() => resolve9(disposablePromiseTimerResult), ms);
36218
+ const promise2 = new Promise((resolve10) => {
36219
+ timer = setTimeout(() => resolve10(disposablePromiseTimerResult), ms);
37546
36220
  });
37547
36221
  return promise2;
37548
36222
  } }, () => {
@@ -37591,15 +36265,15 @@ function _takeWithGrace() {
37591
36265
  return _takeWithGrace.apply(this, arguments);
37592
36266
  }
37593
36267
  function createDeferred() {
37594
- let resolve9;
36268
+ let resolve10;
37595
36269
  let reject;
37596
36270
  const promise2 = new Promise((res, rej) => {
37597
- resolve9 = res;
36271
+ resolve10 = res;
37598
36272
  reject = rej;
37599
36273
  });
37600
36274
  return {
37601
36275
  promise: promise2,
37602
- resolve: resolve9,
36276
+ resolve: resolve10,
37603
36277
  reject
37604
36278
  };
37605
36279
  }
@@ -38575,8 +37249,8 @@ var import_objectSpread2$13, jsonContentTypeHandler, formDataContentTypeHandler,
38575
37249
  status: "fulfilled",
38576
37250
  value
38577
37251
  };
38578
- subscribers === null || subscribers === undefined || subscribers.forEach(({ resolve: resolve9 }) => {
38579
- resolve9(value);
37252
+ subscribers === null || subscribers === undefined || subscribers.forEach(({ resolve: resolve10 }) => {
37253
+ resolve10(value);
38580
37254
  });
38581
37255
  });
38582
37256
  if ("catch" in thenReturn)
@@ -39307,14 +37981,14 @@ var init_observable = __esm(() => {
39307
37981
 
39308
37982
  // ../../apps/desktop/src/core/pairing/runtime/bridge/server-trpc.ts
39309
37983
  import { realpathSync as realpathSync3 } from "fs";
39310
- import { homedir as homedir17 } from "os";
37984
+ import { homedir as homedir18 } from "os";
39311
37985
  function resolveCurrentDirectory() {
39312
37986
  try {
39313
37987
  const config2 = resolveConfig();
39314
37988
  const configuredRoot = config2.workspace?.root;
39315
37989
  if (!configuredRoot)
39316
37990
  return process.cwd();
39317
- const expanded = configuredRoot.replace(/^~/, homedir17());
37991
+ const expanded = configuredRoot.replace(/^~/, homedir18());
39318
37992
  return realpathSync3(expanded);
39319
37993
  } catch {
39320
37994
  return process.cwd();
@@ -39432,8 +38106,8 @@ function startBridgeServerTRPC(options) {
39432
38106
  }
39433
38107
  const iterable = isObservable(result) ? observableToAsyncIterable(result, abortController.signal) : result;
39434
38108
  const iterator = iterable[Symbol.asyncIterator]();
39435
- const abortPromise = new Promise((resolve9) => {
39436
- abortController.signal.addEventListener("abort", () => resolve9("abort"), {
38109
+ const abortPromise = new Promise((resolve10) => {
38110
+ abortController.signal.addEventListener("abort", () => resolve10("abort"), {
39437
38111
  once: true
39438
38112
  });
39439
38113
  });
@@ -39730,7 +38404,7 @@ var init_server_trpc = __esm(async () => {
39730
38404
  });
39731
38405
 
39732
38406
  // ../../apps/desktop/src/core/pairing/runtime/runtime.ts
39733
- import { homedir as homedir18 } from "os";
38407
+ import { homedir as homedir19 } from "os";
39734
38408
  function createPairingAdapterRegistry(configAdapters) {
39735
38409
  const adapters = {
39736
38410
  "claude-code": createAdapter,
@@ -39807,7 +38481,7 @@ async function autoStartConfiguredSessions(bridge, sessions3) {
39807
38481
  try {
39808
38482
  const session = await bridge.createSession(entry.adapter, {
39809
38483
  name: entry.name,
39810
- cwd: entry.cwd?.replace(/^~/, homedir18()),
38484
+ cwd: entry.cwd?.replace(/^~/, homedir19()),
39811
38485
  options: entry.options
39812
38486
  });
39813
38487
  console.log(`[bridge] session started: ${session.name} (${entry.adapter})`);
@@ -40078,11 +38752,11 @@ var BRIDGE_ABSENCE_GRACE_MS = 30000, ROOM_IDLE_TIMEOUT_MS = 60000;
40078
38752
 
40079
38753
  // ../../apps/desktop/src/core/pairing/runtime/relay-runtime.ts
40080
38754
  import { execSync as execSync4 } from "child_process";
40081
- import { existsSync as existsSync15, mkdirSync as mkdirSync9, readdirSync as readdirSync4 } from "fs";
40082
- import { homedir as homedir19 } from "os";
40083
- import { join as join23 } from "path";
38755
+ import { existsSync as existsSync16, mkdirSync as mkdirSync9, readdirSync as readdirSync4 } from "fs";
38756
+ import { homedir as homedir20 } from "os";
38757
+ import { join as join24 } from "path";
40084
38758
  function findStoredCerts() {
40085
- if (!existsSync15(PAIRING_DIR2)) {
38759
+ if (!existsSync16(PAIRING_DIR2)) {
40086
38760
  return null;
40087
38761
  }
40088
38762
  try {
@@ -40096,8 +38770,8 @@ function findStoredCerts() {
40096
38770
  return null;
40097
38771
  }
40098
38772
  return {
40099
- cert: join23(PAIRING_DIR2, certFile),
40100
- key: join23(PAIRING_DIR2, keyFile)
38773
+ cert: join24(PAIRING_DIR2, certFile),
38774
+ key: join24(PAIRING_DIR2, keyFile)
40101
38775
  };
40102
38776
  } catch {
40103
38777
  return null;
@@ -40118,8 +38792,8 @@ function getTailscaleHostname() {
40118
38792
  }
40119
38793
  function generateTailscaleCerts(hostname5) {
40120
38794
  mkdirSync9(PAIRING_DIR2, { recursive: true });
40121
- const certPath = join23(PAIRING_DIR2, `${hostname5}.crt`);
40122
- const keyPath = join23(PAIRING_DIR2, `${hostname5}.key`);
38795
+ const certPath = join24(PAIRING_DIR2, `${hostname5}.crt`);
38796
+ const keyPath = join24(PAIRING_DIR2, `${hostname5}.key`);
40123
38797
  try {
40124
38798
  pairingLog.info("relay", "generating tailscale TLS cert", { hostname: hostname5 });
40125
38799
  execSync4(`tailscale cert --cert-file "${certPath}" --key-file "${keyPath}" "${hostname5}"`, {
@@ -40135,8 +38809,8 @@ function generateTailscaleCerts(hostname5) {
40135
38809
  }
40136
38810
  function generateSelfSignedCert(hostname5) {
40137
38811
  mkdirSync9(PAIRING_DIR2, { recursive: true });
40138
- const certPath = join23(PAIRING_DIR2, `${hostname5}.crt`);
40139
- const keyPath = join23(PAIRING_DIR2, `${hostname5}.key`);
38812
+ const certPath = join24(PAIRING_DIR2, `${hostname5}.crt`);
38813
+ const keyPath = join24(PAIRING_DIR2, `${hostname5}.key`);
40140
38814
  try {
40141
38815
  execSync4(`openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 ` + `-keyout "${keyPath}" -out "${certPath}" -days 365 -nodes ` + `-subj "/CN=${hostname5}" -addext "subjectAltName=DNS:${hostname5}"`, {
40142
38816
  stdio: ["pipe", "pipe", "pipe"],
@@ -40199,7 +38873,7 @@ function startManagedRelay(port = 7889) {
40199
38873
  var PAIRING_DIR2;
40200
38874
  var init_relay_runtime = __esm(() => {
40201
38875
  init_log2();
40202
- PAIRING_DIR2 = join23(homedir19(), ".scout/pairing");
38876
+ PAIRING_DIR2 = join24(homedir20(), ".scout/pairing");
40203
38877
  });
40204
38878
 
40205
38879
  // ../../node_modules/.bun/uqr@0.1.3/node_modules/uqr/dist/index.mjs
@@ -41064,8 +39738,8 @@ async function runPairCommand(context, args) {
41064
39738
  process.on("SIGINT", handleSignal);
41065
39739
  process.on("SIGTERM", handleSignal);
41066
39740
  try {
41067
- await new Promise((resolve9) => {
41068
- controller.signal.addEventListener("abort", () => resolve9(), { once: true });
39741
+ await new Promise((resolve10) => {
39742
+ controller.signal.addEventListener("abort", () => resolve10(), { once: true });
41069
39743
  });
41070
39744
  } finally {
41071
39745
  process.off("SIGINT", handleSignal);
@@ -41128,62 +39802,34 @@ var init_runtimes = __esm(() => {
41128
39802
  init_service3();
41129
39803
  });
41130
39804
 
41131
- // ../../apps/desktop/src/cli/commands/send.ts
41132
- var exports_send = {};
41133
- __export(exports_send, {
41134
- runSendCommand: () => runSendCommand
41135
- });
41136
- async function runSendCommand(context, args) {
41137
- const options = parseSendCommandOptions(args, defaultScoutContextDirectory(context));
41138
- const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
41139
- const senderId = await resolveScoutSenderId(options.agentName, currentDirectory);
41140
- const result = await sendScoutMessage({
41141
- senderId,
41142
- body: options.message,
41143
- channel: options.channel,
41144
- shouldSpeak: options.shouldSpeak,
41145
- executionHarness: parseScoutHarness(options.harness),
41146
- currentDirectory: options.currentDirectory
41147
- });
41148
- if (!result.usedBroker) {
41149
- throw new Error("broker is not reachable");
41150
- }
41151
- context.output.writeValue({
41152
- message: options.message,
41153
- invokedTargets: result.invokedTargets,
41154
- unresolvedTargets: result.unresolvedTargets
41155
- }, renderScoutMessagePostResult);
41156
- }
41157
- var init_send = __esm(async () => {
41158
- init_context();
41159
- init_options();
41160
- init_broker();
41161
- await init_service();
41162
- });
41163
-
41164
39805
  // ../../apps/desktop/src/cli/commands/server.ts
41165
39806
  var exports_server = {};
41166
39807
  __export(exports_server, {
41167
39808
  runServerCommand: () => runServerCommand,
41168
39809
  resolveScoutWebServerEntry: () => resolveScoutWebServerEntry,
41169
39810
  resolveScoutControlPlaneWebServerEntry: () => resolveScoutControlPlaneWebServerEntry,
41170
- renderServerCommandHelp: () => renderServerCommandHelp
39811
+ renderServerCommandHelp: () => renderServerCommandHelp,
39812
+ normalizeServerOpenPath: () => normalizeServerOpenPath
41171
39813
  });
41172
39814
  import { spawn as spawn5 } from "child_process";
41173
- import { existsSync as existsSync16 } from "fs";
41174
- import { dirname as dirname10, join as join24 } from "path";
41175
- import { fileURLToPath as fileURLToPath7 } from "url";
39815
+ import { existsSync as existsSync17 } from "fs";
39816
+ import { dirname as dirname11, join as join25, resolve as resolve10 } from "path";
39817
+ import { fileURLToPath as fileURLToPath8 } from "url";
41176
39818
  function renderServerCommandHelp() {
41177
39819
  return [
41178
39820
  "scout server \u2014 desktop web UI (Bun runtime)",
41179
39821
  "",
41180
39822
  "Usage:",
41181
39823
  " scout server start [options]",
39824
+ " scout server open [options]",
41182
39825
  " scout server control-plane start [options]",
39826
+ " scout server control-plane open [options]",
41183
39827
  "",
41184
39828
  "Subcommands:",
41185
39829
  " start Full desktop web API + UI assets (default stack).",
39830
+ " open Open the full web UI and start it on demand if needed.",
41186
39831
  " control-plane start Pairing + relay/shell activity only (`@openscout/web` surface).",
39832
+ " control-plane open Open the control-plane UI and start it on demand if needed.",
41187
39833
  "",
41188
39834
  "Options:",
41189
39835
  " --port <n> Listen port (default 3200; env SCOUT_WEB_PORT)",
@@ -41191,6 +39837,7 @@ function renderServerCommandHelp() {
41191
39837
  " --static-root DIR Static client root (env SCOUT_STATIC_ROOT)",
41192
39838
  " --vite-url URL Dev proxy target for non-API routes (env SCOUT_VITE_URL)",
41193
39839
  " --cwd DIR Workspace / setup root (env OPENSCOUT_SETUP_CWD)",
39840
+ " --path PATH Browser path for `open` (default /)",
41194
39841
  "",
41195
39842
  "Requires `bun` on PATH.",
41196
39843
  "Published installs include dist/client for the full web UI and dist/control-plane-client",
@@ -41200,31 +39847,32 @@ function renderServerCommandHelp() {
41200
39847
  `);
41201
39848
  }
41202
39849
  function resolveScoutWebServerEntry() {
41203
- const mainDir = dirname10(fileURLToPath7(import.meta.url));
41204
- const bundled = join24(mainDir, "scout-web-server.mjs");
41205
- if (existsSync16(bundled)) {
39850
+ const mainDir = dirname11(fileURLToPath8(import.meta.url));
39851
+ const bundled = join25(mainDir, "scout-web-server.mjs");
39852
+ if (existsSync17(bundled)) {
41206
39853
  return bundled;
41207
39854
  }
41208
- const source = fileURLToPath7(new URL("../../server/index.ts", import.meta.url));
41209
- if (existsSync16(source)) {
39855
+ const source = fileURLToPath8(new URL("../../server/index.ts", import.meta.url));
39856
+ if (existsSync17(source)) {
41210
39857
  return source;
41211
39858
  }
41212
39859
  throw new ScoutCliError("Could not find Scout web server entry. Rebuild @openscout/scout or run from the OpenScout repository.");
41213
39860
  }
41214
39861
  function resolveScoutControlPlaneWebServerEntry() {
41215
- const mainDir = dirname10(fileURLToPath7(import.meta.url));
41216
- const bundled = join24(mainDir, "scout-control-plane-web.mjs");
41217
- if (existsSync16(bundled)) {
39862
+ const mainDir = dirname11(fileURLToPath8(import.meta.url));
39863
+ const bundled = join25(mainDir, "scout-control-plane-web.mjs");
39864
+ if (existsSync17(bundled)) {
41218
39865
  return bundled;
41219
39866
  }
41220
- const source = fileURLToPath7(new URL("../../server/control-plane-index.ts", import.meta.url));
41221
- if (existsSync16(source)) {
39867
+ const source = fileURLToPath8(new URL("../../server/control-plane-index.ts", import.meta.url));
39868
+ if (existsSync17(source)) {
41222
39869
  return source;
41223
39870
  }
41224
39871
  throw new ScoutCliError("Could not find Scout control-plane web server entry. Rebuild @openscout/scout or run from the OpenScout repository.");
41225
39872
  }
41226
- function parseServerStartFlags(args) {
39873
+ function parseServerFlags(args) {
41227
39874
  const env = {};
39875
+ let openPath = "/";
41228
39876
  for (let i = 0;i < args.length; i++) {
41229
39877
  const a = args[i];
41230
39878
  if (a === "--port") {
@@ -41259,39 +39907,44 @@ function parseServerStartFlags(args) {
41259
39907
  env.OPENSCOUT_SETUP_CWD = v;
41260
39908
  continue;
41261
39909
  }
39910
+ if (a === "--path") {
39911
+ const v = args[++i];
39912
+ if (!v)
39913
+ throw new ScoutCliError("--path requires a value");
39914
+ openPath = v;
39915
+ continue;
39916
+ }
39917
+ if (a.startsWith("--port=")) {
39918
+ env.SCOUT_WEB_PORT = a.slice("--port=".length);
39919
+ continue;
39920
+ }
39921
+ if (a.startsWith("--static-root=")) {
39922
+ env.SCOUT_STATIC_ROOT = a.slice("--static-root=".length);
39923
+ continue;
39924
+ }
39925
+ if (a.startsWith("--vite-url=")) {
39926
+ env.SCOUT_VITE_URL = a.slice("--vite-url=".length);
39927
+ continue;
39928
+ }
39929
+ if (a.startsWith("--cwd=")) {
39930
+ env.OPENSCOUT_SETUP_CWD = a.slice("--cwd=".length);
39931
+ continue;
39932
+ }
39933
+ if (a.startsWith("--path=")) {
39934
+ openPath = a.slice("--path=".length);
39935
+ continue;
39936
+ }
41262
39937
  throw new ScoutCliError(`unknown option: ${a}`);
41263
39938
  }
41264
- return { env };
39939
+ return { env, openPath };
41265
39940
  }
41266
39941
  function resolveBundledStaticClientRoot(entry, mode) {
41267
- const entryDir = dirname10(entry);
41268
- const clientDirectory = mode === "control-plane" ? join24(entryDir, "control-plane-client") : join24(entryDir, "client");
41269
- const indexPath = join24(clientDirectory, "index.html");
41270
- return existsSync16(indexPath) ? clientDirectory : null;
39942
+ const entryDir = dirname11(entry);
39943
+ const clientDirectory = mode === "control-plane" ? join25(entryDir, "control-plane-client") : join25(entryDir, "client");
39944
+ const indexPath = join25(clientDirectory, "index.html");
39945
+ return existsSync17(indexPath) ? clientDirectory : null;
41271
39946
  }
41272
- async function runServerCommand(context, args) {
41273
- if (args.length === 0 || args[0] === "help" || args[0] === "--help" || args[0] === "-h") {
41274
- context.output.writeText(renderServerCommandHelp());
41275
- return;
41276
- }
41277
- let flagArgs;
41278
- let entry;
41279
- let mode;
41280
- if (args[0] === "start") {
41281
- flagArgs = args.slice(1);
41282
- entry = resolveScoutWebServerEntry();
41283
- mode = "full";
41284
- } else if (args[0] === "control-plane") {
41285
- if (args[1] !== "start") {
41286
- throw new ScoutCliError("expected: scout server control-plane start");
41287
- }
41288
- flagArgs = args.slice(2);
41289
- entry = resolveScoutControlPlaneWebServerEntry();
41290
- mode = "control-plane";
41291
- } else {
41292
- throw new ScoutCliError(`unknown subcommand: ${args[0]} (try: scout server start)`);
41293
- }
41294
- const { env: flagEnv } = parseServerStartFlags(flagArgs);
39947
+ function buildMergedServerEnv(entry, mode, flagEnv) {
41295
39948
  const bundledStaticClientRoot = resolveBundledStaticClientRoot(entry, mode);
41296
39949
  const autoEnv = {};
41297
39950
  if (bundledStaticClientRoot) {
@@ -41301,9 +39954,254 @@ async function runServerCommand(context, args) {
41301
39954
  autoEnv.SCOUT_STATIC_ROOT = bundledStaticClientRoot;
41302
39955
  }
41303
39956
  }
41304
- const mergedEnv = { ...process.env, ...autoEnv, ...flagEnv };
39957
+ return { ...process.env, ...autoEnv, ...flagEnv };
39958
+ }
39959
+ function parseServerSelection(args) {
39960
+ if (args[0] === "start") {
39961
+ return {
39962
+ action: "start",
39963
+ flagArgs: args.slice(1),
39964
+ entry: resolveScoutWebServerEntry(),
39965
+ mode: "full"
39966
+ };
39967
+ }
39968
+ if (args[0] === "open") {
39969
+ return {
39970
+ action: "open",
39971
+ flagArgs: args.slice(1),
39972
+ entry: resolveScoutWebServerEntry(),
39973
+ mode: "full"
39974
+ };
39975
+ }
39976
+ if (args[0] === "control-plane") {
39977
+ if (args[1] !== "start" && args[1] !== "open") {
39978
+ throw new ScoutCliError("expected: scout server control-plane <start|open>");
39979
+ }
39980
+ return {
39981
+ action: args[1],
39982
+ flagArgs: args.slice(2),
39983
+ entry: resolveScoutControlPlaneWebServerEntry(),
39984
+ mode: "control-plane"
39985
+ };
39986
+ }
39987
+ throw new ScoutCliError(`unknown subcommand: ${args[0]} (try: scout server open)`);
39988
+ }
39989
+ function normalizeServerOpenPath(value) {
39990
+ const trimmed = value.trim();
39991
+ if (!trimmed) {
39992
+ return "/";
39993
+ }
39994
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
39995
+ throw new ScoutCliError("--path must be a local path, not an absolute URL");
39996
+ }
39997
+ if (trimmed.startsWith("/")) {
39998
+ return trimmed;
39999
+ }
40000
+ return `/${trimmed}`;
40001
+ }
40002
+ function resolveServerPort(env) {
40003
+ const raw2 = env.SCOUT_WEB_PORT?.trim() || "3200";
40004
+ const port = Number.parseInt(raw2, 10);
40005
+ if (!Number.isFinite(port) || port <= 0) {
40006
+ throw new ScoutCliError(`invalid port: ${raw2}`);
40007
+ }
40008
+ return port;
40009
+ }
40010
+ function resolveExpectedCurrentDirectory(env) {
40011
+ return resolve10(env.OPENSCOUT_SETUP_CWD?.trim() || process.cwd());
40012
+ }
40013
+ function renderModeLabel(mode) {
40014
+ return mode === "control-plane" ? "Scout control plane" : "Scout";
40015
+ }
40016
+ function renderSurfaceLabel(surface) {
40017
+ switch (surface) {
40018
+ case "control-plane":
40019
+ return "control-plane";
40020
+ case "openscout-web":
40021
+ return "@openscout/web";
40022
+ case "full":
40023
+ default:
40024
+ return "full";
40025
+ }
40026
+ }
40027
+ function renderServerOpenResult(result) {
40028
+ const prefix = result.mode === "control-plane" ? "Opened Scout control plane" : "Opened Scout";
40029
+ return `${prefix} at ${result.url}${result.reusedExistingServer ? "" : " (started server)"}`;
40030
+ }
40031
+ function healthUrlForPort(port) {
40032
+ return new URL(`/api/health`, `http://127.0.0.1:${port}`);
40033
+ }
40034
+ async function probeScoutServer(port) {
40035
+ const controller = new AbortController;
40036
+ const timeout = setTimeout(() => controller.abort(), SERVER_HEALTH_TIMEOUT_MS);
40037
+ try {
40038
+ const response = await fetch(healthUrlForPort(port), {
40039
+ headers: { accept: "application/json" },
40040
+ signal: controller.signal
40041
+ });
40042
+ if (!response.ok) {
40043
+ return {
40044
+ status: "non-scout",
40045
+ statusCode: response.status
40046
+ };
40047
+ }
40048
+ let body;
40049
+ try {
40050
+ body = await response.json();
40051
+ } catch {
40052
+ return {
40053
+ status: "non-scout",
40054
+ statusCode: response.status
40055
+ };
40056
+ }
40057
+ if (body.ok === true && (body.surface === "full" || body.surface === "control-plane" || body.surface === "openscout-web") && typeof body.currentDirectory === "string") {
40058
+ return {
40059
+ status: "healthy",
40060
+ health: {
40061
+ ok: true,
40062
+ surface: body.surface,
40063
+ currentDirectory: body.currentDirectory
40064
+ }
40065
+ };
40066
+ }
40067
+ return {
40068
+ status: "non-scout",
40069
+ statusCode: response.status
40070
+ };
40071
+ } catch {
40072
+ return { status: "unreachable" };
40073
+ } finally {
40074
+ clearTimeout(timeout);
40075
+ }
40076
+ }
40077
+ async function openBrowser(url2) {
40078
+ await new Promise((resolvePromise, rejectPromise) => {
40079
+ let command;
40080
+ let args;
40081
+ if (process.platform === "darwin") {
40082
+ command = "open";
40083
+ args = [url2];
40084
+ } else if (process.platform === "win32") {
40085
+ command = "cmd";
40086
+ args = ["/c", "start", "", url2];
40087
+ } else {
40088
+ command = "xdg-open";
40089
+ args = [url2];
40090
+ }
40091
+ const child = spawn5(command, args, {
40092
+ detached: true,
40093
+ stdio: "ignore",
40094
+ windowsHide: true
40095
+ });
40096
+ child.once("error", (error48) => {
40097
+ if (error48.code === "ENOENT") {
40098
+ rejectPromise(new ScoutCliError(`could not open a browser automatically; ${command} is not available`));
40099
+ return;
40100
+ }
40101
+ rejectPromise(error48);
40102
+ });
40103
+ child.once("spawn", () => {
40104
+ child.unref();
40105
+ resolvePromise();
40106
+ });
40107
+ });
40108
+ }
40109
+ async function spawnDetachedServer(entry, env) {
41305
40110
  await new Promise((resolvePromise, rejectPromise) => {
41306
40111
  const child = spawn5("bun", ["run", entry], {
40112
+ detached: true,
40113
+ stdio: "ignore",
40114
+ env,
40115
+ windowsHide: true
40116
+ });
40117
+ child.once("error", (error48) => {
40118
+ if (error48.code === "ENOENT") {
40119
+ rejectPromise(new ScoutCliError("`bun` was not found on PATH. Install Bun (https://bun.sh) to run scout server."));
40120
+ return;
40121
+ }
40122
+ rejectPromise(error48);
40123
+ });
40124
+ child.once("spawn", () => {
40125
+ child.unref();
40126
+ resolvePromise();
40127
+ });
40128
+ });
40129
+ }
40130
+ async function waitForScoutServer(port, mode, expectedCurrentDirectory) {
40131
+ const deadline = Date.now() + SERVER_OPEN_TIMEOUT_MS;
40132
+ while (Date.now() < deadline) {
40133
+ const probe = await probeScoutServer(port);
40134
+ if (probe.status === "healthy") {
40135
+ const actualCurrentDirectory = resolve10(probe.health.currentDirectory);
40136
+ if (probe.health.surface !== mode) {
40137
+ throw new ScoutCliError(`port ${port} is serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(mode)}.`);
40138
+ }
40139
+ if (actualCurrentDirectory !== expectedCurrentDirectory) {
40140
+ throw new ScoutCliError(`port ${port} is already serving Scout for ${actualCurrentDirectory}, not ${expectedCurrentDirectory}.`);
40141
+ }
40142
+ return;
40143
+ }
40144
+ if (probe.status === "non-scout") {
40145
+ throw new ScoutCliError(`port ${port} is already serving another HTTP app or an older Scout server; choose a different --port.`);
40146
+ }
40147
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 250));
40148
+ }
40149
+ throw new ScoutCliError(`timed out waiting for ${renderModeLabel(mode)} on port ${port}`);
40150
+ }
40151
+ async function openScoutServer(options) {
40152
+ const port = resolveServerPort(options.env);
40153
+ const expectedCurrentDirectory = resolveExpectedCurrentDirectory(options.env);
40154
+ const browserUrl = new URL(normalizeServerOpenPath(options.openPath), `http://127.0.0.1:${port}`).toString();
40155
+ const probe = await probeScoutServer(port);
40156
+ if (probe.status === "healthy") {
40157
+ const actualCurrentDirectory = resolve10(probe.health.currentDirectory);
40158
+ if (probe.health.surface !== options.mode) {
40159
+ throw new ScoutCliError(`port ${port} is already serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(options.mode)}.`);
40160
+ }
40161
+ if (actualCurrentDirectory !== expectedCurrentDirectory) {
40162
+ throw new ScoutCliError(`port ${port} is already serving Scout for ${actualCurrentDirectory}, not ${expectedCurrentDirectory}.`);
40163
+ }
40164
+ await openBrowser(browserUrl);
40165
+ return {
40166
+ url: browserUrl,
40167
+ port,
40168
+ mode: options.mode,
40169
+ reusedExistingServer: true
40170
+ };
40171
+ }
40172
+ if (probe.status === "non-scout") {
40173
+ throw new ScoutCliError(`port ${port} is already serving another HTTP app or an older Scout server; choose a different --port.`);
40174
+ }
40175
+ await spawnDetachedServer(options.entry, options.env);
40176
+ await waitForScoutServer(port, options.mode, expectedCurrentDirectory);
40177
+ await openBrowser(browserUrl);
40178
+ return {
40179
+ url: browserUrl,
40180
+ port,
40181
+ mode: options.mode,
40182
+ reusedExistingServer: false
40183
+ };
40184
+ }
40185
+ async function runServerCommand(context, args) {
40186
+ if (args.length === 0 || args[0] === "help" || args[0] === "--help" || args[0] === "-h") {
40187
+ context.output.writeText(renderServerCommandHelp());
40188
+ return;
40189
+ }
40190
+ const selection = parseServerSelection(args);
40191
+ const { env: flagEnv, openPath } = parseServerFlags(selection.flagArgs);
40192
+ const mergedEnv = buildMergedServerEnv(selection.entry, selection.mode, flagEnv);
40193
+ if (selection.action === "open") {
40194
+ const result = await openScoutServer({
40195
+ entry: selection.entry,
40196
+ mode: selection.mode,
40197
+ env: mergedEnv,
40198
+ openPath
40199
+ });
40200
+ context.output.writeValue(result, renderServerOpenResult);
40201
+ return;
40202
+ }
40203
+ await new Promise((resolvePromise, rejectPromise) => {
40204
+ const child = spawn5("bun", ["run", selection.entry], {
41307
40205
  stdio: "inherit",
41308
40206
  env: mergedEnv
41309
40207
  });
@@ -41331,13 +40229,14 @@ async function runServerCommand(context, args) {
41331
40229
  });
41332
40230
  });
41333
40231
  }
40232
+ var SERVER_OPEN_TIMEOUT_MS = 15000, SERVER_HEALTH_TIMEOUT_MS = 1500;
41334
40233
  var init_server2 = __esm(() => {
41335
40234
  init_errors();
41336
40235
  });
41337
40236
 
41338
40237
  // ../../apps/desktop/src/cli/commands/setup.ts
41339
- var exports_setup2 = {};
41340
- __export(exports_setup2, {
40238
+ var exports_setup = {};
40239
+ __export(exports_setup, {
41341
40240
  runSetupCommand: () => runSetupCommand
41342
40241
  });
41343
40242
  async function runSetupCommand(context, args) {
@@ -41359,7 +40258,7 @@ __export(exports_speak, {
41359
40258
  async function runSpeakCommand(context, args) {
41360
40259
  const options = parseSendCommandOptions(args, defaultScoutContextDirectory(context));
41361
40260
  const currentDirectory = options.currentDirectory ?? defaultScoutContextDirectory(context);
41362
- const senderId = await resolveScoutSenderId(options.agentName, currentDirectory);
40261
+ const senderId = await resolveScoutSenderId(options.agentName, currentDirectory, context.env);
41363
40262
  const config3 = await loadScoutRelayConfig();
41364
40263
  const voice = getScoutVoiceForChannel(config3, "voice");
41365
40264
  await acquireScoutOnAir(senderId);
@@ -41382,6 +40281,9 @@ async function runSpeakCommand(context, args) {
41382
40281
  if (!result.usedBroker) {
41383
40282
  throw new Error("broker is not reachable");
41384
40283
  }
40284
+ if (result.unresolvedTargets.length > 0) {
40285
+ throw new Error(formatScoutSendRoutingError(result.unresolvedTargets));
40286
+ }
41385
40287
  context.output.writeValue({
41386
40288
  message: options.message,
41387
40289
  invokedTargets: result.invokedTargets,
@@ -41392,7 +40294,10 @@ var init_speak = __esm(async () => {
41392
40294
  init_context();
41393
40295
  init_options();
41394
40296
  init_broker();
41395
- await init_service();
40297
+ await __promiseAll([
40298
+ init_service(),
40299
+ init_send()
40300
+ ]);
41396
40301
  });
41397
40302
 
41398
40303
  // ../../node_modules/.bun/@opentui+core@0.1.96+a7e4efcb39f2fc50/node_modules/@opentui/core/assets/javascript/highlights.scm
@@ -41444,11 +40349,11 @@ import { EventEmitter } from "events";
41444
40349
  import { Buffer as Buffer2 } from "buffer";
41445
40350
  import { Buffer as Buffer3 } from "buffer";
41446
40351
  import { EventEmitter as EventEmitter2 } from "events";
41447
- import { resolve as resolve9, dirname as dirname11 } from "path";
41448
- import { fileURLToPath as fileURLToPath8 } from "url";
40352
+ import { resolve as resolve11, dirname as dirname12 } from "path";
40353
+ import { fileURLToPath as fileURLToPath9 } from "url";
41449
40354
  import { resolve as resolve22, isAbsolute as isAbsolute5, parse as parse6 } from "path";
41450
- import { existsSync as existsSync17 } from "fs";
41451
- import { basename as basename9, join as join25 } from "path";
40355
+ import { existsSync as existsSync18 } from "fs";
40356
+ import { basename as basename9, join as join26 } from "path";
41452
40357
  import os from "os";
41453
40358
  import path2 from "path";
41454
40359
  import { EventEmitter as EventEmitter3 } from "events";
@@ -44580,13 +43485,13 @@ class DebounceController {
44580
43485
  }
44581
43486
  debounce(id, ms, fn) {
44582
43487
  const scopeMap = TIMERS_MAP.get(this.scopeId);
44583
- return new Promise((resolve11, reject) => {
43488
+ return new Promise((resolve13, reject) => {
44584
43489
  if (scopeMap.has(id)) {
44585
43490
  clearTimeout(scopeMap.get(id));
44586
43491
  }
44587
43492
  const timerId = setTimeout(() => {
44588
43493
  try {
44589
- resolve11(fn());
43494
+ resolve13(fn());
44590
43495
  } catch (error48) {
44591
43496
  reject(error48);
44592
43497
  }
@@ -44676,25 +43581,25 @@ function getParsers() {
44676
43581
  filetype: "javascript",
44677
43582
  aliases: ["javascriptreact"],
44678
43583
  queries: {
44679
- highlights: [resolve9(dirname11(fileURLToPath8(import.meta.url)), highlights_default)]
43584
+ highlights: [resolve11(dirname12(fileURLToPath9(import.meta.url)), highlights_default)]
44680
43585
  },
44681
- wasm: resolve9(dirname11(fileURLToPath8(import.meta.url)), tree_sitter_javascript_default)
43586
+ wasm: resolve11(dirname12(fileURLToPath9(import.meta.url)), tree_sitter_javascript_default)
44682
43587
  },
44683
43588
  {
44684
43589
  filetype: "typescript",
44685
43590
  aliases: ["typescriptreact"],
44686
43591
  queries: {
44687
- highlights: [resolve9(dirname11(fileURLToPath8(import.meta.url)), highlights_default2)]
43592
+ highlights: [resolve11(dirname12(fileURLToPath9(import.meta.url)), highlights_default2)]
44688
43593
  },
44689
- wasm: resolve9(dirname11(fileURLToPath8(import.meta.url)), tree_sitter_typescript_default)
43594
+ wasm: resolve11(dirname12(fileURLToPath9(import.meta.url)), tree_sitter_typescript_default)
44690
43595
  },
44691
43596
  {
44692
43597
  filetype: "markdown",
44693
43598
  queries: {
44694
- highlights: [resolve9(dirname11(fileURLToPath8(import.meta.url)), highlights_default3)],
44695
- injections: [resolve9(dirname11(fileURLToPath8(import.meta.url)), injections_default)]
43599
+ highlights: [resolve11(dirname12(fileURLToPath9(import.meta.url)), highlights_default3)],
43600
+ injections: [resolve11(dirname12(fileURLToPath9(import.meta.url)), injections_default)]
44696
43601
  },
44697
- wasm: resolve9(dirname11(fileURLToPath8(import.meta.url)), tree_sitter_markdown_default),
43602
+ wasm: resolve11(dirname12(fileURLToPath9(import.meta.url)), tree_sitter_markdown_default),
44698
43603
  injectionMapping: {
44699
43604
  nodeTypes: {
44700
43605
  inline: "markdown_inline",
@@ -44717,16 +43622,16 @@ function getParsers() {
44717
43622
  {
44718
43623
  filetype: "markdown_inline",
44719
43624
  queries: {
44720
- highlights: [resolve9(dirname11(fileURLToPath8(import.meta.url)), highlights_default4)]
43625
+ highlights: [resolve11(dirname12(fileURLToPath9(import.meta.url)), highlights_default4)]
44721
43626
  },
44722
- wasm: resolve9(dirname11(fileURLToPath8(import.meta.url)), tree_sitter_markdown_inline_default)
43627
+ wasm: resolve11(dirname12(fileURLToPath9(import.meta.url)), tree_sitter_markdown_inline_default)
44723
43628
  },
44724
43629
  {
44725
43630
  filetype: "zig",
44726
43631
  queries: {
44727
- highlights: [resolve9(dirname11(fileURLToPath8(import.meta.url)), highlights_default5)]
43632
+ highlights: [resolve11(dirname12(fileURLToPath9(import.meta.url)), highlights_default5)]
44728
43633
  },
44729
- wasm: resolve9(dirname11(fileURLToPath8(import.meta.url)), tree_sitter_zig_default)
43634
+ wasm: resolve11(dirname12(fileURLToPath9(import.meta.url)), tree_sitter_zig_default)
44730
43635
  }
44731
43636
  ];
44732
43637
  }
@@ -44739,7 +43644,7 @@ function getBunfsRootPath() {
44739
43644
  return process.platform === "win32" ? "B:\\~BUN\\root" : "/$bunfs/root";
44740
43645
  }
44741
43646
  function normalizeBunfsPath(fileName) {
44742
- return join25(getBunfsRootPath(), basename9(fileName));
43647
+ return join26(getBunfsRootPath(), basename9(fileName));
44743
43648
  }
44744
43649
  function addDefaultParsers(parsers) {
44745
43650
  for (const parser of parsers) {
@@ -55661,7 +54566,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
55661
54566
  worker_path = this.options.workerPath;
55662
54567
  } else {
55663
54568
  worker_path = new URL("./parser.worker.js", import.meta.url).href;
55664
- if (!existsSync17(resolve22(import.meta.dirname, "parser.worker.js"))) {
54569
+ if (!existsSync18(resolve22(import.meta.dirname, "parser.worker.js"))) {
55665
54570
  worker_path = new URL("./parser.worker.ts", import.meta.url).href;
55666
54571
  }
55667
54572
  }
@@ -73962,14 +72867,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
73962
72867
  prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
73963
72868
  actScopeDepth = prevActScopeDepth;
73964
72869
  }
73965
- function recursivelyFlushAsyncActWork(returnValue, resolve11, reject) {
72870
+ function recursivelyFlushAsyncActWork(returnValue, resolve13, reject) {
73966
72871
  var queue = ReactSharedInternals.actQueue;
73967
72872
  if (queue !== null)
73968
72873
  if (queue.length !== 0)
73969
72874
  try {
73970
72875
  flushActQueue(queue);
73971
72876
  enqueueTask(function() {
73972
- return recursivelyFlushAsyncActWork(returnValue, resolve11, reject);
72877
+ return recursivelyFlushAsyncActWork(returnValue, resolve13, reject);
73973
72878
  });
73974
72879
  return;
73975
72880
  } catch (error48) {
@@ -73977,7 +72882,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
73977
72882
  }
73978
72883
  else
73979
72884
  ReactSharedInternals.actQueue = null;
73980
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve11(returnValue);
72885
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve13(returnValue);
73981
72886
  }
73982
72887
  function flushActQueue(queue) {
73983
72888
  if (!isFlushing) {
@@ -74153,14 +73058,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
74153
73058
  didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = true, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
74154
73059
  });
74155
73060
  return {
74156
- then: function(resolve11, reject) {
73061
+ then: function(resolve13, reject) {
74157
73062
  didAwaitActCall = true;
74158
73063
  thenable.then(function(returnValue) {
74159
73064
  popActScope(prevActQueue, prevActScopeDepth);
74160
73065
  if (prevActScopeDepth === 0) {
74161
73066
  try {
74162
73067
  flushActQueue(queue), enqueueTask(function() {
74163
- return recursivelyFlushAsyncActWork(returnValue, resolve11, reject);
73068
+ return recursivelyFlushAsyncActWork(returnValue, resolve13, reject);
74164
73069
  });
74165
73070
  } catch (error$0) {
74166
73071
  ReactSharedInternals.thrownErrors.push(error$0);
@@ -74171,7 +73076,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
74171
73076
  reject(_thrownError);
74172
73077
  }
74173
73078
  } else
74174
- resolve11(returnValue);
73079
+ resolve13(returnValue);
74175
73080
  }, function(error48) {
74176
73081
  popActScope(prevActQueue, prevActScopeDepth);
74177
73082
  0 < ReactSharedInternals.thrownErrors.length ? (error48 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(error48)) : reject(error48);
@@ -74187,11 +73092,11 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
74187
73092
  if (0 < ReactSharedInternals.thrownErrors.length)
74188
73093
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
74189
73094
  return {
74190
- then: function(resolve11, reject) {
73095
+ then: function(resolve13, reject) {
74191
73096
  didAwaitActCall = true;
74192
73097
  prevActScopeDepth === 0 ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
74193
- return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve11, reject);
74194
- })) : resolve11(returnValue$jscomp$0);
73098
+ return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve13, reject);
73099
+ })) : resolve13(returnValue$jscomp$0);
74195
73100
  }
74196
73101
  };
74197
73102
  };
@@ -76672,8 +75577,8 @@ It can also happen if the client has a browser extension installed which messes
76672
75577
  currentEntangledActionThenable = {
76673
75578
  status: "pending",
76674
75579
  value: undefined,
76675
- then: function(resolve11) {
76676
- entangledListeners.push(resolve11);
75580
+ then: function(resolve13) {
75581
+ entangledListeners.push(resolve13);
76677
75582
  }
76678
75583
  };
76679
75584
  }
@@ -76697,8 +75602,8 @@ It can also happen if the client has a browser extension installed which messes
76697
75602
  status: "pending",
76698
75603
  value: null,
76699
75604
  reason: null,
76700
- then: function(resolve11) {
76701
- listeners.push(resolve11);
75605
+ then: function(resolve13) {
75606
+ listeners.push(resolve13);
76702
75607
  }
76703
75608
  };
76704
75609
  thenable.then(function() {
@@ -102680,14 +101585,14 @@ async function runScoutMonitorApp(options) {
102680
101585
  }
102681
101586
  const renderer = await createCliRenderer();
102682
101587
  let closed = false;
102683
- await new Promise((resolve11) => {
101588
+ await new Promise((resolve13) => {
102684
101589
  const close = () => {
102685
101590
  if (closed) {
102686
101591
  return;
102687
101592
  }
102688
101593
  closed = true;
102689
101594
  renderer.destroy();
102690
- resolve11();
101595
+ resolve13();
102691
101596
  };
102692
101597
  createRoot(renderer).render(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ScoutMonitorApp, {
102693
101598
  currentDirectory: options.currentDirectory,
@@ -102735,8 +101640,8 @@ var exports_up = {};
102735
101640
  __export(exports_up, {
102736
101641
  runUpCommand: () => runUpCommand
102737
101642
  });
102738
- import { existsSync as existsSync18 } from "fs";
102739
- import { resolve as resolve11 } from "path";
101643
+ import { existsSync as existsSync19 } from "fs";
101644
+ import { resolve as resolve13 } from "path";
102740
101645
  function looksLikePath(value) {
102741
101646
  return value.includes("/") || value.startsWith(".") || value.startsWith("~");
102742
101647
  }
@@ -102784,8 +101689,8 @@ async function runUpCommand(context, args) {
102784
101689
  throw new ScoutCliError("usage: scout up <name|path> [--name <alias>] [--harness <claude|codex>]");
102785
101690
  }
102786
101691
  let projectPath;
102787
- if (looksLikePath(target) || existsSync18(resolve11(target))) {
102788
- projectPath = resolve11(target);
101692
+ if (looksLikePath(target) || existsSync19(resolve13(target))) {
101693
+ projectPath = resolve13(target);
102789
101694
  } else {
102790
101695
  const resolved = await resolveLocalAgentByName(target);
102791
101696
  if (!resolved) {
@@ -102823,10 +101728,9 @@ async function runWatchCommand(context, args) {
102823
101728
  process.on("SIGINT", shutdown);
102824
101729
  try {
102825
101730
  if (context.output.mode === "plain") {
102826
- context.stdout(`Watching ${options.channel?.trim() || "shared"} as ${resolveScoutAgentName(options.agentName)}`);
101731
+ context.stdout(`Watching ${options.channel?.trim() || "shared"}`);
102827
101732
  }
102828
101733
  await watchScoutMessages({
102829
- agentId: resolveScoutAgentName(options.agentName),
102830
101734
  channel: options.channel,
102831
101735
  signal: controller.signal,
102832
101736
  onMessage(message) {
@@ -102861,6 +101765,49 @@ var init_who = __esm(async () => {
102861
101765
  await init_service();
102862
101766
  });
102863
101767
 
101768
+ // ../../apps/desktop/src/cli/commands/whoami.ts
101769
+ var exports_whoami = {};
101770
+ __export(exports_whoami, {
101771
+ runWhoAmICommand: () => runWhoAmICommand
101772
+ });
101773
+ async function loadScoutWhoAmIReport(context, currentDirectory) {
101774
+ const defaultSenderId = await resolveScoutSenderId(null, currentDirectory, context.env);
101775
+ const projectRoot = await findNearestProjectRoot(currentDirectory);
101776
+ return {
101777
+ defaultSenderId,
101778
+ envAgent: context.env.OPENSCOUT_AGENT?.trim() || null,
101779
+ currentDirectory,
101780
+ projectRoot,
101781
+ brokerUrl: resolveScoutBrokerUrl()
101782
+ };
101783
+ }
101784
+ function renderScoutWhoAmIReport(report) {
101785
+ const lines = [
101786
+ `Default Sender: ${report.defaultSenderId}`,
101787
+ `Current Directory: ${report.currentDirectory}`,
101788
+ `Broker: ${report.brokerUrl}`
101789
+ ];
101790
+ if (report.projectRoot) {
101791
+ lines.splice(3, 0, `Project Root: ${report.projectRoot}`);
101792
+ }
101793
+ if (report.envAgent) {
101794
+ lines.splice(2, 0, `OPENSCOUT_AGENT: ${report.envAgent}`);
101795
+ }
101796
+ return lines.join(`
101797
+ `);
101798
+ }
101799
+ async function runWhoAmICommand(context, args) {
101800
+ const options = parseContextRootCommandOptions("whoami", args, defaultScoutContextDirectory(context));
101801
+ const report = await loadScoutWhoAmIReport(context, options.currentDirectory);
101802
+ context.output.writeValue(report, renderScoutWhoAmIReport);
101803
+ }
101804
+ var init_whoami = __esm(async () => {
101805
+ init_setup();
101806
+ init_context();
101807
+ init_options();
101808
+ await init_service();
101809
+ });
101810
+
102864
101811
  // ../../apps/desktop/src/cli/argv.ts
102865
101812
  function parseScoutArgv(argv) {
102866
101813
  let command = null;
@@ -102920,6 +101867,10 @@ async function loadScoutCommandHandler(name) {
102920
101867
  return (await init_enroll().then(() => exports_enroll)).runEnrollCommand;
102921
101868
  case "env":
102922
101869
  return (await init_env().then(() => exports_env)).runEnvCommand;
101870
+ case "latest":
101871
+ return (await init_latest().then(() => exports_latest)).runLatestCommand;
101872
+ case "menu":
101873
+ return (await Promise.resolve().then(() => (init_menu(), exports_menu))).runMenuCommand;
102923
101874
  case "mesh":
102924
101875
  return (await init_mesh().then(() => exports_mesh)).runMeshCommand;
102925
101876
  case "pair":
@@ -102935,7 +101886,7 @@ async function loadScoutCommandHandler(name) {
102935
101886
  case "server":
102936
101887
  return (await Promise.resolve().then(() => (init_server2(), exports_server))).runServerCommand;
102937
101888
  case "setup":
102938
- return (await Promise.resolve().then(() => (init_setup2(), exports_setup2))).runSetupCommand;
101889
+ return (await Promise.resolve().then(() => (init_setup2(), exports_setup))).runSetupCommand;
102939
101890
  case "speak":
102940
101891
  return (await init_speak().then(() => exports_speak)).runSpeakCommand;
102941
101892
  case "tui":
@@ -102946,6 +101897,8 @@ async function loadScoutCommandHandler(name) {
102946
101897
  return (await init_watch().then(() => exports_watch)).runWatchCommand;
102947
101898
  case "who":
102948
101899
  return (await init_who().then(() => exports_who)).runWhoCommand;
101900
+ case "whoami":
101901
+ return (await init_whoami().then(() => exports_whoami)).runWhoAmICommand;
102949
101902
  }
102950
101903
  }
102951
101904
 
@@ -102957,18 +101910,21 @@ var SCOUT_COMMANDS = [
102957
101910
  { name: "doctor", summary: "Show broker health and project inventory" },
102958
101911
  { name: "runtimes", summary: "Show harness catalog and readiness" },
102959
101912
  { name: "env", summary: "Show executable and agent identity context" },
101913
+ { name: "whoami", summary: "Show your current Scout identity defaults" },
102960
101914
  { name: "send", summary: "Post a broker-backed message" },
102961
101915
  { name: "speak", summary: "Send and speak aloud via TTS" },
102962
101916
  { name: "ask", summary: "Ask an agent and wait for the answer" },
102963
101917
  { name: "card", summary: "Create a dedicated relay agent card" },
102964
101918
  { name: "watch", summary: "Stream broker messages" },
102965
101919
  { name: "who", summary: "List agents and last activity" },
101920
+ { name: "latest", summary: "Show the latest Scout activity" },
102966
101921
  { name: "enroll", summary: "Generate enrollment prompt" },
102967
101922
  { name: "broadcast", summary: "Send to all routable agents" },
102968
101923
  { name: "up", summary: "Spawn a local agent for a project" },
102969
101924
  { name: "down", summary: "Stop one or all local agents" },
102970
101925
  { name: "ps", summary: "List configured local agents" },
102971
101926
  { name: "restart", summary: "Restart configured local agents" },
101927
+ { name: "menu", summary: "Launch the OpenScout macOS menu bar app" },
102972
101928
  { name: "config", summary: "View or set user config (name, etc.)" },
102973
101929
  { name: "mesh", summary: "Mesh status and diagnostics" },
102974
101930
  { name: "pair", summary: "Pair a companion device via QR" },
@@ -103021,6 +101977,13 @@ function renderScoutHelp(version2 = "0.2.18") {
103021
101977
  " scout hey @agent can you review this?",
103022
101978
  " scout @agent.harness:codex use the Codex-backed one",
103023
101979
  "",
101980
+ "Operator loop:",
101981
+ " scout whoami",
101982
+ " scout who",
101983
+ " scout latest",
101984
+ " scout menu",
101985
+ " scout server open",
101986
+ "",
103024
101987
  "Addressing:",
103025
101988
  " @name short form; requires exactly one live match",
103026
101989
  " @name.harness:<codex|claude|...> pin a specific harness (alias: runtime:)",