@openscout/scout 0.2.95 → 0.2.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/bin/scout +26 -10
  2. package/bin/scoutd +0 -0
  3. package/dist/build-manifest.json +3 -3
  4. package/dist/client/assets/{ActivityScreen--eANwuxa.js → ActivityScreen-y-5DLleY.js} +1 -1
  5. package/dist/client/assets/{AddProjectForm-CcD6qvV8.js → AddProjectForm-r_1tVuWk.js} +1 -1
  6. package/dist/client/assets/{AgentAvatar-CYhf6twS.js → AgentAvatar-B7c0egdP.js} +1 -1
  7. package/dist/client/assets/{AgentConfigurationScreen-BTI24HQX.js → AgentConfigurationScreen-BD7Unt2P.js} +1 -1
  8. package/dist/client/assets/{AgentDefinitionsWorkspace-srmN6-vj.js → AgentDefinitionsWorkspace-BbcZc9Xk.js} +1 -1
  9. package/dist/client/assets/{AgentDetailCard-UqPNpcvB.js → AgentDetailCard-B_KLcgsP.js} +1 -1
  10. package/dist/client/assets/{AgentFloorView-XXNgI5Cj.js → AgentFloorView-DMFiHs21.js} +1 -1
  11. package/dist/client/assets/{AgentInfoScreen-CimdOnZ9.js → AgentInfoScreen-DHGLRddI.js} +1 -1
  12. package/dist/client/assets/{AgentLaneCard-Z-aOBR3W.js → AgentLaneCard-ajZvcfdW.js} +1 -1
  13. package/dist/client/assets/{AgentLaneChrome-CyhA1reW.js → AgentLaneChrome-CTp39syR.js} +1 -1
  14. package/dist/client/assets/{AgentLaneDetailSheet-BNzjMjIH.js → AgentLaneDetailSheet-DRc-TpMB.js} +1 -1
  15. package/dist/client/assets/{AgentLaneSummaryResize-Box8-_rS.js → AgentLaneSummaryResize-VMNDo7kD.js} +1 -1
  16. package/dist/client/assets/AgentLanesEmbedScreen-C5k_dDS8.js +1 -0
  17. package/dist/client/assets/AgentLanesPreflight-CFadSaia.js +1 -0
  18. package/dist/client/assets/AgentLanesView-u5vpLwQf.js +2 -0
  19. package/dist/client/assets/{AgentLiveActions-BwuzOE8h.js → AgentLiveActions-CsQA3pu_.js} +1 -1
  20. package/dist/client/assets/AgentMasterScreen-C5WlWNmk.js +1 -0
  21. package/dist/client/assets/{AgentSessions-CsZwoukw.js → AgentSessions-BePq5Lgc.js} +1 -1
  22. package/dist/client/assets/{AgentsConsole-KCao4XmX.js → AgentsConsole-BuiZ_bDo.js} +1 -1
  23. package/dist/client/assets/{AgentsScreen-CtPAmwRX.js → AgentsScreen-CRi-hG2k.js} +1 -1
  24. package/dist/client/assets/AgentsSubnav-DUsya4WK.js +1 -0
  25. package/dist/client/assets/{AstronautSuit-Ckx7lu7G.js → AstronautSuit-Cjjcv6yQ.js} +1 -1
  26. package/dist/client/assets/{AtopView-DYlpgEHf.js → AtopView-PxWQd-sX.js} +1 -1
  27. package/dist/client/assets/{BackToPicker-4JaeRvDm.js → BackToPicker-Cto0Ioz9.js} +1 -1
  28. package/dist/client/assets/{BriefingDetailScreen-D6-xqrpo.js → BriefingDetailScreen-C29smC5d.js} +1 -1
  29. package/dist/client/assets/{BriefingsScreen-DKxSDEmk.js → BriefingsScreen-DNyFBrPn.js} +1 -1
  30. package/dist/client/assets/{BrokerMetadataPanel-BbREmsbK.js → BrokerMetadataPanel-DbaCQlhw.js} +1 -1
  31. package/dist/client/assets/{BrokerScreen-fbPIouMw.js → BrokerScreen-CS0s4Gm4.js} +1 -1
  32. package/dist/client/assets/{CastPicker-BcA2vDDV.js → CastPicker-CTBHjjGw.js} +1 -1
  33. package/dist/client/assets/ChatCollapsedStrip-D4rc7wBQ.js +1 -0
  34. package/dist/client/assets/{CodeDiffPane-Cn8Y9hAt.js → CodeDiffPane-QvxCUzTi.js} +1 -1
  35. package/dist/client/assets/{CodeProjectPicker-nPFktrco.js → CodeProjectPicker-DplkpM7f.js} +1 -1
  36. package/dist/client/assets/{CodeScreen-BlAxtxE5.js → CodeScreen-CrR2JaVj.js} +1 -1
  37. package/dist/client/assets/{ConversationComposer-2HjbGPL9.js → ConversationComposer-H8_hsYSc.js} +1 -1
  38. package/dist/client/assets/{ConversationFeedRows-C6wUqJrx.js → ConversationFeedRows-hlEFlURs.js} +1 -1
  39. package/dist/client/assets/{ConversationHeader-C8vfXaTF.js → ConversationHeader-B0ZBhE_E.js} +1 -1
  40. package/dist/client/assets/{ConversationPanels-CNCIbpqU.js → ConversationPanels-fMo6Uy1v.js} +2 -2
  41. package/dist/client/assets/ConversationScreen-CXAHQwQ3.js +2 -0
  42. package/dist/client/assets/{ConversationStatus-DZuyKsWN.js → ConversationStatus-DI5GCiyW.js} +1 -1
  43. package/dist/client/assets/{CrewAvatar-CTIuxp4G.js → CrewAvatar-CXJHLNVM.js} +1 -1
  44. package/dist/client/assets/CrewWorkspaces-Bz2Dcnt4.js +1 -0
  45. package/dist/client/assets/CrewWorkspaces-Diq11KDq.css +1 -0
  46. package/dist/client/assets/{DataTable-6IDhanr5.js → DataTable-DXIlOlVu.js} +1 -1
  47. package/dist/client/assets/{DispatchAftermath-DC1u9itO.js → DispatchAftermath-CrELgl2e.js} +1 -1
  48. package/dist/client/assets/{DocumentFocusViewer-DZVhS7as.js → DocumentFocusViewer-DFFE85Tq.js} +1 -1
  49. package/dist/client/assets/{EmbeddableSurfacesLab-BKUFJMCV.js → EmbeddableSurfacesLab-DgpewB3y.js} +1 -1
  50. package/dist/client/assets/{EmptyState-CeBl0iGd.js → EmptyState-D9X16fKq.js} +1 -1
  51. package/dist/client/assets/{FileViewerPane-DQlpNt2S.js → FileViewerPane-CY0RN78e.js} +1 -1
  52. package/dist/client/assets/FlightObserveScreen-qyzHzxLa.js +1 -0
  53. package/dist/client/assets/{FollowScreen-BXHOp3gb.js → FollowScreen-wZ-z67mX.js} +1 -1
  54. package/dist/client/assets/{HarnessMark-D5CA5uSp.js → HarnessMark-DAaWPiT_.js} +1 -1
  55. package/dist/client/assets/{HarnessesScreen-DhFXq1qz.js → HarnessesScreen-DbKkQM4o.js} +1 -1
  56. package/dist/client/assets/{HerdrSession-BRhs5LIR.js → HerdrSession-CYKRfBad.js} +1 -1
  57. package/dist/client/assets/{HomeHero-DMABJuze.js → HomeHero-DYyd3cpz.js} +1 -1
  58. package/dist/client/assets/{HostAdvisorView-RAociATV.js → HostAdvisorView-NtM0bsg8.js} +1 -1
  59. package/dist/client/assets/{KnowledgeSearchInspector-CZPyVNSw.js → KnowledgeSearchInspector-CU6BCFcS.js} +1 -1
  60. package/dist/client/assets/{KnowledgeSearchScreen-mGTKIg00.js → KnowledgeSearchScreen-DvbRRPQB.js} +1 -1
  61. package/dist/client/assets/{LaneToolHoverCard-D_DMlBeT.js → LaneToolHoverCard-Bo2Zl-1a.js} +1 -1
  62. package/dist/client/assets/{LaneTraceDetailSheet-BEHg3LmH.js → LaneTraceDetailSheet-C3C8UjXv.js} +1 -1
  63. package/dist/client/assets/{LaneTraceEventFocus-DVimcizS.js → LaneTraceEventFocus-Cek_64zR.js} +1 -1
  64. package/dist/client/assets/{MeshCanvas-BOuZx4AM.js → MeshCanvas-TvsID4fe.js} +1 -1
  65. package/dist/client/assets/{MeshCanvasMinimap-wL-1N94e.js → MeshCanvasMinimap-CxqeVVco.js} +1 -1
  66. package/dist/client/assets/{MeshScreen-DMY9Bmh5.js → MeshScreen-BJ-j9YXv.js} +1 -1
  67. package/dist/client/assets/{MeshTopologyView-DiDcX5f4.js → MeshTopologyView-BjprutE8.js} +2 -2
  68. package/dist/client/assets/MessagesScreen-CyNIZ3-X.js +1 -0
  69. package/dist/client/assets/{MissionContextRail-DrNKXG5u.js → MissionContextRail-Be0uXljQ.js} +1 -1
  70. package/dist/client/assets/{MissionControlView-ByBlHVN8.js → MissionControlView-Bj7cAbZd.js} +1 -1
  71. package/dist/client/assets/{MissionFocusOverlay-D1i6R6Zt.js → MissionFocusOverlay-DvTw6ts5.js} +1 -1
  72. package/dist/client/assets/{MissionLeftAgentRow-DN9NcCML.js → MissionLeftAgentRow-B0QG7Gys.js} +1 -1
  73. package/dist/client/assets/{MissionLogPane-DSeIv3Xo.js → MissionLogPane-EsM_S3ro.js} +1 -1
  74. package/dist/client/assets/{NewAgentModal-V7yK6J6j.js → NewAgentModal-Cqiq1CE1.js} +1 -1
  75. package/dist/client/assets/NewChatComposer-C_1op4dH.js +19 -0
  76. package/dist/client/assets/{NewChatOrigin-DdpzE4kz.js → NewChatOrigin-D7hwp6dm.js} +1 -1
  77. package/dist/client/assets/{ObserveEmbedScreen-l_Q3ts2q.js → ObserveEmbedScreen-CGHI_hb5.js} +1 -1
  78. package/dist/client/assets/{ObserveTextFlow-BSEtYTxU.js → ObserveTextFlow-BcrGQet8.js} +2 -2
  79. package/dist/client/assets/{ObservedTopologyPanel-CdGUXxGR.js → ObservedTopologyPanel-BhVsvwkh.js} +1 -1
  80. package/dist/client/assets/{OpenScoutAppShell-DN8dOSK4.js → OpenScoutAppShell-BtfvMkAp.js} +25 -25
  81. package/dist/client/assets/{OpsAgentsView-ntGr4pz4.js → OpsAgentsView-DBYtmLnh.js} +1 -1
  82. package/dist/client/assets/OpsScreen-CuYVgkC_.js +1 -0
  83. package/dist/client/assets/OpsSubnav-BEU4kKXm.js +1 -0
  84. package/dist/client/assets/ProjectAgentProfile-BhXAZEkW.js +1 -0
  85. package/dist/client/assets/{ProjectAgentProfileBar-BQET-qrY.js → ProjectAgentProfileBar-uJw9Wdfk.js} +1 -1
  86. package/dist/client/assets/{ProjectAgentProfileHero-Ck8mThtx.js → ProjectAgentProfileHero-CIMS4Dy_.js} +1 -1
  87. package/dist/client/assets/ProjectsInbox-JgU0lrY8.js +2 -0
  88. package/dist/client/assets/ProjectsRail-p99BRBOa.js +1 -0
  89. package/dist/client/assets/ProjectsScreen-BB45DU53.js +1 -0
  90. package/dist/client/assets/ProjectsThreadAside-BHDTJWOz.js +1 -0
  91. package/dist/client/assets/{Provider-B2xuo3rf.js → Provider-Bu-C8Hxc.js} +1 -1
  92. package/dist/client/assets/{RailRow-B-GycCBx.js → RailRow-ClMmtaIE.js} +1 -1
  93. package/dist/client/assets/{RawSessionsTable-AQThi6sm.js → RawSessionsTable-BtKdQPiv.js} +1 -1
  94. package/dist/client/assets/RealtimeVoiceScreen-KPHJgO1l.js +1 -0
  95. package/dist/client/assets/{RepoDiffEmbedScreen-DXWoRF_c.js → RepoDiffEmbedScreen-CjZ5ew3y.js} +1 -1
  96. package/dist/client/assets/{RepoDiffPageScreen-D_274ExO.js → RepoDiffPageScreen-CdTFfmbk.js} +1 -1
  97. package/dist/client/assets/{RepoDiffViewer-DbedmHDX.js → RepoDiffViewer-BFperHQg.js} +1 -1
  98. package/dist/client/assets/{RepoDiffViewerLazy-C0--fPtk.js → RepoDiffViewerLazy-BkBmbi1Z.js} +2 -2
  99. package/dist/client/assets/{ReposScreen-D39_YG4H.js → ReposScreen-C8dT4bK0.js} +1 -1
  100. package/dist/client/assets/ScoutDeckSurface-BcJsm2Kq.js +1 -0
  101. package/dist/client/assets/{ScoutSurface-UsRy0t-I.js → ScoutSurface-DmzEtrx8.js} +1 -1
  102. package/dist/client/assets/{ScoutbotFxLab-Fmdfboxs.js → ScoutbotFxLab-DU15Plja.js} +1 -1
  103. package/dist/client/assets/{SecondaryNav-o2RL9H8H.js → SecondaryNav-BrXMtvKE.js} +1 -1
  104. package/dist/client/assets/SessionEmbedScreen-Bt4flmAx.js +1 -0
  105. package/dist/client/assets/{SessionObserve-Dsd0j05N.js → SessionObserve-BCehTlO8.js} +1 -1
  106. package/dist/client/assets/{SessionObserveEvidence-DopQYDkD.js → SessionObserveEvidence-Bb6U-dem.js} +1 -1
  107. package/dist/client/assets/SessionRefScreen-C8BiRVPx.js +1 -0
  108. package/dist/client/assets/SessionsScreen-BNvjUflN.js +1 -0
  109. package/dist/client/assets/{SettingsDrawer-_AwuopMS.js → SettingsDrawer-BieUmb8g.js} +1 -1
  110. package/dist/client/assets/{SettingsScreen-Cw4DDfJP.js → SettingsScreen-3JvX1WFL.js} +1 -1
  111. package/dist/client/assets/{ShikiPane-BfHc_3pM.js → ShikiPane-DPWsVNtn.js} +1 -1
  112. package/dist/client/assets/{SlidePanel-C-q7XwTa.js → SlidePanel-BMRIR6bH.js} +1 -1
  113. package/dist/client/assets/{SpriteAvatar-Bs68ZENy.js → SpriteAvatar-56DTXxSV.js} +1 -1
  114. package/dist/client/assets/{StatusPill-ZZQwxB1a.js → StatusPill-BcuYUgsD.js} +1 -1
  115. package/dist/client/assets/{TailView-Q4FVMsUH.js → TailView-BDy8c2VB.js} +1 -1
  116. package/dist/client/assets/{Terminal-D7zqHwHV.js → Terminal-BlUQY9_Z.js} +1 -1
  117. package/dist/client/assets/{TerminalEmbedScreen-CXIJ7qv6.js → TerminalEmbedScreen-DTjxktyr.js} +1 -1
  118. package/dist/client/assets/{TerminalHeaderMount-BnBLOPbb.js → TerminalHeaderMount-Cy-uxkey.js} +1 -1
  119. package/dist/client/assets/{TextDocumentSurface-CKQP-cBT.js → TextDocumentSurface-BeSwe3Yl.js} +1 -1
  120. package/dist/client/assets/{TmuxPeek-V9dGESfO.js → TmuxPeek-B4no_aGI.js} +1 -1
  121. package/dist/client/assets/{VoicePermissionsPanel-k7sbLXx2.js → VoicePermissionsPanel-Bd16scwz.js} +1 -1
  122. package/dist/client/assets/{WorkDetailScreen-60p9litn.js → WorkDetailScreen-DIFQufak.js} +1 -1
  123. package/dist/client/assets/{WorkFilesViewer-BjJS7GyM.js → WorkFilesViewer-CT5XEFGf.js} +1 -1
  124. package/dist/client/assets/{activity-Bj3Hnbj3.js → activity-CqZafWfx.js} +1 -1
  125. package/dist/client/assets/{addon-fit-BL-NTWRU.js → addon-fit-CM6F_JRD.js} +1 -1
  126. package/dist/client/assets/{addon-webgl-CIMgmNjg.js → addon-webgl-DRTiePFC.js} +1 -1
  127. package/dist/client/assets/{agent-autocomplete-DXlvdc4b.js → agent-autocomplete-DXUBnlkN.js} +1 -1
  128. package/dist/client/assets/{agent-essentials-BOMHR0_U.js → agent-essentials-vytdybaX.js} +1 -1
  129. package/dist/client/assets/{agent-lane-card-model-CTLxf3R4.js → agent-lane-card-model-CNZMPkhc.js} +1 -1
  130. package/dist/client/assets/{agent-lanes-model-AfrjRNK5.js → agent-lanes-model-DXqjs0OK.js} +2 -2
  131. package/dist/client/assets/agent-master-I1W-5d-x.css +1 -0
  132. package/dist/client/assets/agent-master-model-DAZL92oe.js +1 -0
  133. package/dist/client/assets/{agent-specialization-BIPIBW8u.js → agent-specialization-BtxLN4UR.js} +1 -1
  134. package/dist/client/assets/agents-project-DgbqDc65.css +1 -0
  135. package/dist/client/assets/{arc.es-IR5r6sj_.js → arc.es-Bh84oHJ7.js} +1 -1
  136. package/dist/client/assets/{arrow-down-BydciYFZ.js → arrow-down-DvTsLokL.js} +1 -1
  137. package/dist/client/assets/{arrow-left-CghA0kTw.js → arrow-left-DHfdXJVZ.js} +1 -1
  138. package/dist/client/assets/{arrow-right-iJFv3jNh.js → arrow-right-BBsrogWf.js} +1 -1
  139. package/dist/client/assets/{arrow-up-right-CCfZXPqD.js → arrow-up-right-CY0Vw_67.js} +1 -1
  140. package/dist/client/assets/{bot-B5sub1-i.js → bot-DzkOUFBb.js} +1 -1
  141. package/dist/client/assets/{check-C18YQC90.js → check-DbIkldnK.js} +1 -1
  142. package/dist/client/assets/{chevron-down-Mp9mu_yd.js → chevron-down-C475vltS.js} +1 -1
  143. package/dist/client/assets/{chevron-right-CzH-TaSF.js → chevron-right-CnZRSd-V.js} +1 -1
  144. package/dist/client/assets/{chunk-HRAB3GC3-swV8PA2k.js → chunk-HRAB3GC3-DLNT8aOo.js} +1 -1
  145. package/dist/client/assets/{chunk-JTPDIGGW-BKXqcLKw.js → chunk-JTPDIGGW-Ci5j4vzV.js} +3 -3
  146. package/dist/client/assets/{chunk-T65CQEWV-vYbNJ8E6.js → chunk-T65CQEWV-BtNvicWs.js} +1 -1
  147. package/dist/client/assets/{chunk-YOZR3KYS-U65yUmqt.js → chunk-YOZR3KYS-DN5EJulq.js} +1 -1
  148. package/dist/client/assets/{clock-B4PTFi5m.js → clock-DVvENUU5.js} +1 -1
  149. package/dist/client/assets/{code-xml-Jtxsp5d1.js → code-xml-CvKBY3KA.js} +1 -1
  150. package/dist/client/assets/{compass-0_54-vz3.js → compass-BcRI_YXo.js} +1 -1
  151. package/dist/client/assets/content--T3p6akp.js +1 -0
  152. package/dist/client/assets/content-4nFMLuzF.js +1 -0
  153. package/dist/client/assets/content-BJ4nHvcu.js +1 -0
  154. package/dist/client/assets/{content-DMbawvqU.js → content-BNyAjpUl.js} +1 -1
  155. package/dist/client/assets/content-BmGp9wNl.js +1 -0
  156. package/dist/client/assets/content-CBWhwaRo.js +1 -0
  157. package/dist/client/assets/content-CQlykFbe.js +1 -0
  158. package/dist/client/assets/content-CevL0DgF.js +1 -0
  159. package/dist/client/assets/content-Ch8pf2bj.js +1 -0
  160. package/dist/client/assets/content-Cpvdcm_l.js +1 -0
  161. package/dist/client/assets/content-DfS3vgAu.js +1 -0
  162. package/dist/client/assets/content-DpQdNePJ.js +1 -0
  163. package/dist/client/assets/content-Dqpibejl.js +1 -0
  164. package/dist/client/assets/content-TV0nd6-r.js +1 -0
  165. package/dist/client/assets/{content-CHghn-rT.js → content-odbb5H4U.js} +1 -1
  166. package/dist/client/assets/content-uXHLPHe0.js +1 -0
  167. package/dist/client/assets/{conversation-icons-BV10B2Me.js → conversation-icons-CBMKBGHd.js} +1 -1
  168. package/dist/client/assets/{conversation-model-CwF6Y9gx.js → conversation-model-QsJVfSeG.js} +1 -1
  169. package/dist/client/assets/conversation-motion-features-BFLHX2eg.js +1 -0
  170. package/dist/client/assets/{copy-Dtpx4FOE.js → copy-Cj-owMb-.js} +1 -1
  171. package/dist/client/assets/{cpu-BjVis9mc.js → cpu-DuD3RFX5.js} +1 -1
  172. package/dist/client/assets/{createLucideIcon-DXpqZP7I.js → createLucideIcon-DthwWQIg.js} +1 -1
  173. package/dist/client/assets/{ctx-panel-D_HJ3nhb.css → ctx-panel-F05jXK4Q.css} +1 -1
  174. package/dist/client/assets/discover-B3uQlh5L.js +2 -0
  175. package/dist/client/assets/embed-entry-DkRcHKTh.js +2 -0
  176. package/dist/client/assets/{external-link-Ckiyzjwo.js → external-link-C_nxk0MJ.js} +1 -1
  177. package/dist/client/assets/{eye-CpfyvdLq.js → eye-FnaaPfja.js} +1 -1
  178. package/dist/client/assets/{file-code-corner-7mo7EviR.js → file-code-corner-DDPq4Cur.js} +1 -1
  179. package/dist/client/assets/{file-text-KijGBnME.js → file-text--hfhwZH1.js} +1 -1
  180. package/dist/client/assets/{gauge-y-Xg8Mrv.js → gauge-BVEknshP.js} +1 -1
  181. package/dist/client/assets/{gestures-Bq9XnSqY.js → gestures-3gw5VgUS.js} +1 -1
  182. package/dist/client/assets/{git-branch-BQ_x8KZL.js → git-branch-DJyvr0k-.js} +1 -1
  183. package/dist/client/assets/{home-moving-signal-DtRpiNa0.js → home-moving-signal-BPqBoNSm.js} +1 -1
  184. package/dist/client/assets/{home-now-card-DFb4GOgM.js → home-now-card-UspVW2-2.js} +1 -1
  185. package/dist/client/assets/{home-now-card-nano-D--m9jpq.js → home-now-card-nano-G8kMB32m.js} +1 -1
  186. package/dist/client/assets/{hooks-9d1SoaQA.js → hooks-BMsQzo9j.js} +1 -1
  187. package/dist/client/assets/{index-CZ-05hKz.js → index-BI9AljSD.js} +5 -5
  188. package/dist/client/assets/index-C-v0Z6ZS.js +1 -0
  189. package/dist/client/assets/index-D9DzUORB.js +1 -0
  190. package/dist/client/assets/index-DfsTyYDx.js +1 -0
  191. package/dist/client/assets/{inspector-panel-C-EhEDYy.js → inspector-panel-CFSArQ8Y.js} +1 -1
  192. package/dist/client/assets/{keyboard-nav-DFhAJkW3.js → keyboard-nav-e6yGeorb.js} +1 -1
  193. package/dist/client/assets/{lane-roster-store-gUBsydNf.js → lane-roster-store-Q7pcblY5.js} +1 -1
  194. package/dist/client/assets/{layers-CGou-LwV.js → layers-Dz2dKOnU.js} +1 -1
  195. package/dist/client/assets/{layout-grid-BrZt31QH.js → layout-grid-Bvs5fdB4.js} +1 -1
  196. package/dist/client/assets/{left-CwHZCGO3.js → left-B4Ak-Ibt.js} +1 -1
  197. package/dist/client/assets/{left-DA0amyfu.js → left-BMb493FZ.js} +1 -1
  198. package/dist/client/assets/{left-CwLs0okF.js → left-BjFLvVOC.js} +1 -1
  199. package/dist/client/assets/{left-B_o51JV6.js → left-CsXQ8Q3G.js} +1 -1
  200. package/dist/client/assets/left-FNH7faCI.js +1 -0
  201. package/dist/client/assets/left-POkqE8gI.js +2 -0
  202. package/dist/client/assets/left-UulOTcWh.js +1 -0
  203. package/dist/client/assets/{left-advisor-DU3s01gk.js → left-advisor-CJkQS2d6.js} +1 -1
  204. package/dist/client/assets/left-agents-CFMxyEjT.js +1 -0
  205. package/dist/client/assets/{left-default-uvuvDmt0.js → left-default-rnX5HANc.js} +1 -1
  206. package/dist/client/assets/{left-lanes-DnNLgec8.js → left-lanes-BpLXT_qH.js} +1 -1
  207. package/dist/client/assets/{left-mission-DN3-NLPC.js → left-mission-B0bhseWw.js} +1 -1
  208. package/dist/client/assets/library-D1iXfda2.js +1 -0
  209. package/dist/client/assets/{loader-circle-BiohlpM3.js → loader-circle-B2W9DPG5.js} +1 -1
  210. package/dist/client/assets/{local-agents-wUIaBcEr.js → local-agents-DKr-gIDC.js} +1 -1
  211. package/dist/client/assets/{mentions-CcPFMbmZ.js → mentions-DuLQnEcn.js} +1 -1
  212. package/dist/client/assets/{mesh-buckets-ByyFXdKT.js → mesh-buckets-6y3ZIibI.js} +1 -1
  213. package/dist/client/assets/{mesh-ops-CvGyjK84.js → mesh-ops-CRR-KnCG.js} +1 -1
  214. package/dist/client/assets/{mesh-roster-n2nDpEmA.js → mesh-roster-Bc7T4b7z.js} +1 -1
  215. package/dist/client/assets/{mesh-view-store-BpxGm71b.js → mesh-view-store-CiET-yN8.js} +1 -1
  216. package/dist/client/assets/{message-circle-B2ddzBrX.js → message-circle-B2DoYKDO.js} +1 -1
  217. package/dist/client/assets/{message-markup-RkfmDfME.js → message-markup-Cc_O-WdA.js} +1 -1
  218. package/dist/client/assets/{message-square-D9YYWE0x.js → message-square-O-Hu9Mk2.js} +1 -1
  219. package/dist/client/assets/{message-visibility-DwxgcgYK.js → message-visibility-CGOmSSKs.js} +1 -1
  220. package/dist/client/assets/{mission-control-store-Bf4b54MV.js → mission-control-store-DDAgWKUl.js} +1 -1
  221. package/dist/client/assets/{observe-CJRIvOCn.js → observe-Da2mRAsc.js} +1 -1
  222. package/dist/client/assets/{page-status-BptpTdQ-.js → page-status-Crtgjbq3.js} +1 -1
  223. package/dist/client/assets/{paperclip-B1UNGl5d.js → paperclip-BudMKSyr.js} +1 -1
  224. package/dist/client/assets/{persistent-state-YI-WHSCF.js → persistent-state-xJz-qAFc.js} +1 -1
  225. package/dist/client/assets/{plus-CuU_X0-8.js → plus-BJ1If8vj.js} +1 -1
  226. package/dist/client/assets/{profile-rY8jO38P.js → profile-B3qDSbrH.js} +1 -1
  227. package/dist/client/assets/projects-inbox-CLek3QaG.js +1 -0
  228. package/dist/client/assets/projects-inbox-DRBl2N85.css +1 -0
  229. package/dist/client/assets/{proxy-DnLpZIi9.js → proxy-1BABCaDb.js} +1 -1
  230. package/dist/client/assets/{radio-Dm1ItnA4.js → radio-B5OpHrrK.js} +1 -1
  231. package/dist/client/assets/{refresh-cw-BRn9Oxxl.js → refresh-cw-DmNAhaH0.js} +1 -1
  232. package/dist/client/assets/resolve-panes-DXw-cHaj.js +1 -0
  233. package/dist/client/assets/resolve-sidebar-context-CDdbRDEa.js +1 -0
  234. package/dist/client/assets/{right-BJltPRnd.js → right-BgJ4mQLf.js} +1 -1
  235. package/dist/client/assets/{right-DtsRNX5b.js → right-Bk6h7o-Z.js} +1 -1
  236. package/dist/client/assets/{right-C1yumXNx.js → right-BrICCmEt.js} +1 -1
  237. package/dist/client/assets/{right-BbyYdzHn.js → right-C1QIbqFZ.js} +1 -1
  238. package/dist/client/assets/{right-Shpw_xDy.js → right-CfliKiHY.js} +1 -1
  239. package/dist/client/assets/{right-D3aTKf1F.js → right-CtOLXB9s.js} +1 -1
  240. package/dist/client/assets/{right-CJnB_Bg7.js → right-D8id_DWQ.js} +1 -1
  241. package/dist/client/assets/{right-CPP7GyOm.js → right-DJ34yMAi.js} +1 -1
  242. package/dist/client/assets/{right-DI1B79nR.js → right-DiFmtw6d.js} +1 -1
  243. package/dist/client/assets/{right-Beh3nCUB.js → right-Lx79pxpG.js} +1 -1
  244. package/dist/client/assets/{rocket-DOwXHKWA.js → rocket-D3-TUJ2m.js} +1 -1
  245. package/dist/client/assets/router-DTtux5sP.js +1 -0
  246. package/dist/client/assets/{scope-views-A4_vP97W.js → scope-views-CPM0sBw_.js} +1 -1
  247. package/dist/client/assets/{scout-voice-BhPzrc6P.js → scout-voice-BZKsdquE.js} +1 -1
  248. package/dist/client/assets/{scoutbot-markdown-jh7tzFB1.js → scoutbot-markdown-CQ7Y1McC.js} +1 -1
  249. package/dist/client/assets/{scroll-text-DO0GQzvN.js → scroll-text-kPhYbh-M.js} +1 -1
  250. package/dist/client/assets/{search-D1tJSAWe.js → search-DmFM_9jm.js} +1 -1
  251. package/dist/client/assets/{secondaryNavConfig-B9IF8wXl.js → secondaryNavConfig-C9bQ9_IA.js} +1 -1
  252. package/dist/client/assets/{selection-bridge-i2S9zpf8.js → selection-bridge-Dy2mtMyi.js} +1 -1
  253. package/dist/client/assets/{send-horizontal-CgLppsIC.js → send-horizontal-qyjNgEaV.js} +1 -1
  254. package/dist/client/assets/{server-Dymv5Emh.js → server-nWqndnxu.js} +1 -1
  255. package/dist/client/assets/{session-catalog-DNaOEzoQ.js → session-catalog-BkmktBHI.js} +1 -1
  256. package/dist/client/assets/session-ref-lookup-state-SATH_jAP.js +1 -0
  257. package/dist/client/assets/{session-table-PTQVoTyc.js → session-table-DKSF0mrS.js} +1 -1
  258. package/dist/client/assets/{settings-2fC_G9nX.js → settings-C6h9y5XX.js} +1 -1
  259. package/dist/client/assets/{sliders-horizontal-BPSMHhbm.js → sliders-horizontal-BhHKxsCO.js} +1 -1
  260. package/dist/client/assets/{sparkles-Dy8p5xN8.js → sparkles-Cf8gin88.js} +1 -1
  261. package/dist/client/assets/{sse-CiOSzE5n.js → sse-DDHEHUgz.js} +1 -1
  262. package/dist/client/assets/{tail-events-BMQmSx1n.js → tail-events-BdUrO0kb.js} +1 -1
  263. package/dist/client/assets/{terminal-yN9xN-h9.js → terminal-C3YRPfyz.js} +1 -1
  264. package/dist/client/assets/{terminal-relay-9lhzEp2V.js → terminal-relay-BFhdIstF.js} +1 -1
  265. package/dist/client/assets/{terminal-takeover-DANQfTcy.js → terminal-takeover-B6H4llRR.js} +1 -1
  266. package/dist/client/assets/{text-rLpBg4_z.css → text-BYAFNLNq.css} +1 -1
  267. package/dist/client/assets/{text-CnZ0e1-V.js → text-Faa3ZStB.js} +1 -1
  268. package/dist/client/assets/{use-conversation-list-D9BNLfnW.js → use-conversation-list-DO5J8T1H.js} +1 -1
  269. package/dist/client/assets/use-fleet-active-asks-BtgNnRgK.js +1 -0
  270. package/dist/client/assets/{use-mesh-ops-items-BIvIHlqe.js → use-mesh-ops-items-N1HHhChY.js} +1 -1
  271. package/dist/client/assets/{use-reduced-motion-CKkhq9c5.js → use-reduced-motion-8WuxLa5l.js} +1 -1
  272. package/dist/client/assets/{use-scout-dev-flags-B2KCzPxn.js → use-scout-dev-flags-B31qTy6m.js} +1 -1
  273. package/dist/client/assets/{useAgentHoverCard-CHBsxcbp.js → useAgentHoverCard-Bkv-Uz_V.js} +1 -1
  274. package/dist/client/assets/useContentSecondaryNav-Bpkc8ayb.js +1 -0
  275. package/dist/client/assets/{usePierreRuntime-DUuXH5yp.js → usePierreRuntime-BfZHKAkI.js} +1 -1
  276. package/dist/client/assets/{wrench-YcXMlsh2.js → wrench-DkxnZRlR.js} +1 -1
  277. package/dist/client/assets/{x-e3h2hDsp.js → x-DfRJbqrh.js} +1 -1
  278. package/dist/client/assets/{xterm-Byp1Hep_.js → xterm-CJw8Epxv.js} +1 -1
  279. package/dist/client/assets/{zap-nMcjPoWB.js → zap-CIo8msJB.js} +1 -1
  280. package/dist/client/index.html +1 -1
  281. package/dist/drizzle/0009_old_korg.sql +72 -0
  282. package/dist/drizzle/0010_sparkling_colonel_america.sql +2 -0
  283. package/dist/drizzle/0011_funny_onslaught.sql +4 -0
  284. package/dist/drizzle/0012_high_blazing_skull.sql +16 -0
  285. package/dist/drizzle/0013_perfect_juggernaut.sql +1 -0
  286. package/dist/drizzle/0014_legal_meggan.sql +6 -0
  287. package/dist/drizzle/0015_lame_marvel_boy.sql +12 -0
  288. package/dist/drizzle/0016_early_goliath.sql +6 -0
  289. package/dist/drizzle/meta/0009_snapshot.json +6262 -0
  290. package/dist/drizzle/meta/0010_snapshot.json +6286 -0
  291. package/dist/drizzle/meta/0011_snapshot.json +6337 -0
  292. package/dist/drizzle/meta/0012_snapshot.json +6458 -0
  293. package/dist/drizzle/meta/0013_snapshot.json +6465 -0
  294. package/dist/drizzle/meta/0014_snapshot.json +6503 -0
  295. package/dist/drizzle/meta/0015_snapshot.json +6596 -0
  296. package/dist/drizzle/meta/0016_snapshot.json +6611 -0
  297. package/dist/drizzle/meta/_journal.json +56 -0
  298. package/dist/main.mjs +1355 -365
  299. package/dist/node/main.mjs +314 -12
  300. package/dist/pairing-runtime-controller.mjs +616 -108
  301. package/dist/runtime/base-daemon.mjs +30 -3
  302. package/dist/runtime/broker-daemon.mjs +4822 -532
  303. package/dist/runtime/broker-process-manager.mjs +23 -2
  304. package/dist/runtime/mesh-discover.mjs +23 -2
  305. package/dist/scout-control-plane-web.mjs +1922 -478
  306. package/package.json +2 -2
  307. package/dist/client/assets/AgentLanesEmbedScreen-BmLnN4on.js +0 -1
  308. package/dist/client/assets/AgentLanesPreflight-5B6sXvRB.js +0 -1
  309. package/dist/client/assets/AgentLanesView-DHyzVBk6.js +0 -2
  310. package/dist/client/assets/AgentsSubnav-CDMjKeYC.js +0 -1
  311. package/dist/client/assets/ChatCollapsedStrip-Ycm0ecaX.js +0 -1
  312. package/dist/client/assets/ConversationScreen-BNULZAp2.js +0 -2
  313. package/dist/client/assets/CrewWorkspaces-CJhBKVoZ.css +0 -1
  314. package/dist/client/assets/CrewWorkspaces-CkPYIA46.js +0 -1
  315. package/dist/client/assets/FlightObserveScreen-CDWGSKZg.js +0 -1
  316. package/dist/client/assets/MessagesScreen-Eulveud3.js +0 -1
  317. package/dist/client/assets/NewChatComposer-hlWT-nbR.js +0 -19
  318. package/dist/client/assets/OpsScreen-Ppen_Lmj.js +0 -1
  319. package/dist/client/assets/OpsSubnav-GAKsoSor.js +0 -1
  320. package/dist/client/assets/ProjectAgentProfile-BLlp2e6t.js +0 -1
  321. package/dist/client/assets/ProjectsInbox-Dlt8OQ8O.js +0 -2
  322. package/dist/client/assets/ProjectsRail-DCfNMmnW.js +0 -1
  323. package/dist/client/assets/ProjectsScreen-BP-hmI1m.js +0 -1
  324. package/dist/client/assets/ProjectsThreadAside-CWpMjGIH.js +0 -1
  325. package/dist/client/assets/RealtimeVoiceScreen-D7zZHK_t.js +0 -1
  326. package/dist/client/assets/ScoutDeckSurface-CKT3qdqY.js +0 -1
  327. package/dist/client/assets/SessionEmbedScreen-RD5XOx7R.js +0 -1
  328. package/dist/client/assets/SessionRefScreen-B_cTv47v.js +0 -1
  329. package/dist/client/assets/SessionsScreen-Bq0Z77On.js +0 -1
  330. package/dist/client/assets/agents-project-COzHxZAF.css +0 -1
  331. package/dist/client/assets/archive-BjcGiAy8.js +0 -1
  332. package/dist/client/assets/content-6RvWTrtk.js +0 -1
  333. package/dist/client/assets/content-BCgwMUqz.js +0 -1
  334. package/dist/client/assets/content-Bf2VLhx0.js +0 -1
  335. package/dist/client/assets/content-BrU_38-m.js +0 -1
  336. package/dist/client/assets/content-BvvJN8rN.js +0 -1
  337. package/dist/client/assets/content-CAeEqYPw.js +0 -1
  338. package/dist/client/assets/content-CI_Z9DXk.js +0 -1
  339. package/dist/client/assets/content-CbyKONhW.js +0 -1
  340. package/dist/client/assets/content-Czogvp43.js +0 -1
  341. package/dist/client/assets/content-DFk0zNzX.js +0 -1
  342. package/dist/client/assets/content-DHe4DgkA.js +0 -1
  343. package/dist/client/assets/content-DTz7cKjo.js +0 -1
  344. package/dist/client/assets/content-E3kudY-W.js +0 -1
  345. package/dist/client/assets/content-LpO0aO_U.js +0 -1
  346. package/dist/client/assets/conversation-motion-features-BKkCRaWR.js +0 -1
  347. package/dist/client/assets/discover-CHbrEItO.js +0 -2
  348. package/dist/client/assets/embed-entry-vil28MyU.js +0 -2
  349. package/dist/client/assets/index-BVlMw8x_.js +0 -1
  350. package/dist/client/assets/index-Cn0GR-q1.js +0 -1
  351. package/dist/client/assets/index-DosI4AG9.js +0 -1
  352. package/dist/client/assets/left-BpgWnaah.js +0 -1
  353. package/dist/client/assets/left-Cx0kv6pe.js +0 -1
  354. package/dist/client/assets/left-agents-qL5PpUY-.js +0 -1
  355. package/dist/client/assets/left-z-6CkuBe.js +0 -2
  356. package/dist/client/assets/library-DIo5NiGP.js +0 -1
  357. package/dist/client/assets/projects-inbox-BBs1hG13.css +0 -1
  358. package/dist/client/assets/projects-inbox-D-zAb9Gp.js +0 -1
  359. package/dist/client/assets/resolve-panes-DO6LkwhQ.js +0 -1
  360. package/dist/client/assets/resolve-sidebar-context-CIF288wY.js +0 -1
  361. package/dist/client/assets/router-DJj2WbDg.js +0 -1
  362. package/dist/client/assets/use-fleet-active-asks-BeAdcl09.js +0 -1
  363. package/dist/client/assets/useContentSecondaryNav-D0mUhxTF.js +0 -1
@@ -5539,7 +5539,7 @@ var init_sqlite_core = __esm(() => {
5539
5539
  });
5540
5540
 
5541
5541
  // packages/runtime/src/drizzle-schema.ts
5542
- 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;
5542
+ 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;
5543
5543
  var init_drizzle_schema = __esm(() => {
5544
5544
  init_drizzle_orm();
5545
5545
  init_sqlite_core();
@@ -5611,7 +5611,10 @@ var init_drizzle_schema = __esm(() => {
5611
5611
  advertiseScope: text("advertise_scope").notNull(),
5612
5612
  ownerId: text("owner_id"),
5613
5613
  metadataJson: text("metadata_json")
5614
- });
5614
+ }, (table3) => [
5615
+ index("idx_agents_home_node_id").on(table3.homeNodeId),
5616
+ index("idx_agents_authority_node_id").on(table3.authorityNodeId)
5617
+ ]);
5615
5618
  agentEndpointsTable = sqliteTable("agent_endpoints", {
5616
5619
  id: text("id").primaryKey(),
5617
5620
  agentId: text("agent_id").notNull().references(() => agentsTable.id, { onDelete: "cascade" }),
@@ -5627,7 +5630,14 @@ var init_drizzle_schema = __esm(() => {
5627
5630
  metadataJson: text("metadata_json"),
5628
5631
  updatedAt: integer("updated_at").notNull().default(epochMsNow)
5629
5632
  }, (table3) => [
5630
- index("idx_agent_endpoints_agent_updated_at").on(table3.agentId, desc(table3.updatedAt))
5633
+ index("idx_agent_endpoints_agent_updated_at").on(table3.agentId, desc(table3.updatedAt)),
5634
+ index("idx_agent_endpoints_roster_recency").on(desc(sql`CASE
5635
+ WHEN ${table3.updatedAt} IS NULL THEN NULL
5636
+ WHEN CAST(${table3.updatedAt} AS REAL) < 1000000000000
5637
+ THEN CAST(CAST(${table3.updatedAt} AS REAL) * 1000 AS INTEGER)
5638
+ ELSE CAST(${table3.updatedAt} AS INTEGER)
5639
+ END`), table3.agentId),
5640
+ index("idx_agent_endpoints_node_id").on(table3.nodeId)
5631
5641
  ]);
5632
5642
  runtimeSessionsTable = sqliteTable("runtime_sessions", {
5633
5643
  id: text("id").primaryKey(),
@@ -5651,6 +5661,7 @@ var init_drizzle_schema = __esm(() => {
5651
5661
  }, (table3) => [
5652
5662
  index("idx_runtime_sessions_agent_last_seen").on(table3.agentId, desc(table3.lastSeenAt)),
5653
5663
  index("idx_runtime_sessions_endpoint_last_seen").on(table3.endpointId, desc(table3.lastSeenAt)),
5664
+ index("idx_runtime_sessions_node_id").on(table3.nodeId),
5654
5665
  index("idx_runtime_sessions_external").on(table3.externalSessionId),
5655
5666
  index("idx_runtime_sessions_expires").on(table3.expiresAt).where(sql`expires_at IS NOT NULL`)
5656
5667
  ]);
@@ -5670,6 +5681,8 @@ var init_drizzle_schema = __esm(() => {
5670
5681
  primaryKey({ columns: [table3.alias, table3.sessionId] }),
5671
5682
  index("idx_runtime_session_aliases_alias").on(table3.alias, desc(table3.lastSeenAt)),
5672
5683
  index("idx_runtime_session_aliases_session").on(table3.sessionId),
5684
+ index("idx_runtime_session_aliases_endpoint").on(table3.endpointId),
5685
+ index("idx_runtime_session_aliases_node_id").on(table3.nodeId),
5673
5686
  index("idx_runtime_session_aliases_expires").on(table3.expiresAt).where(sql`expires_at IS NOT NULL`)
5674
5687
  ]);
5675
5688
  routeAliasBindingsTable = sqliteTable("route_alias_bindings", {
@@ -5749,7 +5762,8 @@ var init_drizzle_schema = __esm(() => {
5749
5762
  actorId: text("actor_id").notNull().references(() => actorsTable.id, { onDelete: "cascade" }),
5750
5763
  role: text("role")
5751
5764
  }, (table3) => [
5752
- primaryKey({ columns: [table3.conversationId, table3.actorId] })
5765
+ primaryKey({ columns: [table3.conversationId, table3.actorId] }),
5766
+ index("idx_conversation_members_actor").on(table3.actorId, table3.conversationId)
5753
5767
  ]);
5754
5768
  messagesTable = sqliteTable("messages", {
5755
5769
  id: text("id").primaryKey(),
@@ -5769,7 +5783,8 @@ var init_drizzle_schema = __esm(() => {
5769
5783
  }, (table3) => [
5770
5784
  index("idx_messages_conversation_created_at").on(table3.conversationId, table3.createdAt),
5771
5785
  index("idx_messages_created_at").on(desc(table3.createdAt)),
5772
- index("idx_messages_actor_created_at").on(table3.actorId, desc(table3.createdAt))
5786
+ index("idx_messages_actor_created_at").on(table3.actorId, desc(table3.createdAt)),
5787
+ index("idx_messages_origin_node_id").on(table3.originNodeId)
5773
5788
  ]);
5774
5789
  messageMentionsTable = sqliteTable("message_mentions", {
5775
5790
  messageId: text("message_id").notNull().references(() => messagesTable.id, { onDelete: "cascade" }),
@@ -5798,7 +5813,102 @@ var init_drizzle_schema = __esm(() => {
5798
5813
  metadataJson: text("metadata_json")
5799
5814
  }, (table3) => [
5800
5815
  primaryKey({ columns: [table3.conversationId, table3.actorId] }),
5801
- index("idx_read_cursors_conversation_updated_at").on(table3.conversationId, desc(table3.updatedAt))
5816
+ index("idx_read_cursors_conversation_updated_at").on(table3.conversationId, desc(table3.updatedAt)),
5817
+ index("idx_read_cursors_reader_node_id").on(table3.readerNodeId)
5818
+ ]);
5819
+ brokerJournalProjectionCheckpointsTable = sqliteTable("broker_journal_projection_checkpoints", {
5820
+ projectionId: text("projection_id").primaryKey(),
5821
+ projectionVersion: integer("projection_version").notNull(),
5822
+ barrierId: text("barrier_id").notNull(),
5823
+ updatedAt: integer("updated_at").notNull()
5824
+ });
5825
+ conversationProjectionMetaTable = sqliteTable("conversation_projection_meta", {
5826
+ singleton: integer("singleton").primaryKey(),
5827
+ projectionId: text("projection_id").notNull(),
5828
+ projectionVersion: integer("projection_version").notNull(),
5829
+ headSeq: integer("head_seq").notNull(),
5830
+ minReplayableSeq: integer("min_replayable_seq").notNull(),
5831
+ updatedAt: integer("updated_at").notNull()
5832
+ }, (table3) => [
5833
+ check("conversation_projection_meta_singleton_check", sql`${table3.singleton} = 1`)
5834
+ ]);
5835
+ conversationProjectionItemsTable = sqliteTable("conversation_projection_items", {
5836
+ feedId: text("feed_id").primaryKey(),
5837
+ entityKind: text("entity_kind").notNull(),
5838
+ kind: text("kind").notNull(),
5839
+ conversationId: text("conversation_id"),
5840
+ runtimeSessionId: text("runtime_session_id"),
5841
+ source: text("source"),
5842
+ sourceSessionId: text("source_session_id"),
5843
+ title: text("title"),
5844
+ alias: text("alias"),
5845
+ naturalKey: text("natural_key"),
5846
+ projectRoot: text("project_root"),
5847
+ harness: text("harness"),
5848
+ model: text("model"),
5849
+ effort: text("effort"),
5850
+ agentId: text("agent_id"),
5851
+ agentName: text("agent_name"),
5852
+ currentBranch: text("current_branch"),
5853
+ authorityNodeId: text("authority_node_id"),
5854
+ authorityNodeName: text("authority_node_name"),
5855
+ parentConversationId: text("parent_conversation_id"),
5856
+ anchorMessageId: text("anchor_message_id"),
5857
+ activityState: text("activity_state").notNull(),
5858
+ lastMessageId: text("last_message_id"),
5859
+ lastMessageAt: integer("last_message_at"),
5860
+ lastActivityAt: integer("last_activity_at").notNull(),
5861
+ messageCount: integer("message_count").notNull().default(0),
5862
+ unreadCount: integer("unread_count").notNull().default(0),
5863
+ participantCount: integer("participant_count").notNull().default(0),
5864
+ preview: text("preview"),
5865
+ lastEngagedAt: integer("last_engaged_at"),
5866
+ sourceFreshAt: integer("source_fresh_at"),
5867
+ visibilityState: text("visibility_state").notNull().default("visible"),
5868
+ updatedSeq: integer("updated_seq").notNull(),
5869
+ updatedAt: integer("updated_at").notNull()
5870
+ }, (table3) => [
5871
+ check("conversation_projection_items_entity_kind_check", sql`${table3.entityKind} IN ('scout_conversation', 'observed_session')`),
5872
+ check("conversation_projection_items_visibility_check", sql`${table3.visibilityState} IN ('visible', 'hidden')`),
5873
+ index("idx_conversation_projection_recent").on(table3.visibilityState, desc(table3.lastActivityAt), table3.feedId),
5874
+ index("idx_conversation_projection_project_recent").on(table3.projectRoot, table3.visibilityState, desc(table3.lastActivityAt)),
5875
+ index("idx_conversation_projection_engaged").on(table3.visibilityState, desc(table3.lastEngagedAt), desc(table3.lastActivityAt), table3.feedId),
5876
+ index("idx_conversation_projection_source_fresh").on(table3.visibilityState, desc(table3.sourceFreshAt)),
5877
+ index("idx_conversation_projection_runtime_session").on(table3.entityKind, table3.runtimeSessionId),
5878
+ index("idx_conversation_projection_source_session").on(table3.entityKind, table3.sourceSessionId),
5879
+ uniqueIndex("idx_conversation_projection_conversation").on(table3.conversationId).where(sql`conversation_id IS NOT NULL`)
5880
+ ]);
5881
+ conversationProjectionMessageFactsTable = sqliteTable("conversation_projection_message_facts", {
5882
+ messageId: text("message_id").primaryKey().references(() => messagesTable.id, { onDelete: "cascade" }),
5883
+ conversationId: text("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
5884
+ createdAt: integer("created_at").notNull()
5885
+ }, (table3) => [
5886
+ index("idx_conversation_projection_message_facts_conversation").on(table3.conversationId, table3.createdAt, table3.messageId)
5887
+ ]);
5888
+ conversationProjectionMessageStatsTable = sqliteTable("conversation_projection_message_stats", {
5889
+ conversationId: text("conversation_id").primaryKey().references(() => conversationsTable.id, { onDelete: "cascade" }),
5890
+ messageCount: integer("message_count").notNull().default(0),
5891
+ latestMessageId: text("latest_message_id"),
5892
+ latestMessageAt: integer("latest_message_at")
5893
+ });
5894
+ conversationProjectionSourcesTable = sqliteTable("conversation_projection_sources", {
5895
+ source: text("source").notNull(),
5896
+ sourceSessionId: text("source_session_id").notNull(),
5897
+ feedId: text("feed_id").notNull().references(() => conversationProjectionItemsTable.feedId, { onDelete: "cascade" }),
5898
+ firstSeenAt: integer("first_seen_at").notNull(),
5899
+ lastSeenAt: integer("last_seen_at").notNull()
5900
+ }, (table3) => [
5901
+ primaryKey({ columns: [table3.source, table3.sourceSessionId] }),
5902
+ index("idx_conversation_projection_sources_feed").on(table3.feedId)
5903
+ ]);
5904
+ conversationProjectionEventsTable = sqliteTable("conversation_projection_events", {
5905
+ seq: integer("seq").primaryKey({ autoIncrement: true }),
5906
+ projectionId: text("projection_id").notNull(),
5907
+ ts: integer("ts").notNull(),
5908
+ payloadJson: text("payload_json").notNull()
5909
+ }, (table3) => [
5910
+ index("idx_conversation_projection_events_projection_seq").on(table3.projectionId, table3.seq),
5911
+ index("idx_conversation_projection_events_ts").on(table3.ts)
5802
5912
  ]);
5803
5913
  invocationsTable = sqliteTable("invocations", {
5804
5914
  id: text("id").primaryKey(),
@@ -5830,7 +5940,10 @@ var init_drizzle_schema = __esm(() => {
5830
5940
  executionResolutionJson: text("execution_resolution_json")
5831
5941
  }, (table3) => [
5832
5942
  index("idx_invocations_target_created_at").on(table3.targetAgentId, table3.createdAt),
5833
- index("idx_invocations_requester_created_at").on(table3.requesterId, desc(table3.createdAt))
5943
+ index("idx_invocations_requester_created_at").on(table3.requesterId, desc(table3.createdAt)),
5944
+ index("idx_invocations_conversation_created_at").on(table3.conversationId, desc(table3.createdAt)),
5945
+ index("idx_invocations_requester_node_id").on(table3.requesterNodeId),
5946
+ index("idx_invocations_target_node_id").on(table3.targetNodeId)
5834
5947
  ]);
5835
5948
  flightsTable = sqliteTable("flights", {
5836
5949
  id: text("id").primaryKey(),
@@ -5876,7 +5989,8 @@ var init_drizzle_schema = __esm(() => {
5876
5989
  createdAt: integer("created_at").notNull().default(epochMsNow)
5877
5990
  }, (table3) => [
5878
5991
  index("idx_deliveries_status_transport").on(table3.status, table3.transport),
5879
- index("idx_deliveries_created_at").on(desc(table3.createdAt))
5992
+ index("idx_deliveries_created_at").on(desc(table3.createdAt)),
5993
+ index("idx_deliveries_target_node_id").on(table3.targetNodeId)
5880
5994
  ]);
5881
5995
  deliveryAttemptsTable = sqliteTable("delivery_attempts", {
5882
5996
  id: text("id").primaryKey(),
@@ -6046,7 +6160,8 @@ var init_drizzle_schema = __esm(() => {
6046
6160
  }, (table3) => [
6047
6161
  unique2().on(table3.conversationId, table3.seq),
6048
6162
  index("idx_thread_events_conversation_seq").on(table3.conversationId, desc(table3.seq)),
6049
- index("idx_thread_events_conversation_ts").on(table3.conversationId, desc(table3.ts))
6163
+ index("idx_thread_events_conversation_ts").on(table3.conversationId, desc(table3.ts)),
6164
+ index("idx_thread_events_authority_node_id").on(table3.authorityNodeId)
6050
6165
  ]);
6051
6166
  threadCursorsTable = sqliteTable("thread_cursors", {
6052
6167
  conversationId: text("conversation_id").notNull().references(() => conversationsTable.id, { onDelete: "cascade" }),
@@ -6054,7 +6169,8 @@ var init_drizzle_schema = __esm(() => {
6054
6169
  lastAppliedSeq: integer("last_applied_seq").notNull(),
6055
6170
  updatedAt: integer("updated_at").notNull()
6056
6171
  }, (table3) => [
6057
- primaryKey({ columns: [table3.conversationId, table3.authorityNodeId] })
6172
+ primaryKey({ columns: [table3.conversationId, table3.authorityNodeId] }),
6173
+ index("idx_thread_cursors_authority_node_id").on(table3.authorityNodeId)
6058
6174
  ]);
6059
6175
  scoutDispatchesTable = sqliteTable("scout_dispatches", {
6060
6176
  id: text("id").primaryKey(),
@@ -6256,6 +6372,13 @@ var init_drizzle_schema = __esm(() => {
6256
6372
  messageMentions: messageMentionsTable,
6257
6373
  messageAttachments: messageAttachmentsTable,
6258
6374
  conversationReadCursors: conversationReadCursorsTable,
6375
+ brokerJournalProjectionCheckpoints: brokerJournalProjectionCheckpointsTable,
6376
+ conversationProjectionMeta: conversationProjectionMetaTable,
6377
+ conversationProjectionItems: conversationProjectionItemsTable,
6378
+ conversationProjectionMessageFacts: conversationProjectionMessageFactsTable,
6379
+ conversationProjectionMessageStats: conversationProjectionMessageStatsTable,
6380
+ conversationProjectionSources: conversationProjectionSourcesTable,
6381
+ conversationProjectionEvents: conversationProjectionEventsTable,
6259
6382
  invocations: invocationsTable,
6260
6383
  flights: flightsTable,
6261
6384
  bindings: bindingsTable,
@@ -6328,6 +6451,8 @@ CREATE INDEX IF NOT EXISTS idx_runtime_sessions_agent_last_seen
6328
6451
  ON runtime_sessions (agent_id, last_seen_at DESC);
6329
6452
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_endpoint_last_seen
6330
6453
  ON runtime_sessions (endpoint_id, last_seen_at DESC);
6454
+ CREATE INDEX IF NOT EXISTS idx_runtime_sessions_node_id
6455
+ ON runtime_sessions (node_id);
6331
6456
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_external
6332
6457
  ON runtime_sessions (external_session_id);
6333
6458
  CREATE INDEX IF NOT EXISTS idx_runtime_sessions_expires
@@ -6337,6 +6462,10 @@ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_alias
6337
6462
  ON runtime_session_aliases (alias, last_seen_at DESC);
6338
6463
  CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_session
6339
6464
  ON runtime_session_aliases (session_id);
6465
+ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_endpoint
6466
+ ON runtime_session_aliases (endpoint_id);
6467
+ CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_node_id
6468
+ ON runtime_session_aliases (node_id);
6340
6469
  CREATE INDEX IF NOT EXISTS idx_runtime_session_aliases_expires
6341
6470
  ON runtime_session_aliases (expires_at)
6342
6471
  WHERE expires_at IS NOT NULL;
@@ -6582,6 +6711,93 @@ CREATE TABLE IF NOT EXISTS conversation_read_cursors (
6582
6711
  PRIMARY KEY (conversation_id, actor_id)
6583
6712
  );
6584
6713
 
6714
+ CREATE TABLE IF NOT EXISTS broker_journal_projection_checkpoints (
6715
+ projection_id TEXT PRIMARY KEY,
6716
+ projection_version INTEGER NOT NULL,
6717
+ barrier_id TEXT NOT NULL,
6718
+ updated_at INTEGER NOT NULL
6719
+ );
6720
+
6721
+ CREATE TABLE IF NOT EXISTS conversation_projection_meta (
6722
+ singleton INTEGER PRIMARY KEY,
6723
+ projection_id TEXT NOT NULL,
6724
+ projection_version INTEGER NOT NULL,
6725
+ head_seq INTEGER NOT NULL,
6726
+ min_replayable_seq INTEGER NOT NULL,
6727
+ updated_at INTEGER NOT NULL,
6728
+ CONSTRAINT conversation_projection_meta_singleton_check CHECK (singleton = 1)
6729
+ );
6730
+
6731
+ CREATE TABLE IF NOT EXISTS conversation_projection_items (
6732
+ feed_id TEXT PRIMARY KEY,
6733
+ entity_kind TEXT NOT NULL,
6734
+ kind TEXT NOT NULL,
6735
+ conversation_id TEXT,
6736
+ runtime_session_id TEXT,
6737
+ source TEXT,
6738
+ source_session_id TEXT,
6739
+ title TEXT,
6740
+ alias TEXT,
6741
+ natural_key TEXT,
6742
+ project_root TEXT,
6743
+ harness TEXT,
6744
+ model TEXT,
6745
+ effort TEXT,
6746
+ agent_id TEXT,
6747
+ agent_name TEXT,
6748
+ current_branch TEXT,
6749
+ authority_node_id TEXT,
6750
+ authority_node_name TEXT,
6751
+ parent_conversation_id TEXT,
6752
+ anchor_message_id TEXT,
6753
+ activity_state TEXT NOT NULL,
6754
+ last_message_id TEXT,
6755
+ last_message_at INTEGER,
6756
+ last_activity_at INTEGER NOT NULL,
6757
+ message_count INTEGER NOT NULL DEFAULT 0,
6758
+ unread_count INTEGER NOT NULL DEFAULT 0,
6759
+ participant_count INTEGER NOT NULL DEFAULT 0,
6760
+ preview TEXT,
6761
+ last_engaged_at INTEGER,
6762
+ source_fresh_at INTEGER,
6763
+ visibility_state TEXT NOT NULL DEFAULT 'visible',
6764
+ updated_seq INTEGER NOT NULL,
6765
+ updated_at INTEGER NOT NULL,
6766
+ CONSTRAINT conversation_projection_items_entity_kind_check
6767
+ CHECK (entity_kind IN ('scout_conversation', 'observed_session')),
6768
+ CONSTRAINT conversation_projection_items_visibility_check
6769
+ CHECK (visibility_state IN ('visible', 'hidden'))
6770
+ );
6771
+
6772
+ CREATE TABLE IF NOT EXISTS conversation_projection_message_facts (
6773
+ message_id TEXT PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
6774
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
6775
+ created_at INTEGER NOT NULL
6776
+ );
6777
+
6778
+ CREATE TABLE IF NOT EXISTS conversation_projection_message_stats (
6779
+ conversation_id TEXT PRIMARY KEY REFERENCES conversations(id) ON DELETE CASCADE,
6780
+ message_count INTEGER NOT NULL DEFAULT 0,
6781
+ latest_message_id TEXT,
6782
+ latest_message_at INTEGER
6783
+ );
6784
+
6785
+ CREATE TABLE IF NOT EXISTS conversation_projection_sources (
6786
+ source TEXT NOT NULL,
6787
+ source_session_id TEXT NOT NULL,
6788
+ feed_id TEXT NOT NULL REFERENCES conversation_projection_items(feed_id) ON DELETE CASCADE,
6789
+ first_seen_at INTEGER NOT NULL,
6790
+ last_seen_at INTEGER NOT NULL,
6791
+ PRIMARY KEY (source, source_session_id)
6792
+ );
6793
+
6794
+ CREATE TABLE IF NOT EXISTS conversation_projection_events (
6795
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
6796
+ projection_id TEXT NOT NULL,
6797
+ ts INTEGER NOT NULL,
6798
+ payload_json TEXT NOT NULL
6799
+ );
6800
+
6585
6801
  CREATE TABLE IF NOT EXISTS invocations (
6586
6802
  id TEXT PRIMARY KEY,
6587
6803
  requester_id TEXT NOT NULL REFERENCES actors(id) ON DELETE RESTRICT,
@@ -6989,18 +7205,74 @@ CREATE INDEX IF NOT EXISTS idx_peer_nonces_seen_at
6989
7205
  ON peer_nonces (seen_at);
6990
7206
  CREATE INDEX IF NOT EXISTS idx_agent_endpoints_agent_updated_at
6991
7207
  ON agent_endpoints (agent_id, updated_at DESC);
7208
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_roster_recency
7209
+ ON agent_endpoints (
7210
+ CASE
7211
+ WHEN updated_at IS NULL THEN NULL
7212
+ WHEN CAST(updated_at AS REAL) < 1000000000000
7213
+ THEN CAST(CAST(updated_at AS REAL) * 1000 AS INTEGER)
7214
+ ELSE CAST(updated_at AS INTEGER)
7215
+ END DESC,
7216
+ agent_id
7217
+ );
7218
+ CREATE INDEX IF NOT EXISTS idx_agents_home_node_id
7219
+ ON agents (home_node_id);
7220
+ CREATE INDEX IF NOT EXISTS idx_agents_authority_node_id
7221
+ ON agents (authority_node_id);
7222
+ CREATE INDEX IF NOT EXISTS idx_agent_endpoints_node_id
7223
+ ON agent_endpoints (node_id);
7224
+ CREATE INDEX IF NOT EXISTS idx_conversation_members_actor
7225
+ ON conversation_members (actor_id, conversation_id);
6992
7226
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_created_at
6993
7227
  ON messages (conversation_id, created_at);
6994
7228
  CREATE INDEX IF NOT EXISTS idx_messages_created_at
6995
7229
  ON messages (created_at DESC);
6996
7230
  CREATE INDEX IF NOT EXISTS idx_messages_actor_created_at
6997
7231
  ON messages (actor_id, created_at DESC);
7232
+ CREATE INDEX IF NOT EXISTS idx_messages_origin_node_id
7233
+ ON messages (origin_node_id);
6998
7234
  CREATE INDEX IF NOT EXISTS idx_read_cursors_conversation_updated_at
6999
7235
  ON conversation_read_cursors (conversation_id, updated_at DESC);
7236
+ CREATE INDEX IF NOT EXISTS idx_read_cursors_reader_node_id
7237
+ ON conversation_read_cursors (reader_node_id);
7238
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_recent
7239
+ ON conversation_projection_items (visibility_state, last_activity_at DESC, feed_id);
7240
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_project_recent
7241
+ ON conversation_projection_items (project_root, visibility_state, last_activity_at DESC);
7242
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_message_facts_conversation
7243
+ ON conversation_projection_message_facts (conversation_id, created_at, message_id);
7244
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_engaged
7245
+ ON conversation_projection_items (
7246
+ visibility_state,
7247
+ last_engaged_at DESC,
7248
+ last_activity_at DESC,
7249
+ feed_id
7250
+ );
7251
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_source_fresh
7252
+ ON conversation_projection_items (visibility_state, source_fresh_at DESC);
7253
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_runtime_session
7254
+ ON conversation_projection_items (entity_kind, runtime_session_id);
7255
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_source_session
7256
+ ON conversation_projection_items (entity_kind, source_session_id);
7257
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_conversation_projection_conversation
7258
+ ON conversation_projection_items (conversation_id)
7259
+ WHERE conversation_id IS NOT NULL;
7260
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_sources_feed
7261
+ ON conversation_projection_sources (feed_id);
7262
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_events_projection_seq
7263
+ ON conversation_projection_events (projection_id, seq);
7264
+ CREATE INDEX IF NOT EXISTS idx_conversation_projection_events_ts
7265
+ ON conversation_projection_events (ts);
7000
7266
  CREATE INDEX IF NOT EXISTS idx_invocations_target_created_at
7001
7267
  ON invocations (target_agent_id, created_at);
7002
7268
  CREATE INDEX IF NOT EXISTS idx_invocations_requester_created_at
7003
7269
  ON invocations (requester_id, created_at DESC);
7270
+ CREATE INDEX IF NOT EXISTS idx_invocations_conversation_created_at
7271
+ ON invocations (conversation_id, created_at DESC);
7272
+ CREATE INDEX IF NOT EXISTS idx_invocations_requester_node_id
7273
+ ON invocations (requester_node_id);
7274
+ CREATE INDEX IF NOT EXISTS idx_invocations_target_node_id
7275
+ ON invocations (target_node_id);
7004
7276
  CREATE INDEX IF NOT EXISTS idx_flights_target_state
7005
7277
  ON flights (target_agent_id, state);
7006
7278
  CREATE INDEX IF NOT EXISTS idx_flights_invocation_id
@@ -7009,6 +7281,8 @@ CREATE INDEX IF NOT EXISTS idx_deliveries_status_transport
7009
7281
  ON deliveries (status, transport);
7010
7282
  CREATE INDEX IF NOT EXISTS idx_deliveries_created_at
7011
7283
  ON deliveries (created_at DESC);
7284
+ CREATE INDEX IF NOT EXISTS idx_deliveries_target_node_id
7285
+ ON deliveries (target_node_id);
7012
7286
  CREATE INDEX IF NOT EXISTS idx_delivery_attempts_created_at
7013
7287
  ON delivery_attempts (created_at DESC);
7014
7288
  CREATE UNIQUE INDEX IF NOT EXISTS idx_durable_actions_idempotency_key
@@ -7055,6 +7329,10 @@ CREATE INDEX IF NOT EXISTS idx_thread_events_conversation_seq
7055
7329
  ON thread_events (conversation_id, seq DESC);
7056
7330
  CREATE INDEX IF NOT EXISTS idx_thread_events_conversation_ts
7057
7331
  ON thread_events (conversation_id, ts DESC);
7332
+ CREATE INDEX IF NOT EXISTS idx_thread_events_authority_node_id
7333
+ ON thread_events (authority_node_id);
7334
+ CREATE INDEX IF NOT EXISTS idx_thread_cursors_authority_node_id
7335
+ ON thread_cursors (authority_node_id);
7058
7336
  CREATE INDEX IF NOT EXISTS idx_activity_items_agent_ts
7059
7337
  ON activity_items (agent_id, ts DESC);
7060
7338
  CREATE INDEX IF NOT EXISTS idx_activity_items_actor_ts
@@ -8630,12 +8908,15 @@ var init_host_integrations = () => {};
8630
8908
  // packages/protocol/src/native-interactions.ts
8631
8909
  var init_native_interactions = () => {};
8632
8910
 
8911
+ // packages/protocol/src/session-handle.ts
8912
+ var init_session_handle = () => {};
8913
+
8633
8914
  // packages/protocol/src/runtime-catalog.generated.ts
8634
8915
  var SCOUT_RUNTIME_CATALOG_DATA;
8635
8916
  var init_runtime_catalog_generated = __esm(() => {
8636
8917
  SCOUT_RUNTIME_CATALOG_DATA = {
8637
8918
  schemaVersion: "openscout.runtime-catalog.v1",
8638
- revision: "2026-08-18.1",
8919
+ revision: "2026-09-01.1",
8639
8920
  harnesses: [
8640
8921
  {
8641
8922
  id: "claude",
@@ -8659,6 +8940,13 @@ var init_runtime_catalog_generated = __esm(() => {
8659
8940
  family: "Opus",
8660
8941
  version: "5"
8661
8942
  },
8943
+ {
8944
+ id: "claude-fable-5",
8945
+ label: "Fable 5",
8946
+ enabled: true,
8947
+ family: "Fable",
8948
+ version: "5"
8949
+ },
8662
8950
  {
8663
8951
  id: "claude-sonnet-4-6",
8664
8952
  label: "Sonnet 4.6",
@@ -9277,6 +9565,12 @@ var init_runtime_catalog = __esm(() => {
9277
9565
  reasoningEfforts = new Set(SCOUT_REASONING_EFFORTS);
9278
9566
  });
9279
9567
 
9568
+ // packages/protocol/src/scout-baseline-runtime.ts
9569
+ var init_scout_baseline_runtime = () => {};
9570
+
9571
+ // packages/protocol/src/session-title.ts
9572
+ var init_session_title = () => {};
9573
+
9280
9574
  // packages/protocol/src/reserved-vocabulary.ts
9281
9575
  function scoutReservedVocabularyKind(value) {
9282
9576
  const normalized = normalizeAgentSelectorSegment(value ?? "");
@@ -9769,6 +10063,7 @@ var init_broker_routes = __esm(() => {
9769
10063
  home: "/v1/home",
9770
10064
  node: "/v1/node",
9771
10065
  snapshot: "/v1/snapshot",
10066
+ conversationProjection: "/v1/conversation-projection",
9772
10067
  capabilities: "/v1/capabilities",
9773
10068
  runtimeCatalog: "/v1/runtime-catalog",
9774
10069
  topologySnapshot: "/v1/topology/snapshot",
@@ -9837,9 +10132,12 @@ var init_src = __esm(() => {
9837
10132
  init_budget();
9838
10133
  init_host_integrations();
9839
10134
  init_native_interactions();
10135
+ init_session_handle();
9840
10136
  init_runtime_profiles();
9841
10137
  init_runtime_execution();
9842
10138
  init_runtime_catalog();
10139
+ init_scout_baseline_runtime();
10140
+ init_session_title();
9843
10141
  init_reserved_vocabulary();
9844
10142
  init_scout_composer();
9845
10143
  init_inbox();
@@ -15281,6 +15579,9 @@ var init_budget_observations = __esm(() => {
15281
15579
  QUOTA_HISTORY_BUCKET_MS = 60 * 60 * 1000;
15282
15580
  });
15283
15581
 
15582
+ // packages/runtime/src/runtime-session-handle.ts
15583
+ var init_runtime_session_handle = () => {};
15584
+
15284
15585
  // packages/runtime/src/sqlite-store.ts
15285
15586
  var RUNTIME_SESSION_RETENTION_MS;
15286
15587
  var init_sqlite_store = __esm(() => {
@@ -15292,6 +15593,7 @@ var init_sqlite_store = __esm(() => {
15292
15593
  init_api();
15293
15594
  init_drizzle_schema();
15294
15595
  init_budget_observations();
15596
+ init_runtime_session_handle();
15295
15597
  RUNTIME_SESSION_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
15296
15598
  });
15297
15599
 
@@ -32595,10 +32897,12 @@ var init_iroh_bridge = __esm(() => {
32595
32897
  var init_mesh_dial_order = () => {};
32596
32898
 
32597
32899
  // packages/runtime/src/mesh-forwarding.ts
32900
+ var MAX_PEER_AGENT_SNAPSHOT_BYTES;
32598
32901
  var init_mesh_forwarding = __esm(() => {
32599
32902
  init_iroh_bridge();
32600
32903
  init_mesh_peer_client();
32601
32904
  init_mesh_dial_order();
32905
+ MAX_PEER_AGENT_SNAPSHOT_BYTES = 2 * 1024 * 1024;
32602
32906
  });
32603
32907
 
32604
32908
  // packages/runtime/src/broker-api.ts
@@ -33406,11 +33710,32 @@ function resolveBundledRuntimeDirFromModuleDir(moduleDir) {
33406
33710
  resolve2(moduleDir, "..", "..")
33407
33711
  ];
33408
33712
  for (const candidate of candidates) {
33409
- if (isInstalledRuntimePackageDir(candidate))
33410
- return candidate;
33713
+ if (!isInstalledRuntimePackageDir(candidate))
33714
+ continue;
33715
+ const sourceWorkspaceRuntime = resolveSourceWorkspaceRuntimeDir(candidate);
33716
+ if (sourceWorkspaceRuntime)
33717
+ return sourceWorkspaceRuntime;
33718
+ return candidate;
33411
33719
  }
33412
33720
  return null;
33413
33721
  }
33722
+ function resolveSourceWorkspaceRuntimeDir(bundledPackageDir) {
33723
+ const packagesDir = dirname6(bundledPackageDir);
33724
+ if (resolve2(packagesDir, "cli") !== resolve2(bundledPackageDir))
33725
+ return null;
33726
+ const runtimeDir = join8(packagesDir, "runtime");
33727
+ if (!existsSync8(join8(runtimeDir, "src")))
33728
+ return null;
33729
+ if (!isInstalledRuntimePackageDir(runtimeDir))
33730
+ return null;
33731
+ try {
33732
+ const cliPackage = JSON.parse(readFileSync5(join8(bundledPackageDir, "package.json"), "utf8"));
33733
+ const runtimePackage = JSON.parse(readFileSync5(join8(runtimeDir, "package.json"), "utf8"));
33734
+ return cliPackage.name === "@openscout/scout" && runtimePackage.name === "@openscout/runtime" ? runtimeDir : null;
33735
+ } catch {
33736
+ return null;
33737
+ }
33738
+ }
33414
33739
  function findWorkspaceRuntimeDir(startDir) {
33415
33740
  let current = resolve2(startDir);
33416
33741
  while (true) {
@@ -54137,6 +54462,7 @@ var TERMINAL_ENDPOINT_STATES;
54137
54462
  var init_broker_endpoint_selection = __esm(() => {
54138
54463
  init_a2a_http_endpoint();
54139
54464
  init_local_agent_transports();
54465
+ init_runtime_session_handle();
54140
54466
  TERMINAL_ENDPOINT_STATES = new Set([
54141
54467
  "failed",
54142
54468
  "superseded",
@@ -57632,6 +57958,14 @@ function resolveScoutWebBindHost(env) {
57632
57958
  }
57633
57959
  return host;
57634
57960
  }
57961
+ function resolveScoutWebLanAccessScope(env) {
57962
+ const scope = env.OPENSCOUT_WEB_LAN_SCOPE?.trim().toLowerCase();
57963
+ if (!scope || scope === "full")
57964
+ return "full";
57965
+ if (scope === "pairing")
57966
+ return "pairing";
57967
+ throw new Error(`Unsupported OPENSCOUT_WEB_LAN_SCOPE: ${scope}`);
57968
+ }
57635
57969
  function constantTimeTokenMatch(candidate, expected) {
57636
57970
  if (!candidate)
57637
57971
  return false;
@@ -57673,20 +58007,29 @@ function scoutWebAuthCookie(token, secure) {
57673
58007
  function localInterfaceAddresses() {
57674
58008
  return Object.values(networkInterfaces2()).flatMap((entries) => entries ?? []).map((entry) => entry.address);
57675
58009
  }
57676
- function shouldIssueLocalScoutWebCredential(request, peerAddress, ownAddresses = localInterfaceAddresses()) {
58010
+ function isSameMacScoutRequest(request, peerAddress, ownAddresses = localInterfaceAddresses()) {
57677
58011
  if (!peerAddress || !isLoopbackScoutAddress(peerAddress))
57678
58012
  return false;
57679
58013
  if (request.headers.has("forwarded"))
57680
58014
  return false;
57681
- const forwardedFor = request.headers.get("x-forwarded-for");
57682
- if (!forwardedFor)
57683
- return true;
57684
- if (forwardedFor.includes(","))
57685
- return false;
57686
- const clientAddress = normalizeIpAddress(forwardedFor);
57687
- if (!clientAddress)
57688
- return false;
57689
- return isLoopbackScoutAddress(clientAddress) || ownAddresses.some((address) => normalizeIpAddress(address) === clientAddress);
58015
+ const forwardedAddresses = [
58016
+ request.headers.get("x-forwarded-for"),
58017
+ request.headers.get("x-real-ip")
58018
+ ].filter((value) => value !== null);
58019
+ for (const value of forwardedAddresses) {
58020
+ if (value.includes(","))
58021
+ return false;
58022
+ const clientAddress = normalizeIpAddress(value);
58023
+ if (!clientAddress)
58024
+ return false;
58025
+ if (!isLoopbackScoutAddress(clientAddress) && !ownAddresses.some((address) => normalizeIpAddress(address) === clientAddress)) {
58026
+ return false;
58027
+ }
58028
+ }
58029
+ return true;
58030
+ }
58031
+ function shouldIssueLocalScoutWebCredential(request, peerAddress, ownAddresses = localInterfaceAddresses()) {
58032
+ return isSameMacScoutRequest(request, peerAddress, ownAddresses);
57690
58033
  }
57691
58034
  function normalizeIpAddress(address) {
57692
58035
  const normalized = normalizeHostname(address);
@@ -57728,13 +58071,32 @@ function isLoopbackScoutAddress(address) {
57728
58071
  const mapped = normalized.startsWith("::ffff:") ? normalized.slice(7) : normalized;
57729
58072
  return LOOPBACK_IPV4_HOST_PATTERN.test(mapped);
57730
58073
  }
57731
- function defaultPeerAddress(c) {
58074
+ function resolveScoutRequestPeerAddress(c) {
57732
58075
  try {
57733
58076
  return getConnInfo(c).remote.address;
57734
58077
  } catch {
57735
58078
  return;
57736
58079
  }
57737
58080
  }
58081
+ function createScoutRequestPeerAddressRegistry() {
58082
+ const peers = new WeakMap;
58083
+ return {
58084
+ remember(request, address) {
58085
+ if (address)
58086
+ peers.set(request, address);
58087
+ },
58088
+ resolve(c) {
58089
+ return peers.get(c.req.raw) ?? resolveScoutRequestPeerAddress(c);
58090
+ }
58091
+ };
58092
+ }
58093
+ function isScoutWebRequestAllowedFromPeer(request, peerAddress, scope) {
58094
+ if (scope === "full" || isSameMacScoutRequest(request, peerAddress)) {
58095
+ return true;
58096
+ }
58097
+ const url = new URL(request.url);
58098
+ return request.method === "GET" && url.pathname === "/pair" && request.headers.get("upgrade") === null;
58099
+ }
57738
58100
  function isTrustedScoutApiRequest(request, options = {}, peerAddress) {
57739
58101
  const requestUrl = new URL(request.url);
57740
58102
  const hostname2 = normalizeHostname(requestUrl.hostname);
@@ -57880,7 +58242,7 @@ function scoutApiPerfReport() {
57880
58242
  }
57881
58243
  function installScoutApiMiddleware(app, label = "api", options = {}) {
57882
58244
  app.use("/api/*", async (c, next) => {
57883
- const peerAddress = (options.resolvePeerAddress ?? defaultPeerAddress)(c);
58245
+ const peerAddress = (options.resolvePeerAddress ?? resolveScoutRequestPeerAddress)(c);
57884
58246
  if (!isTrustedScoutApiRequest(c.req.raw, options, peerAddress)) {
57885
58247
  return c.json({ error: "forbidden" }, 403);
57886
58248
  }
@@ -58451,6 +58813,19 @@ async function readScoutBrokerHome(baseUrl = resolveScoutBrokerUrl(), options =
58451
58813
  return null;
58452
58814
  }
58453
58815
  }
58816
+ async function readScoutConversationProjection(limit = 160, baseUrl = resolveScoutBrokerUrl(), options = {}) {
58817
+ const query = new URLSearchParams({
58818
+ limit: String(Math.min(160, Math.max(1, Math.floor(limit))))
58819
+ });
58820
+ try {
58821
+ return await brokerReadJson(baseUrl, `${scoutBrokerPaths.v1.conversationProjection}?${query}`, {
58822
+ ...options,
58823
+ signal: options.signal ?? AbortSignal.timeout(450)
58824
+ });
58825
+ } catch {
58826
+ return null;
58827
+ }
58828
+ }
58454
58829
  async function readScoutBrokerRuntimeCatalog(baseUrl = resolveScoutBrokerUrl()) {
58455
58830
  try {
58456
58831
  return await brokerReadJson(baseUrl, scoutBrokerPaths.v1.runtimeCatalog);
@@ -58458,6 +58833,13 @@ async function readScoutBrokerRuntimeCatalog(baseUrl = resolveScoutBrokerUrl())
58458
58833
  return null;
58459
58834
  }
58460
58835
  }
58836
+ async function readScoutBrokerTailDiscovery(baseUrl = resolveScoutBrokerUrl()) {
58837
+ try {
58838
+ return await brokerReadJson(baseUrl, scoutBrokerPaths.v1.tailDiscover);
58839
+ } catch {
58840
+ return null;
58841
+ }
58842
+ }
58461
58843
  async function readScoutBrokerSnapshot(baseUrl = resolveScoutBrokerUrl(), options = {}) {
58462
58844
  try {
58463
58845
  return await brokerReadJson(baseUrl, scoutBrokerPaths.v1.snapshot, options);
@@ -58495,26 +58877,24 @@ async function recordScoutBrokerReadCursor(input, baseUrl = resolveScoutBrokerUr
58495
58877
  });
58496
58878
  }
58497
58879
  async function loadScoutBrokerContext(baseUrl = resolveScoutBrokerUrl(), options = {}) {
58498
- const since = options.since === undefined ? Math.floor((Date.now() - DEFAULT_SCOUT_BROKER_SNAPSHOT_WINDOW_MS) / DEFAULT_SCOUT_BROKER_SNAPSHOT_BUCKET_MS) * DEFAULT_SCOUT_BROKER_SNAPSHOT_BUCKET_MS : options.since;
58880
+ const usesRollingWindow = options.since === undefined;
58881
+ const since = usesRollingWindow ? Math.floor((Date.now() - DEFAULT_SCOUT_BROKER_SNAPSHOT_WINDOW_MS) / DEFAULT_SCOUT_BROKER_SNAPSHOT_BUCKET_MS) * DEFAULT_SCOUT_BROKER_SNAPSHOT_BUCKET_MS : options.since;
58499
58882
  if (options.force) {
58500
58883
  invalidateScoutBrokerContextCache(baseUrl);
58501
58884
  }
58502
58885
  const cacheKey = options.signal ? null : [
58503
58886
  baseUrl,
58504
58887
  resolveBrokerSocketPathForBaseUrl(baseUrl) ?? "http",
58505
- since ?? "full",
58888
+ usesRollingWindow ? "rolling-24h" : since ?? "full",
58506
58889
  options.scope ?? "default"
58507
58890
  ].join("\x00");
58508
58891
  const now = Date.now();
58509
- for (const [key, entry] of scoutBrokerContextCache) {
58510
- if (entry.expiresAt <= now)
58892
+ for (const [key, entry2] of scoutBrokerContextCache) {
58893
+ if (key !== cacheKey && !entry2.inFlight && entry2.expiresAt + SCOUT_BROKER_CONTEXT_STALE_RETENTION_MS <= now) {
58511
58894
  scoutBrokerContextCache.delete(key);
58895
+ }
58512
58896
  }
58513
- const cached = cacheKey ? scoutBrokerContextCache.get(cacheKey) : null;
58514
- if (cached && cached.expiresAt > now) {
58515
- return cached.promise;
58516
- }
58517
- const promise = (async () => {
58897
+ const readContext = async () => {
58518
58898
  const health = await readScoutBrokerHealth(baseUrl, { signal: options.signal });
58519
58899
  if (!health.reachable || !health.ok) {
58520
58900
  return null;
@@ -58538,19 +58918,60 @@ async function loadScoutBrokerContext(baseUrl = resolveScoutBrokerUrl(), options
58538
58918
  } catch {
58539
58919
  return null;
58540
58920
  }
58541
- })();
58542
- if (cacheKey) {
58543
- scoutBrokerContextCache.set(cacheKey, {
58544
- expiresAt: now + SCOUT_BROKER_CONTEXT_CACHE_TTL_MS,
58545
- promise
58546
- });
58547
- promise.then((context) => {
58548
- if (!context && scoutBrokerContextCache.get(cacheKey)?.promise === promise) {
58921
+ };
58922
+ if (!cacheKey) {
58923
+ return readContext();
58924
+ }
58925
+ const refresh = (entry2, delayMs = 0) => {
58926
+ if (entry2.inFlight)
58927
+ return entry2.inFlight;
58928
+ const attempt = (delayMs > 0 ? new Promise((resolve17) => setTimeout(resolve17, delayMs)) : Promise.resolve()).then(readContext).then((context) => {
58929
+ if (scoutBrokerContextCache.get(cacheKey) !== entry2)
58930
+ return context;
58931
+ if (context) {
58932
+ entry2.value = context;
58933
+ entry2.hasValue = true;
58934
+ entry2.expiresAt = Date.now() + SCOUT_BROKER_CONTEXT_CACHE_TTL_MS;
58935
+ } else if (!entry2.hasValue) {
58549
58936
  scoutBrokerContextCache.delete(cacheKey);
58937
+ } else {
58938
+ entry2.expiresAt = Date.now() + SCOUT_BROKER_CONTEXT_CACHE_TTL_MS;
58939
+ }
58940
+ return context;
58941
+ }).finally(() => {
58942
+ if (scoutBrokerContextCache.get(cacheKey) === entry2) {
58943
+ entry2.inFlight = null;
58550
58944
  }
58551
58945
  });
58946
+ entry2.inFlight = attempt;
58947
+ return attempt;
58948
+ };
58949
+ const cached = scoutBrokerContextCache.get(cacheKey);
58950
+ if (cached) {
58951
+ if (cached.hasValue) {
58952
+ if (cached.expiresAt <= now && !cached.inFlight) {
58953
+ refresh(cached);
58954
+ }
58955
+ return cached.value;
58956
+ }
58957
+ if (options.waitForInitial === false) {
58958
+ refresh(cached, options.initialRefreshDelayMs);
58959
+ return null;
58960
+ }
58961
+ return refresh(cached);
58552
58962
  }
58553
- return promise;
58963
+ const entry = {
58964
+ expiresAt: now + SCOUT_BROKER_CONTEXT_CACHE_TTL_MS,
58965
+ value: null,
58966
+ hasValue: false,
58967
+ inFlight: null
58968
+ };
58969
+ scoutBrokerContextCache.set(cacheKey, entry);
58970
+ if (options.waitForInitial === false) {
58971
+ refresh(entry, options.initialRefreshDelayMs);
58972
+ return null;
58973
+ }
58974
+ return refresh(entry);
58554
58975
  }
58555
58976
  async function requireScoutBrokerContext(baseUrl = resolveScoutBrokerUrl()) {
58556
58977
  const context = await loadScoutBrokerContext(baseUrl);
@@ -59336,14 +59757,11 @@ async function loadScoutReadCursors(input) {
59336
59757
  return brokerReadJson(input.baseUrl ?? resolveScoutBrokerUrl(), path);
59337
59758
  }
59338
59759
  async function markScoutConversationRead(input) {
59339
- const broker2 = await loadScoutBrokerContext(input.baseUrl);
59340
- if (!broker2) {
59341
- throw new Error("broker unreachable");
59342
- }
59760
+ const baseUrl = input.baseUrl ?? resolveScoutBrokerUrl();
59343
59761
  const path = `/v1/conversations/${encodeURIComponent(input.conversationId)}/read-cursors`;
59344
- return brokerPostJson(broker2.baseUrl, path, {
59762
+ return brokerPostJson(baseUrl, path, {
59345
59763
  actorId: input.actorId,
59346
- readerNodeId: input.readerNodeId ?? broker2.node.id,
59764
+ readerNodeId: input.readerNodeId,
59347
59765
  lastReadMessageId: input.lastReadMessageId,
59348
59766
  lastReadSeq: input.lastReadSeq,
59349
59767
  lastReadAt: input.lastReadAt,
@@ -59507,7 +59925,7 @@ async function loadScoutRelayConfig() {
59507
59925
  return {};
59508
59926
  }
59509
59927
  }
59510
- var DEFAULT_SCOUT_BROKER_SNAPSHOT_WINDOW_MS, DEFAULT_SCOUT_BROKER_SNAPSHOT_BUCKET_MS, SCOUT_BROKER_CONTEXT_CACHE_TTL_MS = 5000, scoutBrokerContextCache, ScoutDirectDeliveryUnavailableError, OPERATOR_ID = "operator", SCOPED_ALIAS_POOL, readScoutBrokerNodeIdCoalesced;
59928
+ var DEFAULT_SCOUT_BROKER_SNAPSHOT_WINDOW_MS, DEFAULT_SCOUT_BROKER_SNAPSHOT_BUCKET_MS, SCOUT_BROKER_CONTEXT_CACHE_TTL_MS = 30000, SCOUT_BROKER_CONTEXT_STALE_RETENTION_MS = 60000, scoutBrokerContextCache, ScoutDirectDeliveryUnavailableError, OPERATOR_ID = "operator", SCOPED_ALIAS_POOL, readScoutBrokerNodeIdCoalesced;
59511
59929
  var init_service = __esm(() => {
59512
59930
  init_src();
59513
59931
  init_setup();
@@ -60886,14 +61304,47 @@ function existingDirectConversationIdsForAgents(agentIds) {
60886
61304
  }
60887
61305
  return conversationIdByAgentId;
60888
61306
  }
61307
+ function queryAgentIdsForBoundedRoster(limit) {
61308
+ const database = db2();
61309
+ let recentIds;
61310
+ try {
61311
+ recentIds = database.prepare(RECENT_ENDPOINT_AGENT_IDS_SQL).all(limit);
61312
+ } catch (error) {
61313
+ if (!(error instanceof Error) || !error.message.includes("idx_agent_endpoints_roster_recency")) {
61314
+ throw error;
61315
+ }
61316
+ recentIds = database.prepare(RECENT_ENDPOINT_AGENT_IDS_WITHOUT_INDEX_SQL).all(limit);
61317
+ }
61318
+ if (recentIds.length >= limit) {
61319
+ return recentIds.map((row) => row.id);
61320
+ }
61321
+ const endpointUpdatedAtExpression2 = sqlTimestampMsExpression("ep.updated_at");
61322
+ const fallbackIds = database.prepare(`SELECT a.id
61323
+ FROM agents a
61324
+ JOIN actors ac ON ac.id = a.id
61325
+ ${LATEST_AGENT_ENDPOINT_JOIN}
61326
+ WHERE ${activeAgentMetadataPredicate("a")}
61327
+ AND COALESCE(${endpointUpdatedAtExpression2}, 0) <= 0
61328
+ ORDER BY COALESCE(${endpointUpdatedAtExpression2}, 0) DESC, ac.display_name ASC
61329
+ LIMIT ?`).all(limit - recentIds.length);
61330
+ return [...recentIds, ...fallbackIds].map((row) => row.id);
61331
+ }
61332
+ function queryAgentRowsByIds(agentIds) {
61333
+ if (agentIds.length === 0)
61334
+ return [];
61335
+ return db2().prepare(`${AGENT_ROW_SELECT}
61336
+ WHERE a.id IN (SELECT value FROM json_each(?))
61337
+ AND ${activeAgentMetadataPredicate("a")}
61338
+ ORDER BY COALESCE(${endpointUpdatedAtExpression}, 0) DESC, ac.display_name ASC`).all(JSON.stringify(agentIds));
61339
+ }
60889
61340
  function queryAgentIdsByEndpointSessionId() {
60890
- const endpointUpdatedAtExpression = sqlTimestampMsExpression("ep.updated_at");
61341
+ const endpointUpdatedAtExpression2 = sqlTimestampMsExpression("ep.updated_at");
60891
61342
  const rows = db2().prepare(`SELECT a.id, ep.session_id
60892
61343
  FROM agents a
60893
61344
  ${LATEST_AGENT_ENDPOINT_JOIN}
60894
61345
  WHERE ep.session_id IS NOT NULL
60895
61346
  AND ${activeAgentMetadataPredicate("a")}
60896
- ORDER BY COALESCE(${endpointUpdatedAtExpression}, 0) ASC`).all();
61347
+ ORDER BY COALESCE(${endpointUpdatedAtExpression2}, 0) ASC`).all();
60897
61348
  const map = new Map;
60898
61349
  for (const row of rows) {
60899
61350
  const sessionId = row.session_id.trim();
@@ -60905,87 +61356,20 @@ function queryAgentIdsByEndpointSessionId() {
60905
61356
  }
60906
61357
  function queryAgents(limit = 500) {
60907
61358
  const flightPhases = queryAgentFlightPhases();
60908
- const actorCreatedAtExpression = sqlTimestampMsExpression("ac.created_at");
60909
- const endpointUpdatedAtExpression = sqlTimestampMsExpression("ep.updated_at");
60910
- const rows = db2().prepare(`SELECT
60911
- a.id,
60912
- a.definition_id,
60913
- a.node_qualifier,
60914
- a.workspace_qualifier,
60915
- a.selector,
60916
- ac.display_name AS name,
60917
- ac.handle,
60918
- ${actorCreatedAtExpression} AS actor_created_at,
60919
- a.agent_class,
60920
- a.default_selector,
60921
- a.wake_policy,
60922
- a.capabilities_json,
60923
- a.metadata_json,
60924
- a.authority_node_id,
60925
- an.name AS authority_node_name,
60926
- a.home_node_id,
60927
- hn.name AS home_node_name,
60928
- a.owner_id,
60929
- oa.display_name AS owner_name,
60930
- oa.handle AS owner_handle,
60931
- ep.harness,
60932
- ep.transport,
60933
- ep.state,
60934
- ep.project_root,
60935
- ep.cwd,
60936
- ep.session_id,
60937
- ep.metadata_json AS endpoint_metadata_json,
60938
- ${endpointUpdatedAtExpression} AS updated_at
60939
- FROM agents a
60940
- JOIN actors ac ON ac.id = a.id
60941
- LEFT JOIN nodes an ON an.id = a.authority_node_id
60942
- LEFT JOIN nodes hn ON hn.id = a.home_node_id
60943
- LEFT JOIN actors oa ON oa.id = a.owner_id
60944
- ${LATEST_AGENT_ENDPOINT_JOIN}
60945
- WHERE ${activeAgentMetadataPredicate("a")}
60946
- ORDER BY COALESCE(${endpointUpdatedAtExpression}, 0) DESC, ac.display_name ASC
60947
- LIMIT ?`).all(limit);
61359
+ let rows;
61360
+ if (limit < 0) {
61361
+ rows = db2().prepare(`${AGENT_ROW_SELECT}
61362
+ WHERE ${activeAgentMetadataPredicate("a")}
61363
+ ORDER BY COALESCE(${endpointUpdatedAtExpression}, 0) DESC, ac.display_name ASC
61364
+ LIMIT ?`).all(limit);
61365
+ } else {
61366
+ rows = queryAgentRowsByIds(queryAgentIdsForBoundedRoster(limit));
61367
+ }
60948
61368
  return mapAgentRows(rows, flightPhases, existingDirectConversationIdsForAgents(rows.map((row) => row.id)));
60949
61369
  }
60950
61370
  function queryAgentById(agentId) {
60951
61371
  const flightPhases = queryAgentFlightPhases();
60952
- const actorCreatedAtExpression = sqlTimestampMsExpression("ac.created_at");
60953
- const endpointUpdatedAtExpression = sqlTimestampMsExpression("ep.updated_at");
60954
- const row = db2().prepare(`SELECT
60955
- a.id,
60956
- a.definition_id,
60957
- a.node_qualifier,
60958
- a.workspace_qualifier,
60959
- a.selector,
60960
- ac.display_name AS name,
60961
- ac.handle,
60962
- ${actorCreatedAtExpression} AS actor_created_at,
60963
- a.agent_class,
60964
- a.default_selector,
60965
- a.wake_policy,
60966
- a.capabilities_json,
60967
- a.metadata_json,
60968
- a.authority_node_id,
60969
- an.name AS authority_node_name,
60970
- a.home_node_id,
60971
- hn.name AS home_node_name,
60972
- a.owner_id,
60973
- oa.display_name AS owner_name,
60974
- oa.handle AS owner_handle,
60975
- ep.harness,
60976
- ep.transport,
60977
- ep.state,
60978
- ep.project_root,
60979
- ep.cwd,
60980
- ep.session_id,
60981
- ep.metadata_json AS endpoint_metadata_json,
60982
- ${endpointUpdatedAtExpression} AS updated_at
60983
- FROM agents a
60984
- JOIN actors ac ON ac.id = a.id
60985
- LEFT JOIN nodes an ON an.id = a.authority_node_id
60986
- LEFT JOIN nodes hn ON hn.id = a.home_node_id
60987
- LEFT JOIN actors oa ON oa.id = a.owner_id
60988
- ${LATEST_AGENT_ENDPOINT_JOIN}
61372
+ const row = db2().prepare(`${AGENT_ROW_SELECT}
60989
61373
  WHERE a.id = ?
60990
61374
  AND ${activeAgentMetadataPredicate("a")}
60991
61375
  LIMIT 1`).get(agentId);
@@ -61051,12 +61435,70 @@ function mapAgentRows(rows, flightPhases, conversationIdByAgentId) {
61051
61435
  };
61052
61436
  });
61053
61437
  }
61438
+ var actorCreatedAtExpression, endpointUpdatedAtExpression, AGENT_ROW_SELECT, RECENT_ENDPOINT_AGENT_IDS_WITHOUT_INDEX_SQL, RECENT_ENDPOINT_AGENT_IDS_SQL;
61054
61439
  var init_agents = __esm(() => {
61055
61440
  init_src();
61056
61441
  init_db2();
61057
61442
  init_parse2();
61058
61443
  init_paths3();
61059
61444
  init_sql_helpers();
61445
+ actorCreatedAtExpression = sqlTimestampMsExpression("ac.created_at");
61446
+ endpointUpdatedAtExpression = sqlTimestampMsExpression("ep.updated_at");
61447
+ AGENT_ROW_SELECT = `SELECT
61448
+ a.id,
61449
+ a.definition_id,
61450
+ a.node_qualifier,
61451
+ a.workspace_qualifier,
61452
+ a.selector,
61453
+ ac.display_name AS name,
61454
+ ac.handle,
61455
+ ${actorCreatedAtExpression} AS actor_created_at,
61456
+ a.agent_class,
61457
+ a.default_selector,
61458
+ a.wake_policy,
61459
+ a.capabilities_json,
61460
+ a.metadata_json,
61461
+ a.authority_node_id,
61462
+ an.name AS authority_node_name,
61463
+ a.home_node_id,
61464
+ hn.name AS home_node_name,
61465
+ a.owner_id,
61466
+ oa.display_name AS owner_name,
61467
+ oa.handle AS owner_handle,
61468
+ ep.harness,
61469
+ ep.transport,
61470
+ ep.state,
61471
+ ep.project_root,
61472
+ ep.cwd,
61473
+ ep.session_id,
61474
+ ep.metadata_json AS endpoint_metadata_json,
61475
+ ${endpointUpdatedAtExpression} AS updated_at
61476
+ FROM agents a
61477
+ JOIN actors ac ON ac.id = a.id
61478
+ LEFT JOIN nodes an ON an.id = a.authority_node_id
61479
+ LEFT JOIN nodes hn ON hn.id = a.home_node_id
61480
+ LEFT JOIN actors oa ON oa.id = a.owner_id
61481
+ ${LATEST_AGENT_ENDPOINT_JOIN}`;
61482
+ RECENT_ENDPOINT_AGENT_IDS_WITHOUT_INDEX_SQL = (() => {
61483
+ const endpointUpdatedAtExpression2 = sqlTimestampMsExpression("ep.updated_at");
61484
+ const latestEndpointUpdatedAtExpression = sqlTimestampMsCoalesceExpression("ep2.updated_at");
61485
+ return `SELECT a.id
61486
+ FROM agent_endpoints ep
61487
+ JOIN agents a ON a.id = ep.agent_id
61488
+ JOIN actors ac ON ac.id = a.id
61489
+ WHERE ep.id = (
61490
+ SELECT ep2.id
61491
+ FROM agent_endpoints ep2
61492
+ WHERE ep2.agent_id = a.id
61493
+ ORDER BY ${latestEndpointUpdatedAtExpression} DESC
61494
+ LIMIT 1
61495
+ )
61496
+ AND ${endpointUpdatedAtExpression2} > 0
61497
+ AND ${activeAgentMetadataPredicate("a")}
61498
+ ORDER BY ${endpointUpdatedAtExpression2} DESC, ac.display_name ASC
61499
+ LIMIT ?`;
61500
+ })();
61501
+ RECENT_ENDPOINT_AGENT_IDS_SQL = RECENT_ENDPOINT_AGENT_IDS_WITHOUT_INDEX_SQL.replace("FROM agent_endpoints ep", "FROM agent_endpoints ep INDEXED BY idx_agent_endpoints_roster_recency");
61060
61502
  });
61061
61503
 
61062
61504
  // packages/web/server/db/internal/conversation-ids.ts
@@ -61148,7 +61590,11 @@ function queryRecentMessages(limit = 80, opts) {
61148
61590
  const beforeMessage = resolveBeforeMessage(opts?.beforeMessageId, conversationIds, messageCreatedAtExpression);
61149
61591
  const where = sqlJoinClauses([
61150
61592
  transientBrokerWorkingStatusPredicate("m"),
61151
- conversationIds.length > 0 ? `m.conversation_id IN (${sqlPlaceholders(conversationIds.length)})` : "m.conversation_id LIKE 'c.%' AND length(m.conversation_id) > 2",
61593
+ conversationIds.length > 0 ? `m.conversation_id IN (${sqlPlaceholders(conversationIds.length)})` : `(
61594
+ (m.conversation_id LIKE 'chn-%' AND length(m.conversation_id) > 4)
61595
+ OR (substr(m.conversation_id, 1, 5) = 'chat_' AND length(m.conversation_id) > 5)
61596
+ OR (m.conversation_id LIKE 'c.%' AND length(m.conversation_id) > 2)
61597
+ )`,
61152
61598
  beforeMessage ? `(
61153
61599
  ${messageCreatedAtExpression} < ?
61154
61600
  OR (${messageCreatedAtExpression} = ? AND m.id < ?)
@@ -62747,6 +63193,30 @@ function metadataString8(metadata, key) {
62747
63193
  const value = metadata[key];
62748
63194
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
62749
63195
  }
63196
+ function executionResolutionDimension(resolution, dimension) {
63197
+ const value = resolution?.[dimension];
63198
+ return value?.observed?.trim() || value?.resolved?.trim() || value?.requested?.trim() || null;
63199
+ }
63200
+ function durableConversationRuntime(row) {
63201
+ if (!row)
63202
+ return null;
63203
+ const execution = parseMetadataJson(row.execution_json);
63204
+ const rawResolution = parseMetadataJson(row.execution_resolution_json);
63205
+ const resolution = rawResolution.schemaVersion === "openscout.execution-resolution.v1" ? rawResolution : null;
63206
+ const flightMetadata = parseMetadataJson(row.flight_metadata_json);
63207
+ const trace = Array.isArray(flightMetadata.sessionTrace) ? [...flightMetadata.sessionTrace].reverse().find((entry) => entry && typeof entry === "object" && !Array.isArray(entry)) : undefined;
63208
+ const traceResolutionRaw = trace?.executionResolution;
63209
+ const traceResolution = traceResolutionRaw && typeof traceResolutionRaw === "object" && !Array.isArray(traceResolutionRaw) && traceResolutionRaw.schemaVersion === "openscout.execution-resolution.v1" ? traceResolutionRaw : null;
63210
+ const effectiveResolution = traceResolution ?? resolution;
63211
+ return {
63212
+ targetAgentId: row.target_agent_id,
63213
+ harness: executionResolutionDimension(effectiveResolution, "harness") ?? metadataString8(trace ?? {}, "harness") ?? metadataString8(execution, "harness"),
63214
+ model: executionResolutionDimension(effectiveResolution, "model") ?? metadataString8(execution, "model"),
63215
+ reasoningEffort: executionResolutionDimension(effectiveResolution, "reasoningEffort") ?? metadataString8(execution, "reasoningEffort"),
63216
+ transport: metadataString8(trace ?? {}, "transport"),
63217
+ sessionId: metadataString8(trace ?? {}, "sessionId") ?? resolution?.sessionId?.trim() ?? metadataString8(execution, "targetSessionId")
63218
+ };
63219
+ }
62750
63220
  function formatChannelAlias(value) {
62751
63221
  const trimmed = value.trim();
62752
63222
  if (!trimmed)
@@ -62798,22 +63268,34 @@ function projectSessionConversationRows(rows, opts) {
62798
63268
  AND ${transientBrokerWorkingStatusPredicate("m")}
62799
63269
  ORDER BY ${messageCreatedAtExpression} DESC
62800
63270
  LIMIT 1`);
63271
+ const runtimeStmt = db2().prepare(`SELECT target_agent_id, execution_json, execution_resolution_json, flight_metadata_json
63272
+ FROM invocations
63273
+ WHERE conversation_id = ?
63274
+ ORDER BY ${sqlTimestampMsExpression("created_at")} DESC, id DESC
63275
+ LIMIT 1`);
62801
63276
  const summaries = rows.flatMap((r) => {
62802
63277
  const conversationMetadata = parseMetadataJson(r.metadata_json);
62803
63278
  const participants = memberStmt.all(r.id).map((m) => m.actor_id);
62804
63279
  const agentParticipants = agentMemberStmt.all(r.id);
62805
- const primaryAgentId = r.kind === "direct" ? pickDirectConversationAgentId(participants, agentParticipants.map((entry) => entry.agent_id)) : agentParticipants.length === 1 ? agentParticipants[0]?.agent_id ?? null : null;
63280
+ const runtime = durableConversationRuntime(runtimeStmt.get(r.id));
63281
+ const runtimeTargetAgentId = runtime?.targetAgentId && agentParticipants.some((entry) => entry.agent_id === runtime.targetAgentId) ? runtime.targetAgentId : null;
63282
+ const primaryAgentId = r.kind === "direct" ? runtimeTargetAgentId ?? pickDirectConversationAgentId(participants, agentParticipants.map((entry) => entry.agent_id)) : agentParticipants.length === 1 ? agentParticipants[0]?.agent_id ?? null : null;
62806
63283
  const primaryAgent = primaryAgentId ? agentParticipants.find((entry) => entry.agent_id === primaryAgentId) ?? null : null;
62807
63284
  const agentId = primaryAgent?.agent_id ?? null;
62808
63285
  let agentName = null;
62809
- let harness2 = null;
63286
+ let harness2 = runtime?.harness ?? null;
63287
+ let model2 = runtime?.model ?? null;
63288
+ let reasoningEffort = runtime?.reasoningEffort ?? null;
63289
+ let transport = runtime?.transport ?? null;
63290
+ let sessionId = runtime?.sessionId ?? null;
62810
63291
  let harnessSessionId = null;
62811
63292
  let harnessLogPath = null;
62812
63293
  let branch = null;
62813
63294
  let workspaceRoot = null;
62814
63295
  if (primaryAgent) {
62815
63296
  agentName = primaryAgent.display_name;
62816
- harness2 = primaryAgent.harness;
63297
+ harness2 = harness2 ?? primaryAgent.harness;
63298
+ transport = transport ?? primaryAgent.transport;
62817
63299
  workspaceRoot = compact(primaryAgent.project_root);
62818
63300
  try {
62819
63301
  const meta = primaryAgent.metadata_json ? JSON.parse(primaryAgent.metadata_json) : {};
@@ -62821,13 +63303,19 @@ function projectSessionConversationRows(rows, opts) {
62821
63303
  return [];
62822
63304
  }
62823
63305
  branch = meta.branch ?? meta.workspaceQualifier ?? null;
63306
+ model2 = model2 ?? metadataString8(meta, "model");
63307
+ reasoningEffort = reasoningEffort ?? metadataString8(meta, "reasoningEffort");
62824
63308
  } catch {}
62825
63309
  try {
62826
63310
  const endpointMeta = primaryAgent.endpoint_metadata_json ? JSON.parse(primaryAgent.endpoint_metadata_json) : {};
62827
63311
  harnessSessionId = resolveHarnessSessionId(primaryAgent.transport, primaryAgent.session_id, endpointMeta);
63312
+ sessionId = sessionId ?? harnessSessionId;
63313
+ model2 = model2 ?? metadataString8(endpointMeta, "observedModel") ?? metadataString8(endpointMeta, "model");
63314
+ reasoningEffort = reasoningEffort ?? metadataString8(endpointMeta, "observedReasoningEffort") ?? metadataString8(endpointMeta, "reasoningEffort");
62828
63315
  harnessLogPath = resolveHarnessLogPath(primaryAgent.agent_id, primaryAgent.transport, primaryAgent.session_id, endpointMeta);
62829
63316
  } catch {
62830
63317
  harnessSessionId = resolveHarnessSessionId(primaryAgent.transport, primaryAgent.session_id, undefined);
63318
+ sessionId = sessionId ?? harnessSessionId;
62831
63319
  harnessLogPath = resolveHarnessLogPath(primaryAgent.agent_id, primaryAgent.transport, primaryAgent.session_id, undefined);
62832
63320
  }
62833
63321
  }
@@ -62842,6 +63330,10 @@ function projectSessionConversationRows(rows, opts) {
62842
63330
  agentId,
62843
63331
  agentName,
62844
63332
  harness: harness2,
63333
+ model: model2,
63334
+ reasoningEffort,
63335
+ transport,
63336
+ sessionId,
62845
63337
  harnessSessionId,
62846
63338
  harnessLogPath,
62847
63339
  currentBranch: branch,
@@ -62898,7 +63390,9 @@ function querySessions(limit = 80) {
62898
63390
  FROM messages
62899
63391
  GROUP BY conversation_id
62900
63392
  ) ms ON ms.conversation_id = c.id
62901
- WHERE c.id LIKE 'c.%' AND length(c.id) > 2
63393
+ WHERE (c.id LIKE 'chn-%' AND length(c.id) > 4)
63394
+ OR (substr(c.id, 1, 5) = 'chat_' AND length(c.id) > 5)
63395
+ OR (c.id LIKE 'c.%' AND length(c.id) > 2)
62902
63396
  ORDER BY COALESCE(ms.last_message_at, ${conversationCreatedAtExpression}, 0) DESC, ${conversationCreatedAtExpression} DESC, c.id ASC
62903
63397
  LIMIT ?`).all(limit);
62904
63398
  return projectSessionConversationRows(rows, {
@@ -62926,6 +63420,7 @@ function querySessionFallbackFromMessages(conversationId) {
62926
63420
  m.actor_id AS agent_id,
62927
63421
  ac.display_name,
62928
63422
  ep.harness,
63423
+ ep.transport,
62929
63424
  ep.project_root,
62930
63425
  ep.session_id,
62931
63426
  ep.metadata_json AS endpoint_metadata_json
@@ -62947,8 +63442,12 @@ function querySessionFallbackFromMessages(conversationId) {
62947
63442
  agentId: agentRow?.agent_id ?? null,
62948
63443
  agentName: agentRow?.display_name ?? null,
62949
63444
  harness: agentRow?.harness ?? null,
62950
- harnessSessionId: resolveHarnessSessionId(agentRow?.session_id, endpointMetadata),
62951
- harnessLogPath: agentRow?.agent_id ? resolveHarnessLogPath(agentRow.agent_id, agentRow.harness, agentRow.session_id, endpointMetadata) : null,
63445
+ model: metadataString8(endpointMetadata, "observedModel") ?? metadataString8(endpointMetadata, "model"),
63446
+ reasoningEffort: metadataString8(endpointMetadata, "observedReasoningEffort") ?? metadataString8(endpointMetadata, "reasoningEffort"),
63447
+ transport: agentRow?.transport ?? null,
63448
+ sessionId: resolveHarnessSessionId(agentRow?.transport ?? null, agentRow?.session_id ?? null, endpointMetadata),
63449
+ harnessSessionId: resolveHarnessSessionId(agentRow?.transport ?? null, agentRow?.session_id ?? null, endpointMetadata),
63450
+ harnessLogPath: agentRow?.agent_id ? resolveHarnessLogPath(agentRow.agent_id, agentRow.transport, agentRow.session_id, endpointMetadata) : null,
62952
63451
  currentBranch: metadataString8(endpointMetadata, "branch"),
62953
63452
  preview: previewRow?.body?.trim() || null,
62954
63453
  messageCount: stats.message_count,
@@ -64000,8 +64499,8 @@ function queryFleetAskRows(requesterIds, limit) {
64000
64499
  ep.state AS endpoint_state,
64001
64500
  cr.title AS work_title,
64002
64501
  cr.summary AS work_summary,
64502
+ cr.kind AS record_kind,
64003
64503
  cr.state AS work_state,
64004
- cr.acceptance_state,
64005
64504
  cr.next_move_owner_id,
64006
64505
  cr.updated_at AS work_updated_at
64007
64506
  FROM invocations inv
@@ -64063,7 +64562,8 @@ function projectFleetAsk(row, requesterIdSet) {
64063
64562
  const queuedUntilOnline = queuedFlight && row.dispatch_outcome_status === "queued_until_online";
64064
64563
  const staleActiveFlight = hasFlight && row.flight_state !== null && !TERMINAL_FLIGHT_STATES.has(row.flight_state) && isStaleActiveFlight(row.started_at, row.created_at);
64065
64564
  const isActiveFlight = hasFlight && row.flight_state !== null && !TERMINAL_FLIGHT_STATES.has(row.flight_state) && !failed && !staleActiveFlight;
64066
- const awaitingOperator = Boolean(row.next_move_owner_id && requesterIdSet.has(row.next_move_owner_id) || row.acceptance_state === "pending");
64565
+ const attentionEligibleState = row.record_kind === "work_item" && row.work_state !== null && ["open", "working", "waiting", "review"].includes(row.work_state) || row.record_kind === "question" && row.work_state !== null && ["open", "answered"].includes(row.work_state);
64566
+ const awaitingOperator = attentionEligibleState && Boolean(row.next_move_owner_id && requesterIdSet.has(row.next_move_owner_id));
64067
64567
  const updatedAt = normalizeTimestampMs(row.status_ts ?? row.completed_at ?? row.started_at ?? row.work_updated_at ?? row.created_at) ?? Date.now();
64068
64568
  const dismissedAt = normalizeTimestampMs(row.flight_dismissed_at);
64069
64569
  const recoveredAfterFailureAt = normalizeTimestampMs(row.recovered_after_failure_at);
@@ -64076,10 +64576,10 @@ function projectFleetAsk(row, requesterIdSet) {
64076
64576
  status = queuedUntilOnline ? "failed" : "queued";
64077
64577
  } else if (isActiveFlight) {
64078
64578
  status = "working";
64079
- } else if (awaitingOperator) {
64080
- status = "needs_attention";
64081
64579
  } else if (failed || staleActiveFlight) {
64082
64580
  status = "failed";
64581
+ } else if (awaitingOperator) {
64582
+ status = "needs_attention";
64083
64583
  } else {
64084
64584
  status = "completed";
64085
64585
  }
@@ -64123,34 +64623,7 @@ function queryFleetAttentionRows(requesterIds, limit) {
64123
64623
  (cr.kind = 'work_item' AND cr.state IN ('open', 'working', 'waiting', 'review'))
64124
64624
  OR (cr.kind = 'question' AND cr.state IN ('open', 'answered'))
64125
64625
  )
64126
- AND (
64127
- cr.next_move_owner_id IN (${requesterClause})
64128
- OR (
64129
- cr.kind = 'work_item'
64130
- AND cr.state = 'review'
64131
- AND cr.acceptance_state = 'pending'
64132
- AND NOT EXISTS (
64133
- SELECT 1
64134
- FROM collaboration_events e
64135
- WHERE e.record_id = cr.id
64136
- AND e.created_at > cr.updated_at
64137
- )
64138
- AND NOT EXISTS (
64139
- SELECT 1
64140
- FROM invocations inv
64141
- WHERE inv.collaboration_record_id = cr.id
64142
- AND inv.flight_id IS NOT NULL
64143
- AND COALESCE(inv.completed_at, inv.started_at, 0) > cr.updated_at
64144
- )
64145
- AND NOT EXISTS (
64146
- SELECT 1
64147
- FROM messages m
64148
- WHERE m.conversation_id = cr.conversation_id
64149
- AND m.actor_id = cr.owner_id
64150
- AND m.created_at > cr.updated_at
64151
- )
64152
- )
64153
- )
64626
+ AND cr.next_move_owner_id IN (${requesterClause})
64154
64627
  AND NOT EXISTS (
64155
64628
  SELECT 1
64156
64629
  FROM collaboration_events dismissed
@@ -66101,7 +66574,7 @@ var init_service2 = __esm(() => {
66101
66574
  // packages/web/server/service-budgets.ts
66102
66575
  import { createHash as createHash8 } from "crypto";
66103
66576
  import { existsSync as existsSync45, mkdirSync as mkdirSync19, readdirSync as readdirSync12, readFileSync as readFileSync27, renameSync as renameSync7, statSync as statSync18, writeFileSync as writeFileSync17 } from "fs";
66104
- import { open as open2 } from "fs/promises";
66577
+ import { open as open2, readdir as readdir4, stat as stat10 } from "fs/promises";
66105
66578
  import { homedir as homedir45 } from "os";
66106
66579
  import { dirname as dirname35, join as join63 } from "path";
66107
66580
  import { createInterface as createInterface2 } from "readline";
@@ -66181,12 +66654,14 @@ async function loadCodexGauge(forceRefresh = false) {
66181
66654
  const root = join63(homeDir(), ".codex", "sessions");
66182
66655
  if (!existsSync45(root))
66183
66656
  return fallback;
66184
- const recent = findRecentCodexJsonl(root, CODEX_LOOKBACK_DAYS);
66185
- if (recent.length === 0)
66657
+ const candidates = await findRecentCodexJsonl(root, CODEX_LOOKBACK_DAYS);
66658
+ if (candidates.length === 0)
66186
66659
  return fallback;
66187
- const observations = (await Promise.all(recent.map((path2) => readLatestCodexRateLimits(path2)))).flat().sort((left, right) => left.capturedAt - right.capturedAt);
66660
+ const observations = (await harvestRecentCodexRateLimits(candidates)).sort((left, right) => left.capturedAt - right.capturedAt);
66188
66661
  if (observations.length === 0)
66189
66662
  return fallback;
66663
+ if (!observations.some(isCodexAccountPoolObservation))
66664
+ return fallback;
66190
66665
  const snapshots = bindingCodexPoolSnapshots(observations.flatMap((observation) => codexQuotaSnapshotsFromObservation(observation.usage, observation.capturedAt)));
66191
66666
  persistQuotaSnapshots(snapshots);
66192
66667
  const persisted = loadPersistedProviderQuotaGauge({
@@ -66243,9 +66718,11 @@ function bindingCodexPoolSnapshots(snapshots) {
66243
66718
  const usedPercent = quotaSnapshotUsage(row)?.fill;
66244
66719
  if (usedPercent === undefined)
66245
66720
  continue;
66721
+ const poolId = poolIdOf(row);
66722
+ const accountPool = isCodexAccountPoolId(poolId);
66246
66723
  const best = bindingPoolByLabel.get(row.label);
66247
- if (!best || usedPercent > best.usedPercent || usedPercent === best.usedPercent && row.capturedAt > best.capturedAt) {
66248
- bindingPoolByLabel.set(row.label, { poolId: poolIdOf(row), usedPercent, capturedAt: row.capturedAt });
66724
+ if (!best || usedPercent > best.usedPercent || usedPercent === best.usedPercent && accountPool && !best.accountPool || usedPercent === best.usedPercent && accountPool === best.accountPool && row.capturedAt > best.capturedAt) {
66725
+ bindingPoolByLabel.set(row.label, { poolId, usedPercent, capturedAt: row.capturedAt, accountPool });
66249
66726
  }
66250
66727
  }
66251
66728
  return snapshots.filter((row) => bindingPoolByLabel.get(row.label)?.poolId === poolIdOf(row));
@@ -66262,37 +66739,83 @@ function formatQuotaWindowLabel(windowMinutes, fallback) {
66262
66739
  }
66263
66740
  return `${windowMinutes}m`;
66264
66741
  }
66265
- function findRecentCodexJsonl(root, lookbackDays) {
66742
+ async function findRecentCodexJsonl(root, lookbackDays) {
66266
66743
  const cutoffMs = Date.now() - lookbackDays * 86400 * 1000;
66267
66744
  const paths2 = [];
66268
- const walk = (dir) => {
66745
+ const pending = [root];
66746
+ while (pending.length > 0) {
66747
+ const dir = pending.pop();
66748
+ if (!dir)
66749
+ continue;
66269
66750
  let entries2;
66270
66751
  try {
66271
- entries2 = readdirSync12(dir, { withFileTypes: true });
66752
+ entries2 = await readdir4(dir, { withFileTypes: true });
66272
66753
  } catch {
66273
- return;
66754
+ continue;
66274
66755
  }
66275
66756
  for (const entry of entries2) {
66276
66757
  const full = join63(dir, entry.name);
66277
66758
  if (entry.isDirectory()) {
66278
- walk(full);
66759
+ pending.push(full);
66279
66760
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
66280
- const stat10 = safeStat(full);
66281
- if (!stat10 || stat10.mtimeMs < cutoffMs)
66282
- continue;
66283
- paths2.push({ path: full, mtimeMs: stat10.mtimeMs });
66761
+ try {
66762
+ const fileStat = await stat10(full);
66763
+ if (fileStat.mtimeMs < cutoffMs)
66764
+ continue;
66765
+ paths2.push({ path: full, mtimeMs: fileStat.mtimeMs });
66766
+ } catch {}
66284
66767
  }
66285
66768
  }
66286
- };
66287
- walk(root);
66288
- return paths2.sort((left, right) => right.mtimeMs - left.mtimeMs).map((entry) => entry.path);
66769
+ }
66770
+ return paths2.sort((left, right) => right.mtimeMs - left.mtimeMs).slice(0, CODEX_RECENT_CANDIDATE_LIMIT);
66771
+ }
66772
+ async function harvestRecentCodexRateLimits(candidates) {
66773
+ const observations = await readCodexCandidateBatch(candidates.slice(0, CODEX_INITIAL_FILE_LIMIT));
66774
+ if (observations.some(isCodexAccountPoolObservation))
66775
+ return observations;
66776
+ const overflow = candidates.slice(CODEX_INITIAL_FILE_LIMIT, CODEX_RECENT_CANDIDATE_LIMIT);
66777
+ for (let index2 = 0;index2 < overflow.length; index2 += CODEX_JSONL_READ_CONCURRENCY) {
66778
+ observations.push(...await readCodexCandidateBatch(overflow.slice(index2, index2 + CODEX_JSONL_READ_CONCURRENCY)));
66779
+ if (observations.some(isCodexAccountPoolObservation))
66780
+ break;
66781
+ }
66782
+ return observations;
66783
+ }
66784
+ async function readCodexCandidateBatch(candidates) {
66785
+ return (await Promise.all(candidates.map(async (candidate) => {
66786
+ try {
66787
+ return await readLatestCodexRateLimits(candidate.path);
66788
+ } catch {
66789
+ return [];
66790
+ }
66791
+ }))).flat();
66792
+ }
66793
+ function isCodexAccountPoolObservation(observation) {
66794
+ return isCodexAccountPoolId(observation.usage.limitId);
66795
+ }
66796
+ function isCodexAccountPoolId(limitId) {
66797
+ const normalized = limitId?.trim().toLowerCase();
66798
+ return !normalized || normalized === "codex";
66289
66799
  }
66290
66800
  async function readLatestCodexRateLimits(path2) {
66291
66801
  const handle = await open2(path2, "r");
66292
66802
  try {
66293
- const rl = createInterface2({ input: handle.createReadStream({ encoding: "utf8" }) });
66803
+ const fileStat = await handle.stat();
66804
+ const start = Math.max(0, fileStat.size - CODEX_JSONL_TAIL_MAX_BYTES);
66805
+ const rl = createInterface2({
66806
+ input: handle.createReadStream({
66807
+ encoding: "utf8",
66808
+ start,
66809
+ autoClose: false
66810
+ })
66811
+ });
66294
66812
  const latestByPool = new Map;
66813
+ let firstLine = start > 0;
66295
66814
  for await (const line of rl) {
66815
+ if (firstLine) {
66816
+ firstLine = false;
66817
+ continue;
66818
+ }
66296
66819
  if (!line.includes('"rate_limits"'))
66297
66820
  continue;
66298
66821
  try {
@@ -66369,8 +66892,8 @@ function loadClaudeStatuslineQuotaSnapshots() {
66369
66892
  }
66370
66893
  function readClaudeStatuslineHistory(minCapturedAt) {
66371
66894
  const path2 = join63(claudeStatuslineDir(), "claude-history.jsonl");
66372
- const stat10 = safeStat(path2);
66373
- if (!stat10)
66895
+ const stat11 = safeStat(path2);
66896
+ if (!stat11)
66374
66897
  return [];
66375
66898
  let content;
66376
66899
  try {
@@ -66378,7 +66901,7 @@ function readClaudeStatuslineHistory(minCapturedAt) {
66378
66901
  } catch {
66379
66902
  return [];
66380
66903
  }
66381
- if (stat10.size > CLAUDE_STATUSLINE_HISTORY_MAX_BYTES) {
66904
+ if (stat11.size > CLAUDE_STATUSLINE_HISTORY_MAX_BYTES) {
66382
66905
  content = content.slice(-CLAUDE_STATUSLINE_HISTORY_MAX_BYTES);
66383
66906
  const firstNewline = content.indexOf(`
66384
66907
  `);
@@ -67078,11 +67601,11 @@ async function loadGrokGauge() {
67078
67601
  }
67079
67602
  async function loadGrokBillingQuotaSnapshots() {
67080
67603
  const path2 = join63(homeDir(), ".grok", "logs", "unified.jsonl");
67081
- const stat10 = safeStat(path2);
67082
- if (!stat10 || stat10.size <= 0)
67604
+ const stat11 = safeStat(path2);
67605
+ if (!stat11 || stat11.size <= 0)
67083
67606
  return [];
67084
- const length = Math.min(stat10.size, GROK_BILLING_LOG_MAX_BYTES);
67085
- const start = Math.max(0, stat10.size - length);
67607
+ const length = Math.min(stat11.size, GROK_BILLING_LOG_MAX_BYTES);
67608
+ const start = Math.max(0, stat11.size - length);
67086
67609
  let content = "";
67087
67610
  let handle;
67088
67611
  try {
@@ -67609,11 +68132,13 @@ function persistQuotaSnapshots(snapshots) {
67609
68132
  return;
67610
68133
  statement.run(id, snapshot.source ?? "provider_reported", snapshot.provider ?? null, snapshot.harness ?? null, snapshot.transport ?? null, snapshot.planType ?? null, snapshot.label, snapshot.windowKind ?? null, finiteNumber(snapshot.usedPercent) ?? null, finiteNumber(snapshot.percentRemaining) ?? null, finiteNumber(snapshot.used) ?? null, finiteNumber(snapshot.limitValue) ?? null, finiteNumber(snapshot.resetAt) ?? null, finiteNumber(snapshot.windowMs) ?? null, snapshot.capturedAt, JSON.stringify(metadata ?? {}), createdAt);
67611
68134
  };
67612
- for (const snapshot of snapshots) {
67613
- writeSnapshot(quotaSnapshotId(snapshot), snapshot, snapshot.metadata);
67614
- writeSnapshot(quotaHistorySnapshotId(snapshot), snapshot, quotaHistoryMetadata(snapshot));
67615
- }
67616
- pruneHistory.run(createdAt - QUOTA_HISTORY_LOOKBACK_MS - QUOTA_HISTORY_BUCKET_MS2);
68135
+ writer.transaction(() => {
68136
+ for (const snapshot of snapshots) {
68137
+ writeSnapshot(quotaSnapshotId(snapshot), snapshot, snapshot.metadata);
68138
+ writeSnapshot(quotaHistorySnapshotId(snapshot), snapshot, quotaHistoryMetadata(snapshot));
68139
+ }
68140
+ pruneHistory.run(createdAt - QUOTA_HISTORY_LOOKBACK_MS - QUOTA_HISTORY_BUCKET_MS2);
68141
+ })();
67617
68142
  } catch {}
67618
68143
  }
67619
68144
  function quotaSnapshotMayReplace(existing, incoming) {
@@ -67718,7 +68243,7 @@ function safeStat(path2) {
67718
68243
  return null;
67719
68244
  }
67720
68245
  }
67721
- var CACHE_TTL_MS, WEEK_MS, CODEX_LOOKBACK_DAYS = 3, LOCAL_QUOTA_FRESH_MS, REMOTE_QUOTA_FRESH_MS, GH_CLI_TIMEOUT_MS = 4000, KIMI_USAGE_TIMEOUT_MS = 4000, MINIMAX_REMAINS_TIMEOUT_MS = 4000, DB_BUSY_TIMEOUT_MS3 = 2500, QUOTA_HISTORY_BUCKET_MS2, QUOTA_HISTORY_LOOKBACK_MS, QUOTA_HISTORY_ID_PREFIX = "budget:quota:history:", PERSISTED_QUOTA_ROW_LIMIT = 768, CLAUDE_STATUSLINE_HISTORY_MAX_BYTES, GROK_BILLING_LOG_MAX_BYTES, GH_CLI_BIN_ENV = "OPENSCOUT_GH_BIN", GH_RATE_LIMIT_JSON_ENV = "OPENSCOUT_GH_RATE_LIMIT_JSON", KIMI_USAGE_JSON_ENV = "OPENSCOUT_KIMI_USAGE_JSON", CURSOR_STATUS_JSON_ENV = "OPENSCOUT_CURSOR_STATUS_JSON", GROK_USAGE_JSON_ENV = "OPENSCOUT_GROK_USAGE_JSON", MINIMAX_REMAINS_JSON_ENV = "OPENSCOUT_MINIMAX_REMAINS_JSON", DASHBOARD_IMPORT_MAX_CHARS, cached = null, inflightNormal = null, inflightForce = null, quotaWriteDb = null;
68246
+ var CACHE_TTL_MS, WEEK_MS, CODEX_LOOKBACK_DAYS = 3, CODEX_JSONL_TAIL_MAX_BYTES, CODEX_INITIAL_FILE_LIMIT = 32, CODEX_RECENT_CANDIDATE_LIMIT = 64, CODEX_JSONL_READ_CONCURRENCY = 8, LOCAL_QUOTA_FRESH_MS, REMOTE_QUOTA_FRESH_MS, GH_CLI_TIMEOUT_MS = 4000, KIMI_USAGE_TIMEOUT_MS = 4000, MINIMAX_REMAINS_TIMEOUT_MS = 4000, DB_BUSY_TIMEOUT_MS3 = 2500, QUOTA_HISTORY_BUCKET_MS2, QUOTA_HISTORY_LOOKBACK_MS, QUOTA_HISTORY_ID_PREFIX = "budget:quota:history:", PERSISTED_QUOTA_ROW_LIMIT = 768, CLAUDE_STATUSLINE_HISTORY_MAX_BYTES, GROK_BILLING_LOG_MAX_BYTES, GH_CLI_BIN_ENV = "OPENSCOUT_GH_BIN", GH_RATE_LIMIT_JSON_ENV = "OPENSCOUT_GH_RATE_LIMIT_JSON", KIMI_USAGE_JSON_ENV = "OPENSCOUT_KIMI_USAGE_JSON", CURSOR_STATUS_JSON_ENV = "OPENSCOUT_CURSOR_STATUS_JSON", GROK_USAGE_JSON_ENV = "OPENSCOUT_GROK_USAGE_JSON", MINIMAX_REMAINS_JSON_ENV = "OPENSCOUT_MINIMAX_REMAINS_JSON", DASHBOARD_IMPORT_MAX_CHARS, cached = null, inflightNormal = null, inflightForce = null, quotaWriteDb = null;
67722
68247
  var init_service_budgets = __esm(() => {
67723
68248
  init_src2();
67724
68249
  init_src();
@@ -67728,6 +68253,7 @@ var init_service_budgets = __esm(() => {
67728
68253
  init_db2();
67729
68254
  CACHE_TTL_MS = 60 * 1000;
67730
68255
  WEEK_MS = 7 * 24 * 3600 * 1000;
68256
+ CODEX_JSONL_TAIL_MAX_BYTES = 2 * 1024 * 1024;
67731
68257
  LOCAL_QUOTA_FRESH_MS = 2 * 60 * 1000;
67732
68258
  REMOTE_QUOTA_FRESH_MS = 5 * 60 * 1000;
67733
68259
  QUOTA_HISTORY_BUCKET_MS2 = 60 * 60 * 1000;
@@ -69159,9 +69685,9 @@ function readFilePreview(input) {
69159
69685
  }
69160
69686
  const { realPath, root } = resolved;
69161
69687
  try {
69162
- const stat11 = statSync22(realPath);
69688
+ const stat12 = statSync22(realPath);
69163
69689
  const title = realPath === "/" ? "/" : realPath.split("/").pop() || realPath;
69164
- if (stat11.isDirectory()) {
69690
+ if (stat12.isDirectory()) {
69165
69691
  return {
69166
69692
  ok: true,
69167
69693
  content: {
@@ -69177,7 +69703,7 @@ function readFilePreview(input) {
69177
69703
  }
69178
69704
  };
69179
69705
  }
69180
- if (!stat11.isFile()) {
69706
+ if (!stat12.isFile()) {
69181
69707
  return { ok: false, status: 415, error: "path is not a file or directory" };
69182
69708
  }
69183
69709
  const mediaType = mediaTypeFor(realPath);
@@ -69194,14 +69720,14 @@ function readFilePreview(input) {
69194
69720
  title,
69195
69721
  mediaType,
69196
69722
  rawUrl,
69197
- sizeBytes: stat11.size,
69723
+ sizeBytes: stat12.size,
69198
69724
  previewReason: previewReasonFor(mediaType),
69199
69725
  generatedAt: Date.now()
69200
69726
  }
69201
69727
  };
69202
69728
  }
69203
- const truncated = stat11.size > MAX_PREVIEW_BYTES;
69204
- const readable = readPreviewBuffer(realPath, Math.min(stat11.size, MAX_PREVIEW_BYTES));
69729
+ const truncated = stat12.size > MAX_PREVIEW_BYTES;
69730
+ const readable = readPreviewBuffer(realPath, Math.min(stat12.size, MAX_PREVIEW_BYTES));
69205
69731
  if (!looksTextual2(realPath, readable)) {
69206
69732
  return {
69207
69733
  ok: true,
@@ -69214,7 +69740,7 @@ function readFilePreview(input) {
69214
69740
  title,
69215
69741
  mediaType,
69216
69742
  rawUrl,
69217
- sizeBytes: stat11.size,
69743
+ sizeBytes: stat12.size,
69218
69744
  previewReason: previewReasonFor(mediaType),
69219
69745
  generatedAt: Date.now()
69220
69746
  }
@@ -69232,7 +69758,7 @@ function readFilePreview(input) {
69232
69758
  mediaType,
69233
69759
  rawUrl,
69234
69760
  content: readable.toString("utf8"),
69235
- sizeBytes: stat11.size,
69761
+ sizeBytes: stat12.size,
69236
69762
  truncated,
69237
69763
  generatedAt: Date.now()
69238
69764
  }
@@ -71432,7 +71958,7 @@ __export(exports_agent_definitions, {
71432
71958
  resolveAgentProjectRoot: () => resolveAgentProjectRoot,
71433
71959
  buildAgentDefinitions: () => buildAgentDefinitions
71434
71960
  });
71435
- import { access as access3, readdir as readdir5, readFile as readFile19 } from "fs/promises";
71961
+ import { access as access3, readdir as readdir6, readFile as readFile19 } from "fs/promises";
71436
71962
  import { join as join71 } from "path";
71437
71963
  function excerptFromText2(raw2) {
71438
71964
  const trimmed = raw2.trim();
@@ -71476,7 +72002,7 @@ function agentSlugFromHandle(handle, name2) {
71476
72002
  return fromName || null;
71477
72003
  }
71478
72004
  async function listFolderFiles(folderPath, slug) {
71479
- const entries2 = await readdir5(folderPath, { withFileTypes: true });
72005
+ const entries2 = await readdir6(folderPath, { withFileTypes: true });
71480
72006
  const files = [];
71481
72007
  for (const entry of entries2) {
71482
72008
  if (!entry.isFile())
@@ -71516,7 +72042,7 @@ async function buildAgentDefinitions(input) {
71516
72042
  const selectedSlug = agentSlugFromHandle(input.agentHandle, input.agentName ?? "");
71517
72043
  const folders = [];
71518
72044
  if (agentsRootExists) {
71519
- const entries2 = await readdir5(agentsRoot, { withFileTypes: true });
72045
+ const entries2 = await readdir6(agentsRoot, { withFileTypes: true });
71520
72046
  for (const entry of entries2) {
71521
72047
  if (!entry.isDirectory() || SKIPPED_NAMES.has(entry.name) || entry.name.startsWith(".")) {
71522
72048
  continue;
@@ -73133,8 +73659,55 @@ function assertIsolatedPairingRunStateWrite(path) {
73133
73659
  }
73134
73660
 
73135
73661
  // packages/web/server/pairing-pair-requests.ts
73662
+ var SCOUT_PAIR_CLIENT_HEADER = "x-scout-client";
73663
+ var SCOUT_PAIR_RELAY_HEADER = "x-scout-pair-relay";
73664
+
73665
+ class PairRequestCapacityError extends Error {
73666
+ retryAfterMs;
73667
+ constructor(retryAfterMs) {
73668
+ super("Too many live pairing requests");
73669
+ this.name = "PairRequestCapacityError";
73670
+ this.retryAfterMs = retryAfterMs;
73671
+ }
73672
+ }
73673
+ var PAIR_CLIENT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
73674
+ var PAIR_CLIENT_LABELS = {
73675
+ "scout-ios": "Scout iOS",
73676
+ "scout-macos": "Scout macOS",
73677
+ "talkie-ios": "Talkie iOS",
73678
+ "talkie-macos": "Talkie macOS"
73679
+ };
73680
+ function parseScoutPairClient(headerValue, userAgent) {
73681
+ const raw2 = headerValue?.trim().toLowerCase() ?? "";
73682
+ if (PAIR_CLIENT_ID_PATTERN.test(raw2))
73683
+ return raw2;
73684
+ const ua = userAgent ?? "";
73685
+ if (/Scout/i.test(ua) && /iPhone|iPad|iOS/i.test(ua))
73686
+ return "scout-ios";
73687
+ if (/Talkie/i.test(ua) && /iPhone|iPad|iOS/i.test(ua))
73688
+ return "talkie-ios";
73689
+ return null;
73690
+ }
73691
+ function formatScoutPairClient(app) {
73692
+ const id = app?.trim() || null;
73693
+ if (!id)
73694
+ return null;
73695
+ return PAIR_CLIENT_LABELS[id] ?? id;
73696
+ }
73697
+ function pairRequesterDisplay(req) {
73698
+ const label = req.requesterLabel?.trim() || null;
73699
+ const app = formatScoutPairClient(req.requesterApp);
73700
+ if (label && app)
73701
+ return `${label} \xB7 ${app}`;
73702
+ return label || app || "A device";
73703
+ }
73136
73704
  var DEFAULT_TTL_MS = 2 * 60 * 1000;
73705
+ var DEFAULT_MAX_LIVE_REQUESTS = 32;
73137
73706
  var SWEEP_INTERVAL_MS = 30 * 1000;
73707
+ function boundedMetadata(value, maxLength) {
73708
+ const trimmed = value?.trim() || null;
73709
+ return trimmed ? trimmed.slice(0, maxLength) : null;
73710
+ }
73138
73711
  var MAX_PUBLISH_ATTEMPTS = 32;
73139
73712
  var PUBLISH_SPIN_ATTEMPTS = 8;
73140
73713
  var PUBLISH_BACKOFF_CEILING_MS = 8;
@@ -73187,7 +73760,7 @@ function isPairRequest(value) {
73187
73760
  if (!value || typeof value !== "object")
73188
73761
  return false;
73189
73762
  const r = value;
73190
- return typeof r.token === "string" && r.token.length > 0 && (r.status === "pending" || r.status === "approved" || r.status === "denied") && isNullableString(r.requesterIp) && isNullableString(r.requesterLabel) && isNullableString(r.route) && typeof r.createdAt === "number" && typeof r.updatedAt === "number" && typeof r.expiresAt === "number";
73763
+ return typeof r.token === "string" && r.token.length > 0 && (r.status === "pending" || r.status === "approved" || r.status === "denied") && isNullableString(r.requesterIp) && isNullableString(r.requesterLabel) && (r.requesterApp === undefined || isNullableString(r.requesterApp)) && isNullableString(r.route) && typeof r.createdAt === "number" && typeof r.updatedAt === "number" && typeof r.expiresAt === "number";
73191
73764
  }
73192
73765
  function fileSignature(path) {
73193
73766
  try {
@@ -73209,7 +73782,11 @@ function parseTerminalMarkers(value) {
73209
73782
  continue;
73210
73783
  if (typeof marker.collectAfter !== "number" || !Number.isFinite(marker.collectAfter))
73211
73784
  continue;
73212
- markers.push({ token: marker.token, collectAfter: marker.collectAfter });
73785
+ markers.push({
73786
+ token: marker.token,
73787
+ collectAfter: marker.collectAfter,
73788
+ ...typeof marker.claimId === "string" && marker.claimId.length > 0 ? { claimId: marker.claimId } : {}
73789
+ });
73213
73790
  }
73214
73791
  return markers;
73215
73792
  }
@@ -73231,6 +73808,8 @@ function createPendingPairRequestStore(options = {}) {
73231
73808
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
73232
73809
  const now = options.now ?? (() => Date.now());
73233
73810
  const statePath = options.statePath;
73811
+ const maxLiveRequests = Math.max(1, Math.floor(options.maxLiveRequests ?? DEFAULT_MAX_LIVE_REQUESTS));
73812
+ const renewalWindowMs = Math.max(0, options.renewalWindowMs ?? Math.floor(ttlMs / 2));
73234
73813
  const byToken = new Map;
73235
73814
  let loadedGeneration = null;
73236
73815
  let loadedSignature = null;
@@ -73239,6 +73818,7 @@ function createPendingPairRequestStore(options = {}) {
73239
73818
  const unpersistedUpserts = new Set;
73240
73819
  const unpersistedExpiries = new Map;
73241
73820
  const fulfilledMarkers = new Map;
73821
+ const fulfilledClaims = new Map;
73242
73822
  const unpublishedFulfilled = new Set;
73243
73823
  const lastSeenInShared = new Map;
73244
73824
  const tempPath = statePath ? `${statePath}.${process.pid}.${crypto.randomUUID().slice(0, 8)}.tmp` : null;
@@ -73256,6 +73836,17 @@ function createPendingPairRequestStore(options = {}) {
73256
73836
  function latestGeneration() {
73257
73837
  return statePath ? latestPairRequestGeneration(statePath) : null;
73258
73838
  }
73839
+ function generationHasTerminalClaim(generation, claim) {
73840
+ const raw2 = readFileOrNull(generationPath(generation));
73841
+ if (raw2 === null)
73842
+ return false;
73843
+ try {
73844
+ const parsed = JSON.parse(raw2);
73845
+ return parseTerminalMarkers(parsed.fulfilled).some((marker) => marker.token === claim.token && marker.claimId === claim.claimId);
73846
+ } catch {
73847
+ return false;
73848
+ }
73849
+ }
73259
73850
  function movedOnSince(sighting) {
73260
73851
  if (loadedEpoch !== sighting.epoch)
73261
73852
  return false;
@@ -73267,10 +73858,11 @@ function createPendingPairRequestStore(options = {}) {
73267
73858
  function terminalMarkerHorizon(expiresAt) {
73268
73859
  return Math.max(expiresAt, now()) + ttlMs;
73269
73860
  }
73270
- function markFulfilled(request) {
73861
+ function markFulfilled(request, claimId) {
73271
73862
  const horizon = terminalMarkerHorizon(request.expiresAt);
73272
73863
  const known = fulfilledMarkers.get(request.token);
73273
73864
  fulfilledMarkers.set(request.token, known === undefined ? horizon : Math.max(known, horizon));
73865
+ fulfilledClaims.set(request.token, claimId);
73274
73866
  if (statePath)
73275
73867
  unpublishedFulfilled.add(request.token);
73276
73868
  }
@@ -73279,8 +73871,10 @@ function createPendingPairRequestStore(options = {}) {
73279
73871
  for (const [token, collectAfter] of fulfilledMarkers) {
73280
73872
  if (unpublishedFulfilled.has(token))
73281
73873
  continue;
73282
- if (collectAfter < t)
73874
+ if (collectAfter < t) {
73283
73875
  fulfilledMarkers.delete(token);
73876
+ fulfilledClaims.delete(token);
73877
+ }
73284
73878
  }
73285
73879
  }
73286
73880
  function mergeUnpersisted(loaded) {
@@ -73340,6 +73934,8 @@ function createPendingPairRequestStore(options = {}) {
73340
73934
  for (const marker of markers) {
73341
73935
  const known = fulfilledMarkers.get(marker.token);
73342
73936
  fulfilledMarkers.set(marker.token, known === undefined ? marker.collectAfter : Math.max(known, marker.collectAfter));
73937
+ if (marker.claimId !== undefined)
73938
+ fulfilledClaims.set(marker.token, marker.claimId);
73343
73939
  }
73344
73940
  for (const [token, row] of loaded) {
73345
73941
  const known = fulfilledMarkers.get(token);
@@ -73406,7 +74002,10 @@ function createPendingPairRequestStore(options = {}) {
73406
74002
  }
73407
74003
  loadedEpoch = epoch;
73408
74004
  loadedHasTerminalLedger = hasLedger;
73409
- adopt(rows.filter(isPairRequest), markers);
74005
+ adopt(rows.filter(isPairRequest).map((row) => ({
74006
+ ...row,
74007
+ requesterApp: row.requesterApp ?? null
74008
+ })), markers);
73410
74009
  return;
73411
74010
  }
73412
74011
  }
@@ -73417,7 +74016,7 @@ function createPendingPairRequestStore(options = {}) {
73417
74016
  rmSync5(tempPath, { force: true });
73418
74017
  } catch {}
73419
74018
  }
73420
- function publish(touched, expired) {
74019
+ function publish(touched, expired, terminalClaim) {
73421
74020
  if (!statePath || !tempPath)
73422
74021
  return "won";
73423
74022
  if (touched.length === 0 && expired.length === 0 && !hasOutstandingWrites())
@@ -73433,7 +74032,11 @@ function createPendingPairRequestStore(options = {}) {
73433
74032
  version: 1,
73434
74033
  epoch,
73435
74034
  requests: [...byToken.values()],
73436
- fulfilled: [...fulfilledMarkers].map(([token, collectAfter]) => ({ token, collectAfter }))
74035
+ fulfilled: [...fulfilledMarkers].map(([token, collectAfter]) => ({
74036
+ token,
74037
+ collectAfter,
74038
+ ...fulfilledClaims.has(token) ? { claimId: fulfilledClaims.get(token) } : {}
74039
+ }))
73437
74040
  });
73438
74041
  try {
73439
74042
  mkdirSync11(dirname26(statePath), { recursive: true, mode: 448 });
@@ -73454,7 +74057,9 @@ function createPendingPairRequestStore(options = {}) {
73454
74057
  discardTemp();
73455
74058
  if (!landed || signature === null)
73456
74059
  return "lost";
73457
- if (latestGeneration() !== next) {
74060
+ const frontier = latestGeneration();
74061
+ const inheritedClaim = terminalClaim !== undefined && frontier !== null && frontier > next && generationHasTerminalClaim(frontier, terminalClaim);
74062
+ if (frontier !== next && !inheritedClaim) {
73458
74063
  try {
73459
74064
  if (inodeOf(target2) === ours)
73460
74065
  unlinkSync2(target2);
@@ -73472,7 +74077,7 @@ function createPendingPairRequestStore(options = {}) {
73472
74077
  for (const token of byToken.keys())
73473
74078
  lastSeenInShared.set(token, sighting);
73474
74079
  forgetSightingsWeNoLongerHold();
73475
- collectSuperseded(next);
74080
+ collectSuperseded(frontier ?? next);
73476
74081
  return "won";
73477
74082
  }
73478
74083
  function collectSuperseded(published) {
@@ -73514,7 +74119,7 @@ function createPendingPairRequestStore(options = {}) {
73514
74119
  function hasOutstandingWrites() {
73515
74120
  return unpersistedUpserts.size > 0 || unpersistedExpiries.size > 0 || unpublishedFulfilled.size > 0;
73516
74121
  }
73517
- function commit(apply) {
74122
+ function commit(apply, options2 = {}) {
73518
74123
  if (!statePath) {
73519
74124
  pruneExpired();
73520
74125
  collectTerminalMarkers();
@@ -73525,10 +74130,17 @@ function createPendingPairRequestStore(options = {}) {
73525
74130
  const expired = pruneExpired();
73526
74131
  collectTerminalMarkers();
73527
74132
  const { value, touched } = apply();
73528
- const outcome = publish(touched, expired);
74133
+ const outcome = publish(touched, expired, options2.terminalClaim);
73529
74134
  if (outcome === "won")
73530
74135
  return value;
74136
+ if (outcome === "lost")
74137
+ options2.rollback?.();
73531
74138
  if (outcome === "failed" || attempt >= MAX_PUBLISH_ATTEMPTS) {
74139
+ if (options2.failClosed) {
74140
+ if (outcome === "failed")
74141
+ options2.rollback?.();
74142
+ return options2.failClosed();
74143
+ }
73532
74144
  recordOutstanding(touched, expired);
73533
74145
  return value;
73534
74146
  }
@@ -73556,7 +74168,7 @@ function createPendingPairRequestStore(options = {}) {
73556
74168
  return null;
73557
74169
  const t = now();
73558
74170
  for (const req of byToken.values()) {
73559
- if (req.requesterIp === requesterIp && req.expiresAt > t && (req.status === "pending" || req.status === "approved")) {
74171
+ if (req.requesterIp === requesterIp && req.expiresAt > t) {
73560
74172
  return req;
73561
74173
  }
73562
74174
  }
@@ -73564,19 +74176,38 @@ function createPendingPairRequestStore(options = {}) {
73564
74176
  }
73565
74177
  return {
73566
74178
  create(input) {
73567
- const requesterIp = input.requesterIp?.trim() || null;
73568
- const label = input.requesterLabel?.trim() || null;
74179
+ const requesterIp = boundedMetadata(input.requesterIp, 64);
74180
+ const label = boundedMetadata(input.requesterLabel, 96);
74181
+ const app = boundedMetadata(input.requesterApp, 32);
74182
+ const route = boundedMetadata(input.route, 16);
73569
74183
  const token = crypto.randomUUID();
73570
- return commit(() => {
74184
+ const result = commit(() => {
73571
74185
  const existing = findReusable(requesterIp);
73572
74186
  if (existing) {
73573
- existing.updatedAt = now();
73574
- existing.expiresAt = now() + ttlMs;
73575
- if (input.route)
73576
- existing.route = input.route;
73577
- if (label)
73578
- existing.requesterLabel = label;
73579
- return { value: existing, touched: [existing.token] };
74187
+ const t2 = now();
74188
+ const renew = existing.status !== "denied" && existing.expiresAt - t2 <= renewalWindowMs;
74189
+ if (renew) {
74190
+ existing.updatedAt = t2;
74191
+ existing.expiresAt = t2 + ttlMs;
74192
+ if (route)
74193
+ existing.route = route;
74194
+ if (label)
74195
+ existing.requesterLabel = label;
74196
+ if (app)
74197
+ existing.requesterApp = app;
74198
+ }
74199
+ return { value: existing, touched: renew ? [existing.token] : [] };
74200
+ }
74201
+ if (byToken.size >= maxLiveRequests) {
74202
+ const t2 = now();
74203
+ const earliestExpiry = Math.min(...[...byToken.values()].map((request2) => request2.expiresAt));
74204
+ return {
74205
+ value: {
74206
+ capacity: true,
74207
+ retryAfterMs: Math.max(1000, earliestExpiry - t2)
74208
+ },
74209
+ touched: []
74210
+ };
73580
74211
  }
73581
74212
  const t = now();
73582
74213
  const request = {
@@ -73584,7 +74215,8 @@ function createPendingPairRequestStore(options = {}) {
73584
74215
  status: "pending",
73585
74216
  requesterIp,
73586
74217
  requesterLabel: label,
73587
- route: input.route ?? null,
74218
+ requesterApp: app,
74219
+ route,
73588
74220
  createdAt: t,
73589
74221
  updatedAt: t,
73590
74222
  expiresAt: t + ttlMs
@@ -73592,6 +74224,10 @@ function createPendingPairRequestStore(options = {}) {
73592
74224
  byToken.set(token, request);
73593
74225
  return { value: request, touched: [token] };
73594
74226
  });
74227
+ if ("capacity" in result) {
74228
+ throw new PairRequestCapacityError(result.retryAfterMs);
74229
+ }
74230
+ return result;
73595
74231
  },
73596
74232
  get(token) {
73597
74233
  reload();
@@ -73601,8 +74237,9 @@ function createPendingPairRequestStore(options = {}) {
73601
74237
  touch(token) {
73602
74238
  commit(() => {
73603
74239
  const req = byToken.get(token);
73604
- const extended = now() + ttlMs;
73605
- const extend = req !== undefined && (req.status === "pending" || req.status === "approved") && extended > req.expiresAt;
74240
+ const t = now();
74241
+ const extended = t + ttlMs;
74242
+ const extend = req !== undefined && (req.status === "pending" || req.status === "approved") && req.expiresAt - t <= renewalWindowMs && extended > req.expiresAt;
73606
74243
  if (extend && req)
73607
74244
  req.expiresAt = extended;
73608
74245
  return { value: undefined, touched: extend ? [token] : [] };
@@ -73619,24 +74256,55 @@ function createPendingPairRequestStore(options = {}) {
73619
74256
  return { value: null, touched: [] };
73620
74257
  req.status = decision === "approve" ? "approved" : "denied";
73621
74258
  req.updatedAt = now();
73622
- if (decision === "approve")
73623
- req.expiresAt = now() + ttlMs;
74259
+ req.expiresAt = now() + ttlMs;
73624
74260
  return { value: req, touched: [token] };
73625
74261
  });
73626
74262
  },
73627
- fulfill(token) {
73628
- let delivered = null;
74263
+ fulfill(token, requiredStatus) {
74264
+ let proposal = null;
74265
+ const claim = { token, claimId: crypto.randomUUID() };
74266
+ let previousMarker;
74267
+ let previousClaim;
74268
+ let previouslyUnpublished = false;
74269
+ let previouslyUnpersisted = false;
74270
+ const rollback = () => {
74271
+ if (proposal === null)
74272
+ return;
74273
+ if (previousMarker === undefined)
74274
+ fulfilledMarkers.delete(token);
74275
+ else
74276
+ fulfilledMarkers.set(token, previousMarker);
74277
+ if (previousClaim === undefined)
74278
+ fulfilledClaims.delete(token);
74279
+ else
74280
+ fulfilledClaims.set(token, previousClaim);
74281
+ if (!previouslyUnpublished)
74282
+ unpublishedFulfilled.delete(token);
74283
+ byToken.set(token, proposal);
74284
+ if (previouslyUnpersisted)
74285
+ unpersistedUpserts.add(token);
74286
+ else
74287
+ unpersistedUpserts.delete(token);
74288
+ proposal = null;
74289
+ };
73629
74290
  return commit(() => {
73630
- if (delivered === null) {
73631
- const request = byToken.get(token);
73632
- if (!request || !isLive(request))
73633
- return { value: null, touched: [] };
73634
- delivered = { ...request };
74291
+ const request = byToken.get(token);
74292
+ if (!request || !isLive(request) || requiredStatus !== undefined && request.status !== requiredStatus) {
74293
+ return { value: null, touched: [] };
73635
74294
  }
73636
- markFulfilled(delivered);
74295
+ proposal = { ...request };
74296
+ previousMarker = fulfilledMarkers.get(token);
74297
+ previousClaim = fulfilledClaims.get(token);
74298
+ previouslyUnpublished = unpublishedFulfilled.has(token);
74299
+ previouslyUnpersisted = unpersistedUpserts.has(token);
74300
+ markFulfilled(proposal, claim.claimId);
73637
74301
  byToken.delete(token);
73638
74302
  unpersistedUpserts.delete(token);
73639
- return { value: delivered, touched: [] };
74303
+ return { value: proposal, touched: [] };
74304
+ }, {
74305
+ rollback,
74306
+ terminalClaim: claim,
74307
+ failClosed: () => null
73640
74308
  });
73641
74309
  },
73642
74310
  dispose() {
@@ -73647,6 +74315,7 @@ function createPendingPairRequestStore(options = {}) {
73647
74315
  unpersistedExpiries.clear();
73648
74316
  unpublishedFulfilled.clear();
73649
74317
  fulfilledMarkers.clear();
74318
+ fulfilledClaims.clear();
73650
74319
  lastSeenInShared.clear();
73651
74320
  }
73652
74321
  };
@@ -73887,7 +74556,8 @@ init_agent_endpoints();
73887
74556
 
73888
74557
  // packages/web/server/core/repo-identity.ts
73889
74558
  init_system_probes();
73890
- var REPO_KEY_TTL_MS = 60000;
74559
+ var REPO_KEY_TTL_MS = 10 * 60000;
74560
+ var REPO_KEY_PROBE_CONCURRENCY = 4;
73891
74561
  function normalizeGitRemoteUrl(remote) {
73892
74562
  const trimmed = remote?.trim() ?? "";
73893
74563
  if (!trimmed)
@@ -73911,15 +74581,14 @@ function canonicalRepoKey(host, path2) {
73911
74581
  }
73912
74582
  var repoKeyCache = new Map;
73913
74583
  var repoKeyInFlight = new Map;
74584
+ var queuedRepoKeyRoots = [];
74585
+ var queuedRepoKeyRootSet = new Set;
74586
+ var activeRepoKeyProbes = 0;
73914
74587
  async function probeRepoKey(root) {
73915
74588
  const remote = await gitRemoteGetUrlOrigin(root).catch(() => null);
73916
74589
  return normalizeGitRemoteUrl(remote);
73917
74590
  }
73918
- function resolveRepoKeyForRoot(root) {
73919
- const cached = repoKeyCache.get(root);
73920
- if (cached && Date.now() - cached.at < REPO_KEY_TTL_MS) {
73921
- return Promise.resolve(cached.key);
73922
- }
74591
+ function refreshRepoKeyForRoot(root) {
73923
74592
  const inFlight = repoKeyInFlight.get(root);
73924
74593
  if (inFlight)
73925
74594
  return inFlight;
@@ -73933,10 +74602,38 @@ function resolveRepoKeyForRoot(root) {
73933
74602
  repoKeyInFlight.set(root, promise);
73934
74603
  return promise;
73935
74604
  }
73936
- async function resolveRepoKeysByRoot(roots) {
74605
+ function drainRepoKeyProbeQueue() {
74606
+ while (activeRepoKeyProbes < REPO_KEY_PROBE_CONCURRENCY) {
74607
+ const root = queuedRepoKeyRoots.shift();
74608
+ if (!root)
74609
+ return;
74610
+ queuedRepoKeyRootSet.delete(root);
74611
+ activeRepoKeyProbes += 1;
74612
+ refreshRepoKeyForRoot(root).catch(() => null).finally(() => {
74613
+ activeRepoKeyProbes -= 1;
74614
+ drainRepoKeyProbeQueue();
74615
+ });
74616
+ }
74617
+ }
74618
+ function scheduleRepoKeyProbe(root) {
74619
+ if (repoKeyInFlight.has(root) || queuedRepoKeyRootSet.has(root))
74620
+ return;
74621
+ queuedRepoKeyRootSet.add(root);
74622
+ queuedRepoKeyRoots.push(root);
74623
+ drainRepoKeyProbeQueue();
74624
+ }
74625
+ function cachedRepoKeysByRoot(roots) {
74626
+ const now = Date.now();
73937
74627
  const distinct = [...new Set(roots)];
73938
- const keys = await Promise.all(distinct.map((root) => resolveRepoKeyForRoot(root)));
73939
- return new Map(distinct.map((root, index2) => [root, keys[index2] ?? null]));
74628
+ const result = new Map;
74629
+ for (const root of distinct) {
74630
+ const cached = repoKeyCache.get(root);
74631
+ result.set(root, cached?.key ?? null);
74632
+ if (!cached || now - cached.at >= REPO_KEY_TTL_MS) {
74633
+ scheduleRepoKeyProbe(root);
74634
+ }
74635
+ }
74636
+ return result;
73940
74637
  }
73941
74638
 
73942
74639
  // packages/web/server/terminal-session-discovery.ts
@@ -75353,6 +76050,50 @@ init_src();
75353
76050
  init_legacy_ids();
75354
76051
  init_service();
75355
76052
  init_message_pagination();
76053
+ function provisionalScoutConversations(snapshot, filters = {}) {
76054
+ const allowedKinds = new Set(filters.kinds ?? DEFAULT_CONVERSATION_KINDS);
76055
+ const query = normalizeQuery(filters.query);
76056
+ const limit = Math.max(1, Math.floor(filters.limit ?? snapshot.items.length));
76057
+ return snapshot.items.filter((item) => item.entityKind === "scout_conversation" && item.visibilityState === "visible" && Boolean(item.conversationId) && allowedKinds.has(item.kind)).filter((item) => !query || [
76058
+ item.title,
76059
+ item.alias,
76060
+ item.agentName,
76061
+ item.preview,
76062
+ item.projectRoot
76063
+ ].some((value) => value?.toLocaleLowerCase().includes(query))).slice(0, limit).map((item) => {
76064
+ const conversationId = item.conversationId;
76065
+ const participantIds = item.kind === "direct" && item.agentId ? ["operator", item.agentId] : [];
76066
+ return {
76067
+ id: conversationId,
76068
+ chatId: conversationId,
76069
+ equivalentConversationIds: [conversationId],
76070
+ kind: item.kind,
76071
+ title: item.title ?? item.agentName ?? item.alias ?? conversationId,
76072
+ alias: item.alias,
76073
+ naturalKey: item.naturalKey,
76074
+ participantIds,
76075
+ participants: [],
76076
+ participantCount: item.participantCount,
76077
+ authorityNodeId: item.authorityNodeId,
76078
+ authorityNodeName: item.authorityNodeName,
76079
+ agentId: item.agentId,
76080
+ agentName: item.agentName,
76081
+ harness: item.harness,
76082
+ model: item.model,
76083
+ reasoningEffort: item.effort,
76084
+ transport: null,
76085
+ sessionId: item.runtimeSessionId,
76086
+ currentBranch: item.currentBranch,
76087
+ parentConversationId: item.parentConversationId,
76088
+ anchorMessageId: item.anchorMessageId,
76089
+ preview: item.preview,
76090
+ messageCount: item.messageCount,
76091
+ lastMessageAt: item.lastMessageAt,
76092
+ workspaceRoot: item.projectRoot,
76093
+ unreadCount: item.unreadCount
76094
+ };
76095
+ });
76096
+ }
75356
76097
  var DEFAULT_CONVERSATION_KINDS = [
75357
76098
  "direct",
75358
76099
  "channel",
@@ -75421,6 +76162,53 @@ function metadataObject2(metadata, key) {
75421
76162
  function metadataSessionId(metadata) {
75422
76163
  return metadataString9(metadata, "targetSessionId") ?? metadataString9(metadata, "responderSessionId") ?? metadataString9(metadata, "sessionId") ?? metadataString9(metadata, "externalSessionId") ?? metadataString9(metadata, "threadId") ?? metadataString9(metadataObject2(metadata, "returnAddress"), "sessionId");
75423
76164
  }
76165
+ function executionResolutionDimension2(resolution, dimension) {
76166
+ const value = resolution?.[dimension];
76167
+ return value?.observed?.trim() || value?.resolved?.trim() || value?.requested?.trim() || null;
76168
+ }
76169
+ function executionResolutionFromUnknown(value) {
76170
+ if (!value || typeof value !== "object" || Array.isArray(value))
76171
+ return null;
76172
+ const resolution = value;
76173
+ return resolution.schemaVersion === "openscout.execution-resolution.v1" ? resolution : null;
76174
+ }
76175
+ function flightSessionRuntime(flights, sessionId) {
76176
+ for (const flight of [...flights].reverse()) {
76177
+ const rawTrace = flight.metadata?.sessionTrace;
76178
+ if (!Array.isArray(rawTrace))
76179
+ continue;
76180
+ const entries2 = [...rawTrace].reverse();
76181
+ const candidate = entries2.find((entry) => {
76182
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
76183
+ return false;
76184
+ const id = metadataString9(entry, "sessionId");
76185
+ return sessionId ? id === sessionId : Boolean(id);
76186
+ });
76187
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
76188
+ continue;
76189
+ const record2 = candidate;
76190
+ return {
76191
+ resolution: executionResolutionFromUnknown(record2.executionResolution),
76192
+ harness: metadataString9(record2, "harness"),
76193
+ transport: metadataString9(record2, "transport")
76194
+ };
76195
+ }
76196
+ return null;
76197
+ }
76198
+ function latestConversationRuntime(input) {
76199
+ const matchingParticipant = input.sessionId ? input.participants.find((participant) => participant.sessionId === input.sessionId) ?? null : null;
76200
+ const matchingInvocation = input.sessionId ? [...input.invocations].reverse().find((invocation2) => invocation2.executionResolution?.sessionId === input.sessionId || invocation2.execution?.targetSessionId === input.sessionId || metadataSessionId(invocation2.metadata) === input.sessionId) ?? null : null;
76201
+ const latestInvocation = input.invocations.at(-1) ?? null;
76202
+ const invocation = matchingInvocation ?? latestInvocation;
76203
+ const trace = flightSessionRuntime(input.flights, input.sessionId);
76204
+ const resolution = matchingInvocation?.executionResolution ?? trace?.resolution ?? invocation?.executionResolution ?? null;
76205
+ return {
76206
+ harness: executionResolutionDimension2(resolution, "harness") ?? trace?.harness ?? matchingParticipant?.harness ?? invocation?.execution?.harness?.trim() ?? input.endpoint?.harness ?? null,
76207
+ model: executionResolutionDimension2(resolution, "model") ?? matchingParticipant?.model ?? invocation?.execution?.model?.trim() ?? metadataString9(input.endpoint?.metadata, "observedModel") ?? metadataString9(input.endpoint?.metadata, "model"),
76208
+ reasoningEffort: executionResolutionDimension2(resolution, "reasoningEffort") ?? matchingParticipant?.reasoningEffort ?? invocation?.execution?.reasoningEffort?.trim() ?? metadataString9(input.endpoint?.metadata, "observedReasoningEffort") ?? metadataString9(input.endpoint?.metadata, "reasoningEffort"),
76209
+ transport: trace?.transport ?? matchingParticipant?.transport ?? input.endpoint?.transport ?? null
76210
+ };
76211
+ }
75424
76212
  function formatChannelAlias2(value) {
75425
76213
  const trimmed = value.trim();
75426
76214
  if (!trimmed)
@@ -75751,6 +76539,8 @@ function buildScopedParticipants(snapshot, endpoints, operatorIds, conversationI
75751
76539
  agentId: agent?.id ?? null,
75752
76540
  sessionId: endpoint?.sessionId ?? metadataSessionId(endpoint?.metadata) ?? metadataSessionId(actor?.metadata) ?? (kind === "session" ? participantId : null),
75753
76541
  harness: endpoint?.harness ?? metadataString9(actor?.metadata, "harness"),
76542
+ model: metadataString9(endpoint?.metadata, "observedModel") ?? metadataString9(endpoint?.metadata, "model") ?? metadataString9(actor?.metadata, "model"),
76543
+ reasoningEffort: metadataString9(endpoint?.metadata, "observedReasoningEffort") ?? metadataString9(endpoint?.metadata, "reasoningEffort") ?? metadataString9(actor?.metadata, "reasoningEffort"),
75754
76544
  transport: endpoint?.transport ?? metadataString9(actor?.metadata, "transport"),
75755
76545
  workspaceRoot: endpoint?.projectRoot ?? endpoint?.cwd ?? metadataString9(actor?.metadata, "projectRoot")
75756
76546
  };
@@ -75772,6 +76562,9 @@ function includeConversation(summary, query) {
75772
76562
  summary.title,
75773
76563
  summary.agentId ?? "",
75774
76564
  summary.agentName ?? "",
76565
+ summary.harness ?? "",
76566
+ summary.model ?? "",
76567
+ summary.reasoningEffort ?? "",
75775
76568
  summary.preview ?? "",
75776
76569
  summary.workspaceRoot ?? "",
75777
76570
  ...summary.participantIds,
@@ -75839,6 +76632,10 @@ function coalesceDuplicateNamedChannels(summaries, snapshot, endpoints, operator
75839
76632
  preview: latest.preview,
75840
76633
  lastMessageAt: latest.lastMessageAt,
75841
76634
  sessionId: latest.sessionId ?? canonical2.sessionId,
76635
+ harness: latest.harness ?? canonical2.harness,
76636
+ model: latest.model ?? canonical2.model,
76637
+ reasoningEffort: latest.reasoningEffort ?? canonical2.reasoningEffort,
76638
+ transport: latest.transport ?? canonical2.transport,
75842
76639
  messageCount: Math.max(...group.map((summary) => summary.messageCount)),
75843
76640
  unreadCount: Math.max(...group.map((summary) => summary.unreadCount)),
75844
76641
  ...latestTurn ? { turn: latestTurn } : {}
@@ -75910,6 +76707,13 @@ async function getScoutConversations(filters = {}, brokerContext) {
75910
76707
  }
75911
76708
  const title = agentDisplayName(snapshot, endpointsByAgent, agentId);
75912
76709
  const identityFields2 = conversationIdentityFields2(conversation);
76710
+ const runtime2 = latestConversationRuntime({
76711
+ sessionId,
76712
+ participants,
76713
+ invocations: invocations2,
76714
+ flights,
76715
+ endpoint
76716
+ });
75913
76717
  return [{
75914
76718
  id: conversation.id,
75915
76719
  chatId: conversation.id,
@@ -75924,7 +76728,10 @@ async function getScoutConversations(filters = {}, brokerContext) {
75924
76728
  authorityNodeName: snapshot.nodes?.[conversation.authorityNodeId]?.name ?? null,
75925
76729
  agentId,
75926
76730
  agentName: title,
75927
- harness: endpoint?.harness ?? null,
76731
+ harness: runtime2.harness,
76732
+ model: runtime2.model,
76733
+ reasoningEffort: runtime2.reasoningEffort,
76734
+ transport: runtime2.transport,
75928
76735
  sessionId,
75929
76736
  currentBranch: metadataString9(endpoint?.metadata, "branch") ?? metadataString9(endpoint?.metadata, "workspaceQualifier") ?? metadataString9(agent?.metadata, "branch") ?? metadataString9(agent?.metadata, "workspaceQualifier"),
75930
76737
  parentConversationId: conversation.parentConversationId ?? null,
@@ -75951,6 +76758,13 @@ async function getScoutConversations(filters = {}, brokerContext) {
75951
76758
  return [];
75952
76759
  }
75953
76760
  const identityFields = conversationIdentityFields2(conversation);
76761
+ const runtime = latestConversationRuntime({
76762
+ sessionId,
76763
+ participants,
76764
+ invocations: invocations2,
76765
+ flights,
76766
+ endpoint: null
76767
+ });
75954
76768
  return [{
75955
76769
  id: conversation.id,
75956
76770
  chatId: conversation.id,
@@ -75965,7 +76779,10 @@ async function getScoutConversations(filters = {}, brokerContext) {
75965
76779
  authorityNodeName: snapshot.nodes?.[conversation.authorityNodeId]?.name ?? null,
75966
76780
  agentId: null,
75967
76781
  agentName: null,
75968
- harness: null,
76782
+ harness: runtime.harness,
76783
+ model: runtime.model,
76784
+ reasoningEffort: runtime.reasoningEffort,
76785
+ transport: runtime.transport,
75969
76786
  sessionId,
75970
76787
  currentBranch: null,
75971
76788
  parentConversationId: conversation.parentConversationId ?? null,
@@ -75992,15 +76809,17 @@ function capSummaryRoster(summary, operatorIds, endpoints) {
75992
76809
  if (summary.participantIds.length <= CHANNEL_ROSTER_CAP)
75993
76810
  return summary;
75994
76811
  const keep = new Set;
75995
- for (const id of summary.participantIds) {
75996
- if (operatorIds.has(id) || participantEndpoint(endpoints, id))
75997
- keep.add(id);
75998
- }
75999
- for (const id of summary.participantIds) {
76000
- if (keep.size >= CHANNEL_ROSTER_CAP)
76001
- break;
76002
- keep.add(id);
76003
- }
76812
+ const fillFromTier = (matches) => {
76813
+ for (const id of summary.participantIds) {
76814
+ if (keep.size >= CHANNEL_ROSTER_CAP)
76815
+ return;
76816
+ if (matches(id))
76817
+ keep.add(id);
76818
+ }
76819
+ };
76820
+ fillFromTier((id) => operatorIds.has(id));
76821
+ fillFromTier((id) => !operatorIds.has(id) && participantEndpoint(endpoints, id) !== null);
76822
+ fillFromTier(() => true);
76004
76823
  const keptIds = summary.participantIds.filter((id) => keep.has(id));
76005
76824
  const droppedIds = summary.participantIds.filter((id) => !keep.has(id));
76006
76825
  return {
@@ -76009,12 +76828,12 @@ function capSummaryRoster(summary, operatorIds, endpoints) {
76009
76828
  participants: summary.participants.filter((participant) => keep.has(participant.actorId))
76010
76829
  };
76011
76830
  }
76012
- async function getScoutConversationMessages(conversationId, limit = 80, beforeMessageId) {
76831
+ async function getScoutConversationMessages(conversationId, limit = 80, beforeMessageId, brokerContext) {
76013
76832
  const normalizedId = conversationId.trim();
76014
76833
  if (!normalizedId || !isOpaqueChannelId(normalizedId)) {
76015
76834
  return [];
76016
76835
  }
76017
- const broker2 = await loadScoutBrokerContext(undefined, { scope: "conversations" });
76836
+ const broker2 = brokerContext === undefined ? await loadScoutBrokerContext(undefined, { scope: "conversations" }) : brokerContext;
76018
76837
  if (!broker2)
76019
76838
  return null;
76020
76839
  const snapshot = broker2.snapshot;
@@ -76085,6 +76904,7 @@ import { basename as basename20, join as join54, resolve as resolve20 } from "pa
76085
76904
  init_secret_redaction();
76086
76905
  import { basename as basename19 } from "path";
76087
76906
  import { open, stat as stat9 } from "fs/promises";
76907
+ import { StringDecoder } from "string_decoder";
76088
76908
 
76089
76909
  // packages/runtime/src/tail/claude-source.ts
76090
76910
  init_user_config();
@@ -78991,8 +79811,10 @@ function readPositiveIntEnv8(name2, fallback) {
78991
79811
  const raw2 = Number.parseInt(process.env[name2] ?? "", 10);
78992
79812
  return Number.isFinite(raw2) && raw2 > 0 ? raw2 : fallback;
78993
79813
  }
78994
- var TAIL_POLL_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_POLL_INTERVAL_MS", 500);
78995
- var HOT_DISCOVERY_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_HOT_DISCOVERY_INTERVAL_MS", 30000);
79814
+ var DEFAULT_TAIL_POLL_INTERVAL_MS = 1e4;
79815
+ var DEFAULT_HOT_DISCOVERY_INTERVAL_MS = 60000;
79816
+ var TAIL_POLL_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_POLL_INTERVAL_MS", DEFAULT_TAIL_POLL_INTERVAL_MS);
79817
+ var HOT_DISCOVERY_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_HOT_DISCOVERY_INTERVAL_MS", DEFAULT_HOT_DISCOVERY_INTERVAL_MS);
78996
79818
  var DISCOVERY_CACHE_MAX_AGE_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_DISCOVERY_CACHE_MAX_AGE_MS", HOT_DISCOVERY_INTERVAL_MS);
78997
79819
  var SHALLOW_DISCOVERY_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_SHALLOW_DISCOVERY_INTERVAL_MS", 10 * 60000);
78998
79820
  var DEEP_DISCOVERY_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_DEEP_DISCOVERY_INTERVAL_MS", 60 * 60000);
@@ -79006,23 +79828,42 @@ var RAW_MAX_STRING_LEN = 1000;
79006
79828
  var RAW_MAX_ARRAY_ITEMS = 25;
79007
79829
  var RAW_MAX_OBJECT_KEYS = 50;
79008
79830
  var RECENT_TRANSCRIPT_READ_BYTES = 512 * 1024;
79831
+ var WATCHER_READ_BYTES = readPositiveIntEnv8("OPENSCOUT_TAIL_WATCHER_READ_BYTES", 512 * 1024);
79832
+ var WATCHER_DRAIN_BYTES = Math.max(WATCHER_READ_BYTES, readPositiveIntEnv8("OPENSCOUT_TAIL_WATCHER_DRAIN_BYTES", 4 * 1024 * 1024));
79833
+ var WATCHER_PUMP_CONCURRENCY = readPositiveIntEnv8("OPENSCOUT_TAIL_WATCHER_PUMP_CONCURRENCY", 16);
79009
79834
  var SESSION_TRANSCRIPT_READ_BYTES = 8 * 1024 * 1024;
79010
79835
  var RECENT_TRANSCRIPT_KIND_SCAN_READ_BYTES = 32 * 1024 * 1024;
79011
79836
  var RECENT_TRANSCRIPT_LINES_PER_FILE = 200;
79012
79837
  var RECENT_TRANSCRIPT_KIND_SCAN_MAX_LINES = 65536;
79013
79838
  var RECENT_TRANSCRIPT_MAX_FILES = readPositiveIntEnv8("OPENSCOUT_TAIL_RECENT_TRANSCRIPT_MAX_FILES", 24);
79839
+ var INTERNAL_RECONCILE_SESSIONS_PER_SOURCE = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_RECONCILE_SESSIONS_PER_SOURCE", 12);
79840
+ var INTERNAL_RECONCILE_LINES_PER_SESSION = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_RECONCILE_LINES_PER_SESSION", 128);
79841
+ var INTERNAL_RECONCILE_READ_BYTES = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_RECONCILE_READ_BYTES", 256 * 1024);
79842
+ var INTERNAL_HOT_WATCHER_WINDOW_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_HOT_WATCHER_WINDOW_MS", 60000);
79843
+ var INTERNAL_IDLE_WATCHER_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_INTERVAL_MS", 15000);
79844
+ var INTERNAL_IDLE_WATCHER_BATCH_SIZE = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_BATCH_SIZE", 32);
79845
+ var INTERNAL_ACTIVE_STALE_AFTER_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_ACTIVE_STALE_AFTER_MS", 30 * 60000);
79846
+ var INTERNAL_STALE_CONFIRMATION_COUNT = 2;
79847
+ var AUTHORITATIVE_MISSING_CONFIRMATION_COUNT = 2;
79848
+ var INTERNAL_PERSISTED_OBSERVED_SEED_LIMIT = readPositiveIntEnv8("OPENSCOUT_TAIL_PERSISTED_OBSERVED_SEED_LIMIT", 4096);
79014
79849
  var NATIVE_TAIL_SOURCES = new Set(["grok", "kimi", "opencode", "cursor"]);
79015
79850
  var TRANSCRIPT_REPLAY_MEMO_MAX_ENTRIES = readPositiveIntEnv8("OPENSCOUT_TAIL_REPLAY_MEMO_MAX_ENTRIES", 256);
79016
79851
  var TRANSCRIPT_REPLAY_ACTIVE_GRACE_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_REPLAY_MEMO_ACTIVE_GRACE_MS", 2000);
79017
79852
  var transcriptReplayMemo = new Map;
79018
79853
  var transcriptReplayInFlight = new Map;
79854
+ var INTERNAL_TAIL_SESSION_OFFLINE_SUMMARY = "session lifecycle \xB7 confirmed offline";
79855
+ var INTERNAL_TAIL_SESSION_STALLED_SUMMARY = "session lifecycle \xB7 confirmed stale";
79019
79856
  var sources = [GrokSource, KimiSource, ClaudeSource, CodexSource, CursorSource, OpenCodeSource, PiSource];
79020
79857
  var watchers = new Map;
79021
79858
  var aggregateBuffer = [];
79022
79859
  var assistantBuffer = [];
79023
79860
  var perSessionBuffer = new Map;
79024
79861
  var subscribers = new Set;
79862
+ var internalSubscribers = new Set;
79863
+ var watcherPumpQueue = [];
79864
+ var activeWatcherPumps = 0;
79025
79865
  var knownTranscripts = new Map;
79866
+ var persistedObservedSessions = new Map;
79026
79867
  var quietEventLastSeen = new Map;
79027
79868
  var pollTimer = null;
79028
79869
  var hotDiscoveryTimer = null;
@@ -79219,10 +80060,19 @@ function shouldCoalesceQuietTailEvent(event) {
79219
80060
  pruneQuietTailCoalescer(now);
79220
80061
  return false;
79221
80062
  }
79222
- function pushEvent(rawEvent) {
80063
+ function pushInternalEvent(rawEvent) {
79223
80064
  const event = compactEvent(rawEvent);
80065
+ for (const subscriber of [...internalSubscribers]) {
80066
+ try {
80067
+ subscriber(event);
80068
+ } catch {}
80069
+ }
80070
+ return event;
80071
+ }
80072
+ function pushEvent(rawEvent) {
80073
+ const event = pushInternalEvent(rawEvent);
79224
80074
  if (shouldCoalesceQuietTailEvent(event))
79225
- return;
80075
+ return event;
79226
80076
  aggregateBuffer.push(event);
79227
80077
  if (aggregateBuffer.length > AGGREGATE_BUFFER_LIMIT) {
79228
80078
  aggregateBuffer.splice(0, aggregateBuffer.length - AGGREGATE_BUFFER_LIMIT);
@@ -79247,15 +80097,114 @@ function pushEvent(rawEvent) {
79247
80097
  subscriber(event);
79248
80098
  } catch {}
79249
80099
  }
80100
+ return event;
80101
+ }
80102
+ function rememberWatcherEvent(watcher, rawEvent, delivery) {
80103
+ if (watcher.emittedEventIds.has(rawEvent.id))
80104
+ return;
80105
+ watcher.emittedEventIds.add(rawEvent.id);
80106
+ const event = delivery === "all" ? pushEvent(rawEvent) : pushInternalEvent(rawEvent);
80107
+ watcher.internalObserved = true;
80108
+ watcher.lastInternalEvent = event;
80109
+ watcher.staleObservations = 0;
80110
+ watcher.lifecycleNotification = "none";
80111
+ const lifecycle = persistedObservedSessions.get(observedSessionLifecycleKey(watcher.source.name, event.sessionId));
80112
+ if (lifecycle) {
80113
+ lifecycle.missingAuthoritativePasses = 0;
80114
+ lifecycle.lifecycleNotification = "none";
80115
+ }
80116
+ }
80117
+ function observedSessionLifecycleKey(source, sourceSessionId) {
80118
+ return `${source.trim()}\x00${sourceSessionId.trim()}`;
80119
+ }
80120
+ function persistedSeedEvent(seed) {
80121
+ const source = seed.source.trim();
80122
+ const sourceSessionId = seed.sourceSessionId.trim();
80123
+ const timestamp = Number.isFinite(seed.lastActivityAt) && seed.lastActivityAt > 0 ? seed.lastActivityAt : 0;
80124
+ return {
80125
+ id: `tail-persisted-seed:${source}:${sourceSessionId}:${timestamp}`,
80126
+ ts: timestamp,
80127
+ source,
80128
+ sessionId: sourceSessionId,
80129
+ pid: virtualPidForPath(`${source}:${sourceSessionId}`),
80130
+ parentPid: null,
80131
+ project: seed.project?.trim() ?? "",
80132
+ cwd: seed.cwd?.trim() || seed.projectRoot?.trim() || "",
80133
+ harness: "unattributed",
80134
+ kind: "system",
80135
+ summary: "session lifecycle \xB7 restored persisted active identity",
80136
+ raw: { reason: "persisted_active_seed" }
80137
+ };
80138
+ }
80139
+ function applyPersistedObservedSeedToWatcher(watcher) {
80140
+ if (watcher.internalObserved)
80141
+ return;
80142
+ const sessionId = watcher.transcript.sessionId?.trim();
80143
+ if (!sessionId)
80144
+ return;
80145
+ const lifecycle = persistedObservedSessions.get(observedSessionLifecycleKey(watcher.source.name, sessionId));
80146
+ if (!lifecycle)
80147
+ return;
80148
+ watcher.internalObserved = true;
80149
+ watcher.lastInternalEvent = persistedSeedEvent(lifecycle.seed);
80150
+ watcher.missingAuthoritativePasses = lifecycle.missingAuthoritativePasses;
80151
+ watcher.lifecycleNotification = lifecycle.lifecycleNotification;
80152
+ }
80153
+ function watcherSessionId(watcher) {
80154
+ return watcher.lastInternalEvent?.sessionId.trim() || watcher.transcript.sessionId?.trim() || null;
80155
+ }
80156
+ function notifyWatcherLifecycle(watcher, reason, observedAt) {
80157
+ const nextNotification = reason === "missing" ? "offline" : "stalled";
80158
+ const sessionId = watcherSessionId(watcher);
80159
+ if (!sessionId)
80160
+ return;
80161
+ const lifecycleKey = observedSessionLifecycleKey(watcher.source.name, sessionId);
80162
+ const persistedLifecycle = persistedObservedSessions.get(lifecycleKey);
80163
+ if (!watcher.internalObserved || watcher.lifecycleNotification === nextNotification || watcher.lifecycleNotification === "offline" || persistedLifecycle?.lifecycleNotification === nextNotification || persistedLifecycle?.lifecycleNotification === "offline")
80164
+ return;
80165
+ watcher.lifecycleNotification = nextNotification;
80166
+ if (persistedLifecycle) {
80167
+ persistedLifecycle.lifecycleNotification = nextNotification;
80168
+ }
80169
+ pushInternalEvent({
80170
+ id: `tail-lifecycle:${watcher.source.name}:${sessionId}:${nextNotification}:${observedAt}`,
80171
+ ts: observedAt,
80172
+ source: watcher.source.name,
80173
+ sessionId,
80174
+ pid: watcher.process.pid,
80175
+ parentPid: watcher.process.ppid || null,
80176
+ project: watcher.transcript.project,
80177
+ cwd: watcher.transcript.cwd ?? watcher.process.cwd ?? "",
80178
+ harness: watcher.transcript.harness,
80179
+ kind: "system",
80180
+ summary: reason === "missing" ? INTERNAL_TAIL_SESSION_OFFLINE_SUMMARY : INTERNAL_TAIL_SESSION_STALLED_SUMMARY,
80181
+ raw: { reason }
80182
+ });
80183
+ if (nextNotification === "offline") {
80184
+ persistedObservedSessions.delete(lifecycleKey);
80185
+ }
80186
+ }
80187
+ function observeWatcherStaleness(watcher, observedAt) {
80188
+ if (watcher.lifecycleNotification !== "none" || !watcher.internalObserved || observedAt - watcher.lastObservedChangeAt < INTERNAL_ACTIVE_STALE_AFTER_MS) {
80189
+ watcher.staleObservations = 0;
80190
+ return;
80191
+ }
80192
+ watcher.staleObservations++;
80193
+ if (watcher.staleObservations >= INTERNAL_STALE_CONFIRMATION_COUNT) {
80194
+ notifyWatcherLifecycle(watcher, "stale", observedAt);
80195
+ }
79250
80196
  }
79251
80197
  async function readNew(handle, fromOffset, fileSize) {
79252
80198
  if (fileSize <= fromOffset) {
79253
- return { text: "", nextOffset: fromOffset };
80199
+ return { bytes: Buffer.alloc(0), nextOffset: fromOffset };
79254
80200
  }
79255
- const length = fileSize - fromOffset;
79256
- const buffer = Buffer.alloc(length);
79257
- await handle.read(buffer, 0, length, fromOffset);
79258
- return { text: buffer.toString("utf8"), nextOffset: fileSize };
80201
+ const length = Math.min(fileSize - fromOffset, WATCHER_READ_BYTES);
80202
+ const buffer = Buffer.allocUnsafe(length);
80203
+ const { bytesRead } = await handle.read(buffer, 0, length, fromOffset);
80204
+ return {
80205
+ bytes: buffer.subarray(0, bytesRead),
80206
+ nextOffset: fromOffset + bytesRead
80207
+ };
79259
80208
  }
79260
80209
  async function readTranscriptText(path2, maxBytes = RECENT_TRANSCRIPT_READ_BYTES) {
79261
80210
  let handle = null;
@@ -79275,23 +80224,35 @@ async function readTranscriptText(path2, maxBytes = RECENT_TRANSCRIPT_READ_BYTES
79275
80224
  await handle?.close().catch(() => {});
79276
80225
  }
79277
80226
  }
79278
- async function pumpWatcher(watcher) {
80227
+ async function runWatcherPump(watcher, observedAt) {
79279
80228
  let handle = null;
80229
+ watcher.lastPumpAt = observedAt;
79280
80230
  try {
79281
80231
  const stats = await stat9(watcher.transcriptPath);
80232
+ const changed = stats.size !== watcher.lastKnownSize || stats.mtimeMs !== watcher.lastKnownMtimeMs;
80233
+ watcher.lastKnownSize = stats.size;
80234
+ watcher.lastKnownMtimeMs = stats.mtimeMs;
80235
+ if (changed) {
80236
+ watcher.lastObservedChangeAt = observedAt;
80237
+ watcher.staleObservations = 0;
80238
+ watcher.lifecycleNotification = "none";
80239
+ }
79282
80240
  if (stats.size < watcher.offset) {
79283
80241
  watcher.offset = 0;
79284
80242
  watcher.carry = "";
80243
+ watcher.decoder = new StringDecoder("utf8");
79285
80244
  watcher.emittedEventIds.clear();
79286
80245
  }
79287
- if (stats.size <= watcher.offset)
80246
+ if (stats.size <= watcher.offset) {
80247
+ observeWatcherStaleness(watcher, observedAt);
79288
80248
  return;
80249
+ }
79289
80250
  if (watcher.source.parseFile) {
79290
- const text5 = await readTranscriptText(watcher.transcriptPath);
80251
+ const text4 = await readTranscriptText(watcher.transcriptPath);
79291
80252
  watcher.offset = stats.size;
79292
- if (!text5)
80253
+ if (!text4)
79293
80254
  return;
79294
- const events2 = parsedEventsToArray(watcher.source.parseFile(text5, {
80255
+ const events2 = parsedEventsToArray(watcher.source.parseFile(text4, {
79295
80256
  process: watcher.process,
79296
80257
  transcript: watcher.transcript,
79297
80258
  transcriptPath: watcher.transcriptPath,
@@ -79300,74 +80261,203 @@ async function pumpWatcher(watcher) {
79300
80261
  }));
79301
80262
  watcher.lineCounter += Math.max(1, events2.length);
79302
80263
  for (const event of events2) {
79303
- if (watcher.emittedEventIds.has(event.id))
79304
- continue;
79305
- watcher.emittedEventIds.add(event.id);
79306
- pushEvent(event);
80264
+ rememberWatcherEvent(watcher, event, "all");
79307
80265
  }
79308
80266
  return;
79309
80267
  }
79310
- handle = await open(watcher.transcriptPath, "r");
79311
- const { text: text4, nextOffset } = await readNew(handle, watcher.offset, stats.size);
79312
- watcher.offset = nextOffset;
79313
- if (!text4)
79314
- return;
79315
- const combined = watcher.carry + text4;
79316
- const lines = combined.split(`
79317
- `);
79318
- watcher.carry = lines.pop() ?? "";
79319
- for (const line of lines) {
79320
- if (!line)
80268
+ const drainEnd = Math.min(stats.size, watcher.offset + WATCHER_DRAIN_BYTES);
80269
+ while (watcher.offset < drainEnd) {
80270
+ handle = await open(watcher.transcriptPath, "r");
80271
+ const { bytes, nextOffset } = await readNew(handle, watcher.offset, drainEnd);
80272
+ await handle.close();
80273
+ handle = null;
80274
+ if (nextOffset <= watcher.offset)
80275
+ break;
80276
+ watcher.offset = nextOffset;
80277
+ const text4 = watcher.decoder.write(bytes);
80278
+ if (!text4)
79321
80279
  continue;
79322
- const event = watcher.source.parseLine(line, {
79323
- process: watcher.process,
79324
- transcript: watcher.transcript,
79325
- transcriptPath: watcher.transcriptPath,
79326
- lineOffset: watcher.lineCounter,
79327
- state: watcher.state
79328
- });
79329
- watcher.lineCounter++;
79330
- if (event) {
79331
- if (watcher.emittedEventIds.has(event.id))
80280
+ const combined = watcher.carry + text4;
80281
+ const lines = combined.split(`
80282
+ `);
80283
+ watcher.carry = lines.pop() ?? "";
80284
+ for (const line of lines) {
80285
+ if (!line)
79332
80286
  continue;
79333
- watcher.emittedEventIds.add(event.id);
79334
- pushEvent(event);
80287
+ const event = watcher.source.parseLine(line, {
80288
+ process: watcher.process,
80289
+ transcript: watcher.transcript,
80290
+ transcriptPath: watcher.transcriptPath,
80291
+ lineOffset: watcher.lineCounter,
80292
+ state: watcher.state
80293
+ });
80294
+ watcher.lineCounter++;
80295
+ if (event) {
80296
+ rememberWatcherEvent(watcher, event, "all");
80297
+ }
79335
80298
  }
79336
80299
  }
80300
+ if (stats.size > watcher.offset) {
80301
+ const continuation = setTimeout(() => {
80302
+ pumpWatcher(watcher);
80303
+ }, 0);
80304
+ continuation.unref?.();
80305
+ }
79337
80306
  } catch {} finally {
79338
80307
  await handle?.close().catch(() => {});
79339
80308
  }
79340
80309
  }
79341
- async function pumpAllWatchers() {
79342
- await Promise.allSettled([...watchers.values()].map(pumpWatcher));
80310
+ function scheduleWatcherPump(run) {
80311
+ return new Promise((resolve20, reject) => {
80312
+ const start = () => {
80313
+ activeWatcherPumps++;
80314
+ Promise.resolve().then(run).then(resolve20, reject).finally(() => {
80315
+ activeWatcherPumps--;
80316
+ watcherPumpQueue.shift()?.();
80317
+ });
80318
+ };
80319
+ if (activeWatcherPumps < WATCHER_PUMP_CONCURRENCY) {
80320
+ start();
80321
+ } else {
80322
+ watcherPumpQueue.push(start);
80323
+ }
80324
+ });
79343
80325
  }
79344
- async function seedTail(watcher) {
80326
+ function pumpWatcher(watcher, observedAt = Date.now()) {
80327
+ if (watcher.pumpInFlight) {
80328
+ return watcher.pumpInFlight;
80329
+ }
80330
+ const request = scheduleWatcherPump(() => runWatcherPump(watcher, observedAt));
80331
+ watcher.pumpInFlight = request;
80332
+ request.finally(() => {
80333
+ if (watcher.pumpInFlight === request) {
80334
+ watcher.pumpInFlight = null;
80335
+ }
80336
+ }).catch(() => {});
80337
+ return request;
80338
+ }
80339
+ function selectWatchersForCurrentDemand(now = Date.now()) {
80340
+ const all = [...watchers.values()];
80341
+ if (subscribers.size > 0)
80342
+ return all;
80343
+ if (internalSubscribers.size === 0)
80344
+ return [];
80345
+ const hot = [];
80346
+ const coldDue = [];
80347
+ for (const watcher of all) {
80348
+ if (now - watcher.lastObservedChangeAt <= INTERNAL_HOT_WATCHER_WINDOW_MS) {
80349
+ hot.push(watcher);
80350
+ } else if (now - watcher.lastPumpAt >= INTERNAL_IDLE_WATCHER_INTERVAL_MS) {
80351
+ coldDue.push(watcher);
80352
+ }
80353
+ }
80354
+ coldDue.sort((left, right) => left.lastPumpAt - right.lastPumpAt);
80355
+ return [...hot, ...coldDue.slice(0, INTERNAL_IDLE_WATCHER_BATCH_SIZE)];
80356
+ }
80357
+ async function pumpWatchersForCurrentDemand(now = Date.now()) {
80358
+ const selected = selectWatchersForCurrentDemand(now);
80359
+ await Promise.allSettled(selected.map((watcher) => pumpWatcher(watcher, now)));
80360
+ }
80361
+ async function seedTail(watcher, options = { reconcileInternal: false }) {
80362
+ let stats;
79345
80363
  try {
79346
- const stats = await stat9(watcher.transcriptPath);
79347
- watcher.offset = watcher.source.parseFile ? 0 : stats.size;
79348
- if (!watcher.source.parseFile && watcher.source.name === "codex") {
79349
- const lines = await readRecentTranscriptLines(watcher.transcriptPath, RECENT_TRANSCRIPT_LINES_PER_FILE);
80364
+ stats = await stat9(watcher.transcriptPath);
80365
+ } catch {
80366
+ watcher.offset = 0;
80367
+ return;
80368
+ }
80369
+ const seededAt = Date.now();
80370
+ watcher.offset = options.reconcileInternal || !watcher.source.parseFile ? stats.size : 0;
80371
+ watcher.lastPumpAt = seededAt;
80372
+ watcher.lastKnownSize = stats.size;
80373
+ watcher.lastKnownMtimeMs = stats.mtimeMs;
80374
+ watcher.lastObservedChangeAt = Math.max(watcher.transcript.lastEventAt ?? 0, watcher.transcript.mtimeMs, stats.mtimeMs);
80375
+ try {
80376
+ if (options.reconcileInternal && watcher.source.parseFile) {
80377
+ const text4 = await readTranscriptText(watcher.transcriptPath, INTERNAL_RECONCILE_READ_BYTES);
80378
+ if (!text4)
80379
+ return;
80380
+ const events2 = parsedEventsToArray(watcher.source.parseFile(text4, {
80381
+ process: watcher.process,
80382
+ transcript: watcher.transcript,
80383
+ transcriptPath: watcher.transcriptPath,
80384
+ lineOffset: 0,
80385
+ state: watcher.state
80386
+ }));
80387
+ watcher.lineCounter = Math.max(1, events2.length);
80388
+ for (const event of events2)
80389
+ rememberWatcherEvent(watcher, event, "internal");
80390
+ return;
80391
+ }
80392
+ if (!watcher.source.parseFile && (options.reconcileInternal || watcher.source.name === "codex")) {
80393
+ const lines = await readRecentTranscriptLines(watcher.transcriptPath, options.reconcileInternal ? INTERNAL_RECONCILE_LINES_PER_SESSION : RECENT_TRANSCRIPT_LINES_PER_FILE, options.reconcileInternal ? INTERNAL_RECONCILE_READ_BYTES : RECENT_TRANSCRIPT_READ_BYTES);
79350
80394
  lines.forEach((line, index2) => {
79351
- watcher.source.parseLine(line, {
80395
+ const parsed = watcher.source.parseLine(line, {
79352
80396
  process: watcher.process,
79353
80397
  transcript: watcher.transcript,
79354
80398
  transcriptPath: watcher.transcriptPath,
79355
80399
  lineOffset: index2,
79356
80400
  state: watcher.state
79357
80401
  });
80402
+ if (options.reconcileInternal && parsed) {
80403
+ rememberWatcherEvent(watcher, parsed, "internal");
80404
+ }
79358
80405
  });
80406
+ watcher.lineCounter = lines.length;
79359
80407
  }
79360
- } catch {
79361
- watcher.offset = 0;
80408
+ } catch {}
80409
+ }
80410
+ function reconcileMissingWatchers(seenSessionKeys, successfullyScannedSources, observedAt) {
80411
+ for (const [sessionKey4, watcher] of watchers) {
80412
+ if (seenSessionKeys.has(sessionKey4)) {
80413
+ watcher.missingAuthoritativePasses = 0;
80414
+ continue;
80415
+ }
80416
+ if (!successfullyScannedSources.has(watcher.source.name))
80417
+ continue;
80418
+ watcher.missingAuthoritativePasses++;
80419
+ if (watcher.missingAuthoritativePasses < AUTHORITATIVE_MISSING_CONFIRMATION_COUNT) {
80420
+ continue;
80421
+ }
80422
+ notifyWatcherLifecycle(watcher, "missing", observedAt);
80423
+ watchers.delete(sessionKey4);
80424
+ knownTranscripts.delete(sessionKey4);
80425
+ }
80426
+ }
80427
+ function reconcilePersistedObservedSessions(seenLifecycleKeys, successfullyScannedSources, observedAt) {
80428
+ for (const [lifecycleKey, lifecycle] of persistedObservedSessions) {
80429
+ if (seenLifecycleKeys.has(lifecycleKey)) {
80430
+ lifecycle.missingAuthoritativePasses = 0;
80431
+ continue;
80432
+ }
80433
+ if (!successfullyScannedSources.has(lifecycle.seed.source))
80434
+ continue;
80435
+ lifecycle.missingAuthoritativePasses++;
80436
+ if (lifecycle.missingAuthoritativePasses < AUTHORITATIVE_MISSING_CONFIRMATION_COUNT || lifecycle.lifecycleNotification === "offline") {
80437
+ continue;
80438
+ }
80439
+ lifecycle.lifecycleNotification = "offline";
80440
+ const seedEvent = persistedSeedEvent(lifecycle.seed);
80441
+ pushInternalEvent({
80442
+ ...seedEvent,
80443
+ id: `tail-lifecycle:${lifecycle.seed.source}:${lifecycle.seed.sourceSessionId}:offline:${observedAt}`,
80444
+ ts: observedAt,
80445
+ summary: INTERNAL_TAIL_SESSION_OFFLINE_SUMMARY,
80446
+ raw: { reason: "missing" }
80447
+ });
80448
+ persistedObservedSessions.delete(lifecycleKey);
79362
80449
  }
79363
80450
  }
79364
80451
  async function refreshDiscovery(scope = "shallow", options = { pruneMissing: scope !== "hot" }) {
79365
80452
  const allProcesses = [];
79366
80453
  const cachedProcesses = lastDiscovery?.processes ?? [];
79367
80454
  const seenSessionKeys = new Set;
80455
+ const seenObservedLifecycleKeys = new Set;
80456
+ const successfullyScannedSources = new Set;
79368
80457
  const discoveryIssues = [];
79369
80458
  const discoveryIssueKeys = new Set;
79370
80459
  for (const source of sources) {
80460
+ let remainingInternalReconciliations = internalSubscribers.size > 0 ? INTERNAL_RECONCILE_SESSIONS_PER_SOURCE : 0;
79371
80461
  let processes = [];
79372
80462
  if (scope === "hot" && cachedProcesses.length > 0) {
79373
80463
  processes = cachedProcesses.filter((proc) => proc.source === source.name);
@@ -79382,14 +80472,19 @@ async function refreshDiscovery(scope = "shallow", options = { pruneMissing: sco
79382
80472
  let transcripts = [];
79383
80473
  try {
79384
80474
  transcripts = await source.discoverTranscripts(processes, scope);
80475
+ successfullyScannedSources.add(source.name);
79385
80476
  } catch {
79386
80477
  transcripts = [];
79387
80478
  }
79388
- for (const transcript of transcripts) {
80479
+ for (const transcript of [...transcripts].sort((left, right) => right.mtimeMs - left.mtimeMs)) {
79389
80480
  const primary = processForTranscript(transcript, processes);
79390
80481
  const transcriptPath = transcript.transcriptPath;
79391
80482
  const sessionKey4 = sessionRegistryKey(transcript);
79392
80483
  seenSessionKeys.add(sessionKey4);
80484
+ const sourceSessionId = transcript.sessionId?.trim();
80485
+ if (sourceSessionId) {
80486
+ seenObservedLifecycleKeys.add(observedSessionLifecycleKey(source.name, sourceSessionId));
80487
+ }
79393
80488
  const prior = knownTranscripts.get(sessionKey4);
79394
80489
  if (transcript.source !== "cursor" && prior && prior.transcriptPath !== transcriptPath) {
79395
80490
  const issueKey = `${sessionKey4}\x00${prior.transcriptPath}\x00${transcriptPath}`;
@@ -79417,10 +80512,25 @@ async function refreshDiscovery(scope = "shallow", options = { pruneMissing: sco
79417
80512
  offset: 0,
79418
80513
  lineCounter: 0,
79419
80514
  carry: "",
80515
+ decoder: new StringDecoder("utf8"),
79420
80516
  emittedEventIds: new Set,
79421
- state: {}
80517
+ state: {},
80518
+ lastPumpAt: 0,
80519
+ lastObservedChangeAt: Math.max(transcript.lastEventAt ?? 0, transcript.mtimeMs),
80520
+ lastKnownSize: transcript.size,
80521
+ lastKnownMtimeMs: transcript.mtimeMs,
80522
+ missingAuthoritativePasses: 0,
80523
+ staleObservations: 0,
80524
+ lifecycleNotification: "none",
80525
+ internalObserved: false,
80526
+ lastInternalEvent: null,
80527
+ pumpInFlight: null
79422
80528
  };
79423
- await seedTail(watcher);
80529
+ const reconcileInternal = remainingInternalReconciliations > 0;
80530
+ if (reconcileInternal)
80531
+ remainingInternalReconciliations--;
80532
+ await seedTail(watcher, { reconcileInternal });
80533
+ applyPersistedObservedSeedToWatcher(watcher);
79424
80534
  watchers.set(sessionKey4, watcher);
79425
80535
  continue;
79426
80536
  }
@@ -79428,21 +80538,33 @@ async function refreshDiscovery(scope = "shallow", options = { pruneMissing: sco
79428
80538
  watcher.transcriptPath = transcriptPath;
79429
80539
  watcher.lineCounter = 0;
79430
80540
  watcher.carry = "";
80541
+ watcher.decoder = new StringDecoder("utf8");
79431
80542
  watcher.emittedEventIds = new Set;
79432
80543
  watcher.state = {};
79433
- await seedTail(watcher);
80544
+ watcher.internalObserved = false;
80545
+ watcher.lastInternalEvent = null;
80546
+ const reconcileInternal = remainingInternalReconciliations > 0;
80547
+ if (reconcileInternal)
80548
+ remainingInternalReconciliations--;
80549
+ await seedTail(watcher, { reconcileInternal });
80550
+ }
80551
+ applyPersistedObservedSeedToWatcher(watcher);
80552
+ if (transcript.mtimeMs > watcher.transcript.mtimeMs) {
80553
+ watcher.lastObservedChangeAt = Date.now();
80554
+ watcher.staleObservations = 0;
80555
+ watcher.lifecycleNotification = "none";
80556
+ if (watcher.source.parseFile)
80557
+ watcher.offset = 0;
79434
80558
  }
79435
80559
  watcher.process = primary;
79436
80560
  watcher.transcript = transcript;
80561
+ watcher.missingAuthoritativePasses = 0;
79437
80562
  }
79438
80563
  }
79439
80564
  if (options.pruneMissing) {
79440
- for (const sessionKey4 of [...watchers.keys()]) {
79441
- if (!seenSessionKeys.has(sessionKey4)) {
79442
- watchers.delete(sessionKey4);
79443
- knownTranscripts.delete(sessionKey4);
79444
- }
79445
- }
80565
+ const observedAt = Date.now();
80566
+ reconcileMissingWatchers(seenSessionKeys, successfullyScannedSources, observedAt);
80567
+ reconcilePersistedObservedSessions(seenObservedLifecycleKeys, successfullyScannedSources, observedAt);
79446
80568
  }
79447
80569
  const allTranscripts = [...knownTranscripts.values()].sort((a, b) => b.mtimeMs - a.mtimeMs);
79448
80570
  let scoutManaged = 0;
@@ -79486,7 +80608,7 @@ function scheduleDiscovery(scope, options = { pruneMissing: scope !== "hot" }) {
79486
80608
  function ensureLoopRunning() {
79487
80609
  if (!pollTimer) {
79488
80610
  pollTimer = setInterval(() => {
79489
- pumpAllWatchers();
80611
+ pumpWatchersForCurrentDemand();
79490
80612
  }, TAIL_POLL_INTERVAL_MS);
79491
80613
  }
79492
80614
  if (!hotDiscoveryTimer) {
@@ -79506,7 +80628,7 @@ function ensureLoopRunning() {
79506
80628
  }
79507
80629
  }
79508
80630
  function stopLoopIfIdle() {
79509
- if (subscribers.size > 0)
80631
+ if (subscribers.size > 0 || internalSubscribers.size > 0)
79510
80632
  return;
79511
80633
  if (pollTimer) {
79512
80634
  clearInterval(pollTimer);
@@ -79525,6 +80647,7 @@ function stopLoopIfIdle() {
79525
80647
  deepDiscoveryTimer = null;
79526
80648
  }
79527
80649
  watchers.clear();
80650
+ persistedObservedSessions.clear();
79528
80651
  }
79529
80652
  function normalizeTailDiscoveryOptions(input = false) {
79530
80653
  if (typeof input === "boolean") {
@@ -79550,18 +80673,19 @@ async function refreshTailDiscovery(scope = "shallow") {
79550
80673
  }
79551
80674
  function subscribeTail(handler) {
79552
80675
  subscribers.add(handler);
80676
+ startTailLoopForSubscriber();
80677
+ return () => {
80678
+ subscribers.delete(handler);
80679
+ stopLoopIfIdle();
80680
+ };
80681
+ }
80682
+ function startTailLoopForSubscriber() {
79553
80683
  ensureLoopRunning();
79554
80684
  if (watchers.size === 0) {
79555
- runDiscovery("shallow", { pruneMissing: true }).then(() => pumpAllWatchers()).catch(() => {});
80685
+ runDiscovery("shallow", { pruneMissing: true }).then(() => pumpWatchersForCurrentDemand()).catch(() => {});
79556
80686
  } else {
79557
- pumpAllWatchers();
80687
+ pumpWatchersForCurrentDemand();
79558
80688
  }
79559
- return () => {
79560
- subscribers.delete(handler);
79561
- if (subscribers.size === 0) {
79562
- stopLoopIfIdle();
79563
- }
79564
- };
79565
80689
  }
79566
80690
  function snapshotRecentEvents(limit = 500) {
79567
80691
  return aggregateBuffer.slice(-limit);
@@ -79755,7 +80879,7 @@ async function readRecentTranscriptEvents(limit = 50, options) {
79755
80879
  const lineLimit = options?.perTranscriptLineLimit ?? RECENT_TRANSCRIPT_LINES_PER_FILE;
79756
80880
  const kinds = options?.kinds?.length ? new Set(options.kinds) : null;
79757
80881
  const kindQuota = kinds ? Math.max(1, options?.perTranscriptKindLimit ?? limit) : undefined;
79758
- const activeWatchers = [...watchers.values()].sort((left, right) => right.transcript.mtimeMs - left.transcript.mtimeMs).slice(0, transcriptReadLimit);
80882
+ const activeWatchers = [...watchers.values()].filter((watcher) => options?.since === undefined || watcher.transcript.mtimeMs >= options.since).sort((left, right) => right.transcript.mtimeMs - left.transcript.mtimeMs).slice(0, transcriptReadLimit);
79759
80883
  for (const watcher of activeWatchers) {
79760
80884
  seenTranscriptPaths.add(watcher.transcriptPath);
79761
80885
  await appendWatcherTranscriptEvents(events2, seenEvents, watcher, {
@@ -79765,7 +80889,7 @@ async function readRecentTranscriptEvents(limit = 50, options) {
79765
80889
  });
79766
80890
  }
79767
80891
  if (options?.discovery?.transcripts?.length) {
79768
- const discoveryTranscripts = [...options.discovery.transcripts].filter((transcript) => !seenTranscriptPaths.has(transcript.transcriptPath)).sort((left, right) => right.mtimeMs - left.mtimeMs).slice(0, transcriptReadLimit);
80892
+ const discoveryTranscripts = [...options.discovery.transcripts].filter((transcript) => !seenTranscriptPaths.has(transcript.transcriptPath) && (options.since === undefined || transcript.mtimeMs >= options.since)).sort((left, right) => right.mtimeMs - left.mtimeMs).slice(0, Math.max(0, transcriptReadLimit - activeWatchers.length));
79769
80893
  for (const transcript of discoveryTranscripts) {
79770
80894
  const parsed = await parseTranscriptSessionEvents(transcript, options.discovery.processes, lineLimit, {
79771
80895
  kinds,
@@ -79778,7 +80902,7 @@ async function readRecentTranscriptEvents(limit = 50, options) {
79778
80902
  seenTranscriptPaths.add(transcript.transcriptPath);
79779
80903
  }
79780
80904
  }
79781
- return events2.filter((event) => !kinds || kinds.has(event.kind)).sort((left, right) => right.ts - left.ts).slice(0, limit);
80905
+ return events2.filter((event) => (!kinds || kinds.has(event.kind)) && (options?.since === undefined || event.ts >= options.since)).sort((left, right) => right.ts - left.ts).slice(0, limit);
79782
80906
  }
79783
80907
  function normalizeTailSessionRef(value) {
79784
80908
  const trimmed = value?.trim();
@@ -80105,7 +81229,7 @@ function buildObserveDataFromTail(transcript, events2, current) {
80105
81229
  var HISTORY_SNAPSHOT_CACHE_LIMIT = 128;
80106
81230
  var historySnapshotCache = new Map;
80107
81231
  var OBSERVE_SUMMARY_TAIL_SIZE = 8;
80108
- var SESSION_REF_LOOKUP_TTL_MS = 1e4;
81232
+ var SESSION_REF_LOOKUP_TTL_MS = 60000;
80109
81233
  var sessionRefLookupCache = null;
80110
81234
  var AMBIGUOUS_TAIL_SESSION_REF = Symbol("ambiguous-tail-session-ref");
80111
81235
  function normalizedComparableString2(value) {
@@ -80194,6 +81318,23 @@ function tailSessionRefTranscripts(discovery, normalizedRef, harness2) {
80194
81318
  return refs.includes(normalizedRef);
80195
81319
  });
80196
81320
  }
81321
+ function bestTailSessionRefLookupEntry(transcripts, normalizedRef) {
81322
+ const matches = [];
81323
+ for (const transcript of transcripts) {
81324
+ const adapterType = adapterTypeFromTailSource(transcript.source);
81325
+ if (!adapterType || !supportsHistorySessionSnapshotForPath(transcript.transcriptPath, adapterType)) {
81326
+ continue;
81327
+ }
81328
+ matches.push({
81329
+ refId: normalizedRef,
81330
+ historyPath: transcript.transcriptPath,
81331
+ adapterType,
81332
+ mtimeMs: transcript.mtimeMs,
81333
+ size: transcript.size
81334
+ });
81335
+ }
81336
+ return matches.sort((left, right) => right.mtimeMs - left.mtimeMs)[0] ?? null;
81337
+ }
80197
81338
  function hasAmbiguousTailHarness(transcripts) {
80198
81339
  const sources2 = new Set(transcripts.map((transcript) => canonicalSessionHarness(transcript.source) ?? transcript.source.trim().toLowerCase()));
80199
81340
  return sources2.size > 1;
@@ -80270,23 +81411,7 @@ async function findTailSessionRefLookupEntry(normalizedRef, harness2 = null) {
80270
81411
  const transcripts = tailSessionRefTranscripts(discovery, normalizedRef, harness2);
80271
81412
  if (!harness2 && hasAmbiguousTailHarness(transcripts))
80272
81413
  return AMBIGUOUS_TAIL_SESSION_REF;
80273
- const matches = [];
80274
- for (const transcript of transcripts) {
80275
- const adapterType = adapterTypeFromTailSource(transcript.source);
80276
- if (!adapterType || !supportsHistorySessionSnapshotForPath(transcript.transcriptPath, adapterType)) {
80277
- continue;
80278
- }
80279
- matches.push({
80280
- refId: normalizedRef,
80281
- historyPath: transcript.transcriptPath,
80282
- adapterType,
80283
- mtimeMs: transcript.mtimeMs,
80284
- size: transcript.size
80285
- });
80286
- }
80287
- if (matches.length === 0)
80288
- return null;
80289
- return matches.sort((left, right) => right.mtimeMs - left.mtimeMs)[0] ?? null;
81414
+ return bestTailSessionRefLookupEntry(transcripts, normalizedRef);
80290
81415
  }
80291
81416
  function historyAdapterAlias(value) {
80292
81417
  const normalized = value?.trim().toLowerCase();
@@ -81707,11 +82832,12 @@ async function loadSessionRefObservePayload(refId) {
81707
82832
  return null;
81708
82833
  }
81709
82834
  let [discovery, brokerContext] = await Promise.all([
81710
- getTailDiscovery().catch(() => null),
82835
+ readScoutBrokerTailDiscovery().catch(() => null),
81711
82836
  loadScoutBrokerContext().catch(() => null)
81712
82837
  ]);
81713
82838
  let matchingTranscripts = discovery ? tailSessionRefTranscripts(discovery, normalizedRef, requestedHarness) : [];
81714
- const cachedHistoryEntry = !requestedHarness || requestedHarness === "claude" ? sessionRefLookup().get(normalizedRef) ?? null : null;
82839
+ const discoveredHistoryEntry = bestTailSessionRefLookupEntry(matchingTranscripts, normalizedRef);
82840
+ const cachedHistoryEntry = !discoveredHistoryEntry && matchingTranscripts.length === 0 && (!requestedHarness || requestedHarness === "claude") ? sessionRefLookup().get(normalizedRef) ?? null : null;
81715
82841
  const matchingAgents = queryAgents(200).filter((agent) => normalizeSessionRefId(agent.harnessSessionId) === normalizedRef && sessionHarnessMatches(requestedHarness, agent.harness));
81716
82842
  const matchingAgentOwners = new Set(matchingAgents.map((agent) => agent.id));
81717
82843
  if (matchingAgentOwners.size > 1)
@@ -81778,7 +82904,7 @@ async function loadSessionRefObservePayload(refId) {
81778
82904
  }
81779
82905
  if (!requestedHarness && hasAmbiguousTailHarness(matchingTranscripts))
81780
82906
  return null;
81781
- const tailHistoryEntry = await findTailSessionRefLookupEntry(normalizedRef, requestedHarness);
82907
+ const tailHistoryEntry = discoveredHistoryEntry ?? await findTailSessionRefLookupEntry(normalizedRef, requestedHarness);
81782
82908
  if (tailHistoryEntry === AMBIGUOUS_TAIL_SESSION_REF)
81783
82909
  return null;
81784
82910
  const historyEntry = tailHistoryEntry ?? (requestedHarness === "claude" || !requestedHarness && (!discovery || matchingTranscripts.length === 0) ? cachedHistoryEntry : null);
@@ -86450,6 +87576,7 @@ var MAX_EVENTS_PER_SESSION = 200;
86450
87576
  var sessions4 = new Map;
86451
87577
  var hosts = new Map;
86452
87578
  var sessionSubscribers = new Map;
87579
+ var hostCommandWaiters = new Map;
86453
87580
  var nextHostPollId = 1;
86454
87581
  var DEFAULT_VOICE_SETTINGS = {
86455
87582
  preference: "auto",
@@ -86464,6 +87591,9 @@ function registerScoutVoiceHost(input) {
86464
87591
  const now = Date.now();
86465
87592
  const previous = hosts.get(hostId);
86466
87593
  const instanceId = input.instanceId?.trim() || null;
87594
+ if (previous && previous.instanceId !== instanceId) {
87595
+ finishScoutVoiceHostCommandWaiter(hostId, null);
87596
+ }
86467
87597
  const settings = mergeVoiceSettings(previous?.settings ?? DEFAULT_VOICE_SETTINGS, input.settings);
86468
87598
  hosts.set(hostId, {
86469
87599
  hostId,
@@ -86545,6 +87675,7 @@ function awaitScoutVoiceHostCommand(hostId, timeoutMs = 25000, instanceId, signa
86545
87675
  if (host.instanceId !== normalizedInstanceId) {
86546
87676
  return Promise.resolve({ command: null });
86547
87677
  }
87678
+ finishScoutVoiceHostCommandWaiter(normalizedHostId, null);
86548
87679
  const pollId = nextHostPollId++;
86549
87680
  host.activePollId = pollId;
86550
87681
  touchScoutVoiceHost(normalizedHostId);
@@ -86559,40 +87690,37 @@ function awaitScoutVoiceHostCommand(hostId, timeoutMs = 25000, instanceId, signa
86559
87690
  return Promise.resolve({ command: null });
86560
87691
  }
86561
87692
  return new Promise((resolve23) => {
86562
- const startedAt = Date.now();
86563
87693
  let settled = false;
87694
+ let timeout = null;
86564
87695
  const finish = (command) => {
86565
87696
  if (settled)
86566
87697
  return;
86567
87698
  settled = true;
86568
- clearInterval(timer);
87699
+ if (timeout)
87700
+ clearTimeout(timeout);
86569
87701
  signal?.removeEventListener("abort", abort);
87702
+ const waiter = hostCommandWaiters.get(normalizedHostId);
87703
+ if (waiter?.pollId === pollId)
87704
+ hostCommandWaiters.delete(normalizedHostId);
86570
87705
  const current = hosts.get(normalizedHostId);
86571
87706
  if (current?.activePollId === pollId)
86572
87707
  current.activePollId = null;
86573
87708
  resolve23({ command });
86574
87709
  };
86575
87710
  const abort = () => finish(null);
86576
- const timer = setInterval(() => {
87711
+ hostCommandWaiters.set(normalizedHostId, {
87712
+ pollId,
87713
+ instanceId: normalizedInstanceId,
87714
+ finish
87715
+ });
87716
+ timeout = setTimeout(() => {
86577
87717
  const current = hosts.get(normalizedHostId);
86578
- if (!current) {
86579
- finish(null);
86580
- return;
86581
- }
86582
- if (current.instanceId !== normalizedInstanceId || current.activePollId !== pollId) {
86583
- finish(null);
86584
- return;
86585
- }
86586
- current.lastSeenAt = Date.now();
86587
- if (current.pendingCommands.length > 0) {
86588
- const command = current.pendingCommands.shift() ?? null;
86589
- finish(command);
86590
- return;
87718
+ if (current && current.instanceId === normalizedInstanceId && current.activePollId === pollId) {
87719
+ current.lastSeenAt = Date.now();
86591
87720
  }
86592
- if (Date.now() - startedAt >= timeoutMs) {
86593
- finish(null);
86594
- }
86595
- }, 100);
87721
+ finish(null);
87722
+ }, timeoutMs);
87723
+ timeout.unref?.();
86596
87724
  signal?.addEventListener("abort", abort, { once: true });
86597
87725
  if (signal?.aborted)
86598
87726
  abort();
@@ -86813,6 +87941,22 @@ function queueHostCommand(hostId, command) {
86813
87941
  }
86814
87942
  host.pendingCommands.push(command);
86815
87943
  host.lastSeenAt = Date.now();
87944
+ deliverPendingScoutVoiceHostCommand(host);
87945
+ }
87946
+ function finishScoutVoiceHostCommandWaiter(hostId, command) {
87947
+ hostCommandWaiters.get(hostId)?.finish(command);
87948
+ }
87949
+ function deliverPendingScoutVoiceHostCommand(host) {
87950
+ const waiter = hostCommandWaiters.get(host.hostId);
87951
+ if (!waiter)
87952
+ return;
87953
+ if (waiter.instanceId !== host.instanceId || waiter.pollId !== host.activePollId) {
87954
+ waiter.finish(null);
87955
+ return;
87956
+ }
87957
+ const command = host.pendingCommands.shift();
87958
+ if (command)
87959
+ waiter.finish(command);
86816
87960
  }
86817
87961
  function dispatchSessionCommand(session3, command) {
86818
87962
  const hostId = session3.assignedHostId ?? pickLiveVoiceHost()?.hostId;
@@ -87226,6 +88370,127 @@ function defaultVoiceForModel(providers2, modelId2) {
87226
88370
  return firstNonEmptyString7(provider?.env.VOX_MLX_AUDIO_TTS_DEFAULT_VOICE);
87227
88371
  }
87228
88372
 
88373
+ // packages/web/server/nvidia-speech.ts
88374
+ var NVIDIA_MAGPIE_SPEECH_URL = "https://877104f7-e885-42b9-8de8-f6e4c6303969.invocation.api.nvcf.nvidia.com/v1/audio/synthesize";
88375
+ var NVIDIA_MAGPIE_VOICES_URL = "https://877104f7-e885-42b9-8de8-f6e4c6303969.invocation.api.nvcf.nvidia.com/v1/audio/list_voices";
88376
+ var NVIDIA_MAGPIE_MODEL = "magpie-tts-multilingual";
88377
+ var NVIDIA_MAGPIE_DEFAULT_VOICE = "Magpie-Multilingual.EN-US.Aria";
88378
+ var NVIDIA_MAGPIE_TIMEOUT_MS = 60000;
88379
+ var NVIDIA_MAGPIE_MAX_CHARACTERS = 2000;
88380
+ function resolveNvidiaApiKey(env = process.env) {
88381
+ return firstNonEmptyString8(env.NV_API_KEY, env.NVIDIA_API_KEY);
88382
+ }
88383
+ function isNvidiaMagpieSpeechModel(modelId2) {
88384
+ return modelId2?.trim() === NVIDIA_MAGPIE_MODEL;
88385
+ }
88386
+ async function listNvidiaMagpieVoices(input) {
88387
+ const timeout = AbortSignal.timeout(NVIDIA_MAGPIE_TIMEOUT_MS);
88388
+ const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
88389
+ const response = await fetch(input.endpoint ?? NVIDIA_MAGPIE_VOICES_URL, {
88390
+ method: "GET",
88391
+ headers: { authorization: `Bearer ${input.apiKey}` },
88392
+ signal
88393
+ });
88394
+ if (!response.ok) {
88395
+ const detail = await response.text().catch(() => "");
88396
+ throw new Error(`NVIDIA Magpie voice discovery failed (${response.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`);
88397
+ }
88398
+ const voices = parseNvidiaMagpieVoices(await response.json());
88399
+ if (voices.length === 0) {
88400
+ throw new Error("NVIDIA Magpie voice discovery returned no voices.");
88401
+ }
88402
+ return voices;
88403
+ }
88404
+ async function synthesizeNvidiaMagpieSpeech(input) {
88405
+ const text4 = Array.from(input.text).slice(0, NVIDIA_MAGPIE_MAX_CHARACTERS).join("");
88406
+ const voiceId = input.voiceId?.trim() || NVIDIA_MAGPIE_DEFAULT_VOICE;
88407
+ const language = magpieLanguageForVoice(voiceId);
88408
+ const form = new FormData;
88409
+ form.set("text", text4);
88410
+ form.set("language", language);
88411
+ form.set("voice", voiceId);
88412
+ form.set("encoding", "LINEAR_PCM");
88413
+ form.set("sample_rate_hz", "44100");
88414
+ const timeout = AbortSignal.timeout(NVIDIA_MAGPIE_TIMEOUT_MS);
88415
+ const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
88416
+ const response = await fetch(input.endpoint ?? NVIDIA_MAGPIE_SPEECH_URL, {
88417
+ method: "POST",
88418
+ headers: { authorization: `Bearer ${input.apiKey}` },
88419
+ body: form,
88420
+ signal
88421
+ });
88422
+ if (!response.ok) {
88423
+ const detail = await response.text().catch(() => "");
88424
+ throw new Error(`NVIDIA Magpie speech failed (${response.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`);
88425
+ }
88426
+ const audio = new Uint8Array(await response.arrayBuffer());
88427
+ if (audio.byteLength === 0) {
88428
+ throw new Error("NVIDIA Magpie returned no audio.");
88429
+ }
88430
+ const responseContentType = response.headers.get("content-type")?.split(";")[0];
88431
+ return {
88432
+ contentType: responseContentType?.startsWith("audio/") ? responseContentType : "audio/wav",
88433
+ audioBase64: Buffer.from(audio).toString("base64"),
88434
+ modelId: NVIDIA_MAGPIE_MODEL,
88435
+ voiceId,
88436
+ audioBytes: audio.byteLength
88437
+ };
88438
+ }
88439
+ function magpieLanguageForVoice(voiceId) {
88440
+ const locale = magpieLocaleSegment(voiceId);
88441
+ if (!locale)
88442
+ return "en-US";
88443
+ const [language, region] = locale.split("-");
88444
+ return `${language.toLowerCase()}-${region.toUpperCase()}`;
88445
+ }
88446
+ function parseNvidiaMagpieVoices(value) {
88447
+ if (!value || typeof value !== "object" || Array.isArray(value))
88448
+ return [];
88449
+ const ids = new Set;
88450
+ for (const group of Object.values(value)) {
88451
+ if (!group || typeof group !== "object" || Array.isArray(group))
88452
+ continue;
88453
+ const voices = group.voices;
88454
+ if (!Array.isArray(voices))
88455
+ continue;
88456
+ for (const voice of voices) {
88457
+ if (typeof voice !== "string")
88458
+ continue;
88459
+ const id = voice.trim();
88460
+ if (id)
88461
+ ids.add(id);
88462
+ }
88463
+ }
88464
+ return Array.from(ids, voiceDetails).sort((left, right) => left.language.localeCompare(right.language) || left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
88465
+ }
88466
+ function voiceDetails(id) {
88467
+ const parts = id.split(".");
88468
+ const language = magpieLanguageForVoice(id);
88469
+ const localeIndex = parts.findIndex(isLocaleSegment);
88470
+ const nameParts = localeIndex >= 0 ? parts.slice(localeIndex + 1) : [];
88471
+ const name2 = nameParts.length > 0 ? nameParts.join(" \xB7 ") : parts.at(-1) || id;
88472
+ return {
88473
+ id,
88474
+ name: name2,
88475
+ language,
88476
+ isDefault: id === NVIDIA_MAGPIE_DEFAULT_VOICE
88477
+ };
88478
+ }
88479
+ function magpieLocaleSegment(voiceId) {
88480
+ return voiceId.split(".").find(isLocaleSegment);
88481
+ }
88482
+ function isLocaleSegment(value) {
88483
+ return /^[a-z]{2,3}-[a-z]{2}$/i.test(value);
88484
+ }
88485
+ function firstNonEmptyString8(...values) {
88486
+ for (const value of values) {
88487
+ const trimmed = value?.trim();
88488
+ if (trimmed)
88489
+ return trimmed;
88490
+ }
88491
+ return;
88492
+ }
88493
+
87229
88494
  // packages/web/server/scout-voice.ts
87230
88495
  var SCOUT_VOICE_CLIENT_ID = "openscout-web";
87231
88496
  var DEFAULT_SCOUT_VOICE_ASR_URL = "http://127.0.0.1:43115";
@@ -87268,14 +88533,16 @@ async function getScoutSpeechCatalog(input = {}) {
87268
88533
  let models = [];
87269
88534
  let voices = [];
87270
88535
  let source = "fallback";
87271
- try {
87272
- [models, voices] = await Promise.all([
87273
- listVoxSpeechModels(input.signal),
87274
- listVoxSpeechVoices(requestedModelId, input.signal)
87275
- ]);
87276
- if (models.length > 0)
87277
- source = "vox";
87278
- } catch {}
88536
+ if (requestedModelId !== NVIDIA_MAGPIE_MODEL) {
88537
+ try {
88538
+ [models, voices] = await Promise.all([
88539
+ listVoxSpeechModels(input.signal),
88540
+ listVoxSpeechVoices(requestedModelId, input.signal)
88541
+ ]);
88542
+ if (models.length > 0)
88543
+ source = "vox";
88544
+ } catch {}
88545
+ }
87279
88546
  const fallback = fallbackScoutSpeechCatalog(requestedModelId, defaults);
87280
88547
  for (const model2 of fallback.models) {
87281
88548
  if (!models.some((candidate) => candidate.id === model2.id)) {
@@ -87289,6 +88556,25 @@ async function getScoutSpeechCatalog(input = {}) {
87289
88556
  models = models.map((model2) => model2.provider === "openai" ? { ...model2, available: true } : model2);
87290
88557
  voices = voices.map((voice) => voice.provider === "openai" ? { ...voice, available: true } : voice);
87291
88558
  }
88559
+ if (input.directNvidiaAvailable || input.directNvidiaApiKey) {
88560
+ models = models.map((model2) => model2.provider === "nvidia" ? { ...model2, available: true } : model2);
88561
+ voices = voices.map((voice) => voice.provider === "nvidia" ? { ...voice, available: true } : voice);
88562
+ }
88563
+ if (input.directNvidiaApiKey && requestedModelId === NVIDIA_MAGPIE_MODEL) {
88564
+ try {
88565
+ const discovered = await listNvidiaMagpieVoices({
88566
+ apiKey: input.directNvidiaApiKey,
88567
+ signal: input.signal
88568
+ });
88569
+ voices = discovered.map((voice) => ({
88570
+ ...voice,
88571
+ provider: "nvidia",
88572
+ modelId: NVIDIA_MAGPIE_MODEL,
88573
+ available: true
88574
+ }));
88575
+ source = "nvidia-developer-inference";
88576
+ } catch {}
88577
+ }
87292
88578
  return {
87293
88579
  defaultModelId: defaults.modelId,
87294
88580
  ...defaults.voiceId ? { defaultVoiceId: defaults.voiceId } : {},
@@ -87300,7 +88586,8 @@ async function getScoutSpeechCatalog(input = {}) {
87300
88586
  function fallbackScoutSpeechCatalog(modelId2, defaults = resolveScoutSpeechDefaults()) {
87301
88587
  const models = [
87302
88588
  { id: "gpt-4o-mini-tts", name: "GPT-4o mini TTS", provider: "openai", available: true },
87303
- { id: "eleven_multilingual_v2", name: "Eleven Multilingual v2", provider: "elevenlabs", available: true }
88589
+ { id: "eleven_multilingual_v2", name: "Eleven Multilingual v2", provider: "elevenlabs", available: true },
88590
+ { id: NVIDIA_MAGPIE_MODEL, name: "Magpie TTS Multilingual", provider: "nvidia", available: true }
87304
88591
  ];
87305
88592
  const openAIVoices = ["alloy", "ash", "ballad", "cedar", "coral", "echo", "fable", "marin", "nova", "onyx", "sage", "shimmer", "verse"].map((id) => ({
87306
88593
  id,
@@ -87318,11 +88605,20 @@ function fallbackScoutSpeechCatalog(modelId2, defaults = resolveScoutSpeechDefau
87318
88605
  available: true,
87319
88606
  isDefault: true
87320
88607
  }];
88608
+ const nvidiaVoices = [{
88609
+ id: NVIDIA_MAGPIE_DEFAULT_VOICE,
88610
+ name: "Aria",
88611
+ language: "en-US",
88612
+ provider: "nvidia",
88613
+ modelId: NVIDIA_MAGPIE_MODEL,
88614
+ available: true,
88615
+ isDefault: true
88616
+ }];
87321
88617
  return {
87322
88618
  defaultModelId: defaults.modelId,
87323
88619
  ...defaults.voiceId ? { defaultVoiceId: defaults.voiceId } : {},
87324
88620
  models,
87325
- voices: modelId2 === "eleven_multilingual_v2" ? elevenLabsVoices : openAIVoices,
88621
+ voices: modelId2 === "eleven_multilingual_v2" ? elevenLabsVoices : modelId2 === NVIDIA_MAGPIE_MODEL ? nvidiaVoices : openAIVoices,
87326
88622
  source: "fallback"
87327
88623
  };
87328
88624
  }
@@ -87337,9 +88633,9 @@ function createScoutVoiceAsrClient(probeTimeout) {
87337
88633
  });
87338
88634
  }
87339
88635
  function resolveScoutVoiceAsrUrl(env = process.env) {
87340
- return firstNonEmptyString8(env.OPENSCOUT_VOICE_ASR_URL, env.OPENSCOUT_VOICE_BRIDGE_URL, env.VOX_COMPANION_URL) ?? DEFAULT_SCOUT_VOICE_ASR_URL;
88636
+ return firstNonEmptyString9(env.OPENSCOUT_VOICE_ASR_URL, env.OPENSCOUT_VOICE_BRIDGE_URL, env.VOX_COMPANION_URL) ?? DEFAULT_SCOUT_VOICE_ASR_URL;
87341
88637
  }
87342
- function firstNonEmptyString8(...values) {
88638
+ function firstNonEmptyString9(...values) {
87343
88639
  for (const value of values) {
87344
88640
  const trimmed = value?.trim();
87345
88641
  if (trimmed)
@@ -87528,9 +88824,9 @@ class ScoutRealtimeVoiceAdmission {
87528
88824
  }
87529
88825
  function resolveScoutRealtimeVoiceConfig(env = process.env) {
87530
88826
  return {
87531
- model: firstNonEmptyString9(env.OPENSCOUT_REALTIME_MODEL) ?? DEFAULT_REALTIME_MODEL,
87532
- voice: firstNonEmptyString9(env.OPENSCOUT_REALTIME_VOICE) ?? DEFAULT_REALTIME_VOICE,
87533
- instructions: firstNonEmptyString9(env.OPENSCOUT_REALTIME_INSTRUCTIONS) ?? SCOUT_REALTIME_INSTRUCTIONS
88827
+ model: firstNonEmptyString10(env.OPENSCOUT_REALTIME_MODEL) ?? DEFAULT_REALTIME_MODEL,
88828
+ voice: firstNonEmptyString10(env.OPENSCOUT_REALTIME_VOICE) ?? DEFAULT_REALTIME_VOICE,
88829
+ instructions: firstNonEmptyString10(env.OPENSCOUT_REALTIME_INSTRUCTIONS) ?? SCOUT_REALTIME_INSTRUCTIONS
87534
88830
  };
87535
88831
  }
87536
88832
  function scoutRealtimeVoiceEnvironmentOverride(env = process.env) {
@@ -87668,7 +88964,7 @@ function positiveInteger3(value, fallback) {
87668
88964
  function secondsUntil(timestamp, now) {
87669
88965
  return Math.max(1, Math.ceil((timestamp - now) / 1000));
87670
88966
  }
87671
- function firstNonEmptyString9(value) {
88967
+ function firstNonEmptyString10(value) {
87672
88968
  const trimmed = value?.trim();
87673
88969
  return trimmed || undefined;
87674
88970
  }
@@ -88035,10 +89331,13 @@ function mountScoutVoiceRoutes(app, deps = {}) {
88035
89331
  const directOpenAIAvailable = Boolean(await deps.resolveOpenAIApiKey?.().catch(() => {
88036
89332
  return;
88037
89333
  }) ?? process.env.OPENAI_API_KEY?.trim());
89334
+ const directNvidiaApiKey = resolveNvidiaApiKey();
88038
89335
  return c.json(await getScoutSpeechCatalog({
88039
89336
  modelId: c.req.query("modelId"),
88040
89337
  signal: c.req.raw.signal,
88041
- directOpenAIAvailable
89338
+ directOpenAIAvailable,
89339
+ directNvidiaAvailable: Boolean(directNvidiaApiKey),
89340
+ directNvidiaApiKey
88042
89341
  }));
88043
89342
  });
88044
89343
  app.post("/api/voice/engage", async (c) => {
@@ -88254,6 +89553,28 @@ function mountScoutVoiceRoutes(app, deps = {}) {
88254
89553
  if (speechTiming === null) {
88255
89554
  return c.json({ error: "speechTiming is invalid" }, 400);
88256
89555
  }
89556
+ const defaults = resolveScoutSpeechDefaults();
89557
+ const requestedModelId = body.modelId ?? defaults.modelId;
89558
+ if (isNvidiaMagpieSpeechModel(requestedModelId)) {
89559
+ const nvidiaApiKey = resolveNvidiaApiKey();
89560
+ if (!nvidiaApiKey) {
89561
+ return c.json({ error: "NV_API_KEY is required for hosted NVIDIA Developer Inference." }, 503);
89562
+ }
89563
+ try {
89564
+ return c.json({
89565
+ ...await synthesizeNvidiaMagpieSpeech({
89566
+ text: text4,
89567
+ apiKey: nvidiaApiKey,
89568
+ voiceId: body.voiceId,
89569
+ signal: c.req.raw.signal
89570
+ }),
89571
+ route: "nvidia-developer-inference"
89572
+ });
89573
+ } catch (error) {
89574
+ const message = error instanceof Error ? error.message : "NVIDIA Magpie speech failed";
89575
+ return c.json({ error: message }, 503);
89576
+ }
89577
+ }
88257
89578
  try {
88258
89579
  return c.json(await synthesizeScoutSpeech({
88259
89580
  text: text4,
@@ -88271,13 +89592,12 @@ function mountScoutVoiceRoutes(app, deps = {}) {
88271
89592
  return;
88272
89593
  }) ?? process.env.OPENAI_API_KEY?.trim();
88273
89594
  if (apiKey && !c.req.raw.signal.aborted) {
88274
- const defaults = resolveScoutSpeechDefaults();
88275
89595
  try {
88276
89596
  return c.json({
88277
89597
  ...await synthesizeOpenAISpeech({
88278
89598
  text: text4,
88279
89599
  apiKey,
88280
- modelId: body.modelId ?? defaults.modelId,
89600
+ modelId: requestedModelId,
88281
89601
  voiceId: body.voiceId ?? defaults.voiceId,
88282
89602
  speed: body.speed,
88283
89603
  instructions: optionalString(body.instructions),
@@ -89300,8 +90620,8 @@ function normalizeExistingPath(value) {
89300
90620
  return null;
89301
90621
  }
89302
90622
  try {
89303
- const stat10 = statSync21(absolute);
89304
- return stat10.isDirectory() ? absolute : dirname37(absolute);
90623
+ const stat11 = statSync21(absolute);
90624
+ return stat11.isDirectory() ? absolute : dirname37(absolute);
89305
90625
  } catch {
89306
90626
  return null;
89307
90627
  }
@@ -90050,8 +91370,8 @@ async function resolveTrustedMaterialPath(work, materialId) {
90050
91370
  if (!pathInsideRoot(realPath, realRoot)) {
90051
91371
  return { ok: false, status: 403, error: "material path is outside its trusted root" };
90052
91372
  }
90053
- const stat10 = statSync21(realPath);
90054
- if (!stat10.isFile()) {
91373
+ const stat11 = statSync21(realPath);
91374
+ if (!stat11.isFile()) {
90055
91375
  return { ok: false, status: 415, error: "material is not a file" };
90056
91376
  }
90057
91377
  return { ok: true, material, realPath };
@@ -90065,12 +91385,12 @@ async function readWorkMaterialRaw(work, materialId) {
90065
91385
  return resolved;
90066
91386
  }
90067
91387
  try {
90068
- const stat10 = statSync21(resolved.realPath);
91388
+ const stat11 = statSync21(resolved.realPath);
90069
91389
  return {
90070
91390
  ok: true,
90071
91391
  realPath: resolved.realPath,
90072
91392
  mediaType: materialMediaType(resolved.realPath),
90073
- sizeBytes: stat10.size
91393
+ sizeBytes: stat11.size
90074
91394
  };
90075
91395
  } catch {
90076
91396
  return { ok: false, status: 404, error: "material could not be read" };
@@ -90100,8 +91420,8 @@ async function readWorkMaterialContent(work, materialId) {
90100
91420
  if (!pathInsideRoot(realPath, realRoot)) {
90101
91421
  return { ok: false, status: 403, error: "material path is outside its trusted root" };
90102
91422
  }
90103
- const stat10 = statSync21(realPath);
90104
- if (!stat10.isFile()) {
91423
+ const stat11 = statSync21(realPath);
91424
+ if (!stat11.isFile()) {
90105
91425
  return { ok: false, status: 415, error: "material is not a file" };
90106
91426
  }
90107
91427
  const buffer = readFileSync29(realPath);
@@ -90133,7 +91453,7 @@ async function readWorkMaterialContent(work, materialId) {
90133
91453
 
90134
91454
  // packages/web/server/plan-documents.ts
90135
91455
  import { createHash as createHash9 } from "crypto";
90136
- import { readdir as readdir4, readFile as readFile17, stat as stat10 } from "fs/promises";
91456
+ import { readdir as readdir5, readFile as readFile17, stat as stat11 } from "fs/promises";
90137
91457
  import { homedir as homedir49 } from "os";
90138
91458
  import { basename as basename25, isAbsolute as isAbsolute10, join as join65, relative as relative8, resolve as resolve27 } from "path";
90139
91459
  var HOME3 = homedir49();
@@ -90216,7 +91536,7 @@ async function walkMarkdownFiles(directory, remaining = { count: MAX_FILES_PER_R
90216
91536
  return [];
90217
91537
  let entries2;
90218
91538
  try {
90219
- entries2 = await readdir4(directory, { withFileTypes: true });
91539
+ entries2 = await readdir5(directory, { withFileTypes: true });
90220
91540
  } catch (error) {
90221
91541
  if (error.code === "ENOENT")
90222
91542
  return [];
@@ -90249,7 +91569,7 @@ async function discoverPlanFiles(root) {
90249
91569
  const fromPlanDirs = (await Promise.all(PLAN_SUBDIRECTORIES.map((subdir) => walkMarkdownFiles(join65(root.root, subdir))))).flat();
90250
91570
  let directPlanFiles = [];
90251
91571
  try {
90252
- const entries2 = await readdir4(root.root, { withFileTypes: true });
91572
+ const entries2 = await readdir5(root.root, { withFileTypes: true });
90253
91573
  directPlanFiles = entries2.filter((entry) => entry.isFile() && /\.plan\.md$/i.test(entry.name)).map((entry) => join65(root.root, entry.name));
90254
91574
  } catch {
90255
91575
  directPlanFiles = [];
@@ -90432,7 +91752,7 @@ function idForPlan(root, filePath) {
90432
91752
  return `plan-doc:${hash}`;
90433
91753
  }
90434
91754
  async function readPlanDocument(root, filePath) {
90435
- const fileStats = await stat10(filePath).catch(() => null);
91755
+ const fileStats = await stat11(filePath).catch(() => null);
90436
91756
  if (!fileStats || !fileStats.isFile() || fileStats.size > MAX_DOCUMENT_BYTES) {
90437
91757
  return null;
90438
91758
  }
@@ -92101,7 +93421,8 @@ function brokerCardAgentsForWeb(broker2) {
92101
93421
  }
92102
93422
  var AGENT_ATTENTION_TTL_MS2 = 1e4;
92103
93423
  var AGENT_BACKGROUND_REFRESH_DELAY_MS = 500;
92104
- var AGENT_BROKER_CONTEXT_TTL_MS = 5000;
93424
+ var AGENT_BROKER_CONTEXT_TTL_MS = 60000;
93425
+ var AGENT_BROKER_CONTEXT_COLD_BUDGET_MS = 75;
92105
93426
  var agentAttentionCache = null;
92106
93427
  var agentAttentionInFlight = null;
92107
93428
  function queryAgentAttentionIndex(broker2, capture = defaultCaptureTmuxPane) {
@@ -92151,8 +93472,12 @@ function createAgentBrokerContextReader() {
92151
93472
  return promise;
92152
93473
  };
92153
93474
  return () => {
92154
- if (!cache)
92155
- return refresh(0);
93475
+ if (!cache) {
93476
+ return Promise.race([
93477
+ refresh(0),
93478
+ delay2(AGENT_BROKER_CONTEXT_COLD_BUDGET_MS).then(() => null)
93479
+ ]);
93480
+ }
92156
93481
  if (Date.now() - cache.at >= AGENT_BROKER_CONTEXT_TTL_MS && !inFlight) {
92157
93482
  refresh(AGENT_BACKGROUND_REFRESH_DELAY_MS);
92158
93483
  }
@@ -92246,24 +93571,37 @@ function withPinnedMeshPeerAgents(agents, broker2, limit) {
92246
93571
  if (!localNodeId)
92247
93572
  return mostRecentAgents(agents, limit);
92248
93573
  const peerNodeIds = pinnedMeshPeerNodeIds(broker2);
92249
- const peerAgents = brokerCardAgentsForWeb(broker2).filter((agent) => agent.homeNodeId && peerNodeIds.has(agent.homeNodeId));
93574
+ const representativeCardByNode = new Map;
93575
+ for (const agent of Object.values(broker2.snapshot.agents ?? {})) {
93576
+ const homeNodeId = agent.homeNodeId;
93577
+ if (!homeNodeId || !peerNodeIds.has(homeNodeId) || !isBrokerAgentVisibleInWeb(agent))
93578
+ continue;
93579
+ const existing = representativeCardByNode.get(homeNodeId);
93580
+ if (!existing || (latestBrokerAgentTimestamp(agent, null) ?? 0) > (latestBrokerAgentTimestamp(existing, null) ?? 0)) {
93581
+ representativeCardByNode.set(homeNodeId, agent);
93582
+ }
93583
+ }
93584
+ const peerAgents = [...representativeCardByNode.values()].map((agent) => brokerAgentCardToWebAgent(broker2, agent)).filter((agent) => Boolean(agent)).sort((left, right) => (right.updatedAt ?? 0) - (left.updatedAt ?? 0) || left.name.localeCompare(right.name));
92250
93585
  if (peerAgents.length === 0)
92251
93586
  return mostRecentAgents(agents, limit);
92252
93587
  const byId = new Map(agents.map((agent) => [agent.id, agent]));
92253
93588
  for (const peer of peerAgents) {
92254
- byId.set(peer.id, peer);
93589
+ if (!byId.has(peer.id))
93590
+ byId.set(peer.id, peer);
92255
93591
  }
92256
93592
  const merged = [...byId.values()];
92257
93593
  if (limit === undefined)
92258
93594
  return merged;
92259
- const limited = mostRecentAgents(merged, limit);
92260
- const included = new Set(limited.map((agent) => agent.id));
92261
- for (const peer of peerAgents) {
92262
- if (!included.has(peer.id)) {
92263
- limited.push(peer);
92264
- }
93595
+ const representatives = peerAgents.map((peer) => byId.get(peer.id) ?? peer).slice(0, limit);
93596
+ const included = new Set(representatives.map((agent) => agent.id));
93597
+ const limited = [...representatives];
93598
+ for (const agent of mostRecentAgents(merged, undefined)) {
93599
+ if (limited.length >= limit)
93600
+ break;
93601
+ if (included.add(agent.id))
93602
+ limited.push(agent);
92265
93603
  }
92266
- return limited;
93604
+ return mostRecentAgents(limited, undefined);
92267
93605
  }
92268
93606
  function agentListSummary(agent) {
92269
93607
  return {
@@ -92313,13 +93651,11 @@ function agentListSummary(agent) {
92313
93651
  createdAt: agent.createdAt
92314
93652
  };
92315
93653
  }
92316
- async function withAgentRepoKeys(agents) {
93654
+ function withAgentRepoKeys(agents) {
92317
93655
  const roots = agents.map((agent) => agent.projectRoot).filter((root) => Boolean(root));
92318
93656
  if (roots.length === 0)
92319
93657
  return agents;
92320
- const keys = await resolveRepoKeysByRoot(roots).catch(() => null);
92321
- if (!keys)
92322
- return agents;
93658
+ const keys = cachedRepoKeysByRoot(roots);
92323
93659
  return agents.map((agent) => {
92324
93660
  const repoKey = agent.projectRoot ? keys.get(agent.projectRoot) ?? null : null;
92325
93661
  return repoKey ? { ...agent, repoKey } : agent;
@@ -92403,11 +93739,11 @@ async function queryAgentsIncludingBrokerCards(limit, includeRichBrokerContext =
92403
93739
  const brokerById2 = new Map(brokerAgents2.map((agent) => [agent.id, agent]));
92404
93740
  const mergedAgents2 = agents.map((agent) => mergeBrokerAgentProjection(agent, brokerById2.get(agent.id)));
92405
93741
  const existingIds2 = new Set(mergedAgents2.map((agent) => agent.id));
92406
- const broker3 = await loadBrokerContext();
92407
- const roster2 = withPinnedMeshPeerAgents([
93742
+ const attention2 = includeAttention ? await queryAgentAttentionIndex(null, capture) : new Map;
93743
+ const roster2 = mostRecentAgents(applyAgentAttention([
92408
93744
  ...mergedAgents2,
92409
93745
  ...brokerAgents2.filter((agent) => !existingIds2.has(agent.id))
92410
- ], broker3, limit);
93746
+ ], attention2), limit);
92411
93747
  return withAgentRepoKeys(roster2);
92412
93748
  }
92413
93749
  const broker2 = await loadBrokerContext();
@@ -94039,13 +95375,11 @@ function dedupeHudRunnerProjects(projects) {
94039
95375
  return result;
94040
95376
  }
94041
95377
  async function buildHudRunnerOptions(currentDirectory, input = {}) {
94042
- const [settingsResult, catalogResult, runtimeCatalogResult] = await Promise.allSettled([
95378
+ const [settingsResult, runtimeCatalogResult] = await Promise.allSettled([
94043
95379
  readOpenScoutSettings({ currentDirectory }),
94044
- loadHarnessCatalogSnapshot(),
94045
95380
  loadBrokerRuntimeCatalog()
94046
95381
  ]);
94047
95382
  const settings = settingsResult.status === "fulfilled" ? settingsResult.value : null;
94048
- const catalog = catalogResult.status === "fulfilled" ? catalogResult.value : null;
94049
95383
  const liveRuntimeCatalog = runtimeCatalogResult.status === "fulfilled" ? runtimeCatalogResult.value : null;
94050
95384
  const runtimeCatalog = liveRuntimeCatalog?.catalog ?? SCOUT_RUNTIME_CATALOG;
94051
95385
  const allAgents = queryAgents(50);
@@ -94058,19 +95392,17 @@ async function buildHudRunnerOptions(currentDirectory, input = {}) {
94058
95392
  const configuredDefaultHarness = settings?.agents.defaultHarness?.trim() ?? "";
94059
95393
  const defaultHarness = isScoutRuntimeHarnessEnabled(configuredDefaultHarness, runtimeCatalog) ? configuredDefaultHarness : scoutRuntimeDefaultHarness(runtimeCatalog) ?? "claude";
94060
95394
  const harnessesById = new Map;
94061
- const readinessByHarness = new Map((catalog?.entries ?? []).map((entry) => [entry.harness, entry]));
94062
95395
  for (const entry of runtimeCatalog.harnesses) {
94063
95396
  if (!entry.enabled || entry.listed === false)
94064
95397
  continue;
94065
- const readiness = readinessByHarness.get(entry.id);
94066
95398
  harnessesById.set(entry.id, {
94067
95399
  id: entry.id,
94068
95400
  name: entry.id,
94069
95401
  label: entry.label,
94070
95402
  description: null,
94071
- state: readiness?.readinessReport.state ?? null,
94072
- ready: readiness?.readinessReport.ready ?? null,
94073
- detail: readiness?.readinessReport.detail ?? null
95403
+ state: null,
95404
+ ready: null,
95405
+ detail: null
94074
95406
  });
94075
95407
  }
94076
95408
  const projectOptions = allAgents.map((agent) => agent.projectRoot ?? agent.cwd).filter((root) => typeof root === "string" && root.trim().length > 0).map((root) => {
@@ -94266,6 +95598,7 @@ function fallbackOpenScoutHostInfo(options, currentDirectory) {
94266
95598
  async function createOpenScoutWebServer(options) {
94267
95599
  const shellTtl = options.shellStateCacheTtlMs ?? 15000;
94268
95600
  const currentDirectory = options.currentDirectory;
95601
+ const lanAccessScope = options.lanAccessScope ?? resolveScoutWebLanAccessScope(process.env);
94269
95602
  const pendingPairRequests = createPendingPairRequestStore({
94270
95603
  statePath: pairRequestStatePath(localConfigHome())
94271
95604
  });
@@ -94281,8 +95614,28 @@ async function createOpenScoutWebServer(options) {
94281
95614
  startGlobalHeuristicsWatcher();
94282
95615
  }
94283
95616
  const app = new Hono2;
95617
+ app.use("*", async (c, next) => {
95618
+ const peerAddress = (options.resolvePeerAddress ?? resolveScoutRequestPeerAddress)(c);
95619
+ if (!isScoutWebRequestAllowedFromPeer(c.req.raw, peerAddress, lanAccessScope)) {
95620
+ return c.text("Not Found", 404);
95621
+ }
95622
+ await next();
95623
+ });
94284
95624
  installHttpsEdgeSecurityHeaders(app, options.publicOrigin);
94285
95625
  const shellStateCache = createCachedSnapshot(loadOpenScoutWebShellState, shellTtl);
95626
+ const readAgentConfigurationSnapshot = coalesce(() => buildAgentConfigurationSnapshot(currentDirectory), 30000);
95627
+ const runnerOptionsReaders = new Map;
95628
+ const readRunnerOptions = (scope, projectRoot) => {
95629
+ const key = `${scope}\x00${projectRoot}`;
95630
+ let read = runnerOptionsReaders.get(key);
95631
+ if (!read) {
95632
+ if (runnerOptionsReaders.size >= 32)
95633
+ runnerOptionsReaders.clear();
95634
+ read = coalesce(() => buildHudRunnerOptions(currentDirectory, { scope, projectRoot }), 30000);
95635
+ runnerOptionsReaders.set(key, read);
95636
+ }
95637
+ return read();
95638
+ };
94286
95639
  const agentBrokerContextReader = createAgentBrokerContextReader();
94287
95640
  const agentBrokerHomeReader = createAgentBrokerHomeReader();
94288
95641
  const dispatchBrokerSnapshotCache = createCachedSnapshot(async () => {
@@ -94339,7 +95692,8 @@ async function createOpenScoutWebServer(options) {
94339
95692
  installScoutApiMiddleware(app, "openscout-web api", {
94340
95693
  trustedHosts: options.trustedHosts,
94341
95694
  trustedOrigins: options.trustedOrigins,
94342
- authToken: options.authToken
95695
+ authToken: options.authToken,
95696
+ resolvePeerAddress: options.resolvePeerAddress
94343
95697
  });
94344
95698
  mountScoutDeckSurfaceRoutes(app, {
94345
95699
  currentDirectory,
@@ -94666,33 +96020,68 @@ async function createOpenScoutWebServer(options) {
94666
96020
  const route = c.req.query("route")?.trim().toLowerCase() ?? null;
94667
96021
  const token = c.req.query("token")?.trim() || null;
94668
96022
  const wantsJson = (c.req.header("accept") ?? "").includes("application/json");
94669
- const state = await loadPairingState(currentDirectory, true);
96023
+ const peerAddress = (options.resolvePeerAddress ?? resolveScoutRequestPeerAddress)(c);
96024
+ const xff = c.req.header("x-forwarded-for");
96025
+ const forwardedIp = (xff ? xff.split(",").at(-1)?.trim() : null) || c.req.header("x-real-ip")?.trim() || null;
96026
+ const loopbackPeer = Boolean(peerAddress && isLoopbackScoutAddress(peerAddress));
96027
+ const sameMacRequest = isSameMacScoutRequest(c.req.raw, peerAddress);
96028
+ const relayedLanRequest = Boolean(loopbackPeer && c.req.header(SCOUT_PAIR_RELAY_HEADER) === "1");
96029
+ const directLanRequest = Boolean(peerAddress && !isLoopbackScoutAddress(peerAddress));
96030
+ const proxiedLanRequest = Boolean(loopbackPeer && !relayedLanRequest && !sameMacRequest);
96031
+ const unidentifiedPairingIngress = !peerAddress;
96032
+ const requiresApproval = relayedLanRequest || directLanRequest || proxiedLanRequest || unidentifiedPairingIngress;
96033
+ const requesterApp = parseScoutPairClient(c.req.header(SCOUT_PAIR_CLIENT_HEADER), c.req.header("user-agent"));
96034
+ const state = await loadPairingState(currentDirectory, false);
94670
96035
  const location = pickPairingLocation(state, route);
94671
- if (location) {
94672
- if (token && !pendingPairRequests.fulfill(token)) {
94673
- return wantsJson ? c.json({ status: "expired", token }, 410) : c.text("Pairing request expired.", 410);
94674
- }
94675
- return c.redirect(location, 302);
94676
- }
94677
96036
  if (token) {
94678
- const req2 = pendingPairRequests.get(token);
94679
- if (!req2) {
96037
+ const request2 = pendingPairRequests.get(token);
96038
+ if (!request2) {
94680
96039
  return wantsJson ? c.json({ status: "expired", token }, 410) : c.text("Pairing request expired.", 410);
94681
96040
  }
94682
- if (req2.status === "denied") {
96041
+ if (request2.status === "denied") {
94683
96042
  return wantsJson ? c.json({ status: "denied", token }, 403) : c.text("Pairing request was denied.", 403);
94684
96043
  }
96044
+ if (location && request2.status === "approved") {
96045
+ if (!pendingPairRequests.fulfill(token, "approved")) {
96046
+ const current = pendingPairRequests.get(token);
96047
+ if (current?.status === "approved" || current?.status === "pending") {
96048
+ return c.json({ status: current.status, token, pollAfterMs: 1200 }, 202);
96049
+ }
96050
+ if (current?.status === "denied") {
96051
+ return wantsJson ? c.json({ status: "denied", token }, 403) : c.text("Pairing request was denied.", 403);
96052
+ }
96053
+ return wantsJson ? c.json({ status: "expired", token }, 410) : c.text("Pairing request expired.", 410);
96054
+ }
96055
+ return c.redirect(location, 302);
96056
+ }
94685
96057
  pendingPairRequests.touch(token);
94686
- return c.json({ status: req2.status, token, pollAfterMs: 1200 }, 202);
96058
+ return c.json({ status: request2.status, token, pollAfterMs: 1200 }, 202);
94687
96059
  }
94688
- const xff = c.req.header("x-forwarded-for");
94689
- const requesterIp = (xff ? xff.split(",")[0]?.trim() : null) || c.req.header("x-real-ip")?.trim() || null;
94690
- const req = pendingPairRequests.create({
94691
- requesterIp,
94692
- requesterLabel: c.req.header("x-scout-device-name")?.trim() || null,
94693
- route
94694
- });
94695
- return wantsJson ? c.json({ status: "pending", token: req.token, pollAfterMs: 1200 }, 202) : c.text(`${SCOUT_PAIRING_DEEP_LINK_SCHEME}://${SCOUT_PAIRING_DEEP_LINK_PATH} pairing requires approval on the Mac.`, 202);
96060
+ if (location && !requiresApproval) {
96061
+ return c.redirect(location, 302);
96062
+ }
96063
+ const requesterIp = unidentifiedPairingIngress ? null : relayedLanRequest || proxiedLanRequest ? forwardedIp : directLanRequest ? peerAddress ?? null : forwardedIp;
96064
+ if (requiresApproval && !requesterIp) {
96065
+ return wantsJson ? c.json({ status: "unavailable" }, 503) : c.text("Unable to identify pairing requester.", 503);
96066
+ }
96067
+ let request;
96068
+ try {
96069
+ request = pendingPairRequests.create({
96070
+ requesterIp,
96071
+ requesterLabel: c.req.header("x-scout-device-name")?.trim() || null,
96072
+ requesterApp,
96073
+ route
96074
+ });
96075
+ } catch (error) {
96076
+ if (!(error instanceof PairRequestCapacityError))
96077
+ throw error;
96078
+ c.header("retry-after", String(Math.ceil(error.retryAfterMs / 1000)));
96079
+ return wantsJson ? c.json({ status: "busy", retryAfterMs: error.retryAfterMs }, 429) : c.text("Too many pairing requests. Try again shortly.", 429);
96080
+ }
96081
+ if (request.status === "denied") {
96082
+ return wantsJson ? c.json({ status: "denied", token: request.token }, 403) : c.text("Pairing request was denied.", 403);
96083
+ }
96084
+ return wantsJson ? c.json({ status: request.status, token: request.token, pollAfterMs: 1200 }, 202) : c.text(`${SCOUT_PAIRING_DEEP_LINK_SCHEME}://${SCOUT_PAIRING_DEEP_LINK_PATH} pairing requires approval on the Mac.`, 202);
94696
96085
  });
94697
96086
  app.get("/api/notifications", (c) => {
94698
96087
  const rawType = c.req.query("type") ?? "";
@@ -94701,7 +96090,7 @@ async function createOpenScoutWebServer(options) {
94701
96090
  const notifications = includePairingRequests ? pendingPairRequests.list().filter((request) => request.status === "pending").map((request) => ({
94702
96091
  id: `pairing_request:${request.token}`,
94703
96092
  type: "pairing_request",
94704
- title: `${request.requesterLabel?.trim() || "A device"} wants to pair`,
96093
+ title: `${pairRequesterDisplay(request)} wants to pair`,
94705
96094
  body: `On your network${request.requesterIp ? ` \xB7 ${request.requesterIp}` : ""}.`,
94706
96095
  createdAt: request.createdAt,
94707
96096
  updatedAt: request.updatedAt,
@@ -94791,14 +96180,11 @@ async function createOpenScoutWebServer(options) {
94791
96180
  });
94792
96181
  app.get("/api/shell-state", async (c) => c.json(await shellStateCache.get()));
94793
96182
  app.get("/api/shell-state/refresh", async (c) => c.json(await shellStateCache.refresh()));
94794
- app.get("/api/agent-config/snapshot", async (c) => c.json(await buildAgentConfigurationSnapshot(currentDirectory)));
96183
+ app.get("/api/agent-config/snapshot", async (c) => c.json(await readAgentConfigurationSnapshot()));
94795
96184
  app.get("/api/runner/options", async (c) => {
94796
96185
  const requestedScope = c.req.query("scope");
94797
96186
  const scope = requestedScope === "global" || requestedScope === "project" || requestedScope === "global+project" ? requestedScope : "global+project";
94798
- return c.json(await buildHudRunnerOptions(currentDirectory, {
94799
- scope,
94800
- projectRoot: c.req.query("projectRoot") || currentDirectory
94801
- }));
96187
+ return c.json(await readRunnerOptions(scope, c.req.query("projectRoot") || currentDirectory));
94802
96188
  });
94803
96189
  const agentsResponseCache = new Map;
94804
96190
  const readAgentsResponse = (limit, summary, attentionRequested) => {
@@ -94807,7 +96193,7 @@ async function createOpenScoutWebServer(options) {
94807
96193
  if (!entry) {
94808
96194
  if (agentsResponseCache.size >= 32)
94809
96195
  agentsResponseCache.clear();
94810
- entry = coalesce(() => queryAgentsIncludingBrokerCards(limit, !summary || attentionRequested, attentionRequested, options.captureTmuxPane ?? defaultCaptureTmuxPane, agentBrokerContextReader, agentBrokerHomeReader), 3000);
96196
+ entry = coalesce(() => queryAgentsIncludingBrokerCards(limit, !summary, attentionRequested, options.captureTmuxPane ?? defaultCaptureTmuxPane, agentBrokerContextReader, agentBrokerHomeReader), 3000);
94811
96197
  agentsResponseCache.set(key, entry);
94812
96198
  }
94813
96199
  return entry();
@@ -95580,7 +96966,12 @@ async function createOpenScoutWebServer(options) {
95580
96966
  let messages2;
95581
96967
  try {
95582
96968
  parseMessageHistoryCursor(beforeMessageId);
95583
- const brokerMessages = cId ? await getScoutConversationMessages(cId, limit, beforeMessageId) : null;
96969
+ const brokerContext = cId ? await loadScoutBrokerContext(undefined, {
96970
+ scope: "conversations",
96971
+ waitForInitial: false,
96972
+ initialRefreshDelayMs: 750
96973
+ }).catch(() => null) : null;
96974
+ const brokerMessages = cId ? await getScoutConversationMessages(cId, limit, beforeMessageId, brokerContext) : null;
95584
96975
  if (cId && brokerMessages && brokerMessages.length > 0 && brokerMessages.length < limit) {
95585
96976
  const oldest = brokerMessages[0];
95586
96977
  const seen = new Set(brokerMessages.map((message) => message.id));
@@ -95953,7 +97344,11 @@ async function createOpenScoutWebServer(options) {
95953
97344
  if (flights.length > 0) {
95954
97345
  return c.json(flights);
95955
97346
  }
95956
- const broker2 = await loadScoutBrokerContext().catch(() => null);
97347
+ const broker2 = await loadScoutBrokerContext(undefined, {
97348
+ scope: "conversations",
97349
+ waitForInitial: false,
97350
+ initialRefreshDelayMs: 750
97351
+ }).catch(() => null);
95957
97352
  return c.json(broker2 ? queryBrokerFlightsForWeb(broker2, query) : flights);
95958
97353
  });
95959
97354
  app.get("/api/follow", (c) => {
@@ -95970,7 +97365,7 @@ async function createOpenScoutWebServer(options) {
95970
97365
  targetAgentId: c.req.query("targetAgentId") || undefined
95971
97366
  }));
95972
97367
  });
95973
- const readCommsList = async (c) => {
97368
+ const readCommsList = async (c, options2 = {}) => {
95974
97369
  const rawLimit = Number(c.req.query("limit"));
95975
97370
  const rawKinds = c.req.query("kinds")?.trim();
95976
97371
  const filters = {
@@ -95979,11 +97374,43 @@ async function createOpenScoutWebServer(options) {
95979
97374
  kinds: parseConversationKinds(rawKinds),
95980
97375
  machineId: c.req.query("machineId") || undefined
95981
97376
  };
95982
- const broker2 = await loadScoutBrokerContext(undefined, filters.machineId ? {} : { scope: "conversations" }).catch(() => null);
97377
+ const preferredProjection = options2.preferMaterialized && !filters.machineId ? await readScoutConversationProjection(160) : undefined;
97378
+ if (preferredProjection?.items.some((item) => item.entityKind === "scout_conversation")) {
97379
+ return {
97380
+ items: provisionalScoutConversations(preferredProjection, filters),
97381
+ listReady: true
97382
+ };
97383
+ }
97384
+ const broker2 = await loadScoutBrokerContext(undefined, filters.machineId ? {} : {
97385
+ scope: "conversations",
97386
+ waitForInitial: false,
97387
+ initialRefreshDelayMs: 750
97388
+ }).catch(() => null);
95983
97389
  if (broker2) {
95984
97390
  const items = await getScoutConversations(filters, broker2);
95985
97391
  return { items, listReady: true };
95986
97392
  }
97393
+ if (!filters.machineId) {
97394
+ const launchProjection = preferredProjection === undefined ? await readScoutConversationProjection(Math.min(160, filters.limit ?? 160)) : preferredProjection;
97395
+ if (launchProjection?.items.some((item) => item.entityKind === "scout_conversation")) {
97396
+ return {
97397
+ items: provisionalScoutConversations(launchProjection, filters),
97398
+ listReady: true
97399
+ };
97400
+ }
97401
+ const projected = querySessions(250);
97402
+ if (projected.length > 0) {
97403
+ const query = filters.query?.trim().toLocaleLowerCase() ?? "";
97404
+ const kinds = filters.kinds ? new Set(filters.kinds) : null;
97405
+ const items = projected.filter((item) => !kinds || kinds.has(item.kind)).filter((item) => !query || [
97406
+ item.title,
97407
+ item.alias,
97408
+ item.agentName,
97409
+ item.preview
97410
+ ].some((value) => value?.toLocaleLowerCase().includes(query))).slice(0, filters.limit ?? projected.length);
97411
+ return { items, listReady: true };
97412
+ }
97413
+ }
95987
97414
  const health = await readScoutBrokerHealth(resolveScoutBrokerUrl(), {
95988
97415
  signal: AbortSignal.timeout(2000)
95989
97416
  });
@@ -95996,7 +97423,7 @@ async function createOpenScoutWebServer(options) {
95996
97423
  retryable: true
95997
97424
  }, 503);
95998
97425
  app.get("/api/comms", async (c) => {
95999
- const { items, listReady } = await readCommsList(c);
97426
+ const { items, listReady } = await readCommsList(c, { preferMaterialized: true });
96000
97427
  if (!listReady) {
96001
97428
  return unavailableConversationList(c);
96002
97429
  }
@@ -96176,10 +97603,10 @@ async function createOpenScoutWebServer(options) {
96176
97603
  session: conversation
96177
97604
  });
96178
97605
  }
96179
- const parsedRef = parseSessionRouteRef(refId);
96180
- const harnessSession = parsedRef ? querySessions(200).find((session3) => sessionHarnessMatches(parsedRef.harness, session3.harness) && parseSessionRouteRef(session3.harnessSessionId)?.refId === parsedRef.refId) : null;
96181
97606
  const payload = await loadSessionRefObservePayload(refId);
96182
97607
  if (payload) {
97608
+ const parsedRef = payload.agentId ? parseSessionRouteRef(refId) : null;
97609
+ const harnessSession = parsedRef ? querySessions(200).find((session3) => sessionHarnessMatches(parsedRef.harness, session3.harness) && parseSessionRouteRef(session3.harnessSessionId)?.refId === parsedRef.refId) : null;
96183
97610
  return c.json({
96184
97611
  kind: "observe",
96185
97612
  refId,
@@ -96204,7 +97631,12 @@ async function createOpenScoutWebServer(options) {
96204
97631
  return c.json({ error: "chatId must be an opaque chat id" }, 400);
96205
97632
  }
96206
97633
  const session3 = querySessionById(resolvedChatId);
96207
- const conversation = await getScoutConversationById(resolvedChatId);
97634
+ const broker2 = session3 ? await loadScoutBrokerContext(undefined, {
97635
+ scope: "conversations",
97636
+ waitForInitial: false,
97637
+ initialRefreshDelayMs: 750
97638
+ }).catch(() => null) : undefined;
97639
+ const conversation = broker2 === null ? null : broker2 ? (await getScoutConversations({ conversationId: resolvedChatId, limit: 1 }, broker2))[0] ?? null : await getScoutConversationById(resolvedChatId);
96208
97640
  if (session3 && conversation) {
96209
97641
  return c.json({ ...session3, ...conversation });
96210
97642
  }
@@ -97093,6 +98525,8 @@ async function createOpenScoutWebServer(options) {
97093
98525
  const limitParam = parseOptionalPositiveInt3(c.req.query("limit"), 500) ?? 500;
97094
98526
  const includeTranscripts = c.req.query("transcripts") === "true" || c.req.query("transcripts") === "1";
97095
98527
  const mode = c.req.query("mode") === "assistant-replies" ? "assistant-replies" : null;
98528
+ const requestedWindowMs = parseOptionalPositiveInt3(c.req.query("windowMs"));
98529
+ const windowMs = requestedWindowMs === undefined ? undefined : Math.min(requestedWindowMs, 86400000);
97096
98530
  const url = new URL(scoutBrokerPaths.v1.tailRecent, resolveScoutBrokerUrl());
97097
98531
  url.searchParams.set("limit", String(limitParam));
97098
98532
  if (includeTranscripts) {
@@ -97101,7 +98535,10 @@ async function createOpenScoutWebServer(options) {
97101
98535
  if (mode) {
97102
98536
  url.searchParams.set("mode", mode);
97103
98537
  }
97104
- const cacheKey = `limit=${limitParam};transcripts=${includeTranscripts ? "1" : "0"};mode=${mode ?? "all"}`;
98538
+ if (windowMs !== undefined) {
98539
+ url.searchParams.set("windowMs", String(windowMs));
98540
+ }
98541
+ const cacheKey = `limit=${limitParam};transcripts=${includeTranscripts ? "1" : "0"};mode=${mode ?? "all"};window=${windowMs ?? "all"}`;
97105
98542
  let cache = tailRecentCaches.get(cacheKey);
97106
98543
  if (!cache) {
97107
98544
  cache = createBrokerJsonCache();
@@ -97811,6 +99248,7 @@ function startProcessParentWatchdog(expectedParentPidRaw, options = {}) {
97811
99248
  process.title = "scout-web";
97812
99249
  var port = Number.parseInt(process.env.OPENSCOUT_WEB_PORT ?? process.env.SCOUT_WEB_PORT ?? String(resolveWebPort()), 10);
97813
99250
  var hostname2 = resolveScoutWebBindHost(process.env);
99251
+ var lanAccessScope = resolveScoutWebLanAccessScope(process.env);
97814
99252
  var webAuthToken = resolveWebAuthToken(process.env);
97815
99253
  var currentDirectory = resolveOpenScoutSetupContextRoot({
97816
99254
  env: process.env,
@@ -97820,6 +99258,7 @@ var shellStateCacheTtlMs = Number.parseInt(process.env.OPENSCOUT_WEB_SHELL_CACHE
97820
99258
  var providerTelemetryBootstrapEnabled = process.env.OPENSCOUT_WEB_PROVIDER_TELEMETRY_BOOTSTRAP?.trim() === "1";
97821
99259
  var startupWarmupEnabled = process.env.OPENSCOUT_WEB_STARTUP_WARMUP?.trim() === "1";
97822
99260
  var routes = resolveOpenScoutWebRoutes(process.env);
99261
+ var requestPeerAddresses = createScoutRequestPeerAddressRegistry();
97823
99262
  function resolveStaticRoot2() {
97824
99263
  if (process.env.OPENSCOUT_WEB_STATIC_ROOT?.trim()) {
97825
99264
  return process.env.OPENSCOUT_WEB_STATIC_ROOT.trim();
@@ -97909,6 +99348,7 @@ var web = await createOpenScoutWebServer({
97909
99348
  trustedHosts: applicationServerIdentity.trustedHosts,
97910
99349
  trustedOrigins: applicationServerIdentity.trustedOrigins,
97911
99350
  authToken: webAuthToken,
99351
+ resolvePeerAddress: requestPeerAddresses.resolve,
97912
99352
  runTerminalCommand: async (request) => {
97913
99353
  const relay = await ensureTerminalRelay();
97914
99354
  if (!relay) {
@@ -97948,6 +99388,9 @@ try {
97948
99388
  async fetch(req, server2) {
97949
99389
  const url = new URL(req.url);
97950
99390
  const peerAddress = server2.requestIP(req)?.address;
99391
+ if (!isScoutWebRequestAllowedFromPeer(req, peerAddress, lanAccessScope)) {
99392
+ return new Response("Not Found", { status: 404 });
99393
+ }
97951
99394
  if (req.headers.get("upgrade")?.toLowerCase() === "websocket") {
97952
99395
  let upstreamUrl = null;
97953
99396
  const guardsOrigin = url.pathname === routes.terminalRelayPath || url.pathname === routes.tailStreamPath || url.pathname === routes.eventsStreamPath;
@@ -98003,6 +99446,7 @@ try {
98003
99446
  return headers2;
98004
99447
  })()
98005
99448
  }) : req;
99449
+ requestPeerAddresses.remember(honoRequest, peerAddress);
98006
99450
  const response = await honoFetch(honoRequest, server2);
98007
99451
  if (!bootstrapEligible)
98008
99452
  return response;