@openscout/scout 0.2.95 → 0.2.97

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.
Files changed (363) hide show
  1. package/bin/scout +26 -10
  2. package/bin/scoutd +0 -0
  3. package/dist/build-manifest.json +3 -3
  4. package/dist/client/assets/{ActivityScreen--eANwuxa.js → ActivityScreen-y-5DLleY.js} +1 -1
  5. package/dist/client/assets/{AddProjectForm-CcD6qvV8.js → AddProjectForm-r_1tVuWk.js} +1 -1
  6. package/dist/client/assets/{AgentAvatar-CYhf6twS.js → AgentAvatar-B7c0egdP.js} +1 -1
  7. package/dist/client/assets/{AgentConfigurationScreen-BTI24HQX.js → AgentConfigurationScreen-BD7Unt2P.js} +1 -1
  8. package/dist/client/assets/{AgentDefinitionsWorkspace-srmN6-vj.js → AgentDefinitionsWorkspace-BbcZc9Xk.js} +1 -1
  9. package/dist/client/assets/{AgentDetailCard-UqPNpcvB.js → AgentDetailCard-B_KLcgsP.js} +1 -1
  10. package/dist/client/assets/{AgentFloorView-XXNgI5Cj.js → AgentFloorView-DMFiHs21.js} +1 -1
  11. package/dist/client/assets/{AgentInfoScreen-CimdOnZ9.js → AgentInfoScreen-DHGLRddI.js} +1 -1
  12. package/dist/client/assets/{AgentLaneCard-Z-aOBR3W.js → AgentLaneCard-ajZvcfdW.js} +1 -1
  13. package/dist/client/assets/{AgentLaneChrome-CyhA1reW.js → AgentLaneChrome-CTp39syR.js} +1 -1
  14. package/dist/client/assets/{AgentLaneDetailSheet-BNzjMjIH.js → AgentLaneDetailSheet-DRc-TpMB.js} +1 -1
  15. package/dist/client/assets/{AgentLaneSummaryResize-Box8-_rS.js → AgentLaneSummaryResize-VMNDo7kD.js} +1 -1
  16. package/dist/client/assets/AgentLanesEmbedScreen-C5k_dDS8.js +1 -0
  17. package/dist/client/assets/AgentLanesPreflight-CFadSaia.js +1 -0
  18. package/dist/client/assets/AgentLanesView-u5vpLwQf.js +2 -0
  19. package/dist/client/assets/{AgentLiveActions-BwuzOE8h.js → AgentLiveActions-CsQA3pu_.js} +1 -1
  20. package/dist/client/assets/AgentMasterScreen-C5WlWNmk.js +1 -0
  21. package/dist/client/assets/{AgentSessions-CsZwoukw.js → AgentSessions-BePq5Lgc.js} +1 -1
  22. package/dist/client/assets/{AgentsConsole-KCao4XmX.js → AgentsConsole-BuiZ_bDo.js} +1 -1
  23. package/dist/client/assets/{AgentsScreen-CtPAmwRX.js → AgentsScreen-CRi-hG2k.js} +1 -1
  24. package/dist/client/assets/AgentsSubnav-DUsya4WK.js +1 -0
  25. package/dist/client/assets/{AstronautSuit-Ckx7lu7G.js → AstronautSuit-Cjjcv6yQ.js} +1 -1
  26. package/dist/client/assets/{AtopView-DYlpgEHf.js → AtopView-PxWQd-sX.js} +1 -1
  27. package/dist/client/assets/{BackToPicker-4JaeRvDm.js → BackToPicker-Cto0Ioz9.js} +1 -1
  28. package/dist/client/assets/{BriefingDetailScreen-D6-xqrpo.js → BriefingDetailScreen-C29smC5d.js} +1 -1
  29. package/dist/client/assets/{BriefingsScreen-DKxSDEmk.js → BriefingsScreen-DNyFBrPn.js} +1 -1
  30. package/dist/client/assets/{BrokerMetadataPanel-BbREmsbK.js → BrokerMetadataPanel-DbaCQlhw.js} +1 -1
  31. package/dist/client/assets/{BrokerScreen-fbPIouMw.js → BrokerScreen-CS0s4Gm4.js} +1 -1
  32. package/dist/client/assets/{CastPicker-BcA2vDDV.js → CastPicker-CTBHjjGw.js} +1 -1
  33. package/dist/client/assets/ChatCollapsedStrip-D4rc7wBQ.js +1 -0
  34. package/dist/client/assets/{CodeDiffPane-Cn8Y9hAt.js → CodeDiffPane-QvxCUzTi.js} +1 -1
  35. package/dist/client/assets/{CodeProjectPicker-nPFktrco.js → CodeProjectPicker-DplkpM7f.js} +1 -1
  36. package/dist/client/assets/{CodeScreen-BlAxtxE5.js → CodeScreen-CrR2JaVj.js} +1 -1
  37. package/dist/client/assets/{ConversationComposer-2HjbGPL9.js → ConversationComposer-H8_hsYSc.js} +1 -1
  38. package/dist/client/assets/{ConversationFeedRows-C6wUqJrx.js → ConversationFeedRows-hlEFlURs.js} +1 -1
  39. package/dist/client/assets/{ConversationHeader-C8vfXaTF.js → ConversationHeader-B0ZBhE_E.js} +1 -1
  40. package/dist/client/assets/{ConversationPanels-CNCIbpqU.js → ConversationPanels-fMo6Uy1v.js} +2 -2
  41. package/dist/client/assets/ConversationScreen-CXAHQwQ3.js +2 -0
  42. package/dist/client/assets/{ConversationStatus-DZuyKsWN.js → ConversationStatus-DI5GCiyW.js} +1 -1
  43. package/dist/client/assets/{CrewAvatar-CTIuxp4G.js → CrewAvatar-CXJHLNVM.js} +1 -1
  44. package/dist/client/assets/CrewWorkspaces-Bz2Dcnt4.js +1 -0
  45. package/dist/client/assets/CrewWorkspaces-Diq11KDq.css +1 -0
  46. package/dist/client/assets/{DataTable-6IDhanr5.js → DataTable-DXIlOlVu.js} +1 -1
  47. package/dist/client/assets/{DispatchAftermath-DC1u9itO.js → DispatchAftermath-CrELgl2e.js} +1 -1
  48. package/dist/client/assets/{DocumentFocusViewer-DZVhS7as.js → DocumentFocusViewer-DFFE85Tq.js} +1 -1
  49. package/dist/client/assets/{EmbeddableSurfacesLab-BKUFJMCV.js → EmbeddableSurfacesLab-DgpewB3y.js} +1 -1
  50. package/dist/client/assets/{EmptyState-CeBl0iGd.js → EmptyState-D9X16fKq.js} +1 -1
  51. package/dist/client/assets/{FileViewerPane-DQlpNt2S.js → FileViewerPane-CY0RN78e.js} +1 -1
  52. package/dist/client/assets/FlightObserveScreen-qyzHzxLa.js +1 -0
  53. package/dist/client/assets/{FollowScreen-BXHOp3gb.js → FollowScreen-wZ-z67mX.js} +1 -1
  54. package/dist/client/assets/{HarnessMark-D5CA5uSp.js → HarnessMark-DAaWPiT_.js} +1 -1
  55. package/dist/client/assets/{HarnessesScreen-DhFXq1qz.js → HarnessesScreen-DbKkQM4o.js} +1 -1
  56. package/dist/client/assets/{HerdrSession-BRhs5LIR.js → HerdrSession-CYKRfBad.js} +1 -1
  57. package/dist/client/assets/{HomeHero-DMABJuze.js → HomeHero-DYyd3cpz.js} +1 -1
  58. package/dist/client/assets/{HostAdvisorView-RAociATV.js → HostAdvisorView-NtM0bsg8.js} +1 -1
  59. package/dist/client/assets/{KnowledgeSearchInspector-CZPyVNSw.js → KnowledgeSearchInspector-CU6BCFcS.js} +1 -1
  60. package/dist/client/assets/{KnowledgeSearchScreen-mGTKIg00.js → KnowledgeSearchScreen-DvbRRPQB.js} +1 -1
  61. package/dist/client/assets/{LaneToolHoverCard-D_DMlBeT.js → LaneToolHoverCard-Bo2Zl-1a.js} +1 -1
  62. package/dist/client/assets/{LaneTraceDetailSheet-BEHg3LmH.js → LaneTraceDetailSheet-C3C8UjXv.js} +1 -1
  63. package/dist/client/assets/{LaneTraceEventFocus-DVimcizS.js → LaneTraceEventFocus-Cek_64zR.js} +1 -1
  64. package/dist/client/assets/{MeshCanvas-BOuZx4AM.js → MeshCanvas-TvsID4fe.js} +1 -1
  65. package/dist/client/assets/{MeshCanvasMinimap-wL-1N94e.js → MeshCanvasMinimap-CxqeVVco.js} +1 -1
  66. package/dist/client/assets/{MeshScreen-DMY9Bmh5.js → MeshScreen-BJ-j9YXv.js} +1 -1
  67. package/dist/client/assets/{MeshTopologyView-DiDcX5f4.js → MeshTopologyView-BjprutE8.js} +2 -2
  68. package/dist/client/assets/MessagesScreen-CyNIZ3-X.js +1 -0
  69. package/dist/client/assets/{MissionContextRail-DrNKXG5u.js → MissionContextRail-Be0uXljQ.js} +1 -1
  70. package/dist/client/assets/{MissionControlView-ByBlHVN8.js → MissionControlView-Bj7cAbZd.js} +1 -1
  71. package/dist/client/assets/{MissionFocusOverlay-D1i6R6Zt.js → MissionFocusOverlay-DvTw6ts5.js} +1 -1
  72. package/dist/client/assets/{MissionLeftAgentRow-DN9NcCML.js → MissionLeftAgentRow-B0QG7Gys.js} +1 -1
  73. package/dist/client/assets/{MissionLogPane-DSeIv3Xo.js → MissionLogPane-EsM_S3ro.js} +1 -1
  74. package/dist/client/assets/{NewAgentModal-V7yK6J6j.js → NewAgentModal-Cqiq1CE1.js} +1 -1
  75. package/dist/client/assets/NewChatComposer-C_1op4dH.js +19 -0
  76. package/dist/client/assets/{NewChatOrigin-DdpzE4kz.js → NewChatOrigin-D7hwp6dm.js} +1 -1
  77. package/dist/client/assets/{ObserveEmbedScreen-l_Q3ts2q.js → ObserveEmbedScreen-CGHI_hb5.js} +1 -1
  78. package/dist/client/assets/{ObserveTextFlow-BSEtYTxU.js → ObserveTextFlow-BcrGQet8.js} +2 -2
  79. package/dist/client/assets/{ObservedTopologyPanel-CdGUXxGR.js → ObservedTopologyPanel-BhVsvwkh.js} +1 -1
  80. package/dist/client/assets/{OpenScoutAppShell-DN8dOSK4.js → OpenScoutAppShell-BtfvMkAp.js} +25 -25
  81. package/dist/client/assets/{OpsAgentsView-ntGr4pz4.js → OpsAgentsView-DBYtmLnh.js} +1 -1
  82. package/dist/client/assets/OpsScreen-CuYVgkC_.js +1 -0
  83. package/dist/client/assets/OpsSubnav-BEU4kKXm.js +1 -0
  84. package/dist/client/assets/ProjectAgentProfile-BhXAZEkW.js +1 -0
  85. package/dist/client/assets/{ProjectAgentProfileBar-BQET-qrY.js → ProjectAgentProfileBar-uJw9Wdfk.js} +1 -1
  86. package/dist/client/assets/{ProjectAgentProfileHero-Ck8mThtx.js → ProjectAgentProfileHero-CIMS4Dy_.js} +1 -1
  87. package/dist/client/assets/ProjectsInbox-JgU0lrY8.js +2 -0
  88. package/dist/client/assets/ProjectsRail-p99BRBOa.js +1 -0
  89. package/dist/client/assets/ProjectsScreen-BB45DU53.js +1 -0
  90. package/dist/client/assets/ProjectsThreadAside-BHDTJWOz.js +1 -0
  91. package/dist/client/assets/{Provider-B2xuo3rf.js → Provider-Bu-C8Hxc.js} +1 -1
  92. package/dist/client/assets/{RailRow-B-GycCBx.js → RailRow-ClMmtaIE.js} +1 -1
  93. package/dist/client/assets/{RawSessionsTable-AQThi6sm.js → RawSessionsTable-BtKdQPiv.js} +1 -1
  94. package/dist/client/assets/RealtimeVoiceScreen-KPHJgO1l.js +1 -0
  95. package/dist/client/assets/{RepoDiffEmbedScreen-DXWoRF_c.js → RepoDiffEmbedScreen-CjZ5ew3y.js} +1 -1
  96. package/dist/client/assets/{RepoDiffPageScreen-D_274ExO.js → RepoDiffPageScreen-CdTFfmbk.js} +1 -1
  97. package/dist/client/assets/{RepoDiffViewer-DbedmHDX.js → RepoDiffViewer-BFperHQg.js} +1 -1
  98. package/dist/client/assets/{RepoDiffViewerLazy-C0--fPtk.js → RepoDiffViewerLazy-BkBmbi1Z.js} +2 -2
  99. package/dist/client/assets/{ReposScreen-D39_YG4H.js → ReposScreen-C8dT4bK0.js} +1 -1
  100. package/dist/client/assets/ScoutDeckSurface-BcJsm2Kq.js +1 -0
  101. package/dist/client/assets/{ScoutSurface-UsRy0t-I.js → ScoutSurface-DmzEtrx8.js} +1 -1
  102. package/dist/client/assets/{ScoutbotFxLab-Fmdfboxs.js → ScoutbotFxLab-DU15Plja.js} +1 -1
  103. package/dist/client/assets/{SecondaryNav-o2RL9H8H.js → SecondaryNav-BrXMtvKE.js} +1 -1
  104. package/dist/client/assets/SessionEmbedScreen-Bt4flmAx.js +1 -0
  105. package/dist/client/assets/{SessionObserve-Dsd0j05N.js → SessionObserve-BCehTlO8.js} +1 -1
  106. package/dist/client/assets/{SessionObserveEvidence-DopQYDkD.js → SessionObserveEvidence-Bb6U-dem.js} +1 -1
  107. package/dist/client/assets/SessionRefScreen-C8BiRVPx.js +1 -0
  108. package/dist/client/assets/SessionsScreen-BNvjUflN.js +1 -0
  109. package/dist/client/assets/{SettingsDrawer-_AwuopMS.js → SettingsDrawer-BieUmb8g.js} +1 -1
  110. package/dist/client/assets/{SettingsScreen-Cw4DDfJP.js → SettingsScreen-3JvX1WFL.js} +1 -1
  111. package/dist/client/assets/{ShikiPane-BfHc_3pM.js → ShikiPane-DPWsVNtn.js} +1 -1
  112. package/dist/client/assets/{SlidePanel-C-q7XwTa.js → SlidePanel-BMRIR6bH.js} +1 -1
  113. package/dist/client/assets/{SpriteAvatar-Bs68ZENy.js → SpriteAvatar-56DTXxSV.js} +1 -1
  114. package/dist/client/assets/{StatusPill-ZZQwxB1a.js → StatusPill-BcuYUgsD.js} +1 -1
  115. package/dist/client/assets/{TailView-Q4FVMsUH.js → TailView-BDy8c2VB.js} +1 -1
  116. package/dist/client/assets/{Terminal-D7zqHwHV.js → Terminal-BlUQY9_Z.js} +1 -1
  117. package/dist/client/assets/{TerminalEmbedScreen-CXIJ7qv6.js → TerminalEmbedScreen-DTjxktyr.js} +1 -1
  118. package/dist/client/assets/{TerminalHeaderMount-BnBLOPbb.js → TerminalHeaderMount-Cy-uxkey.js} +1 -1
  119. package/dist/client/assets/{TextDocumentSurface-CKQP-cBT.js → TextDocumentSurface-BeSwe3Yl.js} +1 -1
  120. package/dist/client/assets/{TmuxPeek-V9dGESfO.js → TmuxPeek-B4no_aGI.js} +1 -1
  121. package/dist/client/assets/{VoicePermissionsPanel-k7sbLXx2.js → VoicePermissionsPanel-Bd16scwz.js} +1 -1
  122. package/dist/client/assets/{WorkDetailScreen-60p9litn.js → WorkDetailScreen-DIFQufak.js} +1 -1
  123. package/dist/client/assets/{WorkFilesViewer-BjJS7GyM.js → WorkFilesViewer-CT5XEFGf.js} +1 -1
  124. package/dist/client/assets/{activity-Bj3Hnbj3.js → activity-CqZafWfx.js} +1 -1
  125. package/dist/client/assets/{addon-fit-BL-NTWRU.js → addon-fit-CM6F_JRD.js} +1 -1
  126. package/dist/client/assets/{addon-webgl-CIMgmNjg.js → addon-webgl-DRTiePFC.js} +1 -1
  127. package/dist/client/assets/{agent-autocomplete-DXlvdc4b.js → agent-autocomplete-DXUBnlkN.js} +1 -1
  128. package/dist/client/assets/{agent-essentials-BOMHR0_U.js → agent-essentials-vytdybaX.js} +1 -1
  129. package/dist/client/assets/{agent-lane-card-model-CTLxf3R4.js → agent-lane-card-model-CNZMPkhc.js} +1 -1
  130. package/dist/client/assets/{agent-lanes-model-AfrjRNK5.js → agent-lanes-model-DXqjs0OK.js} +2 -2
  131. package/dist/client/assets/agent-master-I1W-5d-x.css +1 -0
  132. package/dist/client/assets/agent-master-model-DAZL92oe.js +1 -0
  133. package/dist/client/assets/{agent-specialization-BIPIBW8u.js → agent-specialization-BtxLN4UR.js} +1 -1
  134. package/dist/client/assets/agents-project-DgbqDc65.css +1 -0
  135. package/dist/client/assets/{arc.es-IR5r6sj_.js → arc.es-Bh84oHJ7.js} +1 -1
  136. package/dist/client/assets/{arrow-down-BydciYFZ.js → arrow-down-DvTsLokL.js} +1 -1
  137. package/dist/client/assets/{arrow-left-CghA0kTw.js → arrow-left-DHfdXJVZ.js} +1 -1
  138. package/dist/client/assets/{arrow-right-iJFv3jNh.js → arrow-right-BBsrogWf.js} +1 -1
  139. package/dist/client/assets/{arrow-up-right-CCfZXPqD.js → arrow-up-right-CY0Vw_67.js} +1 -1
  140. package/dist/client/assets/{bot-B5sub1-i.js → bot-DzkOUFBb.js} +1 -1
  141. package/dist/client/assets/{check-C18YQC90.js → check-DbIkldnK.js} +1 -1
  142. package/dist/client/assets/{chevron-down-Mp9mu_yd.js → chevron-down-C475vltS.js} +1 -1
  143. package/dist/client/assets/{chevron-right-CzH-TaSF.js → chevron-right-CnZRSd-V.js} +1 -1
  144. package/dist/client/assets/{chunk-HRAB3GC3-swV8PA2k.js → chunk-HRAB3GC3-DLNT8aOo.js} +1 -1
  145. package/dist/client/assets/{chunk-JTPDIGGW-BKXqcLKw.js → chunk-JTPDIGGW-Ci5j4vzV.js} +3 -3
  146. package/dist/client/assets/{chunk-T65CQEWV-vYbNJ8E6.js → chunk-T65CQEWV-BtNvicWs.js} +1 -1
  147. package/dist/client/assets/{chunk-YOZR3KYS-U65yUmqt.js → chunk-YOZR3KYS-DN5EJulq.js} +1 -1
  148. package/dist/client/assets/{clock-B4PTFi5m.js → clock-DVvENUU5.js} +1 -1
  149. package/dist/client/assets/{code-xml-Jtxsp5d1.js → code-xml-CvKBY3KA.js} +1 -1
  150. package/dist/client/assets/{compass-0_54-vz3.js → compass-BcRI_YXo.js} +1 -1
  151. package/dist/client/assets/content--T3p6akp.js +1 -0
  152. package/dist/client/assets/content-4nFMLuzF.js +1 -0
  153. package/dist/client/assets/content-BJ4nHvcu.js +1 -0
  154. package/dist/client/assets/{content-DMbawvqU.js → content-BNyAjpUl.js} +1 -1
  155. package/dist/client/assets/content-BmGp9wNl.js +1 -0
  156. package/dist/client/assets/content-CBWhwaRo.js +1 -0
  157. package/dist/client/assets/content-CQlykFbe.js +1 -0
  158. package/dist/client/assets/content-CevL0DgF.js +1 -0
  159. package/dist/client/assets/content-Ch8pf2bj.js +1 -0
  160. package/dist/client/assets/content-Cpvdcm_l.js +1 -0
  161. package/dist/client/assets/content-DfS3vgAu.js +1 -0
  162. package/dist/client/assets/content-DpQdNePJ.js +1 -0
  163. package/dist/client/assets/content-Dqpibejl.js +1 -0
  164. package/dist/client/assets/content-TV0nd6-r.js +1 -0
  165. package/dist/client/assets/{content-CHghn-rT.js → content-odbb5H4U.js} +1 -1
  166. package/dist/client/assets/content-uXHLPHe0.js +1 -0
  167. package/dist/client/assets/{conversation-icons-BV10B2Me.js → conversation-icons-CBMKBGHd.js} +1 -1
  168. package/dist/client/assets/{conversation-model-CwF6Y9gx.js → conversation-model-QsJVfSeG.js} +1 -1
  169. package/dist/client/assets/conversation-motion-features-BFLHX2eg.js +1 -0
  170. package/dist/client/assets/{copy-Dtpx4FOE.js → copy-Cj-owMb-.js} +1 -1
  171. package/dist/client/assets/{cpu-BjVis9mc.js → cpu-DuD3RFX5.js} +1 -1
  172. package/dist/client/assets/{createLucideIcon-DXpqZP7I.js → createLucideIcon-DthwWQIg.js} +1 -1
  173. package/dist/client/assets/{ctx-panel-D_HJ3nhb.css → ctx-panel-F05jXK4Q.css} +1 -1
  174. package/dist/client/assets/discover-B3uQlh5L.js +2 -0
  175. package/dist/client/assets/embed-entry-DkRcHKTh.js +2 -0
  176. package/dist/client/assets/{external-link-Ckiyzjwo.js → external-link-C_nxk0MJ.js} +1 -1
  177. package/dist/client/assets/{eye-CpfyvdLq.js → eye-FnaaPfja.js} +1 -1
  178. package/dist/client/assets/{file-code-corner-7mo7EviR.js → file-code-corner-DDPq4Cur.js} +1 -1
  179. package/dist/client/assets/{file-text-KijGBnME.js → file-text--hfhwZH1.js} +1 -1
  180. package/dist/client/assets/{gauge-y-Xg8Mrv.js → gauge-BVEknshP.js} +1 -1
  181. package/dist/client/assets/{gestures-Bq9XnSqY.js → gestures-3gw5VgUS.js} +1 -1
  182. package/dist/client/assets/{git-branch-BQ_x8KZL.js → git-branch-DJyvr0k-.js} +1 -1
  183. package/dist/client/assets/{home-moving-signal-DtRpiNa0.js → home-moving-signal-BPqBoNSm.js} +1 -1
  184. package/dist/client/assets/{home-now-card-DFb4GOgM.js → home-now-card-UspVW2-2.js} +1 -1
  185. package/dist/client/assets/{home-now-card-nano-D--m9jpq.js → home-now-card-nano-G8kMB32m.js} +1 -1
  186. package/dist/client/assets/{hooks-9d1SoaQA.js → hooks-BMsQzo9j.js} +1 -1
  187. package/dist/client/assets/{index-CZ-05hKz.js → index-BI9AljSD.js} +5 -5
  188. package/dist/client/assets/index-C-v0Z6ZS.js +1 -0
  189. package/dist/client/assets/index-D9DzUORB.js +1 -0
  190. package/dist/client/assets/index-DfsTyYDx.js +1 -0
  191. package/dist/client/assets/{inspector-panel-C-EhEDYy.js → inspector-panel-CFSArQ8Y.js} +1 -1
  192. package/dist/client/assets/{keyboard-nav-DFhAJkW3.js → keyboard-nav-e6yGeorb.js} +1 -1
  193. package/dist/client/assets/{lane-roster-store-gUBsydNf.js → lane-roster-store-Q7pcblY5.js} +1 -1
  194. package/dist/client/assets/{layers-CGou-LwV.js → layers-Dz2dKOnU.js} +1 -1
  195. package/dist/client/assets/{layout-grid-BrZt31QH.js → layout-grid-Bvs5fdB4.js} +1 -1
  196. package/dist/client/assets/{left-CwHZCGO3.js → left-B4Ak-Ibt.js} +1 -1
  197. package/dist/client/assets/{left-DA0amyfu.js → left-BMb493FZ.js} +1 -1
  198. package/dist/client/assets/{left-CwLs0okF.js → left-BjFLvVOC.js} +1 -1
  199. package/dist/client/assets/{left-B_o51JV6.js → left-CsXQ8Q3G.js} +1 -1
  200. package/dist/client/assets/left-FNH7faCI.js +1 -0
  201. package/dist/client/assets/left-POkqE8gI.js +2 -0
  202. package/dist/client/assets/left-UulOTcWh.js +1 -0
  203. package/dist/client/assets/{left-advisor-DU3s01gk.js → left-advisor-CJkQS2d6.js} +1 -1
  204. package/dist/client/assets/left-agents-CFMxyEjT.js +1 -0
  205. package/dist/client/assets/{left-default-uvuvDmt0.js → left-default-rnX5HANc.js} +1 -1
  206. package/dist/client/assets/{left-lanes-DnNLgec8.js → left-lanes-BpLXT_qH.js} +1 -1
  207. package/dist/client/assets/{left-mission-DN3-NLPC.js → left-mission-B0bhseWw.js} +1 -1
  208. package/dist/client/assets/library-D1iXfda2.js +1 -0
  209. package/dist/client/assets/{loader-circle-BiohlpM3.js → loader-circle-B2W9DPG5.js} +1 -1
  210. package/dist/client/assets/{local-agents-wUIaBcEr.js → local-agents-DKr-gIDC.js} +1 -1
  211. package/dist/client/assets/{mentions-CcPFMbmZ.js → mentions-DuLQnEcn.js} +1 -1
  212. package/dist/client/assets/{mesh-buckets-ByyFXdKT.js → mesh-buckets-6y3ZIibI.js} +1 -1
  213. package/dist/client/assets/{mesh-ops-CvGyjK84.js → mesh-ops-CRR-KnCG.js} +1 -1
  214. package/dist/client/assets/{mesh-roster-n2nDpEmA.js → mesh-roster-Bc7T4b7z.js} +1 -1
  215. package/dist/client/assets/{mesh-view-store-BpxGm71b.js → mesh-view-store-CiET-yN8.js} +1 -1
  216. package/dist/client/assets/{message-circle-B2ddzBrX.js → message-circle-B2DoYKDO.js} +1 -1
  217. package/dist/client/assets/{message-markup-RkfmDfME.js → message-markup-Cc_O-WdA.js} +1 -1
  218. package/dist/client/assets/{message-square-D9YYWE0x.js → message-square-O-Hu9Mk2.js} +1 -1
  219. package/dist/client/assets/{message-visibility-DwxgcgYK.js → message-visibility-CGOmSSKs.js} +1 -1
  220. package/dist/client/assets/{mission-control-store-Bf4b54MV.js → mission-control-store-DDAgWKUl.js} +1 -1
  221. package/dist/client/assets/{observe-CJRIvOCn.js → observe-Da2mRAsc.js} +1 -1
  222. package/dist/client/assets/{page-status-BptpTdQ-.js → page-status-Crtgjbq3.js} +1 -1
  223. package/dist/client/assets/{paperclip-B1UNGl5d.js → paperclip-BudMKSyr.js} +1 -1
  224. package/dist/client/assets/{persistent-state-YI-WHSCF.js → persistent-state-xJz-qAFc.js} +1 -1
  225. package/dist/client/assets/{plus-CuU_X0-8.js → plus-BJ1If8vj.js} +1 -1
  226. package/dist/client/assets/{profile-rY8jO38P.js → profile-B3qDSbrH.js} +1 -1
  227. package/dist/client/assets/projects-inbox-CLek3QaG.js +1 -0
  228. package/dist/client/assets/projects-inbox-DRBl2N85.css +1 -0
  229. package/dist/client/assets/{proxy-DnLpZIi9.js → proxy-1BABCaDb.js} +1 -1
  230. package/dist/client/assets/{radio-Dm1ItnA4.js → radio-B5OpHrrK.js} +1 -1
  231. package/dist/client/assets/{refresh-cw-BRn9Oxxl.js → refresh-cw-DmNAhaH0.js} +1 -1
  232. package/dist/client/assets/resolve-panes-DXw-cHaj.js +1 -0
  233. package/dist/client/assets/resolve-sidebar-context-CDdbRDEa.js +1 -0
  234. package/dist/client/assets/{right-BJltPRnd.js → right-BgJ4mQLf.js} +1 -1
  235. package/dist/client/assets/{right-DtsRNX5b.js → right-Bk6h7o-Z.js} +1 -1
  236. package/dist/client/assets/{right-C1yumXNx.js → right-BrICCmEt.js} +1 -1
  237. package/dist/client/assets/{right-BbyYdzHn.js → right-C1QIbqFZ.js} +1 -1
  238. package/dist/client/assets/{right-Shpw_xDy.js → right-CfliKiHY.js} +1 -1
  239. package/dist/client/assets/{right-D3aTKf1F.js → right-CtOLXB9s.js} +1 -1
  240. package/dist/client/assets/{right-CJnB_Bg7.js → right-D8id_DWQ.js} +1 -1
  241. package/dist/client/assets/{right-CPP7GyOm.js → right-DJ34yMAi.js} +1 -1
  242. package/dist/client/assets/{right-DI1B79nR.js → right-DiFmtw6d.js} +1 -1
  243. package/dist/client/assets/{right-Beh3nCUB.js → right-Lx79pxpG.js} +1 -1
  244. package/dist/client/assets/{rocket-DOwXHKWA.js → rocket-D3-TUJ2m.js} +1 -1
  245. package/dist/client/assets/router-DTtux5sP.js +1 -0
  246. package/dist/client/assets/{scope-views-A4_vP97W.js → scope-views-CPM0sBw_.js} +1 -1
  247. package/dist/client/assets/{scout-voice-BhPzrc6P.js → scout-voice-BZKsdquE.js} +1 -1
  248. package/dist/client/assets/{scoutbot-markdown-jh7tzFB1.js → scoutbot-markdown-CQ7Y1McC.js} +1 -1
  249. package/dist/client/assets/{scroll-text-DO0GQzvN.js → scroll-text-kPhYbh-M.js} +1 -1
  250. package/dist/client/assets/{search-D1tJSAWe.js → search-DmFM_9jm.js} +1 -1
  251. package/dist/client/assets/{secondaryNavConfig-B9IF8wXl.js → secondaryNavConfig-C9bQ9_IA.js} +1 -1
  252. package/dist/client/assets/{selection-bridge-i2S9zpf8.js → selection-bridge-Dy2mtMyi.js} +1 -1
  253. package/dist/client/assets/{send-horizontal-CgLppsIC.js → send-horizontal-qyjNgEaV.js} +1 -1
  254. package/dist/client/assets/{server-Dymv5Emh.js → server-nWqndnxu.js} +1 -1
  255. package/dist/client/assets/{session-catalog-DNaOEzoQ.js → session-catalog-BkmktBHI.js} +1 -1
  256. package/dist/client/assets/session-ref-lookup-state-SATH_jAP.js +1 -0
  257. package/dist/client/assets/{session-table-PTQVoTyc.js → session-table-DKSF0mrS.js} +1 -1
  258. package/dist/client/assets/{settings-2fC_G9nX.js → settings-C6h9y5XX.js} +1 -1
  259. package/dist/client/assets/{sliders-horizontal-BPSMHhbm.js → sliders-horizontal-BhHKxsCO.js} +1 -1
  260. package/dist/client/assets/{sparkles-Dy8p5xN8.js → sparkles-Cf8gin88.js} +1 -1
  261. package/dist/client/assets/{sse-CiOSzE5n.js → sse-DDHEHUgz.js} +1 -1
  262. package/dist/client/assets/{tail-events-BMQmSx1n.js → tail-events-BdUrO0kb.js} +1 -1
  263. package/dist/client/assets/{terminal-yN9xN-h9.js → terminal-C3YRPfyz.js} +1 -1
  264. package/dist/client/assets/{terminal-relay-9lhzEp2V.js → terminal-relay-BFhdIstF.js} +1 -1
  265. package/dist/client/assets/{terminal-takeover-DANQfTcy.js → terminal-takeover-B6H4llRR.js} +1 -1
  266. package/dist/client/assets/{text-rLpBg4_z.css → text-BYAFNLNq.css} +1 -1
  267. package/dist/client/assets/{text-CnZ0e1-V.js → text-Faa3ZStB.js} +1 -1
  268. package/dist/client/assets/{use-conversation-list-D9BNLfnW.js → use-conversation-list-DO5J8T1H.js} +1 -1
  269. package/dist/client/assets/use-fleet-active-asks-BtgNnRgK.js +1 -0
  270. package/dist/client/assets/{use-mesh-ops-items-BIvIHlqe.js → use-mesh-ops-items-N1HHhChY.js} +1 -1
  271. package/dist/client/assets/{use-reduced-motion-CKkhq9c5.js → use-reduced-motion-8WuxLa5l.js} +1 -1
  272. package/dist/client/assets/{use-scout-dev-flags-B2KCzPxn.js → use-scout-dev-flags-B31qTy6m.js} +1 -1
  273. package/dist/client/assets/{useAgentHoverCard-CHBsxcbp.js → useAgentHoverCard-Bkv-Uz_V.js} +1 -1
  274. package/dist/client/assets/useContentSecondaryNav-Bpkc8ayb.js +1 -0
  275. package/dist/client/assets/{usePierreRuntime-DUuXH5yp.js → usePierreRuntime-BfZHKAkI.js} +1 -1
  276. package/dist/client/assets/{wrench-YcXMlsh2.js → wrench-DkxnZRlR.js} +1 -1
  277. package/dist/client/assets/{x-e3h2hDsp.js → x-DfRJbqrh.js} +1 -1
  278. package/dist/client/assets/{xterm-Byp1Hep_.js → xterm-CJw8Epxv.js} +1 -1
  279. package/dist/client/assets/{zap-nMcjPoWB.js → zap-CIo8msJB.js} +1 -1
  280. package/dist/client/index.html +1 -1
  281. package/dist/drizzle/0009_old_korg.sql +72 -0
  282. package/dist/drizzle/0010_sparkling_colonel_america.sql +2 -0
  283. package/dist/drizzle/0011_funny_onslaught.sql +4 -0
  284. package/dist/drizzle/0012_high_blazing_skull.sql +16 -0
  285. package/dist/drizzle/0013_perfect_juggernaut.sql +1 -0
  286. package/dist/drizzle/0014_legal_meggan.sql +6 -0
  287. package/dist/drizzle/0015_lame_marvel_boy.sql +12 -0
  288. package/dist/drizzle/0016_early_goliath.sql +6 -0
  289. package/dist/drizzle/meta/0009_snapshot.json +6262 -0
  290. package/dist/drizzle/meta/0010_snapshot.json +6286 -0
  291. package/dist/drizzle/meta/0011_snapshot.json +6337 -0
  292. package/dist/drizzle/meta/0012_snapshot.json +6458 -0
  293. package/dist/drizzle/meta/0013_snapshot.json +6465 -0
  294. package/dist/drizzle/meta/0014_snapshot.json +6503 -0
  295. package/dist/drizzle/meta/0015_snapshot.json +6596 -0
  296. package/dist/drizzle/meta/0016_snapshot.json +6611 -0
  297. package/dist/drizzle/meta/_journal.json +56 -0
  298. package/dist/main.mjs +1355 -365
  299. package/dist/node/main.mjs +314 -12
  300. package/dist/pairing-runtime-controller.mjs +616 -108
  301. package/dist/runtime/base-daemon.mjs +30 -3
  302. package/dist/runtime/broker-daemon.mjs +4822 -532
  303. package/dist/runtime/broker-process-manager.mjs +23 -2
  304. package/dist/runtime/mesh-discover.mjs +23 -2
  305. package/dist/scout-control-plane-web.mjs +1922 -478
  306. package/package.json +2 -2
  307. package/dist/client/assets/AgentLanesEmbedScreen-BmLnN4on.js +0 -1
  308. package/dist/client/assets/AgentLanesPreflight-5B6sXvRB.js +0 -1
  309. package/dist/client/assets/AgentLanesView-DHyzVBk6.js +0 -2
  310. package/dist/client/assets/AgentsSubnav-CDMjKeYC.js +0 -1
  311. package/dist/client/assets/ChatCollapsedStrip-Ycm0ecaX.js +0 -1
  312. package/dist/client/assets/ConversationScreen-BNULZAp2.js +0 -2
  313. package/dist/client/assets/CrewWorkspaces-CJhBKVoZ.css +0 -1
  314. package/dist/client/assets/CrewWorkspaces-CkPYIA46.js +0 -1
  315. package/dist/client/assets/FlightObserveScreen-CDWGSKZg.js +0 -1
  316. package/dist/client/assets/MessagesScreen-Eulveud3.js +0 -1
  317. package/dist/client/assets/NewChatComposer-hlWT-nbR.js +0 -19
  318. package/dist/client/assets/OpsScreen-Ppen_Lmj.js +0 -1
  319. package/dist/client/assets/OpsSubnav-GAKsoSor.js +0 -1
  320. package/dist/client/assets/ProjectAgentProfile-BLlp2e6t.js +0 -1
  321. package/dist/client/assets/ProjectsInbox-Dlt8OQ8O.js +0 -2
  322. package/dist/client/assets/ProjectsRail-DCfNMmnW.js +0 -1
  323. package/dist/client/assets/ProjectsScreen-BP-hmI1m.js +0 -1
  324. package/dist/client/assets/ProjectsThreadAside-CWpMjGIH.js +0 -1
  325. package/dist/client/assets/RealtimeVoiceScreen-D7zZHK_t.js +0 -1
  326. package/dist/client/assets/ScoutDeckSurface-CKT3qdqY.js +0 -1
  327. package/dist/client/assets/SessionEmbedScreen-RD5XOx7R.js +0 -1
  328. package/dist/client/assets/SessionRefScreen-B_cTv47v.js +0 -1
  329. package/dist/client/assets/SessionsScreen-Bq0Z77On.js +0 -1
  330. package/dist/client/assets/agents-project-COzHxZAF.css +0 -1
  331. package/dist/client/assets/archive-BjcGiAy8.js +0 -1
  332. package/dist/client/assets/content-6RvWTrtk.js +0 -1
  333. package/dist/client/assets/content-BCgwMUqz.js +0 -1
  334. package/dist/client/assets/content-Bf2VLhx0.js +0 -1
  335. package/dist/client/assets/content-BrU_38-m.js +0 -1
  336. package/dist/client/assets/content-BvvJN8rN.js +0 -1
  337. package/dist/client/assets/content-CAeEqYPw.js +0 -1
  338. package/dist/client/assets/content-CI_Z9DXk.js +0 -1
  339. package/dist/client/assets/content-CbyKONhW.js +0 -1
  340. package/dist/client/assets/content-Czogvp43.js +0 -1
  341. package/dist/client/assets/content-DFk0zNzX.js +0 -1
  342. package/dist/client/assets/content-DHe4DgkA.js +0 -1
  343. package/dist/client/assets/content-DTz7cKjo.js +0 -1
  344. package/dist/client/assets/content-E3kudY-W.js +0 -1
  345. package/dist/client/assets/content-LpO0aO_U.js +0 -1
  346. package/dist/client/assets/conversation-motion-features-BKkCRaWR.js +0 -1
  347. package/dist/client/assets/discover-CHbrEItO.js +0 -2
  348. package/dist/client/assets/embed-entry-vil28MyU.js +0 -2
  349. package/dist/client/assets/index-BVlMw8x_.js +0 -1
  350. package/dist/client/assets/index-Cn0GR-q1.js +0 -1
  351. package/dist/client/assets/index-DosI4AG9.js +0 -1
  352. package/dist/client/assets/left-BpgWnaah.js +0 -1
  353. package/dist/client/assets/left-Cx0kv6pe.js +0 -1
  354. package/dist/client/assets/left-agents-qL5PpUY-.js +0 -1
  355. package/dist/client/assets/left-z-6CkuBe.js +0 -2
  356. package/dist/client/assets/library-DIo5NiGP.js +0 -1
  357. package/dist/client/assets/projects-inbox-BBs1hG13.css +0 -1
  358. package/dist/client/assets/projects-inbox-D-zAb9Gp.js +0 -1
  359. package/dist/client/assets/resolve-panes-DO6LkwhQ.js +0 -1
  360. package/dist/client/assets/resolve-sidebar-context-CIF288wY.js +0 -1
  361. package/dist/client/assets/router-DJj2WbDg.js +0 -1
  362. package/dist/client/assets/use-fleet-active-asks-BeAdcl09.js +0 -1
  363. package/dist/client/assets/useContentSecondaryNav-D0mUhxTF.js +0 -1
package/dist/main.mjs CHANGED
@@ -903,6 +903,11 @@ function buildScoutReturnAddress(input) {
903
903
  return next;
904
904
  }
905
905
 
906
+ // ../protocol/src/conversation-projection.ts
907
+ function scoutConversationFeedId(conversationId) {
908
+ return `conv:${conversationId}`;
909
+ }
910
+
906
911
  // ../protocol/src/channel-identity.ts
907
912
  function mintChannelId(randomUuid) {
908
913
  return `${CHAT_ID_PREFIX}${randomUuid().toLowerCase().replace(/-/g, "")}`;
@@ -1589,12 +1594,16 @@ var init_host_integrations = () => {};
1589
1594
  // ../protocol/src/native-interactions.ts
1590
1595
  var init_native_interactions = () => {};
1591
1596
 
1597
+ // ../protocol/src/session-handle.ts
1598
+ var SCOUT_SESSION_HANDLE_PREFIX = "sess.";
1599
+ var init_session_handle = () => {};
1600
+
1592
1601
  // ../protocol/src/runtime-catalog.generated.ts
1593
1602
  var SCOUT_RUNTIME_CATALOG_DATA;
1594
1603
  var init_runtime_catalog_generated = __esm(() => {
1595
1604
  SCOUT_RUNTIME_CATALOG_DATA = {
1596
1605
  schemaVersion: "openscout.runtime-catalog.v1",
1597
- revision: "2026-08-18.1",
1606
+ revision: "2026-09-01.1",
1598
1607
  harnesses: [
1599
1608
  {
1600
1609
  id: "claude",
@@ -1618,6 +1627,13 @@ var init_runtime_catalog_generated = __esm(() => {
1618
1627
  family: "Opus",
1619
1628
  version: "5"
1620
1629
  },
1630
+ {
1631
+ id: "claude-fable-5",
1632
+ label: "Fable 5",
1633
+ enabled: true,
1634
+ family: "Fable",
1635
+ version: "5"
1636
+ },
1621
1637
  {
1622
1638
  id: "claude-sonnet-4-6",
1623
1639
  label: "Sonnet 4.6",
@@ -2179,6 +2195,12 @@ var init_runtime_catalog = __esm(() => {
2179
2195
  reasoningEfforts = new Set(SCOUT_REASONING_EFFORTS);
2180
2196
  });
2181
2197
 
2198
+ // ../protocol/src/scout-baseline-runtime.ts
2199
+ var init_scout_baseline_runtime = () => {};
2200
+
2201
+ // ../protocol/src/session-title.ts
2202
+ var init_session_title = () => {};
2203
+
2182
2204
  // ../protocol/src/reserved-vocabulary.ts
2183
2205
  function scoutReservedVocabularyKind(value) {
2184
2206
  const normalized = normalizeAgentSelectorSegment(value ?? "");
@@ -2543,6 +2565,7 @@ var init_broker_routes = __esm(() => {
2543
2565
  home: "/v1/home",
2544
2566
  node: "/v1/node",
2545
2567
  snapshot: "/v1/snapshot",
2568
+ conversationProjection: "/v1/conversation-projection",
2546
2569
  capabilities: "/v1/capabilities",
2547
2570
  runtimeCatalog: "/v1/runtime-catalog",
2548
2571
  topologySnapshot: "/v1/topology/snapshot",
@@ -2614,9 +2637,12 @@ var init_src = __esm(() => {
2614
2637
  init_budget();
2615
2638
  init_host_integrations();
2616
2639
  init_native_interactions();
2640
+ init_session_handle();
2617
2641
  init_runtime_profiles();
2618
2642
  init_runtime_execution();
2619
2643
  init_runtime_catalog();
2644
+ init_scout_baseline_runtime();
2645
+ init_session_title();
2620
2646
  init_reserved_vocabulary();
2621
2647
  init_scout_composer();
2622
2648
  init_inbox();
@@ -9590,8 +9616,18 @@ async function maybeReadJsonFromActiveScoutBrokerService(baseUrl, path) {
9590
9616
  return handled(await service.readNode());
9591
9617
  }
9592
9618
  if (url.pathname === "/v1/snapshot") {
9619
+ const requestedScope = url.searchParams.get("scope");
9593
9620
  return handled(await service.readSnapshot({
9594
- since: parsePositiveInt(url.searchParams.get("since")) ?? null
9621
+ since: parsePositiveInt(url.searchParams.get("since")) ?? null,
9622
+ scope: requestedScope === "conversations" || requestedScope === "agents" ? requestedScope : undefined
9623
+ }));
9624
+ }
9625
+ if (url.pathname === "/v1/conversation-projection") {
9626
+ if (!service.readConversationProjection) {
9627
+ return unhandled();
9628
+ }
9629
+ return handled(await service.readConversationProjection({
9630
+ limit: parseLimit(url.searchParams.get("limit"))
9595
9631
  }));
9596
9632
  }
9597
9633
  if (url.pathname === "/v1/capabilities") {
@@ -12270,11 +12306,32 @@ function resolveBundledRuntimeDirFromModuleDir(moduleDir) {
12270
12306
  resolve6(moduleDir, "..", "..")
12271
12307
  ];
12272
12308
  for (const candidate of candidates) {
12273
- if (isInstalledRuntimePackageDir(candidate))
12274
- return candidate;
12309
+ if (!isInstalledRuntimePackageDir(candidate))
12310
+ continue;
12311
+ const sourceWorkspaceRuntime = resolveSourceWorkspaceRuntimeDir(candidate);
12312
+ if (sourceWorkspaceRuntime)
12313
+ return sourceWorkspaceRuntime;
12314
+ return candidate;
12275
12315
  }
12276
12316
  return null;
12277
12317
  }
12318
+ function resolveSourceWorkspaceRuntimeDir(bundledPackageDir) {
12319
+ const packagesDir = dirname9(bundledPackageDir);
12320
+ if (resolve6(packagesDir, "cli") !== resolve6(bundledPackageDir))
12321
+ return null;
12322
+ const runtimeDir = join11(packagesDir, "runtime");
12323
+ if (!existsSync9(join11(runtimeDir, "src")))
12324
+ return null;
12325
+ if (!isInstalledRuntimePackageDir(runtimeDir))
12326
+ return null;
12327
+ try {
12328
+ const cliPackage = JSON.parse(readFileSync6(join11(bundledPackageDir, "package.json"), "utf8"));
12329
+ const runtimePackage = JSON.parse(readFileSync6(join11(runtimeDir, "package.json"), "utf8"));
12330
+ return cliPackage.name === "@openscout/scout" && runtimePackage.name === "@openscout/runtime" ? runtimeDir : null;
12331
+ } catch {
12332
+ return null;
12333
+ }
12334
+ }
12278
12335
  function findWorkspaceRuntimeDir(startDir) {
12279
12336
  let current = resolve6(startDir);
12280
12337
  while (true) {
@@ -33490,26 +33547,6 @@ var init_local_agents = __esm(() => {
33490
33547
  TMUX_DEFAULT_ROWS = parsePositiveInteger2(process.env.OPENSCOUT_LOCAL_AGENT_TMUX_ROWS, 48);
33491
33548
  });
33492
33549
 
33493
- // ../../apps/desktop/src/core/broker/paths.ts
33494
- var openAiAudioSpeechUrl = "https://api.openai.com/v1/audio/speech";
33495
- var init_paths = __esm(() => {
33496
- init_src();
33497
- });
33498
-
33499
- // ../../apps/desktop/src/core/broker/ask-metadata.ts
33500
- function buildScoutAskMetadata(input) {
33501
- return {
33502
- source: input.source,
33503
- ...input.workRecordId ? {
33504
- collaborationRecordId: input.workRecordId,
33505
- workId: input.workRecordId
33506
- } : {},
33507
- ...input.senderContext ? { senderContext: input.senderContext } : {},
33508
- ...input.workspace ? { askWorkspace: input.workspace } : {},
33509
- ...input.labels ? { labels: input.labels } : {}
33510
- };
33511
- }
33512
-
33513
33550
  // ../runtime/src/registry.ts
33514
33551
  function createRuntimeRegistrySnapshot(value = {}) {
33515
33552
  return {
@@ -33532,6 +33569,26 @@ var init_registry3 = __esm(() => {
33532
33569
  TERMINAL_COLLABORATION_STATES = new Set(["done", "cancelled", "closed", "declined"]);
33533
33570
  });
33534
33571
 
33572
+ // ../../apps/desktop/src/core/broker/paths.ts
33573
+ var openAiAudioSpeechUrl = "https://api.openai.com/v1/audio/speech";
33574
+ var init_paths = __esm(() => {
33575
+ init_src();
33576
+ });
33577
+
33578
+ // ../../apps/desktop/src/core/broker/ask-metadata.ts
33579
+ function buildScoutAskMetadata(input) {
33580
+ return {
33581
+ source: input.source,
33582
+ ...input.workRecordId ? {
33583
+ collaborationRecordId: input.workRecordId,
33584
+ workId: input.workRecordId
33585
+ } : {},
33586
+ ...input.senderContext ? { senderContext: input.senderContext } : {},
33587
+ ...input.workspace ? { askWorkspace: input.workspace } : {},
33588
+ ...input.labels ? { labels: input.labels } : {}
33589
+ };
33590
+ }
33591
+
33535
33592
  // ../runtime/src/planner.ts
33536
33593
  function createDeliveryId(messageId, targetId, reason, transport) {
33537
33594
  return `del-${messageId}-${targetId}-${reason}-${transport}`;
@@ -37881,7 +37938,7 @@ var init_sqlite_core = __esm(() => {
37881
37938
  });
37882
37939
 
37883
37940
  // ../runtime/src/drizzle-schema.ts
37884
- var epochMsNow, nodesTable, trustedPeersTable, peerNoncesTable, actorsTable, agentsTable, agentEndpointsTable, runtimeSessionsTable, runtimeSessionAliasesTable, routeAliasBindingsTable, routeAliasRevisionsTable, conversationsTable, conversationMembersTable, messagesTable, messageMentionsTable, messageAttachmentsTable, conversationReadCursorsTable, invocationsTable, flightsTable, bindingsTable, deliveriesTable, deliveryAttemptsTable, durableActionsTable, durableAttemptsTable, durableCheckpointsTable, durableSignalsTable, collaborationRecordsTable, collaborationEventsTable, contextBlocksTable, contextPacksTable, eventsTable, threadEventsTable, threadCursorsTable, scoutDispatchesTable, activityItemsTable, budgetUsageEventsTable, budgetQuotaWindowSnapshotsTable, mobilePushRegistrationsTable, briefingsTable, roleAssignmentsTable, missionLogEntriesTable, controlPlaneDrizzleSchema;
37941
+ var epochMsNow, nodesTable, trustedPeersTable, peerNoncesTable, actorsTable, agentsTable, agentEndpointsTable, runtimeSessionsTable, runtimeSessionAliasesTable, routeAliasBindingsTable, routeAliasRevisionsTable, conversationsTable, conversationMembersTable, messagesTable, messageMentionsTable, messageAttachmentsTable, conversationReadCursorsTable, brokerJournalProjectionCheckpointsTable, conversationProjectionMetaTable, conversationProjectionItemsTable, conversationProjectionMessageFactsTable, conversationProjectionMessageStatsTable, conversationProjectionSourcesTable, conversationProjectionEventsTable, invocationsTable, flightsTable, bindingsTable, deliveriesTable, deliveryAttemptsTable, durableActionsTable, durableAttemptsTable, durableCheckpointsTable, durableSignalsTable, collaborationRecordsTable, collaborationEventsTable, contextBlocksTable, contextPacksTable, eventsTable, threadEventsTable, threadCursorsTable, scoutDispatchesTable, activityItemsTable, budgetUsageEventsTable, budgetQuotaWindowSnapshotsTable, mobilePushRegistrationsTable, briefingsTable, roleAssignmentsTable, missionLogEntriesTable, controlPlaneDrizzleSchema;
37885
37942
  var init_drizzle_schema = __esm(() => {
37886
37943
  init_drizzle_orm();
37887
37944
  init_sqlite_core();
@@ -37953,7 +38010,10 @@ var init_drizzle_schema = __esm(() => {
37953
38010
  advertiseScope: text("advertise_scope").notNull(),
37954
38011
  ownerId: text("owner_id"),
37955
38012
  metadataJson: text("metadata_json")
37956
- });
38013
+ }, (table3) => [
38014
+ index("idx_agents_home_node_id").on(table3.homeNodeId),
38015
+ index("idx_agents_authority_node_id").on(table3.authorityNodeId)
38016
+ ]);
37957
38017
  agentEndpointsTable = sqliteTable("agent_endpoints", {
37958
38018
  id: text("id").primaryKey(),
37959
38019
  agentId: text("agent_id").notNull().references(() => agentsTable.id, { onDelete: "cascade" }),
@@ -37969,7 +38029,14 @@ var init_drizzle_schema = __esm(() => {
37969
38029
  metadataJson: text("metadata_json"),
37970
38030
  updatedAt: integer("updated_at").notNull().default(epochMsNow)
37971
38031
  }, (table3) => [
37972
- index("idx_agent_endpoints_agent_updated_at").on(table3.agentId, desc(table3.updatedAt))
38032
+ index("idx_agent_endpoints_agent_updated_at").on(table3.agentId, desc(table3.updatedAt)),
38033
+ index("idx_agent_endpoints_roster_recency").on(desc(sql`CASE
38034
+ WHEN ${table3.updatedAt} IS NULL THEN NULL
38035
+ WHEN CAST(${table3.updatedAt} AS REAL) < 1000000000000
38036
+ THEN CAST(CAST(${table3.updatedAt} AS REAL) * 1000 AS INTEGER)
38037
+ ELSE CAST(${table3.updatedAt} AS INTEGER)
38038
+ END`), table3.agentId),
38039
+ index("idx_agent_endpoints_node_id").on(table3.nodeId)
37973
38040
  ]);
37974
38041
  runtimeSessionsTable = sqliteTable("runtime_sessions", {
37975
38042
  id: text("id").primaryKey(),
@@ -37993,6 +38060,7 @@ var init_drizzle_schema = __esm(() => {
37993
38060
  }, (table3) => [
37994
38061
  index("idx_runtime_sessions_agent_last_seen").on(table3.agentId, desc(table3.lastSeenAt)),
37995
38062
  index("idx_runtime_sessions_endpoint_last_seen").on(table3.endpointId, desc(table3.lastSeenAt)),
38063
+ index("idx_runtime_sessions_node_id").on(table3.nodeId),
37996
38064
  index("idx_runtime_sessions_external").on(table3.externalSessionId),
37997
38065
  index("idx_runtime_sessions_expires").on(table3.expiresAt).where(sql`expires_at IS NOT NULL`)
37998
38066
  ]);
@@ -38012,6 +38080,8 @@ var init_drizzle_schema = __esm(() => {
38012
38080
  primaryKey({ columns: [table3.alias, table3.sessionId] }),
38013
38081
  index("idx_runtime_session_aliases_alias").on(table3.alias, desc(table3.lastSeenAt)),
38014
38082
  index("idx_runtime_session_aliases_session").on(table3.sessionId),
38083
+ index("idx_runtime_session_aliases_endpoint").on(table3.endpointId),
38084
+ index("idx_runtime_session_aliases_node_id").on(table3.nodeId),
38015
38085
  index("idx_runtime_session_aliases_expires").on(table3.expiresAt).where(sql`expires_at IS NOT NULL`)
38016
38086
  ]);
38017
38087
  routeAliasBindingsTable = sqliteTable("route_alias_bindings", {
@@ -38091,7 +38161,8 @@ var init_drizzle_schema = __esm(() => {
38091
38161
  actorId: text("actor_id").notNull().references(() => actorsTable.id, { onDelete: "cascade" }),
38092
38162
  role: text("role")
38093
38163
  }, (table3) => [
38094
- primaryKey({ columns: [table3.conversationId, table3.actorId] })
38164
+ primaryKey({ columns: [table3.conversationId, table3.actorId] }),
38165
+ index("idx_conversation_members_actor").on(table3.actorId, table3.conversationId)
38095
38166
  ]);
38096
38167
  messagesTable = sqliteTable("messages", {
38097
38168
  id: text("id").primaryKey(),
@@ -38111,7 +38182,8 @@ var init_drizzle_schema = __esm(() => {
38111
38182
  }, (table3) => [
38112
38183
  index("idx_messages_conversation_created_at").on(table3.conversationId, table3.createdAt),
38113
38184
  index("idx_messages_created_at").on(desc(table3.createdAt)),
38114
- index("idx_messages_actor_created_at").on(table3.actorId, desc(table3.createdAt))
38185
+ index("idx_messages_actor_created_at").on(table3.actorId, desc(table3.createdAt)),
38186
+ index("idx_messages_origin_node_id").on(table3.originNodeId)
38115
38187
  ]);
38116
38188
  messageMentionsTable = sqliteTable("message_mentions", {
38117
38189
  messageId: text("message_id").notNull().references(() => messagesTable.id, { onDelete: "cascade" }),
@@ -38140,7 +38212,102 @@ var init_drizzle_schema = __esm(() => {
38140
38212
  metadataJson: text("metadata_json")
38141
38213
  }, (table3) => [
38142
38214
  primaryKey({ columns: [table3.conversationId, table3.actorId] }),
38143
- index("idx_read_cursors_conversation_updated_at").on(table3.conversationId, desc(table3.updatedAt))
38215
+ index("idx_read_cursors_conversation_updated_at").on(table3.conversationId, desc(table3.updatedAt)),
38216
+ index("idx_read_cursors_reader_node_id").on(table3.readerNodeId)
38217
+ ]);
38218
+ brokerJournalProjectionCheckpointsTable = sqliteTable("broker_journal_projection_checkpoints", {
38219
+ projectionId: text("projection_id").primaryKey(),
38220
+ projectionVersion: integer("projection_version").notNull(),
38221
+ barrierId: text("barrier_id").notNull(),
38222
+ updatedAt: integer("updated_at").notNull()
38223
+ });
38224
+ conversationProjectionMetaTable = sqliteTable("conversation_projection_meta", {
38225
+ singleton: integer("singleton").primaryKey(),
38226
+ projectionId: text("projection_id").notNull(),
38227
+ projectionVersion: integer("projection_version").notNull(),
38228
+ headSeq: integer("head_seq").notNull(),
38229
+ minReplayableSeq: integer("min_replayable_seq").notNull(),
38230
+ updatedAt: integer("updated_at").notNull()
38231
+ }, (table3) => [
38232
+ check("conversation_projection_meta_singleton_check", sql`${table3.singleton} = 1`)
38233
+ ]);
38234
+ conversationProjectionItemsTable = sqliteTable("conversation_projection_items", {
38235
+ feedId: text("feed_id").primaryKey(),
38236
+ entityKind: text("entity_kind").notNull(),
38237
+ kind: text("kind").notNull(),
38238
+ conversationId: text("conversation_id"),
38239
+ runtimeSessionId: text("runtime_session_id"),
38240
+ source: text("source"),
38241
+ sourceSessionId: text("source_session_id"),
38242
+ title: text("title"),
38243
+ alias: text("alias"),
38244
+ naturalKey: text("natural_key"),
38245
+ projectRoot: text("project_root"),
38246
+ harness: text("harness"),
38247
+ model: text("model"),
38248
+ effort: text("effort"),
38249
+ agentId: text("agent_id"),
38250
+ agentName: text("agent_name"),
38251
+ currentBranch: text("current_branch"),
38252
+ authorityNodeId: text("authority_node_id"),
38253
+ authorityNodeName: text("authority_node_name"),
38254
+ parentConversationId: text("parent_conversation_id"),
38255
+ anchorMessageId: text("anchor_message_id"),
38256
+ activityState: text("activity_state").notNull(),
38257
+ lastMessageId: text("last_message_id"),
38258
+ lastMessageAt: integer("last_message_at"),
38259
+ lastActivityAt: integer("last_activity_at").notNull(),
38260
+ messageCount: integer("message_count").notNull().default(0),
38261
+ unreadCount: integer("unread_count").notNull().default(0),
38262
+ participantCount: integer("participant_count").notNull().default(0),
38263
+ preview: text("preview"),
38264
+ lastEngagedAt: integer("last_engaged_at"),
38265
+ sourceFreshAt: integer("source_fresh_at"),
38266
+ visibilityState: text("visibility_state").notNull().default("visible"),
38267
+ updatedSeq: integer("updated_seq").notNull(),
38268
+ updatedAt: integer("updated_at").notNull()
38269
+ }, (table3) => [
38270
+ check("conversation_projection_items_entity_kind_check", sql`${table3.entityKind} IN ('scout_conversation', 'observed_session')`),
38271
+ check("conversation_projection_items_visibility_check", sql`${table3.visibilityState} IN ('visible', 'hidden')`),
38272
+ index("idx_conversation_projection_recent").on(table3.visibilityState, desc(table3.lastActivityAt), table3.feedId),
38273
+ index("idx_conversation_projection_project_recent").on(table3.projectRoot, table3.visibilityState, desc(table3.lastActivityAt)),
38274
+ index("idx_conversation_projection_engaged").on(table3.visibilityState, desc(table3.lastEngagedAt), desc(table3.lastActivityAt), table3.feedId),
38275
+ index("idx_conversation_projection_source_fresh").on(table3.visibilityState, desc(table3.sourceFreshAt)),
38276
+ index("idx_conversation_projection_runtime_session").on(table3.entityKind, table3.runtimeSessionId),
38277
+ index("idx_conversation_projection_source_session").on(table3.entityKind, table3.sourceSessionId),
38278
+ uniqueIndex("idx_conversation_projection_conversation").on(table3.conversationId).where(sql`conversation_id IS NOT NULL`)
38279
+ ]);
38280
+ conversationProjectionMessageFactsTable = sqliteTable("conversation_projection_message_facts", {
38281
+ messageId: text("message_id").primaryKey().references(() => messagesTable.id, { onDelete: "cascade" }),
38282
+ conversationId: text("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
38283
+ createdAt: integer("created_at").notNull()
38284
+ }, (table3) => [
38285
+ index("idx_conversation_projection_message_facts_conversation").on(table3.conversationId, table3.createdAt, table3.messageId)
38286
+ ]);
38287
+ conversationProjectionMessageStatsTable = sqliteTable("conversation_projection_message_stats", {
38288
+ conversationId: text("conversation_id").primaryKey().references(() => conversationsTable.id, { onDelete: "cascade" }),
38289
+ messageCount: integer("message_count").notNull().default(0),
38290
+ latestMessageId: text("latest_message_id"),
38291
+ latestMessageAt: integer("latest_message_at")
38292
+ });
38293
+ conversationProjectionSourcesTable = sqliteTable("conversation_projection_sources", {
38294
+ source: text("source").notNull(),
38295
+ sourceSessionId: text("source_session_id").notNull(),
38296
+ feedId: text("feed_id").notNull().references(() => conversationProjectionItemsTable.feedId, { onDelete: "cascade" }),
38297
+ firstSeenAt: integer("first_seen_at").notNull(),
38298
+ lastSeenAt: integer("last_seen_at").notNull()
38299
+ }, (table3) => [
38300
+ primaryKey({ columns: [table3.source, table3.sourceSessionId] }),
38301
+ index("idx_conversation_projection_sources_feed").on(table3.feedId)
38302
+ ]);
38303
+ conversationProjectionEventsTable = sqliteTable("conversation_projection_events", {
38304
+ seq: integer("seq").primaryKey({ autoIncrement: true }),
38305
+ projectionId: text("projection_id").notNull(),
38306
+ ts: integer("ts").notNull(),
38307
+ payloadJson: text("payload_json").notNull()
38308
+ }, (table3) => [
38309
+ index("idx_conversation_projection_events_projection_seq").on(table3.projectionId, table3.seq),
38310
+ index("idx_conversation_projection_events_ts").on(table3.ts)
38144
38311
  ]);
38145
38312
  invocationsTable = sqliteTable("invocations", {
38146
38313
  id: text("id").primaryKey(),
@@ -38172,7 +38339,10 @@ var init_drizzle_schema = __esm(() => {
38172
38339
  executionResolutionJson: text("execution_resolution_json")
38173
38340
  }, (table3) => [
38174
38341
  index("idx_invocations_target_created_at").on(table3.targetAgentId, table3.createdAt),
38175
- index("idx_invocations_requester_created_at").on(table3.requesterId, desc(table3.createdAt))
38342
+ index("idx_invocations_requester_created_at").on(table3.requesterId, desc(table3.createdAt)),
38343
+ index("idx_invocations_conversation_created_at").on(table3.conversationId, desc(table3.createdAt)),
38344
+ index("idx_invocations_requester_node_id").on(table3.requesterNodeId),
38345
+ index("idx_invocations_target_node_id").on(table3.targetNodeId)
38176
38346
  ]);
38177
38347
  flightsTable = sqliteTable("flights", {
38178
38348
  id: text("id").primaryKey(),
@@ -38218,7 +38388,8 @@ var init_drizzle_schema = __esm(() => {
38218
38388
  createdAt: integer("created_at").notNull().default(epochMsNow)
38219
38389
  }, (table3) => [
38220
38390
  index("idx_deliveries_status_transport").on(table3.status, table3.transport),
38221
- index("idx_deliveries_created_at").on(desc(table3.createdAt))
38391
+ index("idx_deliveries_created_at").on(desc(table3.createdAt)),
38392
+ index("idx_deliveries_target_node_id").on(table3.targetNodeId)
38222
38393
  ]);
38223
38394
  deliveryAttemptsTable = sqliteTable("delivery_attempts", {
38224
38395
  id: text("id").primaryKey(),
@@ -38388,7 +38559,8 @@ var init_drizzle_schema = __esm(() => {
38388
38559
  }, (table3) => [
38389
38560
  unique2().on(table3.conversationId, table3.seq),
38390
38561
  index("idx_thread_events_conversation_seq").on(table3.conversationId, desc(table3.seq)),
38391
- index("idx_thread_events_conversation_ts").on(table3.conversationId, desc(table3.ts))
38562
+ index("idx_thread_events_conversation_ts").on(table3.conversationId, desc(table3.ts)),
38563
+ index("idx_thread_events_authority_node_id").on(table3.authorityNodeId)
38392
38564
  ]);
38393
38565
  threadCursorsTable = sqliteTable("thread_cursors", {
38394
38566
  conversationId: text("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
@@ -38396,7 +38568,8 @@ var init_drizzle_schema = __esm(() => {
38396
38568
  lastAppliedSeq: integer("last_applied_seq").notNull(),
38397
38569
  updatedAt: integer("updated_at").notNull()
38398
38570
  }, (table3) => [
38399
- primaryKey({ columns: [table3.conversationId, table3.authorityNodeId] })
38571
+ primaryKey({ columns: [table3.conversationId, table3.authorityNodeId] }),
38572
+ index("idx_thread_cursors_authority_node_id").on(table3.authorityNodeId)
38400
38573
  ]);
38401
38574
  scoutDispatchesTable = sqliteTable("scout_dispatches", {
38402
38575
  id: text("id").primaryKey(),
@@ -38598,6 +38771,13 @@ var init_drizzle_schema = __esm(() => {
38598
38771
  messageMentions: messageMentionsTable,
38599
38772
  messageAttachments: messageAttachmentsTable,
38600
38773
  conversationReadCursors: conversationReadCursorsTable,
38774
+ brokerJournalProjectionCheckpoints: brokerJournalProjectionCheckpointsTable,
38775
+ conversationProjectionMeta: conversationProjectionMetaTable,
38776
+ conversationProjectionItems: conversationProjectionItemsTable,
38777
+ conversationProjectionMessageFacts: conversationProjectionMessageFactsTable,
38778
+ conversationProjectionMessageStats: conversationProjectionMessageStatsTable,
38779
+ conversationProjectionSources: conversationProjectionSourcesTable,
38780
+ conversationProjectionEvents: conversationProjectionEventsTable,
38601
38781
  invocations: invocationsTable,
38602
38782
  flights: flightsTable,
38603
38783
  bindings: bindingsTable,
@@ -38667,6 +38847,8 @@ CREATE INDEX IF NOT EXISTS idx_runtime_sessions_agent_last_seen
38667
38847
  ON runtime_sessions (agent_id, last_seen_at DESC);
38668
38848
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_endpoint_last_seen
38669
38849
  ON runtime_sessions (endpoint_id, last_seen_at DESC);
38850
+ CREATE INDEX IF NOT EXISTS idx_runtime_sessions_node_id
38851
+ ON runtime_sessions (node_id);
38670
38852
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_external
38671
38853
  ON runtime_sessions (external_session_id);
38672
38854
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_expires
@@ -38676,6 +38858,10 @@ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_alias
38676
38858
  ON runtime_session_aliases (alias, last_seen_at DESC);
38677
38859
  CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_session
38678
38860
  ON runtime_session_aliases (session_id);
38861
+ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_endpoint
38862
+ ON runtime_session_aliases (endpoint_id);
38863
+ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_node_id
38864
+ ON runtime_session_aliases (node_id);
38679
38865
  CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_expires
38680
38866
  ON runtime_session_aliases (expires_at)
38681
38867
  WHERE expires_at IS NOT NULL;
@@ -38938,6 +39124,93 @@ CREATE TABLE IF NOT EXISTS conversation_read_cursors (
38938
39124
  PRIMARY KEY (conversation_id, actor_id)
38939
39125
  );
38940
39126
 
39127
+ CREATE TABLE IF NOT EXISTS broker_journal_projection_checkpoints (
39128
+ projection_id TEXT PRIMARY KEY,
39129
+ projection_version INTEGER NOT NULL,
39130
+ barrier_id TEXT NOT NULL,
39131
+ updated_at INTEGER NOT NULL
39132
+ );
39133
+
39134
+ CREATE TABLE IF NOT EXISTS conversation_projection_meta (
39135
+ singleton INTEGER PRIMARY KEY,
39136
+ projection_id TEXT NOT NULL,
39137
+ projection_version INTEGER NOT NULL,
39138
+ head_seq INTEGER NOT NULL,
39139
+ min_replayable_seq INTEGER NOT NULL,
39140
+ updated_at INTEGER NOT NULL,
39141
+ CONSTRAINT conversation_projection_meta_singleton_check CHECK (singleton = 1)
39142
+ );
39143
+
39144
+ CREATE TABLE IF NOT EXISTS conversation_projection_items (
39145
+ feed_id TEXT PRIMARY KEY,
39146
+ entity_kind TEXT NOT NULL,
39147
+ kind TEXT NOT NULL,
39148
+ conversation_id TEXT,
39149
+ runtime_session_id TEXT,
39150
+ source TEXT,
39151
+ source_session_id TEXT,
39152
+ title TEXT,
39153
+ alias TEXT,
39154
+ natural_key TEXT,
39155
+ project_root TEXT,
39156
+ harness TEXT,
39157
+ model TEXT,
39158
+ effort TEXT,
39159
+ agent_id TEXT,
39160
+ agent_name TEXT,
39161
+ current_branch TEXT,
39162
+ authority_node_id TEXT,
39163
+ authority_node_name TEXT,
39164
+ parent_conversation_id TEXT,
39165
+ anchor_message_id TEXT,
39166
+ activity_state TEXT NOT NULL,
39167
+ last_message_id TEXT,
39168
+ last_message_at INTEGER,
39169
+ last_activity_at INTEGER NOT NULL,
39170
+ message_count INTEGER NOT NULL DEFAULT 0,
39171
+ unread_count INTEGER NOT NULL DEFAULT 0,
39172
+ participant_count INTEGER NOT NULL DEFAULT 0,
39173
+ preview TEXT,
39174
+ last_engaged_at INTEGER,
39175
+ source_fresh_at INTEGER,
39176
+ visibility_state TEXT NOT NULL DEFAULT 'visible',
39177
+ updated_seq INTEGER NOT NULL,
39178
+ updated_at INTEGER NOT NULL,
39179
+ CONSTRAINT conversation_projection_items_entity_kind_check
39180
+ CHECK (entity_kind IN ('scout_conversation', 'observed_session')),
39181
+ CONSTRAINT conversation_projection_items_visibility_check
39182
+ CHECK (visibility_state IN ('visible', 'hidden'))
39183
+ );
39184
+
39185
+ CREATE TABLE IF NOT EXISTS conversation_projection_message_facts (
39186
+ message_id TEXT PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
39187
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
39188
+ created_at INTEGER NOT NULL
39189
+ );
39190
+
39191
+ CREATE TABLE IF NOT EXISTS conversation_projection_message_stats (
39192
+ conversation_id TEXT PRIMARY KEY REFERENCES conversations(id) ON DELETE CASCADE,
39193
+ message_count INTEGER NOT NULL DEFAULT 0,
39194
+ latest_message_id TEXT,
39195
+ latest_message_at INTEGER
39196
+ );
39197
+
39198
+ CREATE TABLE IF NOT EXISTS conversation_projection_sources (
39199
+ source TEXT NOT NULL,
39200
+ source_session_id TEXT NOT NULL,
39201
+ feed_id TEXT NOT NULL REFERENCES conversation_projection_items(feed_id) ON DELETE CASCADE,
39202
+ first_seen_at INTEGER NOT NULL,
39203
+ last_seen_at INTEGER NOT NULL,
39204
+ PRIMARY KEY (source, source_session_id)
39205
+ );
39206
+
39207
+ CREATE TABLE IF NOT EXISTS conversation_projection_events (
39208
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
39209
+ projection_id TEXT NOT NULL,
39210
+ ts INTEGER NOT NULL,
39211
+ payload_json TEXT NOT NULL
39212
+ );
39213
+
38941
39214
  CREATE TABLE IF NOT EXISTS invocations (
38942
39215
  id TEXT PRIMARY KEY,
38943
39216
  requester_id TEXT NOT NULL REFERENCES actors(id) ON DELETE RESTRICT,
@@ -39345,18 +39618,74 @@ CREATE INDEX IF NOT EXISTS idx_peer_nonces_seen_at
39345
39618
  ON peer_nonces (seen_at);
39346
39619
  CREATE INDEX IF NOT EXISTS idx_agent_endpoints_agent_updated_at
39347
39620
  ON agent_endpoints (agent_id, updated_at DESC);
39621
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_roster_recency
39622
+ ON agent_endpoints (
39623
+ CASE
39624
+ WHEN updated_at IS NULL THEN NULL
39625
+ WHEN CAST(updated_at AS REAL) < 1000000000000
39626
+ THEN CAST(CAST(updated_at AS REAL) * 1000 AS INTEGER)
39627
+ ELSE CAST(updated_at AS INTEGER)
39628
+ END DESC,
39629
+ agent_id
39630
+ );
39631
+ CREATE INDEX IF NOT EXISTS idx_agents_home_node_id
39632
+ ON agents (home_node_id);
39633
+ CREATE INDEX IF NOT EXISTS idx_agents_authority_node_id
39634
+ ON agents (authority_node_id);
39635
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_node_id
39636
+ ON agent_endpoints (node_id);
39637
+ CREATE INDEX IF NOT EXISTS idx_conversation_members_actor
39638
+ ON conversation_members (actor_id, conversation_id);
39348
39639
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_created_at
39349
39640
  ON messages (conversation_id, created_at);
39350
39641
  CREATE INDEX IF NOT EXISTS idx_messages_created_at
39351
39642
  ON messages (created_at DESC);
39352
39643
  CREATE INDEX IF NOT EXISTS idx_messages_actor_created_at
39353
39644
  ON messages (actor_id, created_at DESC);
39645
+ CREATE INDEX IF NOT EXISTS idx_messages_origin_node_id
39646
+ ON messages (origin_node_id);
39354
39647
  CREATE INDEX IF NOT EXISTS idx_read_cursors_conversation_updated_at
39355
39648
  ON conversation_read_cursors (conversation_id, updated_at DESC);
39649
+ CREATE INDEX IF NOT EXISTS idx_read_cursors_reader_node_id
39650
+ ON conversation_read_cursors (reader_node_id);
39651
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_recent
39652
+ ON conversation_projection_items (visibility_state, last_activity_at DESC, feed_id);
39653
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_project_recent
39654
+ ON conversation_projection_items (project_root, visibility_state, last_activity_at DESC);
39655
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_message_facts_conversation
39656
+ ON conversation_projection_message_facts (conversation_id, created_at, message_id);
39657
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_engaged
39658
+ ON conversation_projection_items (
39659
+ visibility_state,
39660
+ last_engaged_at DESC,
39661
+ last_activity_at DESC,
39662
+ feed_id
39663
+ );
39664
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_source_fresh
39665
+ ON conversation_projection_items (visibility_state, source_fresh_at DESC);
39666
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_runtime_session
39667
+ ON conversation_projection_items (entity_kind, runtime_session_id);
39668
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_source_session
39669
+ ON conversation_projection_items (entity_kind, source_session_id);
39670
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_conversation_projection_conversation
39671
+ ON conversation_projection_items (conversation_id)
39672
+ WHERE conversation_id IS NOT NULL;
39673
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_sources_feed
39674
+ ON conversation_projection_sources (feed_id);
39675
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_events_projection_seq
39676
+ ON conversation_projection_events (projection_id, seq);
39677
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_events_ts
39678
+ ON conversation_projection_events (ts);
39356
39679
  CREATE INDEX IF NOT EXISTS idx_invocations_target_created_at
39357
39680
  ON invocations (target_agent_id, created_at);
39358
39681
  CREATE INDEX IF NOT EXISTS idx_invocations_requester_created_at
39359
39682
  ON invocations (requester_id, created_at DESC);
39683
+ CREATE INDEX IF NOT EXISTS idx_invocations_conversation_created_at
39684
+ ON invocations (conversation_id, created_at DESC);
39685
+ CREATE INDEX IF NOT EXISTS idx_invocations_requester_node_id
39686
+ ON invocations (requester_node_id);
39687
+ CREATE INDEX IF NOT EXISTS idx_invocations_target_node_id
39688
+ ON invocations (target_node_id);
39360
39689
  CREATE INDEX IF NOT EXISTS idx_flights_target_state
39361
39690
  ON flights (target_agent_id, state);
39362
39691
  CREATE INDEX IF NOT EXISTS idx_flights_invocation_id
@@ -39365,6 +39694,8 @@ CREATE INDEX IF NOT EXISTS idx_deliveries_status_transport
39365
39694
  ON deliveries (status, transport);
39366
39695
  CREATE INDEX IF NOT EXISTS idx_deliveries_created_at
39367
39696
  ON deliveries (created_at DESC);
39697
+ CREATE INDEX IF NOT EXISTS idx_deliveries_target_node_id
39698
+ ON deliveries (target_node_id);
39368
39699
  CREATE INDEX IF NOT EXISTS idx_delivery_attempts_created_at
39369
39700
  ON delivery_attempts (created_at DESC);
39370
39701
  CREATE UNIQUE INDEX IF NOT EXISTS idx_durable_actions_idempotency_key
@@ -39411,6 +39742,10 @@ CREATE INDEX IF NOT EXISTS idx_thread_events_conversation_seq
39411
39742
  ON thread_events (conversation_id, seq DESC);
39412
39743
  CREATE INDEX IF NOT EXISTS idx_thread_events_conversation_ts
39413
39744
  ON thread_events (conversation_id, ts DESC);
39745
+ CREATE INDEX IF NOT EXISTS idx_thread_events_authority_node_id
39746
+ ON thread_events (authority_node_id);
39747
+ CREATE INDEX IF NOT EXISTS idx_thread_cursors_authority_node_id
39748
+ ON thread_cursors (authority_node_id);
39414
39749
  CREATE INDEX IF NOT EXISTS idx_activity_items_agent_ts
39415
39750
  ON activity_items (agent_id, ts DESC);
39416
39751
  CREATE INDEX IF NOT EXISTS idx_activity_items_actor_ts
@@ -40638,7 +40973,23 @@ CREATE INDEX IF NOT EXISTS idx_activity_items_ts
40638
40973
  ON activity_items(ts DESC);
40639
40974
  CREATE INDEX IF NOT EXISTS idx_conversations_created_at
40640
40975
  ON conversations(created_at DESC);
40976
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_roster_recency
40977
+ ON agent_endpoints (
40978
+ CASE
40979
+ WHEN updated_at IS NULL THEN NULL
40980
+ WHEN CAST(updated_at AS REAL) < 1000000000000
40981
+ THEN CAST(CAST(updated_at AS REAL) * 1000 AS INTEGER)
40982
+ ELSE CAST(updated_at AS INTEGER)
40983
+ END DESC,
40984
+ agent_id
40985
+ );
40641
40986
  `);
40987
+ if (hasColumn(database, "conversations", "authority_node_id")) {
40988
+ database.exec(`
40989
+ CREATE INDEX IF NOT EXISTS idx_conversations_authority_node_id
40990
+ ON conversations(authority_node_id);
40991
+ `);
40992
+ }
40642
40993
  }
40643
40994
  },
40644
40995
  {
@@ -41014,8 +41365,39 @@ var init_budget_observations2 = __esm(() => {
41014
41365
  QUOTA_HISTORY_BUCKET_MS = 60 * 60 * 1000;
41015
41366
  });
41016
41367
 
41017
- // ../runtime/src/sqlite-store.ts
41368
+ // ../runtime/src/runtime-session-handle.ts
41018
41369
  import { createHash as createHash4 } from "crypto";
41370
+ function metadataStringValue(metadata, key) {
41371
+ const value = metadata?.[key];
41372
+ return typeof value === "string" && value.trim() ? value.trim() : null;
41373
+ }
41374
+ function firstStringValue(...values) {
41375
+ return values.find((value) => Boolean(value?.trim()))?.trim() ?? null;
41376
+ }
41377
+ function runtimeSessionPrimaryAlias(endpoint) {
41378
+ const metadata = endpoint.metadata;
41379
+ if (metadata?.cardless === true) {
41380
+ return firstStringValue(metadataStringValue(metadata, "handle"), metadataStringValue(metadata, "externalSessionId"), metadataStringValue(metadata, "threadId"), endpoint.sessionId, endpoint.agentId);
41381
+ }
41382
+ return firstStringValue(metadataStringValue(metadata, "externalSessionId"), metadataStringValue(metadata, "threadId"), metadataStringValue(metadata, "nativeSessionId"), metadataStringValue(metadata, "pairingSessionId"), metadataStringValue(metadata, "sessionId"), endpoint.sessionId, metadataStringValue(metadata, "runtimeSessionId"), metadataStringValue(metadata, "runtimeInstanceId"), metadataStringValue(metadata, "tmuxSession"));
41383
+ }
41384
+ function runtimeSessionHandleForEndpoint(endpoint, primaryAlias = runtimeSessionPrimaryAlias(endpoint)) {
41385
+ if (!primaryAlias)
41386
+ return null;
41387
+ const resolutionKey = [
41388
+ endpoint.nodeId,
41389
+ endpoint.harness,
41390
+ primaryAlias
41391
+ ].join("\x00");
41392
+ const token = createHash4("sha256").update(resolutionKey).digest("hex").slice(0, 20);
41393
+ return `${SCOUT_SESSION_HANDLE_PREFIX}${token}`;
41394
+ }
41395
+ var init_runtime_session_handle = __esm(() => {
41396
+ init_src();
41397
+ });
41398
+
41399
+ // ../runtime/src/sqlite-store.ts
41400
+ import { createHash as createHash5 } from "crypto";
41019
41401
  import { mkdirSync as mkdirSync7 } from "fs";
41020
41402
  import { dirname as dirname18 } from "path";
41021
41403
  function parseJson(value, fallback2) {
@@ -41027,6 +41409,16 @@ function parseJson(value, fallback2) {
41027
41409
  return fallback2;
41028
41410
  }
41029
41411
  }
41412
+ function isTransientSqliteBusyError(error) {
41413
+ if (typeof error === "object" && error !== null) {
41414
+ const code = error.code;
41415
+ if (code === "SQLITE_BUSY" || code === "SQLITE_LOCKED") {
41416
+ return true;
41417
+ }
41418
+ }
41419
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
41420
+ return message.includes("database is locked") || message.includes("database is busy") || message.includes("sqlite_busy");
41421
+ }
41030
41422
  function stringify3(value) {
41031
41423
  return value === undefined ? null : JSON.stringify(value);
41032
41424
  }
@@ -41037,9 +41429,9 @@ function normalizeTimestampMs(value) {
41037
41429
  return epochMs(value);
41038
41430
  }
41039
41431
  function stableHash2(value, length = 20) {
41040
- return createHash4("sha256").update(value).digest("hex").slice(0, length);
41432
+ return createHash5("sha256").update(value).digest("hex").slice(0, length);
41041
41433
  }
41042
- function metadataStringValue(metadata, key) {
41434
+ function metadataStringValue2(metadata, key) {
41043
41435
  const value = metadata?.[key];
41044
41436
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
41045
41437
  }
@@ -41056,7 +41448,7 @@ function metadataTimestampValue(metadata, key) {
41056
41448
  }
41057
41449
  return null;
41058
41450
  }
41059
- function firstStringValue(...values) {
41451
+ function firstStringValue2(...values) {
41060
41452
  for (const value of values) {
41061
41453
  const trimmed = value?.trim();
41062
41454
  if (trimmed) {
@@ -41274,16 +41666,9 @@ function runtimeSessionAliasFromRow(row) {
41274
41666
  function runtimeSessionMetadata(endpoint) {
41275
41667
  return endpoint.metadata ?? {};
41276
41668
  }
41277
- function endpointRuntimeSessionPrimaryAlias(endpoint) {
41278
- const metadata = runtimeSessionMetadata(endpoint);
41279
- if (metadata.cardless === true) {
41280
- return firstStringValue(metadataStringValue(metadata, "handle"), metadataStringValue(metadata, "externalSessionId"), metadataStringValue(metadata, "threadId"), endpoint.sessionId, endpoint.agentId);
41281
- }
41282
- return firstStringValue(metadataStringValue(metadata, "externalSessionId"), metadataStringValue(metadata, "threadId"), metadataStringValue(metadata, "nativeSessionId"), metadataStringValue(metadata, "pairingSessionId"), metadataStringValue(metadata, "sessionId"), endpoint.sessionId, metadataStringValue(metadata, "runtimeSessionId"), metadataStringValue(metadata, "runtimeInstanceId"), metadataStringValue(metadata, "tmuxSession"));
41283
- }
41284
41669
  function endpointRuntimeSessionExternalId(endpoint) {
41285
41670
  const metadata = runtimeSessionMetadata(endpoint);
41286
- return firstStringValue(metadataStringValue(metadata, "externalSessionId"), metadataStringValue(metadata, "threadId"), metadataStringValue(metadata, "nativeSessionId"), metadataStringValue(metadata, "pairingSessionId"));
41671
+ return firstStringValue2(metadataStringValue2(metadata, "externalSessionId"), metadataStringValue2(metadata, "threadId"), metadataStringValue2(metadata, "nativeSessionId"), metadataStringValue2(metadata, "pairingSessionId"));
41287
41672
  }
41288
41673
  function endpointRuntimeSessionStartedAt(endpoint, fallback2) {
41289
41674
  const metadata = runtimeSessionMetadata(endpoint);
@@ -41299,16 +41684,6 @@ function endpointRuntimeSessionIsTerminal(endpoint) {
41299
41684
  function endpointRuntimeSessionState(endpoint) {
41300
41685
  return endpoint.metadata?.staleLocalRegistration === true ? "superseded" : endpoint.state;
41301
41686
  }
41302
- function endpointRuntimeSessionId(endpoint, primaryAlias) {
41303
- const key = [
41304
- endpoint.nodeId,
41305
- endpoint.agentId,
41306
- endpoint.harness,
41307
- endpoint.transport,
41308
- primaryAlias
41309
- ].join("\x00");
41310
- return `sess.${stableHash2(key)}`;
41311
- }
41312
41687
  function endpointRuntimeSessionAliases(endpoint, scoutSessionId) {
41313
41688
  const metadata = runtimeSessionMetadata(endpoint);
41314
41689
  const aliases = [
@@ -41319,12 +41694,12 @@ function endpointRuntimeSessionAliases(endpoint, scoutSessionId) {
41319
41694
  aliases.push({ alias: endpoint.sessionId.trim(), aliasKind: "endpoint_session" });
41320
41695
  }
41321
41696
  for (const entry of RUNTIME_SESSION_ALIAS_METADATA_KEYS) {
41322
- const value = metadataStringValue(metadata, entry.key);
41697
+ const value = metadataStringValue2(metadata, entry.key);
41323
41698
  if (value) {
41324
41699
  aliases.push({ alias: value, aliasKind: entry.kind });
41325
41700
  }
41326
41701
  }
41327
- const provisionalHandle = metadataStringValue(metadata, "handle");
41702
+ const provisionalHandle = metadataStringValue2(metadata, "handle");
41328
41703
  if (provisionalHandle) {
41329
41704
  aliases.push({ alias: provisionalHandle, aliasKind: "provisional" });
41330
41705
  }
@@ -41443,6 +41818,7 @@ class SQLiteControlPlaneStore {
41443
41818
  pendingEvents = [];
41444
41819
  flushPendingEventsTimer = null;
41445
41820
  conversationsApi = null;
41821
+ closed = false;
41446
41822
  constructor(dbPath) {
41447
41823
  mkdirSync7(dirname18(dbPath), { recursive: true });
41448
41824
  this.db = openControlPlaneSqliteDatabase(dbPath, { create: true });
@@ -41461,6 +41837,9 @@ class SQLiteControlPlaneStore {
41461
41837
  }
41462
41838
  });
41463
41839
  }
41840
+ get isClosed() {
41841
+ return this.closed;
41842
+ }
41464
41843
  listRuntimeSessions(options = {}) {
41465
41844
  const predicates = [];
41466
41845
  const params = [];
@@ -41717,6 +42096,10 @@ class SQLiteControlPlaneStore {
41717
42096
  };
41718
42097
  }
41719
42098
  close() {
42099
+ if (this.closed) {
42100
+ return;
42101
+ }
42102
+ this.closed = true;
41720
42103
  this.flushPendingEvents();
41721
42104
  if (this.flushPendingEventsTimer) {
41722
42105
  clearTimeout(this.flushPendingEventsTimer);
@@ -41731,16 +42114,29 @@ class SQLiteControlPlaneStore {
41731
42114
  }
41732
42115
  const nextBatch = this.pendingEvents;
41733
42116
  this.pendingEvents = [];
41734
- this.persistEventsBatch(nextBatch);
42117
+ try {
42118
+ this.persistEventsBatch(nextBatch);
42119
+ } catch (error) {
42120
+ this.pendingEvents = [...nextBatch, ...this.pendingEvents];
42121
+ throw error;
42122
+ }
41735
42123
  }
41736
- schedulePendingEventFlush() {
42124
+ schedulePendingEventFlush(delayMs = 0) {
41737
42125
  if (this.flushPendingEventsTimer) {
41738
42126
  return;
41739
42127
  }
41740
42128
  this.flushPendingEventsTimer = setTimeout(() => {
41741
42129
  this.flushPendingEventsTimer = null;
41742
- this.flushPendingEvents();
41743
- }, 0);
42130
+ try {
42131
+ this.flushPendingEvents();
42132
+ } catch (error) {
42133
+ if (isTransientSqliteBusyError(error)) {
42134
+ this.schedulePendingEventFlush(PENDING_EVENT_BUSY_RETRY_MS);
42135
+ return;
42136
+ }
42137
+ throw error;
42138
+ }
42139
+ }, delayMs);
41744
42140
  this.flushPendingEventsTimer.unref?.();
41745
42141
  }
41746
42142
  loadSnapshot() {
@@ -41886,7 +42282,7 @@ class SQLiteControlPlaneStore {
41886
42282
  audience: parseJson(row.audience_json, undefined),
41887
42283
  visibility: row.visibility,
41888
42284
  policy: row.policy,
41889
- createdAt: row.created_at,
42285
+ createdAt: normalizeTimestampMs(row.created_at) ?? row.created_at,
41890
42286
  metadata: parseJson(row.metadata_json, undefined)
41891
42287
  };
41892
42288
  }
@@ -41992,6 +42388,51 @@ class SQLiteControlPlaneStore {
41992
42388
  activeFlights: this.listConversationThreadFlights(conversation)
41993
42389
  };
41994
42390
  }
42391
+ getConversationThreadLaunchSnapshot(options) {
42392
+ const conversation = this.getConversation(options.conversationId, this.readDb);
42393
+ if (!conversation) {
42394
+ return null;
42395
+ }
42396
+ const requestedLimit = Math.floor(options.limit ?? 64);
42397
+ const limit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(64, requestedLimit)) : 64;
42398
+ const rows = queryAll(this.readDb, `SELECT
42399
+ m.id,
42400
+ m.actor_id,
42401
+ a.display_name AS actor_name,
42402
+ m.body,
42403
+ m.class,
42404
+ m.created_at
42405
+ FROM messages m
42406
+ LEFT JOIN actors a ON a.id = m.actor_id
42407
+ WHERE m.conversation_id = ?1
42408
+ ORDER BY CASE
42409
+ WHEN m.created_at > 0 AND m.created_at < ${EPOCH_MILLISECONDS_FLOOR}
42410
+ THEN m.created_at * 1000
42411
+ ELSE m.created_at
42412
+ END DESC, m.id DESC
42413
+ LIMIT ?2`, options.conversationId, limit + 1);
42414
+ const hasEarlier = rows.length > limit;
42415
+ const page = rows.slice(0, limit).reverse();
42416
+ return {
42417
+ projectionId: options.projectionId,
42418
+ projectionVersion: options.projectionVersion,
42419
+ sequence: options.sequence,
42420
+ feedId: scoutConversationFeedId(options.conversationId),
42421
+ entityKind: "scout_conversation",
42422
+ conversationId: options.conversationId,
42423
+ cursor: page[0]?.id ?? null,
42424
+ hasEarlier,
42425
+ generatedAt: options.generatedAt ?? currentTimestampMs(),
42426
+ messages: page.map((row) => ({
42427
+ id: row.id,
42428
+ actorId: row.actor_id,
42429
+ actorName: row.actor_name,
42430
+ body: row.body,
42431
+ class: row.class,
42432
+ createdAt: row.created_at
42433
+ }))
42434
+ };
42435
+ }
41995
42436
  upsertThreadCursor(conversationId, authorityNodeId, lastAppliedSeq, updatedAt) {
41996
42437
  this.db.query(`INSERT INTO thread_cursors (
41997
42438
  conversation_id, authority_node_id, last_applied_seq, updated_at
@@ -42202,12 +42643,16 @@ class SQLiteControlPlaneStore {
42202
42643
  WHERE endpoint_id = ?3`).run(expiresAt, observedAt2, endpointId);
42203
42644
  }
42204
42645
  projectRuntimeSessionForEndpoint(endpoint, observedAt2) {
42205
- const primaryAlias = endpointRuntimeSessionPrimaryAlias(endpoint);
42646
+ const primaryAlias = runtimeSessionPrimaryAlias(endpoint);
42206
42647
  if (!primaryAlias) {
42207
42648
  this.markRuntimeSessionsForEndpointEnded(endpoint.id, null, observedAt2);
42208
42649
  return;
42209
42650
  }
42210
- const sessionId = endpointRuntimeSessionId(endpoint, primaryAlias);
42651
+ const sessionId = runtimeSessionHandleForEndpoint(endpoint, primaryAlias);
42652
+ if (!sessionId) {
42653
+ this.markRuntimeSessionsForEndpointEnded(endpoint.id, null, observedAt2);
42654
+ return;
42655
+ }
42211
42656
  const lastSeenAt = endpointRuntimeSessionLastSeenAt(endpoint, observedAt2);
42212
42657
  const startedAt = endpointRuntimeSessionStartedAt(endpoint, lastSeenAt);
42213
42658
  const terminal = endpointRuntimeSessionIsTerminal(endpoint);
@@ -42557,10 +43002,23 @@ class SQLiteControlPlaneStore {
42557
43002
  targetAgentId: identity.target_agent_id
42558
43003
  };
42559
43004
  }
42560
- this.db.query(`INSERT OR REPLACE INTO flights (
43005
+ this.db.query(`INSERT INTO flights (
42561
43006
  id, invocation_id, requester_id, target_agent_id, state, summary, output, error,
42562
43007
  labels_json, metadata_json, started_at, completed_at
42563
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)`).run(recorded.id, recorded.invocationId, recorded.requesterId, recorded.targetAgentId, recorded.state, recorded.summary ?? null, recorded.output ?? null, recorded.error ?? null, stringify3(recorded.labels), stringify3(recorded.metadata), recorded.startedAt ?? null, recorded.completedAt ?? null);
43008
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
43009
+ ON CONFLICT(id) DO UPDATE SET
43010
+ rowid = (SELECT COALESCE(MAX(rowid), 0) + 1 FROM flights),
43011
+ invocation_id = excluded.invocation_id,
43012
+ requester_id = excluded.requester_id,
43013
+ target_agent_id = excluded.target_agent_id,
43014
+ state = excluded.state,
43015
+ summary = excluded.summary,
43016
+ output = excluded.output,
43017
+ error = excluded.error,
43018
+ labels_json = excluded.labels_json,
43019
+ metadata_json = excluded.metadata_json,
43020
+ started_at = excluded.started_at,
43021
+ completed_at = excluded.completed_at`).run(recorded.id, recorded.invocationId, recorded.requesterId, recorded.targetAgentId, recorded.state, recorded.summary ?? null, recorded.output ?? null, recorded.error ?? null, stringify3(recorded.labels), stringify3(recorded.metadata), recorded.startedAt ?? null, recorded.completedAt ?? null);
42564
43022
  this.db.query(`UPDATE invocations SET
42565
43023
  flight_id = ?2, state = ?3, summary = ?4, output = ?5, error = ?6,
42566
43024
  started_at = ?7, completed_at = ?8, flight_metadata_json = ?9
@@ -42740,42 +43198,48 @@ class SQLiteControlPlaneStore {
42740
43198
  }));
42741
43199
  }
42742
43200
  recordDeliveries(deliveries2) {
42743
- for (const delivery of deliveries2) {
42744
- this.drizzleDb.insert(deliveriesTable).values({
42745
- id: delivery.id,
42746
- messageId: delivery.messageId ?? null,
42747
- invocationId: delivery.invocationId ?? null,
42748
- targetId: delivery.targetId,
42749
- targetNodeId: delivery.targetNodeId ?? null,
42750
- targetKind: delivery.targetKind,
42751
- transport: delivery.transport,
42752
- reason: delivery.reason,
42753
- policy: delivery.policy,
42754
- status: delivery.status,
42755
- bindingId: delivery.bindingId ?? null,
42756
- leaseOwner: delivery.leaseOwner ?? null,
42757
- leaseExpiresAt: delivery.leaseExpiresAt ?? null,
42758
- metadataJson: stringify3(delivery.metadata),
42759
- createdAt: this.deliveryCreatedAt(delivery)
42760
- }).onConflictDoUpdate({
42761
- target: deliveriesTable.id,
42762
- set: {
42763
- messageId: delivery.messageId ?? null,
42764
- invocationId: delivery.invocationId ?? null,
42765
- targetId: delivery.targetId,
42766
- targetNodeId: delivery.targetNodeId ?? null,
42767
- targetKind: delivery.targetKind,
42768
- transport: delivery.transport,
42769
- reason: delivery.reason,
42770
- policy: delivery.policy,
42771
- status: delivery.status,
42772
- bindingId: delivery.bindingId ?? null,
42773
- leaseOwner: delivery.leaseOwner ?? null,
42774
- leaseExpiresAt: delivery.leaseExpiresAt ?? null,
42775
- metadataJson: stringify3(delivery.metadata)
42776
- }
42777
- }).run();
42778
- }
43201
+ if (deliveries2.length === 0)
43202
+ return;
43203
+ const statement = this.db.query(`INSERT INTO deliveries (
43204
+ id, message_id, invocation_id, target_id, target_node_id, target_kind,
43205
+ transport, reason, policy, status, binding_id, lease_owner, lease_expires_at,
43206
+ metadata_json, created_at
43207
+ ) VALUES (
43208
+ ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15
43209
+ )
43210
+ ON CONFLICT(id) DO UPDATE SET
43211
+ message_id = excluded.message_id,
43212
+ invocation_id = excluded.invocation_id,
43213
+ target_id = excluded.target_id,
43214
+ target_node_id = excluded.target_node_id,
43215
+ target_kind = excluded.target_kind,
43216
+ transport = excluded.transport,
43217
+ reason = excluded.reason,
43218
+ policy = excluded.policy,
43219
+ status = excluded.status,
43220
+ binding_id = excluded.binding_id,
43221
+ lease_owner = excluded.lease_owner,
43222
+ lease_expires_at = excluded.lease_expires_at,
43223
+ metadata_json = excluded.metadata_json`);
43224
+ this.db.transaction((batch) => {
43225
+ const createdAtBySource = new Map;
43226
+ for (const delivery of batch) {
43227
+ const sourceKey = delivery.messageId || delivery.invocationId ? `${delivery.messageId ?? ""}\x00${delivery.invocationId ?? ""}` : null;
43228
+ let createdAt;
43229
+ if (sourceKey === null) {
43230
+ createdAt = this.deliveryCreatedAt(delivery);
43231
+ } else {
43232
+ const cached = createdAtBySource.get(sourceKey);
43233
+ if (cached !== undefined) {
43234
+ createdAt = cached;
43235
+ } else {
43236
+ createdAt = this.deliveryCreatedAt(delivery);
43237
+ createdAtBySource.set(sourceKey, createdAt);
43238
+ }
43239
+ }
43240
+ statement.run(delivery.id, delivery.messageId ?? null, delivery.invocationId ?? null, delivery.targetId, delivery.targetNodeId ?? null, delivery.targetKind, delivery.transport, delivery.reason, delivery.policy, delivery.status, delivery.bindingId ?? null, delivery.leaseOwner ?? null, delivery.leaseExpiresAt ?? null, stringify3(delivery.metadata), createdAt);
43241
+ }
43242
+ })(deliveries2);
42779
43243
  }
42780
43244
  deliveryCreatedAt(delivery) {
42781
43245
  const messageCreatedAt = delivery.messageId ? queryGet(this.db, "SELECT created_at FROM messages WHERE id = ?1 LIMIT 1", delivery.messageId)?.created_at : null;
@@ -43280,6 +43744,17 @@ class SQLiteControlPlaneStore {
43280
43744
  get writerDb() {
43281
43745
  return this.db;
43282
43746
  }
43747
+ runReplayTransaction(operation) {
43748
+ this.db.transaction(() => {
43749
+ const previousReadDb = this.readDb;
43750
+ this.readDb = this.db;
43751
+ try {
43752
+ operation();
43753
+ } finally {
43754
+ this.readDb = previousReadDb;
43755
+ }
43756
+ })();
43757
+ }
43283
43758
  get readerDb() {
43284
43759
  return this.readDb;
43285
43760
  }
@@ -43563,7 +44038,7 @@ class SQLiteControlPlaneStore {
43563
44038
  this.schedulePendingEventFlush();
43564
44039
  }
43565
44040
  }
43566
- var RUNTIME_SESSION_RETENTION_MS, RUNTIME_SESSION_ALIAS_METADATA_KEYS;
44041
+ var PENDING_EVENT_BUSY_RETRY_MS = 25, RUNTIME_SESSION_RETENTION_MS, RUNTIME_SESSION_ALIAS_METADATA_KEYS;
43567
44042
  var init_sqlite_store = __esm(() => {
43568
44043
  init_drizzle_orm();
43569
44044
  init_node_identity();
@@ -43575,6 +44050,7 @@ var init_sqlite_store = __esm(() => {
43575
44050
  init_api();
43576
44051
  init_drizzle_schema();
43577
44052
  init_budget_observations2();
44053
+ init_runtime_session_handle();
43578
44054
  RUNTIME_SESSION_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
43579
44055
  RUNTIME_SESSION_ALIAS_METADATA_KEYS = [
43580
44056
  { key: "sessionId", kind: "metadata_session" },
@@ -43589,7 +44065,7 @@ var init_sqlite_store = __esm(() => {
43589
44065
  });
43590
44066
 
43591
44067
  // ../runtime/src/mesh-peer-auth.ts
43592
- import { createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
44068
+ import { createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
43593
44069
  function assertNoNewlines(component, name) {
43594
44070
  if (component.includes(`
43595
44071
  `) || component.includes("\r")) {
@@ -43615,7 +44091,7 @@ function peerRequestSigningPayload(input) {
43615
44091
  `);
43616
44092
  }
43617
44093
  function sha256Hex(body) {
43618
- return createHash5("sha256").update(body).digest("hex");
44094
+ return createHash6("sha256").update(body).digest("hex");
43619
44095
  }
43620
44096
  function signPeerRequest(identity, input) {
43621
44097
  const ts = input.ts ?? Date.now();
@@ -58671,14 +59147,14 @@ var init_x509_es = __esm(() => {
58671
59147
 
58672
59148
  // ../runtime/src/node-tls-identity.ts
58673
59149
  import {
58674
- createHash as createHash6,
59150
+ createHash as createHash7,
58675
59151
  createPrivateKey as createPrivateKey2,
58676
59152
  createPublicKey as createPublicKey2,
58677
59153
  generateKeyPairSync as generateKeyPairSync2,
58678
59154
  randomBytes as randomBytes3
58679
59155
  } from "crypto";
58680
59156
  function tlsSpkiFingerprintFromDer(spkiDer) {
58681
- return createHash6("sha256").update(spkiDer).digest("hex");
59157
+ return createHash7("sha256").update(spkiDer).digest("hex");
58682
59158
  }
58683
59159
  function tlsSpkiFingerprintFromCertificateDer(certificateDer) {
58684
59160
  const certificate2 = new X509Certificate(asBufferSource(certificateDer));
@@ -59302,10 +59778,12 @@ var init_mesh_dial_order = __esm(() => {
59302
59778
  });
59303
59779
 
59304
59780
  // ../runtime/src/mesh-forwarding.ts
59781
+ var MAX_PEER_AGENT_SNAPSHOT_BYTES;
59305
59782
  var init_mesh_forwarding = __esm(() => {
59306
59783
  init_iroh_bridge();
59307
59784
  init_mesh_peer_client();
59308
59785
  init_mesh_dial_order();
59786
+ MAX_PEER_AGENT_SNAPSHOT_BYTES = 2 * 1024 * 1024;
59309
59787
  });
59310
59788
 
59311
59789
  // ../runtime/src/mesh-rendezvous.ts
@@ -60639,9 +61117,9 @@ var init_eff_short_wordlist = __esm(() => {
60639
61117
  });
60640
61118
 
60641
61119
  // ../runtime/src/mesh-sas.ts
60642
- import { createHash as createHash7, createHmac } from "crypto";
61120
+ import { createHash as createHash8, createHmac } from "crypto";
60643
61121
  function createEnrollmentCommitment(nonce, cardSignature) {
60644
- return createHash7("sha256").update(Buffer.concat([nonce, Buffer.from(cardSignature, "utf8")])).digest("base64");
61122
+ return createHash8("sha256").update(Buffer.concat([nonce, Buffer.from(cardSignature, "utf8")])).digest("base64");
60645
61123
  }
60646
61124
  function verifyEnrollmentCommitment(commitment, nonce, cardSignature) {
60647
61125
  const expected = createEnrollmentCommitment(nonce, cardSignature);
@@ -62068,6 +62546,7 @@ var TERMINAL_ENDPOINT_STATES;
62068
62546
  var init_broker_endpoint_selection = __esm(() => {
62069
62547
  init_a2a_http_endpoint();
62070
62548
  init_local_agent_transports();
62549
+ init_runtime_session_handle();
62071
62550
  TERMINAL_ENDPOINT_STATES = new Set([
62072
62551
  "failed",
62073
62552
  "superseded",
@@ -64470,11 +64949,11 @@ var init_paths2 = __esm(() => {
64470
64949
 
64471
64950
  // ../runtime/src/knowledge/store.ts
64472
64951
  import { existsSync as existsSync31, mkdirSync as mkdirSync10, statSync as statSync8 } from "fs";
64473
- import { createHash as createHash8, randomUUID as randomUUID4 } from "crypto";
64952
+ import { createHash as createHash9, randomUUID as randomUUID4 } from "crypto";
64474
64953
  import { dirname as dirname21 } from "path";
64475
64954
  import { Database as Database3 } from "bun:sqlite";
64476
64955
  function ftsRowidForChunk(chunkId) {
64477
- return createHash8("sha256").update(chunkId).digest().readBigInt64BE(0);
64956
+ return createHash9("sha256").update(chunkId).digest().readBigInt64BE(0);
64478
64957
  }
64479
64958
  function stringify4(value) {
64480
64959
  return JSON.stringify(value ?? null);
@@ -64513,7 +64992,7 @@ function normalizeFtsQueryOr(value) {
64513
64992
  return terms.map(quoteFtsTerm).join(" OR ");
64514
64993
  }
64515
64994
  function textHash(value) {
64516
- return `sha256:${createHash8("sha256").update(value).digest("hex")}`;
64995
+ return `sha256:${createHash9("sha256").update(value).digest("hex")}`;
64517
64996
  }
64518
64997
  function deterministicKnowledgeChunkId(input) {
64519
64998
  return textHash([
@@ -65268,7 +65747,7 @@ var init_store = __esm(() => {
65268
65747
  });
65269
65748
 
65270
65749
  // ../runtime/src/knowledge/session-indexer.ts
65271
- import { createHash as createHash9 } from "crypto";
65750
+ import { createHash as createHash10 } from "crypto";
65272
65751
  import {
65273
65752
  createReadStream as createReadStream2,
65274
65753
  existsSync as existsSync32,
@@ -65382,7 +65861,7 @@ function discoverRecentSessionFiles(cutoffMs, limit, harnessFilter) {
65382
65861
  async function parseJsonl(file) {
65383
65862
  const maybeYield = createYieldBudget();
65384
65863
  const records = [];
65385
- const hash = createHash9("sha256");
65864
+ const hash = createHash10("sha256");
65386
65865
  let carry = "";
65387
65866
  let offset = 0;
65388
65867
  let index2 = 0;
@@ -65713,10 +66192,10 @@ function trimOneLine(value, max) {
65713
66192
  return flat.length <= max ? flat : `${flat.slice(0, Math.max(0, max - 3))}...`;
65714
66193
  }
65715
66194
  function hashText(value) {
65716
- return `sha256:${createHash9("sha256").update(value).digest("hex")}`;
66195
+ return `sha256:${createHash10("sha256").update(value).digest("hex")}`;
65717
66196
  }
65718
66197
  function stableId(value, length = 16) {
65719
- return createHash9("sha256").update(value).digest("hex").slice(0, length);
66198
+ return createHash10("sha256").update(value).digest("hex").slice(0, length);
65720
66199
  }
65721
66200
  function portablePath(filePath) {
65722
66201
  const home = homedir27();
@@ -66568,7 +67047,7 @@ var init_sender = __esm(() => {
66568
67047
  // ../../apps/desktop/src/core/broker/service.ts
66569
67048
  import { mkdir as mkdir11, readFile as readFile15, unlink, writeFile as writeFile10 } from "fs/promises";
66570
67049
  import { basename as basename12, join as join39, resolve as resolve17 } from "path";
66571
- import { createHash as createHash10, randomUUID as randomUUID5 } from "crypto";
67050
+ import { createHash as createHash11, randomUUID as randomUUID5 } from "crypto";
66572
67051
  function relayHubDirectory() {
66573
67052
  return resolveOpenScoutSupportPaths().relayHubDirectory;
66574
67053
  }
@@ -66660,7 +67139,7 @@ function deliveryRequestId(clientMessageId, createdAt) {
66660
67139
  if (!normalized) {
66661
67140
  return `deliver-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
66662
67141
  }
66663
- const digest = createHash10("sha256").update(normalized).digest("hex").slice(0, 32);
67142
+ const digest = createHash11("sha256").update(normalized).digest("hex").slice(0, 32);
66664
67143
  return `deliver-client-${digest}`;
66665
67144
  }
66666
67145
  function metadataStringList(metadata, key) {
@@ -68040,10 +68519,6 @@ async function ensureBrokerDirectConversationBetween(baseUrl, snapshot, nodeId,
68040
68519
  };
68041
68520
  }
68042
68521
  async function sendScoutMessage(input) {
68043
- const broker2 = await loadScoutBrokerContext();
68044
- if (!broker2) {
68045
- return { usedBroker: false, invokedTargets: [], unresolvedTargets: [] };
68046
- }
68047
68522
  const currentDirectory = input.currentDirectory ?? process.cwd();
68048
68523
  const source = input.source?.trim() || "scout-cli";
68049
68524
  const wake = input.wake ?? false;
@@ -68056,15 +68531,19 @@ async function sendScoutMessage(input) {
68056
68531
  source,
68057
68532
  sourceIntent: "tell_wake"
68058
68533
  } : undefined;
68059
- const senderId = await resolveConversationActorId(broker2.baseUrl, broker2.snapshot, broker2.node.id, input.senderId, currentDirectory);
68060
68534
  const requestedTargetLabel = input.targetLabel?.trim();
68061
68535
  const requestedTargetRef = input.targetRef?.trim();
68062
68536
  if (requestedTargetLabel || requestedTargetRef) {
68537
+ const health = await readScoutBrokerHealth();
68538
+ if (!health.reachable || !health.ok || !health.nodeId) {
68539
+ return { usedBroker: false, invokedTargets: [], unresolvedTargets: [] };
68540
+ }
68541
+ const senderId2 = await resolveConversationActorId(health.baseUrl, createRuntimeRegistrySnapshot(), health.nodeId, input.senderId, currentDirectory);
68063
68542
  const { target: target2, renderedTarget, isBindingRef } = routeTargetForTargetLabel(requestedTargetLabel ?? "", requestedTargetRef, input.aliasScope);
68064
- const delivery = await brokerPostDeliver(broker2.baseUrl, {
68543
+ const delivery = await brokerPostDeliver(health.baseUrl, {
68065
68544
  caller: {
68066
- actorId: senderId,
68067
- nodeId: broker2.node.id,
68545
+ actorId: senderId2,
68546
+ nodeId: health.nodeId,
68068
68547
  currentDirectory,
68069
68548
  metadata: { source }
68070
68549
  },
@@ -68107,6 +68586,11 @@ async function sendScoutMessage(input) {
68107
68586
  routeKind: delivery.routeKind
68108
68587
  };
68109
68588
  }
68589
+ const broker2 = await loadScoutBrokerContext();
68590
+ if (!broker2) {
68591
+ return { usedBroker: false, invokedTargets: [], unresolvedTargets: [] };
68592
+ }
68593
+ const senderId = await resolveConversationActorId(broker2.baseUrl, broker2.snapshot, broker2.node.id, input.senderId, currentDirectory);
68110
68594
  const mentionResolution = await resolveMentionTargets(broker2.snapshot, input.body, currentDirectory);
68111
68595
  const selectors = extractAgentSelectors(input.body);
68112
68596
  if (selectors.length === 1 && mentionResolution.resolved.length + mentionResolution.unresolved.length + mentionResolution.ambiguous.length === 1) {
@@ -69582,6 +70066,7 @@ var init_service3 = __esm(() => {
69582
70066
  init_broker_api();
69583
70067
  init_broker_process_manager();
69584
70068
  init_local_agents();
70069
+ init_registry3();
69585
70070
  init_support_paths();
69586
70071
  init_user_config();
69587
70072
  init_paths();
@@ -70509,6 +70994,12 @@ function describeStopStep(step) {
70509
70994
  return `sweep supervised stragglers (${step.pids.length} candidate${step.pids.length === 1 ? "" : "s"})`;
70510
70995
  }
70511
70996
  }
70997
+ function ownedSweepSurvivorPids(step, tree) {
70998
+ const candidates = new Set(step.pids);
70999
+ const stillOwned = layerProcesses(tree, step.layers);
71000
+ const reparentedFromThisCheckout = detachedExpectedProcesses(tree).filter((entry) => step.layers.includes(entry.layer));
71001
+ return [...stillOwned, ...reparentedFromThisCheckout].map((entry) => entry.pid).filter((pid) => candidates.has(pid));
71002
+ }
70512
71003
  function processAlive(pid) {
70513
71004
  try {
70514
71005
  process.kill(pid, 0);
@@ -70578,6 +71069,9 @@ function bootstrapLaunchdJob(plistPath, uid) {
70578
71069
  const ok = (result.status ?? 1) === 0 || /already bootstrapped|Operation already in progress/i.test(detail);
70579
71070
  return { ok, detail };
70580
71071
  }
71072
+ function scoutdStartArguments(options = {}) {
71073
+ return options.waitForHealth === false ? ["start", "--no-wait"] : ["start"];
71074
+ }
70581
71075
  function chooseLaunchdStartMethod(input) {
70582
71076
  if (input.scoutdPath)
70583
71077
  return "scoutd";
@@ -70597,7 +71091,7 @@ function startLaunchdJob(label, uid, home, options = {}) {
70597
71091
  case "kickstart":
70598
71092
  return { ...kickstartLaunchdJob(label, uid, { restart: options.restart }), method };
70599
71093
  case "scoutd": {
70600
- const result = spawnSync4(scoutdPath, ["start"], { encoding: "utf8" });
71094
+ const result = spawnSync4(scoutdPath, scoutdStartArguments(options), { encoding: "utf8" });
70601
71095
  const detail = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
70602
71096
  return { ok: (result.status ?? 1) === 0, detail, method };
70603
71097
  }
@@ -70692,6 +71186,7 @@ var init_app_lifecycle = __esm(() => {
70692
71186
  // ../../apps/desktop/src/cli/commands/app.ts
70693
71187
  var exports_app = {};
70694
71188
  __export(exports_app, {
71189
+ startTreeReady: () => startTreeReady,
70695
71190
  selectInstalledAppBundle: () => selectInstalledAppBundle,
70696
71191
  runAppCommand: () => runAppCommand,
70697
71192
  renderAppCommandHelp: () => renderAppCommandHelp,
@@ -70828,6 +71323,13 @@ async function waitFor(paths2, predicate, timeoutMs) {
70828
71323
  }
70829
71324
  return tree;
70830
71325
  }
71326
+ function startTreeReady(tree, scope) {
71327
+ const appsReady = tree.layers.app.length > 0 && tree.layers.menu.length > 0;
71328
+ if (!appsReady || scope === "apps") {
71329
+ return appsReady;
71330
+ }
71331
+ return SUPERVISED_LAYERS.every((layer) => tree.layers[layer].length > 0);
71332
+ }
70831
71333
  async function stopSuite(paths2, steps, scope) {
70832
71334
  const tree = readTree(paths2);
70833
71335
  const uid = process.getuid?.() ?? 0;
@@ -70838,7 +71340,12 @@ async function stopSuite(paths2, steps, scope) {
70838
71340
  steps.push(result.ok ? label : `${label} \u2014 failed: ${result.detail || "unknown error"}`);
70839
71341
  continue;
70840
71342
  }
70841
- const { escalated, survivors } = await terminateProcesses(step.pids);
71343
+ let pids = step.pids;
71344
+ if (step.kind === "sweep") {
71345
+ const drainedTree = await waitFor(paths2, (candidate) => ownedSweepSurvivorPids(step, candidate).length === 0, SUPERVISED_DRAIN_TIMEOUT_MS);
71346
+ pids = ownedSweepSurvivorPids(step, drainedTree);
71347
+ }
71348
+ const { escalated, survivors } = await terminateProcesses(pids);
70842
71349
  const notes = [];
70843
71350
  if (escalated.length > 0)
70844
71351
  notes.push(`escalated to SIGKILL: ${escalated.join(", ")}`);
@@ -70851,22 +71358,27 @@ async function stopSuite(paths2, steps, scope) {
70851
71358
  }
70852
71359
  async function startSuite(paths2, steps, scope, options = {}) {
70853
71360
  const uid = process.getuid?.() ?? 0;
71361
+ let supervisedReadiness = null;
70854
71362
  if (scope === "apps") {
70855
71363
  steps.push("skip launchd (--apps-only)");
70856
71364
  } else {
70857
71365
  const label = resolveLaunchdLabel();
70858
71366
  const launched = startLaunchdJob(label, uid, homedir28(), {
70859
71367
  restart: options.restart,
70860
- serviceRoot: paths2.serviceRoot
71368
+ serviceRoot: paths2.serviceRoot,
71369
+ waitForHealth: false
70861
71370
  });
70862
71371
  steps.push(launched.ok ? `${launched.method} ${label}` : `${launched.method} ${label} \u2014 failed: ${launched.detail || "unknown error"}`);
70863
- const supervised = await waitFor(paths2, (tree2) => SUPERVISED_LAYERS.every((layer) => tree2.layers[layer].length > 0), READY_TIMEOUT_MS);
70864
- const missing = SUPERVISED_LAYERS.filter((layer) => supervised.layers[layer].length === 0);
70865
- steps.push(missing.length === 0 ? "supervised tree ready" : `supervised tree incomplete \u2014 missing ${missing.join(", ")}`);
71372
+ supervisedReadiness = waitFor(paths2, (tree2) => SUPERVISED_LAYERS.every((layer) => tree2.layers[layer].length > 0), SUPERVISED_READY_TIMEOUT_MS);
70866
71373
  }
70867
71374
  const open = spawnSync5("open", [paths2.appBundlePath], { encoding: "utf8" });
70868
71375
  steps.push((open.status ?? 1) === 0 ? `open ${paths2.appBundlePath}` : `open ${paths2.appBundlePath} \u2014 failed: ${(open.stderr ?? "").trim() || "unknown error"}`);
70869
- const tree = await waitFor(paths2, (candidate) => candidate.layers.app.length > 0 && candidate.layers.menu.length > 0, READY_TIMEOUT_MS);
71376
+ if (supervisedReadiness) {
71377
+ const supervised = await supervisedReadiness;
71378
+ const missing = SUPERVISED_LAYERS.filter((layer) => supervised.layers[layer].length === 0);
71379
+ steps.push(missing.length === 0 ? "supervised tree ready" : `supervised tree still starting \u2014 missing ${missing.join(", ")}`);
71380
+ }
71381
+ const tree = await waitFor(paths2, (candidate) => startTreeReady(candidate, scope), READY_TIMEOUT_MS);
70870
71382
  const appsMissing = LAUNCH_SERVICES_LAYERS.filter((layer) => layer !== "pairing" && tree.layers[layer].length === 0);
70871
71383
  steps.push(appsMissing.length === 0 ? "Scout and embedded ScoutMenu ready" : `apps incomplete \u2014 missing ${appsMissing.join(", ")}`);
70872
71384
  return tree;
@@ -70992,7 +71504,7 @@ async function runAppCommand(context, args) {
70992
71504
  throw new ScoutCliError(result.message);
70993
71505
  }
70994
71506
  }
70995
- var HELP_FLAGS2, INSTALLED_APP_BUNDLE_ID = "app.openscout.scout", INSTALLED_APP_BUNDLE_NAME = "OpenScout.app", READY_TIMEOUT_MS = 30000, POLL_MS = 250;
71507
+ var HELP_FLAGS2, INSTALLED_APP_BUNDLE_ID = "app.openscout.scout", INSTALLED_APP_BUNDLE_NAME = "OpenScout.app", READY_TIMEOUT_MS = 30000, SUPERVISED_READY_TIMEOUT_MS = 120000, SUPERVISED_DRAIN_TIMEOUT_MS = 20000, POLL_MS = 250;
70996
71508
  var init_app = __esm(() => {
70997
71509
  init_app_lifecycle();
70998
71510
  init_context();
@@ -100958,7 +101470,7 @@ var init_stdio2 = __esm(() => {
100958
101470
  // ../../apps/desktop/src/shared/product.ts
100959
101471
  var SCOUT_APP_VERSION;
100960
101472
  var init_product = __esm(() => {
100961
- SCOUT_APP_VERSION = process.env.SCOUT_APP_VERSION?.trim() || "0.2.95";
101473
+ SCOUT_APP_VERSION = process.env.SCOUT_APP_VERSION?.trim() || "0.2.97";
100962
101474
  });
100963
101475
 
100964
101476
  // ../../apps/desktop/src/core/mcp/stdio-server-lifecycle.ts
@@ -102491,7 +103003,13 @@ function renderOpenScoutCaddyfile(config2) {
102491
103003
  const blocks = schemes.flatMap((scheme) => config2.routes.map((route) => route.mode === "vite-dev" ? renderCaddyViteDevRouteBlock(route, scheme) : renderCaddyDefaultRouteBlock(config2, route, scheme, startPage))).join(`
102492
103004
 
102493
103005
  `);
102494
- return `${blocks}
103006
+ return `{
103007
+ log {
103008
+ level WARN
103009
+ }
103010
+ }
103011
+
103012
+ ${blocks}
102495
103013
  `;
102496
103014
  }
102497
103015
  var init_local_edge = __esm(() => {
@@ -104741,11 +105259,11 @@ function buildFlightCommandResult(flight, env, options = {}) {
104741
105259
  };
104742
105260
  }
104743
105261
  function buildFlightFollowHandles(flight, env) {
104744
- const conversationId = metadataStringValue2(flight.metadata, "conversationId") ?? metadataReturnAddressStringValue(flight.metadata, "conversationId");
104745
- const messageId = metadataStringValue2(flight.metadata, "messageId") ?? metadataReturnAddressStringValue(flight.metadata, "replyToMessageId");
104746
- const workId = metadataStringValue2(flight.metadata, "workId") ?? metadataStringValue2(flight.metadata, "collaborationRecordId");
104747
- const sessionId = metadataStringValue2(flight.metadata, "sessionId") ?? metadataDispatchAckStringValue(flight.metadata, "sessionId");
104748
- const bindingRef = metadataStringValue2(flight.metadata, "bindingRef") ?? flight.id.slice(-8);
105262
+ const conversationId = metadataStringValue3(flight.metadata, "conversationId") ?? metadataReturnAddressStringValue(flight.metadata, "conversationId");
105263
+ const messageId = metadataStringValue3(flight.metadata, "messageId") ?? metadataReturnAddressStringValue(flight.metadata, "replyToMessageId");
105264
+ const workId = metadataStringValue3(flight.metadata, "workId") ?? metadataStringValue3(flight.metadata, "collaborationRecordId");
105265
+ const sessionId = metadataStringValue3(flight.metadata, "sessionId") ?? metadataDispatchAckStringValue(flight.metadata, "sessionId");
105266
+ const bindingRef = metadataStringValue3(flight.metadata, "bindingRef") ?? flight.id.slice(-8);
104749
105267
  const origin = resolveScoutWebOrigin(env);
104750
105268
  const links = buildFlightLinks(origin, {
104751
105269
  flightId: flight.id,
@@ -104833,7 +105351,7 @@ function isFlightWaitTimeout(error48) {
104833
105351
  function isTerminalFlightState(state) {
104834
105352
  return state === "completed" || state === "failed" || state === "cancelled";
104835
105353
  }
104836
- function metadataStringValue2(metadata, key) {
105354
+ function metadataStringValue3(metadata, key) {
104837
105355
  const value = metadata?.[key];
104838
105356
  return typeof value === "string" && value.trim() ? value.trim() : null;
104839
105357
  }
@@ -104841,13 +105359,13 @@ function metadataReturnAddressStringValue(metadata, key) {
104841
105359
  const value = metadata?.["returnAddress"];
104842
105360
  if (!value || typeof value !== "object" || Array.isArray(value))
104843
105361
  return null;
104844
- return metadataStringValue2(value, key);
105362
+ return metadataStringValue3(value, key);
104845
105363
  }
104846
105364
  function metadataDispatchAckStringValue(metadata, key) {
104847
105365
  const value = metadata?.["dispatchAck"];
104848
105366
  if (!value || typeof value !== "object" || Array.isArray(value))
104849
105367
  return null;
104850
- return metadataStringValue2(value, key);
105368
+ return metadataStringValue3(value, key);
104851
105369
  }
104852
105370
  function trimTrailingSlash(value) {
104853
105371
  return value.replace(/\/+$/g, "");
@@ -105019,16 +105537,34 @@ var init_inbox2 = __esm(() => {
105019
105537
  // ../../apps/desktop/src/cli/commands/install.ts
105020
105538
  var exports_install = {};
105021
105539
  __export(exports_install, {
105540
+ verifyStagedOpenScoutApp: () => verifyStagedOpenScoutApp,
105541
+ verifyDownloadedDmg: () => verifyDownloadedDmg,
105542
+ verifyDownloadedAsset: () => verifyDownloadedAsset,
105022
105543
  runInstallCommand: () => runInstallCommand,
105023
105544
  renderInstallCommandHelp: () => renderInstallCommandHelp,
105545
+ processIdsForMenuOutsideApp: () => processIdsForMenuOutsideApp,
105546
+ processIdsForInstalledApp: () => processIdsForInstalledApp,
105024
105547
  parseInstallArgs: () => parseInstallArgs,
105548
+ parseGithubAssetDigest: () => parseGithubAssetDigest,
105025
105549
  findAppDmgAsset: () => findAppDmgAsset,
105550
+ OPENSCOUT_SIGNING_TEAM_ID: () => OPENSCOUT_SIGNING_TEAM_ID,
105026
105551
  OPENSCOUT_RELEASE_REPOSITORY: () => OPENSCOUT_RELEASE_REPOSITORY,
105027
- OPENSCOUT_RELEASE_OWNER: () => OPENSCOUT_RELEASE_OWNER
105552
+ OPENSCOUT_RELEASE_OWNER: () => OPENSCOUT_RELEASE_OWNER,
105553
+ OPENSCOUT_APP_NAME: () => OPENSCOUT_APP_NAME,
105554
+ OPENSCOUT_APP_BUNDLE_ID: () => OPENSCOUT_APP_BUNDLE_ID,
105555
+ DEFAULT_OPENSCOUT_APP_PATH: () => DEFAULT_OPENSCOUT_APP_PATH
105028
105556
  });
105029
105557
  import { spawnSync as spawnSync8 } from "child_process";
105030
- import { existsSync as existsSync39, mkdtempSync, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "fs";
105031
- import { tmpdir } from "os";
105558
+ import { createHash as createHash12 } from "crypto";
105559
+ import {
105560
+ existsSync as fsExistsSync,
105561
+ mkdirSync as fsMkdirSync,
105562
+ mkdtempSync as fsMkdtempSync,
105563
+ renameSync as fsRenameSync,
105564
+ rmSync as fsRmSync,
105565
+ writeFileSync as fsWriteFileSync
105566
+ } from "fs";
105567
+ import { tmpdir as osTmpdir } from "os";
105032
105568
  import { join as join48 } from "path";
105033
105569
  function renderInstallCommandHelp() {
105034
105570
  return [
@@ -105043,11 +105579,16 @@ function renderInstallCommandHelp() {
105043
105579
  "",
105044
105580
  "Behavior:",
105045
105581
  " Downloads the signed + notarized OpenScout.dmg from the GitHub release,",
105046
- " installs OpenScout.app (which embeds the menu bar helper) to /Applications,",
105047
- " and clears the Gatekeeper quarantine flag so it opens cleanly.",
105582
+ " verifies the published byte size and sha256 digest when GitHub provides one,",
105583
+ " then codesign and Gatekeeper-assess the DMG before mounting. OpenScout.app",
105584
+ " must match the pinned bundle id and Team ID, pass codesign --deep --strict,",
105585
+ " and pass Gatekeeper execute after staging. A running copy of the installed",
105586
+ " app and any stale ScoutMenu helpers from other checkouts are stopped first;",
105587
+ " replacement is staged and rolled back on failure.",
105588
+ " Quarantine attributes are not cleared.",
105048
105589
  "",
105049
- " The app drives the bundled local runtime from the global scout CLI, so",
105050
- " `bun add -g @openscout/scout` is the companion install for the command line."
105590
+ " The app uses the local scout CLI for the bundled runtime. Install the CLI",
105591
+ " with `bun add -g @openscout/scout`."
105051
105592
  ].join(`
105052
105593
  `);
105053
105594
  }
@@ -105085,11 +105626,19 @@ function parseInstallArgs(args) {
105085
105626
  continue;
105086
105627
  }
105087
105628
  if (arg.startsWith("--version=")) {
105088
- options.version = arg.slice("--version=".length);
105629
+ const value = arg.slice("--version=".length);
105630
+ if (!value) {
105631
+ throw new ScoutCliError("--version requires a release tag (e.g. --version v0.2.70)");
105632
+ }
105633
+ options.version = value;
105089
105634
  continue;
105090
105635
  }
105091
105636
  if (arg.startsWith("--tag=")) {
105092
- options.version = arg.slice("--tag=".length);
105637
+ const value = arg.slice("--tag=".length);
105638
+ if (!value) {
105639
+ throw new ScoutCliError("--tag requires a release tag (e.g. --tag v0.2.70)");
105640
+ }
105641
+ options.version = value;
105093
105642
  continue;
105094
105643
  }
105095
105644
  throw new ScoutCliError(`unknown option for install: ${arg} (try: scout install --help)`);
@@ -105100,6 +105649,13 @@ function stripLeadingV(tag) {
105100
105649
  const trimmed = tag.trim();
105101
105650
  return trimmed.startsWith("v") ? trimmed.slice(1) : trimmed;
105102
105651
  }
105652
+ function normalizeReleaseVersion(tag) {
105653
+ const version3 = stripLeadingV(tag);
105654
+ if (!/^\d+\.\d+\.\d+$/.test(version3)) {
105655
+ throw new ScoutCliError(`unsupported OpenScout release tag: ${tag}`);
105656
+ }
105657
+ return version3;
105658
+ }
105103
105659
  function formatBytes(bytes) {
105104
105660
  if (bytes <= 0)
105105
105661
  return "unknown size";
@@ -105107,20 +105663,130 @@ function formatBytes(bytes) {
105107
105663
  return `${(bytes / 1024).toFixed(0)} KB`;
105108
105664
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
105109
105665
  }
105110
- function getInstalledVersion() {
105111
- if (!existsSync39(INFO_PLIST_PATH))
105666
+ function combinedOutput(result) {
105667
+ return `${result.stdout ?? ""}
105668
+ ${result.stderr ?? ""}`;
105669
+ }
105670
+ function permissionDeniedMessage(destination, detail) {
105671
+ const suffix = detail?.trim() ? `: ${detail.trim()}` : ".";
105672
+ return `could not replace ${destination} (permission denied)${suffix} Grant write access to the destination and retry.`;
105673
+ }
105674
+ function isPermissionError(error48, result) {
105675
+ const code = error48?.code;
105676
+ if (code === "EACCES" || code === "EPERM")
105677
+ return true;
105678
+ const output = `${error48 instanceof Error ? error48.message : ""} ${result?.stderr ?? ""} ${result?.stdout ?? ""}`.toLowerCase();
105679
+ return output.includes("permission denied") || output.includes("operation not permitted");
105680
+ }
105681
+ function defaultRun(command, args) {
105682
+ const result = spawnSync8(command, [...args], { encoding: "utf8" });
105683
+ return {
105684
+ status: result.status,
105685
+ stdout: result.stdout ?? "",
105686
+ stderr: result.stderr ?? "",
105687
+ error: result.error
105688
+ };
105689
+ }
105690
+ function defaultSleep(ms) {
105691
+ Bun.sleepSync(ms);
105692
+ }
105693
+ function resolveInstallDependencies(dependencies = {}) {
105694
+ return {
105695
+ platform: dependencies.platform ?? process.platform,
105696
+ fetch: dependencies.fetch ?? fetch,
105697
+ run: dependencies.run ?? defaultRun,
105698
+ existsSync: dependencies.existsSync ?? fsExistsSync,
105699
+ mkdirSync: dependencies.mkdirSync ?? ((path, options) => {
105700
+ fsMkdirSync(path, options);
105701
+ }),
105702
+ mkdtempSync: dependencies.mkdtempSync ?? fsMkdtempSync,
105703
+ writeFileSync: dependencies.writeFileSync ?? ((path, data) => {
105704
+ fsWriteFileSync(path, data);
105705
+ }),
105706
+ rmSync: dependencies.rmSync ?? fsRmSync,
105707
+ renameSync: dependencies.renameSync ?? fsRenameSync,
105708
+ tmpdir: dependencies.tmpdir ?? osTmpdir,
105709
+ sleep: dependencies.sleep ?? defaultSleep,
105710
+ appPath: dependencies.appPath ?? DEFAULT_OPENSCOUT_APP_PATH
105711
+ };
105712
+ }
105713
+ function requireSuccess(result, message) {
105714
+ if (result.error && result.error.code === "ENOENT") {
105715
+ throw new ScoutCliError(`${message}: ${result.error.message}`);
105716
+ }
105717
+ if ((result.status ?? 1) !== 0) {
105718
+ throw new ScoutCliError(`${message}: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.status ?? "unknown"}`}`);
105719
+ }
105720
+ return result;
105721
+ }
105722
+ function parseGithubAssetDigest(digest) {
105723
+ const trimmed = digest.trim();
105724
+ const separator = trimmed.indexOf(":");
105725
+ if (separator <= 0) {
105726
+ throw new ScoutCliError(`unsupported GitHub asset digest: ${digest}`);
105727
+ }
105728
+ const algorithm = trimmed.slice(0, separator).trim().toLowerCase();
105729
+ const hex4 = trimmed.slice(separator + 1).trim().toLowerCase();
105730
+ if (algorithm !== GITHUB_ASSET_DIGEST_ALGORITHM || !/^[0-9a-f]+$/.test(hex4) || hex4.length !== GITHUB_ASSET_DIGEST_HEX_LENGTH) {
105731
+ throw new ScoutCliError(`unsupported GitHub asset digest: ${digest}`);
105732
+ }
105733
+ return { algorithm, hex: hex4 };
105734
+ }
105735
+ function verifyDownloadedAsset(buffer, asset) {
105736
+ if (typeof asset.size !== "number" || !Number.isFinite(asset.size) || asset.size < 0) {
105737
+ throw new ScoutCliError(`release asset ${asset.name} is missing an exact byte size`);
105738
+ }
105739
+ if (buffer.byteLength !== asset.size) {
105740
+ throw new ScoutCliError(`download size mismatch for ${asset.name}: expected ${asset.size} bytes, got ${buffer.byteLength}`);
105741
+ }
105742
+ if (buffer.byteLength === 0) {
105743
+ throw new ScoutCliError("download failed: empty response body");
105744
+ }
105745
+ const digest = asset.digest?.trim();
105746
+ if (!digest)
105747
+ return;
105748
+ const parsed = parseGithubAssetDigest(digest);
105749
+ let actual;
105750
+ try {
105751
+ actual = createHash12(parsed.algorithm).update(buffer).digest("hex");
105752
+ } catch {
105753
+ throw new ScoutCliError(`unsupported GitHub asset digest algorithm: ${parsed.algorithm}`);
105754
+ }
105755
+ if (actual !== parsed.hex) {
105756
+ throw new ScoutCliError(`download digest mismatch for ${asset.name}: expected ${digest}, got ${parsed.algorithm}:${actual}`);
105757
+ }
105758
+ }
105759
+ function findAppDmgAsset(release) {
105760
+ const assets = release.assets ?? [];
105761
+ const versionedName = `OpenScout-${normalizeReleaseVersion(release.tag_name)}.dmg`;
105762
+ const versioned = assets.find((asset) => asset.name === versionedName);
105763
+ if (versioned)
105764
+ return versioned;
105765
+ const latestAlias = assets.find((asset) => asset.name === LATEST_DMG_ALIAS);
105766
+ if (latestAlias)
105767
+ return latestAlias;
105768
+ throw new ScoutCliError(`no OpenScout.app DMG found in release ${release.tag_name}. Assets: ${assets.map((asset) => asset.name).join(", ") || "(none)"}`);
105769
+ }
105770
+ function infoPlistPath(appPath) {
105771
+ return join48(appPath, "Contents", "Info.plist");
105772
+ }
105773
+ function readPlistString(deps, plistPath, key) {
105774
+ if (!deps.existsSync(plistPath))
105112
105775
  return null;
105113
- const result = spawnSync8("defaults", ["read", INFO_PLIST_PATH, "CFBundleShortVersionString"], { encoding: "utf8" });
105776
+ const result = deps.run("plutil", ["-extract", key, "raw", "-o", "-", plistPath]);
105114
105777
  if ((result.status ?? 1) !== 0)
105115
105778
  return null;
105116
- const version3 = result.stdout.trim();
105117
- return version3 || null;
105779
+ const value = result.stdout.trim();
105780
+ return value || null;
105118
105781
  }
105119
- async function fetchRelease(version3) {
105782
+ function getInstalledVersion(deps) {
105783
+ return readPlistString(deps, infoPlistPath(deps.appPath), "CFBundleShortVersionString");
105784
+ }
105785
+ async function fetchRelease(version3, deps) {
105120
105786
  const base = `https://api.github.com/repos/${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY}/releases`;
105121
- const apiUrl = version3 ? `${base}/tags/${version3}` : `${base}/latest`;
105787
+ const apiUrl = version3 ? `${base}/tags/${encodeURIComponent(version3)}` : `${base}/latest`;
105122
105788
  try {
105123
- const response = await fetch(apiUrl, {
105789
+ const response = await deps.fetch(apiUrl, {
105124
105790
  headers: { Accept: "application/vnd.github+json", "User-Agent": USER_AGENT }
105125
105791
  });
105126
105792
  if (response.ok) {
@@ -105128,30 +105794,15 @@ async function fetchRelease(version3) {
105128
105794
  }
105129
105795
  } catch {}
105130
105796
  const apiPath = version3 ? `repos/${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY}/releases/tags/${version3}` : `repos/${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY}/releases/latest`;
105131
- const gh = spawnSync8("gh", ["api", apiPath], { encoding: "utf8" });
105797
+ const gh = deps.run("gh", ["api", apiPath]);
105132
105798
  if ((gh.status ?? 1) === 0 && gh.stdout.trim()) {
105133
105799
  return JSON.parse(gh.stdout);
105134
105800
  }
105135
105801
  throw new ScoutCliError(version3 ? `release "${version3}" not found on GitHub (${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY})` : `could not fetch the latest OpenScout release from GitHub (${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY})`);
105136
105802
  }
105137
- function findAppDmgAsset(release) {
105138
- const assets = release.assets ?? [];
105139
- const isMenuOnly = (name2) => name2.toLowerCase().startsWith("openscoutmenu");
105140
- const isDmg = (name2) => name2.toLowerCase().endsWith(".dmg");
105141
- const versioned = assets.find((asset) => /^openscout-\d/i.test(asset.name) && isDmg(asset.name) && !isMenuOnly(asset.name));
105142
- if (versioned)
105143
- return versioned;
105144
- const latestAlias = assets.find((asset) => asset.name.toLowerCase() === "openscout.dmg");
105145
- if (latestAlias)
105146
- return latestAlias;
105147
- const anyProductDmg = assets.find((asset) => isDmg(asset.name) && !isMenuOnly(asset.name));
105148
- if (anyProductDmg)
105149
- return anyProductDmg;
105150
- throw new ScoutCliError(`no OpenScout.app DMG found in release ${release.tag_name}. Assets: ${assets.map((asset) => asset.name).join(", ") || "(none)"}`);
105151
- }
105152
- async function downloadDmg(asset, destination, context) {
105803
+ async function downloadDmg(asset, destination, context, deps) {
105153
105804
  context.stderr(`Downloading ${asset.name} (${formatBytes(asset.size)})\u2026`);
105154
- const response = await fetch(asset.browser_download_url, {
105805
+ const response = await deps.fetch(asset.browser_download_url, {
105155
105806
  redirect: "follow",
105156
105807
  headers: { "User-Agent": USER_AGENT }
105157
105808
  });
@@ -105159,61 +105810,298 @@ async function downloadDmg(asset, destination, context) {
105159
105810
  throw new ScoutCliError(`download failed: HTTP ${response.status} for ${asset.browser_download_url}`);
105160
105811
  }
105161
105812
  const buffer = Buffer.from(await response.arrayBuffer());
105162
- if (buffer.length === 0) {
105163
- throw new ScoutCliError("download failed: empty response body");
105813
+ verifyDownloadedAsset(buffer, asset);
105814
+ deps.writeFileSync(destination, buffer);
105815
+ }
105816
+ function mountDmg(dmgPath, mountPoint, deps) {
105817
+ deps.mkdirSync(mountPoint, { recursive: true });
105818
+ const result = deps.run("hdiutil", [
105819
+ "attach",
105820
+ dmgPath,
105821
+ "-mountpoint",
105822
+ mountPoint,
105823
+ "-nobrowse",
105824
+ "-readonly",
105825
+ "-noautoopen"
105826
+ ]);
105827
+ if ((result.status ?? 1) !== 0) {
105828
+ throw new ScoutCliError(`could not mount ${dmgPath}: ${result.stderr.trim() || result.stdout.trim() || "hdiutil attach failed"}`);
105164
105829
  }
105165
- writeFileSync9(destination, buffer);
105166
105830
  }
105167
- function mountDmg(dmgPath) {
105168
- const result = spawnSync8("hdiutil", ["attach", dmgPath, "-nobrowse", "-noverify", "-noautoopen", "-plist"], { encoding: "utf8" });
105169
- if ((result.status ?? 1) !== 0) {
105170
- throw new ScoutCliError(`could not mount ${dmgPath}: ${result.stderr.trim() || "hdiutil attach failed"}`);
105831
+ function unmountDmg(mountPoint, deps) {
105832
+ const detached = deps.run("hdiutil", ["detach", mountPoint, "-quiet"]);
105833
+ if ((detached.status ?? 1) === 0)
105834
+ return;
105835
+ deps.run("hdiutil", ["detach", mountPoint, "-force", "-quiet"]);
105836
+ }
105837
+ function requireMountedOpenScoutApp(mountPoint, deps) {
105838
+ const source = join48(mountPoint, OPENSCOUT_APP_NAME);
105839
+ if (!deps.existsSync(source) || !deps.existsSync(infoPlistPath(source))) {
105840
+ throw new ScoutCliError(`no ${OPENSCOUT_APP_NAME} found in the mounted DMG at ${mountPoint}`);
105171
105841
  }
105172
- const mountPointMatch = result.stdout.match(/<key>mount-point<\/key>\s*<string>([^<]+)<\/string>/);
105173
- if (mountPointMatch) {
105174
- return mountPointMatch[1];
105842
+ return source;
105843
+ }
105844
+ function codesignField(output, key) {
105845
+ const match = output.match(new RegExp(`^${key}=(.+)$`, "m"));
105846
+ return match ? match[1].trim() : null;
105847
+ }
105848
+ function verifyDownloadedDmg(dmgPath, deps = {}) {
105849
+ const resolved = resolveInstallDependencies(deps);
105850
+ requireSuccess(resolved.run("codesign", ["--verify", "--strict", dmgPath]), `codesign --verify --strict failed for ${dmgPath}`);
105851
+ requireSuccess(resolved.run("spctl", [
105852
+ "--assess",
105853
+ "--type",
105854
+ "open",
105855
+ "--context",
105856
+ "context:primary-signature",
105857
+ "-vv",
105858
+ dmgPath
105859
+ ]), `Gatekeeper rejected ${dmgPath}`);
105860
+ }
105861
+ function verifyStagedOpenScoutApp(bundlePath, deps = {}, expectedVersion) {
105862
+ const resolved = resolveInstallDependencies(deps);
105863
+ if (!resolved.existsSync(infoPlistPath(bundlePath))) {
105864
+ throw new ScoutCliError(`${bundlePath} is missing Contents/Info.plist`);
105865
+ }
105866
+ const bundleId = readPlistString(resolved, infoPlistPath(bundlePath), "CFBundleIdentifier");
105867
+ if (bundleId !== OPENSCOUT_APP_BUNDLE_ID) {
105868
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} bundle id is ${bundleId ?? "(missing)"}; expected ${OPENSCOUT_APP_BUNDLE_ID}`);
105869
+ }
105870
+ const version3 = readPlistString(resolved, infoPlistPath(bundlePath), "CFBundleShortVersionString");
105871
+ if (!version3) {
105872
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} is missing CFBundleShortVersionString`);
105873
+ }
105874
+ if (expectedVersion && version3 !== expectedVersion) {
105875
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} version is ${version3}; expected release ${expectedVersion}`);
105876
+ }
105877
+ requireSuccess(resolved.run("codesign", ["--verify", "--deep", "--strict", bundlePath]), `codesign --verify --deep --strict failed for ${bundlePath}`);
105878
+ const details = requireSuccess(resolved.run("codesign", ["-dvv", bundlePath]), `could not read codesign details for ${bundlePath}`);
105879
+ const detailsOutput = combinedOutput(details);
105880
+ const identifier = codesignField(detailsOutput, "Identifier");
105881
+ if (identifier !== OPENSCOUT_APP_BUNDLE_ID) {
105882
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} signature identifier is ${identifier ?? "(missing)"}; expected ${OPENSCOUT_APP_BUNDLE_ID}`);
105883
+ }
105884
+ const teamId = codesignField(detailsOutput, "TeamIdentifier");
105885
+ if (teamId !== OPENSCOUT_SIGNING_TEAM_ID) {
105886
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} signing Team ID is ${teamId ?? "(missing)"}; expected ${OPENSCOUT_SIGNING_TEAM_ID}`);
105887
+ }
105888
+ if (!/^Authority=Developer ID Application:/m.test(detailsOutput)) {
105889
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} is not signed with Developer ID Application`);
105890
+ }
105891
+ requireSuccess(resolved.run("spctl", ["--assess", "--type", "execute", "-vv", bundlePath]), `Gatekeeper rejected ${bundlePath}`);
105892
+ return version3;
105893
+ }
105894
+ function copyApp(source, destination, deps) {
105895
+ const copied = deps.run("ditto", [source, destination]);
105896
+ if ((copied.status ?? 1) !== 0) {
105897
+ if (isPermissionError(copied.error, copied)) {
105898
+ throw new ScoutCliError(permissionDeniedMessage(destination, copied.stderr));
105899
+ }
105900
+ throw new ScoutCliError(`could not copy the app to ${destination}: ${copied.stderr.trim() || copied.stdout.trim() || "ditto failed"}`);
105175
105901
  }
105176
- throw new ScoutCliError("could not determine the DMG mount point");
105177
105902
  }
105178
- function unmountDmg(mountPoint) {
105179
- spawnSync8("hdiutil", ["detach", mountPoint, "-quiet"], { encoding: "utf8" });
105903
+ function moveReplacing(source, destination, deps) {
105904
+ try {
105905
+ deps.renameSync(source, destination);
105906
+ return;
105907
+ } catch (error48) {
105908
+ const code = error48?.code;
105909
+ if (code !== "EXDEV") {
105910
+ if (isPermissionError(error48)) {
105911
+ throw new ScoutCliError(permissionDeniedMessage(destination));
105912
+ }
105913
+ throw new ScoutCliError(`could not move ${source} to ${destination}: ${error48 instanceof Error ? error48.message : String(error48)}`);
105914
+ }
105915
+ }
105916
+ copyApp(source, destination, deps);
105917
+ deps.rmSync(source, { recursive: true, force: true });
105918
+ }
105919
+ function backupPathFor(appPath) {
105920
+ return `${appPath}${BACKUP_SUFFIX}`;
105921
+ }
105922
+ function installLockPathFor(appPath) {
105923
+ return `${appPath}${INSTALL_LOCK_SUFFIX}`;
105180
105924
  }
105181
- function copyAppFromMount(mountPoint) {
105182
- const listing = spawnSync8("ls", [mountPoint], { encoding: "utf8" });
105183
- const appEntry = listing.stdout.split(`
105184
- `).map((line) => line.trim()).find((line) => line === APP_NAME) ?? listing.stdout.split(`
105185
- `).map((line) => line.trim()).find((line) => line.endsWith(".app") && !line.toLowerCase().startsWith("openscoutmenu"));
105186
- if (!appEntry) {
105187
- throw new ScoutCliError(`no ${APP_NAME} found in the mounted DMG at ${mountPoint}`);
105925
+ function acquireInstallLock(deps) {
105926
+ const lockPath = installLockPathFor(deps.appPath);
105927
+ const result = deps.run(SHLOCK_PATH, ["-f", lockPath, "-p", String(process.pid)]);
105928
+ if ((result.status ?? 1) !== 0) {
105929
+ if (isPermissionError(result.error, result)) {
105930
+ throw new ScoutCliError(permissionDeniedMessage(lockPath, result.stderr));
105931
+ }
105932
+ throw new ScoutCliError(`another scout install is already updating ${deps.appPath}. ` + `If no installer is running, remove the stale lock at ${lockPath} and retry.`);
105188
105933
  }
105189
- const source = join48(mountPoint, appEntry);
105190
- if (existsSync39(APP_PATH)) {
105191
- const removed = spawnSync8("rm", ["-rf", APP_PATH], { encoding: "utf8" });
105192
- if ((removed.status ?? 1) !== 0) {
105193
- throw new ScoutCliError(`could not replace ${APP_PATH} (permission denied?). Try: sudo scout install`);
105934
+ let released = false;
105935
+ return () => {
105936
+ if (released)
105937
+ return;
105938
+ released = true;
105939
+ deps.rmSync(lockPath, { force: true });
105940
+ };
105941
+ }
105942
+ function recoverIncompleteInstall(deps) {
105943
+ const backupPath = backupPathFor(deps.appPath);
105944
+ const destExists = deps.existsSync(deps.appPath);
105945
+ const backupExists = deps.existsSync(backupPath);
105946
+ if (!destExists && backupExists) {
105947
+ verifyStagedOpenScoutApp(backupPath, deps);
105948
+ deps.renameSync(backupPath, deps.appPath);
105949
+ return;
105950
+ }
105951
+ if (destExists && backupExists) {
105952
+ let destinationError = null;
105953
+ try {
105954
+ verifyStagedOpenScoutApp(deps.appPath, deps);
105955
+ } catch (error48) {
105956
+ destinationError = error48;
105194
105957
  }
105958
+ if (!destinationError) {
105959
+ deps.rmSync(backupPath, { recursive: true, force: true });
105960
+ return;
105961
+ }
105962
+ try {
105963
+ verifyStagedOpenScoutApp(backupPath, deps);
105964
+ } catch (backupError) {
105965
+ throw new ScoutCliError(`incomplete prior install found, but neither ${deps.appPath} nor ${backupPath} passed verification: ` + `${destinationError instanceof Error ? destinationError.message : String(destinationError)}; ` + `${backupError instanceof Error ? backupError.message : String(backupError)}`);
105966
+ }
105967
+ deps.rmSync(deps.appPath, { recursive: true, force: true });
105968
+ deps.renameSync(backupPath, deps.appPath);
105195
105969
  }
105196
- const copied = spawnSync8("cp", ["-R", source, APP_PATH], { encoding: "utf8" });
105197
- if ((copied.status ?? 1) !== 0) {
105198
- throw new ScoutCliError(`could not copy the app to ${APP_PATH}: ${copied.stderr.trim() || "permission denied?"}. Try: sudo scout install`);
105970
+ }
105971
+ function replaceAppTransactionally(stagedPath, deps, expectedVersion) {
105972
+ const backupPath = backupPathFor(deps.appPath);
105973
+ const hadPriorApp = deps.existsSync(deps.appPath);
105974
+ let backedUp = false;
105975
+ let installedVersion = "";
105976
+ try {
105977
+ if (hadPriorApp) {
105978
+ try {
105979
+ deps.renameSync(deps.appPath, backupPath);
105980
+ } catch (error48) {
105981
+ if (isPermissionError(error48)) {
105982
+ throw new ScoutCliError(permissionDeniedMessage(deps.appPath));
105983
+ }
105984
+ throw new ScoutCliError(`could not move the installed app aside: ${error48 instanceof Error ? error48.message : String(error48)}`);
105985
+ }
105986
+ backedUp = true;
105987
+ }
105988
+ moveReplacing(stagedPath, deps.appPath, deps);
105989
+ installedVersion = verifyStagedOpenScoutApp(deps.appPath, deps, expectedVersion);
105990
+ } catch (error48) {
105991
+ if (backedUp) {
105992
+ if (deps.existsSync(deps.appPath)) {
105993
+ deps.rmSync(deps.appPath, { recursive: true, force: true });
105994
+ }
105995
+ if (deps.existsSync(backupPath)) {
105996
+ try {
105997
+ deps.renameSync(backupPath, deps.appPath);
105998
+ } catch {
105999
+ throw new ScoutCliError(`install failed and rollback failed. The previous app is at ${backupPath}. ${error48 instanceof Error ? error48.message : String(error48)}`);
106000
+ }
106001
+ }
106002
+ } else if (!hadPriorApp && deps.existsSync(deps.appPath)) {
106003
+ deps.rmSync(deps.appPath, { recursive: true, force: true });
106004
+ }
106005
+ throw error48;
106006
+ }
106007
+ if (backedUp && deps.existsSync(backupPath)) {
106008
+ deps.rmSync(backupPath, { recursive: true, force: true });
105199
106009
  }
106010
+ if (!installedVersion) {
106011
+ throw new ScoutCliError(`installed ${OPENSCOUT_APP_NAME} has no verified version`);
106012
+ }
106013
+ return installedVersion;
106014
+ }
106015
+ function executableIsInsideApp(executable, appPath) {
106016
+ const normalizedApp = appPath.endsWith("/") ? appPath.slice(0, -1) : appPath;
106017
+ return executable === normalizedApp || executable.startsWith(`${normalizedApp}/`);
105200
106018
  }
105201
- function removeQuarantine() {
105202
- spawnSync8("xattr", ["-rd", "com.apple.quarantine", APP_PATH], { encoding: "utf8" });
106019
+ function processIdsForInstalledApp(psOutput, appPath) {
106020
+ const pids = [];
106021
+ for (const rawLine of psOutput.split(`
106022
+ `)) {
106023
+ const line = rawLine.trim();
106024
+ if (!line)
106025
+ continue;
106026
+ const match = line.match(/^(\d+)\s+(\S+)/);
106027
+ if (!match)
106028
+ continue;
106029
+ const pid = Number(match[1]);
106030
+ const executable = match[2];
106031
+ if (!Number.isInteger(pid) || pid <= 0)
106032
+ continue;
106033
+ if (executableIsInsideApp(executable, appPath))
106034
+ pids.push(pid);
106035
+ }
106036
+ return pids;
105203
106037
  }
105204
- function isAppRunning() {
105205
- return (spawnSync8("pgrep", ["-x", APP_PROCESS_NAME], { encoding: "utf8" }).status ?? 1) === 0;
106038
+ function processIdsForMenuOutsideApp(psOutput, appPath) {
106039
+ const pids = [];
106040
+ for (const rawLine of psOutput.split(`
106041
+ `)) {
106042
+ const line = rawLine.trim();
106043
+ if (!line)
106044
+ continue;
106045
+ const match = line.match(/^(\d+)\s+(\S+)/);
106046
+ if (!match)
106047
+ continue;
106048
+ const pid = Number(match[1]);
106049
+ const executable = match[2];
106050
+ if (!Number.isInteger(pid) || pid <= 0)
106051
+ continue;
106052
+ if (executable !== MENU_PROCESS_EXECUTABLE && !executable.endsWith(`/${MENU_PROCESS_EXECUTABLE}`)) {
106053
+ continue;
106054
+ }
106055
+ if (executableIsInsideApp(executable, appPath))
106056
+ continue;
106057
+ pids.push(pid);
106058
+ }
106059
+ return pids;
105206
106060
  }
105207
- function quitApp() {
105208
- spawnSync8("osascript", ["-e", `tell application id "${APP_BUNDLE_ID}" to quit`], {
105209
- encoding: "utf8"
105210
- });
106061
+ function inspectInstallProcessIds(deps) {
106062
+ const result = deps.run("ps", ["-axo", "pid=,args="]);
106063
+ requireSuccess(result, "could not inspect running OpenScout processes");
106064
+ return {
106065
+ installed: processIdsForInstalledApp(result.stdout, deps.appPath),
106066
+ staleMenus: processIdsForMenuOutsideApp(result.stdout, deps.appPath)
106067
+ };
105211
106068
  }
105212
- function launchApp() {
105213
- const byId = spawnSync8("open", ["-b", APP_BUNDLE_ID], { encoding: "utf8" });
105214
- if ((byId.status ?? 1) !== 0) {
105215
- spawnSync8("open", [APP_PATH], { encoding: "utf8" });
106069
+ function listPidsBlockingInstall(deps) {
106070
+ const { installed, staleMenus } = inspectInstallProcessIds(deps);
106071
+ return [...new Set([...installed, ...staleMenus])];
106072
+ }
106073
+ function isInstallBlocked(deps) {
106074
+ return listPidsBlockingInstall(deps).length > 0;
106075
+ }
106076
+ function waitUntilInstallUnblocked(deps, attempts) {
106077
+ for (let index2 = 0;index2 < attempts; index2 += 1) {
106078
+ if (!isInstallBlocked(deps))
106079
+ return true;
106080
+ if (index2 + 1 < attempts)
106081
+ deps.sleep(STOP_POLL_MS);
106082
+ }
106083
+ return !isInstallBlocked(deps);
106084
+ }
106085
+ function stopRunningApp(deps) {
106086
+ const { installed, staleMenus } = inspectInstallProcessIds(deps);
106087
+ const hadInstalledRunning = installed.length > 0;
106088
+ const initialPids = [...new Set([...installed, ...staleMenus])];
106089
+ if (initialPids.length === 0)
106090
+ return false;
106091
+ for (const pid of initialPids) {
106092
+ deps.run("kill", [String(pid)]);
106093
+ }
106094
+ if (waitUntilInstallUnblocked(deps, STOP_POLL_ATTEMPTS))
106095
+ return hadInstalledRunning;
106096
+ for (const pid of listPidsBlockingInstall(deps)) {
106097
+ deps.run("kill", ["-9", String(pid)]);
105216
106098
  }
106099
+ if (waitUntilInstallUnblocked(deps, STOP_POLL_ATTEMPTS))
106100
+ return hadInstalledRunning;
106101
+ throw new ScoutCliError("could not stop OpenScout before replacing it");
106102
+ }
106103
+ function launchApp(deps) {
106104
+ requireSuccess(deps.run("open", [deps.appPath]), `could not relaunch ${deps.appPath}`);
105217
106105
  }
105218
106106
  function renderInstallResult(result) {
105219
106107
  if (result.action === "check") {
@@ -105227,90 +106115,130 @@ function renderInstallResult(result) {
105227
106115
  }
105228
106116
  return result.message;
105229
106117
  }
105230
- async function runCheck(context, options) {
105231
- const installed = getInstalledVersion();
105232
- const release = await fetchRelease(options.version);
105233
- const target2 = stripLeadingV(release.tag_name);
106118
+ async function runCheck(context, options, deps) {
106119
+ const release = await fetchRelease(options.version, deps);
106120
+ const target2 = normalizeReleaseVersion(release.tag_name);
106121
+ let installed = getInstalledVersion(deps);
105234
106122
  let status;
105235
106123
  let message;
105236
- if (!installed) {
105237
- status = "not-installed";
105238
- message = "OpenScout is not installed \u2014 run `scout install`.";
105239
- } else if (installed === target2) {
105240
- status = "up-to-date";
105241
- message = `OpenScout ${installed} is up to date.`;
106124
+ if (!deps.existsSync(deps.appPath)) {
106125
+ if (deps.existsSync(backupPathFor(deps.appPath))) {
106126
+ status = "repair-needed";
106127
+ message = "An interrupted OpenScout install needs repair \u2014 run `scout install --force`.";
106128
+ } else {
106129
+ status = "not-installed";
106130
+ message = "OpenScout is not installed \u2014 run `scout install`.";
106131
+ }
105242
106132
  } else {
105243
- status = "update-available";
105244
- message = `Update available: ${installed} \u2192 ${target2}. Run \`scout install\`.`;
106133
+ try {
106134
+ installed = verifyStagedOpenScoutApp(deps.appPath, deps);
106135
+ if (installed === target2) {
106136
+ status = "up-to-date";
106137
+ message = `OpenScout ${installed} is up to date.`;
106138
+ } else {
106139
+ status = "update-available";
106140
+ message = `Update available: ${installed} \u2192 ${target2}. Run \`scout install\`.`;
106141
+ }
106142
+ } catch (error48) {
106143
+ status = "repair-needed";
106144
+ message = `OpenScout needs repair (${error48 instanceof Error ? error48.message : String(error48)}). ` + "Run `scout install --force`.";
106145
+ }
105245
106146
  }
105246
- context.output.writeValue({ action: "check", status, installed, target: target2, bundlePath: APP_PATH, message }, renderInstallResult);
106147
+ context.output.writeValue({ action: "check", status, installed, target: target2, bundlePath: deps.appPath, message }, renderInstallResult);
105247
106148
  }
105248
- async function runInstallCommand(context, args) {
106149
+ async function runInstallCommand(context, args, dependencies = {}) {
105249
106150
  if (HELP_FLAGS9.has(args[0] ?? "")) {
105250
106151
  context.output.writeText(renderInstallCommandHelp());
105251
106152
  return;
105252
106153
  }
105253
- if (process.platform !== "darwin") {
106154
+ const deps = resolveInstallDependencies(dependencies);
106155
+ if (deps.platform !== "darwin") {
105254
106156
  throw new ScoutCliError("scout install is only supported on macOS.");
105255
106157
  }
105256
106158
  const options = parseInstallArgs(args);
106159
+ if (options.version)
106160
+ normalizeReleaseVersion(options.version);
105257
106161
  if (options.check) {
105258
- await runCheck(context, options);
106162
+ await runCheck(context, options, deps);
105259
106163
  return;
105260
106164
  }
105261
- const installedBefore = getInstalledVersion();
105262
106165
  context.stderr(options.version ? `Fetching release ${options.version}\u2026` : "Fetching the latest OpenScout release\u2026");
105263
- const release = await fetchRelease(options.version);
105264
- const target2 = stripLeadingV(release.tag_name);
105265
- if (!options.force && installedBefore && installedBefore === target2) {
106166
+ const release = await fetchRelease(options.version, deps);
106167
+ const target2 = normalizeReleaseVersion(release.tag_name);
106168
+ const releaseInstallLock = acquireInstallLock(deps);
106169
+ try {
106170
+ recoverIncompleteInstall(deps);
106171
+ const hadInstalledApp = deps.existsSync(deps.appPath);
106172
+ const installedBefore = getInstalledVersion(deps);
106173
+ let repairingInstalledApp = false;
106174
+ if (!options.force && installedBefore === target2) {
106175
+ try {
106176
+ const verifiedVersion = verifyStagedOpenScoutApp(deps.appPath, deps, target2);
106177
+ context.output.writeValue({
106178
+ action: "install",
106179
+ status: "up-to-date",
106180
+ installed: verifiedVersion,
106181
+ target: target2,
106182
+ bundlePath: deps.appPath,
106183
+ message: `OpenScout ${target2} is already installed (use --force to reinstall).`
106184
+ }, renderInstallResult);
106185
+ return;
106186
+ } catch (error48) {
106187
+ repairingInstalledApp = true;
106188
+ context.stderr(`Installed OpenScout ${target2} failed verification; repairing it: ${error48 instanceof Error ? error48.message : String(error48)}`);
106189
+ }
106190
+ }
106191
+ const asset = findAppDmgAsset(release);
106192
+ const workDir = deps.mkdtempSync(join48(deps.tmpdir(), "scout-install-"));
106193
+ const dmgPath = join48(workDir, asset.name);
106194
+ const mountPoint = join48(workDir, "mnt");
106195
+ const stagedPath = join48(workDir, OPENSCOUT_APP_NAME);
106196
+ let mounted = false;
106197
+ let wasRunning = false;
106198
+ let installedAfter = "";
106199
+ try {
106200
+ await downloadDmg(asset, dmgPath, context, deps);
106201
+ context.stderr(`Installing OpenScout ${target2} to ${deps.appPath}\u2026`);
106202
+ verifyDownloadedDmg(dmgPath, deps);
106203
+ mountDmg(dmgPath, mountPoint, deps);
106204
+ mounted = true;
106205
+ const source = requireMountedOpenScoutApp(mountPoint, deps);
106206
+ copyApp(source, stagedPath, deps);
106207
+ verifyStagedOpenScoutApp(stagedPath, deps, target2);
106208
+ wasRunning = stopRunningApp(deps);
106209
+ installedAfter = replaceAppTransactionally(stagedPath, deps, target2);
106210
+ } finally {
106211
+ if (mounted)
106212
+ unmountDmg(mountPoint, deps);
106213
+ deps.rmSync(workDir, { recursive: true, force: true });
106214
+ }
106215
+ if (wasRunning && options.restart) {
106216
+ launchApp(deps);
106217
+ }
106218
+ const status = hadInstalledApp ? "updated" : "installed";
106219
+ const relaunchNote = wasRunning ? options.restart ? " (relaunched)" : " (restart OpenScout to use the new version)" : "";
106220
+ let verb = "Installed";
106221
+ if (repairingInstalledApp || hadInstalledApp && !installedBefore) {
106222
+ verb = "Repaired";
106223
+ } else if (installedBefore) {
106224
+ verb = `Updated ${installedBefore} \u2192`;
106225
+ }
105266
106226
  context.output.writeValue({
105267
106227
  action: "install",
105268
- status: "up-to-date",
105269
- installed: installedBefore,
106228
+ status,
106229
+ installed: installedAfter,
105270
106230
  target: target2,
105271
- bundlePath: APP_PATH,
105272
- message: `OpenScout ${target2} is already installed (use --force to reinstall).`
106231
+ bundlePath: deps.appPath,
106232
+ message: `${verb} OpenScout ${installedAfter} \u2192 ${deps.appPath}${relaunchNote}`
105273
106233
  }, renderInstallResult);
105274
- return;
105275
- }
105276
- const asset = findAppDmgAsset(release);
105277
- const wasRunning = isAppRunning();
105278
- const workDir = mkdtempSync(join48(tmpdir(), "scout-install-"));
105279
- const dmgPath = join48(workDir, asset.name);
105280
- let mountPoint = null;
105281
- try {
105282
- await downloadDmg(asset, dmgPath, context);
105283
- context.stderr(`Installing OpenScout ${target2} to ${APP_PATH}\u2026`);
105284
- mountPoint = mountDmg(dmgPath);
105285
- copyAppFromMount(mountPoint);
105286
106234
  } finally {
105287
- if (mountPoint)
105288
- unmountDmg(mountPoint);
105289
- rmSync5(workDir, { recursive: true, force: true });
105290
- }
105291
- removeQuarantine();
105292
- if (wasRunning && options.restart) {
105293
- quitApp();
105294
- launchApp();
105295
- }
105296
- const installedAfter = getInstalledVersion() ?? target2;
105297
- const status = installedBefore ? "updated" : "installed";
105298
- const relaunchNote = wasRunning ? options.restart ? " (relaunched)" : " (restart OpenScout to use the new version)" : "";
105299
- const verb = installedBefore ? `Updated ${installedBefore} \u2192` : "Installed";
105300
- context.output.writeValue({
105301
- action: "install",
105302
- status,
105303
- installed: installedAfter,
105304
- target: target2,
105305
- bundlePath: APP_PATH,
105306
- message: `${verb} OpenScout ${installedAfter} \u2192 ${APP_PATH}${relaunchNote}`
105307
- }, renderInstallResult);
106235
+ releaseInstallLock();
106236
+ }
105308
106237
  }
105309
- var OPENSCOUT_RELEASE_OWNER = "oscout", OPENSCOUT_RELEASE_REPOSITORY = "scout", APP_NAME = "OpenScout.app", APP_PATH, INFO_PLIST_PATH, APP_BUNDLE_ID = "app.openscout.scout", APP_PROCESS_NAME = "Scout", USER_AGENT = "scout-cli", HELP_FLAGS9;
106238
+ var OPENSCOUT_RELEASE_OWNER = "oscout", OPENSCOUT_RELEASE_REPOSITORY = "scout", OPENSCOUT_APP_NAME = "OpenScout.app", OPENSCOUT_APP_BUNDLE_ID = "app.openscout.scout", OPENSCOUT_SIGNING_TEAM_ID = "2U83JFPW66", DEFAULT_OPENSCOUT_APP_PATH, GITHUB_ASSET_DIGEST_ALGORITHM = "sha256", GITHUB_ASSET_DIGEST_HEX_LENGTH = 64, LATEST_DMG_ALIAS = "OpenScout.dmg", USER_AGENT = "scout-cli", BACKUP_SUFFIX = ".openscout-previous", INSTALL_LOCK_SUFFIX = ".openscout-install.lock", SHLOCK_PATH = "/usr/bin/shlock", STOP_POLL_ATTEMPTS = 50, STOP_POLL_MS = 100, MENU_PROCESS_EXECUTABLE = "ScoutMenu", HELP_FLAGS9;
105310
106239
  var init_install = __esm(() => {
105311
106240
  init_errors();
105312
- APP_PATH = `/Applications/${APP_NAME}`;
105313
- INFO_PLIST_PATH = `${APP_PATH}/Contents/Info.plist`;
106241
+ DEFAULT_OPENSCOUT_APP_PATH = `/Applications/${OPENSCOUT_APP_NAME}`;
105314
106242
  HELP_FLAGS9 = new Set(["help", "--help", "-h"]);
105315
106243
  });
105316
106244
 
@@ -110025,12 +110953,12 @@ function normalizedStringOrNull(value) {
110025
110953
  const trimmed = value?.trim();
110026
110954
  return trimmed ? trimmed : null;
110027
110955
  }
110028
- function metadataStringValue3(metadata, key) {
110956
+ function metadataStringValue4(metadata, key) {
110029
110957
  const value = metadata?.[key];
110030
110958
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
110031
110959
  }
110032
110960
  function isBuiltInDirectoryAgent(agent) {
110033
- const definitionId = agent.definitionId || metadataStringValue3(agent.metadata, "definitionId") || agent.id;
110961
+ const definitionId = agent.definitionId || metadataStringValue4(agent.metadata, "definitionId") || agent.id;
110034
110962
  return BUILT_IN_AGENT_DEFINITION_IDS.has(definitionId);
110035
110963
  }
110036
110964
  function rankState(state) {
@@ -113316,7 +114244,7 @@ var init_scout_mcp = __esm(() => {
113316
114244
 
113317
114245
  // ../../apps/desktop/src/cli/commands/mcp-install.ts
113318
114246
  import { spawnSync as spawnSync9 } from "child_process";
113319
- import { accessSync as accessSync4, constants as constants4, existsSync as existsSync40 } from "fs";
114247
+ import { accessSync as accessSync4, constants as constants4, existsSync as existsSync39 } from "fs";
113320
114248
  import { homedir as homedir31 } from "os";
113321
114249
  import { delimiter as delimiter4, join as join49, resolve as resolve29 } from "path";
113322
114250
  function isExecutable3(filePath) {
@@ -113357,7 +114285,7 @@ function resolveHostExecutable(host, env) {
113357
114285
  }
113358
114286
  function resolveCurrentScoutMcpLaunchCommand(env) {
113359
114287
  const currentScript = process.argv[1];
113360
- if (currentScript && existsSync40(currentScript) && isExecutable3(process.execPath)) {
114288
+ if (currentScript && existsSync39(currentScript) && isExecutable3(process.execPath)) {
113361
114289
  return {
113362
114290
  command: process.execPath,
113363
114291
  args: [resolve29(currentScript), "mcp"]
@@ -113730,7 +114658,7 @@ __export(exports_menu, {
113730
114658
  parseMenuCommand: () => parseMenuCommand
113731
114659
  });
113732
114660
  import { spawnSync as spawnSync10 } from "child_process";
113733
- import { existsSync as existsSync41 } from "fs";
114661
+ import { existsSync as existsSync40 } from "fs";
113734
114662
  import { homedir as homedir32 } from "os";
113735
114663
  import { dirname as dirname28, join as join50, resolve as resolve30 } from "path";
113736
114664
  import { fileURLToPath as fileURLToPath11 } from "url";
@@ -113753,9 +114681,9 @@ function renderMenuCommandHelp() {
113753
114681
  "",
113754
114682
  "Behavior:",
113755
114683
  " On macOS, `scout menu` loads the menu bar app from the installed OpenScout.app",
113756
- " (the menu ships as an embedded helper). If OpenScout.app is not installed, it",
113757
- " points you to `scout install`. Inside an OpenScout repo checkout it prefers",
113758
- " `apps/macos/bin/openscout-menu.ts` so launch/build/restart reuse the repo helper.",
114684
+ " (the menu ships as an embedded helper). If OpenScout.app is not installed, a",
114685
+ " repo checkout can fall back to `apps/macos/bin/openscout-menu.ts` for local",
114686
+ " development. `scout menu build` and `scout menu dmg` always require a checkout.",
113759
114687
  "",
113760
114688
  "Examples:",
113761
114689
  " scout menu",
@@ -113820,7 +114748,7 @@ function findRepoMenuHelper(startDirectory) {
113820
114748
  let current = resolve30(startDirectory);
113821
114749
  while (true) {
113822
114750
  const candidate = join50(current, "apps", "macos", "bin", "openscout-menu.ts");
113823
- if (existsSync41(candidate)) {
114751
+ if (existsSync40(candidate)) {
113824
114752
  return candidate;
113825
114753
  }
113826
114754
  const parent = dirname28(current);
@@ -113830,7 +114758,7 @@ function findRepoMenuHelper(startDirectory) {
113830
114758
  current = parent;
113831
114759
  }
113832
114760
  const sourceRelativeCandidate = fileURLToPath11(new URL("../../../../macos/bin/openscout-menu.ts", import.meta.url).toString());
113833
- return existsSync41(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
114761
+ return existsSync40(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
113834
114762
  }
113835
114763
  function resolveRepoBundlePath(helperPath) {
113836
114764
  return resolve30(dirname28(helperPath), "..", "dist", "Scout.app", EMBEDDED_MENU_RELATIVE_PATH);
@@ -113862,14 +114790,14 @@ async function stopRunningMenu(helperBundlePath) {
113862
114790
  return survivors.length === 0;
113863
114791
  }
113864
114792
  function resolveInstalledAppBundlePath(env) {
113865
- const spotlight = runProcess("mdfind", [`kMDItemCFBundleIdentifier == '${APP_BUNDLE_ID2}'`], { env, allowFailure: true });
114793
+ const spotlight = runProcess("mdfind", [`kMDItemCFBundleIdentifier == '${APP_BUNDLE_ID}'`], { env, allowFailure: true });
113866
114794
  const indexedPath = spotlight.stdout.split(`
113867
114795
  `).map((line) => line.trim()).find(Boolean);
113868
114796
  if (indexedPath) {
113869
114797
  return indexedPath;
113870
114798
  }
113871
114799
  for (const candidate of COMMON_APP_BUNDLE_PATHS) {
113872
- if (existsSync41(candidate)) {
114800
+ if (existsSync40(candidate)) {
113873
114801
  return candidate;
113874
114802
  }
113875
114803
  }
@@ -113957,7 +114885,7 @@ async function runWithRepoHelper(context, helperPath, command) {
113957
114885
  });
113958
114886
  }
113959
114887
  const running = command.action === "quit" ? false : isMenuRunning(bundlePath);
113960
- const installed = existsSync41(bundlePath) || running;
114888
+ const installed = existsSync40(bundlePath) || running;
113961
114889
  return {
113962
114890
  action: command.action,
113963
114891
  mode: "repo-helper",
@@ -114019,11 +114947,13 @@ async function runMenuCommand(context, args) {
114019
114947
  }
114020
114948
  const command = parseMenuCommand(args);
114021
114949
  await ensureMenuProviderTelemetry(context, command.action);
114022
- const helperPath = findRepoMenuHelper(defaultScoutContextDirectory(context));
114023
- const result = helperPath ? await runWithRepoHelper(context, helperPath, command) : await runWithInstalledApp(context, command);
114950
+ const installedAppPath = resolveInstalledAppBundlePath(context.env);
114951
+ const repoHelperPath = findRepoMenuHelper(defaultScoutContextDirectory(context));
114952
+ const preferRepoHelper = command.action === "build" || command.action === "dmg";
114953
+ const result = preferRepoHelper && repoHelperPath ? await runWithRepoHelper(context, repoHelperPath, command) : installedAppPath ? await runWithInstalledApp(context, command) : repoHelperPath ? await runWithRepoHelper(context, repoHelperPath, command) : await runWithInstalledApp(context, command);
114024
114954
  context.output.writeValue(result, renderMenuResult);
114025
114955
  }
114026
- var MENU_BUNDLE_ID = "app.openscout.scout.menu", MENU_PROCESS_NAME = "ScoutMenu", APP_BUNDLE_ID2 = "app.openscout.scout", APP_BUNDLE_NAME = "OpenScout.app", EMBEDDED_MENU_RELATIVE_PATH, HELP_FLAGS14, COMMON_APP_BUNDLE_PATHS;
114956
+ var MENU_BUNDLE_ID = "app.openscout.scout.menu", MENU_PROCESS_NAME = "ScoutMenu", APP_BUNDLE_ID = "app.openscout.scout", APP_BUNDLE_NAME = "OpenScout.app", EMBEDDED_MENU_RELATIVE_PATH, HELP_FLAGS14, COMMON_APP_BUNDLE_PATHS;
114027
114957
  var init_menu = __esm(() => {
114028
114958
  init_src3();
114029
114959
  init_app_lifecycle();
@@ -114840,7 +115770,7 @@ function renderMeshNodes(input) {
114840
115770
  var init_mesh = () => {};
114841
115771
 
114842
115772
  // ../../apps/desktop/src/core/mesh/enroll-pending.ts
114843
- import { existsSync as existsSync42, mkdirSync as mkdirSync12, readFileSync as readFileSync19, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
115773
+ import { existsSync as existsSync41, mkdirSync as mkdirSync12, readFileSync as readFileSync19, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "fs";
114844
115774
  import { dirname as dirname29, join as join51 } from "path";
114845
115775
  function pendingMeshEnrollmentPath(supportDirectory) {
114846
115776
  const dir = supportDirectory ?? resolveOpenScoutSupportPaths().supportDirectory;
@@ -114852,12 +115782,12 @@ function savePendingMeshEnrollment(handshake, supportDirectory) {
114852
115782
  }
114853
115783
  const path = pendingMeshEnrollmentPath(supportDirectory);
114854
115784
  mkdirSync12(dirname29(path), { recursive: true });
114855
- writeFileSync10(path, `${JSON.stringify(handshake, null, 2)}
115785
+ writeFileSync9(path, `${JSON.stringify(handshake, null, 2)}
114856
115786
  `, { encoding: "utf8", mode: 384 });
114857
115787
  }
114858
115788
  function loadPendingMeshEnrollment(supportDirectory) {
114859
115789
  const path = pendingMeshEnrollmentPath(supportDirectory);
114860
- if (!existsSync42(path)) {
115790
+ if (!existsSync41(path)) {
114861
115791
  return null;
114862
115792
  }
114863
115793
  let parsed;
@@ -114873,8 +115803,8 @@ function loadPendingMeshEnrollment(supportDirectory) {
114873
115803
  }
114874
115804
  function clearPendingMeshEnrollment(supportDirectory) {
114875
115805
  const path = pendingMeshEnrollmentPath(supportDirectory);
114876
- if (existsSync42(path)) {
114877
- rmSync6(path);
115806
+ if (existsSync41(path)) {
115807
+ rmSync5(path);
114878
115808
  }
114879
115809
  }
114880
115810
  var PENDING_FILE = "mesh-enroll-pending.json";
@@ -115637,7 +116567,7 @@ var exports_mesh_bridge = {};
115637
116567
  __export(exports_mesh_bridge, {
115638
116568
  runMeshBridgeCommand: () => runMeshBridgeCommand
115639
116569
  });
115640
- import { existsSync as existsSync43, mkdirSync as mkdirSync13, readFileSync as readFileSync21, unlinkSync, writeFileSync as writeFileSync11, chmodSync as chmodSync2 } from "fs";
116570
+ import { existsSync as existsSync42, mkdirSync as mkdirSync13, readFileSync as readFileSync21, unlinkSync, writeFileSync as writeFileSync10, chmodSync as chmodSync2 } from "fs";
115641
116571
  import { homedir as homedir33 } from "os";
115642
116572
  import { join as join52 } from "path";
115643
116573
  function supportDirectory() {
@@ -115656,7 +116586,7 @@ function readFlag(args, name2) {
115656
116586
  return args[index2 + 1]?.trim() || undefined;
115657
116587
  }
115658
116588
  function loadConfigFile(path) {
115659
- if (!existsSync43(path))
116589
+ if (!existsSync42(path))
115660
116590
  return null;
115661
116591
  return JSON.parse(readFileSync21(path, "utf8"));
115662
116592
  }
@@ -115744,7 +116674,7 @@ async function installBridge(context, args) {
115744
116674
  return;
115745
116675
  }
115746
116676
  mkdirSync13(supportDirectory(), { recursive: true });
115747
- writeFileSync11(configPath, `${JSON.stringify(config2, null, 2)}
116677
+ writeFileSync10(configPath, `${JSON.stringify(config2, null, 2)}
115748
116678
  `);
115749
116679
  chmodSync2(configPath, 384);
115750
116680
  const logDirectory = join52(supportDirectory(), "logs");
@@ -115772,7 +116702,7 @@ async function installBridge(context, args) {
115772
116702
  </dict>
115773
116703
  </plist>
115774
116704
  `;
115775
- writeFileSync11(launchAgentPlistPath2(), plist);
116705
+ writeFileSync10(launchAgentPlistPath2(), plist);
115776
116706
  const domain2 = `gui/${process.getuid?.() ?? 501}`;
115777
116707
  launchctl(context, ["bootout", `${domain2}/${LAUNCH_AGENT_LABEL}`]);
115778
116708
  const bootstrap = launchctl(context, ["bootstrap", domain2, launchAgentPlistPath2()]);
@@ -115789,7 +116719,7 @@ async function installBridge(context, args) {
115789
116719
  async function uninstallBridge(context) {
115790
116720
  const domain2 = `gui/${process.getuid?.() ?? 501}`;
115791
116721
  launchctl(context, ["bootout", `${domain2}/${LAUNCH_AGENT_LABEL}`]);
115792
- if (existsSync43(launchAgentPlistPath2())) {
116722
+ if (existsSync42(launchAgentPlistPath2())) {
115793
116723
  unlinkSync(launchAgentPlistPath2());
115794
116724
  }
115795
116725
  context.stdout(`Removed ${LAUNCH_AGENT_LABEL} (config file kept).`);
@@ -115993,7 +116923,7 @@ var init_mesh2 = __esm(() => {
115993
116923
  });
115994
116924
 
115995
116925
  // ../runtime/src/agent-workspace.ts
115996
- import { existsSync as existsSync44 } from "fs";
116926
+ import { existsSync as existsSync43 } from "fs";
115997
116927
  import { mkdir as mkdir13 } from "fs/promises";
115998
116928
  import { join as join53 } from "path";
115999
116929
  async function isGitRepo(projectRoot) {
@@ -116017,7 +116947,7 @@ async function createAgentWorkspace(projectRoot, agentName, requestedBranch) {
116017
116947
  validateWorktreeBranch(branch);
116018
116948
  const workspaceDir = join53(projectRoot, SCOUT_WORKSPACES_DIRNAME);
116019
116949
  const workspacePath = join53(workspaceDir, agentName);
116020
- if (existsSync44(workspacePath)) {
116950
+ if (existsSync43(workspacePath)) {
116021
116951
  return { path: workspacePath, branch };
116022
116952
  }
116023
116953
  await mkdir13(workspaceDir, { recursive: true });
@@ -117734,12 +118664,12 @@ var init_pairing_supervisor = () => {};
117734
118664
 
117735
118665
  // ../../apps/desktop/src/app/host/pairing.ts
117736
118666
  import {
117737
- existsSync as existsSync45,
118667
+ existsSync as existsSync44,
117738
118668
  mkdirSync as mkdirSync14,
117739
118669
  readFileSync as readFileSync22,
117740
118670
  statSync as statSync12,
117741
118671
  unlinkSync as unlinkSync2,
117742
- writeFileSync as writeFileSync12
118672
+ writeFileSync as writeFileSync11
117743
118673
  } from "fs";
117744
118674
  import { homedir as homedir35 } from "os";
117745
118675
  import { join as join55 } from "path";
@@ -117886,7 +118816,7 @@ async function withScoutPairingBridgeClient(port, run2) {
117886
118816
  }
117887
118817
  function loadScoutPairingConfig() {
117888
118818
  const { configPath } = resolveScoutPairingPaths();
117889
- if (!existsSync45(configPath)) {
118819
+ if (!existsSync44(configPath)) {
117890
118820
  return {};
117891
118821
  }
117892
118822
  try {
@@ -117922,7 +118852,7 @@ function resolveScoutPairingConfig() {
117922
118852
  }
117923
118853
  function readScoutPairingRuntimeSnapshot() {
117924
118854
  const { runtimeStatePath } = resolveScoutPairingPaths();
117925
- if (!existsSync45(runtimeStatePath)) {
118855
+ if (!existsSync44(runtimeStatePath)) {
117926
118856
  return null;
117927
118857
  }
117928
118858
  try {
@@ -117934,7 +118864,7 @@ function readScoutPairingRuntimeSnapshot() {
117934
118864
  }
117935
118865
  function readScoutPairingRuntimePid() {
117936
118866
  const { runtimePidPath } = resolveScoutPairingPaths();
117937
- if (!existsSync45(runtimePidPath)) {
118867
+ if (!existsSync44(runtimePidPath)) {
117938
118868
  return null;
117939
118869
  }
117940
118870
  try {
@@ -117986,7 +118916,7 @@ var init_pairing = __esm(() => {
117986
118916
 
117987
118917
  // ../../apps/desktop/src/app/host/agent-session.ts
117988
118918
  import { execFileSync as execFileSync6 } from "child_process";
117989
- import { existsSync as existsSync46 } from "fs";
118919
+ import { existsSync as existsSync45 } from "fs";
117990
118920
  import { open as open2 } from "fs/promises";
117991
118921
  import path from "path";
117992
118922
  function compactHomePath2(value) {
@@ -118127,7 +119057,7 @@ async function readAgentSessionLogs(agentId, tailLines) {
118127
119057
  let foundAny = false;
118128
119058
  let targetPath = null;
118129
119059
  for (const filePath of sources) {
118130
- if (!existsSync46(filePath)) {
119060
+ if (!existsSync45(filePath)) {
118131
119061
  continue;
118132
119062
  }
118133
119063
  foundAny = true;
@@ -121783,7 +122713,7 @@ var init_monitor2 = __esm(() => {
121783
122713
  });
121784
122714
 
121785
122715
  // ../../apps/desktop/src/core/pairing/runtime/config.ts
121786
- import { existsSync as existsSync47, mkdirSync as mkdirSync15, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
122716
+ import { existsSync as existsSync46, mkdirSync as mkdirSync15, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
121787
122717
  import { homedir as homedir36 } from "os";
121788
122718
  import path2 from "path";
121789
122719
  function pairingPaths() {
@@ -121800,7 +122730,7 @@ function pairingPaths() {
121800
122730
  }
121801
122731
  function loadPairingConfig() {
121802
122732
  const { configPath } = pairingPaths();
121803
- if (!existsSync47(configPath)) {
122733
+ if (!existsSync46(configPath)) {
121804
122734
  return {};
121805
122735
  }
121806
122736
  try {
@@ -123904,11 +124834,11 @@ var init_noise = __esm(() => {
123904
124834
  });
123905
124835
 
123906
124836
  // ../../apps/desktop/src/core/pairing/runtime/security/identity.ts
123907
- import { existsSync as existsSync48, mkdirSync as mkdirSync16, readFileSync as readFileSync24, writeFileSync as writeFileSync14 } from "fs";
124837
+ import { existsSync as existsSync47, mkdirSync as mkdirSync16, readFileSync as readFileSync24, writeFileSync as writeFileSync13 } from "fs";
123908
124838
  import { join as join56 } from "path";
123909
124839
  import { homedir as homedir37 } from "os";
123910
124840
  function loadOrCreateIdentity() {
123911
- if (existsSync48(IDENTITY_FILE)) {
124841
+ if (existsSync47(IDENTITY_FILE)) {
123912
124842
  return loadIdentity();
123913
124843
  }
123914
124844
  return createAndSaveIdentity();
@@ -123928,11 +124858,11 @@ function createAndSaveIdentity() {
123928
124858
  privateKey: bytesToHex2(keyPair.privateKey),
123929
124859
  createdAt: new Date().toISOString()
123930
124860
  };
123931
- writeFileSync14(IDENTITY_FILE, JSON.stringify(data, null, 2), { mode: 384 });
124861
+ writeFileSync13(IDENTITY_FILE, JSON.stringify(data, null, 2), { mode: 384 });
123932
124862
  return keyPair;
123933
124863
  }
123934
124864
  function loadTrustedPeers() {
123935
- if (!existsSync48(TRUSTED_PEERS_FILE))
124865
+ if (!existsSync47(TRUSTED_PEERS_FILE))
123936
124866
  return new Map;
123937
124867
  const data = JSON.parse(readFileSync24(TRUSTED_PEERS_FILE, "utf8"));
123938
124868
  return new Map(data.map((p) => [p.publicKey, p]));
@@ -123941,7 +124871,7 @@ function saveTrustedPeer(peer) {
123941
124871
  const peers = loadTrustedPeers();
123942
124872
  peers.set(peer.publicKey, peer);
123943
124873
  mkdirSync16(PAIRING_DIR, { recursive: true });
123944
- writeFileSync14(TRUSTED_PEERS_FILE, JSON.stringify([...peers.values()], null, 2), { mode: 384 });
124874
+ writeFileSync13(TRUSTED_PEERS_FILE, JSON.stringify([...peers.values()], null, 2), { mode: 384 });
123945
124875
  }
123946
124876
  function isTrustedPeer(publicKeyHex) {
123947
124877
  return loadTrustedPeers().has(publicKeyHex);
@@ -124146,10 +125076,10 @@ var init_security2 = __esm(() => {
124146
125076
  });
124147
125077
 
124148
125078
  // ../../apps/desktop/src/core/pairing/runtime/runtime-state.ts
124149
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync25, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync15 } from "fs";
125079
+ import { existsSync as existsSync48, mkdirSync as mkdirSync17, readFileSync as readFileSync25, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "fs";
124150
125080
  function readPairingRuntimeSnapshot() {
124151
125081
  const { runtimeStatePath } = pairingPaths();
124152
- if (!existsSync49(runtimeStatePath)) {
125082
+ if (!existsSync48(runtimeStatePath)) {
124153
125083
  return null;
124154
125084
  }
124155
125085
  try {
@@ -124271,11 +125201,11 @@ var init_bridge = __esm(() => {
124271
125201
  });
124272
125202
 
124273
125203
  // ../../apps/desktop/src/core/pairing/runtime/bridge/config.ts
124274
- import { existsSync as existsSync50, readFileSync as readFileSync26 } from "fs";
125204
+ import { existsSync as existsSync49, readFileSync as readFileSync26 } from "fs";
124275
125205
  import { join as join58 } from "path";
124276
125206
  import { homedir as homedir39 } from "os";
124277
125207
  function loadConfigFile2() {
124278
- if (!existsSync50(CONFIG_FILE)) {
125208
+ if (!existsSync49(CONFIG_FILE)) {
124279
125209
  return {};
124280
125210
  }
124281
125211
  try {
@@ -124607,7 +125537,7 @@ var init_artifact_presentation = __esm(() => {
124607
125537
 
124608
125538
  // ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
124609
125539
  import { randomUUID as randomUUID6 } from "crypto";
124610
- import { existsSync as existsSync51, mkdirSync as mkdirSync19, readFileSync as readFileSync27, realpathSync as realpathSync4, statSync as statSync14, unlinkSync as unlinkSync4, writeFileSync as writeFileSync16 } from "fs";
125540
+ import { existsSync as existsSync50, mkdirSync as mkdirSync19, readFileSync as readFileSync27, realpathSync as realpathSync4, statSync as statSync14, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "fs";
124611
125541
  import { BlockList, isIP } from "net";
124612
125542
  import { isAbsolute as isAbsolute6, join as join59, relative as relative6, sep as sep4 } from "path";
124613
125543
  import { homedir as homedir40 } from "os";
@@ -124630,7 +125560,7 @@ function storePairingAttachmentBlob(input, options) {
124630
125560
  purgeExpiredPairingAttachments(options.now);
124631
125561
  const id = `att-${randomUUID6()}`;
124632
125562
  const path3 = join59(PAIRING_ATTACHMENT_ROOT, id);
124633
- writeFileSync16(path3, data, { mode: 384 });
125563
+ writeFileSync15(path3, data, { mode: 384 });
124634
125564
  const now = options.now ?? Date.now();
124635
125565
  const entry = {
124636
125566
  id,
@@ -124678,14 +125608,14 @@ function resolvePairingAttachment(id, now) {
124678
125608
  }
124679
125609
  const known = attachmentBlobs.get(id);
124680
125610
  if (known) {
124681
- if (known.expiresAt <= now || !existsSync51(known.path)) {
125611
+ if (known.expiresAt <= now || !existsSync50(known.path)) {
124682
125612
  forgetPairingAttachment(id);
124683
125613
  return null;
124684
125614
  }
124685
125615
  return known;
124686
125616
  }
124687
125617
  const rehydrated = readAttachmentSidecar(id);
124688
- if (!rehydrated || rehydrated.expiresAt <= now || !existsSync51(rehydrated.path)) {
125618
+ if (!rehydrated || rehydrated.expiresAt <= now || !existsSync50(rehydrated.path)) {
124689
125619
  return null;
124690
125620
  }
124691
125621
  attachmentBlobs.set(id, rehydrated);
@@ -124696,7 +125626,7 @@ function attachmentSidecarPath(id) {
124696
125626
  }
124697
125627
  function writeAttachmentSidecar(entry) {
124698
125628
  try {
124699
- writeFileSync16(attachmentSidecarPath(entry.id), JSON.stringify({
125629
+ writeFileSync15(attachmentSidecarPath(entry.id), JSON.stringify({
124700
125630
  id: entry.id,
124701
125631
  mediaType: entry.mediaType,
124702
125632
  fileName: entry.fileName,
@@ -125444,7 +126374,7 @@ var init_fileserver = __esm(() => {
125444
126374
  });
125445
126375
 
125446
126376
  // ../../apps/desktop/src/core/pairing/runtime/bridge/ssh-terminal-access.ts
125447
- import { createHash as createHash11 } from "crypto";
126377
+ import { createHash as createHash13 } from "crypto";
125448
126378
  function validateIosGeneratedSshPublicKey(publicKey) {
125449
126379
  const fields = publicKey.trim().split(/\s+/);
125450
126380
  if (fields.length < 2) {
@@ -125605,10 +126535,10 @@ function readSshWireString(buffer, offset) {
125605
126535
  };
125606
126536
  }
125607
126537
  function fingerprintKeyData(keyData) {
125608
- return `SHA256:${createHash11("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "")}`;
126538
+ return `SHA256:${createHash13("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "")}`;
125609
126539
  }
125610
126540
  function fingerprintUtf8(value) {
125611
- return `SHA256:${createHash11("sha256").update(value, "utf8").digest("base64").replace(/=+$/, "")}`;
126541
+ return `SHA256:${createHash13("sha256").update(value, "utf8").digest("base64").replace(/=+$/, "")}`;
125612
126542
  }
125613
126543
  function encodeMarkerValue(value) {
125614
126544
  return Buffer.from(value, "utf8").toString("base64url");
@@ -125631,10 +126561,10 @@ var init_ssh_terminal_access = __esm(() => {
125631
126561
  });
125632
126562
 
125633
126563
  // ../../apps/desktop/src/core/pairing/runtime/bridge/mobile-terminal-session.ts
125634
- import { createHash as createHash12 } from "crypto";
126564
+ import { createHash as createHash14 } from "crypto";
125635
126565
  function mobileTerminalSessionName(sshPublicKey, deviceClass) {
125636
126566
  const normalizedClass = normalizeMobileTerminalDeviceClass(deviceClass);
125637
- const signature = createHash12("sha256").update(sshPublicKey.trim()).digest("hex").slice(0, 8);
126567
+ const signature = createHash14("sha256").update(sshPublicKey.trim()).digest("hex").slice(0, 8);
125638
126568
  return `scout-${normalizedClass}-${signature}`;
125639
126569
  }
125640
126570
  function provisionedMobileTerminalSessionName(sshPublicKey, deviceClass) {
@@ -125670,23 +126600,23 @@ var init_mobile_terminal_session = __esm(() => {
125670
126600
  });
125671
126601
 
125672
126602
  // ../../apps/desktop/src/core/pairing/runtime/bridge/mobile-terminal-provision.ts
125673
- import { createHash as createHash13 } from "crypto";
126603
+ import { createHash as createHash15 } from "crypto";
125674
126604
  import {
125675
126605
  chmodSync as chmodSync3,
125676
126606
  closeSync,
125677
- existsSync as existsSync52,
126607
+ existsSync as existsSync51,
125678
126608
  mkdirSync as mkdirSync20,
125679
126609
  openSync,
125680
126610
  readFileSync as readFileSync28,
125681
126611
  renameSync as renameSync6,
125682
126612
  unlinkSync as unlinkSync5,
125683
- writeFileSync as writeFileSync17
126613
+ writeFileSync as writeFileSync16
125684
126614
  } from "fs";
125685
126615
  import { homedir as homedir41, hostname as hostname5, userInfo as userInfo2 } from "os";
125686
126616
  import { join as join60 } from "path";
125687
126617
  async function readMobileTerminalStatus(requestedSessionName, deviceId) {
125688
126618
  const authorizedKeysPath = join60(homedir41(), ".ssh", "authorized_keys");
125689
- const authorizedKeys = existsSync52(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
126619
+ const authorizedKeys = existsSync51(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
125690
126620
  const provisionedSessionName = findSshTerminalSessionForDevice(authorizedKeys, deviceId);
125691
126621
  const sessionName = authorizedMobileTerminalSessionName(requestedSessionName, provisionedSessionName);
125692
126622
  const base = {
@@ -125762,13 +126692,13 @@ async function provisionMobileTerminalAccess(sshPublicKey, deviceId, deviceClass
125762
126692
  const authorizedKeysPath = join60(sshDir, "authorized_keys");
125763
126693
  const hostKeyFingerprint = readRequiredHostKeyFingerprint();
125764
126694
  const result = withAuthorizedKeysLock(sshDir, () => {
125765
- if (!existsSync52(sshDir)) {
126695
+ if (!existsSync51(sshDir)) {
125766
126696
  mkdirSync20(sshDir, { recursive: true, mode: 448 });
125767
126697
  }
125768
126698
  try {
125769
126699
  chmodSync3(sshDir, 448);
125770
126700
  } catch {}
125771
- const existing = existsSync52(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
126701
+ const existing = existsSync51(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
125772
126702
  const result2 = provisionSshTerminalAccess({
125773
126703
  authorizedKeys: existing,
125774
126704
  deviceId: id,
@@ -125820,7 +126750,7 @@ function readRequiredHostKeyFingerprint() {
125820
126750
  const keyData = pub.trim().split(/\s+/)[1];
125821
126751
  if (!keyData)
125822
126752
  throw new Error("missing key data");
125823
- const digest = createHash13("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "");
126753
+ const digest = createHash15("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "");
125824
126754
  return `SHA256:${digest}`;
125825
126755
  } catch (error48) {
125826
126756
  const detail = error48 instanceof Error ? error48.message : String(error48);
@@ -125830,7 +126760,7 @@ function readRequiredHostKeyFingerprint() {
125830
126760
  function writeAuthorizedKeysAtomically(path3, contents) {
125831
126761
  const tmpPath = `${path3}.scout-${process.pid}-${Date.now()}.tmp`;
125832
126762
  try {
125833
- writeFileSync17(tmpPath, contents, { mode: 384, flag: "wx" });
126763
+ writeFileSync16(tmpPath, contents, { mode: 384, flag: "wx" });
125834
126764
  renameSync6(tmpPath, path3);
125835
126765
  } catch (error48) {
125836
126766
  try {
@@ -125840,7 +126770,7 @@ function writeAuthorizedKeysAtomically(path3, contents) {
125840
126770
  }
125841
126771
  }
125842
126772
  function withAuthorizedKeysLock(sshDir, action) {
125843
- if (!existsSync52(sshDir)) {
126773
+ if (!existsSync51(sshDir)) {
125844
126774
  mkdirSync20(sshDir, { recursive: true, mode: 448 });
125845
126775
  }
125846
126776
  const lockPath = join60(sshDir, ".scout-authorized-keys.lock");
@@ -130601,6 +131531,9 @@ function startRelay(port, options = {}) {
130601
131531
  } : {},
130602
131532
  fetch(req, server2) {
130603
131533
  const url2 = new URL(req.url);
131534
+ if (url2.pathname === "/pair" && req.method === "GET") {
131535
+ return proxyPairHttp(req, url2, options.pairHttpOrigin, server2.requestIP(req)?.address);
131536
+ }
130604
131537
  if (url2.pathname === "/resolve" && req.method === "POST") {
130605
131538
  return handleResolve(req, roomByBridgeKey, rooms);
130606
131539
  }
@@ -130744,6 +131677,46 @@ function startRelay(port, options = {}) {
130744
131677
  }
130745
131678
  };
130746
131679
  }
131680
+ async function proxyPairHttp(req, url2, pairHttpOrigin, clientIp) {
131681
+ const origin = pairHttpOrigin === undefined ? `http://127.0.0.1:${resolveWebPort()}` : pairHttpOrigin?.trim() || null;
131682
+ if (!origin) {
131683
+ return new Response("Pairing HTTP is not configured", { status: 503 });
131684
+ }
131685
+ const target2 = new URL("/pair", origin);
131686
+ target2.search = url2.search;
131687
+ const headers2 = pairProxyHeaders(req, clientIp);
131688
+ try {
131689
+ const response = await fetch(target2, {
131690
+ method: "GET",
131691
+ headers: headers2,
131692
+ redirect: "manual",
131693
+ signal: AbortSignal.timeout(3000)
131694
+ });
131695
+ const out = new Headers;
131696
+ for (const name2 of ["location", "cache-control", "content-type"]) {
131697
+ const value = response.headers.get(name2);
131698
+ if (value)
131699
+ out.set(name2, value);
131700
+ }
131701
+ return new Response(response.body, { status: response.status, headers: out });
131702
+ } catch {
131703
+ return new Response("Pairing service unreachable", { status: 502 });
131704
+ }
131705
+ }
131706
+ function pairProxyHeaders(req, clientIp) {
131707
+ const headers2 = new Headers;
131708
+ for (const name2 of ["accept", "user-agent", "x-scout-client", "x-scout-device-name"]) {
131709
+ const value = req.headers.get(name2);
131710
+ if (value)
131711
+ headers2.set(name2, value);
131712
+ }
131713
+ headers2.set("x-scout-pair-relay", "1");
131714
+ if (clientIp) {
131715
+ headers2.set("x-forwarded-for", clientIp);
131716
+ headers2.set("x-real-ip", clientIp);
131717
+ }
131718
+ return headers2;
131719
+ }
130747
131720
  async function handleResolve(req, roomByBridgeKey, rooms) {
130748
131721
  try {
130749
131722
  const body = await req.json();
@@ -130809,18 +131782,21 @@ function parseRelayEnvelope2(data) {
130809
131782
  }
130810
131783
  }
130811
131784
  var BRIDGE_ABSENCE_GRACE_MS = 30000, ROOM_IDLE_TIMEOUT_MS = 60000;
131785
+ var init_relay = __esm(() => {
131786
+ init_local_config();
131787
+ });
130812
131788
 
130813
131789
  // ../../apps/desktop/src/core/pairing/runtime/relay-runtime.ts
130814
- import { existsSync as existsSync53, mkdirSync as mkdirSync22 } from "fs";
131790
+ import { existsSync as existsSync52, mkdirSync as mkdirSync22 } from "fs";
130815
131791
  import { homedir as homedir46, networkInterfaces as networkInterfaces2 } from "os";
130816
131792
  import { join as join63 } from "path";
130817
131793
  async function findStoredTailscaleCert(hostname6) {
130818
- if (!existsSync53(PAIRING_DIR2)) {
131794
+ if (!existsSync52(PAIRING_DIR2)) {
130819
131795
  return null;
130820
131796
  }
130821
131797
  const certPath = join63(PAIRING_DIR2, `${hostname6}.crt`);
130822
131798
  const keyPath = join63(PAIRING_DIR2, `${hostname6}.key`);
130823
- if (!existsSync53(certPath) || !existsSync53(keyPath)) {
131799
+ if (!existsSync52(certPath) || !existsSync52(keyPath)) {
130824
131800
  return null;
130825
131801
  }
130826
131802
  if (!await storedCertificateLooksPubliclyTrusted(certPath)) {
@@ -131032,6 +132008,7 @@ var PAIRING_DIR2;
131032
132008
  var init_relay_runtime = __esm(() => {
131033
132009
  init_system_probes();
131034
132010
  init_log2();
132011
+ init_relay();
131035
132012
  PAIRING_DIR2 = join63(homedir46(), ".scout/pairing");
131036
132013
  });
131037
132014
 
@@ -133198,7 +134175,7 @@ __export(exports_session, {
133198
134175
  mergeTerminalSurfaces: () => mergeTerminalSurfaces
133199
134176
  });
133200
134177
  import { spawnSync as spawnSync11 } from "child_process";
133201
- import { createHash as createHash14 } from "crypto";
134178
+ import { createHash as createHash16 } from "crypto";
133202
134179
  import { mkdirSync as mkdirSync23 } from "fs";
133203
134180
  import { homedir as homedir47 } from "os";
133204
134181
  import { basename as basename22, join as join64, resolve as resolve35 } from "path";
@@ -133563,7 +134540,7 @@ function terminalSurfaceKey(surface) {
133563
134540
  function defaultTerminalSessionName(input) {
133564
134541
  const projectName2 = sanitizeTerminalSessionNamePart(basename22(input.cwd) || "workspace", 28);
133565
134542
  const harnessName = sanitizeTerminalSessionNamePart(input.harness, 18);
133566
- const fingerprint = createHash14("sha1").update(`${input.backend}\x00${input.harness}\x00${input.cwd}\x00${input.sessionId}`).digest("hex").slice(0, 10);
134543
+ const fingerprint = createHash16("sha1").update(`${input.backend}\x00${input.harness}\x00${input.cwd}\x00${input.sessionId}`).digest("hex").slice(0, 10);
133567
134544
  return `scout-${harnessName}-${projectName2}-${fingerprint}`;
133568
134545
  }
133569
134546
  function sanitizeTerminalSessionNamePart(value, maxLength) {
@@ -133932,7 +134909,7 @@ __export(exports_server, {
133932
134909
  normalizeServerOpenPath: () => normalizeServerOpenPath
133933
134910
  });
133934
134911
  import { spawn as spawn10 } from "child_process";
133935
- import { existsSync as existsSync54, mkdirSync as mkdirSync24, openSync as openSync2, writeFileSync as writeFileSync18 } from "fs";
134912
+ import { existsSync as existsSync53, mkdirSync as mkdirSync24, openSync as openSync2, writeFileSync as writeFileSync17 } from "fs";
133936
134913
  import { homedir as homedir48 } from "os";
133937
134914
  import { dirname as dirname31, join as join65, resolve as resolve36 } from "path";
133938
134915
  import { fileURLToPath as fileURLToPath12 } from "url";
@@ -134372,7 +135349,7 @@ async function runScoutLocalEdge(env) {
134372
135349
  const config3 = resolveServerLocalEdgeConfig(env);
134373
135350
  const schemes = config3.scheme === "both" ? ["http", "https"] : [config3.scheme];
134374
135351
  const caddyfilePath = resolveScoutLocalEdgeCaddyfilePath();
134375
- writeFileSync18(caddyfilePath, renderOpenScoutCaddyfile(config3), "utf8");
135352
+ writeFileSync17(caddyfilePath, renderOpenScoutCaddyfile(config3), "utf8");
134376
135353
  const mdnsProcesses = schemes.flatMap((scheme) => {
134377
135354
  const edgePort = scheme === "https" ? 443 : 80;
134378
135355
  const suffix = scheme.toUpperCase();
@@ -134514,14 +135491,14 @@ __export(exports_setup, {
134514
135491
  runSetupCommand: () => runSetupCommand,
134515
135492
  resolveSetupSourceRoots: () => resolveSetupSourceRoots
134516
135493
  });
134517
- import { existsSync as existsSync55 } from "fs";
135494
+ import { existsSync as existsSync54 } from "fs";
134518
135495
  import { homedir as homedir49 } from "os";
134519
135496
  import { dirname as dirname32, join as join66, resolve as resolve37 } from "path";
134520
135497
  import { createInterface as createInterface4 } from "readline/promises";
134521
135498
  function findGitRoot2(startDirectory) {
134522
135499
  let current = resolve37(startDirectory);
134523
135500
  while (true) {
134524
- if (existsSync55(join66(current, ".git")))
135501
+ if (existsSync54(join66(current, ".git")))
134525
135502
  return current;
134526
135503
  const parent = dirname32(current);
134527
135504
  if (parent === current)
@@ -134538,7 +135515,7 @@ function suggestProjectSourceRoot(currentDirectory, homeDirectory = homedir49())
134538
135515
  join66(homeDirectory, "Developer"),
134539
135516
  join66(homeDirectory, "Projects")
134540
135517
  ]) {
134541
- if (existsSync55(candidate))
135518
+ if (existsSync54(candidate))
134542
135519
  return candidate;
134543
135520
  }
134544
135521
  return resolve37(currentDirectory);
@@ -134575,7 +135552,7 @@ async function resolveSetupSourceRoots(context, currentDirectory, sourceRoots, e
134575
135552
  async function runSetupCommand(context, args) {
134576
135553
  const options = parseSetupCommandOptions(args, defaultScoutContextDirectory(context));
134577
135554
  const settings = await readOpenScoutSettings({ currentDirectory: options.currentDirectory });
134578
- const hasSavedSettings = existsSync55(resolveOpenScoutSupportPaths().settingsPath);
135555
+ const hasSavedSettings = existsSync54(resolveOpenScoutSupportPaths().settingsPath);
134579
135556
  options.sourceRoots = await resolveSetupSourceRoots({ ...context, isTty: context.isTty && Boolean(process.stdin.isTTY) }, options.currentDirectory, options.sourceRoots, {
134580
135557
  roots: hasSavedSettings ? settings.discovery.workspaceRoots : [],
134581
135558
  answeredAt: hasSavedSettings ? settings.onboarding.sourceRootsAnsweredAt : null
@@ -134831,7 +135808,7 @@ function readPositiveIntEnv(name2, fallback2) {
134831
135808
  const raw2 = Number.parseInt(process.env[name2] ?? "", 10);
134832
135809
  return Number.isFinite(raw2) && raw2 > 0 ? raw2 : fallback2;
134833
135810
  }
134834
- var TAIL_POLL_INTERVAL_MS, HOT_DISCOVERY_INTERVAL_MS, DISCOVERY_CACHE_MAX_AGE_MS, SHALLOW_DISCOVERY_INTERVAL_MS, DEEP_DISCOVERY_INTERVAL_MS, QUIET_EVENT_COALESCE_WINDOW_MS, QUIET_EVENT_COALESCE_MAX_KEYS, RECENT_TRANSCRIPT_READ_BYTES, SESSION_TRANSCRIPT_READ_BYTES, RECENT_TRANSCRIPT_KIND_SCAN_READ_BYTES, RECENT_TRANSCRIPT_MAX_FILES, NATIVE_TAIL_SOURCES, TRANSCRIPT_REPLAY_MEMO_MAX_ENTRIES, TRANSCRIPT_REPLAY_ACTIVE_GRACE_MS, transcriptReplayMemo, transcriptReplayInFlight, watchers, perSessionBuffer, subscribers, knownTranscripts, quietEventLastSeen, GROK_QUIET_PHASES;
135811
+ var DEFAULT_TAIL_POLL_INTERVAL_MS = 1e4, DEFAULT_HOT_DISCOVERY_INTERVAL_MS = 60000, TAIL_POLL_INTERVAL_MS, HOT_DISCOVERY_INTERVAL_MS, DISCOVERY_CACHE_MAX_AGE_MS, SHALLOW_DISCOVERY_INTERVAL_MS, DEEP_DISCOVERY_INTERVAL_MS, QUIET_EVENT_COALESCE_WINDOW_MS, QUIET_EVENT_COALESCE_MAX_KEYS, RECENT_TRANSCRIPT_READ_BYTES, WATCHER_READ_BYTES, WATCHER_DRAIN_BYTES, WATCHER_PUMP_CONCURRENCY, SESSION_TRANSCRIPT_READ_BYTES, RECENT_TRANSCRIPT_KIND_SCAN_READ_BYTES, RECENT_TRANSCRIPT_MAX_FILES, INTERNAL_RECONCILE_SESSIONS_PER_SOURCE, INTERNAL_RECONCILE_LINES_PER_SESSION, INTERNAL_RECONCILE_READ_BYTES, INTERNAL_HOT_WATCHER_WINDOW_MS, INTERNAL_IDLE_WATCHER_INTERVAL_MS, INTERNAL_IDLE_WATCHER_BATCH_SIZE, INTERNAL_ACTIVE_STALE_AFTER_MS, INTERNAL_PERSISTED_OBSERVED_SEED_LIMIT, NATIVE_TAIL_SOURCES, TRANSCRIPT_REPLAY_MEMO_MAX_ENTRIES, TRANSCRIPT_REPLAY_ACTIVE_GRACE_MS, transcriptReplayMemo, transcriptReplayInFlight, watchers, perSessionBuffer, subscribers, internalSubscribers, knownTranscripts, persistedObservedSessions, quietEventLastSeen, GROK_QUIET_PHASES;
134835
135812
  var init_service11 = __esm(() => {
134836
135813
  init_claude_source();
134837
135814
  init_codex_source();
@@ -134840,17 +135817,28 @@ var init_service11 = __esm(() => {
134840
135817
  init_kimi_source();
134841
135818
  init_opencode_source();
134842
135819
  init_pi_source();
134843
- TAIL_POLL_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_POLL_INTERVAL_MS", 500);
134844
- HOT_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_HOT_DISCOVERY_INTERVAL_MS", 30000);
135820
+ TAIL_POLL_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_POLL_INTERVAL_MS", DEFAULT_TAIL_POLL_INTERVAL_MS);
135821
+ HOT_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_HOT_DISCOVERY_INTERVAL_MS", DEFAULT_HOT_DISCOVERY_INTERVAL_MS);
134845
135822
  DISCOVERY_CACHE_MAX_AGE_MS = readPositiveIntEnv("OPENSCOUT_TAIL_DISCOVERY_CACHE_MAX_AGE_MS", HOT_DISCOVERY_INTERVAL_MS);
134846
135823
  SHALLOW_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_SHALLOW_DISCOVERY_INTERVAL_MS", 10 * 60000);
134847
135824
  DEEP_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_DEEP_DISCOVERY_INTERVAL_MS", 60 * 60000);
134848
135825
  QUIET_EVENT_COALESCE_WINDOW_MS = readPositiveIntEnv("OPENSCOUT_TAIL_QUIET_EVENT_COALESCE_WINDOW_MS", 5000);
134849
135826
  QUIET_EVENT_COALESCE_MAX_KEYS = readPositiveIntEnv("OPENSCOUT_TAIL_QUIET_EVENT_COALESCE_MAX_KEYS", 2000);
134850
135827
  RECENT_TRANSCRIPT_READ_BYTES = 512 * 1024;
135828
+ WATCHER_READ_BYTES = readPositiveIntEnv("OPENSCOUT_TAIL_WATCHER_READ_BYTES", 512 * 1024);
135829
+ WATCHER_DRAIN_BYTES = Math.max(WATCHER_READ_BYTES, readPositiveIntEnv("OPENSCOUT_TAIL_WATCHER_DRAIN_BYTES", 4 * 1024 * 1024));
135830
+ WATCHER_PUMP_CONCURRENCY = readPositiveIntEnv("OPENSCOUT_TAIL_WATCHER_PUMP_CONCURRENCY", 16);
134851
135831
  SESSION_TRANSCRIPT_READ_BYTES = 8 * 1024 * 1024;
134852
135832
  RECENT_TRANSCRIPT_KIND_SCAN_READ_BYTES = 32 * 1024 * 1024;
134853
135833
  RECENT_TRANSCRIPT_MAX_FILES = readPositiveIntEnv("OPENSCOUT_TAIL_RECENT_TRANSCRIPT_MAX_FILES", 24);
135834
+ INTERNAL_RECONCILE_SESSIONS_PER_SOURCE = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_RECONCILE_SESSIONS_PER_SOURCE", 12);
135835
+ INTERNAL_RECONCILE_LINES_PER_SESSION = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_RECONCILE_LINES_PER_SESSION", 128);
135836
+ INTERNAL_RECONCILE_READ_BYTES = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_RECONCILE_READ_BYTES", 256 * 1024);
135837
+ INTERNAL_HOT_WATCHER_WINDOW_MS = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_HOT_WATCHER_WINDOW_MS", 60000);
135838
+ INTERNAL_IDLE_WATCHER_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_INTERVAL_MS", 15000);
135839
+ INTERNAL_IDLE_WATCHER_BATCH_SIZE = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_BATCH_SIZE", 32);
135840
+ INTERNAL_ACTIVE_STALE_AFTER_MS = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_ACTIVE_STALE_AFTER_MS", 30 * 60000);
135841
+ INTERNAL_PERSISTED_OBSERVED_SEED_LIMIT = readPositiveIntEnv("OPENSCOUT_TAIL_PERSISTED_OBSERVED_SEED_LIMIT", 4096);
134854
135842
  NATIVE_TAIL_SOURCES = new Set(["grok", "kimi", "opencode", "cursor"]);
134855
135843
  TRANSCRIPT_REPLAY_MEMO_MAX_ENTRIES = readPositiveIntEnv("OPENSCOUT_TAIL_REPLAY_MEMO_MAX_ENTRIES", 256);
134856
135844
  TRANSCRIPT_REPLAY_ACTIVE_GRACE_MS = readPositiveIntEnv("OPENSCOUT_TAIL_REPLAY_MEMO_ACTIVE_GRACE_MS", 2000);
@@ -134859,7 +135847,9 @@ var init_service11 = __esm(() => {
134859
135847
  watchers = new Map;
134860
135848
  perSessionBuffer = new Map;
134861
135849
  subscribers = new Set;
135850
+ internalSubscribers = new Set;
134862
135851
  knownTranscripts = new Map;
135852
+ persistedObservedSessions = new Map;
134863
135853
  quietEventLastSeen = new Map;
134864
135854
  GROK_QUIET_PHASES = new Set([
134865
135855
  "streaming_reasoning",
@@ -135415,7 +136405,7 @@ __export(exports_tui, {
135415
136405
  parseTuiLaunchOptions: () => parseTuiLaunchOptions
135416
136406
  });
135417
136407
  import { spawnSync as spawnSync12 } from "child_process";
135418
- import { accessSync as accessSync5, constants as constants5, existsSync as existsSync56 } from "fs";
136408
+ import { accessSync as accessSync5, constants as constants5, existsSync as existsSync55 } from "fs";
135419
136409
  import { dirname as dirname33, join as join67, resolve as resolve38 } from "path";
135420
136410
  import { fileURLToPath as fileURLToPath13 } from "url";
135421
136411
  function renderTuiCommandHelp() {
@@ -135544,7 +136534,7 @@ function findScoutTuiCheckout(cwd, env) {
135544
136534
  for (const start of starts) {
135545
136535
  let current = resolve38(start);
135546
136536
  while (true) {
135547
- if (existsSync56(join67(current, "crates/scout-tui/Cargo.toml"))) {
136537
+ if (existsSync55(join67(current, "crates/scout-tui/Cargo.toml"))) {
135548
136538
  return current;
135549
136539
  }
135550
136540
  const parent = dirname33(current);
@@ -135598,7 +136588,7 @@ var exports_up = {};
135598
136588
  __export(exports_up, {
135599
136589
  runUpCommand: () => runUpCommand
135600
136590
  });
135601
- import { existsSync as existsSync57 } from "fs";
136591
+ import { existsSync as existsSync56 } from "fs";
135602
136592
  import { resolve as resolve39 } from "path";
135603
136593
  function looksLikePath(value) {
135604
136594
  return value.includes("/") || value.startsWith(".") || value.startsWith("~");
@@ -135707,7 +136697,7 @@ async function runUpCommand(context, args) {
135707
136697
  throw new ScoutCliError(`usage: scout up <name|path> [--name <alias>] [--harness <${SUPPORTED_LOCAL_AGENT_HARNESSES.join("|")}>] [--provider <provider>] [--model <model>] [--reasoning-effort <effort>] [--permission-profile <${formatScoutPermissionProfiles()}>]`);
135708
136698
  }
135709
136699
  let projectPath;
135710
- if (looksLikePath(target2) || existsSync57(resolve39(target2))) {
136700
+ if (looksLikePath(target2) || existsSync56(resolve39(target2))) {
135711
136701
  projectPath = resolve39(target2);
135712
136702
  } else {
135713
136703
  const resolved = await resolveLocalAgentByName(target2);
@@ -136117,7 +137107,7 @@ var init_whoami = __esm(() => {
136117
137107
  });
136118
137108
 
136119
137109
  // ../../apps/desktop/src/cli/main.ts
136120
- import { existsSync as existsSync58, statSync as statSync17 } from "fs";
137110
+ import { existsSync as existsSync57, statSync as statSync17 } from "fs";
136121
137111
  import { join as join70 } from "path";
136122
137112
  import { spawnSync as spawnSync13 } from "child_process";
136123
137113
  import { homedir as homedir50 } from "os";
@@ -136828,7 +137818,7 @@ function getScoutBinPath() {
136828
137818
  if (_scoutBinPath)
136829
137819
  return _scoutBinPath;
136830
137820
  _scoutBinPath = join70(homedir50(), ".bun", "bin", "scout");
136831
- if (!existsSync58(_scoutBinPath)) {
137821
+ if (!existsSync57(_scoutBinPath)) {
136832
137822
  _scoutBinPath = spawnSync13("which", ["scout"], { encoding: "utf8" }).stdout.trim();
136833
137823
  }
136834
137824
  return _scoutBinPath;