@openscout/scout 0.2.94 → 0.2.96

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 (362) hide show
  1. package/NOTICE +14 -0
  2. package/README.md +164 -342
  3. package/bin/scout +26 -10
  4. package/bin/scoutd +0 -0
  5. package/dist/build-manifest.json +3 -3
  6. package/dist/client/assets/{ActivityScreen--eANwuxa.js → ActivityScreen-BnoaUkVZ.js} +1 -1
  7. package/dist/client/assets/{AddProjectForm-CcD6qvV8.js → AddProjectForm-IqOtJfRs.js} +1 -1
  8. package/dist/client/assets/{AgentAvatar-CYhf6twS.js → AgentAvatar-6-KBVKUu.js} +1 -1
  9. package/dist/client/assets/{AgentConfigurationScreen-BTI24HQX.js → AgentConfigurationScreen-CuZJy07q.js} +1 -1
  10. package/dist/client/assets/{AgentDefinitionsWorkspace-srmN6-vj.js → AgentDefinitionsWorkspace-Iyl4BesU.js} +1 -1
  11. package/dist/client/assets/{AgentDetailCard-UqPNpcvB.js → AgentDetailCard-C9rn3Fj5.js} +1 -1
  12. package/dist/client/assets/{AgentFloorView-XXNgI5Cj.js → AgentFloorView-YT-Jjccf.js} +1 -1
  13. package/dist/client/assets/{AgentInfoScreen-CimdOnZ9.js → AgentInfoScreen-Dr99gQEZ.js} +1 -1
  14. package/dist/client/assets/{AgentLaneCard-Z-aOBR3W.js → AgentLaneCard-DI2iW9zO.js} +1 -1
  15. package/dist/client/assets/{AgentLaneChrome-CyhA1reW.js → AgentLaneChrome-D8bARHMx.js} +1 -1
  16. package/dist/client/assets/{AgentLaneDetailSheet-BNzjMjIH.js → AgentLaneDetailSheet-52un7dKC.js} +1 -1
  17. package/dist/client/assets/{AgentLaneSummaryResize-Box8-_rS.js → AgentLaneSummaryResize-CEBjmPTa.js} +1 -1
  18. package/dist/client/assets/AgentLanesEmbedScreen-Dgcm7zd_.js +1 -0
  19. package/dist/client/assets/AgentLanesPreflight-q20mTjms.js +1 -0
  20. package/dist/client/assets/AgentLanesView-CetZZDyF.js +2 -0
  21. package/dist/client/assets/{AgentLiveActions-BwuzOE8h.js → AgentLiveActions-DrEJDJef.js} +1 -1
  22. package/dist/client/assets/AgentMasterScreen-DJPnstoD.js +1 -0
  23. package/dist/client/assets/{AgentSessions-CsZwoukw.js → AgentSessions-DCPCG4xb.js} +1 -1
  24. package/dist/client/assets/{AgentsConsole-KCao4XmX.js → AgentsConsole-CsYOoFJi.js} +1 -1
  25. package/dist/client/assets/{AgentsScreen-CtPAmwRX.js → AgentsScreen-k8LvBuU_.js} +1 -1
  26. package/dist/client/assets/AgentsSubnav-C4xpQhsv.js +1 -0
  27. package/dist/client/assets/{AstronautSuit-Ckx7lu7G.js → AstronautSuit-DbjQtfD_.js} +1 -1
  28. package/dist/client/assets/{AtopView-DYlpgEHf.js → AtopView-Daq_ob3z.js} +1 -1
  29. package/dist/client/assets/{BackToPicker-4JaeRvDm.js → BackToPicker-CXZjBRAe.js} +1 -1
  30. package/dist/client/assets/{BriefingDetailScreen-D6-xqrpo.js → BriefingDetailScreen-CUCXO-KH.js} +1 -1
  31. package/dist/client/assets/{BriefingsScreen-DKxSDEmk.js → BriefingsScreen-B5oMTGKv.js} +1 -1
  32. package/dist/client/assets/{BrokerMetadataPanel-BbREmsbK.js → BrokerMetadataPanel-BsiU8lTD.js} +1 -1
  33. package/dist/client/assets/{BrokerScreen-fbPIouMw.js → BrokerScreen-BPbOHhCS.js} +1 -1
  34. package/dist/client/assets/{CastPicker-BcA2vDDV.js → CastPicker-_5E5hNUe.js} +1 -1
  35. package/dist/client/assets/ChatCollapsedStrip-D-eXhzPf.js +1 -0
  36. package/dist/client/assets/{CodeDiffPane-Cn8Y9hAt.js → CodeDiffPane-n7W4iGUG.js} +1 -1
  37. package/dist/client/assets/{CodeProjectPicker-nPFktrco.js → CodeProjectPicker-CMsbSTDq.js} +1 -1
  38. package/dist/client/assets/{CodeScreen-BlAxtxE5.js → CodeScreen-CeuO3EhR.js} +1 -1
  39. package/dist/client/assets/{ConversationComposer-2HjbGPL9.js → ConversationComposer-CXPKx86g.js} +1 -1
  40. package/dist/client/assets/{ConversationFeedRows-C6wUqJrx.js → ConversationFeedRows-WrH-YI3L.js} +1 -1
  41. package/dist/client/assets/{ConversationHeader-C8vfXaTF.js → ConversationHeader-BQWzGzLK.js} +1 -1
  42. package/dist/client/assets/{ConversationPanels-CNCIbpqU.js → ConversationPanels-Dujvnmup.js} +2 -2
  43. package/dist/client/assets/ConversationScreen-CR9pWB65.js +2 -0
  44. package/dist/client/assets/{ConversationStatus-DZuyKsWN.js → ConversationStatus-DVzSb2Hv.js} +1 -1
  45. package/dist/client/assets/{CrewAvatar-CTIuxp4G.js → CrewAvatar-BwXjyg8N.js} +1 -1
  46. package/dist/client/assets/CrewWorkspaces-CkQDhyie.css +1 -0
  47. package/dist/client/assets/CrewWorkspaces-NKujRzwh.js +1 -0
  48. package/dist/client/assets/{DataTable-6IDhanr5.js → DataTable-Bp8tU9AK.js} +1 -1
  49. package/dist/client/assets/{DispatchAftermath-DC1u9itO.js → DispatchAftermath-B-6rmXzL.js} +1 -1
  50. package/dist/client/assets/{DocumentFocusViewer-DZVhS7as.js → DocumentFocusViewer-Do3wQ_3Z.js} +1 -1
  51. package/dist/client/assets/{EmbeddableSurfacesLab-BKUFJMCV.js → EmbeddableSurfacesLab-C1orRyP9.js} +1 -1
  52. package/dist/client/assets/{EmptyState-CeBl0iGd.js → EmptyState-CJzXj1l5.js} +1 -1
  53. package/dist/client/assets/{FileViewerPane-DQlpNt2S.js → FileViewerPane-Df6VzXjA.js} +1 -1
  54. package/dist/client/assets/FlightObserveScreen-6mGnmWQA.js +1 -0
  55. package/dist/client/assets/{FollowScreen-BXHOp3gb.js → FollowScreen-DvnxYLVh.js} +1 -1
  56. package/dist/client/assets/{HarnessMark-D5CA5uSp.js → HarnessMark-B979GCSB.js} +1 -1
  57. package/dist/client/assets/{HarnessesScreen-DhFXq1qz.js → HarnessesScreen-CHTuv8p6.js} +1 -1
  58. package/dist/client/assets/{HerdrSession-BRhs5LIR.js → HerdrSession-B3McuYgi.js} +1 -1
  59. package/dist/client/assets/{HomeHero-DMABJuze.js → HomeHero-CdUVPjkG.js} +1 -1
  60. package/dist/client/assets/{HostAdvisorView-RAociATV.js → HostAdvisorView-Bok5Sk5c.js} +1 -1
  61. package/dist/client/assets/{KnowledgeSearchInspector-CZPyVNSw.js → KnowledgeSearchInspector-B7XSoJsu.js} +1 -1
  62. package/dist/client/assets/{KnowledgeSearchScreen-mGTKIg00.js → KnowledgeSearchScreen-D3o3IODW.js} +1 -1
  63. package/dist/client/assets/{LaneToolHoverCard-D_DMlBeT.js → LaneToolHoverCard-HByf2ndx.js} +1 -1
  64. package/dist/client/assets/{LaneTraceDetailSheet-BEHg3LmH.js → LaneTraceDetailSheet-BUgHz-sd.js} +1 -1
  65. package/dist/client/assets/{LaneTraceEventFocus-DVimcizS.js → LaneTraceEventFocus-DM-hoPxG.js} +1 -1
  66. package/dist/client/assets/{MeshCanvas-BOuZx4AM.js → MeshCanvas-DCLJRsBy.js} +1 -1
  67. package/dist/client/assets/{MeshCanvasMinimap-wL-1N94e.js → MeshCanvasMinimap-cEFS9eW7.js} +1 -1
  68. package/dist/client/assets/{MeshScreen-DMY9Bmh5.js → MeshScreen-BcxPkhm6.js} +1 -1
  69. package/dist/client/assets/{MeshTopologyView-DiDcX5f4.js → MeshTopologyView-CCRPd8PU.js} +2 -2
  70. package/dist/client/assets/MessagesScreen-Bb5l13MO.js +1 -0
  71. package/dist/client/assets/{MissionContextRail-DrNKXG5u.js → MissionContextRail-DF0_kiU7.js} +1 -1
  72. package/dist/client/assets/{MissionControlView-ByBlHVN8.js → MissionControlView-E3V7bENU.js} +1 -1
  73. package/dist/client/assets/{MissionFocusOverlay-D1i6R6Zt.js → MissionFocusOverlay-h15sQFvP.js} +1 -1
  74. package/dist/client/assets/{MissionLeftAgentRow-DN9NcCML.js → MissionLeftAgentRow-KPdk9rSa.js} +1 -1
  75. package/dist/client/assets/{MissionLogPane-DSeIv3Xo.js → MissionLogPane-BbdDKjUl.js} +1 -1
  76. package/dist/client/assets/{NewAgentModal-V7yK6J6j.js → NewAgentModal-DfHxad34.js} +1 -1
  77. package/dist/client/assets/NewChatComposer-_Stj0fZy.js +19 -0
  78. package/dist/client/assets/{NewChatOrigin-DdpzE4kz.js → NewChatOrigin-B1u7ki70.js} +1 -1
  79. package/dist/client/assets/{ObserveEmbedScreen-l_Q3ts2q.js → ObserveEmbedScreen-Co0xTii6.js} +1 -1
  80. package/dist/client/assets/{ObserveTextFlow-BSEtYTxU.js → ObserveTextFlow-ZQWwrK3i.js} +2 -2
  81. package/dist/client/assets/{ObservedTopologyPanel-CdGUXxGR.js → ObservedTopologyPanel-BJUhDSuQ.js} +1 -1
  82. package/dist/client/assets/{OpenScoutAppShell-DN8dOSK4.js → OpenScoutAppShell-CUd2yOLu.js} +25 -25
  83. package/dist/client/assets/{OpsAgentsView-ntGr4pz4.js → OpsAgentsView-DI5wu1si.js} +1 -1
  84. package/dist/client/assets/OpsScreen-D8fQafCl.js +1 -0
  85. package/dist/client/assets/OpsSubnav-1K4Jaf__.js +1 -0
  86. package/dist/client/assets/ProjectAgentProfile-Bdjd_dAx.js +1 -0
  87. package/dist/client/assets/{ProjectAgentProfileBar-BQET-qrY.js → ProjectAgentProfileBar-k2QHHHsQ.js} +1 -1
  88. package/dist/client/assets/{ProjectAgentProfileHero-Ck8mThtx.js → ProjectAgentProfileHero-Bpxk1ui2.js} +1 -1
  89. package/dist/client/assets/ProjectsInbox-C_bMHLCc.js +2 -0
  90. package/dist/client/assets/ProjectsRail-J-aH_pxT.js +1 -0
  91. package/dist/client/assets/ProjectsScreen-cVUkGbPz.js +1 -0
  92. package/dist/client/assets/ProjectsThreadAside-_ourdM-Q.js +1 -0
  93. package/dist/client/assets/{Provider-B2xuo3rf.js → Provider-CtXJeLrP.js} +1 -1
  94. package/dist/client/assets/{RailRow-B-GycCBx.js → RailRow-CEH2MFBw.js} +1 -1
  95. package/dist/client/assets/{RawSessionsTable-AQThi6sm.js → RawSessionsTable-CDyrom-F.js} +1 -1
  96. package/dist/client/assets/RealtimeVoiceScreen-DBl3DN1H.js +1 -0
  97. package/dist/client/assets/{RepoDiffEmbedScreen-DXWoRF_c.js → RepoDiffEmbedScreen-kY2pHjIs.js} +1 -1
  98. package/dist/client/assets/{RepoDiffPageScreen-D_274ExO.js → RepoDiffPageScreen-C6LhoKrn.js} +1 -1
  99. package/dist/client/assets/{RepoDiffViewer-DbedmHDX.js → RepoDiffViewer-BhcIIQQg.js} +1 -1
  100. package/dist/client/assets/{RepoDiffViewerLazy-C0--fPtk.js → RepoDiffViewerLazy-Csy_j9LD.js} +2 -2
  101. package/dist/client/assets/{ReposScreen-D39_YG4H.js → ReposScreen-B_cB0zu7.js} +1 -1
  102. package/dist/client/assets/ScoutDeckSurface-CSy_fjcG.js +1 -0
  103. package/dist/client/assets/{ScoutSurface-UsRy0t-I.js → ScoutSurface-CQVcSrin.js} +1 -1
  104. package/dist/client/assets/{ScoutbotFxLab-Fmdfboxs.js → ScoutbotFxLab-DqGiLYNy.js} +1 -1
  105. package/dist/client/assets/{SecondaryNav-o2RL9H8H.js → SecondaryNav-BR4VRdgh.js} +1 -1
  106. package/dist/client/assets/SessionEmbedScreen-BTlZ9gAg.js +1 -0
  107. package/dist/client/assets/{SessionObserve-Dsd0j05N.js → SessionObserve-dQDN23pP.js} +1 -1
  108. package/dist/client/assets/{SessionObserveEvidence-DopQYDkD.js → SessionObserveEvidence-BRBGKAgd.js} +1 -1
  109. package/dist/client/assets/SessionRefScreen-COVQMbtE.js +1 -0
  110. package/dist/client/assets/SessionsScreen--5OJ3p3v.js +1 -0
  111. package/dist/client/assets/{SettingsDrawer-_AwuopMS.js → SettingsDrawer-RGVK_bmE.js} +1 -1
  112. package/dist/client/assets/{SettingsScreen-Cw4DDfJP.js → SettingsScreen-CCxzy5IU.js} +1 -1
  113. package/dist/client/assets/{ShikiPane-BfHc_3pM.js → ShikiPane-DYI9Hkge.js} +1 -1
  114. package/dist/client/assets/{SlidePanel-C-q7XwTa.js → SlidePanel-B9ugxWgA.js} +1 -1
  115. package/dist/client/assets/{SpriteAvatar-Bs68ZENy.js → SpriteAvatar-z5hHesx9.js} +1 -1
  116. package/dist/client/assets/{StatusPill-ZZQwxB1a.js → StatusPill-BFvMjmZV.js} +1 -1
  117. package/dist/client/assets/{TailView-Q4FVMsUH.js → TailView-0uEIj2JH.js} +1 -1
  118. package/dist/client/assets/{Terminal-D7zqHwHV.js → Terminal-DouZeNxc.js} +1 -1
  119. package/dist/client/assets/{TerminalEmbedScreen-CXIJ7qv6.js → TerminalEmbedScreen-dMNUbzXr.js} +1 -1
  120. package/dist/client/assets/{TerminalHeaderMount-BnBLOPbb.js → TerminalHeaderMount-BL07cd0q.js} +1 -1
  121. package/dist/client/assets/{TextDocumentSurface-CKQP-cBT.js → TextDocumentSurface-BflAwXeZ.js} +1 -1
  122. package/dist/client/assets/{TmuxPeek-V9dGESfO.js → TmuxPeek-BTnjOQi0.js} +1 -1
  123. package/dist/client/assets/{VoicePermissionsPanel-k7sbLXx2.js → VoicePermissionsPanel-CgQ6GGS-.js} +1 -1
  124. package/dist/client/assets/{WorkDetailScreen-60p9litn.js → WorkDetailScreen-CaLG-zDs.js} +1 -1
  125. package/dist/client/assets/{WorkFilesViewer-BjJS7GyM.js → WorkFilesViewer-B9Sil_No.js} +1 -1
  126. package/dist/client/assets/{activity-Bj3Hnbj3.js → activity-DcrMApUY.js} +1 -1
  127. package/dist/client/assets/{addon-fit-BL-NTWRU.js → addon-fit-DMVrz5Sv.js} +1 -1
  128. package/dist/client/assets/{addon-webgl-CIMgmNjg.js → addon-webgl-BYvpZUyu.js} +1 -1
  129. package/dist/client/assets/{agent-autocomplete-DXlvdc4b.js → agent-autocomplete-B-5QTeas.js} +1 -1
  130. package/dist/client/assets/{agent-essentials-BOMHR0_U.js → agent-essentials-D7KRGYvF.js} +1 -1
  131. package/dist/client/assets/{agent-lane-card-model-CTLxf3R4.js → agent-lane-card-model-CNZMPkhc.js} +1 -1
  132. package/dist/client/assets/{agent-lanes-model-AfrjRNK5.js → agent-lanes-model-DXqjs0OK.js} +2 -2
  133. package/dist/client/assets/agent-master-I1W-5d-x.css +1 -0
  134. package/dist/client/assets/agent-master-model-DqjrVI2h.js +1 -0
  135. package/dist/client/assets/{agent-specialization-BIPIBW8u.js → agent-specialization-BujJVUTD.js} +1 -1
  136. package/dist/client/assets/{arc.es-IR5r6sj_.js → arc.es-NHqHWohb.js} +1 -1
  137. package/dist/client/assets/{arrow-down-BydciYFZ.js → arrow-down-CKtSUjmW.js} +1 -1
  138. package/dist/client/assets/{arrow-left-CghA0kTw.js → arrow-left-Czpf-AE4.js} +1 -1
  139. package/dist/client/assets/{arrow-right-iJFv3jNh.js → arrow-right-BuzMnLYP.js} +1 -1
  140. package/dist/client/assets/{arrow-up-right-CCfZXPqD.js → arrow-up-right-C6Wl-1uw.js} +1 -1
  141. package/dist/client/assets/{bot-B5sub1-i.js → bot-rd0awxD3.js} +1 -1
  142. package/dist/client/assets/{check-C18YQC90.js → check-ofKUE8yH.js} +1 -1
  143. package/dist/client/assets/{chevron-down-Mp9mu_yd.js → chevron-down-wciXnOQz.js} +1 -1
  144. package/dist/client/assets/{chevron-right-CzH-TaSF.js → chevron-right-D-HdlPg8.js} +1 -1
  145. package/dist/client/assets/{chunk-HRAB3GC3-swV8PA2k.js → chunk-HRAB3GC3-Tyv9rzx7.js} +1 -1
  146. package/dist/client/assets/{chunk-JTPDIGGW-BKXqcLKw.js → chunk-JTPDIGGW-BC2Mandc.js} +3 -3
  147. package/dist/client/assets/{chunk-T65CQEWV-vYbNJ8E6.js → chunk-T65CQEWV-C1AWTYZZ.js} +1 -1
  148. package/dist/client/assets/{chunk-YOZR3KYS-U65yUmqt.js → chunk-YOZR3KYS-DwHPHa6f.js} +1 -1
  149. package/dist/client/assets/{clock-B4PTFi5m.js → clock-B88r8OrZ.js} +1 -1
  150. package/dist/client/assets/{code-xml-Jtxsp5d1.js → code-xml-D7pOQM9l.js} +1 -1
  151. package/dist/client/assets/{compass-0_54-vz3.js → compass-Dt1AMXz2.js} +1 -1
  152. package/dist/client/assets/content-9K38QchR.js +1 -0
  153. package/dist/client/assets/content-BRgnyXWh.js +1 -0
  154. package/dist/client/assets/{content-CHghn-rT.js → content-BWPNyXJ0.js} +1 -1
  155. package/dist/client/assets/content-BdkI07v3.js +1 -0
  156. package/dist/client/assets/content-C4oWa8SB.js +1 -0
  157. package/dist/client/assets/content-C9ijJxyl.js +1 -0
  158. package/dist/client/assets/content-CFZ4rghn.js +1 -0
  159. package/dist/client/assets/content-CIqu_Ci_.js +1 -0
  160. package/dist/client/assets/content-CnJF9DGO.js +1 -0
  161. package/dist/client/assets/content-CriW8Y8u.js +1 -0
  162. package/dist/client/assets/content-DDCyPPk_.js +1 -0
  163. package/dist/client/assets/content-DtcZeh5m.js +1 -0
  164. package/dist/client/assets/content-U9tsdtle.js +1 -0
  165. package/dist/client/assets/content-hFLK3CON.js +1 -0
  166. package/dist/client/assets/{content-DMbawvqU.js → content-nkx0swDe.js} +1 -1
  167. package/dist/client/assets/content-p00KQrtD.js +1 -0
  168. package/dist/client/assets/{conversation-icons-BV10B2Me.js → conversation-icons-tp9BX6X2.js} +1 -1
  169. package/dist/client/assets/{conversation-model-CwF6Y9gx.js → conversation-model-Dc02NsGp.js} +1 -1
  170. package/dist/client/assets/conversation-motion-features-D9-RmBSq.js +1 -0
  171. package/dist/client/assets/{copy-Dtpx4FOE.js → copy-CWWAJ75H.js} +1 -1
  172. package/dist/client/assets/{cpu-BjVis9mc.js → cpu-DrqejfkB.js} +1 -1
  173. package/dist/client/assets/{createLucideIcon-DXpqZP7I.js → createLucideIcon-DezKXSG2.js} +1 -1
  174. package/dist/client/assets/{ctx-panel-D_HJ3nhb.css → ctx-panel-F05jXK4Q.css} +1 -1
  175. package/dist/client/assets/discover-C2CZO4Tk.js +2 -0
  176. package/dist/client/assets/embed-entry-BSMUykuG.js +2 -0
  177. package/dist/client/assets/{external-link-Ckiyzjwo.js → external-link-Dp_a2vRZ.js} +1 -1
  178. package/dist/client/assets/{eye-CpfyvdLq.js → eye-BRyD0jno.js} +1 -1
  179. package/dist/client/assets/{file-code-corner-7mo7EviR.js → file-code-corner-muIVH_xO.js} +1 -1
  180. package/dist/client/assets/{file-text-KijGBnME.js → file-text-CqJgwxmv.js} +1 -1
  181. package/dist/client/assets/{gauge-y-Xg8Mrv.js → gauge-hU--hKVP.js} +1 -1
  182. package/dist/client/assets/{gestures-Bq9XnSqY.js → gestures-DN4hEeqv.js} +1 -1
  183. package/dist/client/assets/{git-branch-BQ_x8KZL.js → git-branch-CK5v8F2v.js} +1 -1
  184. package/dist/client/assets/{home-moving-signal-DtRpiNa0.js → home-moving-signal-BjDtEJUP.js} +1 -1
  185. package/dist/client/assets/{home-now-card-DFb4GOgM.js → home-now-card-nF8OotI-.js} +1 -1
  186. package/dist/client/assets/{home-now-card-nano-D--m9jpq.js → home-now-card-nano-C-r_bBg5.js} +1 -1
  187. package/dist/client/assets/{hooks-9d1SoaQA.js → hooks-D46bABUP.js} +1 -1
  188. package/dist/client/assets/index-CPrU5q04.js +1 -0
  189. package/dist/client/assets/{index-CZ-05hKz.js → index-DvLtl7rV.js} +5 -5
  190. package/dist/client/assets/index-kL1WLbeS.js +1 -0
  191. package/dist/client/assets/index-zvhpRHao.js +1 -0
  192. package/dist/client/assets/{inspector-panel-C-EhEDYy.js → inspector-panel-Dkc7rLvY.js} +1 -1
  193. package/dist/client/assets/{keyboard-nav-DFhAJkW3.js → keyboard-nav-BLYDhL3Z.js} +1 -1
  194. package/dist/client/assets/{lane-roster-store-gUBsydNf.js → lane-roster-store-DOdI1O13.js} +1 -1
  195. package/dist/client/assets/{layers-CGou-LwV.js → layers-CSsg3JVP.js} +1 -1
  196. package/dist/client/assets/{layout-grid-BrZt31QH.js → layout-grid-DOChPJdZ.js} +1 -1
  197. package/dist/client/assets/left-B-SdEJSJ.js +1 -0
  198. package/dist/client/assets/{left-B_o51JV6.js → left-B3wSE6ql.js} +1 -1
  199. package/dist/client/assets/{left-CwHZCGO3.js → left-BNRFiRsb.js} +1 -1
  200. package/dist/client/assets/{left-DA0amyfu.js → left-BggzhgXN.js} +1 -1
  201. package/dist/client/assets/left-CARCvvxS.js +2 -0
  202. package/dist/client/assets/left-CpokZYHt.js +1 -0
  203. package/dist/client/assets/{left-CwLs0okF.js → left-YT7mIGuH.js} +1 -1
  204. package/dist/client/assets/{left-advisor-DU3s01gk.js → left-advisor-CCY0vkcO.js} +1 -1
  205. package/dist/client/assets/left-agents-trhO5sL8.js +1 -0
  206. package/dist/client/assets/{left-default-uvuvDmt0.js → left-default-CG33elBs.js} +1 -1
  207. package/dist/client/assets/{left-lanes-DnNLgec8.js → left-lanes-CitMgeTp.js} +1 -1
  208. package/dist/client/assets/{left-mission-DN3-NLPC.js → left-mission-Zl2m_5eU.js} +1 -1
  209. package/dist/client/assets/{library-DIo5NiGP.js → library-DkALuCh5.js} +1 -1
  210. package/dist/client/assets/{loader-circle-BiohlpM3.js → loader-circle-pM56HNhR.js} +1 -1
  211. package/dist/client/assets/{local-agents-wUIaBcEr.js → local-agents-C8xAhbO5.js} +1 -1
  212. package/dist/client/assets/{mentions-CcPFMbmZ.js → mentions-DG6OQrsc.js} +1 -1
  213. package/dist/client/assets/{mesh-buckets-ByyFXdKT.js → mesh-buckets-6y3ZIibI.js} +1 -1
  214. package/dist/client/assets/{mesh-ops-CvGyjK84.js → mesh-ops-BZ1h5RQs.js} +1 -1
  215. package/dist/client/assets/{mesh-roster-n2nDpEmA.js → mesh-roster-Bc7T4b7z.js} +1 -1
  216. package/dist/client/assets/{mesh-view-store-BpxGm71b.js → mesh-view-store-ChUOASK8.js} +1 -1
  217. package/dist/client/assets/{message-circle-B2ddzBrX.js → message-circle-mZCv8tvB.js} +1 -1
  218. package/dist/client/assets/{message-markup-RkfmDfME.js → message-markup-C2LQDm_k.js} +1 -1
  219. package/dist/client/assets/{message-square-D9YYWE0x.js → message-square-C7t_qtuj.js} +1 -1
  220. package/dist/client/assets/{message-visibility-DwxgcgYK.js → message-visibility-CGOmSSKs.js} +1 -1
  221. package/dist/client/assets/{mission-control-store-Bf4b54MV.js → mission-control-store-nDLdUZPA.js} +1 -1
  222. package/dist/client/assets/{observe-CJRIvOCn.js → observe-BpEm1INu.js} +1 -1
  223. package/dist/client/assets/{page-status-BptpTdQ-.js → page-status-E5jnk48p.js} +1 -1
  224. package/dist/client/assets/{paperclip-B1UNGl5d.js → paperclip-C64lkcbM.js} +1 -1
  225. package/dist/client/assets/{persistent-state-YI-WHSCF.js → persistent-state-EHXw8WNy.js} +1 -1
  226. package/dist/client/assets/{plus-CuU_X0-8.js → plus-Co-dqKul.js} +1 -1
  227. package/dist/client/assets/{profile-rY8jO38P.js → profile-APxDsN2P.js} +1 -1
  228. package/dist/client/assets/projects-inbox-Cg4X2lUQ.js +1 -0
  229. package/dist/client/assets/projects-inbox-DRBl2N85.css +1 -0
  230. package/dist/client/assets/{proxy-DnLpZIi9.js → proxy-BiW5suC7.js} +1 -1
  231. package/dist/client/assets/{radio-Dm1ItnA4.js → radio-BOxBQX-T.js} +1 -1
  232. package/dist/client/assets/{refresh-cw-BRn9Oxxl.js → refresh-cw-DMxGzDwn.js} +1 -1
  233. package/dist/client/assets/resolve-panes-DeVJbS9c.js +1 -0
  234. package/dist/client/assets/resolve-sidebar-context-BQe5J6xF.js +1 -0
  235. package/dist/client/assets/{right-BJltPRnd.js → right-Bmfd3pjP.js} +1 -1
  236. package/dist/client/assets/{right-Beh3nCUB.js → right-BqI-nr9G.js} +1 -1
  237. package/dist/client/assets/{right-D3aTKf1F.js → right-BuCgE0fZ.js} +1 -1
  238. package/dist/client/assets/{right-Shpw_xDy.js → right-BwlW6E57.js} +1 -1
  239. package/dist/client/assets/{right-CPP7GyOm.js → right-C0ybUw0j.js} +1 -1
  240. package/dist/client/assets/{right-BbyYdzHn.js → right-CdkcrmeD.js} +1 -1
  241. package/dist/client/assets/{right-CJnB_Bg7.js → right-CimaAvQN.js} +1 -1
  242. package/dist/client/assets/{right-C1yumXNx.js → right-CkCCPbCX.js} +1 -1
  243. package/dist/client/assets/{right-DI1B79nR.js → right-D3eSiy41.js} +1 -1
  244. package/dist/client/assets/{right-DtsRNX5b.js → right-Dz_xD0Vj.js} +1 -1
  245. package/dist/client/assets/{rocket-DOwXHKWA.js → rocket-BQm5_CvR.js} +1 -1
  246. package/dist/client/assets/router-B9vQkNJL.js +1 -0
  247. package/dist/client/assets/{scope-views-A4_vP97W.js → scope-views-1IYJdl41.js} +1 -1
  248. package/dist/client/assets/{scout-voice-BhPzrc6P.js → scout-voice-BVUqGhth.js} +1 -1
  249. package/dist/client/assets/{scoutbot-markdown-jh7tzFB1.js → scoutbot-markdown-BQ_Ee2Ic.js} +1 -1
  250. package/dist/client/assets/{scroll-text-DO0GQzvN.js → scroll-text-Dze_dXtQ.js} +1 -1
  251. package/dist/client/assets/{search-D1tJSAWe.js → search-CDko0PKE.js} +1 -1
  252. package/dist/client/assets/{secondaryNavConfig-B9IF8wXl.js → secondaryNavConfig-BoQ_zYR9.js} +1 -1
  253. package/dist/client/assets/{selection-bridge-i2S9zpf8.js → selection-bridge-Din6yV3h.js} +1 -1
  254. package/dist/client/assets/{send-horizontal-CgLppsIC.js → send-horizontal-CSjIm6xg.js} +1 -1
  255. package/dist/client/assets/{server-Dymv5Emh.js → server-Bzj7lPIx.js} +1 -1
  256. package/dist/client/assets/{session-catalog-DNaOEzoQ.js → session-catalog-DsaZJXZt.js} +1 -1
  257. package/dist/client/assets/session-ref-lookup-state-SATH_jAP.js +1 -0
  258. package/dist/client/assets/{session-table-PTQVoTyc.js → session-table-BQfR7u6r.js} +1 -1
  259. package/dist/client/assets/{settings-2fC_G9nX.js → settings-Dyvpkb1D.js} +1 -1
  260. package/dist/client/assets/{sliders-horizontal-BPSMHhbm.js → sliders-horizontal-C8njVwfg.js} +1 -1
  261. package/dist/client/assets/{sparkles-Dy8p5xN8.js → sparkles-CETYrxGA.js} +1 -1
  262. package/dist/client/assets/{sse-CiOSzE5n.js → sse-CD9MMUxl.js} +1 -1
  263. package/dist/client/assets/{tail-events-BMQmSx1n.js → tail-events-Ps8KpKah.js} +1 -1
  264. package/dist/client/assets/{terminal-yN9xN-h9.js → terminal-9GRS_Qdc.js} +1 -1
  265. package/dist/client/assets/{terminal-relay-9lhzEp2V.js → terminal-relay-GYMIvKs1.js} +1 -1
  266. package/dist/client/assets/{terminal-takeover-DANQfTcy.js → terminal-takeover-_Jyys4me.js} +1 -1
  267. package/dist/client/assets/{text-CnZ0e1-V.js → text-BP3oyl8-.js} +1 -1
  268. package/dist/client/assets/{text-rLpBg4_z.css → text-BYAFNLNq.css} +1 -1
  269. package/dist/client/assets/{use-conversation-list-D9BNLfnW.js → use-conversation-list-C9ABzjXI.js} +1 -1
  270. package/dist/client/assets/use-fleet-active-asks-DAspZQjE.js +1 -0
  271. package/dist/client/assets/{use-mesh-ops-items-BIvIHlqe.js → use-mesh-ops-items-Dh54ymEp.js} +1 -1
  272. package/dist/client/assets/{use-reduced-motion-CKkhq9c5.js → use-reduced-motion-x8pJMVrG.js} +1 -1
  273. package/dist/client/assets/{use-scout-dev-flags-B2KCzPxn.js → use-scout-dev-flags-DhCbiDK3.js} +1 -1
  274. package/dist/client/assets/{useAgentHoverCard-CHBsxcbp.js → useAgentHoverCard-DoBwV0LX.js} +1 -1
  275. package/dist/client/assets/useContentSecondaryNav-pZe2Zfk1.js +1 -0
  276. package/dist/client/assets/{usePierreRuntime-DUuXH5yp.js → usePierreRuntime-ejq9dCWV.js} +1 -1
  277. package/dist/client/assets/{wrench-YcXMlsh2.js → wrench-DjkoAOQt.js} +1 -1
  278. package/dist/client/assets/{x-e3h2hDsp.js → x-CAbNespN.js} +1 -1
  279. package/dist/client/assets/{xterm-Byp1Hep_.js → xterm-Cf-ngcGP.js} +1 -1
  280. package/dist/client/assets/{zap-nMcjPoWB.js → zap-Cq8eWkcF.js} +1 -1
  281. package/dist/client/index.html +1 -1
  282. package/dist/drizzle/0009_old_korg.sql +72 -0
  283. package/dist/drizzle/0010_sparkling_colonel_america.sql +2 -0
  284. package/dist/drizzle/0011_funny_onslaught.sql +4 -0
  285. package/dist/drizzle/0012_high_blazing_skull.sql +16 -0
  286. package/dist/drizzle/0013_perfect_juggernaut.sql +1 -0
  287. package/dist/drizzle/0014_legal_meggan.sql +6 -0
  288. package/dist/drizzle/0015_lame_marvel_boy.sql +12 -0
  289. package/dist/drizzle/0016_early_goliath.sql +6 -0
  290. package/dist/drizzle/meta/0009_snapshot.json +6262 -0
  291. package/dist/drizzle/meta/0010_snapshot.json +6286 -0
  292. package/dist/drizzle/meta/0011_snapshot.json +6337 -0
  293. package/dist/drizzle/meta/0012_snapshot.json +6458 -0
  294. package/dist/drizzle/meta/0013_snapshot.json +6465 -0
  295. package/dist/drizzle/meta/0014_snapshot.json +6503 -0
  296. package/dist/drizzle/meta/0015_snapshot.json +6596 -0
  297. package/dist/drizzle/meta/0016_snapshot.json +6611 -0
  298. package/dist/drizzle/meta/_journal.json +56 -0
  299. package/dist/main.mjs +1311 -363
  300. package/dist/node/main.mjs +314 -12
  301. package/dist/pairing-runtime-controller.mjs +608 -108
  302. package/dist/runtime/base-daemon.mjs +30 -3
  303. package/dist/runtime/broker-daemon.mjs +4771 -529
  304. package/dist/runtime/broker-process-manager.mjs +23 -2
  305. package/dist/runtime/mesh-discover.mjs +23 -2
  306. package/dist/scout-control-plane-web.mjs +1914 -478
  307. package/package.json +25 -6
  308. package/dist/client/assets/AgentLanesEmbedScreen-BmLnN4on.js +0 -1
  309. package/dist/client/assets/AgentLanesPreflight-5B6sXvRB.js +0 -1
  310. package/dist/client/assets/AgentLanesView-DHyzVBk6.js +0 -2
  311. package/dist/client/assets/AgentsSubnav-CDMjKeYC.js +0 -1
  312. package/dist/client/assets/ChatCollapsedStrip-Ycm0ecaX.js +0 -1
  313. package/dist/client/assets/ConversationScreen-BNULZAp2.js +0 -2
  314. package/dist/client/assets/CrewWorkspaces-CJhBKVoZ.css +0 -1
  315. package/dist/client/assets/CrewWorkspaces-CkPYIA46.js +0 -1
  316. package/dist/client/assets/FlightObserveScreen-CDWGSKZg.js +0 -1
  317. package/dist/client/assets/MessagesScreen-Eulveud3.js +0 -1
  318. package/dist/client/assets/NewChatComposer-hlWT-nbR.js +0 -19
  319. package/dist/client/assets/OpsScreen-Ppen_Lmj.js +0 -1
  320. package/dist/client/assets/OpsSubnav-GAKsoSor.js +0 -1
  321. package/dist/client/assets/ProjectAgentProfile-BLlp2e6t.js +0 -1
  322. package/dist/client/assets/ProjectsInbox-Dlt8OQ8O.js +0 -2
  323. package/dist/client/assets/ProjectsRail-DCfNMmnW.js +0 -1
  324. package/dist/client/assets/ProjectsScreen-BP-hmI1m.js +0 -1
  325. package/dist/client/assets/ProjectsThreadAside-CWpMjGIH.js +0 -1
  326. package/dist/client/assets/RealtimeVoiceScreen-D7zZHK_t.js +0 -1
  327. package/dist/client/assets/ScoutDeckSurface-CKT3qdqY.js +0 -1
  328. package/dist/client/assets/SessionEmbedScreen-RD5XOx7R.js +0 -1
  329. package/dist/client/assets/SessionRefScreen-B_cTv47v.js +0 -1
  330. package/dist/client/assets/SessionsScreen-Bq0Z77On.js +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/projects-inbox-BBs1hG13.css +0 -1
  357. package/dist/client/assets/projects-inbox-D-zAb9Gp.js +0 -1
  358. package/dist/client/assets/resolve-panes-DO6LkwhQ.js +0 -1
  359. package/dist/client/assets/resolve-sidebar-context-CIF288wY.js +0 -1
  360. package/dist/client/assets/router-DJj2WbDg.js +0 -1
  361. package/dist/client/assets/use-fleet-active-asks-BeAdcl09.js +0 -1
  362. 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",
@@ -2543,6 +2559,7 @@ var init_broker_routes = __esm(() => {
2543
2559
  home: "/v1/home",
2544
2560
  node: "/v1/node",
2545
2561
  snapshot: "/v1/snapshot",
2562
+ conversationProjection: "/v1/conversation-projection",
2546
2563
  capabilities: "/v1/capabilities",
2547
2564
  runtimeCatalog: "/v1/runtime-catalog",
2548
2565
  topologySnapshot: "/v1/topology/snapshot",
@@ -2614,6 +2631,7 @@ var init_src = __esm(() => {
2614
2631
  init_budget();
2615
2632
  init_host_integrations();
2616
2633
  init_native_interactions();
2634
+ init_session_handle();
2617
2635
  init_runtime_profiles();
2618
2636
  init_runtime_execution();
2619
2637
  init_runtime_catalog();
@@ -9590,8 +9608,18 @@ async function maybeReadJsonFromActiveScoutBrokerService(baseUrl, path) {
9590
9608
  return handled(await service.readNode());
9591
9609
  }
9592
9610
  if (url.pathname === "/v1/snapshot") {
9611
+ const requestedScope = url.searchParams.get("scope");
9593
9612
  return handled(await service.readSnapshot({
9594
- since: parsePositiveInt(url.searchParams.get("since")) ?? null
9613
+ since: parsePositiveInt(url.searchParams.get("since")) ?? null,
9614
+ scope: requestedScope === "conversations" || requestedScope === "agents" ? requestedScope : undefined
9615
+ }));
9616
+ }
9617
+ if (url.pathname === "/v1/conversation-projection") {
9618
+ if (!service.readConversationProjection) {
9619
+ return unhandled();
9620
+ }
9621
+ return handled(await service.readConversationProjection({
9622
+ limit: parseLimit(url.searchParams.get("limit"))
9595
9623
  }));
9596
9624
  }
9597
9625
  if (url.pathname === "/v1/capabilities") {
@@ -12270,11 +12298,32 @@ function resolveBundledRuntimeDirFromModuleDir(moduleDir) {
12270
12298
  resolve6(moduleDir, "..", "..")
12271
12299
  ];
12272
12300
  for (const candidate of candidates) {
12273
- if (isInstalledRuntimePackageDir(candidate))
12274
- return candidate;
12301
+ if (!isInstalledRuntimePackageDir(candidate))
12302
+ continue;
12303
+ const sourceWorkspaceRuntime = resolveSourceWorkspaceRuntimeDir(candidate);
12304
+ if (sourceWorkspaceRuntime)
12305
+ return sourceWorkspaceRuntime;
12306
+ return candidate;
12275
12307
  }
12276
12308
  return null;
12277
12309
  }
12310
+ function resolveSourceWorkspaceRuntimeDir(bundledPackageDir) {
12311
+ const packagesDir = dirname9(bundledPackageDir);
12312
+ if (resolve6(packagesDir, "cli") !== resolve6(bundledPackageDir))
12313
+ return null;
12314
+ const runtimeDir = join11(packagesDir, "runtime");
12315
+ if (!existsSync9(join11(runtimeDir, "src")))
12316
+ return null;
12317
+ if (!isInstalledRuntimePackageDir(runtimeDir))
12318
+ return null;
12319
+ try {
12320
+ const cliPackage = JSON.parse(readFileSync6(join11(bundledPackageDir, "package.json"), "utf8"));
12321
+ const runtimePackage = JSON.parse(readFileSync6(join11(runtimeDir, "package.json"), "utf8"));
12322
+ return cliPackage.name === "@openscout/scout" && runtimePackage.name === "@openscout/runtime" ? runtimeDir : null;
12323
+ } catch {
12324
+ return null;
12325
+ }
12326
+ }
12278
12327
  function findWorkspaceRuntimeDir(startDir) {
12279
12328
  let current = resolve6(startDir);
12280
12329
  while (true) {
@@ -33490,26 +33539,6 @@ var init_local_agents = __esm(() => {
33490
33539
  TMUX_DEFAULT_ROWS = parsePositiveInteger2(process.env.OPENSCOUT_LOCAL_AGENT_TMUX_ROWS, 48);
33491
33540
  });
33492
33541
 
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
33542
  // ../runtime/src/registry.ts
33514
33543
  function createRuntimeRegistrySnapshot(value = {}) {
33515
33544
  return {
@@ -33532,6 +33561,26 @@ var init_registry3 = __esm(() => {
33532
33561
  TERMINAL_COLLABORATION_STATES = new Set(["done", "cancelled", "closed", "declined"]);
33533
33562
  });
33534
33563
 
33564
+ // ../../apps/desktop/src/core/broker/paths.ts
33565
+ var openAiAudioSpeechUrl = "https://api.openai.com/v1/audio/speech";
33566
+ var init_paths = __esm(() => {
33567
+ init_src();
33568
+ });
33569
+
33570
+ // ../../apps/desktop/src/core/broker/ask-metadata.ts
33571
+ function buildScoutAskMetadata(input) {
33572
+ return {
33573
+ source: input.source,
33574
+ ...input.workRecordId ? {
33575
+ collaborationRecordId: input.workRecordId,
33576
+ workId: input.workRecordId
33577
+ } : {},
33578
+ ...input.senderContext ? { senderContext: input.senderContext } : {},
33579
+ ...input.workspace ? { askWorkspace: input.workspace } : {},
33580
+ ...input.labels ? { labels: input.labels } : {}
33581
+ };
33582
+ }
33583
+
33535
33584
  // ../runtime/src/planner.ts
33536
33585
  function createDeliveryId(messageId, targetId, reason, transport) {
33537
33586
  return `del-${messageId}-${targetId}-${reason}-${transport}`;
@@ -37881,7 +37930,7 @@ var init_sqlite_core = __esm(() => {
37881
37930
  });
37882
37931
 
37883
37932
  // ../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;
37933
+ 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
37934
  var init_drizzle_schema = __esm(() => {
37886
37935
  init_drizzle_orm();
37887
37936
  init_sqlite_core();
@@ -37953,7 +38002,10 @@ var init_drizzle_schema = __esm(() => {
37953
38002
  advertiseScope: text("advertise_scope").notNull(),
37954
38003
  ownerId: text("owner_id"),
37955
38004
  metadataJson: text("metadata_json")
37956
- });
38005
+ }, (table3) => [
38006
+ index("idx_agents_home_node_id").on(table3.homeNodeId),
38007
+ index("idx_agents_authority_node_id").on(table3.authorityNodeId)
38008
+ ]);
37957
38009
  agentEndpointsTable = sqliteTable("agent_endpoints", {
37958
38010
  id: text("id").primaryKey(),
37959
38011
  agentId: text("agent_id").notNull().references(() => agentsTable.id, { onDelete: "cascade" }),
@@ -37969,7 +38021,14 @@ var init_drizzle_schema = __esm(() => {
37969
38021
  metadataJson: text("metadata_json"),
37970
38022
  updatedAt: integer("updated_at").notNull().default(epochMsNow)
37971
38023
  }, (table3) => [
37972
- index("idx_agent_endpoints_agent_updated_at").on(table3.agentId, desc(table3.updatedAt))
38024
+ index("idx_agent_endpoints_agent_updated_at").on(table3.agentId, desc(table3.updatedAt)),
38025
+ index("idx_agent_endpoints_roster_recency").on(desc(sql`CASE
38026
+ WHEN ${table3.updatedAt} IS NULL THEN NULL
38027
+ WHEN CAST(${table3.updatedAt} AS REAL) < 1000000000000
38028
+ THEN CAST(CAST(${table3.updatedAt} AS REAL) * 1000 AS INTEGER)
38029
+ ELSE CAST(${table3.updatedAt} AS INTEGER)
38030
+ END`), table3.agentId),
38031
+ index("idx_agent_endpoints_node_id").on(table3.nodeId)
37973
38032
  ]);
37974
38033
  runtimeSessionsTable = sqliteTable("runtime_sessions", {
37975
38034
  id: text("id").primaryKey(),
@@ -37993,6 +38052,7 @@ var init_drizzle_schema = __esm(() => {
37993
38052
  }, (table3) => [
37994
38053
  index("idx_runtime_sessions_agent_last_seen").on(table3.agentId, desc(table3.lastSeenAt)),
37995
38054
  index("idx_runtime_sessions_endpoint_last_seen").on(table3.endpointId, desc(table3.lastSeenAt)),
38055
+ index("idx_runtime_sessions_node_id").on(table3.nodeId),
37996
38056
  index("idx_runtime_sessions_external").on(table3.externalSessionId),
37997
38057
  index("idx_runtime_sessions_expires").on(table3.expiresAt).where(sql`expires_at IS NOT NULL`)
37998
38058
  ]);
@@ -38012,6 +38072,8 @@ var init_drizzle_schema = __esm(() => {
38012
38072
  primaryKey({ columns: [table3.alias, table3.sessionId] }),
38013
38073
  index("idx_runtime_session_aliases_alias").on(table3.alias, desc(table3.lastSeenAt)),
38014
38074
  index("idx_runtime_session_aliases_session").on(table3.sessionId),
38075
+ index("idx_runtime_session_aliases_endpoint").on(table3.endpointId),
38076
+ index("idx_runtime_session_aliases_node_id").on(table3.nodeId),
38015
38077
  index("idx_runtime_session_aliases_expires").on(table3.expiresAt).where(sql`expires_at IS NOT NULL`)
38016
38078
  ]);
38017
38079
  routeAliasBindingsTable = sqliteTable("route_alias_bindings", {
@@ -38091,7 +38153,8 @@ var init_drizzle_schema = __esm(() => {
38091
38153
  actorId: text("actor_id").notNull().references(() => actorsTable.id, { onDelete: "cascade" }),
38092
38154
  role: text("role")
38093
38155
  }, (table3) => [
38094
- primaryKey({ columns: [table3.conversationId, table3.actorId] })
38156
+ primaryKey({ columns: [table3.conversationId, table3.actorId] }),
38157
+ index("idx_conversation_members_actor").on(table3.actorId, table3.conversationId)
38095
38158
  ]);
38096
38159
  messagesTable = sqliteTable("messages", {
38097
38160
  id: text("id").primaryKey(),
@@ -38111,7 +38174,8 @@ var init_drizzle_schema = __esm(() => {
38111
38174
  }, (table3) => [
38112
38175
  index("idx_messages_conversation_created_at").on(table3.conversationId, table3.createdAt),
38113
38176
  index("idx_messages_created_at").on(desc(table3.createdAt)),
38114
- index("idx_messages_actor_created_at").on(table3.actorId, desc(table3.createdAt))
38177
+ index("idx_messages_actor_created_at").on(table3.actorId, desc(table3.createdAt)),
38178
+ index("idx_messages_origin_node_id").on(table3.originNodeId)
38115
38179
  ]);
38116
38180
  messageMentionsTable = sqliteTable("message_mentions", {
38117
38181
  messageId: text("message_id").notNull().references(() => messagesTable.id, { onDelete: "cascade" }),
@@ -38140,7 +38204,102 @@ var init_drizzle_schema = __esm(() => {
38140
38204
  metadataJson: text("metadata_json")
38141
38205
  }, (table3) => [
38142
38206
  primaryKey({ columns: [table3.conversationId, table3.actorId] }),
38143
- index("idx_read_cursors_conversation_updated_at").on(table3.conversationId, desc(table3.updatedAt))
38207
+ index("idx_read_cursors_conversation_updated_at").on(table3.conversationId, desc(table3.updatedAt)),
38208
+ index("idx_read_cursors_reader_node_id").on(table3.readerNodeId)
38209
+ ]);
38210
+ brokerJournalProjectionCheckpointsTable = sqliteTable("broker_journal_projection_checkpoints", {
38211
+ projectionId: text("projection_id").primaryKey(),
38212
+ projectionVersion: integer("projection_version").notNull(),
38213
+ barrierId: text("barrier_id").notNull(),
38214
+ updatedAt: integer("updated_at").notNull()
38215
+ });
38216
+ conversationProjectionMetaTable = sqliteTable("conversation_projection_meta", {
38217
+ singleton: integer("singleton").primaryKey(),
38218
+ projectionId: text("projection_id").notNull(),
38219
+ projectionVersion: integer("projection_version").notNull(),
38220
+ headSeq: integer("head_seq").notNull(),
38221
+ minReplayableSeq: integer("min_replayable_seq").notNull(),
38222
+ updatedAt: integer("updated_at").notNull()
38223
+ }, (table3) => [
38224
+ check("conversation_projection_meta_singleton_check", sql`${table3.singleton} = 1`)
38225
+ ]);
38226
+ conversationProjectionItemsTable = sqliteTable("conversation_projection_items", {
38227
+ feedId: text("feed_id").primaryKey(),
38228
+ entityKind: text("entity_kind").notNull(),
38229
+ kind: text("kind").notNull(),
38230
+ conversationId: text("conversation_id"),
38231
+ runtimeSessionId: text("runtime_session_id"),
38232
+ source: text("source"),
38233
+ sourceSessionId: text("source_session_id"),
38234
+ title: text("title"),
38235
+ alias: text("alias"),
38236
+ naturalKey: text("natural_key"),
38237
+ projectRoot: text("project_root"),
38238
+ harness: text("harness"),
38239
+ model: text("model"),
38240
+ effort: text("effort"),
38241
+ agentId: text("agent_id"),
38242
+ agentName: text("agent_name"),
38243
+ currentBranch: text("current_branch"),
38244
+ authorityNodeId: text("authority_node_id"),
38245
+ authorityNodeName: text("authority_node_name"),
38246
+ parentConversationId: text("parent_conversation_id"),
38247
+ anchorMessageId: text("anchor_message_id"),
38248
+ activityState: text("activity_state").notNull(),
38249
+ lastMessageId: text("last_message_id"),
38250
+ lastMessageAt: integer("last_message_at"),
38251
+ lastActivityAt: integer("last_activity_at").notNull(),
38252
+ messageCount: integer("message_count").notNull().default(0),
38253
+ unreadCount: integer("unread_count").notNull().default(0),
38254
+ participantCount: integer("participant_count").notNull().default(0),
38255
+ preview: text("preview"),
38256
+ lastEngagedAt: integer("last_engaged_at"),
38257
+ sourceFreshAt: integer("source_fresh_at"),
38258
+ visibilityState: text("visibility_state").notNull().default("visible"),
38259
+ updatedSeq: integer("updated_seq").notNull(),
38260
+ updatedAt: integer("updated_at").notNull()
38261
+ }, (table3) => [
38262
+ check("conversation_projection_items_entity_kind_check", sql`${table3.entityKind} IN ('scout_conversation', 'observed_session')`),
38263
+ check("conversation_projection_items_visibility_check", sql`${table3.visibilityState} IN ('visible', 'hidden')`),
38264
+ index("idx_conversation_projection_recent").on(table3.visibilityState, desc(table3.lastActivityAt), table3.feedId),
38265
+ index("idx_conversation_projection_project_recent").on(table3.projectRoot, table3.visibilityState, desc(table3.lastActivityAt)),
38266
+ index("idx_conversation_projection_engaged").on(table3.visibilityState, desc(table3.lastEngagedAt), desc(table3.lastActivityAt), table3.feedId),
38267
+ index("idx_conversation_projection_source_fresh").on(table3.visibilityState, desc(table3.sourceFreshAt)),
38268
+ index("idx_conversation_projection_runtime_session").on(table3.entityKind, table3.runtimeSessionId),
38269
+ index("idx_conversation_projection_source_session").on(table3.entityKind, table3.sourceSessionId),
38270
+ uniqueIndex("idx_conversation_projection_conversation").on(table3.conversationId).where(sql`conversation_id IS NOT NULL`)
38271
+ ]);
38272
+ conversationProjectionMessageFactsTable = sqliteTable("conversation_projection_message_facts", {
38273
+ messageId: text("message_id").primaryKey().references(() => messagesTable.id, { onDelete: "cascade" }),
38274
+ conversationId: text("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
38275
+ createdAt: integer("created_at").notNull()
38276
+ }, (table3) => [
38277
+ index("idx_conversation_projection_message_facts_conversation").on(table3.conversationId, table3.createdAt, table3.messageId)
38278
+ ]);
38279
+ conversationProjectionMessageStatsTable = sqliteTable("conversation_projection_message_stats", {
38280
+ conversationId: text("conversation_id").primaryKey().references(() => conversationsTable.id, { onDelete: "cascade" }),
38281
+ messageCount: integer("message_count").notNull().default(0),
38282
+ latestMessageId: text("latest_message_id"),
38283
+ latestMessageAt: integer("latest_message_at")
38284
+ });
38285
+ conversationProjectionSourcesTable = sqliteTable("conversation_projection_sources", {
38286
+ source: text("source").notNull(),
38287
+ sourceSessionId: text("source_session_id").notNull(),
38288
+ feedId: text("feed_id").notNull().references(() => conversationProjectionItemsTable.feedId, { onDelete: "cascade" }),
38289
+ firstSeenAt: integer("first_seen_at").notNull(),
38290
+ lastSeenAt: integer("last_seen_at").notNull()
38291
+ }, (table3) => [
38292
+ primaryKey({ columns: [table3.source, table3.sourceSessionId] }),
38293
+ index("idx_conversation_projection_sources_feed").on(table3.feedId)
38294
+ ]);
38295
+ conversationProjectionEventsTable = sqliteTable("conversation_projection_events", {
38296
+ seq: integer("seq").primaryKey({ autoIncrement: true }),
38297
+ projectionId: text("projection_id").notNull(),
38298
+ ts: integer("ts").notNull(),
38299
+ payloadJson: text("payload_json").notNull()
38300
+ }, (table3) => [
38301
+ index("idx_conversation_projection_events_projection_seq").on(table3.projectionId, table3.seq),
38302
+ index("idx_conversation_projection_events_ts").on(table3.ts)
38144
38303
  ]);
38145
38304
  invocationsTable = sqliteTable("invocations", {
38146
38305
  id: text("id").primaryKey(),
@@ -38172,7 +38331,10 @@ var init_drizzle_schema = __esm(() => {
38172
38331
  executionResolutionJson: text("execution_resolution_json")
38173
38332
  }, (table3) => [
38174
38333
  index("idx_invocations_target_created_at").on(table3.targetAgentId, table3.createdAt),
38175
- index("idx_invocations_requester_created_at").on(table3.requesterId, desc(table3.createdAt))
38334
+ index("idx_invocations_requester_created_at").on(table3.requesterId, desc(table3.createdAt)),
38335
+ index("idx_invocations_conversation_created_at").on(table3.conversationId, desc(table3.createdAt)),
38336
+ index("idx_invocations_requester_node_id").on(table3.requesterNodeId),
38337
+ index("idx_invocations_target_node_id").on(table3.targetNodeId)
38176
38338
  ]);
38177
38339
  flightsTable = sqliteTable("flights", {
38178
38340
  id: text("id").primaryKey(),
@@ -38218,7 +38380,8 @@ var init_drizzle_schema = __esm(() => {
38218
38380
  createdAt: integer("created_at").notNull().default(epochMsNow)
38219
38381
  }, (table3) => [
38220
38382
  index("idx_deliveries_status_transport").on(table3.status, table3.transport),
38221
- index("idx_deliveries_created_at").on(desc(table3.createdAt))
38383
+ index("idx_deliveries_created_at").on(desc(table3.createdAt)),
38384
+ index("idx_deliveries_target_node_id").on(table3.targetNodeId)
38222
38385
  ]);
38223
38386
  deliveryAttemptsTable = sqliteTable("delivery_attempts", {
38224
38387
  id: text("id").primaryKey(),
@@ -38388,7 +38551,8 @@ var init_drizzle_schema = __esm(() => {
38388
38551
  }, (table3) => [
38389
38552
  unique2().on(table3.conversationId, table3.seq),
38390
38553
  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))
38554
+ index("idx_thread_events_conversation_ts").on(table3.conversationId, desc(table3.ts)),
38555
+ index("idx_thread_events_authority_node_id").on(table3.authorityNodeId)
38392
38556
  ]);
38393
38557
  threadCursorsTable = sqliteTable("thread_cursors", {
38394
38558
  conversationId: text("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
@@ -38396,7 +38560,8 @@ var init_drizzle_schema = __esm(() => {
38396
38560
  lastAppliedSeq: integer("last_applied_seq").notNull(),
38397
38561
  updatedAt: integer("updated_at").notNull()
38398
38562
  }, (table3) => [
38399
- primaryKey({ columns: [table3.conversationId, table3.authorityNodeId] })
38563
+ primaryKey({ columns: [table3.conversationId, table3.authorityNodeId] }),
38564
+ index("idx_thread_cursors_authority_node_id").on(table3.authorityNodeId)
38400
38565
  ]);
38401
38566
  scoutDispatchesTable = sqliteTable("scout_dispatches", {
38402
38567
  id: text("id").primaryKey(),
@@ -38598,6 +38763,13 @@ var init_drizzle_schema = __esm(() => {
38598
38763
  messageMentions: messageMentionsTable,
38599
38764
  messageAttachments: messageAttachmentsTable,
38600
38765
  conversationReadCursors: conversationReadCursorsTable,
38766
+ brokerJournalProjectionCheckpoints: brokerJournalProjectionCheckpointsTable,
38767
+ conversationProjectionMeta: conversationProjectionMetaTable,
38768
+ conversationProjectionItems: conversationProjectionItemsTable,
38769
+ conversationProjectionMessageFacts: conversationProjectionMessageFactsTable,
38770
+ conversationProjectionMessageStats: conversationProjectionMessageStatsTable,
38771
+ conversationProjectionSources: conversationProjectionSourcesTable,
38772
+ conversationProjectionEvents: conversationProjectionEventsTable,
38601
38773
  invocations: invocationsTable,
38602
38774
  flights: flightsTable,
38603
38775
  bindings: bindingsTable,
@@ -38667,6 +38839,8 @@ CREATE INDEX IF NOT EXISTS idx_runtime_sessions_agent_last_seen
38667
38839
  ON runtime_sessions (agent_id, last_seen_at DESC);
38668
38840
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_endpoint_last_seen
38669
38841
  ON runtime_sessions (endpoint_id, last_seen_at DESC);
38842
+ CREATE INDEX IF NOT EXISTS idx_runtime_sessions_node_id
38843
+ ON runtime_sessions (node_id);
38670
38844
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_external
38671
38845
  ON runtime_sessions (external_session_id);
38672
38846
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_expires
@@ -38676,6 +38850,10 @@ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_alias
38676
38850
  ON runtime_session_aliases (alias, last_seen_at DESC);
38677
38851
  CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_session
38678
38852
  ON runtime_session_aliases (session_id);
38853
+ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_endpoint
38854
+ ON runtime_session_aliases (endpoint_id);
38855
+ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_node_id
38856
+ ON runtime_session_aliases (node_id);
38679
38857
  CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_expires
38680
38858
  ON runtime_session_aliases (expires_at)
38681
38859
  WHERE expires_at IS NOT NULL;
@@ -38938,6 +39116,93 @@ CREATE TABLE IF NOT EXISTS conversation_read_cursors (
38938
39116
  PRIMARY KEY (conversation_id, actor_id)
38939
39117
  );
38940
39118
 
39119
+ CREATE TABLE IF NOT EXISTS broker_journal_projection_checkpoints (
39120
+ projection_id TEXT PRIMARY KEY,
39121
+ projection_version INTEGER NOT NULL,
39122
+ barrier_id TEXT NOT NULL,
39123
+ updated_at INTEGER NOT NULL
39124
+ );
39125
+
39126
+ CREATE TABLE IF NOT EXISTS conversation_projection_meta (
39127
+ singleton INTEGER PRIMARY KEY,
39128
+ projection_id TEXT NOT NULL,
39129
+ projection_version INTEGER NOT NULL,
39130
+ head_seq INTEGER NOT NULL,
39131
+ min_replayable_seq INTEGER NOT NULL,
39132
+ updated_at INTEGER NOT NULL,
39133
+ CONSTRAINT conversation_projection_meta_singleton_check CHECK (singleton = 1)
39134
+ );
39135
+
39136
+ CREATE TABLE IF NOT EXISTS conversation_projection_items (
39137
+ feed_id TEXT PRIMARY KEY,
39138
+ entity_kind TEXT NOT NULL,
39139
+ kind TEXT NOT NULL,
39140
+ conversation_id TEXT,
39141
+ runtime_session_id TEXT,
39142
+ source TEXT,
39143
+ source_session_id TEXT,
39144
+ title TEXT,
39145
+ alias TEXT,
39146
+ natural_key TEXT,
39147
+ project_root TEXT,
39148
+ harness TEXT,
39149
+ model TEXT,
39150
+ effort TEXT,
39151
+ agent_id TEXT,
39152
+ agent_name TEXT,
39153
+ current_branch TEXT,
39154
+ authority_node_id TEXT,
39155
+ authority_node_name TEXT,
39156
+ parent_conversation_id TEXT,
39157
+ anchor_message_id TEXT,
39158
+ activity_state TEXT NOT NULL,
39159
+ last_message_id TEXT,
39160
+ last_message_at INTEGER,
39161
+ last_activity_at INTEGER NOT NULL,
39162
+ message_count INTEGER NOT NULL DEFAULT 0,
39163
+ unread_count INTEGER NOT NULL DEFAULT 0,
39164
+ participant_count INTEGER NOT NULL DEFAULT 0,
39165
+ preview TEXT,
39166
+ last_engaged_at INTEGER,
39167
+ source_fresh_at INTEGER,
39168
+ visibility_state TEXT NOT NULL DEFAULT 'visible',
39169
+ updated_seq INTEGER NOT NULL,
39170
+ updated_at INTEGER NOT NULL,
39171
+ CONSTRAINT conversation_projection_items_entity_kind_check
39172
+ CHECK (entity_kind IN ('scout_conversation', 'observed_session')),
39173
+ CONSTRAINT conversation_projection_items_visibility_check
39174
+ CHECK (visibility_state IN ('visible', 'hidden'))
39175
+ );
39176
+
39177
+ CREATE TABLE IF NOT EXISTS conversation_projection_message_facts (
39178
+ message_id TEXT PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
39179
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
39180
+ created_at INTEGER NOT NULL
39181
+ );
39182
+
39183
+ CREATE TABLE IF NOT EXISTS conversation_projection_message_stats (
39184
+ conversation_id TEXT PRIMARY KEY REFERENCES conversations(id) ON DELETE CASCADE,
39185
+ message_count INTEGER NOT NULL DEFAULT 0,
39186
+ latest_message_id TEXT,
39187
+ latest_message_at INTEGER
39188
+ );
39189
+
39190
+ CREATE TABLE IF NOT EXISTS conversation_projection_sources (
39191
+ source TEXT NOT NULL,
39192
+ source_session_id TEXT NOT NULL,
39193
+ feed_id TEXT NOT NULL REFERENCES conversation_projection_items(feed_id) ON DELETE CASCADE,
39194
+ first_seen_at INTEGER NOT NULL,
39195
+ last_seen_at INTEGER NOT NULL,
39196
+ PRIMARY KEY (source, source_session_id)
39197
+ );
39198
+
39199
+ CREATE TABLE IF NOT EXISTS conversation_projection_events (
39200
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
39201
+ projection_id TEXT NOT NULL,
39202
+ ts INTEGER NOT NULL,
39203
+ payload_json TEXT NOT NULL
39204
+ );
39205
+
38941
39206
  CREATE TABLE IF NOT EXISTS invocations (
38942
39207
  id TEXT PRIMARY KEY,
38943
39208
  requester_id TEXT NOT NULL REFERENCES actors(id) ON DELETE RESTRICT,
@@ -39345,18 +39610,74 @@ CREATE INDEX IF NOT EXISTS idx_peer_nonces_seen_at
39345
39610
  ON peer_nonces (seen_at);
39346
39611
  CREATE INDEX IF NOT EXISTS idx_agent_endpoints_agent_updated_at
39347
39612
  ON agent_endpoints (agent_id, updated_at DESC);
39613
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_roster_recency
39614
+ ON agent_endpoints (
39615
+ CASE
39616
+ WHEN updated_at IS NULL THEN NULL
39617
+ WHEN CAST(updated_at AS REAL) < 1000000000000
39618
+ THEN CAST(CAST(updated_at AS REAL) * 1000 AS INTEGER)
39619
+ ELSE CAST(updated_at AS INTEGER)
39620
+ END DESC,
39621
+ agent_id
39622
+ );
39623
+ CREATE INDEX IF NOT EXISTS idx_agents_home_node_id
39624
+ ON agents (home_node_id);
39625
+ CREATE INDEX IF NOT EXISTS idx_agents_authority_node_id
39626
+ ON agents (authority_node_id);
39627
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_node_id
39628
+ ON agent_endpoints (node_id);
39629
+ CREATE INDEX IF NOT EXISTS idx_conversation_members_actor
39630
+ ON conversation_members (actor_id, conversation_id);
39348
39631
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_created_at
39349
39632
  ON messages (conversation_id, created_at);
39350
39633
  CREATE INDEX IF NOT EXISTS idx_messages_created_at
39351
39634
  ON messages (created_at DESC);
39352
39635
  CREATE INDEX IF NOT EXISTS idx_messages_actor_created_at
39353
39636
  ON messages (actor_id, created_at DESC);
39637
+ CREATE INDEX IF NOT EXISTS idx_messages_origin_node_id
39638
+ ON messages (origin_node_id);
39354
39639
  CREATE INDEX IF NOT EXISTS idx_read_cursors_conversation_updated_at
39355
39640
  ON conversation_read_cursors (conversation_id, updated_at DESC);
39641
+ CREATE INDEX IF NOT EXISTS idx_read_cursors_reader_node_id
39642
+ ON conversation_read_cursors (reader_node_id);
39643
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_recent
39644
+ ON conversation_projection_items (visibility_state, last_activity_at DESC, feed_id);
39645
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_project_recent
39646
+ ON conversation_projection_items (project_root, visibility_state, last_activity_at DESC);
39647
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_message_facts_conversation
39648
+ ON conversation_projection_message_facts (conversation_id, created_at, message_id);
39649
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_engaged
39650
+ ON conversation_projection_items (
39651
+ visibility_state,
39652
+ last_engaged_at DESC,
39653
+ last_activity_at DESC,
39654
+ feed_id
39655
+ );
39656
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_source_fresh
39657
+ ON conversation_projection_items (visibility_state, source_fresh_at DESC);
39658
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_runtime_session
39659
+ ON conversation_projection_items (entity_kind, runtime_session_id);
39660
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_source_session
39661
+ ON conversation_projection_items (entity_kind, source_session_id);
39662
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_conversation_projection_conversation
39663
+ ON conversation_projection_items (conversation_id)
39664
+ WHERE conversation_id IS NOT NULL;
39665
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_sources_feed
39666
+ ON conversation_projection_sources (feed_id);
39667
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_events_projection_seq
39668
+ ON conversation_projection_events (projection_id, seq);
39669
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_events_ts
39670
+ ON conversation_projection_events (ts);
39356
39671
  CREATE INDEX IF NOT EXISTS idx_invocations_target_created_at
39357
39672
  ON invocations (target_agent_id, created_at);
39358
39673
  CREATE INDEX IF NOT EXISTS idx_invocations_requester_created_at
39359
39674
  ON invocations (requester_id, created_at DESC);
39675
+ CREATE INDEX IF NOT EXISTS idx_invocations_conversation_created_at
39676
+ ON invocations (conversation_id, created_at DESC);
39677
+ CREATE INDEX IF NOT EXISTS idx_invocations_requester_node_id
39678
+ ON invocations (requester_node_id);
39679
+ CREATE INDEX IF NOT EXISTS idx_invocations_target_node_id
39680
+ ON invocations (target_node_id);
39360
39681
  CREATE INDEX IF NOT EXISTS idx_flights_target_state
39361
39682
  ON flights (target_agent_id, state);
39362
39683
  CREATE INDEX IF NOT EXISTS idx_flights_invocation_id
@@ -39365,6 +39686,8 @@ CREATE INDEX IF NOT EXISTS idx_deliveries_status_transport
39365
39686
  ON deliveries (status, transport);
39366
39687
  CREATE INDEX IF NOT EXISTS idx_deliveries_created_at
39367
39688
  ON deliveries (created_at DESC);
39689
+ CREATE INDEX IF NOT EXISTS idx_deliveries_target_node_id
39690
+ ON deliveries (target_node_id);
39368
39691
  CREATE INDEX IF NOT EXISTS idx_delivery_attempts_created_at
39369
39692
  ON delivery_attempts (created_at DESC);
39370
39693
  CREATE UNIQUE INDEX IF NOT EXISTS idx_durable_actions_idempotency_key
@@ -39411,6 +39734,10 @@ CREATE INDEX IF NOT EXISTS idx_thread_events_conversation_seq
39411
39734
  ON thread_events (conversation_id, seq DESC);
39412
39735
  CREATE INDEX IF NOT EXISTS idx_thread_events_conversation_ts
39413
39736
  ON thread_events (conversation_id, ts DESC);
39737
+ CREATE INDEX IF NOT EXISTS idx_thread_events_authority_node_id
39738
+ ON thread_events (authority_node_id);
39739
+ CREATE INDEX IF NOT EXISTS idx_thread_cursors_authority_node_id
39740
+ ON thread_cursors (authority_node_id);
39414
39741
  CREATE INDEX IF NOT EXISTS idx_activity_items_agent_ts
39415
39742
  ON activity_items (agent_id, ts DESC);
39416
39743
  CREATE INDEX IF NOT EXISTS idx_activity_items_actor_ts
@@ -40638,7 +40965,23 @@ CREATE INDEX IF NOT EXISTS idx_activity_items_ts
40638
40965
  ON activity_items(ts DESC);
40639
40966
  CREATE INDEX IF NOT EXISTS idx_conversations_created_at
40640
40967
  ON conversations(created_at DESC);
40968
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_roster_recency
40969
+ ON agent_endpoints (
40970
+ CASE
40971
+ WHEN updated_at IS NULL THEN NULL
40972
+ WHEN CAST(updated_at AS REAL) < 1000000000000
40973
+ THEN CAST(CAST(updated_at AS REAL) * 1000 AS INTEGER)
40974
+ ELSE CAST(updated_at AS INTEGER)
40975
+ END DESC,
40976
+ agent_id
40977
+ );
40978
+ `);
40979
+ if (hasColumn(database, "conversations", "authority_node_id")) {
40980
+ database.exec(`
40981
+ CREATE INDEX IF NOT EXISTS idx_conversations_authority_node_id
40982
+ ON conversations(authority_node_id);
40641
40983
  `);
40984
+ }
40642
40985
  }
40643
40986
  },
40644
40987
  {
@@ -41014,8 +41357,39 @@ var init_budget_observations2 = __esm(() => {
41014
41357
  QUOTA_HISTORY_BUCKET_MS = 60 * 60 * 1000;
41015
41358
  });
41016
41359
 
41017
- // ../runtime/src/sqlite-store.ts
41360
+ // ../runtime/src/runtime-session-handle.ts
41018
41361
  import { createHash as createHash4 } from "crypto";
41362
+ function metadataStringValue(metadata, key) {
41363
+ const value = metadata?.[key];
41364
+ return typeof value === "string" && value.trim() ? value.trim() : null;
41365
+ }
41366
+ function firstStringValue(...values) {
41367
+ return values.find((value) => Boolean(value?.trim()))?.trim() ?? null;
41368
+ }
41369
+ function runtimeSessionPrimaryAlias(endpoint) {
41370
+ const metadata = endpoint.metadata;
41371
+ if (metadata?.cardless === true) {
41372
+ return firstStringValue(metadataStringValue(metadata, "handle"), metadataStringValue(metadata, "externalSessionId"), metadataStringValue(metadata, "threadId"), endpoint.sessionId, endpoint.agentId);
41373
+ }
41374
+ 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"));
41375
+ }
41376
+ function runtimeSessionHandleForEndpoint(endpoint, primaryAlias = runtimeSessionPrimaryAlias(endpoint)) {
41377
+ if (!primaryAlias)
41378
+ return null;
41379
+ const resolutionKey = [
41380
+ endpoint.nodeId,
41381
+ endpoint.harness,
41382
+ primaryAlias
41383
+ ].join("\x00");
41384
+ const token = createHash4("sha256").update(resolutionKey).digest("hex").slice(0, 20);
41385
+ return `${SCOUT_SESSION_HANDLE_PREFIX}${token}`;
41386
+ }
41387
+ var init_runtime_session_handle = __esm(() => {
41388
+ init_src();
41389
+ });
41390
+
41391
+ // ../runtime/src/sqlite-store.ts
41392
+ import { createHash as createHash5 } from "crypto";
41019
41393
  import { mkdirSync as mkdirSync7 } from "fs";
41020
41394
  import { dirname as dirname18 } from "path";
41021
41395
  function parseJson(value, fallback2) {
@@ -41027,6 +41401,16 @@ function parseJson(value, fallback2) {
41027
41401
  return fallback2;
41028
41402
  }
41029
41403
  }
41404
+ function isTransientSqliteBusyError(error) {
41405
+ if (typeof error === "object" && error !== null) {
41406
+ const code = error.code;
41407
+ if (code === "SQLITE_BUSY" || code === "SQLITE_LOCKED") {
41408
+ return true;
41409
+ }
41410
+ }
41411
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
41412
+ return message.includes("database is locked") || message.includes("database is busy") || message.includes("sqlite_busy");
41413
+ }
41030
41414
  function stringify3(value) {
41031
41415
  return value === undefined ? null : JSON.stringify(value);
41032
41416
  }
@@ -41037,9 +41421,9 @@ function normalizeTimestampMs(value) {
41037
41421
  return epochMs(value);
41038
41422
  }
41039
41423
  function stableHash2(value, length = 20) {
41040
- return createHash4("sha256").update(value).digest("hex").slice(0, length);
41424
+ return createHash5("sha256").update(value).digest("hex").slice(0, length);
41041
41425
  }
41042
- function metadataStringValue(metadata, key) {
41426
+ function metadataStringValue2(metadata, key) {
41043
41427
  const value = metadata?.[key];
41044
41428
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
41045
41429
  }
@@ -41056,7 +41440,7 @@ function metadataTimestampValue(metadata, key) {
41056
41440
  }
41057
41441
  return null;
41058
41442
  }
41059
- function firstStringValue(...values) {
41443
+ function firstStringValue2(...values) {
41060
41444
  for (const value of values) {
41061
41445
  const trimmed = value?.trim();
41062
41446
  if (trimmed) {
@@ -41274,16 +41658,9 @@ function runtimeSessionAliasFromRow(row) {
41274
41658
  function runtimeSessionMetadata(endpoint) {
41275
41659
  return endpoint.metadata ?? {};
41276
41660
  }
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
41661
  function endpointRuntimeSessionExternalId(endpoint) {
41285
41662
  const metadata = runtimeSessionMetadata(endpoint);
41286
- return firstStringValue(metadataStringValue(metadata, "externalSessionId"), metadataStringValue(metadata, "threadId"), metadataStringValue(metadata, "nativeSessionId"), metadataStringValue(metadata, "pairingSessionId"));
41663
+ return firstStringValue2(metadataStringValue2(metadata, "externalSessionId"), metadataStringValue2(metadata, "threadId"), metadataStringValue2(metadata, "nativeSessionId"), metadataStringValue2(metadata, "pairingSessionId"));
41287
41664
  }
41288
41665
  function endpointRuntimeSessionStartedAt(endpoint, fallback2) {
41289
41666
  const metadata = runtimeSessionMetadata(endpoint);
@@ -41299,16 +41676,6 @@ function endpointRuntimeSessionIsTerminal(endpoint) {
41299
41676
  function endpointRuntimeSessionState(endpoint) {
41300
41677
  return endpoint.metadata?.staleLocalRegistration === true ? "superseded" : endpoint.state;
41301
41678
  }
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
41679
  function endpointRuntimeSessionAliases(endpoint, scoutSessionId) {
41313
41680
  const metadata = runtimeSessionMetadata(endpoint);
41314
41681
  const aliases = [
@@ -41319,12 +41686,12 @@ function endpointRuntimeSessionAliases(endpoint, scoutSessionId) {
41319
41686
  aliases.push({ alias: endpoint.sessionId.trim(), aliasKind: "endpoint_session" });
41320
41687
  }
41321
41688
  for (const entry of RUNTIME_SESSION_ALIAS_METADATA_KEYS) {
41322
- const value = metadataStringValue(metadata, entry.key);
41689
+ const value = metadataStringValue2(metadata, entry.key);
41323
41690
  if (value) {
41324
41691
  aliases.push({ alias: value, aliasKind: entry.kind });
41325
41692
  }
41326
41693
  }
41327
- const provisionalHandle = metadataStringValue(metadata, "handle");
41694
+ const provisionalHandle = metadataStringValue2(metadata, "handle");
41328
41695
  if (provisionalHandle) {
41329
41696
  aliases.push({ alias: provisionalHandle, aliasKind: "provisional" });
41330
41697
  }
@@ -41443,6 +41810,7 @@ class SQLiteControlPlaneStore {
41443
41810
  pendingEvents = [];
41444
41811
  flushPendingEventsTimer = null;
41445
41812
  conversationsApi = null;
41813
+ closed = false;
41446
41814
  constructor(dbPath) {
41447
41815
  mkdirSync7(dirname18(dbPath), { recursive: true });
41448
41816
  this.db = openControlPlaneSqliteDatabase(dbPath, { create: true });
@@ -41461,6 +41829,9 @@ class SQLiteControlPlaneStore {
41461
41829
  }
41462
41830
  });
41463
41831
  }
41832
+ get isClosed() {
41833
+ return this.closed;
41834
+ }
41464
41835
  listRuntimeSessions(options = {}) {
41465
41836
  const predicates = [];
41466
41837
  const params = [];
@@ -41717,6 +42088,10 @@ class SQLiteControlPlaneStore {
41717
42088
  };
41718
42089
  }
41719
42090
  close() {
42091
+ if (this.closed) {
42092
+ return;
42093
+ }
42094
+ this.closed = true;
41720
42095
  this.flushPendingEvents();
41721
42096
  if (this.flushPendingEventsTimer) {
41722
42097
  clearTimeout(this.flushPendingEventsTimer);
@@ -41731,16 +42106,29 @@ class SQLiteControlPlaneStore {
41731
42106
  }
41732
42107
  const nextBatch = this.pendingEvents;
41733
42108
  this.pendingEvents = [];
41734
- this.persistEventsBatch(nextBatch);
42109
+ try {
42110
+ this.persistEventsBatch(nextBatch);
42111
+ } catch (error) {
42112
+ this.pendingEvents = [...nextBatch, ...this.pendingEvents];
42113
+ throw error;
42114
+ }
41735
42115
  }
41736
- schedulePendingEventFlush() {
42116
+ schedulePendingEventFlush(delayMs = 0) {
41737
42117
  if (this.flushPendingEventsTimer) {
41738
42118
  return;
41739
42119
  }
41740
42120
  this.flushPendingEventsTimer = setTimeout(() => {
41741
42121
  this.flushPendingEventsTimer = null;
41742
- this.flushPendingEvents();
41743
- }, 0);
42122
+ try {
42123
+ this.flushPendingEvents();
42124
+ } catch (error) {
42125
+ if (isTransientSqliteBusyError(error)) {
42126
+ this.schedulePendingEventFlush(PENDING_EVENT_BUSY_RETRY_MS);
42127
+ return;
42128
+ }
42129
+ throw error;
42130
+ }
42131
+ }, delayMs);
41744
42132
  this.flushPendingEventsTimer.unref?.();
41745
42133
  }
41746
42134
  loadSnapshot() {
@@ -41886,7 +42274,7 @@ class SQLiteControlPlaneStore {
41886
42274
  audience: parseJson(row.audience_json, undefined),
41887
42275
  visibility: row.visibility,
41888
42276
  policy: row.policy,
41889
- createdAt: row.created_at,
42277
+ createdAt: normalizeTimestampMs(row.created_at) ?? row.created_at,
41890
42278
  metadata: parseJson(row.metadata_json, undefined)
41891
42279
  };
41892
42280
  }
@@ -41992,6 +42380,51 @@ class SQLiteControlPlaneStore {
41992
42380
  activeFlights: this.listConversationThreadFlights(conversation)
41993
42381
  };
41994
42382
  }
42383
+ getConversationThreadLaunchSnapshot(options) {
42384
+ const conversation = this.getConversation(options.conversationId, this.readDb);
42385
+ if (!conversation) {
42386
+ return null;
42387
+ }
42388
+ const requestedLimit = Math.floor(options.limit ?? 64);
42389
+ const limit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(64, requestedLimit)) : 64;
42390
+ const rows = queryAll(this.readDb, `SELECT
42391
+ m.id,
42392
+ m.actor_id,
42393
+ a.display_name AS actor_name,
42394
+ m.body,
42395
+ m.class,
42396
+ m.created_at
42397
+ FROM messages m
42398
+ LEFT JOIN actors a ON a.id = m.actor_id
42399
+ WHERE m.conversation_id = ?1
42400
+ ORDER BY CASE
42401
+ WHEN m.created_at > 0 AND m.created_at < ${EPOCH_MILLISECONDS_FLOOR}
42402
+ THEN m.created_at * 1000
42403
+ ELSE m.created_at
42404
+ END DESC, m.id DESC
42405
+ LIMIT ?2`, options.conversationId, limit + 1);
42406
+ const hasEarlier = rows.length > limit;
42407
+ const page = rows.slice(0, limit).reverse();
42408
+ return {
42409
+ projectionId: options.projectionId,
42410
+ projectionVersion: options.projectionVersion,
42411
+ sequence: options.sequence,
42412
+ feedId: scoutConversationFeedId(options.conversationId),
42413
+ entityKind: "scout_conversation",
42414
+ conversationId: options.conversationId,
42415
+ cursor: page[0]?.id ?? null,
42416
+ hasEarlier,
42417
+ generatedAt: options.generatedAt ?? currentTimestampMs(),
42418
+ messages: page.map((row) => ({
42419
+ id: row.id,
42420
+ actorId: row.actor_id,
42421
+ actorName: row.actor_name,
42422
+ body: row.body,
42423
+ class: row.class,
42424
+ createdAt: row.created_at
42425
+ }))
42426
+ };
42427
+ }
41995
42428
  upsertThreadCursor(conversationId, authorityNodeId, lastAppliedSeq, updatedAt) {
41996
42429
  this.db.query(`INSERT INTO thread_cursors (
41997
42430
  conversation_id, authority_node_id, last_applied_seq, updated_at
@@ -42202,12 +42635,16 @@ class SQLiteControlPlaneStore {
42202
42635
  WHERE endpoint_id = ?3`).run(expiresAt, observedAt2, endpointId);
42203
42636
  }
42204
42637
  projectRuntimeSessionForEndpoint(endpoint, observedAt2) {
42205
- const primaryAlias = endpointRuntimeSessionPrimaryAlias(endpoint);
42638
+ const primaryAlias = runtimeSessionPrimaryAlias(endpoint);
42206
42639
  if (!primaryAlias) {
42207
42640
  this.markRuntimeSessionsForEndpointEnded(endpoint.id, null, observedAt2);
42208
42641
  return;
42209
42642
  }
42210
- const sessionId = endpointRuntimeSessionId(endpoint, primaryAlias);
42643
+ const sessionId = runtimeSessionHandleForEndpoint(endpoint, primaryAlias);
42644
+ if (!sessionId) {
42645
+ this.markRuntimeSessionsForEndpointEnded(endpoint.id, null, observedAt2);
42646
+ return;
42647
+ }
42211
42648
  const lastSeenAt = endpointRuntimeSessionLastSeenAt(endpoint, observedAt2);
42212
42649
  const startedAt = endpointRuntimeSessionStartedAt(endpoint, lastSeenAt);
42213
42650
  const terminal = endpointRuntimeSessionIsTerminal(endpoint);
@@ -42557,10 +42994,23 @@ class SQLiteControlPlaneStore {
42557
42994
  targetAgentId: identity.target_agent_id
42558
42995
  };
42559
42996
  }
42560
- this.db.query(`INSERT OR REPLACE INTO flights (
42997
+ this.db.query(`INSERT INTO flights (
42561
42998
  id, invocation_id, requester_id, target_agent_id, state, summary, output, error,
42562
42999
  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);
43000
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
43001
+ ON CONFLICT(id) DO UPDATE SET
43002
+ rowid = (SELECT COALESCE(MAX(rowid), 0) + 1 FROM flights),
43003
+ invocation_id = excluded.invocation_id,
43004
+ requester_id = excluded.requester_id,
43005
+ target_agent_id = excluded.target_agent_id,
43006
+ state = excluded.state,
43007
+ summary = excluded.summary,
43008
+ output = excluded.output,
43009
+ error = excluded.error,
43010
+ labels_json = excluded.labels_json,
43011
+ metadata_json = excluded.metadata_json,
43012
+ started_at = excluded.started_at,
43013
+ 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
43014
  this.db.query(`UPDATE invocations SET
42565
43015
  flight_id = ?2, state = ?3, summary = ?4, output = ?5, error = ?6,
42566
43016
  started_at = ?7, completed_at = ?8, flight_metadata_json = ?9
@@ -42740,42 +43190,48 @@ class SQLiteControlPlaneStore {
42740
43190
  }));
42741
43191
  }
42742
43192
  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
- }
43193
+ if (deliveries2.length === 0)
43194
+ return;
43195
+ const statement = this.db.query(`INSERT INTO deliveries (
43196
+ id, message_id, invocation_id, target_id, target_node_id, target_kind,
43197
+ transport, reason, policy, status, binding_id, lease_owner, lease_expires_at,
43198
+ metadata_json, created_at
43199
+ ) VALUES (
43200
+ ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15
43201
+ )
43202
+ ON CONFLICT(id) DO UPDATE SET
43203
+ message_id = excluded.message_id,
43204
+ invocation_id = excluded.invocation_id,
43205
+ target_id = excluded.target_id,
43206
+ target_node_id = excluded.target_node_id,
43207
+ target_kind = excluded.target_kind,
43208
+ transport = excluded.transport,
43209
+ reason = excluded.reason,
43210
+ policy = excluded.policy,
43211
+ status = excluded.status,
43212
+ binding_id = excluded.binding_id,
43213
+ lease_owner = excluded.lease_owner,
43214
+ lease_expires_at = excluded.lease_expires_at,
43215
+ metadata_json = excluded.metadata_json`);
43216
+ this.db.transaction((batch) => {
43217
+ const createdAtBySource = new Map;
43218
+ for (const delivery of batch) {
43219
+ const sourceKey = delivery.messageId || delivery.invocationId ? `${delivery.messageId ?? ""}\x00${delivery.invocationId ?? ""}` : null;
43220
+ let createdAt;
43221
+ if (sourceKey === null) {
43222
+ createdAt = this.deliveryCreatedAt(delivery);
43223
+ } else {
43224
+ const cached = createdAtBySource.get(sourceKey);
43225
+ if (cached !== undefined) {
43226
+ createdAt = cached;
43227
+ } else {
43228
+ createdAt = this.deliveryCreatedAt(delivery);
43229
+ createdAtBySource.set(sourceKey, createdAt);
43230
+ }
43231
+ }
43232
+ 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);
43233
+ }
43234
+ })(deliveries2);
42779
43235
  }
42780
43236
  deliveryCreatedAt(delivery) {
42781
43237
  const messageCreatedAt = delivery.messageId ? queryGet(this.db, "SELECT created_at FROM messages WHERE id = ?1 LIMIT 1", delivery.messageId)?.created_at : null;
@@ -43280,6 +43736,17 @@ class SQLiteControlPlaneStore {
43280
43736
  get writerDb() {
43281
43737
  return this.db;
43282
43738
  }
43739
+ runReplayTransaction(operation) {
43740
+ this.db.transaction(() => {
43741
+ const previousReadDb = this.readDb;
43742
+ this.readDb = this.db;
43743
+ try {
43744
+ operation();
43745
+ } finally {
43746
+ this.readDb = previousReadDb;
43747
+ }
43748
+ })();
43749
+ }
43283
43750
  get readerDb() {
43284
43751
  return this.readDb;
43285
43752
  }
@@ -43563,7 +44030,7 @@ class SQLiteControlPlaneStore {
43563
44030
  this.schedulePendingEventFlush();
43564
44031
  }
43565
44032
  }
43566
- var RUNTIME_SESSION_RETENTION_MS, RUNTIME_SESSION_ALIAS_METADATA_KEYS;
44033
+ var PENDING_EVENT_BUSY_RETRY_MS = 25, RUNTIME_SESSION_RETENTION_MS, RUNTIME_SESSION_ALIAS_METADATA_KEYS;
43567
44034
  var init_sqlite_store = __esm(() => {
43568
44035
  init_drizzle_orm();
43569
44036
  init_node_identity();
@@ -43575,6 +44042,7 @@ var init_sqlite_store = __esm(() => {
43575
44042
  init_api();
43576
44043
  init_drizzle_schema();
43577
44044
  init_budget_observations2();
44045
+ init_runtime_session_handle();
43578
44046
  RUNTIME_SESSION_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
43579
44047
  RUNTIME_SESSION_ALIAS_METADATA_KEYS = [
43580
44048
  { key: "sessionId", kind: "metadata_session" },
@@ -43589,7 +44057,7 @@ var init_sqlite_store = __esm(() => {
43589
44057
  });
43590
44058
 
43591
44059
  // ../runtime/src/mesh-peer-auth.ts
43592
- import { createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
44060
+ import { createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
43593
44061
  function assertNoNewlines(component, name) {
43594
44062
  if (component.includes(`
43595
44063
  `) || component.includes("\r")) {
@@ -43615,7 +44083,7 @@ function peerRequestSigningPayload(input) {
43615
44083
  `);
43616
44084
  }
43617
44085
  function sha256Hex(body) {
43618
- return createHash5("sha256").update(body).digest("hex");
44086
+ return createHash6("sha256").update(body).digest("hex");
43619
44087
  }
43620
44088
  function signPeerRequest(identity, input) {
43621
44089
  const ts = input.ts ?? Date.now();
@@ -58671,14 +59139,14 @@ var init_x509_es = __esm(() => {
58671
59139
 
58672
59140
  // ../runtime/src/node-tls-identity.ts
58673
59141
  import {
58674
- createHash as createHash6,
59142
+ createHash as createHash7,
58675
59143
  createPrivateKey as createPrivateKey2,
58676
59144
  createPublicKey as createPublicKey2,
58677
59145
  generateKeyPairSync as generateKeyPairSync2,
58678
59146
  randomBytes as randomBytes3
58679
59147
  } from "crypto";
58680
59148
  function tlsSpkiFingerprintFromDer(spkiDer) {
58681
- return createHash6("sha256").update(spkiDer).digest("hex");
59149
+ return createHash7("sha256").update(spkiDer).digest("hex");
58682
59150
  }
58683
59151
  function tlsSpkiFingerprintFromCertificateDer(certificateDer) {
58684
59152
  const certificate2 = new X509Certificate(asBufferSource(certificateDer));
@@ -59302,10 +59770,12 @@ var init_mesh_dial_order = __esm(() => {
59302
59770
  });
59303
59771
 
59304
59772
  // ../runtime/src/mesh-forwarding.ts
59773
+ var MAX_PEER_AGENT_SNAPSHOT_BYTES;
59305
59774
  var init_mesh_forwarding = __esm(() => {
59306
59775
  init_iroh_bridge();
59307
59776
  init_mesh_peer_client();
59308
59777
  init_mesh_dial_order();
59778
+ MAX_PEER_AGENT_SNAPSHOT_BYTES = 2 * 1024 * 1024;
59309
59779
  });
59310
59780
 
59311
59781
  // ../runtime/src/mesh-rendezvous.ts
@@ -60639,9 +61109,9 @@ var init_eff_short_wordlist = __esm(() => {
60639
61109
  });
60640
61110
 
60641
61111
  // ../runtime/src/mesh-sas.ts
60642
- import { createHash as createHash7, createHmac } from "crypto";
61112
+ import { createHash as createHash8, createHmac } from "crypto";
60643
61113
  function createEnrollmentCommitment(nonce, cardSignature) {
60644
- return createHash7("sha256").update(Buffer.concat([nonce, Buffer.from(cardSignature, "utf8")])).digest("base64");
61114
+ return createHash8("sha256").update(Buffer.concat([nonce, Buffer.from(cardSignature, "utf8")])).digest("base64");
60645
61115
  }
60646
61116
  function verifyEnrollmentCommitment(commitment, nonce, cardSignature) {
60647
61117
  const expected = createEnrollmentCommitment(nonce, cardSignature);
@@ -62068,6 +62538,7 @@ var TERMINAL_ENDPOINT_STATES;
62068
62538
  var init_broker_endpoint_selection = __esm(() => {
62069
62539
  init_a2a_http_endpoint();
62070
62540
  init_local_agent_transports();
62541
+ init_runtime_session_handle();
62071
62542
  TERMINAL_ENDPOINT_STATES = new Set([
62072
62543
  "failed",
62073
62544
  "superseded",
@@ -64470,11 +64941,11 @@ var init_paths2 = __esm(() => {
64470
64941
 
64471
64942
  // ../runtime/src/knowledge/store.ts
64472
64943
  import { existsSync as existsSync31, mkdirSync as mkdirSync10, statSync as statSync8 } from "fs";
64473
- import { createHash as createHash8, randomUUID as randomUUID4 } from "crypto";
64944
+ import { createHash as createHash9, randomUUID as randomUUID4 } from "crypto";
64474
64945
  import { dirname as dirname21 } from "path";
64475
64946
  import { Database as Database3 } from "bun:sqlite";
64476
64947
  function ftsRowidForChunk(chunkId) {
64477
- return createHash8("sha256").update(chunkId).digest().readBigInt64BE(0);
64948
+ return createHash9("sha256").update(chunkId).digest().readBigInt64BE(0);
64478
64949
  }
64479
64950
  function stringify4(value) {
64480
64951
  return JSON.stringify(value ?? null);
@@ -64513,7 +64984,7 @@ function normalizeFtsQueryOr(value) {
64513
64984
  return terms.map(quoteFtsTerm).join(" OR ");
64514
64985
  }
64515
64986
  function textHash(value) {
64516
- return `sha256:${createHash8("sha256").update(value).digest("hex")}`;
64987
+ return `sha256:${createHash9("sha256").update(value).digest("hex")}`;
64517
64988
  }
64518
64989
  function deterministicKnowledgeChunkId(input) {
64519
64990
  return textHash([
@@ -65268,7 +65739,7 @@ var init_store = __esm(() => {
65268
65739
  });
65269
65740
 
65270
65741
  // ../runtime/src/knowledge/session-indexer.ts
65271
- import { createHash as createHash9 } from "crypto";
65742
+ import { createHash as createHash10 } from "crypto";
65272
65743
  import {
65273
65744
  createReadStream as createReadStream2,
65274
65745
  existsSync as existsSync32,
@@ -65382,7 +65853,7 @@ function discoverRecentSessionFiles(cutoffMs, limit, harnessFilter) {
65382
65853
  async function parseJsonl(file) {
65383
65854
  const maybeYield = createYieldBudget();
65384
65855
  const records = [];
65385
- const hash = createHash9("sha256");
65856
+ const hash = createHash10("sha256");
65386
65857
  let carry = "";
65387
65858
  let offset = 0;
65388
65859
  let index2 = 0;
@@ -65713,10 +66184,10 @@ function trimOneLine(value, max) {
65713
66184
  return flat.length <= max ? flat : `${flat.slice(0, Math.max(0, max - 3))}...`;
65714
66185
  }
65715
66186
  function hashText(value) {
65716
- return `sha256:${createHash9("sha256").update(value).digest("hex")}`;
66187
+ return `sha256:${createHash10("sha256").update(value).digest("hex")}`;
65717
66188
  }
65718
66189
  function stableId(value, length = 16) {
65719
- return createHash9("sha256").update(value).digest("hex").slice(0, length);
66190
+ return createHash10("sha256").update(value).digest("hex").slice(0, length);
65720
66191
  }
65721
66192
  function portablePath(filePath) {
65722
66193
  const home = homedir27();
@@ -66568,7 +67039,7 @@ var init_sender = __esm(() => {
66568
67039
  // ../../apps/desktop/src/core/broker/service.ts
66569
67040
  import { mkdir as mkdir11, readFile as readFile15, unlink, writeFile as writeFile10 } from "fs/promises";
66570
67041
  import { basename as basename12, join as join39, resolve as resolve17 } from "path";
66571
- import { createHash as createHash10, randomUUID as randomUUID5 } from "crypto";
67042
+ import { createHash as createHash11, randomUUID as randomUUID5 } from "crypto";
66572
67043
  function relayHubDirectory() {
66573
67044
  return resolveOpenScoutSupportPaths().relayHubDirectory;
66574
67045
  }
@@ -66660,7 +67131,7 @@ function deliveryRequestId(clientMessageId, createdAt) {
66660
67131
  if (!normalized) {
66661
67132
  return `deliver-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
66662
67133
  }
66663
- const digest = createHash10("sha256").update(normalized).digest("hex").slice(0, 32);
67134
+ const digest = createHash11("sha256").update(normalized).digest("hex").slice(0, 32);
66664
67135
  return `deliver-client-${digest}`;
66665
67136
  }
66666
67137
  function metadataStringList(metadata, key) {
@@ -68040,10 +68511,6 @@ async function ensureBrokerDirectConversationBetween(baseUrl, snapshot, nodeId,
68040
68511
  };
68041
68512
  }
68042
68513
  async function sendScoutMessage(input) {
68043
- const broker2 = await loadScoutBrokerContext();
68044
- if (!broker2) {
68045
- return { usedBroker: false, invokedTargets: [], unresolvedTargets: [] };
68046
- }
68047
68514
  const currentDirectory = input.currentDirectory ?? process.cwd();
68048
68515
  const source = input.source?.trim() || "scout-cli";
68049
68516
  const wake = input.wake ?? false;
@@ -68056,15 +68523,19 @@ async function sendScoutMessage(input) {
68056
68523
  source,
68057
68524
  sourceIntent: "tell_wake"
68058
68525
  } : undefined;
68059
- const senderId = await resolveConversationActorId(broker2.baseUrl, broker2.snapshot, broker2.node.id, input.senderId, currentDirectory);
68060
68526
  const requestedTargetLabel = input.targetLabel?.trim();
68061
68527
  const requestedTargetRef = input.targetRef?.trim();
68062
68528
  if (requestedTargetLabel || requestedTargetRef) {
68529
+ const health = await readScoutBrokerHealth();
68530
+ if (!health.reachable || !health.ok || !health.nodeId) {
68531
+ return { usedBroker: false, invokedTargets: [], unresolvedTargets: [] };
68532
+ }
68533
+ const senderId2 = await resolveConversationActorId(health.baseUrl, createRuntimeRegistrySnapshot(), health.nodeId, input.senderId, currentDirectory);
68063
68534
  const { target: target2, renderedTarget, isBindingRef } = routeTargetForTargetLabel(requestedTargetLabel ?? "", requestedTargetRef, input.aliasScope);
68064
- const delivery = await brokerPostDeliver(broker2.baseUrl, {
68535
+ const delivery = await brokerPostDeliver(health.baseUrl, {
68065
68536
  caller: {
68066
- actorId: senderId,
68067
- nodeId: broker2.node.id,
68537
+ actorId: senderId2,
68538
+ nodeId: health.nodeId,
68068
68539
  currentDirectory,
68069
68540
  metadata: { source }
68070
68541
  },
@@ -68107,6 +68578,11 @@ async function sendScoutMessage(input) {
68107
68578
  routeKind: delivery.routeKind
68108
68579
  };
68109
68580
  }
68581
+ const broker2 = await loadScoutBrokerContext();
68582
+ if (!broker2) {
68583
+ return { usedBroker: false, invokedTargets: [], unresolvedTargets: [] };
68584
+ }
68585
+ const senderId = await resolveConversationActorId(broker2.baseUrl, broker2.snapshot, broker2.node.id, input.senderId, currentDirectory);
68110
68586
  const mentionResolution = await resolveMentionTargets(broker2.snapshot, input.body, currentDirectory);
68111
68587
  const selectors = extractAgentSelectors(input.body);
68112
68588
  if (selectors.length === 1 && mentionResolution.resolved.length + mentionResolution.unresolved.length + mentionResolution.ambiguous.length === 1) {
@@ -69582,6 +70058,7 @@ var init_service3 = __esm(() => {
69582
70058
  init_broker_api();
69583
70059
  init_broker_process_manager();
69584
70060
  init_local_agents();
70061
+ init_registry3();
69585
70062
  init_support_paths();
69586
70063
  init_user_config();
69587
70064
  init_paths();
@@ -70509,6 +70986,12 @@ function describeStopStep(step) {
70509
70986
  return `sweep supervised stragglers (${step.pids.length} candidate${step.pids.length === 1 ? "" : "s"})`;
70510
70987
  }
70511
70988
  }
70989
+ function ownedSweepSurvivorPids(step, tree) {
70990
+ const candidates = new Set(step.pids);
70991
+ const stillOwned = layerProcesses(tree, step.layers);
70992
+ const reparentedFromThisCheckout = detachedExpectedProcesses(tree).filter((entry) => step.layers.includes(entry.layer));
70993
+ return [...stillOwned, ...reparentedFromThisCheckout].map((entry) => entry.pid).filter((pid) => candidates.has(pid));
70994
+ }
70512
70995
  function processAlive(pid) {
70513
70996
  try {
70514
70997
  process.kill(pid, 0);
@@ -70578,6 +71061,9 @@ function bootstrapLaunchdJob(plistPath, uid) {
70578
71061
  const ok = (result.status ?? 1) === 0 || /already bootstrapped|Operation already in progress/i.test(detail);
70579
71062
  return { ok, detail };
70580
71063
  }
71064
+ function scoutdStartArguments(options = {}) {
71065
+ return options.waitForHealth === false ? ["start", "--no-wait"] : ["start"];
71066
+ }
70581
71067
  function chooseLaunchdStartMethod(input) {
70582
71068
  if (input.scoutdPath)
70583
71069
  return "scoutd";
@@ -70597,7 +71083,7 @@ function startLaunchdJob(label, uid, home, options = {}) {
70597
71083
  case "kickstart":
70598
71084
  return { ...kickstartLaunchdJob(label, uid, { restart: options.restart }), method };
70599
71085
  case "scoutd": {
70600
- const result = spawnSync4(scoutdPath, ["start"], { encoding: "utf8" });
71086
+ const result = spawnSync4(scoutdPath, scoutdStartArguments(options), { encoding: "utf8" });
70601
71087
  const detail = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
70602
71088
  return { ok: (result.status ?? 1) === 0, detail, method };
70603
71089
  }
@@ -70692,6 +71178,7 @@ var init_app_lifecycle = __esm(() => {
70692
71178
  // ../../apps/desktop/src/cli/commands/app.ts
70693
71179
  var exports_app = {};
70694
71180
  __export(exports_app, {
71181
+ startTreeReady: () => startTreeReady,
70695
71182
  selectInstalledAppBundle: () => selectInstalledAppBundle,
70696
71183
  runAppCommand: () => runAppCommand,
70697
71184
  renderAppCommandHelp: () => renderAppCommandHelp,
@@ -70828,6 +71315,13 @@ async function waitFor(paths2, predicate, timeoutMs) {
70828
71315
  }
70829
71316
  return tree;
70830
71317
  }
71318
+ function startTreeReady(tree, scope) {
71319
+ const appsReady = tree.layers.app.length > 0 && tree.layers.menu.length > 0;
71320
+ if (!appsReady || scope === "apps") {
71321
+ return appsReady;
71322
+ }
71323
+ return SUPERVISED_LAYERS.every((layer) => tree.layers[layer].length > 0);
71324
+ }
70831
71325
  async function stopSuite(paths2, steps, scope) {
70832
71326
  const tree = readTree(paths2);
70833
71327
  const uid = process.getuid?.() ?? 0;
@@ -70838,7 +71332,12 @@ async function stopSuite(paths2, steps, scope) {
70838
71332
  steps.push(result.ok ? label : `${label} \u2014 failed: ${result.detail || "unknown error"}`);
70839
71333
  continue;
70840
71334
  }
70841
- const { escalated, survivors } = await terminateProcesses(step.pids);
71335
+ let pids = step.pids;
71336
+ if (step.kind === "sweep") {
71337
+ const drainedTree = await waitFor(paths2, (candidate) => ownedSweepSurvivorPids(step, candidate).length === 0, SUPERVISED_DRAIN_TIMEOUT_MS);
71338
+ pids = ownedSweepSurvivorPids(step, drainedTree);
71339
+ }
71340
+ const { escalated, survivors } = await terminateProcesses(pids);
70842
71341
  const notes = [];
70843
71342
  if (escalated.length > 0)
70844
71343
  notes.push(`escalated to SIGKILL: ${escalated.join(", ")}`);
@@ -70851,22 +71350,27 @@ async function stopSuite(paths2, steps, scope) {
70851
71350
  }
70852
71351
  async function startSuite(paths2, steps, scope, options = {}) {
70853
71352
  const uid = process.getuid?.() ?? 0;
71353
+ let supervisedReadiness = null;
70854
71354
  if (scope === "apps") {
70855
71355
  steps.push("skip launchd (--apps-only)");
70856
71356
  } else {
70857
71357
  const label = resolveLaunchdLabel();
70858
71358
  const launched = startLaunchdJob(label, uid, homedir28(), {
70859
71359
  restart: options.restart,
70860
- serviceRoot: paths2.serviceRoot
71360
+ serviceRoot: paths2.serviceRoot,
71361
+ waitForHealth: false
70861
71362
  });
70862
71363
  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(", ")}`);
71364
+ supervisedReadiness = waitFor(paths2, (tree2) => SUPERVISED_LAYERS.every((layer) => tree2.layers[layer].length > 0), SUPERVISED_READY_TIMEOUT_MS);
70866
71365
  }
70867
71366
  const open = spawnSync5("open", [paths2.appBundlePath], { encoding: "utf8" });
70868
71367
  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);
71368
+ if (supervisedReadiness) {
71369
+ const supervised = await supervisedReadiness;
71370
+ const missing = SUPERVISED_LAYERS.filter((layer) => supervised.layers[layer].length === 0);
71371
+ steps.push(missing.length === 0 ? "supervised tree ready" : `supervised tree still starting \u2014 missing ${missing.join(", ")}`);
71372
+ }
71373
+ const tree = await waitFor(paths2, (candidate) => startTreeReady(candidate, scope), READY_TIMEOUT_MS);
70870
71374
  const appsMissing = LAUNCH_SERVICES_LAYERS.filter((layer) => layer !== "pairing" && tree.layers[layer].length === 0);
70871
71375
  steps.push(appsMissing.length === 0 ? "Scout and embedded ScoutMenu ready" : `apps incomplete \u2014 missing ${appsMissing.join(", ")}`);
70872
71376
  return tree;
@@ -70992,7 +71496,7 @@ async function runAppCommand(context, args) {
70992
71496
  throw new ScoutCliError(result.message);
70993
71497
  }
70994
71498
  }
70995
- var HELP_FLAGS2, INSTALLED_APP_BUNDLE_ID = "app.openscout.scout", INSTALLED_APP_BUNDLE_NAME = "OpenScout.app", READY_TIMEOUT_MS = 30000, POLL_MS = 250;
71499
+ 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
71500
  var init_app = __esm(() => {
70997
71501
  init_app_lifecycle();
70998
71502
  init_context();
@@ -100958,7 +101462,7 @@ var init_stdio2 = __esm(() => {
100958
101462
  // ../../apps/desktop/src/shared/product.ts
100959
101463
  var SCOUT_APP_VERSION;
100960
101464
  var init_product = __esm(() => {
100961
- SCOUT_APP_VERSION = process.env.SCOUT_APP_VERSION?.trim() || "0.2.94";
101465
+ SCOUT_APP_VERSION = process.env.SCOUT_APP_VERSION?.trim() || "0.2.96";
100962
101466
  });
100963
101467
 
100964
101468
  // ../../apps/desktop/src/core/mcp/stdio-server-lifecycle.ts
@@ -102491,7 +102995,13 @@ function renderOpenScoutCaddyfile(config2) {
102491
102995
  const blocks = schemes.flatMap((scheme) => config2.routes.map((route) => route.mode === "vite-dev" ? renderCaddyViteDevRouteBlock(route, scheme) : renderCaddyDefaultRouteBlock(config2, route, scheme, startPage))).join(`
102492
102996
 
102493
102997
  `);
102494
- return `${blocks}
102998
+ return `{
102999
+ log {
103000
+ level WARN
103001
+ }
103002
+ }
103003
+
103004
+ ${blocks}
102495
103005
  `;
102496
103006
  }
102497
103007
  var init_local_edge = __esm(() => {
@@ -104741,11 +105251,11 @@ function buildFlightCommandResult(flight, env, options = {}) {
104741
105251
  };
104742
105252
  }
104743
105253
  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);
105254
+ const conversationId = metadataStringValue3(flight.metadata, "conversationId") ?? metadataReturnAddressStringValue(flight.metadata, "conversationId");
105255
+ const messageId = metadataStringValue3(flight.metadata, "messageId") ?? metadataReturnAddressStringValue(flight.metadata, "replyToMessageId");
105256
+ const workId = metadataStringValue3(flight.metadata, "workId") ?? metadataStringValue3(flight.metadata, "collaborationRecordId");
105257
+ const sessionId = metadataStringValue3(flight.metadata, "sessionId") ?? metadataDispatchAckStringValue(flight.metadata, "sessionId");
105258
+ const bindingRef = metadataStringValue3(flight.metadata, "bindingRef") ?? flight.id.slice(-8);
104749
105259
  const origin = resolveScoutWebOrigin(env);
104750
105260
  const links = buildFlightLinks(origin, {
104751
105261
  flightId: flight.id,
@@ -104833,7 +105343,7 @@ function isFlightWaitTimeout(error48) {
104833
105343
  function isTerminalFlightState(state) {
104834
105344
  return state === "completed" || state === "failed" || state === "cancelled";
104835
105345
  }
104836
- function metadataStringValue2(metadata, key) {
105346
+ function metadataStringValue3(metadata, key) {
104837
105347
  const value = metadata?.[key];
104838
105348
  return typeof value === "string" && value.trim() ? value.trim() : null;
104839
105349
  }
@@ -104841,13 +105351,13 @@ function metadataReturnAddressStringValue(metadata, key) {
104841
105351
  const value = metadata?.["returnAddress"];
104842
105352
  if (!value || typeof value !== "object" || Array.isArray(value))
104843
105353
  return null;
104844
- return metadataStringValue2(value, key);
105354
+ return metadataStringValue3(value, key);
104845
105355
  }
104846
105356
  function metadataDispatchAckStringValue(metadata, key) {
104847
105357
  const value = metadata?.["dispatchAck"];
104848
105358
  if (!value || typeof value !== "object" || Array.isArray(value))
104849
105359
  return null;
104850
- return metadataStringValue2(value, key);
105360
+ return metadataStringValue3(value, key);
104851
105361
  }
104852
105362
  function trimTrailingSlash(value) {
104853
105363
  return value.replace(/\/+$/g, "");
@@ -105019,14 +105529,33 @@ var init_inbox2 = __esm(() => {
105019
105529
  // ../../apps/desktop/src/cli/commands/install.ts
105020
105530
  var exports_install = {};
105021
105531
  __export(exports_install, {
105532
+ verifyStagedOpenScoutApp: () => verifyStagedOpenScoutApp,
105533
+ verifyDownloadedDmg: () => verifyDownloadedDmg,
105534
+ verifyDownloadedAsset: () => verifyDownloadedAsset,
105022
105535
  runInstallCommand: () => runInstallCommand,
105023
105536
  renderInstallCommandHelp: () => renderInstallCommandHelp,
105537
+ processIdsForInstalledApp: () => processIdsForInstalledApp,
105024
105538
  parseInstallArgs: () => parseInstallArgs,
105025
- findAppDmgAsset: () => findAppDmgAsset
105539
+ parseGithubAssetDigest: () => parseGithubAssetDigest,
105540
+ findAppDmgAsset: () => findAppDmgAsset,
105541
+ OPENSCOUT_SIGNING_TEAM_ID: () => OPENSCOUT_SIGNING_TEAM_ID,
105542
+ OPENSCOUT_RELEASE_REPOSITORY: () => OPENSCOUT_RELEASE_REPOSITORY,
105543
+ OPENSCOUT_RELEASE_OWNER: () => OPENSCOUT_RELEASE_OWNER,
105544
+ OPENSCOUT_APP_NAME: () => OPENSCOUT_APP_NAME,
105545
+ OPENSCOUT_APP_BUNDLE_ID: () => OPENSCOUT_APP_BUNDLE_ID,
105546
+ DEFAULT_OPENSCOUT_APP_PATH: () => DEFAULT_OPENSCOUT_APP_PATH
105026
105547
  });
105027
105548
  import { spawnSync as spawnSync8 } from "child_process";
105028
- import { existsSync as existsSync39, mkdtempSync, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "fs";
105029
- import { tmpdir } from "os";
105549
+ import { createHash as createHash12 } from "crypto";
105550
+ import {
105551
+ existsSync as fsExistsSync,
105552
+ mkdirSync as fsMkdirSync,
105553
+ mkdtempSync as fsMkdtempSync,
105554
+ renameSync as fsRenameSync,
105555
+ rmSync as fsRmSync,
105556
+ writeFileSync as fsWriteFileSync
105557
+ } from "fs";
105558
+ import { tmpdir as osTmpdir } from "os";
105030
105559
  import { join as join48 } from "path";
105031
105560
  function renderInstallCommandHelp() {
105032
105561
  return [
@@ -105041,11 +105570,15 @@ function renderInstallCommandHelp() {
105041
105570
  "",
105042
105571
  "Behavior:",
105043
105572
  " Downloads the signed + notarized OpenScout.dmg from the GitHub release,",
105044
- " installs OpenScout.app (which embeds the menu bar helper) to /Applications,",
105045
- " and clears the Gatekeeper quarantine flag so it opens cleanly.",
105573
+ " verifies the published byte size and sha256 digest when GitHub provides one,",
105574
+ " then codesign and Gatekeeper-assess the DMG before mounting. OpenScout.app",
105575
+ " must match the pinned bundle id and Team ID, pass codesign --deep --strict,",
105576
+ " and pass Gatekeeper execute after staging. A running copy of the installed",
105577
+ " app is stopped first; replacement is staged and rolled back on failure.",
105578
+ " Quarantine attributes are not cleared.",
105046
105579
  "",
105047
- " The app drives the bundled local runtime from the global scout CLI, so",
105048
- " `bun add -g @openscout/scout` is the companion install for the command line."
105580
+ " The app uses the local scout CLI for the bundled runtime. Install the CLI",
105581
+ " with `bun add -g @openscout/scout`."
105049
105582
  ].join(`
105050
105583
  `);
105051
105584
  }
@@ -105083,11 +105616,19 @@ function parseInstallArgs(args) {
105083
105616
  continue;
105084
105617
  }
105085
105618
  if (arg.startsWith("--version=")) {
105086
- options.version = arg.slice("--version=".length);
105619
+ const value = arg.slice("--version=".length);
105620
+ if (!value) {
105621
+ throw new ScoutCliError("--version requires a release tag (e.g. --version v0.2.70)");
105622
+ }
105623
+ options.version = value;
105087
105624
  continue;
105088
105625
  }
105089
105626
  if (arg.startsWith("--tag=")) {
105090
- options.version = arg.slice("--tag=".length);
105627
+ const value = arg.slice("--tag=".length);
105628
+ if (!value) {
105629
+ throw new ScoutCliError("--tag requires a release tag (e.g. --tag v0.2.70)");
105630
+ }
105631
+ options.version = value;
105091
105632
  continue;
105092
105633
  }
105093
105634
  throw new ScoutCliError(`unknown option for install: ${arg} (try: scout install --help)`);
@@ -105098,6 +105639,13 @@ function stripLeadingV(tag) {
105098
105639
  const trimmed = tag.trim();
105099
105640
  return trimmed.startsWith("v") ? trimmed.slice(1) : trimmed;
105100
105641
  }
105642
+ function normalizeReleaseVersion(tag) {
105643
+ const version3 = stripLeadingV(tag);
105644
+ if (!/^\d+\.\d+\.\d+$/.test(version3)) {
105645
+ throw new ScoutCliError(`unsupported OpenScout release tag: ${tag}`);
105646
+ }
105647
+ return version3;
105648
+ }
105101
105649
  function formatBytes(bytes) {
105102
105650
  if (bytes <= 0)
105103
105651
  return "unknown size";
@@ -105105,51 +105653,146 @@ function formatBytes(bytes) {
105105
105653
  return `${(bytes / 1024).toFixed(0)} KB`;
105106
105654
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
105107
105655
  }
105108
- function getInstalledVersion() {
105109
- if (!existsSync39(INFO_PLIST_PATH))
105656
+ function combinedOutput(result) {
105657
+ return `${result.stdout ?? ""}
105658
+ ${result.stderr ?? ""}`;
105659
+ }
105660
+ function permissionDeniedMessage(destination, detail) {
105661
+ const suffix = detail?.trim() ? `: ${detail.trim()}` : ".";
105662
+ return `could not replace ${destination} (permission denied)${suffix} Grant write access to the destination and retry.`;
105663
+ }
105664
+ function isPermissionError(error48, result) {
105665
+ const code = error48?.code;
105666
+ if (code === "EACCES" || code === "EPERM")
105667
+ return true;
105668
+ const output = `${error48 instanceof Error ? error48.message : ""} ${result?.stderr ?? ""} ${result?.stdout ?? ""}`.toLowerCase();
105669
+ return output.includes("permission denied") || output.includes("operation not permitted");
105670
+ }
105671
+ function defaultRun(command, args) {
105672
+ const result = spawnSync8(command, [...args], { encoding: "utf8" });
105673
+ return {
105674
+ status: result.status,
105675
+ stdout: result.stdout ?? "",
105676
+ stderr: result.stderr ?? "",
105677
+ error: result.error
105678
+ };
105679
+ }
105680
+ function defaultSleep(ms) {
105681
+ Bun.sleepSync(ms);
105682
+ }
105683
+ function resolveInstallDependencies(dependencies = {}) {
105684
+ return {
105685
+ platform: dependencies.platform ?? process.platform,
105686
+ fetch: dependencies.fetch ?? fetch,
105687
+ run: dependencies.run ?? defaultRun,
105688
+ existsSync: dependencies.existsSync ?? fsExistsSync,
105689
+ mkdirSync: dependencies.mkdirSync ?? ((path, options) => {
105690
+ fsMkdirSync(path, options);
105691
+ }),
105692
+ mkdtempSync: dependencies.mkdtempSync ?? fsMkdtempSync,
105693
+ writeFileSync: dependencies.writeFileSync ?? ((path, data) => {
105694
+ fsWriteFileSync(path, data);
105695
+ }),
105696
+ rmSync: dependencies.rmSync ?? fsRmSync,
105697
+ renameSync: dependencies.renameSync ?? fsRenameSync,
105698
+ tmpdir: dependencies.tmpdir ?? osTmpdir,
105699
+ sleep: dependencies.sleep ?? defaultSleep,
105700
+ appPath: dependencies.appPath ?? DEFAULT_OPENSCOUT_APP_PATH
105701
+ };
105702
+ }
105703
+ function requireSuccess(result, message) {
105704
+ if (result.error && result.error.code === "ENOENT") {
105705
+ throw new ScoutCliError(`${message}: ${result.error.message}`);
105706
+ }
105707
+ if ((result.status ?? 1) !== 0) {
105708
+ throw new ScoutCliError(`${message}: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.status ?? "unknown"}`}`);
105709
+ }
105710
+ return result;
105711
+ }
105712
+ function parseGithubAssetDigest(digest) {
105713
+ const trimmed = digest.trim();
105714
+ const separator = trimmed.indexOf(":");
105715
+ if (separator <= 0) {
105716
+ throw new ScoutCliError(`unsupported GitHub asset digest: ${digest}`);
105717
+ }
105718
+ const algorithm = trimmed.slice(0, separator).trim().toLowerCase();
105719
+ const hex4 = trimmed.slice(separator + 1).trim().toLowerCase();
105720
+ if (algorithm !== GITHUB_ASSET_DIGEST_ALGORITHM || !/^[0-9a-f]+$/.test(hex4) || hex4.length !== GITHUB_ASSET_DIGEST_HEX_LENGTH) {
105721
+ throw new ScoutCliError(`unsupported GitHub asset digest: ${digest}`);
105722
+ }
105723
+ return { algorithm, hex: hex4 };
105724
+ }
105725
+ function verifyDownloadedAsset(buffer, asset) {
105726
+ if (typeof asset.size !== "number" || !Number.isFinite(asset.size) || asset.size < 0) {
105727
+ throw new ScoutCliError(`release asset ${asset.name} is missing an exact byte size`);
105728
+ }
105729
+ if (buffer.byteLength !== asset.size) {
105730
+ throw new ScoutCliError(`download size mismatch for ${asset.name}: expected ${asset.size} bytes, got ${buffer.byteLength}`);
105731
+ }
105732
+ if (buffer.byteLength === 0) {
105733
+ throw new ScoutCliError("download failed: empty response body");
105734
+ }
105735
+ const digest = asset.digest?.trim();
105736
+ if (!digest)
105737
+ return;
105738
+ const parsed = parseGithubAssetDigest(digest);
105739
+ let actual;
105740
+ try {
105741
+ actual = createHash12(parsed.algorithm).update(buffer).digest("hex");
105742
+ } catch {
105743
+ throw new ScoutCliError(`unsupported GitHub asset digest algorithm: ${parsed.algorithm}`);
105744
+ }
105745
+ if (actual !== parsed.hex) {
105746
+ throw new ScoutCliError(`download digest mismatch for ${asset.name}: expected ${digest}, got ${parsed.algorithm}:${actual}`);
105747
+ }
105748
+ }
105749
+ function findAppDmgAsset(release) {
105750
+ const assets = release.assets ?? [];
105751
+ const versionedName = `OpenScout-${normalizeReleaseVersion(release.tag_name)}.dmg`;
105752
+ const versioned = assets.find((asset) => asset.name === versionedName);
105753
+ if (versioned)
105754
+ return versioned;
105755
+ const latestAlias = assets.find((asset) => asset.name === LATEST_DMG_ALIAS);
105756
+ if (latestAlias)
105757
+ return latestAlias;
105758
+ throw new ScoutCliError(`no OpenScout.app DMG found in release ${release.tag_name}. Assets: ${assets.map((asset) => asset.name).join(", ") || "(none)"}`);
105759
+ }
105760
+ function infoPlistPath(appPath) {
105761
+ return join48(appPath, "Contents", "Info.plist");
105762
+ }
105763
+ function readPlistString(deps, plistPath, key) {
105764
+ if (!deps.existsSync(plistPath))
105110
105765
  return null;
105111
- const result = spawnSync8("defaults", ["read", INFO_PLIST_PATH, "CFBundleShortVersionString"], { encoding: "utf8" });
105766
+ const result = deps.run("plutil", ["-extract", key, "raw", "-o", "-", plistPath]);
105112
105767
  if ((result.status ?? 1) !== 0)
105113
105768
  return null;
105114
- const version3 = result.stdout.trim();
105115
- return version3 || null;
105769
+ const value = result.stdout.trim();
105770
+ return value || null;
105116
105771
  }
105117
- async function fetchRelease(version3) {
105118
- const base = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases`;
105119
- const apiUrl = version3 ? `${base}/tags/${version3}` : `${base}/latest`;
105772
+ function getInstalledVersion(deps) {
105773
+ return readPlistString(deps, infoPlistPath(deps.appPath), "CFBundleShortVersionString");
105774
+ }
105775
+ async function fetchRelease(version3, deps) {
105776
+ const base = `https://api.github.com/repos/${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY}/releases`;
105777
+ const apiUrl = version3 ? `${base}/tags/${encodeURIComponent(version3)}` : `${base}/latest`;
105120
105778
  try {
105121
- const response = await fetch(apiUrl, {
105779
+ const response = await deps.fetch(apiUrl, {
105122
105780
  headers: { Accept: "application/vnd.github+json", "User-Agent": USER_AGENT }
105123
105781
  });
105124
105782
  if (response.ok) {
105125
105783
  return await response.json();
105126
105784
  }
105127
105785
  } catch {}
105128
- const apiPath = version3 ? `repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases/tags/${version3}` : `repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases/latest`;
105129
- const gh = spawnSync8("gh", ["api", apiPath], { encoding: "utf8" });
105786
+ const apiPath = version3 ? `repos/${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY}/releases/tags/${version3}` : `repos/${OPENSCOUT_RELEASE_OWNER}/${OPENSCOUT_RELEASE_REPOSITORY}/releases/latest`;
105787
+ const gh = deps.run("gh", ["api", apiPath]);
105130
105788
  if ((gh.status ?? 1) === 0 && gh.stdout.trim()) {
105131
105789
  return JSON.parse(gh.stdout);
105132
105790
  }
105133
- throw new ScoutCliError(version3 ? `release "${version3}" not found on GitHub (${GITHUB_OWNER}/${GITHUB_REPO})` : `could not fetch the latest OpenScout release from GitHub (${GITHUB_OWNER}/${GITHUB_REPO})`);
105791
+ 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})`);
105134
105792
  }
105135
- function findAppDmgAsset(release) {
105136
- const assets = release.assets ?? [];
105137
- const isMenuOnly = (name2) => name2.toLowerCase().startsWith("openscoutmenu");
105138
- const isDmg = (name2) => name2.toLowerCase().endsWith(".dmg");
105139
- const versioned = assets.find((asset) => /^openscout-\d/i.test(asset.name) && isDmg(asset.name) && !isMenuOnly(asset.name));
105140
- if (versioned)
105141
- return versioned;
105142
- const latestAlias = assets.find((asset) => asset.name.toLowerCase() === "openscout.dmg");
105143
- if (latestAlias)
105144
- return latestAlias;
105145
- const anyProductDmg = assets.find((asset) => isDmg(asset.name) && !isMenuOnly(asset.name));
105146
- if (anyProductDmg)
105147
- return anyProductDmg;
105148
- throw new ScoutCliError(`no OpenScout.app DMG found in release ${release.tag_name}. Assets: ${assets.map((asset) => asset.name).join(", ") || "(none)"}`);
105149
- }
105150
- async function downloadDmg(asset, destination, context) {
105793
+ async function downloadDmg(asset, destination, context, deps) {
105151
105794
  context.stderr(`Downloading ${asset.name} (${formatBytes(asset.size)})\u2026`);
105152
- const response = await fetch(asset.browser_download_url, {
105795
+ const response = await deps.fetch(asset.browser_download_url, {
105153
105796
  redirect: "follow",
105154
105797
  headers: { "User-Agent": USER_AGENT }
105155
105798
  });
@@ -105157,61 +105800,266 @@ async function downloadDmg(asset, destination, context) {
105157
105800
  throw new ScoutCliError(`download failed: HTTP ${response.status} for ${asset.browser_download_url}`);
105158
105801
  }
105159
105802
  const buffer = Buffer.from(await response.arrayBuffer());
105160
- if (buffer.length === 0) {
105161
- throw new ScoutCliError("download failed: empty response body");
105803
+ verifyDownloadedAsset(buffer, asset);
105804
+ deps.writeFileSync(destination, buffer);
105805
+ }
105806
+ function mountDmg(dmgPath, mountPoint, deps) {
105807
+ deps.mkdirSync(mountPoint, { recursive: true });
105808
+ const result = deps.run("hdiutil", [
105809
+ "attach",
105810
+ dmgPath,
105811
+ "-mountpoint",
105812
+ mountPoint,
105813
+ "-nobrowse",
105814
+ "-readonly",
105815
+ "-noautoopen"
105816
+ ]);
105817
+ if ((result.status ?? 1) !== 0) {
105818
+ throw new ScoutCliError(`could not mount ${dmgPath}: ${result.stderr.trim() || result.stdout.trim() || "hdiutil attach failed"}`);
105162
105819
  }
105163
- writeFileSync9(destination, buffer);
105164
105820
  }
105165
- function mountDmg(dmgPath) {
105166
- const result = spawnSync8("hdiutil", ["attach", dmgPath, "-nobrowse", "-noverify", "-noautoopen", "-plist"], { encoding: "utf8" });
105167
- if ((result.status ?? 1) !== 0) {
105168
- throw new ScoutCliError(`could not mount ${dmgPath}: ${result.stderr.trim() || "hdiutil attach failed"}`);
105821
+ function unmountDmg(mountPoint, deps) {
105822
+ const detached = deps.run("hdiutil", ["detach", mountPoint, "-quiet"]);
105823
+ if ((detached.status ?? 1) === 0)
105824
+ return;
105825
+ deps.run("hdiutil", ["detach", mountPoint, "-force", "-quiet"]);
105826
+ }
105827
+ function requireMountedOpenScoutApp(mountPoint, deps) {
105828
+ const source = join48(mountPoint, OPENSCOUT_APP_NAME);
105829
+ if (!deps.existsSync(source) || !deps.existsSync(infoPlistPath(source))) {
105830
+ throw new ScoutCliError(`no ${OPENSCOUT_APP_NAME} found in the mounted DMG at ${mountPoint}`);
105169
105831
  }
105170
- const mountPointMatch = result.stdout.match(/<key>mount-point<\/key>\s*<string>([^<]+)<\/string>/);
105171
- if (mountPointMatch) {
105172
- return mountPointMatch[1];
105832
+ return source;
105833
+ }
105834
+ function codesignField(output, key) {
105835
+ const match = output.match(new RegExp(`^${key}=(.+)$`, "m"));
105836
+ return match ? match[1].trim() : null;
105837
+ }
105838
+ function verifyDownloadedDmg(dmgPath, deps = {}) {
105839
+ const resolved = resolveInstallDependencies(deps);
105840
+ requireSuccess(resolved.run("codesign", ["--verify", "--strict", dmgPath]), `codesign --verify --strict failed for ${dmgPath}`);
105841
+ requireSuccess(resolved.run("spctl", [
105842
+ "--assess",
105843
+ "--type",
105844
+ "open",
105845
+ "--context",
105846
+ "context:primary-signature",
105847
+ "-vv",
105848
+ dmgPath
105849
+ ]), `Gatekeeper rejected ${dmgPath}`);
105850
+ }
105851
+ function verifyStagedOpenScoutApp(bundlePath, deps = {}, expectedVersion) {
105852
+ const resolved = resolveInstallDependencies(deps);
105853
+ if (!resolved.existsSync(infoPlistPath(bundlePath))) {
105854
+ throw new ScoutCliError(`${bundlePath} is missing Contents/Info.plist`);
105855
+ }
105856
+ const bundleId = readPlistString(resolved, infoPlistPath(bundlePath), "CFBundleIdentifier");
105857
+ if (bundleId !== OPENSCOUT_APP_BUNDLE_ID) {
105858
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} bundle id is ${bundleId ?? "(missing)"}; expected ${OPENSCOUT_APP_BUNDLE_ID}`);
105859
+ }
105860
+ const version3 = readPlistString(resolved, infoPlistPath(bundlePath), "CFBundleShortVersionString");
105861
+ if (!version3) {
105862
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} is missing CFBundleShortVersionString`);
105863
+ }
105864
+ if (expectedVersion && version3 !== expectedVersion) {
105865
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} version is ${version3}; expected release ${expectedVersion}`);
105866
+ }
105867
+ requireSuccess(resolved.run("codesign", ["--verify", "--deep", "--strict", bundlePath]), `codesign --verify --deep --strict failed for ${bundlePath}`);
105868
+ const details = requireSuccess(resolved.run("codesign", ["-dvv", bundlePath]), `could not read codesign details for ${bundlePath}`);
105869
+ const detailsOutput = combinedOutput(details);
105870
+ const identifier = codesignField(detailsOutput, "Identifier");
105871
+ if (identifier !== OPENSCOUT_APP_BUNDLE_ID) {
105872
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} signature identifier is ${identifier ?? "(missing)"}; expected ${OPENSCOUT_APP_BUNDLE_ID}`);
105873
+ }
105874
+ const teamId = codesignField(detailsOutput, "TeamIdentifier");
105875
+ if (teamId !== OPENSCOUT_SIGNING_TEAM_ID) {
105876
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} signing Team ID is ${teamId ?? "(missing)"}; expected ${OPENSCOUT_SIGNING_TEAM_ID}`);
105877
+ }
105878
+ if (!/^Authority=Developer ID Application:/m.test(detailsOutput)) {
105879
+ throw new ScoutCliError(`${OPENSCOUT_APP_NAME} is not signed with Developer ID Application`);
105880
+ }
105881
+ requireSuccess(resolved.run("spctl", ["--assess", "--type", "execute", "-vv", bundlePath]), `Gatekeeper rejected ${bundlePath}`);
105882
+ return version3;
105883
+ }
105884
+ function copyApp(source, destination, deps) {
105885
+ const copied = deps.run("ditto", [source, destination]);
105886
+ if ((copied.status ?? 1) !== 0) {
105887
+ if (isPermissionError(copied.error, copied)) {
105888
+ throw new ScoutCliError(permissionDeniedMessage(destination, copied.stderr));
105889
+ }
105890
+ throw new ScoutCliError(`could not copy the app to ${destination}: ${copied.stderr.trim() || copied.stdout.trim() || "ditto failed"}`);
105891
+ }
105892
+ }
105893
+ function moveReplacing(source, destination, deps) {
105894
+ try {
105895
+ deps.renameSync(source, destination);
105896
+ return;
105897
+ } catch (error48) {
105898
+ const code = error48?.code;
105899
+ if (code !== "EXDEV") {
105900
+ if (isPermissionError(error48)) {
105901
+ throw new ScoutCliError(permissionDeniedMessage(destination));
105902
+ }
105903
+ throw new ScoutCliError(`could not move ${source} to ${destination}: ${error48 instanceof Error ? error48.message : String(error48)}`);
105904
+ }
105173
105905
  }
105174
- throw new ScoutCliError("could not determine the DMG mount point");
105906
+ copyApp(source, destination, deps);
105907
+ deps.rmSync(source, { recursive: true, force: true });
105175
105908
  }
105176
- function unmountDmg(mountPoint) {
105177
- spawnSync8("hdiutil", ["detach", mountPoint, "-quiet"], { encoding: "utf8" });
105909
+ function backupPathFor(appPath) {
105910
+ return `${appPath}${BACKUP_SUFFIX}`;
105911
+ }
105912
+ function installLockPathFor(appPath) {
105913
+ return `${appPath}${INSTALL_LOCK_SUFFIX}`;
105914
+ }
105915
+ function acquireInstallLock(deps) {
105916
+ const lockPath = installLockPathFor(deps.appPath);
105917
+ const result = deps.run(SHLOCK_PATH, ["-f", lockPath, "-p", String(process.pid)]);
105918
+ if ((result.status ?? 1) !== 0) {
105919
+ if (isPermissionError(result.error, result)) {
105920
+ throw new ScoutCliError(permissionDeniedMessage(lockPath, result.stderr));
105921
+ }
105922
+ throw new ScoutCliError(`another scout install is already updating ${deps.appPath}. ` + `If no installer is running, remove the stale lock at ${lockPath} and retry.`);
105923
+ }
105924
+ let released = false;
105925
+ return () => {
105926
+ if (released)
105927
+ return;
105928
+ released = true;
105929
+ deps.rmSync(lockPath, { force: true });
105930
+ };
105178
105931
  }
105179
- function copyAppFromMount(mountPoint) {
105180
- const listing = spawnSync8("ls", [mountPoint], { encoding: "utf8" });
105181
- const appEntry = listing.stdout.split(`
105182
- `).map((line) => line.trim()).find((line) => line === APP_NAME) ?? listing.stdout.split(`
105183
- `).map((line) => line.trim()).find((line) => line.endsWith(".app") && !line.toLowerCase().startsWith("openscoutmenu"));
105184
- if (!appEntry) {
105185
- throw new ScoutCliError(`no ${APP_NAME} found in the mounted DMG at ${mountPoint}`);
105932
+ function recoverIncompleteInstall(deps) {
105933
+ const backupPath = backupPathFor(deps.appPath);
105934
+ const destExists = deps.existsSync(deps.appPath);
105935
+ const backupExists = deps.existsSync(backupPath);
105936
+ if (!destExists && backupExists) {
105937
+ verifyStagedOpenScoutApp(backupPath, deps);
105938
+ deps.renameSync(backupPath, deps.appPath);
105939
+ return;
105186
105940
  }
105187
- const source = join48(mountPoint, appEntry);
105188
- if (existsSync39(APP_PATH)) {
105189
- const removed = spawnSync8("rm", ["-rf", APP_PATH], { encoding: "utf8" });
105190
- if ((removed.status ?? 1) !== 0) {
105191
- throw new ScoutCliError(`could not replace ${APP_PATH} (permission denied?). Try: sudo scout install`);
105941
+ if (destExists && backupExists) {
105942
+ let destinationError = null;
105943
+ try {
105944
+ verifyStagedOpenScoutApp(deps.appPath, deps);
105945
+ } catch (error48) {
105946
+ destinationError = error48;
105947
+ }
105948
+ if (!destinationError) {
105949
+ deps.rmSync(backupPath, { recursive: true, force: true });
105950
+ return;
105192
105951
  }
105952
+ try {
105953
+ verifyStagedOpenScoutApp(backupPath, deps);
105954
+ } catch (backupError) {
105955
+ 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)}`);
105956
+ }
105957
+ deps.rmSync(deps.appPath, { recursive: true, force: true });
105958
+ deps.renameSync(backupPath, deps.appPath);
105193
105959
  }
105194
- const copied = spawnSync8("cp", ["-R", source, APP_PATH], { encoding: "utf8" });
105195
- if ((copied.status ?? 1) !== 0) {
105196
- throw new ScoutCliError(`could not copy the app to ${APP_PATH}: ${copied.stderr.trim() || "permission denied?"}. Try: sudo scout install`);
105960
+ }
105961
+ function replaceAppTransactionally(stagedPath, deps, expectedVersion) {
105962
+ const backupPath = backupPathFor(deps.appPath);
105963
+ const hadPriorApp = deps.existsSync(deps.appPath);
105964
+ let backedUp = false;
105965
+ let installedVersion = "";
105966
+ try {
105967
+ if (hadPriorApp) {
105968
+ try {
105969
+ deps.renameSync(deps.appPath, backupPath);
105970
+ } catch (error48) {
105971
+ if (isPermissionError(error48)) {
105972
+ throw new ScoutCliError(permissionDeniedMessage(deps.appPath));
105973
+ }
105974
+ throw new ScoutCliError(`could not move the installed app aside: ${error48 instanceof Error ? error48.message : String(error48)}`);
105975
+ }
105976
+ backedUp = true;
105977
+ }
105978
+ moveReplacing(stagedPath, deps.appPath, deps);
105979
+ installedVersion = verifyStagedOpenScoutApp(deps.appPath, deps, expectedVersion);
105980
+ } catch (error48) {
105981
+ if (backedUp) {
105982
+ if (deps.existsSync(deps.appPath)) {
105983
+ deps.rmSync(deps.appPath, { recursive: true, force: true });
105984
+ }
105985
+ if (deps.existsSync(backupPath)) {
105986
+ try {
105987
+ deps.renameSync(backupPath, deps.appPath);
105988
+ } catch {
105989
+ throw new ScoutCliError(`install failed and rollback failed. The previous app is at ${backupPath}. ${error48 instanceof Error ? error48.message : String(error48)}`);
105990
+ }
105991
+ }
105992
+ } else if (!hadPriorApp && deps.existsSync(deps.appPath)) {
105993
+ deps.rmSync(deps.appPath, { recursive: true, force: true });
105994
+ }
105995
+ throw error48;
105996
+ }
105997
+ if (backedUp && deps.existsSync(backupPath)) {
105998
+ deps.rmSync(backupPath, { recursive: true, force: true });
105197
105999
  }
106000
+ if (!installedVersion) {
106001
+ throw new ScoutCliError(`installed ${OPENSCOUT_APP_NAME} has no verified version`);
106002
+ }
106003
+ return installedVersion;
105198
106004
  }
105199
- function removeQuarantine() {
105200
- spawnSync8("xattr", ["-rd", "com.apple.quarantine", APP_PATH], { encoding: "utf8" });
106005
+ function executableIsInsideApp(executable, appPath) {
106006
+ const normalizedApp = appPath.endsWith("/") ? appPath.slice(0, -1) : appPath;
106007
+ return executable === normalizedApp || executable.startsWith(`${normalizedApp}/`);
105201
106008
  }
105202
- function isAppRunning() {
105203
- return (spawnSync8("pgrep", ["-x", APP_PROCESS_NAME], { encoding: "utf8" }).status ?? 1) === 0;
106009
+ function processIdsForInstalledApp(psOutput, appPath) {
106010
+ const pids = [];
106011
+ for (const rawLine of psOutput.split(`
106012
+ `)) {
106013
+ const line = rawLine.trim();
106014
+ if (!line)
106015
+ continue;
106016
+ const match = line.match(/^(\d+)\s+(\S+)/);
106017
+ if (!match)
106018
+ continue;
106019
+ const pid = Number(match[1]);
106020
+ const executable = match[2];
106021
+ if (!Number.isInteger(pid) || pid <= 0)
106022
+ continue;
106023
+ if (executableIsInsideApp(executable, appPath))
106024
+ pids.push(pid);
106025
+ }
106026
+ return pids;
105204
106027
  }
105205
- function quitApp() {
105206
- spawnSync8("osascript", ["-e", `tell application id "${APP_BUNDLE_ID}" to quit`], {
105207
- encoding: "utf8"
105208
- });
106028
+ function listInstalledAppPids(deps) {
106029
+ const result = deps.run("ps", ["-axo", "pid=,args="]);
106030
+ requireSuccess(result, "could not inspect running OpenScout processes");
106031
+ return processIdsForInstalledApp(result.stdout, deps.appPath);
106032
+ }
106033
+ function isAppRunning(deps) {
106034
+ return listInstalledAppPids(deps).length > 0;
106035
+ }
106036
+ function waitUntilStopped2(deps, attempts) {
106037
+ for (let index2 = 0;index2 < attempts; index2 += 1) {
106038
+ if (!isAppRunning(deps))
106039
+ return true;
106040
+ if (index2 + 1 < attempts)
106041
+ deps.sleep(STOP_POLL_MS);
106042
+ }
106043
+ return !isAppRunning(deps);
105209
106044
  }
105210
- function launchApp() {
105211
- const byId = spawnSync8("open", ["-b", APP_BUNDLE_ID], { encoding: "utf8" });
105212
- if ((byId.status ?? 1) !== 0) {
105213
- spawnSync8("open", [APP_PATH], { encoding: "utf8" });
106045
+ function stopRunningApp(deps) {
106046
+ const initialPids = listInstalledAppPids(deps);
106047
+ if (initialPids.length === 0)
106048
+ return false;
106049
+ for (const pid of initialPids) {
106050
+ deps.run("kill", [String(pid)]);
105214
106051
  }
106052
+ if (waitUntilStopped2(deps, STOP_POLL_ATTEMPTS))
106053
+ return true;
106054
+ for (const pid of listInstalledAppPids(deps)) {
106055
+ deps.run("kill", ["-9", String(pid)]);
106056
+ }
106057
+ if (waitUntilStopped2(deps, STOP_POLL_ATTEMPTS))
106058
+ return true;
106059
+ throw new ScoutCliError("could not stop OpenScout before replacing it");
106060
+ }
106061
+ function launchApp(deps) {
106062
+ requireSuccess(deps.run("open", [deps.appPath]), `could not relaunch ${deps.appPath}`);
105215
106063
  }
105216
106064
  function renderInstallResult(result) {
105217
106065
  if (result.action === "check") {
@@ -105225,90 +106073,130 @@ function renderInstallResult(result) {
105225
106073
  }
105226
106074
  return result.message;
105227
106075
  }
105228
- async function runCheck(context, options) {
105229
- const installed = getInstalledVersion();
105230
- const release = await fetchRelease(options.version);
105231
- const target2 = stripLeadingV(release.tag_name);
106076
+ async function runCheck(context, options, deps) {
106077
+ const release = await fetchRelease(options.version, deps);
106078
+ const target2 = normalizeReleaseVersion(release.tag_name);
106079
+ let installed = getInstalledVersion(deps);
105232
106080
  let status;
105233
106081
  let message;
105234
- if (!installed) {
105235
- status = "not-installed";
105236
- message = "OpenScout is not installed \u2014 run `scout install`.";
105237
- } else if (installed === target2) {
105238
- status = "up-to-date";
105239
- message = `OpenScout ${installed} is up to date.`;
106082
+ if (!deps.existsSync(deps.appPath)) {
106083
+ if (deps.existsSync(backupPathFor(deps.appPath))) {
106084
+ status = "repair-needed";
106085
+ message = "An interrupted OpenScout install needs repair \u2014 run `scout install --force`.";
106086
+ } else {
106087
+ status = "not-installed";
106088
+ message = "OpenScout is not installed \u2014 run `scout install`.";
106089
+ }
105240
106090
  } else {
105241
- status = "update-available";
105242
- message = `Update available: ${installed} \u2192 ${target2}. Run \`scout install\`.`;
106091
+ try {
106092
+ installed = verifyStagedOpenScoutApp(deps.appPath, deps);
106093
+ if (installed === target2) {
106094
+ status = "up-to-date";
106095
+ message = `OpenScout ${installed} is up to date.`;
106096
+ } else {
106097
+ status = "update-available";
106098
+ message = `Update available: ${installed} \u2192 ${target2}. Run \`scout install\`.`;
106099
+ }
106100
+ } catch (error48) {
106101
+ status = "repair-needed";
106102
+ message = `OpenScout needs repair (${error48 instanceof Error ? error48.message : String(error48)}). ` + "Run `scout install --force`.";
106103
+ }
105243
106104
  }
105244
- context.output.writeValue({ action: "check", status, installed, target: target2, bundlePath: APP_PATH, message }, renderInstallResult);
106105
+ context.output.writeValue({ action: "check", status, installed, target: target2, bundlePath: deps.appPath, message }, renderInstallResult);
105245
106106
  }
105246
- async function runInstallCommand(context, args) {
106107
+ async function runInstallCommand(context, args, dependencies = {}) {
105247
106108
  if (HELP_FLAGS9.has(args[0] ?? "")) {
105248
106109
  context.output.writeText(renderInstallCommandHelp());
105249
106110
  return;
105250
106111
  }
105251
- if (process.platform !== "darwin") {
106112
+ const deps = resolveInstallDependencies(dependencies);
106113
+ if (deps.platform !== "darwin") {
105252
106114
  throw new ScoutCliError("scout install is only supported on macOS.");
105253
106115
  }
105254
106116
  const options = parseInstallArgs(args);
106117
+ if (options.version)
106118
+ normalizeReleaseVersion(options.version);
105255
106119
  if (options.check) {
105256
- await runCheck(context, options);
106120
+ await runCheck(context, options, deps);
105257
106121
  return;
105258
106122
  }
105259
- const installedBefore = getInstalledVersion();
105260
106123
  context.stderr(options.version ? `Fetching release ${options.version}\u2026` : "Fetching the latest OpenScout release\u2026");
105261
- const release = await fetchRelease(options.version);
105262
- const target2 = stripLeadingV(release.tag_name);
105263
- if (!options.force && installedBefore && installedBefore === target2) {
106124
+ const release = await fetchRelease(options.version, deps);
106125
+ const target2 = normalizeReleaseVersion(release.tag_name);
106126
+ const releaseInstallLock = acquireInstallLock(deps);
106127
+ try {
106128
+ recoverIncompleteInstall(deps);
106129
+ const hadInstalledApp = deps.existsSync(deps.appPath);
106130
+ const installedBefore = getInstalledVersion(deps);
106131
+ let repairingInstalledApp = false;
106132
+ if (!options.force && installedBefore === target2) {
106133
+ try {
106134
+ const verifiedVersion = verifyStagedOpenScoutApp(deps.appPath, deps, target2);
106135
+ context.output.writeValue({
106136
+ action: "install",
106137
+ status: "up-to-date",
106138
+ installed: verifiedVersion,
106139
+ target: target2,
106140
+ bundlePath: deps.appPath,
106141
+ message: `OpenScout ${target2} is already installed (use --force to reinstall).`
106142
+ }, renderInstallResult);
106143
+ return;
106144
+ } catch (error48) {
106145
+ repairingInstalledApp = true;
106146
+ context.stderr(`Installed OpenScout ${target2} failed verification; repairing it: ${error48 instanceof Error ? error48.message : String(error48)}`);
106147
+ }
106148
+ }
106149
+ const asset = findAppDmgAsset(release);
106150
+ const workDir = deps.mkdtempSync(join48(deps.tmpdir(), "scout-install-"));
106151
+ const dmgPath = join48(workDir, asset.name);
106152
+ const mountPoint = join48(workDir, "mnt");
106153
+ const stagedPath = join48(workDir, OPENSCOUT_APP_NAME);
106154
+ let mounted = false;
106155
+ let wasRunning = false;
106156
+ let installedAfter = "";
106157
+ try {
106158
+ await downloadDmg(asset, dmgPath, context, deps);
106159
+ context.stderr(`Installing OpenScout ${target2} to ${deps.appPath}\u2026`);
106160
+ verifyDownloadedDmg(dmgPath, deps);
106161
+ mountDmg(dmgPath, mountPoint, deps);
106162
+ mounted = true;
106163
+ const source = requireMountedOpenScoutApp(mountPoint, deps);
106164
+ copyApp(source, stagedPath, deps);
106165
+ verifyStagedOpenScoutApp(stagedPath, deps, target2);
106166
+ wasRunning = stopRunningApp(deps);
106167
+ installedAfter = replaceAppTransactionally(stagedPath, deps, target2);
106168
+ } finally {
106169
+ if (mounted)
106170
+ unmountDmg(mountPoint, deps);
106171
+ deps.rmSync(workDir, { recursive: true, force: true });
106172
+ }
106173
+ if (wasRunning && options.restart) {
106174
+ launchApp(deps);
106175
+ }
106176
+ const status = hadInstalledApp ? "updated" : "installed";
106177
+ const relaunchNote = wasRunning ? options.restart ? " (relaunched)" : " (restart OpenScout to use the new version)" : "";
106178
+ let verb = "Installed";
106179
+ if (repairingInstalledApp || hadInstalledApp && !installedBefore) {
106180
+ verb = "Repaired";
106181
+ } else if (installedBefore) {
106182
+ verb = `Updated ${installedBefore} \u2192`;
106183
+ }
105264
106184
  context.output.writeValue({
105265
106185
  action: "install",
105266
- status: "up-to-date",
105267
- installed: installedBefore,
106186
+ status,
106187
+ installed: installedAfter,
105268
106188
  target: target2,
105269
- bundlePath: APP_PATH,
105270
- message: `OpenScout ${target2} is already installed (use --force to reinstall).`
106189
+ bundlePath: deps.appPath,
106190
+ message: `${verb} OpenScout ${installedAfter} \u2192 ${deps.appPath}${relaunchNote}`
105271
106191
  }, renderInstallResult);
105272
- return;
105273
- }
105274
- const asset = findAppDmgAsset(release);
105275
- const wasRunning = isAppRunning();
105276
- const workDir = mkdtempSync(join48(tmpdir(), "scout-install-"));
105277
- const dmgPath = join48(workDir, asset.name);
105278
- let mountPoint = null;
105279
- try {
105280
- await downloadDmg(asset, dmgPath, context);
105281
- context.stderr(`Installing OpenScout ${target2} to ${APP_PATH}\u2026`);
105282
- mountPoint = mountDmg(dmgPath);
105283
- copyAppFromMount(mountPoint);
105284
106192
  } finally {
105285
- if (mountPoint)
105286
- unmountDmg(mountPoint);
105287
- rmSync5(workDir, { recursive: true, force: true });
105288
- }
105289
- removeQuarantine();
105290
- if (wasRunning && options.restart) {
105291
- quitApp();
105292
- launchApp();
105293
- }
105294
- const installedAfter = getInstalledVersion() ?? target2;
105295
- const status = installedBefore ? "updated" : "installed";
105296
- const relaunchNote = wasRunning ? options.restart ? " (relaunched)" : " (restart OpenScout to use the new version)" : "";
105297
- const verb = installedBefore ? `Updated ${installedBefore} \u2192` : "Installed";
105298
- context.output.writeValue({
105299
- action: "install",
105300
- status,
105301
- installed: installedAfter,
105302
- target: target2,
105303
- bundlePath: APP_PATH,
105304
- message: `${verb} OpenScout ${installedAfter} \u2192 ${APP_PATH}${relaunchNote}`
105305
- }, renderInstallResult);
106193
+ releaseInstallLock();
106194
+ }
105306
106195
  }
105307
- var GITHUB_OWNER = "arach", GITHUB_REPO = "openscout", 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;
106196
+ 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, HELP_FLAGS9;
105308
106197
  var init_install = __esm(() => {
105309
106198
  init_errors();
105310
- APP_PATH = `/Applications/${APP_NAME}`;
105311
- INFO_PLIST_PATH = `${APP_PATH}/Contents/Info.plist`;
106199
+ DEFAULT_OPENSCOUT_APP_PATH = `/Applications/${OPENSCOUT_APP_NAME}`;
105312
106200
  HELP_FLAGS9 = new Set(["help", "--help", "-h"]);
105313
106201
  });
105314
106202
 
@@ -110023,12 +110911,12 @@ function normalizedStringOrNull(value) {
110023
110911
  const trimmed = value?.trim();
110024
110912
  return trimmed ? trimmed : null;
110025
110913
  }
110026
- function metadataStringValue3(metadata, key) {
110914
+ function metadataStringValue4(metadata, key) {
110027
110915
  const value = metadata?.[key];
110028
110916
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
110029
110917
  }
110030
110918
  function isBuiltInDirectoryAgent(agent) {
110031
- const definitionId = agent.definitionId || metadataStringValue3(agent.metadata, "definitionId") || agent.id;
110919
+ const definitionId = agent.definitionId || metadataStringValue4(agent.metadata, "definitionId") || agent.id;
110032
110920
  return BUILT_IN_AGENT_DEFINITION_IDS.has(definitionId);
110033
110921
  }
110034
110922
  function rankState(state) {
@@ -113314,7 +114202,7 @@ var init_scout_mcp = __esm(() => {
113314
114202
 
113315
114203
  // ../../apps/desktop/src/cli/commands/mcp-install.ts
113316
114204
  import { spawnSync as spawnSync9 } from "child_process";
113317
- import { accessSync as accessSync4, constants as constants4, existsSync as existsSync40 } from "fs";
114205
+ import { accessSync as accessSync4, constants as constants4, existsSync as existsSync39 } from "fs";
113318
114206
  import { homedir as homedir31 } from "os";
113319
114207
  import { delimiter as delimiter4, join as join49, resolve as resolve29 } from "path";
113320
114208
  function isExecutable3(filePath) {
@@ -113355,7 +114243,7 @@ function resolveHostExecutable(host, env) {
113355
114243
  }
113356
114244
  function resolveCurrentScoutMcpLaunchCommand(env) {
113357
114245
  const currentScript = process.argv[1];
113358
- if (currentScript && existsSync40(currentScript) && isExecutable3(process.execPath)) {
114246
+ if (currentScript && existsSync39(currentScript) && isExecutable3(process.execPath)) {
113359
114247
  return {
113360
114248
  command: process.execPath,
113361
114249
  args: [resolve29(currentScript), "mcp"]
@@ -113728,7 +114616,7 @@ __export(exports_menu, {
113728
114616
  parseMenuCommand: () => parseMenuCommand
113729
114617
  });
113730
114618
  import { spawnSync as spawnSync10 } from "child_process";
113731
- import { existsSync as existsSync41 } from "fs";
114619
+ import { existsSync as existsSync40 } from "fs";
113732
114620
  import { homedir as homedir32 } from "os";
113733
114621
  import { dirname as dirname28, join as join50, resolve as resolve30 } from "path";
113734
114622
  import { fileURLToPath as fileURLToPath11 } from "url";
@@ -113818,7 +114706,7 @@ function findRepoMenuHelper(startDirectory) {
113818
114706
  let current = resolve30(startDirectory);
113819
114707
  while (true) {
113820
114708
  const candidate = join50(current, "apps", "macos", "bin", "openscout-menu.ts");
113821
- if (existsSync41(candidate)) {
114709
+ if (existsSync40(candidate)) {
113822
114710
  return candidate;
113823
114711
  }
113824
114712
  const parent = dirname28(current);
@@ -113828,7 +114716,7 @@ function findRepoMenuHelper(startDirectory) {
113828
114716
  current = parent;
113829
114717
  }
113830
114718
  const sourceRelativeCandidate = fileURLToPath11(new URL("../../../../macos/bin/openscout-menu.ts", import.meta.url).toString());
113831
- return existsSync41(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
114719
+ return existsSync40(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
113832
114720
  }
113833
114721
  function resolveRepoBundlePath(helperPath) {
113834
114722
  return resolve30(dirname28(helperPath), "..", "dist", "Scout.app", EMBEDDED_MENU_RELATIVE_PATH);
@@ -113860,14 +114748,14 @@ async function stopRunningMenu(helperBundlePath) {
113860
114748
  return survivors.length === 0;
113861
114749
  }
113862
114750
  function resolveInstalledAppBundlePath(env) {
113863
- const spotlight = runProcess("mdfind", [`kMDItemCFBundleIdentifier == '${APP_BUNDLE_ID2}'`], { env, allowFailure: true });
114751
+ const spotlight = runProcess("mdfind", [`kMDItemCFBundleIdentifier == '${APP_BUNDLE_ID}'`], { env, allowFailure: true });
113864
114752
  const indexedPath = spotlight.stdout.split(`
113865
114753
  `).map((line) => line.trim()).find(Boolean);
113866
114754
  if (indexedPath) {
113867
114755
  return indexedPath;
113868
114756
  }
113869
114757
  for (const candidate of COMMON_APP_BUNDLE_PATHS) {
113870
- if (existsSync41(candidate)) {
114758
+ if (existsSync40(candidate)) {
113871
114759
  return candidate;
113872
114760
  }
113873
114761
  }
@@ -113955,7 +114843,7 @@ async function runWithRepoHelper(context, helperPath, command) {
113955
114843
  });
113956
114844
  }
113957
114845
  const running = command.action === "quit" ? false : isMenuRunning(bundlePath);
113958
- const installed = existsSync41(bundlePath) || running;
114846
+ const installed = existsSync40(bundlePath) || running;
113959
114847
  return {
113960
114848
  action: command.action,
113961
114849
  mode: "repo-helper",
@@ -114021,7 +114909,7 @@ async function runMenuCommand(context, args) {
114021
114909
  const result = helperPath ? await runWithRepoHelper(context, helperPath, command) : await runWithInstalledApp(context, command);
114022
114910
  context.output.writeValue(result, renderMenuResult);
114023
114911
  }
114024
- 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;
114912
+ 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;
114025
114913
  var init_menu = __esm(() => {
114026
114914
  init_src3();
114027
114915
  init_app_lifecycle();
@@ -114838,7 +115726,7 @@ function renderMeshNodes(input) {
114838
115726
  var init_mesh = () => {};
114839
115727
 
114840
115728
  // ../../apps/desktop/src/core/mesh/enroll-pending.ts
114841
- import { existsSync as existsSync42, mkdirSync as mkdirSync12, readFileSync as readFileSync19, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
115729
+ import { existsSync as existsSync41, mkdirSync as mkdirSync12, readFileSync as readFileSync19, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "fs";
114842
115730
  import { dirname as dirname29, join as join51 } from "path";
114843
115731
  function pendingMeshEnrollmentPath(supportDirectory) {
114844
115732
  const dir = supportDirectory ?? resolveOpenScoutSupportPaths().supportDirectory;
@@ -114850,12 +115738,12 @@ function savePendingMeshEnrollment(handshake, supportDirectory) {
114850
115738
  }
114851
115739
  const path = pendingMeshEnrollmentPath(supportDirectory);
114852
115740
  mkdirSync12(dirname29(path), { recursive: true });
114853
- writeFileSync10(path, `${JSON.stringify(handshake, null, 2)}
115741
+ writeFileSync9(path, `${JSON.stringify(handshake, null, 2)}
114854
115742
  `, { encoding: "utf8", mode: 384 });
114855
115743
  }
114856
115744
  function loadPendingMeshEnrollment(supportDirectory) {
114857
115745
  const path = pendingMeshEnrollmentPath(supportDirectory);
114858
- if (!existsSync42(path)) {
115746
+ if (!existsSync41(path)) {
114859
115747
  return null;
114860
115748
  }
114861
115749
  let parsed;
@@ -114871,8 +115759,8 @@ function loadPendingMeshEnrollment(supportDirectory) {
114871
115759
  }
114872
115760
  function clearPendingMeshEnrollment(supportDirectory) {
114873
115761
  const path = pendingMeshEnrollmentPath(supportDirectory);
114874
- if (existsSync42(path)) {
114875
- rmSync6(path);
115762
+ if (existsSync41(path)) {
115763
+ rmSync5(path);
114876
115764
  }
114877
115765
  }
114878
115766
  var PENDING_FILE = "mesh-enroll-pending.json";
@@ -115635,7 +116523,7 @@ var exports_mesh_bridge = {};
115635
116523
  __export(exports_mesh_bridge, {
115636
116524
  runMeshBridgeCommand: () => runMeshBridgeCommand
115637
116525
  });
115638
- import { existsSync as existsSync43, mkdirSync as mkdirSync13, readFileSync as readFileSync21, unlinkSync, writeFileSync as writeFileSync11, chmodSync as chmodSync2 } from "fs";
116526
+ import { existsSync as existsSync42, mkdirSync as mkdirSync13, readFileSync as readFileSync21, unlinkSync, writeFileSync as writeFileSync10, chmodSync as chmodSync2 } from "fs";
115639
116527
  import { homedir as homedir33 } from "os";
115640
116528
  import { join as join52 } from "path";
115641
116529
  function supportDirectory() {
@@ -115654,7 +116542,7 @@ function readFlag(args, name2) {
115654
116542
  return args[index2 + 1]?.trim() || undefined;
115655
116543
  }
115656
116544
  function loadConfigFile(path) {
115657
- if (!existsSync43(path))
116545
+ if (!existsSync42(path))
115658
116546
  return null;
115659
116547
  return JSON.parse(readFileSync21(path, "utf8"));
115660
116548
  }
@@ -115742,7 +116630,7 @@ async function installBridge(context, args) {
115742
116630
  return;
115743
116631
  }
115744
116632
  mkdirSync13(supportDirectory(), { recursive: true });
115745
- writeFileSync11(configPath, `${JSON.stringify(config2, null, 2)}
116633
+ writeFileSync10(configPath, `${JSON.stringify(config2, null, 2)}
115746
116634
  `);
115747
116635
  chmodSync2(configPath, 384);
115748
116636
  const logDirectory = join52(supportDirectory(), "logs");
@@ -115770,7 +116658,7 @@ async function installBridge(context, args) {
115770
116658
  </dict>
115771
116659
  </plist>
115772
116660
  `;
115773
- writeFileSync11(launchAgentPlistPath2(), plist);
116661
+ writeFileSync10(launchAgentPlistPath2(), plist);
115774
116662
  const domain2 = `gui/${process.getuid?.() ?? 501}`;
115775
116663
  launchctl(context, ["bootout", `${domain2}/${LAUNCH_AGENT_LABEL}`]);
115776
116664
  const bootstrap = launchctl(context, ["bootstrap", domain2, launchAgentPlistPath2()]);
@@ -115787,7 +116675,7 @@ async function installBridge(context, args) {
115787
116675
  async function uninstallBridge(context) {
115788
116676
  const domain2 = `gui/${process.getuid?.() ?? 501}`;
115789
116677
  launchctl(context, ["bootout", `${domain2}/${LAUNCH_AGENT_LABEL}`]);
115790
- if (existsSync43(launchAgentPlistPath2())) {
116678
+ if (existsSync42(launchAgentPlistPath2())) {
115791
116679
  unlinkSync(launchAgentPlistPath2());
115792
116680
  }
115793
116681
  context.stdout(`Removed ${LAUNCH_AGENT_LABEL} (config file kept).`);
@@ -115991,7 +116879,7 @@ var init_mesh2 = __esm(() => {
115991
116879
  });
115992
116880
 
115993
116881
  // ../runtime/src/agent-workspace.ts
115994
- import { existsSync as existsSync44 } from "fs";
116882
+ import { existsSync as existsSync43 } from "fs";
115995
116883
  import { mkdir as mkdir13 } from "fs/promises";
115996
116884
  import { join as join53 } from "path";
115997
116885
  async function isGitRepo(projectRoot) {
@@ -116015,7 +116903,7 @@ async function createAgentWorkspace(projectRoot, agentName, requestedBranch) {
116015
116903
  validateWorktreeBranch(branch);
116016
116904
  const workspaceDir = join53(projectRoot, SCOUT_WORKSPACES_DIRNAME);
116017
116905
  const workspacePath = join53(workspaceDir, agentName);
116018
- if (existsSync44(workspacePath)) {
116906
+ if (existsSync43(workspacePath)) {
116019
116907
  return { path: workspacePath, branch };
116020
116908
  }
116021
116909
  await mkdir13(workspaceDir, { recursive: true });
@@ -117732,12 +118620,12 @@ var init_pairing_supervisor = () => {};
117732
118620
 
117733
118621
  // ../../apps/desktop/src/app/host/pairing.ts
117734
118622
  import {
117735
- existsSync as existsSync45,
118623
+ existsSync as existsSync44,
117736
118624
  mkdirSync as mkdirSync14,
117737
118625
  readFileSync as readFileSync22,
117738
118626
  statSync as statSync12,
117739
118627
  unlinkSync as unlinkSync2,
117740
- writeFileSync as writeFileSync12
118628
+ writeFileSync as writeFileSync11
117741
118629
  } from "fs";
117742
118630
  import { homedir as homedir35 } from "os";
117743
118631
  import { join as join55 } from "path";
@@ -117884,7 +118772,7 @@ async function withScoutPairingBridgeClient(port, run2) {
117884
118772
  }
117885
118773
  function loadScoutPairingConfig() {
117886
118774
  const { configPath } = resolveScoutPairingPaths();
117887
- if (!existsSync45(configPath)) {
118775
+ if (!existsSync44(configPath)) {
117888
118776
  return {};
117889
118777
  }
117890
118778
  try {
@@ -117920,7 +118808,7 @@ function resolveScoutPairingConfig() {
117920
118808
  }
117921
118809
  function readScoutPairingRuntimeSnapshot() {
117922
118810
  const { runtimeStatePath } = resolveScoutPairingPaths();
117923
- if (!existsSync45(runtimeStatePath)) {
118811
+ if (!existsSync44(runtimeStatePath)) {
117924
118812
  return null;
117925
118813
  }
117926
118814
  try {
@@ -117932,7 +118820,7 @@ function readScoutPairingRuntimeSnapshot() {
117932
118820
  }
117933
118821
  function readScoutPairingRuntimePid() {
117934
118822
  const { runtimePidPath } = resolveScoutPairingPaths();
117935
- if (!existsSync45(runtimePidPath)) {
118823
+ if (!existsSync44(runtimePidPath)) {
117936
118824
  return null;
117937
118825
  }
117938
118826
  try {
@@ -117984,7 +118872,7 @@ var init_pairing = __esm(() => {
117984
118872
 
117985
118873
  // ../../apps/desktop/src/app/host/agent-session.ts
117986
118874
  import { execFileSync as execFileSync6 } from "child_process";
117987
- import { existsSync as existsSync46 } from "fs";
118875
+ import { existsSync as existsSync45 } from "fs";
117988
118876
  import { open as open2 } from "fs/promises";
117989
118877
  import path from "path";
117990
118878
  function compactHomePath2(value) {
@@ -118125,7 +119013,7 @@ async function readAgentSessionLogs(agentId, tailLines) {
118125
119013
  let foundAny = false;
118126
119014
  let targetPath = null;
118127
119015
  for (const filePath of sources) {
118128
- if (!existsSync46(filePath)) {
119016
+ if (!existsSync45(filePath)) {
118129
119017
  continue;
118130
119018
  }
118131
119019
  foundAny = true;
@@ -121781,7 +122669,7 @@ var init_monitor2 = __esm(() => {
121781
122669
  });
121782
122670
 
121783
122671
  // ../../apps/desktop/src/core/pairing/runtime/config.ts
121784
- import { existsSync as existsSync47, mkdirSync as mkdirSync15, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
122672
+ import { existsSync as existsSync46, mkdirSync as mkdirSync15, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
121785
122673
  import { homedir as homedir36 } from "os";
121786
122674
  import path2 from "path";
121787
122675
  function pairingPaths() {
@@ -121798,7 +122686,7 @@ function pairingPaths() {
121798
122686
  }
121799
122687
  function loadPairingConfig() {
121800
122688
  const { configPath } = pairingPaths();
121801
- if (!existsSync47(configPath)) {
122689
+ if (!existsSync46(configPath)) {
121802
122690
  return {};
121803
122691
  }
121804
122692
  try {
@@ -123902,11 +124790,11 @@ var init_noise = __esm(() => {
123902
124790
  });
123903
124791
 
123904
124792
  // ../../apps/desktop/src/core/pairing/runtime/security/identity.ts
123905
- import { existsSync as existsSync48, mkdirSync as mkdirSync16, readFileSync as readFileSync24, writeFileSync as writeFileSync14 } from "fs";
124793
+ import { existsSync as existsSync47, mkdirSync as mkdirSync16, readFileSync as readFileSync24, writeFileSync as writeFileSync13 } from "fs";
123906
124794
  import { join as join56 } from "path";
123907
124795
  import { homedir as homedir37 } from "os";
123908
124796
  function loadOrCreateIdentity() {
123909
- if (existsSync48(IDENTITY_FILE)) {
124797
+ if (existsSync47(IDENTITY_FILE)) {
123910
124798
  return loadIdentity();
123911
124799
  }
123912
124800
  return createAndSaveIdentity();
@@ -123926,11 +124814,11 @@ function createAndSaveIdentity() {
123926
124814
  privateKey: bytesToHex2(keyPair.privateKey),
123927
124815
  createdAt: new Date().toISOString()
123928
124816
  };
123929
- writeFileSync14(IDENTITY_FILE, JSON.stringify(data, null, 2), { mode: 384 });
124817
+ writeFileSync13(IDENTITY_FILE, JSON.stringify(data, null, 2), { mode: 384 });
123930
124818
  return keyPair;
123931
124819
  }
123932
124820
  function loadTrustedPeers() {
123933
- if (!existsSync48(TRUSTED_PEERS_FILE))
124821
+ if (!existsSync47(TRUSTED_PEERS_FILE))
123934
124822
  return new Map;
123935
124823
  const data = JSON.parse(readFileSync24(TRUSTED_PEERS_FILE, "utf8"));
123936
124824
  return new Map(data.map((p) => [p.publicKey, p]));
@@ -123939,7 +124827,7 @@ function saveTrustedPeer(peer) {
123939
124827
  const peers = loadTrustedPeers();
123940
124828
  peers.set(peer.publicKey, peer);
123941
124829
  mkdirSync16(PAIRING_DIR, { recursive: true });
123942
- writeFileSync14(TRUSTED_PEERS_FILE, JSON.stringify([...peers.values()], null, 2), { mode: 384 });
124830
+ writeFileSync13(TRUSTED_PEERS_FILE, JSON.stringify([...peers.values()], null, 2), { mode: 384 });
123943
124831
  }
123944
124832
  function isTrustedPeer(publicKeyHex) {
123945
124833
  return loadTrustedPeers().has(publicKeyHex);
@@ -124144,10 +125032,10 @@ var init_security2 = __esm(() => {
124144
125032
  });
124145
125033
 
124146
125034
  // ../../apps/desktop/src/core/pairing/runtime/runtime-state.ts
124147
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync25, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync15 } from "fs";
125035
+ import { existsSync as existsSync48, mkdirSync as mkdirSync17, readFileSync as readFileSync25, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "fs";
124148
125036
  function readPairingRuntimeSnapshot() {
124149
125037
  const { runtimeStatePath } = pairingPaths();
124150
- if (!existsSync49(runtimeStatePath)) {
125038
+ if (!existsSync48(runtimeStatePath)) {
124151
125039
  return null;
124152
125040
  }
124153
125041
  try {
@@ -124269,11 +125157,11 @@ var init_bridge = __esm(() => {
124269
125157
  });
124270
125158
 
124271
125159
  // ../../apps/desktop/src/core/pairing/runtime/bridge/config.ts
124272
- import { existsSync as existsSync50, readFileSync as readFileSync26 } from "fs";
125160
+ import { existsSync as existsSync49, readFileSync as readFileSync26 } from "fs";
124273
125161
  import { join as join58 } from "path";
124274
125162
  import { homedir as homedir39 } from "os";
124275
125163
  function loadConfigFile2() {
124276
- if (!existsSync50(CONFIG_FILE)) {
125164
+ if (!existsSync49(CONFIG_FILE)) {
124277
125165
  return {};
124278
125166
  }
124279
125167
  try {
@@ -124605,7 +125493,7 @@ var init_artifact_presentation = __esm(() => {
124605
125493
 
124606
125494
  // ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
124607
125495
  import { randomUUID as randomUUID6 } from "crypto";
124608
- import { existsSync as existsSync51, mkdirSync as mkdirSync19, readFileSync as readFileSync27, realpathSync as realpathSync4, statSync as statSync14, unlinkSync as unlinkSync4, writeFileSync as writeFileSync16 } from "fs";
125496
+ import { existsSync as existsSync50, mkdirSync as mkdirSync19, readFileSync as readFileSync27, realpathSync as realpathSync4, statSync as statSync14, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "fs";
124609
125497
  import { BlockList, isIP } from "net";
124610
125498
  import { isAbsolute as isAbsolute6, join as join59, relative as relative6, sep as sep4 } from "path";
124611
125499
  import { homedir as homedir40 } from "os";
@@ -124628,7 +125516,7 @@ function storePairingAttachmentBlob(input, options) {
124628
125516
  purgeExpiredPairingAttachments(options.now);
124629
125517
  const id = `att-${randomUUID6()}`;
124630
125518
  const path3 = join59(PAIRING_ATTACHMENT_ROOT, id);
124631
- writeFileSync16(path3, data, { mode: 384 });
125519
+ writeFileSync15(path3, data, { mode: 384 });
124632
125520
  const now = options.now ?? Date.now();
124633
125521
  const entry = {
124634
125522
  id,
@@ -124676,14 +125564,14 @@ function resolvePairingAttachment(id, now) {
124676
125564
  }
124677
125565
  const known = attachmentBlobs.get(id);
124678
125566
  if (known) {
124679
- if (known.expiresAt <= now || !existsSync51(known.path)) {
125567
+ if (known.expiresAt <= now || !existsSync50(known.path)) {
124680
125568
  forgetPairingAttachment(id);
124681
125569
  return null;
124682
125570
  }
124683
125571
  return known;
124684
125572
  }
124685
125573
  const rehydrated = readAttachmentSidecar(id);
124686
- if (!rehydrated || rehydrated.expiresAt <= now || !existsSync51(rehydrated.path)) {
125574
+ if (!rehydrated || rehydrated.expiresAt <= now || !existsSync50(rehydrated.path)) {
124687
125575
  return null;
124688
125576
  }
124689
125577
  attachmentBlobs.set(id, rehydrated);
@@ -124694,7 +125582,7 @@ function attachmentSidecarPath(id) {
124694
125582
  }
124695
125583
  function writeAttachmentSidecar(entry) {
124696
125584
  try {
124697
- writeFileSync16(attachmentSidecarPath(entry.id), JSON.stringify({
125585
+ writeFileSync15(attachmentSidecarPath(entry.id), JSON.stringify({
124698
125586
  id: entry.id,
124699
125587
  mediaType: entry.mediaType,
124700
125588
  fileName: entry.fileName,
@@ -125442,7 +126330,7 @@ var init_fileserver = __esm(() => {
125442
126330
  });
125443
126331
 
125444
126332
  // ../../apps/desktop/src/core/pairing/runtime/bridge/ssh-terminal-access.ts
125445
- import { createHash as createHash11 } from "crypto";
126333
+ import { createHash as createHash13 } from "crypto";
125446
126334
  function validateIosGeneratedSshPublicKey(publicKey) {
125447
126335
  const fields = publicKey.trim().split(/\s+/);
125448
126336
  if (fields.length < 2) {
@@ -125603,10 +126491,10 @@ function readSshWireString(buffer, offset) {
125603
126491
  };
125604
126492
  }
125605
126493
  function fingerprintKeyData(keyData) {
125606
- return `SHA256:${createHash11("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "")}`;
126494
+ return `SHA256:${createHash13("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "")}`;
125607
126495
  }
125608
126496
  function fingerprintUtf8(value) {
125609
- return `SHA256:${createHash11("sha256").update(value, "utf8").digest("base64").replace(/=+$/, "")}`;
126497
+ return `SHA256:${createHash13("sha256").update(value, "utf8").digest("base64").replace(/=+$/, "")}`;
125610
126498
  }
125611
126499
  function encodeMarkerValue(value) {
125612
126500
  return Buffer.from(value, "utf8").toString("base64url");
@@ -125629,10 +126517,10 @@ var init_ssh_terminal_access = __esm(() => {
125629
126517
  });
125630
126518
 
125631
126519
  // ../../apps/desktop/src/core/pairing/runtime/bridge/mobile-terminal-session.ts
125632
- import { createHash as createHash12 } from "crypto";
126520
+ import { createHash as createHash14 } from "crypto";
125633
126521
  function mobileTerminalSessionName(sshPublicKey, deviceClass) {
125634
126522
  const normalizedClass = normalizeMobileTerminalDeviceClass(deviceClass);
125635
- const signature = createHash12("sha256").update(sshPublicKey.trim()).digest("hex").slice(0, 8);
126523
+ const signature = createHash14("sha256").update(sshPublicKey.trim()).digest("hex").slice(0, 8);
125636
126524
  return `scout-${normalizedClass}-${signature}`;
125637
126525
  }
125638
126526
  function provisionedMobileTerminalSessionName(sshPublicKey, deviceClass) {
@@ -125668,23 +126556,23 @@ var init_mobile_terminal_session = __esm(() => {
125668
126556
  });
125669
126557
 
125670
126558
  // ../../apps/desktop/src/core/pairing/runtime/bridge/mobile-terminal-provision.ts
125671
- import { createHash as createHash13 } from "crypto";
126559
+ import { createHash as createHash15 } from "crypto";
125672
126560
  import {
125673
126561
  chmodSync as chmodSync3,
125674
126562
  closeSync,
125675
- existsSync as existsSync52,
126563
+ existsSync as existsSync51,
125676
126564
  mkdirSync as mkdirSync20,
125677
126565
  openSync,
125678
126566
  readFileSync as readFileSync28,
125679
126567
  renameSync as renameSync6,
125680
126568
  unlinkSync as unlinkSync5,
125681
- writeFileSync as writeFileSync17
126569
+ writeFileSync as writeFileSync16
125682
126570
  } from "fs";
125683
126571
  import { homedir as homedir41, hostname as hostname5, userInfo as userInfo2 } from "os";
125684
126572
  import { join as join60 } from "path";
125685
126573
  async function readMobileTerminalStatus(requestedSessionName, deviceId) {
125686
126574
  const authorizedKeysPath = join60(homedir41(), ".ssh", "authorized_keys");
125687
- const authorizedKeys = existsSync52(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
126575
+ const authorizedKeys = existsSync51(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
125688
126576
  const provisionedSessionName = findSshTerminalSessionForDevice(authorizedKeys, deviceId);
125689
126577
  const sessionName = authorizedMobileTerminalSessionName(requestedSessionName, provisionedSessionName);
125690
126578
  const base = {
@@ -125760,13 +126648,13 @@ async function provisionMobileTerminalAccess(sshPublicKey, deviceId, deviceClass
125760
126648
  const authorizedKeysPath = join60(sshDir, "authorized_keys");
125761
126649
  const hostKeyFingerprint = readRequiredHostKeyFingerprint();
125762
126650
  const result = withAuthorizedKeysLock(sshDir, () => {
125763
- if (!existsSync52(sshDir)) {
126651
+ if (!existsSync51(sshDir)) {
125764
126652
  mkdirSync20(sshDir, { recursive: true, mode: 448 });
125765
126653
  }
125766
126654
  try {
125767
126655
  chmodSync3(sshDir, 448);
125768
126656
  } catch {}
125769
- const existing = existsSync52(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
126657
+ const existing = existsSync51(authorizedKeysPath) ? readFileSync28(authorizedKeysPath, "utf8") : "";
125770
126658
  const result2 = provisionSshTerminalAccess({
125771
126659
  authorizedKeys: existing,
125772
126660
  deviceId: id,
@@ -125818,7 +126706,7 @@ function readRequiredHostKeyFingerprint() {
125818
126706
  const keyData = pub.trim().split(/\s+/)[1];
125819
126707
  if (!keyData)
125820
126708
  throw new Error("missing key data");
125821
- const digest = createHash13("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "");
126709
+ const digest = createHash15("sha256").update(Buffer.from(keyData, "base64")).digest("base64").replace(/=+$/, "");
125822
126710
  return `SHA256:${digest}`;
125823
126711
  } catch (error48) {
125824
126712
  const detail = error48 instanceof Error ? error48.message : String(error48);
@@ -125828,7 +126716,7 @@ function readRequiredHostKeyFingerprint() {
125828
126716
  function writeAuthorizedKeysAtomically(path3, contents) {
125829
126717
  const tmpPath = `${path3}.scout-${process.pid}-${Date.now()}.tmp`;
125830
126718
  try {
125831
- writeFileSync17(tmpPath, contents, { mode: 384, flag: "wx" });
126719
+ writeFileSync16(tmpPath, contents, { mode: 384, flag: "wx" });
125832
126720
  renameSync6(tmpPath, path3);
125833
126721
  } catch (error48) {
125834
126722
  try {
@@ -125838,7 +126726,7 @@ function writeAuthorizedKeysAtomically(path3, contents) {
125838
126726
  }
125839
126727
  }
125840
126728
  function withAuthorizedKeysLock(sshDir, action) {
125841
- if (!existsSync52(sshDir)) {
126729
+ if (!existsSync51(sshDir)) {
125842
126730
  mkdirSync20(sshDir, { recursive: true, mode: 448 });
125843
126731
  }
125844
126732
  const lockPath = join60(sshDir, ".scout-authorized-keys.lock");
@@ -130599,6 +131487,9 @@ function startRelay(port, options = {}) {
130599
131487
  } : {},
130600
131488
  fetch(req, server2) {
130601
131489
  const url2 = new URL(req.url);
131490
+ if (url2.pathname === "/pair" && req.method === "GET") {
131491
+ return proxyPairHttp(req, url2, options.pairHttpOrigin, server2.requestIP(req)?.address);
131492
+ }
130602
131493
  if (url2.pathname === "/resolve" && req.method === "POST") {
130603
131494
  return handleResolve(req, roomByBridgeKey, rooms);
130604
131495
  }
@@ -130742,6 +131633,46 @@ function startRelay(port, options = {}) {
130742
131633
  }
130743
131634
  };
130744
131635
  }
131636
+ async function proxyPairHttp(req, url2, pairHttpOrigin, clientIp) {
131637
+ const origin = pairHttpOrigin === undefined ? `http://127.0.0.1:${resolveWebPort()}` : pairHttpOrigin?.trim() || null;
131638
+ if (!origin) {
131639
+ return new Response("Pairing HTTP is not configured", { status: 503 });
131640
+ }
131641
+ const target2 = new URL("/pair", origin);
131642
+ target2.search = url2.search;
131643
+ const headers2 = pairProxyHeaders(req, clientIp);
131644
+ try {
131645
+ const response = await fetch(target2, {
131646
+ method: "GET",
131647
+ headers: headers2,
131648
+ redirect: "manual",
131649
+ signal: AbortSignal.timeout(3000)
131650
+ });
131651
+ const out = new Headers;
131652
+ for (const name2 of ["location", "cache-control", "content-type"]) {
131653
+ const value = response.headers.get(name2);
131654
+ if (value)
131655
+ out.set(name2, value);
131656
+ }
131657
+ return new Response(response.body, { status: response.status, headers: out });
131658
+ } catch {
131659
+ return new Response("Pairing service unreachable", { status: 502 });
131660
+ }
131661
+ }
131662
+ function pairProxyHeaders(req, clientIp) {
131663
+ const headers2 = new Headers;
131664
+ for (const name2 of ["accept", "user-agent", "x-scout-client", "x-scout-device-name"]) {
131665
+ const value = req.headers.get(name2);
131666
+ if (value)
131667
+ headers2.set(name2, value);
131668
+ }
131669
+ headers2.set("x-scout-pair-relay", "1");
131670
+ if (clientIp) {
131671
+ headers2.set("x-forwarded-for", clientIp);
131672
+ headers2.set("x-real-ip", clientIp);
131673
+ }
131674
+ return headers2;
131675
+ }
130745
131676
  async function handleResolve(req, roomByBridgeKey, rooms) {
130746
131677
  try {
130747
131678
  const body = await req.json();
@@ -130807,18 +131738,21 @@ function parseRelayEnvelope2(data) {
130807
131738
  }
130808
131739
  }
130809
131740
  var BRIDGE_ABSENCE_GRACE_MS = 30000, ROOM_IDLE_TIMEOUT_MS = 60000;
131741
+ var init_relay = __esm(() => {
131742
+ init_local_config();
131743
+ });
130810
131744
 
130811
131745
  // ../../apps/desktop/src/core/pairing/runtime/relay-runtime.ts
130812
- import { existsSync as existsSync53, mkdirSync as mkdirSync22 } from "fs";
131746
+ import { existsSync as existsSync52, mkdirSync as mkdirSync22 } from "fs";
130813
131747
  import { homedir as homedir46, networkInterfaces as networkInterfaces2 } from "os";
130814
131748
  import { join as join63 } from "path";
130815
131749
  async function findStoredTailscaleCert(hostname6) {
130816
- if (!existsSync53(PAIRING_DIR2)) {
131750
+ if (!existsSync52(PAIRING_DIR2)) {
130817
131751
  return null;
130818
131752
  }
130819
131753
  const certPath = join63(PAIRING_DIR2, `${hostname6}.crt`);
130820
131754
  const keyPath = join63(PAIRING_DIR2, `${hostname6}.key`);
130821
- if (!existsSync53(certPath) || !existsSync53(keyPath)) {
131755
+ if (!existsSync52(certPath) || !existsSync52(keyPath)) {
130822
131756
  return null;
130823
131757
  }
130824
131758
  if (!await storedCertificateLooksPubliclyTrusted(certPath)) {
@@ -131030,6 +131964,7 @@ var PAIRING_DIR2;
131030
131964
  var init_relay_runtime = __esm(() => {
131031
131965
  init_system_probes();
131032
131966
  init_log2();
131967
+ init_relay();
131033
131968
  PAIRING_DIR2 = join63(homedir46(), ".scout/pairing");
131034
131969
  });
131035
131970
 
@@ -133196,7 +134131,7 @@ __export(exports_session, {
133196
134131
  mergeTerminalSurfaces: () => mergeTerminalSurfaces
133197
134132
  });
133198
134133
  import { spawnSync as spawnSync11 } from "child_process";
133199
- import { createHash as createHash14 } from "crypto";
134134
+ import { createHash as createHash16 } from "crypto";
133200
134135
  import { mkdirSync as mkdirSync23 } from "fs";
133201
134136
  import { homedir as homedir47 } from "os";
133202
134137
  import { basename as basename22, join as join64, resolve as resolve35 } from "path";
@@ -133561,7 +134496,7 @@ function terminalSurfaceKey(surface) {
133561
134496
  function defaultTerminalSessionName(input) {
133562
134497
  const projectName2 = sanitizeTerminalSessionNamePart(basename22(input.cwd) || "workspace", 28);
133563
134498
  const harnessName = sanitizeTerminalSessionNamePart(input.harness, 18);
133564
- const fingerprint = createHash14("sha1").update(`${input.backend}\x00${input.harness}\x00${input.cwd}\x00${input.sessionId}`).digest("hex").slice(0, 10);
134499
+ const fingerprint = createHash16("sha1").update(`${input.backend}\x00${input.harness}\x00${input.cwd}\x00${input.sessionId}`).digest("hex").slice(0, 10);
133565
134500
  return `scout-${harnessName}-${projectName2}-${fingerprint}`;
133566
134501
  }
133567
134502
  function sanitizeTerminalSessionNamePart(value, maxLength) {
@@ -133930,7 +134865,7 @@ __export(exports_server, {
133930
134865
  normalizeServerOpenPath: () => normalizeServerOpenPath
133931
134866
  });
133932
134867
  import { spawn as spawn10 } from "child_process";
133933
- import { existsSync as existsSync54, mkdirSync as mkdirSync24, openSync as openSync2, writeFileSync as writeFileSync18 } from "fs";
134868
+ import { existsSync as existsSync53, mkdirSync as mkdirSync24, openSync as openSync2, writeFileSync as writeFileSync17 } from "fs";
133934
134869
  import { homedir as homedir48 } from "os";
133935
134870
  import { dirname as dirname31, join as join65, resolve as resolve36 } from "path";
133936
134871
  import { fileURLToPath as fileURLToPath12 } from "url";
@@ -134370,7 +135305,7 @@ async function runScoutLocalEdge(env) {
134370
135305
  const config3 = resolveServerLocalEdgeConfig(env);
134371
135306
  const schemes = config3.scheme === "both" ? ["http", "https"] : [config3.scheme];
134372
135307
  const caddyfilePath = resolveScoutLocalEdgeCaddyfilePath();
134373
- writeFileSync18(caddyfilePath, renderOpenScoutCaddyfile(config3), "utf8");
135308
+ writeFileSync17(caddyfilePath, renderOpenScoutCaddyfile(config3), "utf8");
134374
135309
  const mdnsProcesses = schemes.flatMap((scheme) => {
134375
135310
  const edgePort = scheme === "https" ? 443 : 80;
134376
135311
  const suffix = scheme.toUpperCase();
@@ -134512,14 +135447,14 @@ __export(exports_setup, {
134512
135447
  runSetupCommand: () => runSetupCommand,
134513
135448
  resolveSetupSourceRoots: () => resolveSetupSourceRoots
134514
135449
  });
134515
- import { existsSync as existsSync55 } from "fs";
135450
+ import { existsSync as existsSync54 } from "fs";
134516
135451
  import { homedir as homedir49 } from "os";
134517
135452
  import { dirname as dirname32, join as join66, resolve as resolve37 } from "path";
134518
135453
  import { createInterface as createInterface4 } from "readline/promises";
134519
135454
  function findGitRoot2(startDirectory) {
134520
135455
  let current = resolve37(startDirectory);
134521
135456
  while (true) {
134522
- if (existsSync55(join66(current, ".git")))
135457
+ if (existsSync54(join66(current, ".git")))
134523
135458
  return current;
134524
135459
  const parent = dirname32(current);
134525
135460
  if (parent === current)
@@ -134536,7 +135471,7 @@ function suggestProjectSourceRoot(currentDirectory, homeDirectory = homedir49())
134536
135471
  join66(homeDirectory, "Developer"),
134537
135472
  join66(homeDirectory, "Projects")
134538
135473
  ]) {
134539
- if (existsSync55(candidate))
135474
+ if (existsSync54(candidate))
134540
135475
  return candidate;
134541
135476
  }
134542
135477
  return resolve37(currentDirectory);
@@ -134573,7 +135508,7 @@ async function resolveSetupSourceRoots(context, currentDirectory, sourceRoots, e
134573
135508
  async function runSetupCommand(context, args) {
134574
135509
  const options = parseSetupCommandOptions(args, defaultScoutContextDirectory(context));
134575
135510
  const settings = await readOpenScoutSettings({ currentDirectory: options.currentDirectory });
134576
- const hasSavedSettings = existsSync55(resolveOpenScoutSupportPaths().settingsPath);
135511
+ const hasSavedSettings = existsSync54(resolveOpenScoutSupportPaths().settingsPath);
134577
135512
  options.sourceRoots = await resolveSetupSourceRoots({ ...context, isTty: context.isTty && Boolean(process.stdin.isTTY) }, options.currentDirectory, options.sourceRoots, {
134578
135513
  roots: hasSavedSettings ? settings.discovery.workspaceRoots : [],
134579
135514
  answeredAt: hasSavedSettings ? settings.onboarding.sourceRootsAnsweredAt : null
@@ -134829,7 +135764,7 @@ function readPositiveIntEnv(name2, fallback2) {
134829
135764
  const raw2 = Number.parseInt(process.env[name2] ?? "", 10);
134830
135765
  return Number.isFinite(raw2) && raw2 > 0 ? raw2 : fallback2;
134831
135766
  }
134832
- 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;
135767
+ 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;
134833
135768
  var init_service11 = __esm(() => {
134834
135769
  init_claude_source();
134835
135770
  init_codex_source();
@@ -134838,17 +135773,28 @@ var init_service11 = __esm(() => {
134838
135773
  init_kimi_source();
134839
135774
  init_opencode_source();
134840
135775
  init_pi_source();
134841
- TAIL_POLL_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_POLL_INTERVAL_MS", 500);
134842
- HOT_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_HOT_DISCOVERY_INTERVAL_MS", 30000);
135776
+ TAIL_POLL_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_POLL_INTERVAL_MS", DEFAULT_TAIL_POLL_INTERVAL_MS);
135777
+ HOT_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_HOT_DISCOVERY_INTERVAL_MS", DEFAULT_HOT_DISCOVERY_INTERVAL_MS);
134843
135778
  DISCOVERY_CACHE_MAX_AGE_MS = readPositiveIntEnv("OPENSCOUT_TAIL_DISCOVERY_CACHE_MAX_AGE_MS", HOT_DISCOVERY_INTERVAL_MS);
134844
135779
  SHALLOW_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_SHALLOW_DISCOVERY_INTERVAL_MS", 10 * 60000);
134845
135780
  DEEP_DISCOVERY_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_DEEP_DISCOVERY_INTERVAL_MS", 60 * 60000);
134846
135781
  QUIET_EVENT_COALESCE_WINDOW_MS = readPositiveIntEnv("OPENSCOUT_TAIL_QUIET_EVENT_COALESCE_WINDOW_MS", 5000);
134847
135782
  QUIET_EVENT_COALESCE_MAX_KEYS = readPositiveIntEnv("OPENSCOUT_TAIL_QUIET_EVENT_COALESCE_MAX_KEYS", 2000);
134848
135783
  RECENT_TRANSCRIPT_READ_BYTES = 512 * 1024;
135784
+ WATCHER_READ_BYTES = readPositiveIntEnv("OPENSCOUT_TAIL_WATCHER_READ_BYTES", 512 * 1024);
135785
+ WATCHER_DRAIN_BYTES = Math.max(WATCHER_READ_BYTES, readPositiveIntEnv("OPENSCOUT_TAIL_WATCHER_DRAIN_BYTES", 4 * 1024 * 1024));
135786
+ WATCHER_PUMP_CONCURRENCY = readPositiveIntEnv("OPENSCOUT_TAIL_WATCHER_PUMP_CONCURRENCY", 16);
134849
135787
  SESSION_TRANSCRIPT_READ_BYTES = 8 * 1024 * 1024;
134850
135788
  RECENT_TRANSCRIPT_KIND_SCAN_READ_BYTES = 32 * 1024 * 1024;
134851
135789
  RECENT_TRANSCRIPT_MAX_FILES = readPositiveIntEnv("OPENSCOUT_TAIL_RECENT_TRANSCRIPT_MAX_FILES", 24);
135790
+ INTERNAL_RECONCILE_SESSIONS_PER_SOURCE = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_RECONCILE_SESSIONS_PER_SOURCE", 12);
135791
+ INTERNAL_RECONCILE_LINES_PER_SESSION = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_RECONCILE_LINES_PER_SESSION", 128);
135792
+ INTERNAL_RECONCILE_READ_BYTES = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_RECONCILE_READ_BYTES", 256 * 1024);
135793
+ INTERNAL_HOT_WATCHER_WINDOW_MS = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_HOT_WATCHER_WINDOW_MS", 60000);
135794
+ INTERNAL_IDLE_WATCHER_INTERVAL_MS = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_INTERVAL_MS", 15000);
135795
+ INTERNAL_IDLE_WATCHER_BATCH_SIZE = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_BATCH_SIZE", 32);
135796
+ INTERNAL_ACTIVE_STALE_AFTER_MS = readPositiveIntEnv("OPENSCOUT_TAIL_INTERNAL_ACTIVE_STALE_AFTER_MS", 30 * 60000);
135797
+ INTERNAL_PERSISTED_OBSERVED_SEED_LIMIT = readPositiveIntEnv("OPENSCOUT_TAIL_PERSISTED_OBSERVED_SEED_LIMIT", 4096);
134852
135798
  NATIVE_TAIL_SOURCES = new Set(["grok", "kimi", "opencode", "cursor"]);
134853
135799
  TRANSCRIPT_REPLAY_MEMO_MAX_ENTRIES = readPositiveIntEnv("OPENSCOUT_TAIL_REPLAY_MEMO_MAX_ENTRIES", 256);
134854
135800
  TRANSCRIPT_REPLAY_ACTIVE_GRACE_MS = readPositiveIntEnv("OPENSCOUT_TAIL_REPLAY_MEMO_ACTIVE_GRACE_MS", 2000);
@@ -134857,7 +135803,9 @@ var init_service11 = __esm(() => {
134857
135803
  watchers = new Map;
134858
135804
  perSessionBuffer = new Map;
134859
135805
  subscribers = new Set;
135806
+ internalSubscribers = new Set;
134860
135807
  knownTranscripts = new Map;
135808
+ persistedObservedSessions = new Map;
134861
135809
  quietEventLastSeen = new Map;
134862
135810
  GROK_QUIET_PHASES = new Set([
134863
135811
  "streaming_reasoning",
@@ -135413,7 +136361,7 @@ __export(exports_tui, {
135413
136361
  parseTuiLaunchOptions: () => parseTuiLaunchOptions
135414
136362
  });
135415
136363
  import { spawnSync as spawnSync12 } from "child_process";
135416
- import { accessSync as accessSync5, constants as constants5, existsSync as existsSync56 } from "fs";
136364
+ import { accessSync as accessSync5, constants as constants5, existsSync as existsSync55 } from "fs";
135417
136365
  import { dirname as dirname33, join as join67, resolve as resolve38 } from "path";
135418
136366
  import { fileURLToPath as fileURLToPath13 } from "url";
135419
136367
  function renderTuiCommandHelp() {
@@ -135542,7 +136490,7 @@ function findScoutTuiCheckout(cwd, env) {
135542
136490
  for (const start of starts) {
135543
136491
  let current = resolve38(start);
135544
136492
  while (true) {
135545
- if (existsSync56(join67(current, "crates/scout-tui/Cargo.toml"))) {
136493
+ if (existsSync55(join67(current, "crates/scout-tui/Cargo.toml"))) {
135546
136494
  return current;
135547
136495
  }
135548
136496
  const parent = dirname33(current);
@@ -135596,7 +136544,7 @@ var exports_up = {};
135596
136544
  __export(exports_up, {
135597
136545
  runUpCommand: () => runUpCommand
135598
136546
  });
135599
- import { existsSync as existsSync57 } from "fs";
136547
+ import { existsSync as existsSync56 } from "fs";
135600
136548
  import { resolve as resolve39 } from "path";
135601
136549
  function looksLikePath(value) {
135602
136550
  return value.includes("/") || value.startsWith(".") || value.startsWith("~");
@@ -135705,7 +136653,7 @@ async function runUpCommand(context, args) {
135705
136653
  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()}>]`);
135706
136654
  }
135707
136655
  let projectPath;
135708
- if (looksLikePath(target2) || existsSync57(resolve39(target2))) {
136656
+ if (looksLikePath(target2) || existsSync56(resolve39(target2))) {
135709
136657
  projectPath = resolve39(target2);
135710
136658
  } else {
135711
136659
  const resolved = await resolveLocalAgentByName(target2);
@@ -136115,7 +137063,7 @@ var init_whoami = __esm(() => {
136115
137063
  });
136116
137064
 
136117
137065
  // ../../apps/desktop/src/cli/main.ts
136118
- import { existsSync as existsSync58, statSync as statSync17 } from "fs";
137066
+ import { existsSync as existsSync57, statSync as statSync17 } from "fs";
136119
137067
  import { join as join70 } from "path";
136120
137068
  import { spawnSync as spawnSync13 } from "child_process";
136121
137069
  import { homedir as homedir50 } from "os";
@@ -136826,7 +137774,7 @@ function getScoutBinPath() {
136826
137774
  if (_scoutBinPath)
136827
137775
  return _scoutBinPath;
136828
137776
  _scoutBinPath = join70(homedir50(), ".bun", "bin", "scout");
136829
- if (!existsSync58(_scoutBinPath)) {
137777
+ if (!existsSync57(_scoutBinPath)) {
136830
137778
  _scoutBinPath = spawnSync13("which", ["scout"], { encoding: "utf8" }).stdout.trim();
136831
137779
  }
136832
137780
  return _scoutBinPath;