@openscout/scout 0.2.95 → 0.2.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) 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-BnoaUkVZ.js} +1 -1
  5. package/dist/client/assets/{AddProjectForm-CcD6qvV8.js → AddProjectForm-IqOtJfRs.js} +1 -1
  6. package/dist/client/assets/{AgentAvatar-CYhf6twS.js → AgentAvatar-6-KBVKUu.js} +1 -1
  7. package/dist/client/assets/{AgentConfigurationScreen-BTI24HQX.js → AgentConfigurationScreen-CuZJy07q.js} +1 -1
  8. package/dist/client/assets/{AgentDefinitionsWorkspace-srmN6-vj.js → AgentDefinitionsWorkspace-Iyl4BesU.js} +1 -1
  9. package/dist/client/assets/{AgentDetailCard-UqPNpcvB.js → AgentDetailCard-C9rn3Fj5.js} +1 -1
  10. package/dist/client/assets/{AgentFloorView-XXNgI5Cj.js → AgentFloorView-YT-Jjccf.js} +1 -1
  11. package/dist/client/assets/{AgentInfoScreen-CimdOnZ9.js → AgentInfoScreen-Dr99gQEZ.js} +1 -1
  12. package/dist/client/assets/{AgentLaneCard-Z-aOBR3W.js → AgentLaneCard-DI2iW9zO.js} +1 -1
  13. package/dist/client/assets/{AgentLaneChrome-CyhA1reW.js → AgentLaneChrome-D8bARHMx.js} +1 -1
  14. package/dist/client/assets/{AgentLaneDetailSheet-BNzjMjIH.js → AgentLaneDetailSheet-52un7dKC.js} +1 -1
  15. package/dist/client/assets/{AgentLaneSummaryResize-Box8-_rS.js → AgentLaneSummaryResize-CEBjmPTa.js} +1 -1
  16. package/dist/client/assets/AgentLanesEmbedScreen-Dgcm7zd_.js +1 -0
  17. package/dist/client/assets/AgentLanesPreflight-q20mTjms.js +1 -0
  18. package/dist/client/assets/AgentLanesView-CetZZDyF.js +2 -0
  19. package/dist/client/assets/{AgentLiveActions-BwuzOE8h.js → AgentLiveActions-DrEJDJef.js} +1 -1
  20. package/dist/client/assets/AgentMasterScreen-DJPnstoD.js +1 -0
  21. package/dist/client/assets/{AgentSessions-CsZwoukw.js → AgentSessions-DCPCG4xb.js} +1 -1
  22. package/dist/client/assets/{AgentsConsole-KCao4XmX.js → AgentsConsole-CsYOoFJi.js} +1 -1
  23. package/dist/client/assets/{AgentsScreen-CtPAmwRX.js → AgentsScreen-k8LvBuU_.js} +1 -1
  24. package/dist/client/assets/AgentsSubnav-C4xpQhsv.js +1 -0
  25. package/dist/client/assets/{AstronautSuit-Ckx7lu7G.js → AstronautSuit-DbjQtfD_.js} +1 -1
  26. package/dist/client/assets/{AtopView-DYlpgEHf.js → AtopView-Daq_ob3z.js} +1 -1
  27. package/dist/client/assets/{BackToPicker-4JaeRvDm.js → BackToPicker-CXZjBRAe.js} +1 -1
  28. package/dist/client/assets/{BriefingDetailScreen-D6-xqrpo.js → BriefingDetailScreen-CUCXO-KH.js} +1 -1
  29. package/dist/client/assets/{BriefingsScreen-DKxSDEmk.js → BriefingsScreen-B5oMTGKv.js} +1 -1
  30. package/dist/client/assets/{BrokerMetadataPanel-BbREmsbK.js → BrokerMetadataPanel-BsiU8lTD.js} +1 -1
  31. package/dist/client/assets/{BrokerScreen-fbPIouMw.js → BrokerScreen-BPbOHhCS.js} +1 -1
  32. package/dist/client/assets/{CastPicker-BcA2vDDV.js → CastPicker-_5E5hNUe.js} +1 -1
  33. package/dist/client/assets/ChatCollapsedStrip-D-eXhzPf.js +1 -0
  34. package/dist/client/assets/{CodeDiffPane-Cn8Y9hAt.js → CodeDiffPane-n7W4iGUG.js} +1 -1
  35. package/dist/client/assets/{CodeProjectPicker-nPFktrco.js → CodeProjectPicker-CMsbSTDq.js} +1 -1
  36. package/dist/client/assets/{CodeScreen-BlAxtxE5.js → CodeScreen-CeuO3EhR.js} +1 -1
  37. package/dist/client/assets/{ConversationComposer-2HjbGPL9.js → ConversationComposer-CXPKx86g.js} +1 -1
  38. package/dist/client/assets/{ConversationFeedRows-C6wUqJrx.js → ConversationFeedRows-WrH-YI3L.js} +1 -1
  39. package/dist/client/assets/{ConversationHeader-C8vfXaTF.js → ConversationHeader-BQWzGzLK.js} +1 -1
  40. package/dist/client/assets/{ConversationPanels-CNCIbpqU.js → ConversationPanels-Dujvnmup.js} +2 -2
  41. package/dist/client/assets/ConversationScreen-CR9pWB65.js +2 -0
  42. package/dist/client/assets/{ConversationStatus-DZuyKsWN.js → ConversationStatus-DVzSb2Hv.js} +1 -1
  43. package/dist/client/assets/{CrewAvatar-CTIuxp4G.js → CrewAvatar-BwXjyg8N.js} +1 -1
  44. package/dist/client/assets/CrewWorkspaces-CkQDhyie.css +1 -0
  45. package/dist/client/assets/CrewWorkspaces-NKujRzwh.js +1 -0
  46. package/dist/client/assets/{DataTable-6IDhanr5.js → DataTable-Bp8tU9AK.js} +1 -1
  47. package/dist/client/assets/{DispatchAftermath-DC1u9itO.js → DispatchAftermath-B-6rmXzL.js} +1 -1
  48. package/dist/client/assets/{DocumentFocusViewer-DZVhS7as.js → DocumentFocusViewer-Do3wQ_3Z.js} +1 -1
  49. package/dist/client/assets/{EmbeddableSurfacesLab-BKUFJMCV.js → EmbeddableSurfacesLab-C1orRyP9.js} +1 -1
  50. package/dist/client/assets/{EmptyState-CeBl0iGd.js → EmptyState-CJzXj1l5.js} +1 -1
  51. package/dist/client/assets/{FileViewerPane-DQlpNt2S.js → FileViewerPane-Df6VzXjA.js} +1 -1
  52. package/dist/client/assets/FlightObserveScreen-6mGnmWQA.js +1 -0
  53. package/dist/client/assets/{FollowScreen-BXHOp3gb.js → FollowScreen-DvnxYLVh.js} +1 -1
  54. package/dist/client/assets/{HarnessMark-D5CA5uSp.js → HarnessMark-B979GCSB.js} +1 -1
  55. package/dist/client/assets/{HarnessesScreen-DhFXq1qz.js → HarnessesScreen-CHTuv8p6.js} +1 -1
  56. package/dist/client/assets/{HerdrSession-BRhs5LIR.js → HerdrSession-B3McuYgi.js} +1 -1
  57. package/dist/client/assets/{HomeHero-DMABJuze.js → HomeHero-CdUVPjkG.js} +1 -1
  58. package/dist/client/assets/{HostAdvisorView-RAociATV.js → HostAdvisorView-Bok5Sk5c.js} +1 -1
  59. package/dist/client/assets/{KnowledgeSearchInspector-CZPyVNSw.js → KnowledgeSearchInspector-B7XSoJsu.js} +1 -1
  60. package/dist/client/assets/{KnowledgeSearchScreen-mGTKIg00.js → KnowledgeSearchScreen-D3o3IODW.js} +1 -1
  61. package/dist/client/assets/{LaneToolHoverCard-D_DMlBeT.js → LaneToolHoverCard-HByf2ndx.js} +1 -1
  62. package/dist/client/assets/{LaneTraceDetailSheet-BEHg3LmH.js → LaneTraceDetailSheet-BUgHz-sd.js} +1 -1
  63. package/dist/client/assets/{LaneTraceEventFocus-DVimcizS.js → LaneTraceEventFocus-DM-hoPxG.js} +1 -1
  64. package/dist/client/assets/{MeshCanvas-BOuZx4AM.js → MeshCanvas-DCLJRsBy.js} +1 -1
  65. package/dist/client/assets/{MeshCanvasMinimap-wL-1N94e.js → MeshCanvasMinimap-cEFS9eW7.js} +1 -1
  66. package/dist/client/assets/{MeshScreen-DMY9Bmh5.js → MeshScreen-BcxPkhm6.js} +1 -1
  67. package/dist/client/assets/{MeshTopologyView-DiDcX5f4.js → MeshTopologyView-CCRPd8PU.js} +2 -2
  68. package/dist/client/assets/MessagesScreen-Bb5l13MO.js +1 -0
  69. package/dist/client/assets/{MissionContextRail-DrNKXG5u.js → MissionContextRail-DF0_kiU7.js} +1 -1
  70. package/dist/client/assets/{MissionControlView-ByBlHVN8.js → MissionControlView-E3V7bENU.js} +1 -1
  71. package/dist/client/assets/{MissionFocusOverlay-D1i6R6Zt.js → MissionFocusOverlay-h15sQFvP.js} +1 -1
  72. package/dist/client/assets/{MissionLeftAgentRow-DN9NcCML.js → MissionLeftAgentRow-KPdk9rSa.js} +1 -1
  73. package/dist/client/assets/{MissionLogPane-DSeIv3Xo.js → MissionLogPane-BbdDKjUl.js} +1 -1
  74. package/dist/client/assets/{NewAgentModal-V7yK6J6j.js → NewAgentModal-DfHxad34.js} +1 -1
  75. package/dist/client/assets/NewChatComposer-_Stj0fZy.js +19 -0
  76. package/dist/client/assets/{NewChatOrigin-DdpzE4kz.js → NewChatOrigin-B1u7ki70.js} +1 -1
  77. package/dist/client/assets/{ObserveEmbedScreen-l_Q3ts2q.js → ObserveEmbedScreen-Co0xTii6.js} +1 -1
  78. package/dist/client/assets/{ObserveTextFlow-BSEtYTxU.js → ObserveTextFlow-ZQWwrK3i.js} +2 -2
  79. package/dist/client/assets/{ObservedTopologyPanel-CdGUXxGR.js → ObservedTopologyPanel-BJUhDSuQ.js} +1 -1
  80. package/dist/client/assets/{OpenScoutAppShell-DN8dOSK4.js → OpenScoutAppShell-CUd2yOLu.js} +25 -25
  81. package/dist/client/assets/{OpsAgentsView-ntGr4pz4.js → OpsAgentsView-DI5wu1si.js} +1 -1
  82. package/dist/client/assets/OpsScreen-D8fQafCl.js +1 -0
  83. package/dist/client/assets/OpsSubnav-1K4Jaf__.js +1 -0
  84. package/dist/client/assets/ProjectAgentProfile-Bdjd_dAx.js +1 -0
  85. package/dist/client/assets/{ProjectAgentProfileBar-BQET-qrY.js → ProjectAgentProfileBar-k2QHHHsQ.js} +1 -1
  86. package/dist/client/assets/{ProjectAgentProfileHero-Ck8mThtx.js → ProjectAgentProfileHero-Bpxk1ui2.js} +1 -1
  87. package/dist/client/assets/ProjectsInbox-C_bMHLCc.js +2 -0
  88. package/dist/client/assets/ProjectsRail-J-aH_pxT.js +1 -0
  89. package/dist/client/assets/ProjectsScreen-cVUkGbPz.js +1 -0
  90. package/dist/client/assets/ProjectsThreadAside-_ourdM-Q.js +1 -0
  91. package/dist/client/assets/{Provider-B2xuo3rf.js → Provider-CtXJeLrP.js} +1 -1
  92. package/dist/client/assets/{RailRow-B-GycCBx.js → RailRow-CEH2MFBw.js} +1 -1
  93. package/dist/client/assets/{RawSessionsTable-AQThi6sm.js → RawSessionsTable-CDyrom-F.js} +1 -1
  94. package/dist/client/assets/RealtimeVoiceScreen-DBl3DN1H.js +1 -0
  95. package/dist/client/assets/{RepoDiffEmbedScreen-DXWoRF_c.js → RepoDiffEmbedScreen-kY2pHjIs.js} +1 -1
  96. package/dist/client/assets/{RepoDiffPageScreen-D_274ExO.js → RepoDiffPageScreen-C6LhoKrn.js} +1 -1
  97. package/dist/client/assets/{RepoDiffViewer-DbedmHDX.js → RepoDiffViewer-BhcIIQQg.js} +1 -1
  98. package/dist/client/assets/{RepoDiffViewerLazy-C0--fPtk.js → RepoDiffViewerLazy-Csy_j9LD.js} +2 -2
  99. package/dist/client/assets/{ReposScreen-D39_YG4H.js → ReposScreen-B_cB0zu7.js} +1 -1
  100. package/dist/client/assets/ScoutDeckSurface-CSy_fjcG.js +1 -0
  101. package/dist/client/assets/{ScoutSurface-UsRy0t-I.js → ScoutSurface-CQVcSrin.js} +1 -1
  102. package/dist/client/assets/{ScoutbotFxLab-Fmdfboxs.js → ScoutbotFxLab-DqGiLYNy.js} +1 -1
  103. package/dist/client/assets/{SecondaryNav-o2RL9H8H.js → SecondaryNav-BR4VRdgh.js} +1 -1
  104. package/dist/client/assets/SessionEmbedScreen-BTlZ9gAg.js +1 -0
  105. package/dist/client/assets/{SessionObserve-Dsd0j05N.js → SessionObserve-dQDN23pP.js} +1 -1
  106. package/dist/client/assets/{SessionObserveEvidence-DopQYDkD.js → SessionObserveEvidence-BRBGKAgd.js} +1 -1
  107. package/dist/client/assets/SessionRefScreen-COVQMbtE.js +1 -0
  108. package/dist/client/assets/SessionsScreen--5OJ3p3v.js +1 -0
  109. package/dist/client/assets/{SettingsDrawer-_AwuopMS.js → SettingsDrawer-RGVK_bmE.js} +1 -1
  110. package/dist/client/assets/{SettingsScreen-Cw4DDfJP.js → SettingsScreen-CCxzy5IU.js} +1 -1
  111. package/dist/client/assets/{ShikiPane-BfHc_3pM.js → ShikiPane-DYI9Hkge.js} +1 -1
  112. package/dist/client/assets/{SlidePanel-C-q7XwTa.js → SlidePanel-B9ugxWgA.js} +1 -1
  113. package/dist/client/assets/{SpriteAvatar-Bs68ZENy.js → SpriteAvatar-z5hHesx9.js} +1 -1
  114. package/dist/client/assets/{StatusPill-ZZQwxB1a.js → StatusPill-BFvMjmZV.js} +1 -1
  115. package/dist/client/assets/{TailView-Q4FVMsUH.js → TailView-0uEIj2JH.js} +1 -1
  116. package/dist/client/assets/{Terminal-D7zqHwHV.js → Terminal-DouZeNxc.js} +1 -1
  117. package/dist/client/assets/{TerminalEmbedScreen-CXIJ7qv6.js → TerminalEmbedScreen-dMNUbzXr.js} +1 -1
  118. package/dist/client/assets/{TerminalHeaderMount-BnBLOPbb.js → TerminalHeaderMount-BL07cd0q.js} +1 -1
  119. package/dist/client/assets/{TextDocumentSurface-CKQP-cBT.js → TextDocumentSurface-BflAwXeZ.js} +1 -1
  120. package/dist/client/assets/{TmuxPeek-V9dGESfO.js → TmuxPeek-BTnjOQi0.js} +1 -1
  121. package/dist/client/assets/{VoicePermissionsPanel-k7sbLXx2.js → VoicePermissionsPanel-CgQ6GGS-.js} +1 -1
  122. package/dist/client/assets/{WorkDetailScreen-60p9litn.js → WorkDetailScreen-CaLG-zDs.js} +1 -1
  123. package/dist/client/assets/{WorkFilesViewer-BjJS7GyM.js → WorkFilesViewer-B9Sil_No.js} +1 -1
  124. package/dist/client/assets/{activity-Bj3Hnbj3.js → activity-DcrMApUY.js} +1 -1
  125. package/dist/client/assets/{addon-fit-BL-NTWRU.js → addon-fit-DMVrz5Sv.js} +1 -1
  126. package/dist/client/assets/{addon-webgl-CIMgmNjg.js → addon-webgl-BYvpZUyu.js} +1 -1
  127. package/dist/client/assets/{agent-autocomplete-DXlvdc4b.js → agent-autocomplete-B-5QTeas.js} +1 -1
  128. package/dist/client/assets/{agent-essentials-BOMHR0_U.js → agent-essentials-D7KRGYvF.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-DqjrVI2h.js +1 -0
  133. package/dist/client/assets/{agent-specialization-BIPIBW8u.js → agent-specialization-BujJVUTD.js} +1 -1
  134. package/dist/client/assets/{arc.es-IR5r6sj_.js → arc.es-NHqHWohb.js} +1 -1
  135. package/dist/client/assets/{arrow-down-BydciYFZ.js → arrow-down-CKtSUjmW.js} +1 -1
  136. package/dist/client/assets/{arrow-left-CghA0kTw.js → arrow-left-Czpf-AE4.js} +1 -1
  137. package/dist/client/assets/{arrow-right-iJFv3jNh.js → arrow-right-BuzMnLYP.js} +1 -1
  138. package/dist/client/assets/{arrow-up-right-CCfZXPqD.js → arrow-up-right-C6Wl-1uw.js} +1 -1
  139. package/dist/client/assets/{bot-B5sub1-i.js → bot-rd0awxD3.js} +1 -1
  140. package/dist/client/assets/{check-C18YQC90.js → check-ofKUE8yH.js} +1 -1
  141. package/dist/client/assets/{chevron-down-Mp9mu_yd.js → chevron-down-wciXnOQz.js} +1 -1
  142. package/dist/client/assets/{chevron-right-CzH-TaSF.js → chevron-right-D-HdlPg8.js} +1 -1
  143. package/dist/client/assets/{chunk-HRAB3GC3-swV8PA2k.js → chunk-HRAB3GC3-Tyv9rzx7.js} +1 -1
  144. package/dist/client/assets/{chunk-JTPDIGGW-BKXqcLKw.js → chunk-JTPDIGGW-BC2Mandc.js} +3 -3
  145. package/dist/client/assets/{chunk-T65CQEWV-vYbNJ8E6.js → chunk-T65CQEWV-C1AWTYZZ.js} +1 -1
  146. package/dist/client/assets/{chunk-YOZR3KYS-U65yUmqt.js → chunk-YOZR3KYS-DwHPHa6f.js} +1 -1
  147. package/dist/client/assets/{clock-B4PTFi5m.js → clock-B88r8OrZ.js} +1 -1
  148. package/dist/client/assets/{code-xml-Jtxsp5d1.js → code-xml-D7pOQM9l.js} +1 -1
  149. package/dist/client/assets/{compass-0_54-vz3.js → compass-Dt1AMXz2.js} +1 -1
  150. package/dist/client/assets/content-9K38QchR.js +1 -0
  151. package/dist/client/assets/content-BRgnyXWh.js +1 -0
  152. package/dist/client/assets/{content-CHghn-rT.js → content-BWPNyXJ0.js} +1 -1
  153. package/dist/client/assets/content-BdkI07v3.js +1 -0
  154. package/dist/client/assets/content-C4oWa8SB.js +1 -0
  155. package/dist/client/assets/content-C9ijJxyl.js +1 -0
  156. package/dist/client/assets/content-CFZ4rghn.js +1 -0
  157. package/dist/client/assets/content-CIqu_Ci_.js +1 -0
  158. package/dist/client/assets/content-CnJF9DGO.js +1 -0
  159. package/dist/client/assets/content-CriW8Y8u.js +1 -0
  160. package/dist/client/assets/content-DDCyPPk_.js +1 -0
  161. package/dist/client/assets/content-DtcZeh5m.js +1 -0
  162. package/dist/client/assets/content-U9tsdtle.js +1 -0
  163. package/dist/client/assets/content-hFLK3CON.js +1 -0
  164. package/dist/client/assets/{content-DMbawvqU.js → content-nkx0swDe.js} +1 -1
  165. package/dist/client/assets/content-p00KQrtD.js +1 -0
  166. package/dist/client/assets/{conversation-icons-BV10B2Me.js → conversation-icons-tp9BX6X2.js} +1 -1
  167. package/dist/client/assets/{conversation-model-CwF6Y9gx.js → conversation-model-Dc02NsGp.js} +1 -1
  168. package/dist/client/assets/conversation-motion-features-D9-RmBSq.js +1 -0
  169. package/dist/client/assets/{copy-Dtpx4FOE.js → copy-CWWAJ75H.js} +1 -1
  170. package/dist/client/assets/{cpu-BjVis9mc.js → cpu-DrqejfkB.js} +1 -1
  171. package/dist/client/assets/{createLucideIcon-DXpqZP7I.js → createLucideIcon-DezKXSG2.js} +1 -1
  172. package/dist/client/assets/{ctx-panel-D_HJ3nhb.css → ctx-panel-F05jXK4Q.css} +1 -1
  173. package/dist/client/assets/discover-C2CZO4Tk.js +2 -0
  174. package/dist/client/assets/embed-entry-BSMUykuG.js +2 -0
  175. package/dist/client/assets/{external-link-Ckiyzjwo.js → external-link-Dp_a2vRZ.js} +1 -1
  176. package/dist/client/assets/{eye-CpfyvdLq.js → eye-BRyD0jno.js} +1 -1
  177. package/dist/client/assets/{file-code-corner-7mo7EviR.js → file-code-corner-muIVH_xO.js} +1 -1
  178. package/dist/client/assets/{file-text-KijGBnME.js → file-text-CqJgwxmv.js} +1 -1
  179. package/dist/client/assets/{gauge-y-Xg8Mrv.js → gauge-hU--hKVP.js} +1 -1
  180. package/dist/client/assets/{gestures-Bq9XnSqY.js → gestures-DN4hEeqv.js} +1 -1
  181. package/dist/client/assets/{git-branch-BQ_x8KZL.js → git-branch-CK5v8F2v.js} +1 -1
  182. package/dist/client/assets/{home-moving-signal-DtRpiNa0.js → home-moving-signal-BjDtEJUP.js} +1 -1
  183. package/dist/client/assets/{home-now-card-DFb4GOgM.js → home-now-card-nF8OotI-.js} +1 -1
  184. package/dist/client/assets/{home-now-card-nano-D--m9jpq.js → home-now-card-nano-C-r_bBg5.js} +1 -1
  185. package/dist/client/assets/{hooks-9d1SoaQA.js → hooks-D46bABUP.js} +1 -1
  186. package/dist/client/assets/index-CPrU5q04.js +1 -0
  187. package/dist/client/assets/{index-CZ-05hKz.js → index-DvLtl7rV.js} +5 -5
  188. package/dist/client/assets/index-kL1WLbeS.js +1 -0
  189. package/dist/client/assets/index-zvhpRHao.js +1 -0
  190. package/dist/client/assets/{inspector-panel-C-EhEDYy.js → inspector-panel-Dkc7rLvY.js} +1 -1
  191. package/dist/client/assets/{keyboard-nav-DFhAJkW3.js → keyboard-nav-BLYDhL3Z.js} +1 -1
  192. package/dist/client/assets/{lane-roster-store-gUBsydNf.js → lane-roster-store-DOdI1O13.js} +1 -1
  193. package/dist/client/assets/{layers-CGou-LwV.js → layers-CSsg3JVP.js} +1 -1
  194. package/dist/client/assets/{layout-grid-BrZt31QH.js → layout-grid-DOChPJdZ.js} +1 -1
  195. package/dist/client/assets/left-B-SdEJSJ.js +1 -0
  196. package/dist/client/assets/{left-B_o51JV6.js → left-B3wSE6ql.js} +1 -1
  197. package/dist/client/assets/{left-CwHZCGO3.js → left-BNRFiRsb.js} +1 -1
  198. package/dist/client/assets/{left-DA0amyfu.js → left-BggzhgXN.js} +1 -1
  199. package/dist/client/assets/left-CARCvvxS.js +2 -0
  200. package/dist/client/assets/left-CpokZYHt.js +1 -0
  201. package/dist/client/assets/{left-CwLs0okF.js → left-YT7mIGuH.js} +1 -1
  202. package/dist/client/assets/{left-advisor-DU3s01gk.js → left-advisor-CCY0vkcO.js} +1 -1
  203. package/dist/client/assets/left-agents-trhO5sL8.js +1 -0
  204. package/dist/client/assets/{left-default-uvuvDmt0.js → left-default-CG33elBs.js} +1 -1
  205. package/dist/client/assets/{left-lanes-DnNLgec8.js → left-lanes-CitMgeTp.js} +1 -1
  206. package/dist/client/assets/{left-mission-DN3-NLPC.js → left-mission-Zl2m_5eU.js} +1 -1
  207. package/dist/client/assets/{library-DIo5NiGP.js → library-DkALuCh5.js} +1 -1
  208. package/dist/client/assets/{loader-circle-BiohlpM3.js → loader-circle-pM56HNhR.js} +1 -1
  209. package/dist/client/assets/{local-agents-wUIaBcEr.js → local-agents-C8xAhbO5.js} +1 -1
  210. package/dist/client/assets/{mentions-CcPFMbmZ.js → mentions-DG6OQrsc.js} +1 -1
  211. package/dist/client/assets/{mesh-buckets-ByyFXdKT.js → mesh-buckets-6y3ZIibI.js} +1 -1
  212. package/dist/client/assets/{mesh-ops-CvGyjK84.js → mesh-ops-BZ1h5RQs.js} +1 -1
  213. package/dist/client/assets/{mesh-roster-n2nDpEmA.js → mesh-roster-Bc7T4b7z.js} +1 -1
  214. package/dist/client/assets/{mesh-view-store-BpxGm71b.js → mesh-view-store-ChUOASK8.js} +1 -1
  215. package/dist/client/assets/{message-circle-B2ddzBrX.js → message-circle-mZCv8tvB.js} +1 -1
  216. package/dist/client/assets/{message-markup-RkfmDfME.js → message-markup-C2LQDm_k.js} +1 -1
  217. package/dist/client/assets/{message-square-D9YYWE0x.js → message-square-C7t_qtuj.js} +1 -1
  218. package/dist/client/assets/{message-visibility-DwxgcgYK.js → message-visibility-CGOmSSKs.js} +1 -1
  219. package/dist/client/assets/{mission-control-store-Bf4b54MV.js → mission-control-store-nDLdUZPA.js} +1 -1
  220. package/dist/client/assets/{observe-CJRIvOCn.js → observe-BpEm1INu.js} +1 -1
  221. package/dist/client/assets/{page-status-BptpTdQ-.js → page-status-E5jnk48p.js} +1 -1
  222. package/dist/client/assets/{paperclip-B1UNGl5d.js → paperclip-C64lkcbM.js} +1 -1
  223. package/dist/client/assets/{persistent-state-YI-WHSCF.js → persistent-state-EHXw8WNy.js} +1 -1
  224. package/dist/client/assets/{plus-CuU_X0-8.js → plus-Co-dqKul.js} +1 -1
  225. package/dist/client/assets/{profile-rY8jO38P.js → profile-APxDsN2P.js} +1 -1
  226. package/dist/client/assets/projects-inbox-Cg4X2lUQ.js +1 -0
  227. package/dist/client/assets/projects-inbox-DRBl2N85.css +1 -0
  228. package/dist/client/assets/{proxy-DnLpZIi9.js → proxy-BiW5suC7.js} +1 -1
  229. package/dist/client/assets/{radio-Dm1ItnA4.js → radio-BOxBQX-T.js} +1 -1
  230. package/dist/client/assets/{refresh-cw-BRn9Oxxl.js → refresh-cw-DMxGzDwn.js} +1 -1
  231. package/dist/client/assets/resolve-panes-DeVJbS9c.js +1 -0
  232. package/dist/client/assets/resolve-sidebar-context-BQe5J6xF.js +1 -0
  233. package/dist/client/assets/{right-BJltPRnd.js → right-Bmfd3pjP.js} +1 -1
  234. package/dist/client/assets/{right-Beh3nCUB.js → right-BqI-nr9G.js} +1 -1
  235. package/dist/client/assets/{right-D3aTKf1F.js → right-BuCgE0fZ.js} +1 -1
  236. package/dist/client/assets/{right-Shpw_xDy.js → right-BwlW6E57.js} +1 -1
  237. package/dist/client/assets/{right-CPP7GyOm.js → right-C0ybUw0j.js} +1 -1
  238. package/dist/client/assets/{right-BbyYdzHn.js → right-CdkcrmeD.js} +1 -1
  239. package/dist/client/assets/{right-CJnB_Bg7.js → right-CimaAvQN.js} +1 -1
  240. package/dist/client/assets/{right-C1yumXNx.js → right-CkCCPbCX.js} +1 -1
  241. package/dist/client/assets/{right-DI1B79nR.js → right-D3eSiy41.js} +1 -1
  242. package/dist/client/assets/{right-DtsRNX5b.js → right-Dz_xD0Vj.js} +1 -1
  243. package/dist/client/assets/{rocket-DOwXHKWA.js → rocket-BQm5_CvR.js} +1 -1
  244. package/dist/client/assets/router-B9vQkNJL.js +1 -0
  245. package/dist/client/assets/{scope-views-A4_vP97W.js → scope-views-1IYJdl41.js} +1 -1
  246. package/dist/client/assets/{scout-voice-BhPzrc6P.js → scout-voice-BVUqGhth.js} +1 -1
  247. package/dist/client/assets/{scoutbot-markdown-jh7tzFB1.js → scoutbot-markdown-BQ_Ee2Ic.js} +1 -1
  248. package/dist/client/assets/{scroll-text-DO0GQzvN.js → scroll-text-Dze_dXtQ.js} +1 -1
  249. package/dist/client/assets/{search-D1tJSAWe.js → search-CDko0PKE.js} +1 -1
  250. package/dist/client/assets/{secondaryNavConfig-B9IF8wXl.js → secondaryNavConfig-BoQ_zYR9.js} +1 -1
  251. package/dist/client/assets/{selection-bridge-i2S9zpf8.js → selection-bridge-Din6yV3h.js} +1 -1
  252. package/dist/client/assets/{send-horizontal-CgLppsIC.js → send-horizontal-CSjIm6xg.js} +1 -1
  253. package/dist/client/assets/{server-Dymv5Emh.js → server-Bzj7lPIx.js} +1 -1
  254. package/dist/client/assets/{session-catalog-DNaOEzoQ.js → session-catalog-DsaZJXZt.js} +1 -1
  255. package/dist/client/assets/session-ref-lookup-state-SATH_jAP.js +1 -0
  256. package/dist/client/assets/{session-table-PTQVoTyc.js → session-table-BQfR7u6r.js} +1 -1
  257. package/dist/client/assets/{settings-2fC_G9nX.js → settings-Dyvpkb1D.js} +1 -1
  258. package/dist/client/assets/{sliders-horizontal-BPSMHhbm.js → sliders-horizontal-C8njVwfg.js} +1 -1
  259. package/dist/client/assets/{sparkles-Dy8p5xN8.js → sparkles-CETYrxGA.js} +1 -1
  260. package/dist/client/assets/{sse-CiOSzE5n.js → sse-CD9MMUxl.js} +1 -1
  261. package/dist/client/assets/{tail-events-BMQmSx1n.js → tail-events-Ps8KpKah.js} +1 -1
  262. package/dist/client/assets/{terminal-yN9xN-h9.js → terminal-9GRS_Qdc.js} +1 -1
  263. package/dist/client/assets/{terminal-relay-9lhzEp2V.js → terminal-relay-GYMIvKs1.js} +1 -1
  264. package/dist/client/assets/{terminal-takeover-DANQfTcy.js → terminal-takeover-_Jyys4me.js} +1 -1
  265. package/dist/client/assets/{text-CnZ0e1-V.js → text-BP3oyl8-.js} +1 -1
  266. package/dist/client/assets/{text-rLpBg4_z.css → text-BYAFNLNq.css} +1 -1
  267. package/dist/client/assets/{use-conversation-list-D9BNLfnW.js → use-conversation-list-C9ABzjXI.js} +1 -1
  268. package/dist/client/assets/use-fleet-active-asks-DAspZQjE.js +1 -0
  269. package/dist/client/assets/{use-mesh-ops-items-BIvIHlqe.js → use-mesh-ops-items-Dh54ymEp.js} +1 -1
  270. package/dist/client/assets/{use-reduced-motion-CKkhq9c5.js → use-reduced-motion-x8pJMVrG.js} +1 -1
  271. package/dist/client/assets/{use-scout-dev-flags-B2KCzPxn.js → use-scout-dev-flags-DhCbiDK3.js} +1 -1
  272. package/dist/client/assets/{useAgentHoverCard-CHBsxcbp.js → useAgentHoverCard-DoBwV0LX.js} +1 -1
  273. package/dist/client/assets/useContentSecondaryNav-pZe2Zfk1.js +1 -0
  274. package/dist/client/assets/{usePierreRuntime-DUuXH5yp.js → usePierreRuntime-ejq9dCWV.js} +1 -1
  275. package/dist/client/assets/{wrench-YcXMlsh2.js → wrench-DjkoAOQt.js} +1 -1
  276. package/dist/client/assets/{x-e3h2hDsp.js → x-CAbNespN.js} +1 -1
  277. package/dist/client/assets/{xterm-Byp1Hep_.js → xterm-Cf-ngcGP.js} +1 -1
  278. package/dist/client/assets/{zap-nMcjPoWB.js → zap-Cq8eWkcF.js} +1 -1
  279. package/dist/client/index.html +1 -1
  280. package/dist/drizzle/0009_old_korg.sql +72 -0
  281. package/dist/drizzle/0010_sparkling_colonel_america.sql +2 -0
  282. package/dist/drizzle/0011_funny_onslaught.sql +4 -0
  283. package/dist/drizzle/0012_high_blazing_skull.sql +16 -0
  284. package/dist/drizzle/0013_perfect_juggernaut.sql +1 -0
  285. package/dist/drizzle/0014_legal_meggan.sql +6 -0
  286. package/dist/drizzle/0015_lame_marvel_boy.sql +12 -0
  287. package/dist/drizzle/0016_early_goliath.sql +6 -0
  288. package/dist/drizzle/meta/0009_snapshot.json +6262 -0
  289. package/dist/drizzle/meta/0010_snapshot.json +6286 -0
  290. package/dist/drizzle/meta/0011_snapshot.json +6337 -0
  291. package/dist/drizzle/meta/0012_snapshot.json +6458 -0
  292. package/dist/drizzle/meta/0013_snapshot.json +6465 -0
  293. package/dist/drizzle/meta/0014_snapshot.json +6503 -0
  294. package/dist/drizzle/meta/0015_snapshot.json +6596 -0
  295. package/dist/drizzle/meta/0016_snapshot.json +6611 -0
  296. package/dist/drizzle/meta/_journal.json +56 -0
  297. package/dist/main.mjs +1306 -360
  298. package/dist/node/main.mjs +314 -12
  299. package/dist/pairing-runtime-controller.mjs +608 -108
  300. package/dist/runtime/base-daemon.mjs +30 -3
  301. package/dist/runtime/broker-daemon.mjs +4771 -529
  302. package/dist/runtime/broker-process-manager.mjs +23 -2
  303. package/dist/runtime/mesh-discover.mjs +23 -2
  304. package/dist/scout-control-plane-web.mjs +1914 -478
  305. package/package.json +2 -2
  306. package/dist/client/assets/AgentLanesEmbedScreen-BmLnN4on.js +0 -1
  307. package/dist/client/assets/AgentLanesPreflight-5B6sXvRB.js +0 -1
  308. package/dist/client/assets/AgentLanesView-DHyzVBk6.js +0 -2
  309. package/dist/client/assets/AgentsSubnav-CDMjKeYC.js +0 -1
  310. package/dist/client/assets/ChatCollapsedStrip-Ycm0ecaX.js +0 -1
  311. package/dist/client/assets/ConversationScreen-BNULZAp2.js +0 -2
  312. package/dist/client/assets/CrewWorkspaces-CJhBKVoZ.css +0 -1
  313. package/dist/client/assets/CrewWorkspaces-CkPYIA46.js +0 -1
  314. package/dist/client/assets/FlightObserveScreen-CDWGSKZg.js +0 -1
  315. package/dist/client/assets/MessagesScreen-Eulveud3.js +0 -1
  316. package/dist/client/assets/NewChatComposer-hlWT-nbR.js +0 -19
  317. package/dist/client/assets/OpsScreen-Ppen_Lmj.js +0 -1
  318. package/dist/client/assets/OpsSubnav-GAKsoSor.js +0 -1
  319. package/dist/client/assets/ProjectAgentProfile-BLlp2e6t.js +0 -1
  320. package/dist/client/assets/ProjectsInbox-Dlt8OQ8O.js +0 -2
  321. package/dist/client/assets/ProjectsRail-DCfNMmnW.js +0 -1
  322. package/dist/client/assets/ProjectsScreen-BP-hmI1m.js +0 -1
  323. package/dist/client/assets/ProjectsThreadAside-CWpMjGIH.js +0 -1
  324. package/dist/client/assets/RealtimeVoiceScreen-D7zZHK_t.js +0 -1
  325. package/dist/client/assets/ScoutDeckSurface-CKT3qdqY.js +0 -1
  326. package/dist/client/assets/SessionEmbedScreen-RD5XOx7R.js +0 -1
  327. package/dist/client/assets/SessionRefScreen-B_cTv47v.js +0 -1
  328. package/dist/client/assets/SessionsScreen-Bq0Z77On.js +0 -1
  329. package/dist/client/assets/archive-BjcGiAy8.js +0 -1
  330. package/dist/client/assets/content-6RvWTrtk.js +0 -1
  331. package/dist/client/assets/content-BCgwMUqz.js +0 -1
  332. package/dist/client/assets/content-Bf2VLhx0.js +0 -1
  333. package/dist/client/assets/content-BrU_38-m.js +0 -1
  334. package/dist/client/assets/content-BvvJN8rN.js +0 -1
  335. package/dist/client/assets/content-CAeEqYPw.js +0 -1
  336. package/dist/client/assets/content-CI_Z9DXk.js +0 -1
  337. package/dist/client/assets/content-CbyKONhW.js +0 -1
  338. package/dist/client/assets/content-Czogvp43.js +0 -1
  339. package/dist/client/assets/content-DFk0zNzX.js +0 -1
  340. package/dist/client/assets/content-DHe4DgkA.js +0 -1
  341. package/dist/client/assets/content-DTz7cKjo.js +0 -1
  342. package/dist/client/assets/content-E3kudY-W.js +0 -1
  343. package/dist/client/assets/content-LpO0aO_U.js +0 -1
  344. package/dist/client/assets/conversation-motion-features-BKkCRaWR.js +0 -1
  345. package/dist/client/assets/discover-CHbrEItO.js +0 -2
  346. package/dist/client/assets/embed-entry-vil28MyU.js +0 -2
  347. package/dist/client/assets/index-BVlMw8x_.js +0 -1
  348. package/dist/client/assets/index-Cn0GR-q1.js +0 -1
  349. package/dist/client/assets/index-DosI4AG9.js +0 -1
  350. package/dist/client/assets/left-BpgWnaah.js +0 -1
  351. package/dist/client/assets/left-Cx0kv6pe.js +0 -1
  352. package/dist/client/assets/left-agents-qL5PpUY-.js +0 -1
  353. package/dist/client/assets/left-z-6CkuBe.js +0 -2
  354. package/dist/client/assets/projects-inbox-BBs1hG13.css +0 -1
  355. package/dist/client/assets/projects-inbox-D-zAb9Gp.js +0 -1
  356. package/dist/client/assets/resolve-panes-DO6LkwhQ.js +0 -1
  357. package/dist/client/assets/resolve-sidebar-context-CIF288wY.js +0 -1
  358. package/dist/client/assets/router-DJj2WbDg.js +0 -1
  359. package/dist/client/assets/use-fleet-active-asks-BeAdcl09.js +0 -1
  360. 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",
@@ -9769,6 +10057,7 @@ var init_broker_routes = __esm(() => {
9769
10057
  home: "/v1/home",
9770
10058
  node: "/v1/node",
9771
10059
  snapshot: "/v1/snapshot",
10060
+ conversationProjection: "/v1/conversation-projection",
9772
10061
  capabilities: "/v1/capabilities",
9773
10062
  runtimeCatalog: "/v1/runtime-catalog",
9774
10063
  topologySnapshot: "/v1/topology/snapshot",
@@ -9837,6 +10126,7 @@ var init_src = __esm(() => {
9837
10126
  init_budget();
9838
10127
  init_host_integrations();
9839
10128
  init_native_interactions();
10129
+ init_session_handle();
9840
10130
  init_runtime_profiles();
9841
10131
  init_runtime_execution();
9842
10132
  init_runtime_catalog();
@@ -15281,6 +15571,9 @@ var init_budget_observations = __esm(() => {
15281
15571
  QUOTA_HISTORY_BUCKET_MS = 60 * 60 * 1000;
15282
15572
  });
15283
15573
 
15574
+ // packages/runtime/src/runtime-session-handle.ts
15575
+ var init_runtime_session_handle = () => {};
15576
+
15284
15577
  // packages/runtime/src/sqlite-store.ts
15285
15578
  var RUNTIME_SESSION_RETENTION_MS;
15286
15579
  var init_sqlite_store = __esm(() => {
@@ -15292,6 +15585,7 @@ var init_sqlite_store = __esm(() => {
15292
15585
  init_api();
15293
15586
  init_drizzle_schema();
15294
15587
  init_budget_observations();
15588
+ init_runtime_session_handle();
15295
15589
  RUNTIME_SESSION_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
15296
15590
  });
15297
15591
 
@@ -32595,10 +32889,12 @@ var init_iroh_bridge = __esm(() => {
32595
32889
  var init_mesh_dial_order = () => {};
32596
32890
 
32597
32891
  // packages/runtime/src/mesh-forwarding.ts
32892
+ var MAX_PEER_AGENT_SNAPSHOT_BYTES;
32598
32893
  var init_mesh_forwarding = __esm(() => {
32599
32894
  init_iroh_bridge();
32600
32895
  init_mesh_peer_client();
32601
32896
  init_mesh_dial_order();
32897
+ MAX_PEER_AGENT_SNAPSHOT_BYTES = 2 * 1024 * 1024;
32602
32898
  });
32603
32899
 
32604
32900
  // packages/runtime/src/broker-api.ts
@@ -33406,11 +33702,32 @@ function resolveBundledRuntimeDirFromModuleDir(moduleDir) {
33406
33702
  resolve2(moduleDir, "..", "..")
33407
33703
  ];
33408
33704
  for (const candidate of candidates) {
33409
- if (isInstalledRuntimePackageDir(candidate))
33410
- return candidate;
33705
+ if (!isInstalledRuntimePackageDir(candidate))
33706
+ continue;
33707
+ const sourceWorkspaceRuntime = resolveSourceWorkspaceRuntimeDir(candidate);
33708
+ if (sourceWorkspaceRuntime)
33709
+ return sourceWorkspaceRuntime;
33710
+ return candidate;
33411
33711
  }
33412
33712
  return null;
33413
33713
  }
33714
+ function resolveSourceWorkspaceRuntimeDir(bundledPackageDir) {
33715
+ const packagesDir = dirname6(bundledPackageDir);
33716
+ if (resolve2(packagesDir, "cli") !== resolve2(bundledPackageDir))
33717
+ return null;
33718
+ const runtimeDir = join8(packagesDir, "runtime");
33719
+ if (!existsSync8(join8(runtimeDir, "src")))
33720
+ return null;
33721
+ if (!isInstalledRuntimePackageDir(runtimeDir))
33722
+ return null;
33723
+ try {
33724
+ const cliPackage = JSON.parse(readFileSync5(join8(bundledPackageDir, "package.json"), "utf8"));
33725
+ const runtimePackage = JSON.parse(readFileSync5(join8(runtimeDir, "package.json"), "utf8"));
33726
+ return cliPackage.name === "@openscout/scout" && runtimePackage.name === "@openscout/runtime" ? runtimeDir : null;
33727
+ } catch {
33728
+ return null;
33729
+ }
33730
+ }
33414
33731
  function findWorkspaceRuntimeDir(startDir) {
33415
33732
  let current = resolve2(startDir);
33416
33733
  while (true) {
@@ -54137,6 +54454,7 @@ var TERMINAL_ENDPOINT_STATES;
54137
54454
  var init_broker_endpoint_selection = __esm(() => {
54138
54455
  init_a2a_http_endpoint();
54139
54456
  init_local_agent_transports();
54457
+ init_runtime_session_handle();
54140
54458
  TERMINAL_ENDPOINT_STATES = new Set([
54141
54459
  "failed",
54142
54460
  "superseded",
@@ -57632,6 +57950,14 @@ function resolveScoutWebBindHost(env) {
57632
57950
  }
57633
57951
  return host;
57634
57952
  }
57953
+ function resolveScoutWebLanAccessScope(env) {
57954
+ const scope = env.OPENSCOUT_WEB_LAN_SCOPE?.trim().toLowerCase();
57955
+ if (!scope || scope === "full")
57956
+ return "full";
57957
+ if (scope === "pairing")
57958
+ return "pairing";
57959
+ throw new Error(`Unsupported OPENSCOUT_WEB_LAN_SCOPE: ${scope}`);
57960
+ }
57635
57961
  function constantTimeTokenMatch(candidate, expected) {
57636
57962
  if (!candidate)
57637
57963
  return false;
@@ -57673,20 +57999,29 @@ function scoutWebAuthCookie(token, secure) {
57673
57999
  function localInterfaceAddresses() {
57674
58000
  return Object.values(networkInterfaces2()).flatMap((entries) => entries ?? []).map((entry) => entry.address);
57675
58001
  }
57676
- function shouldIssueLocalScoutWebCredential(request, peerAddress, ownAddresses = localInterfaceAddresses()) {
58002
+ function isSameMacScoutRequest(request, peerAddress, ownAddresses = localInterfaceAddresses()) {
57677
58003
  if (!peerAddress || !isLoopbackScoutAddress(peerAddress))
57678
58004
  return false;
57679
58005
  if (request.headers.has("forwarded"))
57680
58006
  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);
58007
+ const forwardedAddresses = [
58008
+ request.headers.get("x-forwarded-for"),
58009
+ request.headers.get("x-real-ip")
58010
+ ].filter((value) => value !== null);
58011
+ for (const value of forwardedAddresses) {
58012
+ if (value.includes(","))
58013
+ return false;
58014
+ const clientAddress = normalizeIpAddress(value);
58015
+ if (!clientAddress)
58016
+ return false;
58017
+ if (!isLoopbackScoutAddress(clientAddress) && !ownAddresses.some((address) => normalizeIpAddress(address) === clientAddress)) {
58018
+ return false;
58019
+ }
58020
+ }
58021
+ return true;
58022
+ }
58023
+ function shouldIssueLocalScoutWebCredential(request, peerAddress, ownAddresses = localInterfaceAddresses()) {
58024
+ return isSameMacScoutRequest(request, peerAddress, ownAddresses);
57690
58025
  }
57691
58026
  function normalizeIpAddress(address) {
57692
58027
  const normalized = normalizeHostname(address);
@@ -57728,13 +58063,32 @@ function isLoopbackScoutAddress(address) {
57728
58063
  const mapped = normalized.startsWith("::ffff:") ? normalized.slice(7) : normalized;
57729
58064
  return LOOPBACK_IPV4_HOST_PATTERN.test(mapped);
57730
58065
  }
57731
- function defaultPeerAddress(c) {
58066
+ function resolveScoutRequestPeerAddress(c) {
57732
58067
  try {
57733
58068
  return getConnInfo(c).remote.address;
57734
58069
  } catch {
57735
58070
  return;
57736
58071
  }
57737
58072
  }
58073
+ function createScoutRequestPeerAddressRegistry() {
58074
+ const peers = new WeakMap;
58075
+ return {
58076
+ remember(request, address) {
58077
+ if (address)
58078
+ peers.set(request, address);
58079
+ },
58080
+ resolve(c) {
58081
+ return peers.get(c.req.raw) ?? resolveScoutRequestPeerAddress(c);
58082
+ }
58083
+ };
58084
+ }
58085
+ function isScoutWebRequestAllowedFromPeer(request, peerAddress, scope) {
58086
+ if (scope === "full" || isSameMacScoutRequest(request, peerAddress)) {
58087
+ return true;
58088
+ }
58089
+ const url = new URL(request.url);
58090
+ return request.method === "GET" && url.pathname === "/pair" && request.headers.get("upgrade") === null;
58091
+ }
57738
58092
  function isTrustedScoutApiRequest(request, options = {}, peerAddress) {
57739
58093
  const requestUrl = new URL(request.url);
57740
58094
  const hostname2 = normalizeHostname(requestUrl.hostname);
@@ -57880,7 +58234,7 @@ function scoutApiPerfReport() {
57880
58234
  }
57881
58235
  function installScoutApiMiddleware(app, label = "api", options = {}) {
57882
58236
  app.use("/api/*", async (c, next) => {
57883
- const peerAddress = (options.resolvePeerAddress ?? defaultPeerAddress)(c);
58237
+ const peerAddress = (options.resolvePeerAddress ?? resolveScoutRequestPeerAddress)(c);
57884
58238
  if (!isTrustedScoutApiRequest(c.req.raw, options, peerAddress)) {
57885
58239
  return c.json({ error: "forbidden" }, 403);
57886
58240
  }
@@ -58451,6 +58805,19 @@ async function readScoutBrokerHome(baseUrl = resolveScoutBrokerUrl(), options =
58451
58805
  return null;
58452
58806
  }
58453
58807
  }
58808
+ async function readScoutConversationProjection(limit = 160, baseUrl = resolveScoutBrokerUrl(), options = {}) {
58809
+ const query = new URLSearchParams({
58810
+ limit: String(Math.min(160, Math.max(1, Math.floor(limit))))
58811
+ });
58812
+ try {
58813
+ return await brokerReadJson(baseUrl, `${scoutBrokerPaths.v1.conversationProjection}?${query}`, {
58814
+ ...options,
58815
+ signal: options.signal ?? AbortSignal.timeout(450)
58816
+ });
58817
+ } catch {
58818
+ return null;
58819
+ }
58820
+ }
58454
58821
  async function readScoutBrokerRuntimeCatalog(baseUrl = resolveScoutBrokerUrl()) {
58455
58822
  try {
58456
58823
  return await brokerReadJson(baseUrl, scoutBrokerPaths.v1.runtimeCatalog);
@@ -58458,6 +58825,13 @@ async function readScoutBrokerRuntimeCatalog(baseUrl = resolveScoutBrokerUrl())
58458
58825
  return null;
58459
58826
  }
58460
58827
  }
58828
+ async function readScoutBrokerTailDiscovery(baseUrl = resolveScoutBrokerUrl()) {
58829
+ try {
58830
+ return await brokerReadJson(baseUrl, scoutBrokerPaths.v1.tailDiscover);
58831
+ } catch {
58832
+ return null;
58833
+ }
58834
+ }
58461
58835
  async function readScoutBrokerSnapshot(baseUrl = resolveScoutBrokerUrl(), options = {}) {
58462
58836
  try {
58463
58837
  return await brokerReadJson(baseUrl, scoutBrokerPaths.v1.snapshot, options);
@@ -58495,26 +58869,24 @@ async function recordScoutBrokerReadCursor(input, baseUrl = resolveScoutBrokerUr
58495
58869
  });
58496
58870
  }
58497
58871
  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;
58872
+ const usesRollingWindow = options.since === undefined;
58873
+ 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
58874
  if (options.force) {
58500
58875
  invalidateScoutBrokerContextCache(baseUrl);
58501
58876
  }
58502
58877
  const cacheKey = options.signal ? null : [
58503
58878
  baseUrl,
58504
58879
  resolveBrokerSocketPathForBaseUrl(baseUrl) ?? "http",
58505
- since ?? "full",
58880
+ usesRollingWindow ? "rolling-24h" : since ?? "full",
58506
58881
  options.scope ?? "default"
58507
58882
  ].join("\x00");
58508
58883
  const now = Date.now();
58509
- for (const [key, entry] of scoutBrokerContextCache) {
58510
- if (entry.expiresAt <= now)
58884
+ for (const [key, entry2] of scoutBrokerContextCache) {
58885
+ if (key !== cacheKey && !entry2.inFlight && entry2.expiresAt + SCOUT_BROKER_CONTEXT_STALE_RETENTION_MS <= now) {
58511
58886
  scoutBrokerContextCache.delete(key);
58887
+ }
58512
58888
  }
58513
- const cached = cacheKey ? scoutBrokerContextCache.get(cacheKey) : null;
58514
- if (cached && cached.expiresAt > now) {
58515
- return cached.promise;
58516
- }
58517
- const promise = (async () => {
58889
+ const readContext = async () => {
58518
58890
  const health = await readScoutBrokerHealth(baseUrl, { signal: options.signal });
58519
58891
  if (!health.reachable || !health.ok) {
58520
58892
  return null;
@@ -58538,19 +58910,60 @@ async function loadScoutBrokerContext(baseUrl = resolveScoutBrokerUrl(), options
58538
58910
  } catch {
58539
58911
  return null;
58540
58912
  }
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) {
58913
+ };
58914
+ if (!cacheKey) {
58915
+ return readContext();
58916
+ }
58917
+ const refresh = (entry2, delayMs = 0) => {
58918
+ if (entry2.inFlight)
58919
+ return entry2.inFlight;
58920
+ const attempt = (delayMs > 0 ? new Promise((resolve17) => setTimeout(resolve17, delayMs)) : Promise.resolve()).then(readContext).then((context) => {
58921
+ if (scoutBrokerContextCache.get(cacheKey) !== entry2)
58922
+ return context;
58923
+ if (context) {
58924
+ entry2.value = context;
58925
+ entry2.hasValue = true;
58926
+ entry2.expiresAt = Date.now() + SCOUT_BROKER_CONTEXT_CACHE_TTL_MS;
58927
+ } else if (!entry2.hasValue) {
58549
58928
  scoutBrokerContextCache.delete(cacheKey);
58929
+ } else {
58930
+ entry2.expiresAt = Date.now() + SCOUT_BROKER_CONTEXT_CACHE_TTL_MS;
58931
+ }
58932
+ return context;
58933
+ }).finally(() => {
58934
+ if (scoutBrokerContextCache.get(cacheKey) === entry2) {
58935
+ entry2.inFlight = null;
58550
58936
  }
58551
58937
  });
58938
+ entry2.inFlight = attempt;
58939
+ return attempt;
58940
+ };
58941
+ const cached = scoutBrokerContextCache.get(cacheKey);
58942
+ if (cached) {
58943
+ if (cached.hasValue) {
58944
+ if (cached.expiresAt <= now && !cached.inFlight) {
58945
+ refresh(cached);
58946
+ }
58947
+ return cached.value;
58948
+ }
58949
+ if (options.waitForInitial === false) {
58950
+ refresh(cached, options.initialRefreshDelayMs);
58951
+ return null;
58952
+ }
58953
+ return refresh(cached);
58552
58954
  }
58553
- return promise;
58955
+ const entry = {
58956
+ expiresAt: now + SCOUT_BROKER_CONTEXT_CACHE_TTL_MS,
58957
+ value: null,
58958
+ hasValue: false,
58959
+ inFlight: null
58960
+ };
58961
+ scoutBrokerContextCache.set(cacheKey, entry);
58962
+ if (options.waitForInitial === false) {
58963
+ refresh(entry, options.initialRefreshDelayMs);
58964
+ return null;
58965
+ }
58966
+ return refresh(entry);
58554
58967
  }
58555
58968
  async function requireScoutBrokerContext(baseUrl = resolveScoutBrokerUrl()) {
58556
58969
  const context = await loadScoutBrokerContext(baseUrl);
@@ -59336,14 +59749,11 @@ async function loadScoutReadCursors(input) {
59336
59749
  return brokerReadJson(input.baseUrl ?? resolveScoutBrokerUrl(), path);
59337
59750
  }
59338
59751
  async function markScoutConversationRead(input) {
59339
- const broker2 = await loadScoutBrokerContext(input.baseUrl);
59340
- if (!broker2) {
59341
- throw new Error("broker unreachable");
59342
- }
59752
+ const baseUrl = input.baseUrl ?? resolveScoutBrokerUrl();
59343
59753
  const path = `/v1/conversations/${encodeURIComponent(input.conversationId)}/read-cursors`;
59344
- return brokerPostJson(broker2.baseUrl, path, {
59754
+ return brokerPostJson(baseUrl, path, {
59345
59755
  actorId: input.actorId,
59346
- readerNodeId: input.readerNodeId ?? broker2.node.id,
59756
+ readerNodeId: input.readerNodeId,
59347
59757
  lastReadMessageId: input.lastReadMessageId,
59348
59758
  lastReadSeq: input.lastReadSeq,
59349
59759
  lastReadAt: input.lastReadAt,
@@ -59507,7 +59917,7 @@ async function loadScoutRelayConfig() {
59507
59917
  return {};
59508
59918
  }
59509
59919
  }
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;
59920
+ 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
59921
  var init_service = __esm(() => {
59512
59922
  init_src();
59513
59923
  init_setup();
@@ -60886,14 +61296,47 @@ function existingDirectConversationIdsForAgents(agentIds) {
60886
61296
  }
60887
61297
  return conversationIdByAgentId;
60888
61298
  }
61299
+ function queryAgentIdsForBoundedRoster(limit) {
61300
+ const database = db2();
61301
+ let recentIds;
61302
+ try {
61303
+ recentIds = database.prepare(RECENT_ENDPOINT_AGENT_IDS_SQL).all(limit);
61304
+ } catch (error) {
61305
+ if (!(error instanceof Error) || !error.message.includes("idx_agent_endpoints_roster_recency")) {
61306
+ throw error;
61307
+ }
61308
+ recentIds = database.prepare(RECENT_ENDPOINT_AGENT_IDS_WITHOUT_INDEX_SQL).all(limit);
61309
+ }
61310
+ if (recentIds.length >= limit) {
61311
+ return recentIds.map((row) => row.id);
61312
+ }
61313
+ const endpointUpdatedAtExpression2 = sqlTimestampMsExpression("ep.updated_at");
61314
+ const fallbackIds = database.prepare(`SELECT a.id
61315
+ FROM agents a
61316
+ JOIN actors ac ON ac.id = a.id
61317
+ ${LATEST_AGENT_ENDPOINT_JOIN}
61318
+ WHERE ${activeAgentMetadataPredicate("a")}
61319
+ AND COALESCE(${endpointUpdatedAtExpression2}, 0) <= 0
61320
+ ORDER BY COALESCE(${endpointUpdatedAtExpression2}, 0) DESC, ac.display_name ASC
61321
+ LIMIT ?`).all(limit - recentIds.length);
61322
+ return [...recentIds, ...fallbackIds].map((row) => row.id);
61323
+ }
61324
+ function queryAgentRowsByIds(agentIds) {
61325
+ if (agentIds.length === 0)
61326
+ return [];
61327
+ return db2().prepare(`${AGENT_ROW_SELECT}
61328
+ WHERE a.id IN (SELECT value FROM json_each(?))
61329
+ AND ${activeAgentMetadataPredicate("a")}
61330
+ ORDER BY COALESCE(${endpointUpdatedAtExpression}, 0) DESC, ac.display_name ASC`).all(JSON.stringify(agentIds));
61331
+ }
60889
61332
  function queryAgentIdsByEndpointSessionId() {
60890
- const endpointUpdatedAtExpression = sqlTimestampMsExpression("ep.updated_at");
61333
+ const endpointUpdatedAtExpression2 = sqlTimestampMsExpression("ep.updated_at");
60891
61334
  const rows = db2().prepare(`SELECT a.id, ep.session_id
60892
61335
  FROM agents a
60893
61336
  ${LATEST_AGENT_ENDPOINT_JOIN}
60894
61337
  WHERE ep.session_id IS NOT NULL
60895
61338
  AND ${activeAgentMetadataPredicate("a")}
60896
- ORDER BY COALESCE(${endpointUpdatedAtExpression}, 0) ASC`).all();
61339
+ ORDER BY COALESCE(${endpointUpdatedAtExpression2}, 0) ASC`).all();
60897
61340
  const map = new Map;
60898
61341
  for (const row of rows) {
60899
61342
  const sessionId = row.session_id.trim();
@@ -60905,87 +61348,20 @@ function queryAgentIdsByEndpointSessionId() {
60905
61348
  }
60906
61349
  function queryAgents(limit = 500) {
60907
61350
  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);
61351
+ let rows;
61352
+ if (limit < 0) {
61353
+ rows = db2().prepare(`${AGENT_ROW_SELECT}
61354
+ WHERE ${activeAgentMetadataPredicate("a")}
61355
+ ORDER BY COALESCE(${endpointUpdatedAtExpression}, 0) DESC, ac.display_name ASC
61356
+ LIMIT ?`).all(limit);
61357
+ } else {
61358
+ rows = queryAgentRowsByIds(queryAgentIdsForBoundedRoster(limit));
61359
+ }
60948
61360
  return mapAgentRows(rows, flightPhases, existingDirectConversationIdsForAgents(rows.map((row) => row.id)));
60949
61361
  }
60950
61362
  function queryAgentById(agentId) {
60951
61363
  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}
61364
+ const row = db2().prepare(`${AGENT_ROW_SELECT}
60989
61365
  WHERE a.id = ?
60990
61366
  AND ${activeAgentMetadataPredicate("a")}
60991
61367
  LIMIT 1`).get(agentId);
@@ -61051,12 +61427,70 @@ function mapAgentRows(rows, flightPhases, conversationIdByAgentId) {
61051
61427
  };
61052
61428
  });
61053
61429
  }
61430
+ var actorCreatedAtExpression, endpointUpdatedAtExpression, AGENT_ROW_SELECT, RECENT_ENDPOINT_AGENT_IDS_WITHOUT_INDEX_SQL, RECENT_ENDPOINT_AGENT_IDS_SQL;
61054
61431
  var init_agents = __esm(() => {
61055
61432
  init_src();
61056
61433
  init_db2();
61057
61434
  init_parse2();
61058
61435
  init_paths3();
61059
61436
  init_sql_helpers();
61437
+ actorCreatedAtExpression = sqlTimestampMsExpression("ac.created_at");
61438
+ endpointUpdatedAtExpression = sqlTimestampMsExpression("ep.updated_at");
61439
+ AGENT_ROW_SELECT = `SELECT
61440
+ a.id,
61441
+ a.definition_id,
61442
+ a.node_qualifier,
61443
+ a.workspace_qualifier,
61444
+ a.selector,
61445
+ ac.display_name AS name,
61446
+ ac.handle,
61447
+ ${actorCreatedAtExpression} AS actor_created_at,
61448
+ a.agent_class,
61449
+ a.default_selector,
61450
+ a.wake_policy,
61451
+ a.capabilities_json,
61452
+ a.metadata_json,
61453
+ a.authority_node_id,
61454
+ an.name AS authority_node_name,
61455
+ a.home_node_id,
61456
+ hn.name AS home_node_name,
61457
+ a.owner_id,
61458
+ oa.display_name AS owner_name,
61459
+ oa.handle AS owner_handle,
61460
+ ep.harness,
61461
+ ep.transport,
61462
+ ep.state,
61463
+ ep.project_root,
61464
+ ep.cwd,
61465
+ ep.session_id,
61466
+ ep.metadata_json AS endpoint_metadata_json,
61467
+ ${endpointUpdatedAtExpression} AS updated_at
61468
+ FROM agents a
61469
+ JOIN actors ac ON ac.id = a.id
61470
+ LEFT JOIN nodes an ON an.id = a.authority_node_id
61471
+ LEFT JOIN nodes hn ON hn.id = a.home_node_id
61472
+ LEFT JOIN actors oa ON oa.id = a.owner_id
61473
+ ${LATEST_AGENT_ENDPOINT_JOIN}`;
61474
+ RECENT_ENDPOINT_AGENT_IDS_WITHOUT_INDEX_SQL = (() => {
61475
+ const endpointUpdatedAtExpression2 = sqlTimestampMsExpression("ep.updated_at");
61476
+ const latestEndpointUpdatedAtExpression = sqlTimestampMsCoalesceExpression("ep2.updated_at");
61477
+ return `SELECT a.id
61478
+ FROM agent_endpoints ep
61479
+ JOIN agents a ON a.id = ep.agent_id
61480
+ JOIN actors ac ON ac.id = a.id
61481
+ WHERE ep.id = (
61482
+ SELECT ep2.id
61483
+ FROM agent_endpoints ep2
61484
+ WHERE ep2.agent_id = a.id
61485
+ ORDER BY ${latestEndpointUpdatedAtExpression} DESC
61486
+ LIMIT 1
61487
+ )
61488
+ AND ${endpointUpdatedAtExpression2} > 0
61489
+ AND ${activeAgentMetadataPredicate("a")}
61490
+ ORDER BY ${endpointUpdatedAtExpression2} DESC, ac.display_name ASC
61491
+ LIMIT ?`;
61492
+ })();
61493
+ 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
61494
  });
61061
61495
 
61062
61496
  // packages/web/server/db/internal/conversation-ids.ts
@@ -61148,7 +61582,11 @@ function queryRecentMessages(limit = 80, opts) {
61148
61582
  const beforeMessage = resolveBeforeMessage(opts?.beforeMessageId, conversationIds, messageCreatedAtExpression);
61149
61583
  const where = sqlJoinClauses([
61150
61584
  transientBrokerWorkingStatusPredicate("m"),
61151
- conversationIds.length > 0 ? `m.conversation_id IN (${sqlPlaceholders(conversationIds.length)})` : "m.conversation_id LIKE 'c.%' AND length(m.conversation_id) > 2",
61585
+ conversationIds.length > 0 ? `m.conversation_id IN (${sqlPlaceholders(conversationIds.length)})` : `(
61586
+ (m.conversation_id LIKE 'chn-%' AND length(m.conversation_id) > 4)
61587
+ OR (substr(m.conversation_id, 1, 5) = 'chat_' AND length(m.conversation_id) > 5)
61588
+ OR (m.conversation_id LIKE 'c.%' AND length(m.conversation_id) > 2)
61589
+ )`,
61152
61590
  beforeMessage ? `(
61153
61591
  ${messageCreatedAtExpression} < ?
61154
61592
  OR (${messageCreatedAtExpression} = ? AND m.id < ?)
@@ -62747,6 +63185,30 @@ function metadataString8(metadata, key) {
62747
63185
  const value = metadata[key];
62748
63186
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
62749
63187
  }
63188
+ function executionResolutionDimension(resolution, dimension) {
63189
+ const value = resolution?.[dimension];
63190
+ return value?.observed?.trim() || value?.resolved?.trim() || value?.requested?.trim() || null;
63191
+ }
63192
+ function durableConversationRuntime(row) {
63193
+ if (!row)
63194
+ return null;
63195
+ const execution = parseMetadataJson(row.execution_json);
63196
+ const rawResolution = parseMetadataJson(row.execution_resolution_json);
63197
+ const resolution = rawResolution.schemaVersion === "openscout.execution-resolution.v1" ? rawResolution : null;
63198
+ const flightMetadata = parseMetadataJson(row.flight_metadata_json);
63199
+ const trace = Array.isArray(flightMetadata.sessionTrace) ? [...flightMetadata.sessionTrace].reverse().find((entry) => entry && typeof entry === "object" && !Array.isArray(entry)) : undefined;
63200
+ const traceResolutionRaw = trace?.executionResolution;
63201
+ const traceResolution = traceResolutionRaw && typeof traceResolutionRaw === "object" && !Array.isArray(traceResolutionRaw) && traceResolutionRaw.schemaVersion === "openscout.execution-resolution.v1" ? traceResolutionRaw : null;
63202
+ const effectiveResolution = traceResolution ?? resolution;
63203
+ return {
63204
+ targetAgentId: row.target_agent_id,
63205
+ harness: executionResolutionDimension(effectiveResolution, "harness") ?? metadataString8(trace ?? {}, "harness") ?? metadataString8(execution, "harness"),
63206
+ model: executionResolutionDimension(effectiveResolution, "model") ?? metadataString8(execution, "model"),
63207
+ reasoningEffort: executionResolutionDimension(effectiveResolution, "reasoningEffort") ?? metadataString8(execution, "reasoningEffort"),
63208
+ transport: metadataString8(trace ?? {}, "transport"),
63209
+ sessionId: metadataString8(trace ?? {}, "sessionId") ?? resolution?.sessionId?.trim() ?? metadataString8(execution, "targetSessionId")
63210
+ };
63211
+ }
62750
63212
  function formatChannelAlias(value) {
62751
63213
  const trimmed = value.trim();
62752
63214
  if (!trimmed)
@@ -62798,22 +63260,34 @@ function projectSessionConversationRows(rows, opts) {
62798
63260
  AND ${transientBrokerWorkingStatusPredicate("m")}
62799
63261
  ORDER BY ${messageCreatedAtExpression} DESC
62800
63262
  LIMIT 1`);
63263
+ const runtimeStmt = db2().prepare(`SELECT target_agent_id, execution_json, execution_resolution_json, flight_metadata_json
63264
+ FROM invocations
63265
+ WHERE conversation_id = ?
63266
+ ORDER BY ${sqlTimestampMsExpression("created_at")} DESC, id DESC
63267
+ LIMIT 1`);
62801
63268
  const summaries = rows.flatMap((r) => {
62802
63269
  const conversationMetadata = parseMetadataJson(r.metadata_json);
62803
63270
  const participants = memberStmt.all(r.id).map((m) => m.actor_id);
62804
63271
  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;
63272
+ const runtime = durableConversationRuntime(runtimeStmt.get(r.id));
63273
+ const runtimeTargetAgentId = runtime?.targetAgentId && agentParticipants.some((entry) => entry.agent_id === runtime.targetAgentId) ? runtime.targetAgentId : null;
63274
+ const primaryAgentId = r.kind === "direct" ? runtimeTargetAgentId ?? pickDirectConversationAgentId(participants, agentParticipants.map((entry) => entry.agent_id)) : agentParticipants.length === 1 ? agentParticipants[0]?.agent_id ?? null : null;
62806
63275
  const primaryAgent = primaryAgentId ? agentParticipants.find((entry) => entry.agent_id === primaryAgentId) ?? null : null;
62807
63276
  const agentId = primaryAgent?.agent_id ?? null;
62808
63277
  let agentName = null;
62809
- let harness2 = null;
63278
+ let harness2 = runtime?.harness ?? null;
63279
+ let model2 = runtime?.model ?? null;
63280
+ let reasoningEffort = runtime?.reasoningEffort ?? null;
63281
+ let transport = runtime?.transport ?? null;
63282
+ let sessionId = runtime?.sessionId ?? null;
62810
63283
  let harnessSessionId = null;
62811
63284
  let harnessLogPath = null;
62812
63285
  let branch = null;
62813
63286
  let workspaceRoot = null;
62814
63287
  if (primaryAgent) {
62815
63288
  agentName = primaryAgent.display_name;
62816
- harness2 = primaryAgent.harness;
63289
+ harness2 = harness2 ?? primaryAgent.harness;
63290
+ transport = transport ?? primaryAgent.transport;
62817
63291
  workspaceRoot = compact(primaryAgent.project_root);
62818
63292
  try {
62819
63293
  const meta = primaryAgent.metadata_json ? JSON.parse(primaryAgent.metadata_json) : {};
@@ -62821,13 +63295,19 @@ function projectSessionConversationRows(rows, opts) {
62821
63295
  return [];
62822
63296
  }
62823
63297
  branch = meta.branch ?? meta.workspaceQualifier ?? null;
63298
+ model2 = model2 ?? metadataString8(meta, "model");
63299
+ reasoningEffort = reasoningEffort ?? metadataString8(meta, "reasoningEffort");
62824
63300
  } catch {}
62825
63301
  try {
62826
63302
  const endpointMeta = primaryAgent.endpoint_metadata_json ? JSON.parse(primaryAgent.endpoint_metadata_json) : {};
62827
63303
  harnessSessionId = resolveHarnessSessionId(primaryAgent.transport, primaryAgent.session_id, endpointMeta);
63304
+ sessionId = sessionId ?? harnessSessionId;
63305
+ model2 = model2 ?? metadataString8(endpointMeta, "observedModel") ?? metadataString8(endpointMeta, "model");
63306
+ reasoningEffort = reasoningEffort ?? metadataString8(endpointMeta, "observedReasoningEffort") ?? metadataString8(endpointMeta, "reasoningEffort");
62828
63307
  harnessLogPath = resolveHarnessLogPath(primaryAgent.agent_id, primaryAgent.transport, primaryAgent.session_id, endpointMeta);
62829
63308
  } catch {
62830
63309
  harnessSessionId = resolveHarnessSessionId(primaryAgent.transport, primaryAgent.session_id, undefined);
63310
+ sessionId = sessionId ?? harnessSessionId;
62831
63311
  harnessLogPath = resolveHarnessLogPath(primaryAgent.agent_id, primaryAgent.transport, primaryAgent.session_id, undefined);
62832
63312
  }
62833
63313
  }
@@ -62842,6 +63322,10 @@ function projectSessionConversationRows(rows, opts) {
62842
63322
  agentId,
62843
63323
  agentName,
62844
63324
  harness: harness2,
63325
+ model: model2,
63326
+ reasoningEffort,
63327
+ transport,
63328
+ sessionId,
62845
63329
  harnessSessionId,
62846
63330
  harnessLogPath,
62847
63331
  currentBranch: branch,
@@ -62898,7 +63382,9 @@ function querySessions(limit = 80) {
62898
63382
  FROM messages
62899
63383
  GROUP BY conversation_id
62900
63384
  ) ms ON ms.conversation_id = c.id
62901
- WHERE c.id LIKE 'c.%' AND length(c.id) > 2
63385
+ WHERE (c.id LIKE 'chn-%' AND length(c.id) > 4)
63386
+ OR (substr(c.id, 1, 5) = 'chat_' AND length(c.id) > 5)
63387
+ OR (c.id LIKE 'c.%' AND length(c.id) > 2)
62902
63388
  ORDER BY COALESCE(ms.last_message_at, ${conversationCreatedAtExpression}, 0) DESC, ${conversationCreatedAtExpression} DESC, c.id ASC
62903
63389
  LIMIT ?`).all(limit);
62904
63390
  return projectSessionConversationRows(rows, {
@@ -62926,6 +63412,7 @@ function querySessionFallbackFromMessages(conversationId) {
62926
63412
  m.actor_id AS agent_id,
62927
63413
  ac.display_name,
62928
63414
  ep.harness,
63415
+ ep.transport,
62929
63416
  ep.project_root,
62930
63417
  ep.session_id,
62931
63418
  ep.metadata_json AS endpoint_metadata_json
@@ -62947,8 +63434,12 @@ function querySessionFallbackFromMessages(conversationId) {
62947
63434
  agentId: agentRow?.agent_id ?? null,
62948
63435
  agentName: agentRow?.display_name ?? null,
62949
63436
  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,
63437
+ model: metadataString8(endpointMetadata, "observedModel") ?? metadataString8(endpointMetadata, "model"),
63438
+ reasoningEffort: metadataString8(endpointMetadata, "observedReasoningEffort") ?? metadataString8(endpointMetadata, "reasoningEffort"),
63439
+ transport: agentRow?.transport ?? null,
63440
+ sessionId: resolveHarnessSessionId(agentRow?.transport ?? null, agentRow?.session_id ?? null, endpointMetadata),
63441
+ harnessSessionId: resolveHarnessSessionId(agentRow?.transport ?? null, agentRow?.session_id ?? null, endpointMetadata),
63442
+ harnessLogPath: agentRow?.agent_id ? resolveHarnessLogPath(agentRow.agent_id, agentRow.transport, agentRow.session_id, endpointMetadata) : null,
62952
63443
  currentBranch: metadataString8(endpointMetadata, "branch"),
62953
63444
  preview: previewRow?.body?.trim() || null,
62954
63445
  messageCount: stats.message_count,
@@ -64000,8 +64491,8 @@ function queryFleetAskRows(requesterIds, limit) {
64000
64491
  ep.state AS endpoint_state,
64001
64492
  cr.title AS work_title,
64002
64493
  cr.summary AS work_summary,
64494
+ cr.kind AS record_kind,
64003
64495
  cr.state AS work_state,
64004
- cr.acceptance_state,
64005
64496
  cr.next_move_owner_id,
64006
64497
  cr.updated_at AS work_updated_at
64007
64498
  FROM invocations inv
@@ -64063,7 +64554,8 @@ function projectFleetAsk(row, requesterIdSet) {
64063
64554
  const queuedUntilOnline = queuedFlight && row.dispatch_outcome_status === "queued_until_online";
64064
64555
  const staleActiveFlight = hasFlight && row.flight_state !== null && !TERMINAL_FLIGHT_STATES.has(row.flight_state) && isStaleActiveFlight(row.started_at, row.created_at);
64065
64556
  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");
64557
+ 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);
64558
+ const awaitingOperator = attentionEligibleState && Boolean(row.next_move_owner_id && requesterIdSet.has(row.next_move_owner_id));
64067
64559
  const updatedAt = normalizeTimestampMs(row.status_ts ?? row.completed_at ?? row.started_at ?? row.work_updated_at ?? row.created_at) ?? Date.now();
64068
64560
  const dismissedAt = normalizeTimestampMs(row.flight_dismissed_at);
64069
64561
  const recoveredAfterFailureAt = normalizeTimestampMs(row.recovered_after_failure_at);
@@ -64076,10 +64568,10 @@ function projectFleetAsk(row, requesterIdSet) {
64076
64568
  status = queuedUntilOnline ? "failed" : "queued";
64077
64569
  } else if (isActiveFlight) {
64078
64570
  status = "working";
64079
- } else if (awaitingOperator) {
64080
- status = "needs_attention";
64081
64571
  } else if (failed || staleActiveFlight) {
64082
64572
  status = "failed";
64573
+ } else if (awaitingOperator) {
64574
+ status = "needs_attention";
64083
64575
  } else {
64084
64576
  status = "completed";
64085
64577
  }
@@ -64123,34 +64615,7 @@ function queryFleetAttentionRows(requesterIds, limit) {
64123
64615
  (cr.kind = 'work_item' AND cr.state IN ('open', 'working', 'waiting', 'review'))
64124
64616
  OR (cr.kind = 'question' AND cr.state IN ('open', 'answered'))
64125
64617
  )
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
- )
64618
+ AND cr.next_move_owner_id IN (${requesterClause})
64154
64619
  AND NOT EXISTS (
64155
64620
  SELECT 1
64156
64621
  FROM collaboration_events dismissed
@@ -66101,7 +66566,7 @@ var init_service2 = __esm(() => {
66101
66566
  // packages/web/server/service-budgets.ts
66102
66567
  import { createHash as createHash8 } from "crypto";
66103
66568
  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";
66569
+ import { open as open2, readdir as readdir4, stat as stat10 } from "fs/promises";
66105
66570
  import { homedir as homedir45 } from "os";
66106
66571
  import { dirname as dirname35, join as join63 } from "path";
66107
66572
  import { createInterface as createInterface2 } from "readline";
@@ -66181,12 +66646,14 @@ async function loadCodexGauge(forceRefresh = false) {
66181
66646
  const root = join63(homeDir(), ".codex", "sessions");
66182
66647
  if (!existsSync45(root))
66183
66648
  return fallback;
66184
- const recent = findRecentCodexJsonl(root, CODEX_LOOKBACK_DAYS);
66185
- if (recent.length === 0)
66649
+ const candidates = await findRecentCodexJsonl(root, CODEX_LOOKBACK_DAYS);
66650
+ if (candidates.length === 0)
66186
66651
  return fallback;
66187
- const observations = (await Promise.all(recent.map((path2) => readLatestCodexRateLimits(path2)))).flat().sort((left, right) => left.capturedAt - right.capturedAt);
66652
+ const observations = (await harvestRecentCodexRateLimits(candidates)).sort((left, right) => left.capturedAt - right.capturedAt);
66188
66653
  if (observations.length === 0)
66189
66654
  return fallback;
66655
+ if (!observations.some(isCodexAccountPoolObservation))
66656
+ return fallback;
66190
66657
  const snapshots = bindingCodexPoolSnapshots(observations.flatMap((observation) => codexQuotaSnapshotsFromObservation(observation.usage, observation.capturedAt)));
66191
66658
  persistQuotaSnapshots(snapshots);
66192
66659
  const persisted = loadPersistedProviderQuotaGauge({
@@ -66243,9 +66710,11 @@ function bindingCodexPoolSnapshots(snapshots) {
66243
66710
  const usedPercent = quotaSnapshotUsage(row)?.fill;
66244
66711
  if (usedPercent === undefined)
66245
66712
  continue;
66713
+ const poolId = poolIdOf(row);
66714
+ const accountPool = isCodexAccountPoolId(poolId);
66246
66715
  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 });
66716
+ if (!best || usedPercent > best.usedPercent || usedPercent === best.usedPercent && accountPool && !best.accountPool || usedPercent === best.usedPercent && accountPool === best.accountPool && row.capturedAt > best.capturedAt) {
66717
+ bindingPoolByLabel.set(row.label, { poolId, usedPercent, capturedAt: row.capturedAt, accountPool });
66249
66718
  }
66250
66719
  }
66251
66720
  return snapshots.filter((row) => bindingPoolByLabel.get(row.label)?.poolId === poolIdOf(row));
@@ -66262,37 +66731,83 @@ function formatQuotaWindowLabel(windowMinutes, fallback) {
66262
66731
  }
66263
66732
  return `${windowMinutes}m`;
66264
66733
  }
66265
- function findRecentCodexJsonl(root, lookbackDays) {
66734
+ async function findRecentCodexJsonl(root, lookbackDays) {
66266
66735
  const cutoffMs = Date.now() - lookbackDays * 86400 * 1000;
66267
66736
  const paths2 = [];
66268
- const walk = (dir) => {
66737
+ const pending = [root];
66738
+ while (pending.length > 0) {
66739
+ const dir = pending.pop();
66740
+ if (!dir)
66741
+ continue;
66269
66742
  let entries2;
66270
66743
  try {
66271
- entries2 = readdirSync12(dir, { withFileTypes: true });
66744
+ entries2 = await readdir4(dir, { withFileTypes: true });
66272
66745
  } catch {
66273
- return;
66746
+ continue;
66274
66747
  }
66275
66748
  for (const entry of entries2) {
66276
66749
  const full = join63(dir, entry.name);
66277
66750
  if (entry.isDirectory()) {
66278
- walk(full);
66751
+ pending.push(full);
66279
66752
  } 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 });
66753
+ try {
66754
+ const fileStat = await stat10(full);
66755
+ if (fileStat.mtimeMs < cutoffMs)
66756
+ continue;
66757
+ paths2.push({ path: full, mtimeMs: fileStat.mtimeMs });
66758
+ } catch {}
66284
66759
  }
66285
66760
  }
66286
- };
66287
- walk(root);
66288
- return paths2.sort((left, right) => right.mtimeMs - left.mtimeMs).map((entry) => entry.path);
66761
+ }
66762
+ return paths2.sort((left, right) => right.mtimeMs - left.mtimeMs).slice(0, CODEX_RECENT_CANDIDATE_LIMIT);
66763
+ }
66764
+ async function harvestRecentCodexRateLimits(candidates) {
66765
+ const observations = await readCodexCandidateBatch(candidates.slice(0, CODEX_INITIAL_FILE_LIMIT));
66766
+ if (observations.some(isCodexAccountPoolObservation))
66767
+ return observations;
66768
+ const overflow = candidates.slice(CODEX_INITIAL_FILE_LIMIT, CODEX_RECENT_CANDIDATE_LIMIT);
66769
+ for (let index2 = 0;index2 < overflow.length; index2 += CODEX_JSONL_READ_CONCURRENCY) {
66770
+ observations.push(...await readCodexCandidateBatch(overflow.slice(index2, index2 + CODEX_JSONL_READ_CONCURRENCY)));
66771
+ if (observations.some(isCodexAccountPoolObservation))
66772
+ break;
66773
+ }
66774
+ return observations;
66775
+ }
66776
+ async function readCodexCandidateBatch(candidates) {
66777
+ return (await Promise.all(candidates.map(async (candidate) => {
66778
+ try {
66779
+ return await readLatestCodexRateLimits(candidate.path);
66780
+ } catch {
66781
+ return [];
66782
+ }
66783
+ }))).flat();
66784
+ }
66785
+ function isCodexAccountPoolObservation(observation) {
66786
+ return isCodexAccountPoolId(observation.usage.limitId);
66787
+ }
66788
+ function isCodexAccountPoolId(limitId) {
66789
+ const normalized = limitId?.trim().toLowerCase();
66790
+ return !normalized || normalized === "codex";
66289
66791
  }
66290
66792
  async function readLatestCodexRateLimits(path2) {
66291
66793
  const handle = await open2(path2, "r");
66292
66794
  try {
66293
- const rl = createInterface2({ input: handle.createReadStream({ encoding: "utf8" }) });
66795
+ const fileStat = await handle.stat();
66796
+ const start = Math.max(0, fileStat.size - CODEX_JSONL_TAIL_MAX_BYTES);
66797
+ const rl = createInterface2({
66798
+ input: handle.createReadStream({
66799
+ encoding: "utf8",
66800
+ start,
66801
+ autoClose: false
66802
+ })
66803
+ });
66294
66804
  const latestByPool = new Map;
66805
+ let firstLine = start > 0;
66295
66806
  for await (const line of rl) {
66807
+ if (firstLine) {
66808
+ firstLine = false;
66809
+ continue;
66810
+ }
66296
66811
  if (!line.includes('"rate_limits"'))
66297
66812
  continue;
66298
66813
  try {
@@ -66369,8 +66884,8 @@ function loadClaudeStatuslineQuotaSnapshots() {
66369
66884
  }
66370
66885
  function readClaudeStatuslineHistory(minCapturedAt) {
66371
66886
  const path2 = join63(claudeStatuslineDir(), "claude-history.jsonl");
66372
- const stat10 = safeStat(path2);
66373
- if (!stat10)
66887
+ const stat11 = safeStat(path2);
66888
+ if (!stat11)
66374
66889
  return [];
66375
66890
  let content;
66376
66891
  try {
@@ -66378,7 +66893,7 @@ function readClaudeStatuslineHistory(minCapturedAt) {
66378
66893
  } catch {
66379
66894
  return [];
66380
66895
  }
66381
- if (stat10.size > CLAUDE_STATUSLINE_HISTORY_MAX_BYTES) {
66896
+ if (stat11.size > CLAUDE_STATUSLINE_HISTORY_MAX_BYTES) {
66382
66897
  content = content.slice(-CLAUDE_STATUSLINE_HISTORY_MAX_BYTES);
66383
66898
  const firstNewline = content.indexOf(`
66384
66899
  `);
@@ -67078,11 +67593,11 @@ async function loadGrokGauge() {
67078
67593
  }
67079
67594
  async function loadGrokBillingQuotaSnapshots() {
67080
67595
  const path2 = join63(homeDir(), ".grok", "logs", "unified.jsonl");
67081
- const stat10 = safeStat(path2);
67082
- if (!stat10 || stat10.size <= 0)
67596
+ const stat11 = safeStat(path2);
67597
+ if (!stat11 || stat11.size <= 0)
67083
67598
  return [];
67084
- const length = Math.min(stat10.size, GROK_BILLING_LOG_MAX_BYTES);
67085
- const start = Math.max(0, stat10.size - length);
67599
+ const length = Math.min(stat11.size, GROK_BILLING_LOG_MAX_BYTES);
67600
+ const start = Math.max(0, stat11.size - length);
67086
67601
  let content = "";
67087
67602
  let handle;
67088
67603
  try {
@@ -67609,11 +68124,13 @@ function persistQuotaSnapshots(snapshots) {
67609
68124
  return;
67610
68125
  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
68126
  };
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);
68127
+ writer.transaction(() => {
68128
+ for (const snapshot of snapshots) {
68129
+ writeSnapshot(quotaSnapshotId(snapshot), snapshot, snapshot.metadata);
68130
+ writeSnapshot(quotaHistorySnapshotId(snapshot), snapshot, quotaHistoryMetadata(snapshot));
68131
+ }
68132
+ pruneHistory.run(createdAt - QUOTA_HISTORY_LOOKBACK_MS - QUOTA_HISTORY_BUCKET_MS2);
68133
+ })();
67617
68134
  } catch {}
67618
68135
  }
67619
68136
  function quotaSnapshotMayReplace(existing, incoming) {
@@ -67718,7 +68235,7 @@ function safeStat(path2) {
67718
68235
  return null;
67719
68236
  }
67720
68237
  }
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;
68238
+ 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
68239
  var init_service_budgets = __esm(() => {
67723
68240
  init_src2();
67724
68241
  init_src();
@@ -67728,6 +68245,7 @@ var init_service_budgets = __esm(() => {
67728
68245
  init_db2();
67729
68246
  CACHE_TTL_MS = 60 * 1000;
67730
68247
  WEEK_MS = 7 * 24 * 3600 * 1000;
68248
+ CODEX_JSONL_TAIL_MAX_BYTES = 2 * 1024 * 1024;
67731
68249
  LOCAL_QUOTA_FRESH_MS = 2 * 60 * 1000;
67732
68250
  REMOTE_QUOTA_FRESH_MS = 5 * 60 * 1000;
67733
68251
  QUOTA_HISTORY_BUCKET_MS2 = 60 * 60 * 1000;
@@ -69159,9 +69677,9 @@ function readFilePreview(input) {
69159
69677
  }
69160
69678
  const { realPath, root } = resolved;
69161
69679
  try {
69162
- const stat11 = statSync22(realPath);
69680
+ const stat12 = statSync22(realPath);
69163
69681
  const title = realPath === "/" ? "/" : realPath.split("/").pop() || realPath;
69164
- if (stat11.isDirectory()) {
69682
+ if (stat12.isDirectory()) {
69165
69683
  return {
69166
69684
  ok: true,
69167
69685
  content: {
@@ -69177,7 +69695,7 @@ function readFilePreview(input) {
69177
69695
  }
69178
69696
  };
69179
69697
  }
69180
- if (!stat11.isFile()) {
69698
+ if (!stat12.isFile()) {
69181
69699
  return { ok: false, status: 415, error: "path is not a file or directory" };
69182
69700
  }
69183
69701
  const mediaType = mediaTypeFor(realPath);
@@ -69194,14 +69712,14 @@ function readFilePreview(input) {
69194
69712
  title,
69195
69713
  mediaType,
69196
69714
  rawUrl,
69197
- sizeBytes: stat11.size,
69715
+ sizeBytes: stat12.size,
69198
69716
  previewReason: previewReasonFor(mediaType),
69199
69717
  generatedAt: Date.now()
69200
69718
  }
69201
69719
  };
69202
69720
  }
69203
- const truncated = stat11.size > MAX_PREVIEW_BYTES;
69204
- const readable = readPreviewBuffer(realPath, Math.min(stat11.size, MAX_PREVIEW_BYTES));
69721
+ const truncated = stat12.size > MAX_PREVIEW_BYTES;
69722
+ const readable = readPreviewBuffer(realPath, Math.min(stat12.size, MAX_PREVIEW_BYTES));
69205
69723
  if (!looksTextual2(realPath, readable)) {
69206
69724
  return {
69207
69725
  ok: true,
@@ -69214,7 +69732,7 @@ function readFilePreview(input) {
69214
69732
  title,
69215
69733
  mediaType,
69216
69734
  rawUrl,
69217
- sizeBytes: stat11.size,
69735
+ sizeBytes: stat12.size,
69218
69736
  previewReason: previewReasonFor(mediaType),
69219
69737
  generatedAt: Date.now()
69220
69738
  }
@@ -69232,7 +69750,7 @@ function readFilePreview(input) {
69232
69750
  mediaType,
69233
69751
  rawUrl,
69234
69752
  content: readable.toString("utf8"),
69235
- sizeBytes: stat11.size,
69753
+ sizeBytes: stat12.size,
69236
69754
  truncated,
69237
69755
  generatedAt: Date.now()
69238
69756
  }
@@ -71432,7 +71950,7 @@ __export(exports_agent_definitions, {
71432
71950
  resolveAgentProjectRoot: () => resolveAgentProjectRoot,
71433
71951
  buildAgentDefinitions: () => buildAgentDefinitions
71434
71952
  });
71435
- import { access as access3, readdir as readdir5, readFile as readFile19 } from "fs/promises";
71953
+ import { access as access3, readdir as readdir6, readFile as readFile19 } from "fs/promises";
71436
71954
  import { join as join71 } from "path";
71437
71955
  function excerptFromText2(raw2) {
71438
71956
  const trimmed = raw2.trim();
@@ -71476,7 +71994,7 @@ function agentSlugFromHandle(handle, name2) {
71476
71994
  return fromName || null;
71477
71995
  }
71478
71996
  async function listFolderFiles(folderPath, slug) {
71479
- const entries2 = await readdir5(folderPath, { withFileTypes: true });
71997
+ const entries2 = await readdir6(folderPath, { withFileTypes: true });
71480
71998
  const files = [];
71481
71999
  for (const entry of entries2) {
71482
72000
  if (!entry.isFile())
@@ -71516,7 +72034,7 @@ async function buildAgentDefinitions(input) {
71516
72034
  const selectedSlug = agentSlugFromHandle(input.agentHandle, input.agentName ?? "");
71517
72035
  const folders = [];
71518
72036
  if (agentsRootExists) {
71519
- const entries2 = await readdir5(agentsRoot, { withFileTypes: true });
72037
+ const entries2 = await readdir6(agentsRoot, { withFileTypes: true });
71520
72038
  for (const entry of entries2) {
71521
72039
  if (!entry.isDirectory() || SKIPPED_NAMES.has(entry.name) || entry.name.startsWith(".")) {
71522
72040
  continue;
@@ -73133,8 +73651,55 @@ function assertIsolatedPairingRunStateWrite(path) {
73133
73651
  }
73134
73652
 
73135
73653
  // packages/web/server/pairing-pair-requests.ts
73654
+ var SCOUT_PAIR_CLIENT_HEADER = "x-scout-client";
73655
+ var SCOUT_PAIR_RELAY_HEADER = "x-scout-pair-relay";
73656
+
73657
+ class PairRequestCapacityError extends Error {
73658
+ retryAfterMs;
73659
+ constructor(retryAfterMs) {
73660
+ super("Too many live pairing requests");
73661
+ this.name = "PairRequestCapacityError";
73662
+ this.retryAfterMs = retryAfterMs;
73663
+ }
73664
+ }
73665
+ var PAIR_CLIENT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
73666
+ var PAIR_CLIENT_LABELS = {
73667
+ "scout-ios": "Scout iOS",
73668
+ "scout-macos": "Scout macOS",
73669
+ "talkie-ios": "Talkie iOS",
73670
+ "talkie-macos": "Talkie macOS"
73671
+ };
73672
+ function parseScoutPairClient(headerValue, userAgent) {
73673
+ const raw2 = headerValue?.trim().toLowerCase() ?? "";
73674
+ if (PAIR_CLIENT_ID_PATTERN.test(raw2))
73675
+ return raw2;
73676
+ const ua = userAgent ?? "";
73677
+ if (/Scout/i.test(ua) && /iPhone|iPad|iOS/i.test(ua))
73678
+ return "scout-ios";
73679
+ if (/Talkie/i.test(ua) && /iPhone|iPad|iOS/i.test(ua))
73680
+ return "talkie-ios";
73681
+ return null;
73682
+ }
73683
+ function formatScoutPairClient(app) {
73684
+ const id = app?.trim() || null;
73685
+ if (!id)
73686
+ return null;
73687
+ return PAIR_CLIENT_LABELS[id] ?? id;
73688
+ }
73689
+ function pairRequesterDisplay(req) {
73690
+ const label = req.requesterLabel?.trim() || null;
73691
+ const app = formatScoutPairClient(req.requesterApp);
73692
+ if (label && app)
73693
+ return `${label} \xB7 ${app}`;
73694
+ return label || app || "A device";
73695
+ }
73136
73696
  var DEFAULT_TTL_MS = 2 * 60 * 1000;
73697
+ var DEFAULT_MAX_LIVE_REQUESTS = 32;
73137
73698
  var SWEEP_INTERVAL_MS = 30 * 1000;
73699
+ function boundedMetadata(value, maxLength) {
73700
+ const trimmed = value?.trim() || null;
73701
+ return trimmed ? trimmed.slice(0, maxLength) : null;
73702
+ }
73138
73703
  var MAX_PUBLISH_ATTEMPTS = 32;
73139
73704
  var PUBLISH_SPIN_ATTEMPTS = 8;
73140
73705
  var PUBLISH_BACKOFF_CEILING_MS = 8;
@@ -73187,7 +73752,7 @@ function isPairRequest(value) {
73187
73752
  if (!value || typeof value !== "object")
73188
73753
  return false;
73189
73754
  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";
73755
+ 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
73756
  }
73192
73757
  function fileSignature(path) {
73193
73758
  try {
@@ -73209,7 +73774,11 @@ function parseTerminalMarkers(value) {
73209
73774
  continue;
73210
73775
  if (typeof marker.collectAfter !== "number" || !Number.isFinite(marker.collectAfter))
73211
73776
  continue;
73212
- markers.push({ token: marker.token, collectAfter: marker.collectAfter });
73777
+ markers.push({
73778
+ token: marker.token,
73779
+ collectAfter: marker.collectAfter,
73780
+ ...typeof marker.claimId === "string" && marker.claimId.length > 0 ? { claimId: marker.claimId } : {}
73781
+ });
73213
73782
  }
73214
73783
  return markers;
73215
73784
  }
@@ -73231,6 +73800,8 @@ function createPendingPairRequestStore(options = {}) {
73231
73800
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
73232
73801
  const now = options.now ?? (() => Date.now());
73233
73802
  const statePath = options.statePath;
73803
+ const maxLiveRequests = Math.max(1, Math.floor(options.maxLiveRequests ?? DEFAULT_MAX_LIVE_REQUESTS));
73804
+ const renewalWindowMs = Math.max(0, options.renewalWindowMs ?? Math.floor(ttlMs / 2));
73234
73805
  const byToken = new Map;
73235
73806
  let loadedGeneration = null;
73236
73807
  let loadedSignature = null;
@@ -73239,6 +73810,7 @@ function createPendingPairRequestStore(options = {}) {
73239
73810
  const unpersistedUpserts = new Set;
73240
73811
  const unpersistedExpiries = new Map;
73241
73812
  const fulfilledMarkers = new Map;
73813
+ const fulfilledClaims = new Map;
73242
73814
  const unpublishedFulfilled = new Set;
73243
73815
  const lastSeenInShared = new Map;
73244
73816
  const tempPath = statePath ? `${statePath}.${process.pid}.${crypto.randomUUID().slice(0, 8)}.tmp` : null;
@@ -73256,6 +73828,17 @@ function createPendingPairRequestStore(options = {}) {
73256
73828
  function latestGeneration() {
73257
73829
  return statePath ? latestPairRequestGeneration(statePath) : null;
73258
73830
  }
73831
+ function generationHasTerminalClaim(generation, claim) {
73832
+ const raw2 = readFileOrNull(generationPath(generation));
73833
+ if (raw2 === null)
73834
+ return false;
73835
+ try {
73836
+ const parsed = JSON.parse(raw2);
73837
+ return parseTerminalMarkers(parsed.fulfilled).some((marker) => marker.token === claim.token && marker.claimId === claim.claimId);
73838
+ } catch {
73839
+ return false;
73840
+ }
73841
+ }
73259
73842
  function movedOnSince(sighting) {
73260
73843
  if (loadedEpoch !== sighting.epoch)
73261
73844
  return false;
@@ -73267,10 +73850,11 @@ function createPendingPairRequestStore(options = {}) {
73267
73850
  function terminalMarkerHorizon(expiresAt) {
73268
73851
  return Math.max(expiresAt, now()) + ttlMs;
73269
73852
  }
73270
- function markFulfilled(request) {
73853
+ function markFulfilled(request, claimId) {
73271
73854
  const horizon = terminalMarkerHorizon(request.expiresAt);
73272
73855
  const known = fulfilledMarkers.get(request.token);
73273
73856
  fulfilledMarkers.set(request.token, known === undefined ? horizon : Math.max(known, horizon));
73857
+ fulfilledClaims.set(request.token, claimId);
73274
73858
  if (statePath)
73275
73859
  unpublishedFulfilled.add(request.token);
73276
73860
  }
@@ -73279,8 +73863,10 @@ function createPendingPairRequestStore(options = {}) {
73279
73863
  for (const [token, collectAfter] of fulfilledMarkers) {
73280
73864
  if (unpublishedFulfilled.has(token))
73281
73865
  continue;
73282
- if (collectAfter < t)
73866
+ if (collectAfter < t) {
73283
73867
  fulfilledMarkers.delete(token);
73868
+ fulfilledClaims.delete(token);
73869
+ }
73284
73870
  }
73285
73871
  }
73286
73872
  function mergeUnpersisted(loaded) {
@@ -73340,6 +73926,8 @@ function createPendingPairRequestStore(options = {}) {
73340
73926
  for (const marker of markers) {
73341
73927
  const known = fulfilledMarkers.get(marker.token);
73342
73928
  fulfilledMarkers.set(marker.token, known === undefined ? marker.collectAfter : Math.max(known, marker.collectAfter));
73929
+ if (marker.claimId !== undefined)
73930
+ fulfilledClaims.set(marker.token, marker.claimId);
73343
73931
  }
73344
73932
  for (const [token, row] of loaded) {
73345
73933
  const known = fulfilledMarkers.get(token);
@@ -73406,7 +73994,10 @@ function createPendingPairRequestStore(options = {}) {
73406
73994
  }
73407
73995
  loadedEpoch = epoch;
73408
73996
  loadedHasTerminalLedger = hasLedger;
73409
- adopt(rows.filter(isPairRequest), markers);
73997
+ adopt(rows.filter(isPairRequest).map((row) => ({
73998
+ ...row,
73999
+ requesterApp: row.requesterApp ?? null
74000
+ })), markers);
73410
74001
  return;
73411
74002
  }
73412
74003
  }
@@ -73417,7 +74008,7 @@ function createPendingPairRequestStore(options = {}) {
73417
74008
  rmSync5(tempPath, { force: true });
73418
74009
  } catch {}
73419
74010
  }
73420
- function publish(touched, expired) {
74011
+ function publish(touched, expired, terminalClaim) {
73421
74012
  if (!statePath || !tempPath)
73422
74013
  return "won";
73423
74014
  if (touched.length === 0 && expired.length === 0 && !hasOutstandingWrites())
@@ -73433,7 +74024,11 @@ function createPendingPairRequestStore(options = {}) {
73433
74024
  version: 1,
73434
74025
  epoch,
73435
74026
  requests: [...byToken.values()],
73436
- fulfilled: [...fulfilledMarkers].map(([token, collectAfter]) => ({ token, collectAfter }))
74027
+ fulfilled: [...fulfilledMarkers].map(([token, collectAfter]) => ({
74028
+ token,
74029
+ collectAfter,
74030
+ ...fulfilledClaims.has(token) ? { claimId: fulfilledClaims.get(token) } : {}
74031
+ }))
73437
74032
  });
73438
74033
  try {
73439
74034
  mkdirSync11(dirname26(statePath), { recursive: true, mode: 448 });
@@ -73454,7 +74049,9 @@ function createPendingPairRequestStore(options = {}) {
73454
74049
  discardTemp();
73455
74050
  if (!landed || signature === null)
73456
74051
  return "lost";
73457
- if (latestGeneration() !== next) {
74052
+ const frontier = latestGeneration();
74053
+ const inheritedClaim = terminalClaim !== undefined && frontier !== null && frontier > next && generationHasTerminalClaim(frontier, terminalClaim);
74054
+ if (frontier !== next && !inheritedClaim) {
73458
74055
  try {
73459
74056
  if (inodeOf(target2) === ours)
73460
74057
  unlinkSync2(target2);
@@ -73472,7 +74069,7 @@ function createPendingPairRequestStore(options = {}) {
73472
74069
  for (const token of byToken.keys())
73473
74070
  lastSeenInShared.set(token, sighting);
73474
74071
  forgetSightingsWeNoLongerHold();
73475
- collectSuperseded(next);
74072
+ collectSuperseded(frontier ?? next);
73476
74073
  return "won";
73477
74074
  }
73478
74075
  function collectSuperseded(published) {
@@ -73514,7 +74111,7 @@ function createPendingPairRequestStore(options = {}) {
73514
74111
  function hasOutstandingWrites() {
73515
74112
  return unpersistedUpserts.size > 0 || unpersistedExpiries.size > 0 || unpublishedFulfilled.size > 0;
73516
74113
  }
73517
- function commit(apply) {
74114
+ function commit(apply, options2 = {}) {
73518
74115
  if (!statePath) {
73519
74116
  pruneExpired();
73520
74117
  collectTerminalMarkers();
@@ -73525,10 +74122,17 @@ function createPendingPairRequestStore(options = {}) {
73525
74122
  const expired = pruneExpired();
73526
74123
  collectTerminalMarkers();
73527
74124
  const { value, touched } = apply();
73528
- const outcome = publish(touched, expired);
74125
+ const outcome = publish(touched, expired, options2.terminalClaim);
73529
74126
  if (outcome === "won")
73530
74127
  return value;
74128
+ if (outcome === "lost")
74129
+ options2.rollback?.();
73531
74130
  if (outcome === "failed" || attempt >= MAX_PUBLISH_ATTEMPTS) {
74131
+ if (options2.failClosed) {
74132
+ if (outcome === "failed")
74133
+ options2.rollback?.();
74134
+ return options2.failClosed();
74135
+ }
73532
74136
  recordOutstanding(touched, expired);
73533
74137
  return value;
73534
74138
  }
@@ -73556,7 +74160,7 @@ function createPendingPairRequestStore(options = {}) {
73556
74160
  return null;
73557
74161
  const t = now();
73558
74162
  for (const req of byToken.values()) {
73559
- if (req.requesterIp === requesterIp && req.expiresAt > t && (req.status === "pending" || req.status === "approved")) {
74163
+ if (req.requesterIp === requesterIp && req.expiresAt > t) {
73560
74164
  return req;
73561
74165
  }
73562
74166
  }
@@ -73564,19 +74168,38 @@ function createPendingPairRequestStore(options = {}) {
73564
74168
  }
73565
74169
  return {
73566
74170
  create(input) {
73567
- const requesterIp = input.requesterIp?.trim() || null;
73568
- const label = input.requesterLabel?.trim() || null;
74171
+ const requesterIp = boundedMetadata(input.requesterIp, 64);
74172
+ const label = boundedMetadata(input.requesterLabel, 96);
74173
+ const app = boundedMetadata(input.requesterApp, 32);
74174
+ const route = boundedMetadata(input.route, 16);
73569
74175
  const token = crypto.randomUUID();
73570
- return commit(() => {
74176
+ const result = commit(() => {
73571
74177
  const existing = findReusable(requesterIp);
73572
74178
  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] };
74179
+ const t2 = now();
74180
+ const renew = existing.status !== "denied" && existing.expiresAt - t2 <= renewalWindowMs;
74181
+ if (renew) {
74182
+ existing.updatedAt = t2;
74183
+ existing.expiresAt = t2 + ttlMs;
74184
+ if (route)
74185
+ existing.route = route;
74186
+ if (label)
74187
+ existing.requesterLabel = label;
74188
+ if (app)
74189
+ existing.requesterApp = app;
74190
+ }
74191
+ return { value: existing, touched: renew ? [existing.token] : [] };
74192
+ }
74193
+ if (byToken.size >= maxLiveRequests) {
74194
+ const t2 = now();
74195
+ const earliestExpiry = Math.min(...[...byToken.values()].map((request2) => request2.expiresAt));
74196
+ return {
74197
+ value: {
74198
+ capacity: true,
74199
+ retryAfterMs: Math.max(1000, earliestExpiry - t2)
74200
+ },
74201
+ touched: []
74202
+ };
73580
74203
  }
73581
74204
  const t = now();
73582
74205
  const request = {
@@ -73584,7 +74207,8 @@ function createPendingPairRequestStore(options = {}) {
73584
74207
  status: "pending",
73585
74208
  requesterIp,
73586
74209
  requesterLabel: label,
73587
- route: input.route ?? null,
74210
+ requesterApp: app,
74211
+ route,
73588
74212
  createdAt: t,
73589
74213
  updatedAt: t,
73590
74214
  expiresAt: t + ttlMs
@@ -73592,6 +74216,10 @@ function createPendingPairRequestStore(options = {}) {
73592
74216
  byToken.set(token, request);
73593
74217
  return { value: request, touched: [token] };
73594
74218
  });
74219
+ if ("capacity" in result) {
74220
+ throw new PairRequestCapacityError(result.retryAfterMs);
74221
+ }
74222
+ return result;
73595
74223
  },
73596
74224
  get(token) {
73597
74225
  reload();
@@ -73601,8 +74229,9 @@ function createPendingPairRequestStore(options = {}) {
73601
74229
  touch(token) {
73602
74230
  commit(() => {
73603
74231
  const req = byToken.get(token);
73604
- const extended = now() + ttlMs;
73605
- const extend = req !== undefined && (req.status === "pending" || req.status === "approved") && extended > req.expiresAt;
74232
+ const t = now();
74233
+ const extended = t + ttlMs;
74234
+ const extend = req !== undefined && (req.status === "pending" || req.status === "approved") && req.expiresAt - t <= renewalWindowMs && extended > req.expiresAt;
73606
74235
  if (extend && req)
73607
74236
  req.expiresAt = extended;
73608
74237
  return { value: undefined, touched: extend ? [token] : [] };
@@ -73619,24 +74248,55 @@ function createPendingPairRequestStore(options = {}) {
73619
74248
  return { value: null, touched: [] };
73620
74249
  req.status = decision === "approve" ? "approved" : "denied";
73621
74250
  req.updatedAt = now();
73622
- if (decision === "approve")
73623
- req.expiresAt = now() + ttlMs;
74251
+ req.expiresAt = now() + ttlMs;
73624
74252
  return { value: req, touched: [token] };
73625
74253
  });
73626
74254
  },
73627
- fulfill(token) {
73628
- let delivered = null;
74255
+ fulfill(token, requiredStatus) {
74256
+ let proposal = null;
74257
+ const claim = { token, claimId: crypto.randomUUID() };
74258
+ let previousMarker;
74259
+ let previousClaim;
74260
+ let previouslyUnpublished = false;
74261
+ let previouslyUnpersisted = false;
74262
+ const rollback = () => {
74263
+ if (proposal === null)
74264
+ return;
74265
+ if (previousMarker === undefined)
74266
+ fulfilledMarkers.delete(token);
74267
+ else
74268
+ fulfilledMarkers.set(token, previousMarker);
74269
+ if (previousClaim === undefined)
74270
+ fulfilledClaims.delete(token);
74271
+ else
74272
+ fulfilledClaims.set(token, previousClaim);
74273
+ if (!previouslyUnpublished)
74274
+ unpublishedFulfilled.delete(token);
74275
+ byToken.set(token, proposal);
74276
+ if (previouslyUnpersisted)
74277
+ unpersistedUpserts.add(token);
74278
+ else
74279
+ unpersistedUpserts.delete(token);
74280
+ proposal = null;
74281
+ };
73629
74282
  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 };
74283
+ const request = byToken.get(token);
74284
+ if (!request || !isLive(request) || requiredStatus !== undefined && request.status !== requiredStatus) {
74285
+ return { value: null, touched: [] };
73635
74286
  }
73636
- markFulfilled(delivered);
74287
+ proposal = { ...request };
74288
+ previousMarker = fulfilledMarkers.get(token);
74289
+ previousClaim = fulfilledClaims.get(token);
74290
+ previouslyUnpublished = unpublishedFulfilled.has(token);
74291
+ previouslyUnpersisted = unpersistedUpserts.has(token);
74292
+ markFulfilled(proposal, claim.claimId);
73637
74293
  byToken.delete(token);
73638
74294
  unpersistedUpserts.delete(token);
73639
- return { value: delivered, touched: [] };
74295
+ return { value: proposal, touched: [] };
74296
+ }, {
74297
+ rollback,
74298
+ terminalClaim: claim,
74299
+ failClosed: () => null
73640
74300
  });
73641
74301
  },
73642
74302
  dispose() {
@@ -73647,6 +74307,7 @@ function createPendingPairRequestStore(options = {}) {
73647
74307
  unpersistedExpiries.clear();
73648
74308
  unpublishedFulfilled.clear();
73649
74309
  fulfilledMarkers.clear();
74310
+ fulfilledClaims.clear();
73650
74311
  lastSeenInShared.clear();
73651
74312
  }
73652
74313
  };
@@ -73887,7 +74548,8 @@ init_agent_endpoints();
73887
74548
 
73888
74549
  // packages/web/server/core/repo-identity.ts
73889
74550
  init_system_probes();
73890
- var REPO_KEY_TTL_MS = 60000;
74551
+ var REPO_KEY_TTL_MS = 10 * 60000;
74552
+ var REPO_KEY_PROBE_CONCURRENCY = 4;
73891
74553
  function normalizeGitRemoteUrl(remote) {
73892
74554
  const trimmed = remote?.trim() ?? "";
73893
74555
  if (!trimmed)
@@ -73911,15 +74573,14 @@ function canonicalRepoKey(host, path2) {
73911
74573
  }
73912
74574
  var repoKeyCache = new Map;
73913
74575
  var repoKeyInFlight = new Map;
74576
+ var queuedRepoKeyRoots = [];
74577
+ var queuedRepoKeyRootSet = new Set;
74578
+ var activeRepoKeyProbes = 0;
73914
74579
  async function probeRepoKey(root) {
73915
74580
  const remote = await gitRemoteGetUrlOrigin(root).catch(() => null);
73916
74581
  return normalizeGitRemoteUrl(remote);
73917
74582
  }
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
- }
74583
+ function refreshRepoKeyForRoot(root) {
73923
74584
  const inFlight = repoKeyInFlight.get(root);
73924
74585
  if (inFlight)
73925
74586
  return inFlight;
@@ -73933,10 +74594,38 @@ function resolveRepoKeyForRoot(root) {
73933
74594
  repoKeyInFlight.set(root, promise);
73934
74595
  return promise;
73935
74596
  }
73936
- async function resolveRepoKeysByRoot(roots) {
74597
+ function drainRepoKeyProbeQueue() {
74598
+ while (activeRepoKeyProbes < REPO_KEY_PROBE_CONCURRENCY) {
74599
+ const root = queuedRepoKeyRoots.shift();
74600
+ if (!root)
74601
+ return;
74602
+ queuedRepoKeyRootSet.delete(root);
74603
+ activeRepoKeyProbes += 1;
74604
+ refreshRepoKeyForRoot(root).catch(() => null).finally(() => {
74605
+ activeRepoKeyProbes -= 1;
74606
+ drainRepoKeyProbeQueue();
74607
+ });
74608
+ }
74609
+ }
74610
+ function scheduleRepoKeyProbe(root) {
74611
+ if (repoKeyInFlight.has(root) || queuedRepoKeyRootSet.has(root))
74612
+ return;
74613
+ queuedRepoKeyRootSet.add(root);
74614
+ queuedRepoKeyRoots.push(root);
74615
+ drainRepoKeyProbeQueue();
74616
+ }
74617
+ function cachedRepoKeysByRoot(roots) {
74618
+ const now = Date.now();
73937
74619
  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]));
74620
+ const result = new Map;
74621
+ for (const root of distinct) {
74622
+ const cached = repoKeyCache.get(root);
74623
+ result.set(root, cached?.key ?? null);
74624
+ if (!cached || now - cached.at >= REPO_KEY_TTL_MS) {
74625
+ scheduleRepoKeyProbe(root);
74626
+ }
74627
+ }
74628
+ return result;
73940
74629
  }
73941
74630
 
73942
74631
  // packages/web/server/terminal-session-discovery.ts
@@ -75353,6 +76042,50 @@ init_src();
75353
76042
  init_legacy_ids();
75354
76043
  init_service();
75355
76044
  init_message_pagination();
76045
+ function provisionalScoutConversations(snapshot, filters = {}) {
76046
+ const allowedKinds = new Set(filters.kinds ?? DEFAULT_CONVERSATION_KINDS);
76047
+ const query = normalizeQuery(filters.query);
76048
+ const limit = Math.max(1, Math.floor(filters.limit ?? snapshot.items.length));
76049
+ return snapshot.items.filter((item) => item.entityKind === "scout_conversation" && item.visibilityState === "visible" && Boolean(item.conversationId) && allowedKinds.has(item.kind)).filter((item) => !query || [
76050
+ item.title,
76051
+ item.alias,
76052
+ item.agentName,
76053
+ item.preview,
76054
+ item.projectRoot
76055
+ ].some((value) => value?.toLocaleLowerCase().includes(query))).slice(0, limit).map((item) => {
76056
+ const conversationId = item.conversationId;
76057
+ const participantIds = item.kind === "direct" && item.agentId ? ["operator", item.agentId] : [];
76058
+ return {
76059
+ id: conversationId,
76060
+ chatId: conversationId,
76061
+ equivalentConversationIds: [conversationId],
76062
+ kind: item.kind,
76063
+ title: item.title ?? item.agentName ?? item.alias ?? conversationId,
76064
+ alias: item.alias,
76065
+ naturalKey: item.naturalKey,
76066
+ participantIds,
76067
+ participants: [],
76068
+ participantCount: item.participantCount,
76069
+ authorityNodeId: item.authorityNodeId,
76070
+ authorityNodeName: item.authorityNodeName,
76071
+ agentId: item.agentId,
76072
+ agentName: item.agentName,
76073
+ harness: item.harness,
76074
+ model: item.model,
76075
+ reasoningEffort: item.effort,
76076
+ transport: null,
76077
+ sessionId: item.runtimeSessionId,
76078
+ currentBranch: item.currentBranch,
76079
+ parentConversationId: item.parentConversationId,
76080
+ anchorMessageId: item.anchorMessageId,
76081
+ preview: item.preview,
76082
+ messageCount: item.messageCount,
76083
+ lastMessageAt: item.lastMessageAt,
76084
+ workspaceRoot: item.projectRoot,
76085
+ unreadCount: item.unreadCount
76086
+ };
76087
+ });
76088
+ }
75356
76089
  var DEFAULT_CONVERSATION_KINDS = [
75357
76090
  "direct",
75358
76091
  "channel",
@@ -75421,6 +76154,53 @@ function metadataObject2(metadata, key) {
75421
76154
  function metadataSessionId(metadata) {
75422
76155
  return metadataString9(metadata, "targetSessionId") ?? metadataString9(metadata, "responderSessionId") ?? metadataString9(metadata, "sessionId") ?? metadataString9(metadata, "externalSessionId") ?? metadataString9(metadata, "threadId") ?? metadataString9(metadataObject2(metadata, "returnAddress"), "sessionId");
75423
76156
  }
76157
+ function executionResolutionDimension2(resolution, dimension) {
76158
+ const value = resolution?.[dimension];
76159
+ return value?.observed?.trim() || value?.resolved?.trim() || value?.requested?.trim() || null;
76160
+ }
76161
+ function executionResolutionFromUnknown(value) {
76162
+ if (!value || typeof value !== "object" || Array.isArray(value))
76163
+ return null;
76164
+ const resolution = value;
76165
+ return resolution.schemaVersion === "openscout.execution-resolution.v1" ? resolution : null;
76166
+ }
76167
+ function flightSessionRuntime(flights, sessionId) {
76168
+ for (const flight of [...flights].reverse()) {
76169
+ const rawTrace = flight.metadata?.sessionTrace;
76170
+ if (!Array.isArray(rawTrace))
76171
+ continue;
76172
+ const entries2 = [...rawTrace].reverse();
76173
+ const candidate = entries2.find((entry) => {
76174
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
76175
+ return false;
76176
+ const id = metadataString9(entry, "sessionId");
76177
+ return sessionId ? id === sessionId : Boolean(id);
76178
+ });
76179
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
76180
+ continue;
76181
+ const record2 = candidate;
76182
+ return {
76183
+ resolution: executionResolutionFromUnknown(record2.executionResolution),
76184
+ harness: metadataString9(record2, "harness"),
76185
+ transport: metadataString9(record2, "transport")
76186
+ };
76187
+ }
76188
+ return null;
76189
+ }
76190
+ function latestConversationRuntime(input) {
76191
+ const matchingParticipant = input.sessionId ? input.participants.find((participant) => participant.sessionId === input.sessionId) ?? null : null;
76192
+ 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;
76193
+ const latestInvocation = input.invocations.at(-1) ?? null;
76194
+ const invocation = matchingInvocation ?? latestInvocation;
76195
+ const trace = flightSessionRuntime(input.flights, input.sessionId);
76196
+ const resolution = matchingInvocation?.executionResolution ?? trace?.resolution ?? invocation?.executionResolution ?? null;
76197
+ return {
76198
+ harness: executionResolutionDimension2(resolution, "harness") ?? trace?.harness ?? matchingParticipant?.harness ?? invocation?.execution?.harness?.trim() ?? input.endpoint?.harness ?? null,
76199
+ model: executionResolutionDimension2(resolution, "model") ?? matchingParticipant?.model ?? invocation?.execution?.model?.trim() ?? metadataString9(input.endpoint?.metadata, "observedModel") ?? metadataString9(input.endpoint?.metadata, "model"),
76200
+ reasoningEffort: executionResolutionDimension2(resolution, "reasoningEffort") ?? matchingParticipant?.reasoningEffort ?? invocation?.execution?.reasoningEffort?.trim() ?? metadataString9(input.endpoint?.metadata, "observedReasoningEffort") ?? metadataString9(input.endpoint?.metadata, "reasoningEffort"),
76201
+ transport: trace?.transport ?? matchingParticipant?.transport ?? input.endpoint?.transport ?? null
76202
+ };
76203
+ }
75424
76204
  function formatChannelAlias2(value) {
75425
76205
  const trimmed = value.trim();
75426
76206
  if (!trimmed)
@@ -75751,6 +76531,8 @@ function buildScopedParticipants(snapshot, endpoints, operatorIds, conversationI
75751
76531
  agentId: agent?.id ?? null,
75752
76532
  sessionId: endpoint?.sessionId ?? metadataSessionId(endpoint?.metadata) ?? metadataSessionId(actor?.metadata) ?? (kind === "session" ? participantId : null),
75753
76533
  harness: endpoint?.harness ?? metadataString9(actor?.metadata, "harness"),
76534
+ model: metadataString9(endpoint?.metadata, "observedModel") ?? metadataString9(endpoint?.metadata, "model") ?? metadataString9(actor?.metadata, "model"),
76535
+ reasoningEffort: metadataString9(endpoint?.metadata, "observedReasoningEffort") ?? metadataString9(endpoint?.metadata, "reasoningEffort") ?? metadataString9(actor?.metadata, "reasoningEffort"),
75754
76536
  transport: endpoint?.transport ?? metadataString9(actor?.metadata, "transport"),
75755
76537
  workspaceRoot: endpoint?.projectRoot ?? endpoint?.cwd ?? metadataString9(actor?.metadata, "projectRoot")
75756
76538
  };
@@ -75772,6 +76554,9 @@ function includeConversation(summary, query) {
75772
76554
  summary.title,
75773
76555
  summary.agentId ?? "",
75774
76556
  summary.agentName ?? "",
76557
+ summary.harness ?? "",
76558
+ summary.model ?? "",
76559
+ summary.reasoningEffort ?? "",
75775
76560
  summary.preview ?? "",
75776
76561
  summary.workspaceRoot ?? "",
75777
76562
  ...summary.participantIds,
@@ -75839,6 +76624,10 @@ function coalesceDuplicateNamedChannels(summaries, snapshot, endpoints, operator
75839
76624
  preview: latest.preview,
75840
76625
  lastMessageAt: latest.lastMessageAt,
75841
76626
  sessionId: latest.sessionId ?? canonical2.sessionId,
76627
+ harness: latest.harness ?? canonical2.harness,
76628
+ model: latest.model ?? canonical2.model,
76629
+ reasoningEffort: latest.reasoningEffort ?? canonical2.reasoningEffort,
76630
+ transport: latest.transport ?? canonical2.transport,
75842
76631
  messageCount: Math.max(...group.map((summary) => summary.messageCount)),
75843
76632
  unreadCount: Math.max(...group.map((summary) => summary.unreadCount)),
75844
76633
  ...latestTurn ? { turn: latestTurn } : {}
@@ -75910,6 +76699,13 @@ async function getScoutConversations(filters = {}, brokerContext) {
75910
76699
  }
75911
76700
  const title = agentDisplayName(snapshot, endpointsByAgent, agentId);
75912
76701
  const identityFields2 = conversationIdentityFields2(conversation);
76702
+ const runtime2 = latestConversationRuntime({
76703
+ sessionId,
76704
+ participants,
76705
+ invocations: invocations2,
76706
+ flights,
76707
+ endpoint
76708
+ });
75913
76709
  return [{
75914
76710
  id: conversation.id,
75915
76711
  chatId: conversation.id,
@@ -75924,7 +76720,10 @@ async function getScoutConversations(filters = {}, brokerContext) {
75924
76720
  authorityNodeName: snapshot.nodes?.[conversation.authorityNodeId]?.name ?? null,
75925
76721
  agentId,
75926
76722
  agentName: title,
75927
- harness: endpoint?.harness ?? null,
76723
+ harness: runtime2.harness,
76724
+ model: runtime2.model,
76725
+ reasoningEffort: runtime2.reasoningEffort,
76726
+ transport: runtime2.transport,
75928
76727
  sessionId,
75929
76728
  currentBranch: metadataString9(endpoint?.metadata, "branch") ?? metadataString9(endpoint?.metadata, "workspaceQualifier") ?? metadataString9(agent?.metadata, "branch") ?? metadataString9(agent?.metadata, "workspaceQualifier"),
75930
76729
  parentConversationId: conversation.parentConversationId ?? null,
@@ -75951,6 +76750,13 @@ async function getScoutConversations(filters = {}, brokerContext) {
75951
76750
  return [];
75952
76751
  }
75953
76752
  const identityFields = conversationIdentityFields2(conversation);
76753
+ const runtime = latestConversationRuntime({
76754
+ sessionId,
76755
+ participants,
76756
+ invocations: invocations2,
76757
+ flights,
76758
+ endpoint: null
76759
+ });
75954
76760
  return [{
75955
76761
  id: conversation.id,
75956
76762
  chatId: conversation.id,
@@ -75965,7 +76771,10 @@ async function getScoutConversations(filters = {}, brokerContext) {
75965
76771
  authorityNodeName: snapshot.nodes?.[conversation.authorityNodeId]?.name ?? null,
75966
76772
  agentId: null,
75967
76773
  agentName: null,
75968
- harness: null,
76774
+ harness: runtime.harness,
76775
+ model: runtime.model,
76776
+ reasoningEffort: runtime.reasoningEffort,
76777
+ transport: runtime.transport,
75969
76778
  sessionId,
75970
76779
  currentBranch: null,
75971
76780
  parentConversationId: conversation.parentConversationId ?? null,
@@ -75992,15 +76801,17 @@ function capSummaryRoster(summary, operatorIds, endpoints) {
75992
76801
  if (summary.participantIds.length <= CHANNEL_ROSTER_CAP)
75993
76802
  return summary;
75994
76803
  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
- }
76804
+ const fillFromTier = (matches) => {
76805
+ for (const id of summary.participantIds) {
76806
+ if (keep.size >= CHANNEL_ROSTER_CAP)
76807
+ return;
76808
+ if (matches(id))
76809
+ keep.add(id);
76810
+ }
76811
+ };
76812
+ fillFromTier((id) => operatorIds.has(id));
76813
+ fillFromTier((id) => !operatorIds.has(id) && participantEndpoint(endpoints, id) !== null);
76814
+ fillFromTier(() => true);
76004
76815
  const keptIds = summary.participantIds.filter((id) => keep.has(id));
76005
76816
  const droppedIds = summary.participantIds.filter((id) => !keep.has(id));
76006
76817
  return {
@@ -76009,12 +76820,12 @@ function capSummaryRoster(summary, operatorIds, endpoints) {
76009
76820
  participants: summary.participants.filter((participant) => keep.has(participant.actorId))
76010
76821
  };
76011
76822
  }
76012
- async function getScoutConversationMessages(conversationId, limit = 80, beforeMessageId) {
76823
+ async function getScoutConversationMessages(conversationId, limit = 80, beforeMessageId, brokerContext) {
76013
76824
  const normalizedId = conversationId.trim();
76014
76825
  if (!normalizedId || !isOpaqueChannelId(normalizedId)) {
76015
76826
  return [];
76016
76827
  }
76017
- const broker2 = await loadScoutBrokerContext(undefined, { scope: "conversations" });
76828
+ const broker2 = brokerContext === undefined ? await loadScoutBrokerContext(undefined, { scope: "conversations" }) : brokerContext;
76018
76829
  if (!broker2)
76019
76830
  return null;
76020
76831
  const snapshot = broker2.snapshot;
@@ -76085,6 +76896,7 @@ import { basename as basename20, join as join54, resolve as resolve20 } from "pa
76085
76896
  init_secret_redaction();
76086
76897
  import { basename as basename19 } from "path";
76087
76898
  import { open, stat as stat9 } from "fs/promises";
76899
+ import { StringDecoder } from "string_decoder";
76088
76900
 
76089
76901
  // packages/runtime/src/tail/claude-source.ts
76090
76902
  init_user_config();
@@ -78991,8 +79803,10 @@ function readPositiveIntEnv8(name2, fallback) {
78991
79803
  const raw2 = Number.parseInt(process.env[name2] ?? "", 10);
78992
79804
  return Number.isFinite(raw2) && raw2 > 0 ? raw2 : fallback;
78993
79805
  }
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);
79806
+ var DEFAULT_TAIL_POLL_INTERVAL_MS = 1e4;
79807
+ var DEFAULT_HOT_DISCOVERY_INTERVAL_MS = 60000;
79808
+ var TAIL_POLL_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_POLL_INTERVAL_MS", DEFAULT_TAIL_POLL_INTERVAL_MS);
79809
+ var HOT_DISCOVERY_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_HOT_DISCOVERY_INTERVAL_MS", DEFAULT_HOT_DISCOVERY_INTERVAL_MS);
78996
79810
  var DISCOVERY_CACHE_MAX_AGE_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_DISCOVERY_CACHE_MAX_AGE_MS", HOT_DISCOVERY_INTERVAL_MS);
78997
79811
  var SHALLOW_DISCOVERY_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_SHALLOW_DISCOVERY_INTERVAL_MS", 10 * 60000);
78998
79812
  var DEEP_DISCOVERY_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_DEEP_DISCOVERY_INTERVAL_MS", 60 * 60000);
@@ -79006,23 +79820,42 @@ var RAW_MAX_STRING_LEN = 1000;
79006
79820
  var RAW_MAX_ARRAY_ITEMS = 25;
79007
79821
  var RAW_MAX_OBJECT_KEYS = 50;
79008
79822
  var RECENT_TRANSCRIPT_READ_BYTES = 512 * 1024;
79823
+ var WATCHER_READ_BYTES = readPositiveIntEnv8("OPENSCOUT_TAIL_WATCHER_READ_BYTES", 512 * 1024);
79824
+ var WATCHER_DRAIN_BYTES = Math.max(WATCHER_READ_BYTES, readPositiveIntEnv8("OPENSCOUT_TAIL_WATCHER_DRAIN_BYTES", 4 * 1024 * 1024));
79825
+ var WATCHER_PUMP_CONCURRENCY = readPositiveIntEnv8("OPENSCOUT_TAIL_WATCHER_PUMP_CONCURRENCY", 16);
79009
79826
  var SESSION_TRANSCRIPT_READ_BYTES = 8 * 1024 * 1024;
79010
79827
  var RECENT_TRANSCRIPT_KIND_SCAN_READ_BYTES = 32 * 1024 * 1024;
79011
79828
  var RECENT_TRANSCRIPT_LINES_PER_FILE = 200;
79012
79829
  var RECENT_TRANSCRIPT_KIND_SCAN_MAX_LINES = 65536;
79013
79830
  var RECENT_TRANSCRIPT_MAX_FILES = readPositiveIntEnv8("OPENSCOUT_TAIL_RECENT_TRANSCRIPT_MAX_FILES", 24);
79831
+ var INTERNAL_RECONCILE_SESSIONS_PER_SOURCE = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_RECONCILE_SESSIONS_PER_SOURCE", 12);
79832
+ var INTERNAL_RECONCILE_LINES_PER_SESSION = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_RECONCILE_LINES_PER_SESSION", 128);
79833
+ var INTERNAL_RECONCILE_READ_BYTES = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_RECONCILE_READ_BYTES", 256 * 1024);
79834
+ var INTERNAL_HOT_WATCHER_WINDOW_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_HOT_WATCHER_WINDOW_MS", 60000);
79835
+ var INTERNAL_IDLE_WATCHER_INTERVAL_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_INTERVAL_MS", 15000);
79836
+ var INTERNAL_IDLE_WATCHER_BATCH_SIZE = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_IDLE_WATCHER_BATCH_SIZE", 32);
79837
+ var INTERNAL_ACTIVE_STALE_AFTER_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_INTERNAL_ACTIVE_STALE_AFTER_MS", 30 * 60000);
79838
+ var INTERNAL_STALE_CONFIRMATION_COUNT = 2;
79839
+ var AUTHORITATIVE_MISSING_CONFIRMATION_COUNT = 2;
79840
+ var INTERNAL_PERSISTED_OBSERVED_SEED_LIMIT = readPositiveIntEnv8("OPENSCOUT_TAIL_PERSISTED_OBSERVED_SEED_LIMIT", 4096);
79014
79841
  var NATIVE_TAIL_SOURCES = new Set(["grok", "kimi", "opencode", "cursor"]);
79015
79842
  var TRANSCRIPT_REPLAY_MEMO_MAX_ENTRIES = readPositiveIntEnv8("OPENSCOUT_TAIL_REPLAY_MEMO_MAX_ENTRIES", 256);
79016
79843
  var TRANSCRIPT_REPLAY_ACTIVE_GRACE_MS = readPositiveIntEnv8("OPENSCOUT_TAIL_REPLAY_MEMO_ACTIVE_GRACE_MS", 2000);
79017
79844
  var transcriptReplayMemo = new Map;
79018
79845
  var transcriptReplayInFlight = new Map;
79846
+ var INTERNAL_TAIL_SESSION_OFFLINE_SUMMARY = "session lifecycle \xB7 confirmed offline";
79847
+ var INTERNAL_TAIL_SESSION_STALLED_SUMMARY = "session lifecycle \xB7 confirmed stale";
79019
79848
  var sources = [GrokSource, KimiSource, ClaudeSource, CodexSource, CursorSource, OpenCodeSource, PiSource];
79020
79849
  var watchers = new Map;
79021
79850
  var aggregateBuffer = [];
79022
79851
  var assistantBuffer = [];
79023
79852
  var perSessionBuffer = new Map;
79024
79853
  var subscribers = new Set;
79854
+ var internalSubscribers = new Set;
79855
+ var watcherPumpQueue = [];
79856
+ var activeWatcherPumps = 0;
79025
79857
  var knownTranscripts = new Map;
79858
+ var persistedObservedSessions = new Map;
79026
79859
  var quietEventLastSeen = new Map;
79027
79860
  var pollTimer = null;
79028
79861
  var hotDiscoveryTimer = null;
@@ -79219,10 +80052,19 @@ function shouldCoalesceQuietTailEvent(event) {
79219
80052
  pruneQuietTailCoalescer(now);
79220
80053
  return false;
79221
80054
  }
79222
- function pushEvent(rawEvent) {
80055
+ function pushInternalEvent(rawEvent) {
79223
80056
  const event = compactEvent(rawEvent);
80057
+ for (const subscriber of [...internalSubscribers]) {
80058
+ try {
80059
+ subscriber(event);
80060
+ } catch {}
80061
+ }
80062
+ return event;
80063
+ }
80064
+ function pushEvent(rawEvent) {
80065
+ const event = pushInternalEvent(rawEvent);
79224
80066
  if (shouldCoalesceQuietTailEvent(event))
79225
- return;
80067
+ return event;
79226
80068
  aggregateBuffer.push(event);
79227
80069
  if (aggregateBuffer.length > AGGREGATE_BUFFER_LIMIT) {
79228
80070
  aggregateBuffer.splice(0, aggregateBuffer.length - AGGREGATE_BUFFER_LIMIT);
@@ -79247,15 +80089,114 @@ function pushEvent(rawEvent) {
79247
80089
  subscriber(event);
79248
80090
  } catch {}
79249
80091
  }
80092
+ return event;
80093
+ }
80094
+ function rememberWatcherEvent(watcher, rawEvent, delivery) {
80095
+ if (watcher.emittedEventIds.has(rawEvent.id))
80096
+ return;
80097
+ watcher.emittedEventIds.add(rawEvent.id);
80098
+ const event = delivery === "all" ? pushEvent(rawEvent) : pushInternalEvent(rawEvent);
80099
+ watcher.internalObserved = true;
80100
+ watcher.lastInternalEvent = event;
80101
+ watcher.staleObservations = 0;
80102
+ watcher.lifecycleNotification = "none";
80103
+ const lifecycle = persistedObservedSessions.get(observedSessionLifecycleKey(watcher.source.name, event.sessionId));
80104
+ if (lifecycle) {
80105
+ lifecycle.missingAuthoritativePasses = 0;
80106
+ lifecycle.lifecycleNotification = "none";
80107
+ }
80108
+ }
80109
+ function observedSessionLifecycleKey(source, sourceSessionId) {
80110
+ return `${source.trim()}\x00${sourceSessionId.trim()}`;
80111
+ }
80112
+ function persistedSeedEvent(seed) {
80113
+ const source = seed.source.trim();
80114
+ const sourceSessionId = seed.sourceSessionId.trim();
80115
+ const timestamp = Number.isFinite(seed.lastActivityAt) && seed.lastActivityAt > 0 ? seed.lastActivityAt : 0;
80116
+ return {
80117
+ id: `tail-persisted-seed:${source}:${sourceSessionId}:${timestamp}`,
80118
+ ts: timestamp,
80119
+ source,
80120
+ sessionId: sourceSessionId,
80121
+ pid: virtualPidForPath(`${source}:${sourceSessionId}`),
80122
+ parentPid: null,
80123
+ project: seed.project?.trim() ?? "",
80124
+ cwd: seed.cwd?.trim() || seed.projectRoot?.trim() || "",
80125
+ harness: "unattributed",
80126
+ kind: "system",
80127
+ summary: "session lifecycle \xB7 restored persisted active identity",
80128
+ raw: { reason: "persisted_active_seed" }
80129
+ };
80130
+ }
80131
+ function applyPersistedObservedSeedToWatcher(watcher) {
80132
+ if (watcher.internalObserved)
80133
+ return;
80134
+ const sessionId = watcher.transcript.sessionId?.trim();
80135
+ if (!sessionId)
80136
+ return;
80137
+ const lifecycle = persistedObservedSessions.get(observedSessionLifecycleKey(watcher.source.name, sessionId));
80138
+ if (!lifecycle)
80139
+ return;
80140
+ watcher.internalObserved = true;
80141
+ watcher.lastInternalEvent = persistedSeedEvent(lifecycle.seed);
80142
+ watcher.missingAuthoritativePasses = lifecycle.missingAuthoritativePasses;
80143
+ watcher.lifecycleNotification = lifecycle.lifecycleNotification;
80144
+ }
80145
+ function watcherSessionId(watcher) {
80146
+ return watcher.lastInternalEvent?.sessionId.trim() || watcher.transcript.sessionId?.trim() || null;
80147
+ }
80148
+ function notifyWatcherLifecycle(watcher, reason, observedAt) {
80149
+ const nextNotification = reason === "missing" ? "offline" : "stalled";
80150
+ const sessionId = watcherSessionId(watcher);
80151
+ if (!sessionId)
80152
+ return;
80153
+ const lifecycleKey = observedSessionLifecycleKey(watcher.source.name, sessionId);
80154
+ const persistedLifecycle = persistedObservedSessions.get(lifecycleKey);
80155
+ if (!watcher.internalObserved || watcher.lifecycleNotification === nextNotification || watcher.lifecycleNotification === "offline" || persistedLifecycle?.lifecycleNotification === nextNotification || persistedLifecycle?.lifecycleNotification === "offline")
80156
+ return;
80157
+ watcher.lifecycleNotification = nextNotification;
80158
+ if (persistedLifecycle) {
80159
+ persistedLifecycle.lifecycleNotification = nextNotification;
80160
+ }
80161
+ pushInternalEvent({
80162
+ id: `tail-lifecycle:${watcher.source.name}:${sessionId}:${nextNotification}:${observedAt}`,
80163
+ ts: observedAt,
80164
+ source: watcher.source.name,
80165
+ sessionId,
80166
+ pid: watcher.process.pid,
80167
+ parentPid: watcher.process.ppid || null,
80168
+ project: watcher.transcript.project,
80169
+ cwd: watcher.transcript.cwd ?? watcher.process.cwd ?? "",
80170
+ harness: watcher.transcript.harness,
80171
+ kind: "system",
80172
+ summary: reason === "missing" ? INTERNAL_TAIL_SESSION_OFFLINE_SUMMARY : INTERNAL_TAIL_SESSION_STALLED_SUMMARY,
80173
+ raw: { reason }
80174
+ });
80175
+ if (nextNotification === "offline") {
80176
+ persistedObservedSessions.delete(lifecycleKey);
80177
+ }
80178
+ }
80179
+ function observeWatcherStaleness(watcher, observedAt) {
80180
+ if (watcher.lifecycleNotification !== "none" || !watcher.internalObserved || observedAt - watcher.lastObservedChangeAt < INTERNAL_ACTIVE_STALE_AFTER_MS) {
80181
+ watcher.staleObservations = 0;
80182
+ return;
80183
+ }
80184
+ watcher.staleObservations++;
80185
+ if (watcher.staleObservations >= INTERNAL_STALE_CONFIRMATION_COUNT) {
80186
+ notifyWatcherLifecycle(watcher, "stale", observedAt);
80187
+ }
79250
80188
  }
79251
80189
  async function readNew(handle, fromOffset, fileSize) {
79252
80190
  if (fileSize <= fromOffset) {
79253
- return { text: "", nextOffset: fromOffset };
80191
+ return { bytes: Buffer.alloc(0), nextOffset: fromOffset };
79254
80192
  }
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 };
80193
+ const length = Math.min(fileSize - fromOffset, WATCHER_READ_BYTES);
80194
+ const buffer = Buffer.allocUnsafe(length);
80195
+ const { bytesRead } = await handle.read(buffer, 0, length, fromOffset);
80196
+ return {
80197
+ bytes: buffer.subarray(0, bytesRead),
80198
+ nextOffset: fromOffset + bytesRead
80199
+ };
79259
80200
  }
79260
80201
  async function readTranscriptText(path2, maxBytes = RECENT_TRANSCRIPT_READ_BYTES) {
79261
80202
  let handle = null;
@@ -79275,23 +80216,35 @@ async function readTranscriptText(path2, maxBytes = RECENT_TRANSCRIPT_READ_BYTES
79275
80216
  await handle?.close().catch(() => {});
79276
80217
  }
79277
80218
  }
79278
- async function pumpWatcher(watcher) {
80219
+ async function runWatcherPump(watcher, observedAt) {
79279
80220
  let handle = null;
80221
+ watcher.lastPumpAt = observedAt;
79280
80222
  try {
79281
80223
  const stats = await stat9(watcher.transcriptPath);
80224
+ const changed = stats.size !== watcher.lastKnownSize || stats.mtimeMs !== watcher.lastKnownMtimeMs;
80225
+ watcher.lastKnownSize = stats.size;
80226
+ watcher.lastKnownMtimeMs = stats.mtimeMs;
80227
+ if (changed) {
80228
+ watcher.lastObservedChangeAt = observedAt;
80229
+ watcher.staleObservations = 0;
80230
+ watcher.lifecycleNotification = "none";
80231
+ }
79282
80232
  if (stats.size < watcher.offset) {
79283
80233
  watcher.offset = 0;
79284
80234
  watcher.carry = "";
80235
+ watcher.decoder = new StringDecoder("utf8");
79285
80236
  watcher.emittedEventIds.clear();
79286
80237
  }
79287
- if (stats.size <= watcher.offset)
80238
+ if (stats.size <= watcher.offset) {
80239
+ observeWatcherStaleness(watcher, observedAt);
79288
80240
  return;
80241
+ }
79289
80242
  if (watcher.source.parseFile) {
79290
- const text5 = await readTranscriptText(watcher.transcriptPath);
80243
+ const text4 = await readTranscriptText(watcher.transcriptPath);
79291
80244
  watcher.offset = stats.size;
79292
- if (!text5)
80245
+ if (!text4)
79293
80246
  return;
79294
- const events2 = parsedEventsToArray(watcher.source.parseFile(text5, {
80247
+ const events2 = parsedEventsToArray(watcher.source.parseFile(text4, {
79295
80248
  process: watcher.process,
79296
80249
  transcript: watcher.transcript,
79297
80250
  transcriptPath: watcher.transcriptPath,
@@ -79300,74 +80253,203 @@ async function pumpWatcher(watcher) {
79300
80253
  }));
79301
80254
  watcher.lineCounter += Math.max(1, events2.length);
79302
80255
  for (const event of events2) {
79303
- if (watcher.emittedEventIds.has(event.id))
79304
- continue;
79305
- watcher.emittedEventIds.add(event.id);
79306
- pushEvent(event);
80256
+ rememberWatcherEvent(watcher, event, "all");
79307
80257
  }
79308
80258
  return;
79309
80259
  }
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)
80260
+ const drainEnd = Math.min(stats.size, watcher.offset + WATCHER_DRAIN_BYTES);
80261
+ while (watcher.offset < drainEnd) {
80262
+ handle = await open(watcher.transcriptPath, "r");
80263
+ const { bytes, nextOffset } = await readNew(handle, watcher.offset, drainEnd);
80264
+ await handle.close();
80265
+ handle = null;
80266
+ if (nextOffset <= watcher.offset)
80267
+ break;
80268
+ watcher.offset = nextOffset;
80269
+ const text4 = watcher.decoder.write(bytes);
80270
+ if (!text4)
79321
80271
  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))
80272
+ const combined = watcher.carry + text4;
80273
+ const lines = combined.split(`
80274
+ `);
80275
+ watcher.carry = lines.pop() ?? "";
80276
+ for (const line of lines) {
80277
+ if (!line)
79332
80278
  continue;
79333
- watcher.emittedEventIds.add(event.id);
79334
- pushEvent(event);
80279
+ const event = watcher.source.parseLine(line, {
80280
+ process: watcher.process,
80281
+ transcript: watcher.transcript,
80282
+ transcriptPath: watcher.transcriptPath,
80283
+ lineOffset: watcher.lineCounter,
80284
+ state: watcher.state
80285
+ });
80286
+ watcher.lineCounter++;
80287
+ if (event) {
80288
+ rememberWatcherEvent(watcher, event, "all");
80289
+ }
79335
80290
  }
79336
80291
  }
80292
+ if (stats.size > watcher.offset) {
80293
+ const continuation = setTimeout(() => {
80294
+ pumpWatcher(watcher);
80295
+ }, 0);
80296
+ continuation.unref?.();
80297
+ }
79337
80298
  } catch {} finally {
79338
80299
  await handle?.close().catch(() => {});
79339
80300
  }
79340
80301
  }
79341
- async function pumpAllWatchers() {
79342
- await Promise.allSettled([...watchers.values()].map(pumpWatcher));
80302
+ function scheduleWatcherPump(run) {
80303
+ return new Promise((resolve20, reject) => {
80304
+ const start = () => {
80305
+ activeWatcherPumps++;
80306
+ Promise.resolve().then(run).then(resolve20, reject).finally(() => {
80307
+ activeWatcherPumps--;
80308
+ watcherPumpQueue.shift()?.();
80309
+ });
80310
+ };
80311
+ if (activeWatcherPumps < WATCHER_PUMP_CONCURRENCY) {
80312
+ start();
80313
+ } else {
80314
+ watcherPumpQueue.push(start);
80315
+ }
80316
+ });
80317
+ }
80318
+ function pumpWatcher(watcher, observedAt = Date.now()) {
80319
+ if (watcher.pumpInFlight) {
80320
+ return watcher.pumpInFlight;
80321
+ }
80322
+ const request = scheduleWatcherPump(() => runWatcherPump(watcher, observedAt));
80323
+ watcher.pumpInFlight = request;
80324
+ request.finally(() => {
80325
+ if (watcher.pumpInFlight === request) {
80326
+ watcher.pumpInFlight = null;
80327
+ }
80328
+ }).catch(() => {});
80329
+ return request;
80330
+ }
80331
+ function selectWatchersForCurrentDemand(now = Date.now()) {
80332
+ const all = [...watchers.values()];
80333
+ if (subscribers.size > 0)
80334
+ return all;
80335
+ if (internalSubscribers.size === 0)
80336
+ return [];
80337
+ const hot = [];
80338
+ const coldDue = [];
80339
+ for (const watcher of all) {
80340
+ if (now - watcher.lastObservedChangeAt <= INTERNAL_HOT_WATCHER_WINDOW_MS) {
80341
+ hot.push(watcher);
80342
+ } else if (now - watcher.lastPumpAt >= INTERNAL_IDLE_WATCHER_INTERVAL_MS) {
80343
+ coldDue.push(watcher);
80344
+ }
80345
+ }
80346
+ coldDue.sort((left, right) => left.lastPumpAt - right.lastPumpAt);
80347
+ return [...hot, ...coldDue.slice(0, INTERNAL_IDLE_WATCHER_BATCH_SIZE)];
80348
+ }
80349
+ async function pumpWatchersForCurrentDemand(now = Date.now()) {
80350
+ const selected = selectWatchersForCurrentDemand(now);
80351
+ await Promise.allSettled(selected.map((watcher) => pumpWatcher(watcher, now)));
79343
80352
  }
79344
- async function seedTail(watcher) {
80353
+ async function seedTail(watcher, options = { reconcileInternal: false }) {
80354
+ let stats;
79345
80355
  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);
80356
+ stats = await stat9(watcher.transcriptPath);
80357
+ } catch {
80358
+ watcher.offset = 0;
80359
+ return;
80360
+ }
80361
+ const seededAt = Date.now();
80362
+ watcher.offset = options.reconcileInternal || !watcher.source.parseFile ? stats.size : 0;
80363
+ watcher.lastPumpAt = seededAt;
80364
+ watcher.lastKnownSize = stats.size;
80365
+ watcher.lastKnownMtimeMs = stats.mtimeMs;
80366
+ watcher.lastObservedChangeAt = Math.max(watcher.transcript.lastEventAt ?? 0, watcher.transcript.mtimeMs, stats.mtimeMs);
80367
+ try {
80368
+ if (options.reconcileInternal && watcher.source.parseFile) {
80369
+ const text4 = await readTranscriptText(watcher.transcriptPath, INTERNAL_RECONCILE_READ_BYTES);
80370
+ if (!text4)
80371
+ return;
80372
+ const events2 = parsedEventsToArray(watcher.source.parseFile(text4, {
80373
+ process: watcher.process,
80374
+ transcript: watcher.transcript,
80375
+ transcriptPath: watcher.transcriptPath,
80376
+ lineOffset: 0,
80377
+ state: watcher.state
80378
+ }));
80379
+ watcher.lineCounter = Math.max(1, events2.length);
80380
+ for (const event of events2)
80381
+ rememberWatcherEvent(watcher, event, "internal");
80382
+ return;
80383
+ }
80384
+ if (!watcher.source.parseFile && (options.reconcileInternal || watcher.source.name === "codex")) {
80385
+ 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
80386
  lines.forEach((line, index2) => {
79351
- watcher.source.parseLine(line, {
80387
+ const parsed = watcher.source.parseLine(line, {
79352
80388
  process: watcher.process,
79353
80389
  transcript: watcher.transcript,
79354
80390
  transcriptPath: watcher.transcriptPath,
79355
80391
  lineOffset: index2,
79356
80392
  state: watcher.state
79357
80393
  });
80394
+ if (options.reconcileInternal && parsed) {
80395
+ rememberWatcherEvent(watcher, parsed, "internal");
80396
+ }
79358
80397
  });
80398
+ watcher.lineCounter = lines.length;
79359
80399
  }
79360
- } catch {
79361
- watcher.offset = 0;
80400
+ } catch {}
80401
+ }
80402
+ function reconcileMissingWatchers(seenSessionKeys, successfullyScannedSources, observedAt) {
80403
+ for (const [sessionKey4, watcher] of watchers) {
80404
+ if (seenSessionKeys.has(sessionKey4)) {
80405
+ watcher.missingAuthoritativePasses = 0;
80406
+ continue;
80407
+ }
80408
+ if (!successfullyScannedSources.has(watcher.source.name))
80409
+ continue;
80410
+ watcher.missingAuthoritativePasses++;
80411
+ if (watcher.missingAuthoritativePasses < AUTHORITATIVE_MISSING_CONFIRMATION_COUNT) {
80412
+ continue;
80413
+ }
80414
+ notifyWatcherLifecycle(watcher, "missing", observedAt);
80415
+ watchers.delete(sessionKey4);
80416
+ knownTranscripts.delete(sessionKey4);
80417
+ }
80418
+ }
80419
+ function reconcilePersistedObservedSessions(seenLifecycleKeys, successfullyScannedSources, observedAt) {
80420
+ for (const [lifecycleKey, lifecycle] of persistedObservedSessions) {
80421
+ if (seenLifecycleKeys.has(lifecycleKey)) {
80422
+ lifecycle.missingAuthoritativePasses = 0;
80423
+ continue;
80424
+ }
80425
+ if (!successfullyScannedSources.has(lifecycle.seed.source))
80426
+ continue;
80427
+ lifecycle.missingAuthoritativePasses++;
80428
+ if (lifecycle.missingAuthoritativePasses < AUTHORITATIVE_MISSING_CONFIRMATION_COUNT || lifecycle.lifecycleNotification === "offline") {
80429
+ continue;
80430
+ }
80431
+ lifecycle.lifecycleNotification = "offline";
80432
+ const seedEvent = persistedSeedEvent(lifecycle.seed);
80433
+ pushInternalEvent({
80434
+ ...seedEvent,
80435
+ id: `tail-lifecycle:${lifecycle.seed.source}:${lifecycle.seed.sourceSessionId}:offline:${observedAt}`,
80436
+ ts: observedAt,
80437
+ summary: INTERNAL_TAIL_SESSION_OFFLINE_SUMMARY,
80438
+ raw: { reason: "missing" }
80439
+ });
80440
+ persistedObservedSessions.delete(lifecycleKey);
79362
80441
  }
79363
80442
  }
79364
80443
  async function refreshDiscovery(scope = "shallow", options = { pruneMissing: scope !== "hot" }) {
79365
80444
  const allProcesses = [];
79366
80445
  const cachedProcesses = lastDiscovery?.processes ?? [];
79367
80446
  const seenSessionKeys = new Set;
80447
+ const seenObservedLifecycleKeys = new Set;
80448
+ const successfullyScannedSources = new Set;
79368
80449
  const discoveryIssues = [];
79369
80450
  const discoveryIssueKeys = new Set;
79370
80451
  for (const source of sources) {
80452
+ let remainingInternalReconciliations = internalSubscribers.size > 0 ? INTERNAL_RECONCILE_SESSIONS_PER_SOURCE : 0;
79371
80453
  let processes = [];
79372
80454
  if (scope === "hot" && cachedProcesses.length > 0) {
79373
80455
  processes = cachedProcesses.filter((proc) => proc.source === source.name);
@@ -79382,14 +80464,19 @@ async function refreshDiscovery(scope = "shallow", options = { pruneMissing: sco
79382
80464
  let transcripts = [];
79383
80465
  try {
79384
80466
  transcripts = await source.discoverTranscripts(processes, scope);
80467
+ successfullyScannedSources.add(source.name);
79385
80468
  } catch {
79386
80469
  transcripts = [];
79387
80470
  }
79388
- for (const transcript of transcripts) {
80471
+ for (const transcript of [...transcripts].sort((left, right) => right.mtimeMs - left.mtimeMs)) {
79389
80472
  const primary = processForTranscript(transcript, processes);
79390
80473
  const transcriptPath = transcript.transcriptPath;
79391
80474
  const sessionKey4 = sessionRegistryKey(transcript);
79392
80475
  seenSessionKeys.add(sessionKey4);
80476
+ const sourceSessionId = transcript.sessionId?.trim();
80477
+ if (sourceSessionId) {
80478
+ seenObservedLifecycleKeys.add(observedSessionLifecycleKey(source.name, sourceSessionId));
80479
+ }
79393
80480
  const prior = knownTranscripts.get(sessionKey4);
79394
80481
  if (transcript.source !== "cursor" && prior && prior.transcriptPath !== transcriptPath) {
79395
80482
  const issueKey = `${sessionKey4}\x00${prior.transcriptPath}\x00${transcriptPath}`;
@@ -79417,10 +80504,25 @@ async function refreshDiscovery(scope = "shallow", options = { pruneMissing: sco
79417
80504
  offset: 0,
79418
80505
  lineCounter: 0,
79419
80506
  carry: "",
80507
+ decoder: new StringDecoder("utf8"),
79420
80508
  emittedEventIds: new Set,
79421
- state: {}
80509
+ state: {},
80510
+ lastPumpAt: 0,
80511
+ lastObservedChangeAt: Math.max(transcript.lastEventAt ?? 0, transcript.mtimeMs),
80512
+ lastKnownSize: transcript.size,
80513
+ lastKnownMtimeMs: transcript.mtimeMs,
80514
+ missingAuthoritativePasses: 0,
80515
+ staleObservations: 0,
80516
+ lifecycleNotification: "none",
80517
+ internalObserved: false,
80518
+ lastInternalEvent: null,
80519
+ pumpInFlight: null
79422
80520
  };
79423
- await seedTail(watcher);
80521
+ const reconcileInternal = remainingInternalReconciliations > 0;
80522
+ if (reconcileInternal)
80523
+ remainingInternalReconciliations--;
80524
+ await seedTail(watcher, { reconcileInternal });
80525
+ applyPersistedObservedSeedToWatcher(watcher);
79424
80526
  watchers.set(sessionKey4, watcher);
79425
80527
  continue;
79426
80528
  }
@@ -79428,21 +80530,33 @@ async function refreshDiscovery(scope = "shallow", options = { pruneMissing: sco
79428
80530
  watcher.transcriptPath = transcriptPath;
79429
80531
  watcher.lineCounter = 0;
79430
80532
  watcher.carry = "";
80533
+ watcher.decoder = new StringDecoder("utf8");
79431
80534
  watcher.emittedEventIds = new Set;
79432
80535
  watcher.state = {};
79433
- await seedTail(watcher);
80536
+ watcher.internalObserved = false;
80537
+ watcher.lastInternalEvent = null;
80538
+ const reconcileInternal = remainingInternalReconciliations > 0;
80539
+ if (reconcileInternal)
80540
+ remainingInternalReconciliations--;
80541
+ await seedTail(watcher, { reconcileInternal });
80542
+ }
80543
+ applyPersistedObservedSeedToWatcher(watcher);
80544
+ if (transcript.mtimeMs > watcher.transcript.mtimeMs) {
80545
+ watcher.lastObservedChangeAt = Date.now();
80546
+ watcher.staleObservations = 0;
80547
+ watcher.lifecycleNotification = "none";
80548
+ if (watcher.source.parseFile)
80549
+ watcher.offset = 0;
79434
80550
  }
79435
80551
  watcher.process = primary;
79436
80552
  watcher.transcript = transcript;
80553
+ watcher.missingAuthoritativePasses = 0;
79437
80554
  }
79438
80555
  }
79439
80556
  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
- }
80557
+ const observedAt = Date.now();
80558
+ reconcileMissingWatchers(seenSessionKeys, successfullyScannedSources, observedAt);
80559
+ reconcilePersistedObservedSessions(seenObservedLifecycleKeys, successfullyScannedSources, observedAt);
79446
80560
  }
79447
80561
  const allTranscripts = [...knownTranscripts.values()].sort((a, b) => b.mtimeMs - a.mtimeMs);
79448
80562
  let scoutManaged = 0;
@@ -79486,7 +80600,7 @@ function scheduleDiscovery(scope, options = { pruneMissing: scope !== "hot" }) {
79486
80600
  function ensureLoopRunning() {
79487
80601
  if (!pollTimer) {
79488
80602
  pollTimer = setInterval(() => {
79489
- pumpAllWatchers();
80603
+ pumpWatchersForCurrentDemand();
79490
80604
  }, TAIL_POLL_INTERVAL_MS);
79491
80605
  }
79492
80606
  if (!hotDiscoveryTimer) {
@@ -79506,7 +80620,7 @@ function ensureLoopRunning() {
79506
80620
  }
79507
80621
  }
79508
80622
  function stopLoopIfIdle() {
79509
- if (subscribers.size > 0)
80623
+ if (subscribers.size > 0 || internalSubscribers.size > 0)
79510
80624
  return;
79511
80625
  if (pollTimer) {
79512
80626
  clearInterval(pollTimer);
@@ -79525,6 +80639,7 @@ function stopLoopIfIdle() {
79525
80639
  deepDiscoveryTimer = null;
79526
80640
  }
79527
80641
  watchers.clear();
80642
+ persistedObservedSessions.clear();
79528
80643
  }
79529
80644
  function normalizeTailDiscoveryOptions(input = false) {
79530
80645
  if (typeof input === "boolean") {
@@ -79550,18 +80665,19 @@ async function refreshTailDiscovery(scope = "shallow") {
79550
80665
  }
79551
80666
  function subscribeTail(handler) {
79552
80667
  subscribers.add(handler);
80668
+ startTailLoopForSubscriber();
80669
+ return () => {
80670
+ subscribers.delete(handler);
80671
+ stopLoopIfIdle();
80672
+ };
80673
+ }
80674
+ function startTailLoopForSubscriber() {
79553
80675
  ensureLoopRunning();
79554
80676
  if (watchers.size === 0) {
79555
- runDiscovery("shallow", { pruneMissing: true }).then(() => pumpAllWatchers()).catch(() => {});
80677
+ runDiscovery("shallow", { pruneMissing: true }).then(() => pumpWatchersForCurrentDemand()).catch(() => {});
79556
80678
  } else {
79557
- pumpAllWatchers();
80679
+ pumpWatchersForCurrentDemand();
79558
80680
  }
79559
- return () => {
79560
- subscribers.delete(handler);
79561
- if (subscribers.size === 0) {
79562
- stopLoopIfIdle();
79563
- }
79564
- };
79565
80681
  }
79566
80682
  function snapshotRecentEvents(limit = 500) {
79567
80683
  return aggregateBuffer.slice(-limit);
@@ -79755,7 +80871,7 @@ async function readRecentTranscriptEvents(limit = 50, options) {
79755
80871
  const lineLimit = options?.perTranscriptLineLimit ?? RECENT_TRANSCRIPT_LINES_PER_FILE;
79756
80872
  const kinds = options?.kinds?.length ? new Set(options.kinds) : null;
79757
80873
  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);
80874
+ 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
80875
  for (const watcher of activeWatchers) {
79760
80876
  seenTranscriptPaths.add(watcher.transcriptPath);
79761
80877
  await appendWatcherTranscriptEvents(events2, seenEvents, watcher, {
@@ -79765,7 +80881,7 @@ async function readRecentTranscriptEvents(limit = 50, options) {
79765
80881
  });
79766
80882
  }
79767
80883
  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);
80884
+ 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
80885
  for (const transcript of discoveryTranscripts) {
79770
80886
  const parsed = await parseTranscriptSessionEvents(transcript, options.discovery.processes, lineLimit, {
79771
80887
  kinds,
@@ -79778,7 +80894,7 @@ async function readRecentTranscriptEvents(limit = 50, options) {
79778
80894
  seenTranscriptPaths.add(transcript.transcriptPath);
79779
80895
  }
79780
80896
  }
79781
- return events2.filter((event) => !kinds || kinds.has(event.kind)).sort((left, right) => right.ts - left.ts).slice(0, limit);
80897
+ 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
80898
  }
79783
80899
  function normalizeTailSessionRef(value) {
79784
80900
  const trimmed = value?.trim();
@@ -80105,7 +81221,7 @@ function buildObserveDataFromTail(transcript, events2, current) {
80105
81221
  var HISTORY_SNAPSHOT_CACHE_LIMIT = 128;
80106
81222
  var historySnapshotCache = new Map;
80107
81223
  var OBSERVE_SUMMARY_TAIL_SIZE = 8;
80108
- var SESSION_REF_LOOKUP_TTL_MS = 1e4;
81224
+ var SESSION_REF_LOOKUP_TTL_MS = 60000;
80109
81225
  var sessionRefLookupCache = null;
80110
81226
  var AMBIGUOUS_TAIL_SESSION_REF = Symbol("ambiguous-tail-session-ref");
80111
81227
  function normalizedComparableString2(value) {
@@ -80194,6 +81310,23 @@ function tailSessionRefTranscripts(discovery, normalizedRef, harness2) {
80194
81310
  return refs.includes(normalizedRef);
80195
81311
  });
80196
81312
  }
81313
+ function bestTailSessionRefLookupEntry(transcripts, normalizedRef) {
81314
+ const matches = [];
81315
+ for (const transcript of transcripts) {
81316
+ const adapterType = adapterTypeFromTailSource(transcript.source);
81317
+ if (!adapterType || !supportsHistorySessionSnapshotForPath(transcript.transcriptPath, adapterType)) {
81318
+ continue;
81319
+ }
81320
+ matches.push({
81321
+ refId: normalizedRef,
81322
+ historyPath: transcript.transcriptPath,
81323
+ adapterType,
81324
+ mtimeMs: transcript.mtimeMs,
81325
+ size: transcript.size
81326
+ });
81327
+ }
81328
+ return matches.sort((left, right) => right.mtimeMs - left.mtimeMs)[0] ?? null;
81329
+ }
80197
81330
  function hasAmbiguousTailHarness(transcripts) {
80198
81331
  const sources2 = new Set(transcripts.map((transcript) => canonicalSessionHarness(transcript.source) ?? transcript.source.trim().toLowerCase()));
80199
81332
  return sources2.size > 1;
@@ -80270,23 +81403,7 @@ async function findTailSessionRefLookupEntry(normalizedRef, harness2 = null) {
80270
81403
  const transcripts = tailSessionRefTranscripts(discovery, normalizedRef, harness2);
80271
81404
  if (!harness2 && hasAmbiguousTailHarness(transcripts))
80272
81405
  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;
81406
+ return bestTailSessionRefLookupEntry(transcripts, normalizedRef);
80290
81407
  }
80291
81408
  function historyAdapterAlias(value) {
80292
81409
  const normalized = value?.trim().toLowerCase();
@@ -81707,11 +82824,12 @@ async function loadSessionRefObservePayload(refId) {
81707
82824
  return null;
81708
82825
  }
81709
82826
  let [discovery, brokerContext] = await Promise.all([
81710
- getTailDiscovery().catch(() => null),
82827
+ readScoutBrokerTailDiscovery().catch(() => null),
81711
82828
  loadScoutBrokerContext().catch(() => null)
81712
82829
  ]);
81713
82830
  let matchingTranscripts = discovery ? tailSessionRefTranscripts(discovery, normalizedRef, requestedHarness) : [];
81714
- const cachedHistoryEntry = !requestedHarness || requestedHarness === "claude" ? sessionRefLookup().get(normalizedRef) ?? null : null;
82831
+ const discoveredHistoryEntry = bestTailSessionRefLookupEntry(matchingTranscripts, normalizedRef);
82832
+ const cachedHistoryEntry = !discoveredHistoryEntry && matchingTranscripts.length === 0 && (!requestedHarness || requestedHarness === "claude") ? sessionRefLookup().get(normalizedRef) ?? null : null;
81715
82833
  const matchingAgents = queryAgents(200).filter((agent) => normalizeSessionRefId(agent.harnessSessionId) === normalizedRef && sessionHarnessMatches(requestedHarness, agent.harness));
81716
82834
  const matchingAgentOwners = new Set(matchingAgents.map((agent) => agent.id));
81717
82835
  if (matchingAgentOwners.size > 1)
@@ -81778,7 +82896,7 @@ async function loadSessionRefObservePayload(refId) {
81778
82896
  }
81779
82897
  if (!requestedHarness && hasAmbiguousTailHarness(matchingTranscripts))
81780
82898
  return null;
81781
- const tailHistoryEntry = await findTailSessionRefLookupEntry(normalizedRef, requestedHarness);
82899
+ const tailHistoryEntry = discoveredHistoryEntry ?? await findTailSessionRefLookupEntry(normalizedRef, requestedHarness);
81782
82900
  if (tailHistoryEntry === AMBIGUOUS_TAIL_SESSION_REF)
81783
82901
  return null;
81784
82902
  const historyEntry = tailHistoryEntry ?? (requestedHarness === "claude" || !requestedHarness && (!discovery || matchingTranscripts.length === 0) ? cachedHistoryEntry : null);
@@ -86450,6 +87568,7 @@ var MAX_EVENTS_PER_SESSION = 200;
86450
87568
  var sessions4 = new Map;
86451
87569
  var hosts = new Map;
86452
87570
  var sessionSubscribers = new Map;
87571
+ var hostCommandWaiters = new Map;
86453
87572
  var nextHostPollId = 1;
86454
87573
  var DEFAULT_VOICE_SETTINGS = {
86455
87574
  preference: "auto",
@@ -86464,6 +87583,9 @@ function registerScoutVoiceHost(input) {
86464
87583
  const now = Date.now();
86465
87584
  const previous = hosts.get(hostId);
86466
87585
  const instanceId = input.instanceId?.trim() || null;
87586
+ if (previous && previous.instanceId !== instanceId) {
87587
+ finishScoutVoiceHostCommandWaiter(hostId, null);
87588
+ }
86467
87589
  const settings = mergeVoiceSettings(previous?.settings ?? DEFAULT_VOICE_SETTINGS, input.settings);
86468
87590
  hosts.set(hostId, {
86469
87591
  hostId,
@@ -86545,6 +87667,7 @@ function awaitScoutVoiceHostCommand(hostId, timeoutMs = 25000, instanceId, signa
86545
87667
  if (host.instanceId !== normalizedInstanceId) {
86546
87668
  return Promise.resolve({ command: null });
86547
87669
  }
87670
+ finishScoutVoiceHostCommandWaiter(normalizedHostId, null);
86548
87671
  const pollId = nextHostPollId++;
86549
87672
  host.activePollId = pollId;
86550
87673
  touchScoutVoiceHost(normalizedHostId);
@@ -86559,40 +87682,37 @@ function awaitScoutVoiceHostCommand(hostId, timeoutMs = 25000, instanceId, signa
86559
87682
  return Promise.resolve({ command: null });
86560
87683
  }
86561
87684
  return new Promise((resolve23) => {
86562
- const startedAt = Date.now();
86563
87685
  let settled = false;
87686
+ let timeout = null;
86564
87687
  const finish = (command) => {
86565
87688
  if (settled)
86566
87689
  return;
86567
87690
  settled = true;
86568
- clearInterval(timer);
87691
+ if (timeout)
87692
+ clearTimeout(timeout);
86569
87693
  signal?.removeEventListener("abort", abort);
87694
+ const waiter = hostCommandWaiters.get(normalizedHostId);
87695
+ if (waiter?.pollId === pollId)
87696
+ hostCommandWaiters.delete(normalizedHostId);
86570
87697
  const current = hosts.get(normalizedHostId);
86571
87698
  if (current?.activePollId === pollId)
86572
87699
  current.activePollId = null;
86573
87700
  resolve23({ command });
86574
87701
  };
86575
87702
  const abort = () => finish(null);
86576
- const timer = setInterval(() => {
87703
+ hostCommandWaiters.set(normalizedHostId, {
87704
+ pollId,
87705
+ instanceId: normalizedInstanceId,
87706
+ finish
87707
+ });
87708
+ timeout = setTimeout(() => {
86577
87709
  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;
87710
+ if (current && current.instanceId === normalizedInstanceId && current.activePollId === pollId) {
87711
+ current.lastSeenAt = Date.now();
86591
87712
  }
86592
- if (Date.now() - startedAt >= timeoutMs) {
86593
- finish(null);
86594
- }
86595
- }, 100);
87713
+ finish(null);
87714
+ }, timeoutMs);
87715
+ timeout.unref?.();
86596
87716
  signal?.addEventListener("abort", abort, { once: true });
86597
87717
  if (signal?.aborted)
86598
87718
  abort();
@@ -86813,6 +87933,22 @@ function queueHostCommand(hostId, command) {
86813
87933
  }
86814
87934
  host.pendingCommands.push(command);
86815
87935
  host.lastSeenAt = Date.now();
87936
+ deliverPendingScoutVoiceHostCommand(host);
87937
+ }
87938
+ function finishScoutVoiceHostCommandWaiter(hostId, command) {
87939
+ hostCommandWaiters.get(hostId)?.finish(command);
87940
+ }
87941
+ function deliverPendingScoutVoiceHostCommand(host) {
87942
+ const waiter = hostCommandWaiters.get(host.hostId);
87943
+ if (!waiter)
87944
+ return;
87945
+ if (waiter.instanceId !== host.instanceId || waiter.pollId !== host.activePollId) {
87946
+ waiter.finish(null);
87947
+ return;
87948
+ }
87949
+ const command = host.pendingCommands.shift();
87950
+ if (command)
87951
+ waiter.finish(command);
86816
87952
  }
86817
87953
  function dispatchSessionCommand(session3, command) {
86818
87954
  const hostId = session3.assignedHostId ?? pickLiveVoiceHost()?.hostId;
@@ -87226,6 +88362,127 @@ function defaultVoiceForModel(providers2, modelId2) {
87226
88362
  return firstNonEmptyString7(provider?.env.VOX_MLX_AUDIO_TTS_DEFAULT_VOICE);
87227
88363
  }
87228
88364
 
88365
+ // packages/web/server/nvidia-speech.ts
88366
+ var NVIDIA_MAGPIE_SPEECH_URL = "https://877104f7-e885-42b9-8de8-f6e4c6303969.invocation.api.nvcf.nvidia.com/v1/audio/synthesize";
88367
+ var NVIDIA_MAGPIE_VOICES_URL = "https://877104f7-e885-42b9-8de8-f6e4c6303969.invocation.api.nvcf.nvidia.com/v1/audio/list_voices";
88368
+ var NVIDIA_MAGPIE_MODEL = "magpie-tts-multilingual";
88369
+ var NVIDIA_MAGPIE_DEFAULT_VOICE = "Magpie-Multilingual.EN-US.Aria";
88370
+ var NVIDIA_MAGPIE_TIMEOUT_MS = 60000;
88371
+ var NVIDIA_MAGPIE_MAX_CHARACTERS = 2000;
88372
+ function resolveNvidiaApiKey(env = process.env) {
88373
+ return firstNonEmptyString8(env.NV_API_KEY, env.NVIDIA_API_KEY);
88374
+ }
88375
+ function isNvidiaMagpieSpeechModel(modelId2) {
88376
+ return modelId2?.trim() === NVIDIA_MAGPIE_MODEL;
88377
+ }
88378
+ async function listNvidiaMagpieVoices(input) {
88379
+ const timeout = AbortSignal.timeout(NVIDIA_MAGPIE_TIMEOUT_MS);
88380
+ const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
88381
+ const response = await fetch(input.endpoint ?? NVIDIA_MAGPIE_VOICES_URL, {
88382
+ method: "GET",
88383
+ headers: { authorization: `Bearer ${input.apiKey}` },
88384
+ signal
88385
+ });
88386
+ if (!response.ok) {
88387
+ const detail = await response.text().catch(() => "");
88388
+ throw new Error(`NVIDIA Magpie voice discovery failed (${response.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`);
88389
+ }
88390
+ const voices = parseNvidiaMagpieVoices(await response.json());
88391
+ if (voices.length === 0) {
88392
+ throw new Error("NVIDIA Magpie voice discovery returned no voices.");
88393
+ }
88394
+ return voices;
88395
+ }
88396
+ async function synthesizeNvidiaMagpieSpeech(input) {
88397
+ const text4 = Array.from(input.text).slice(0, NVIDIA_MAGPIE_MAX_CHARACTERS).join("");
88398
+ const voiceId = input.voiceId?.trim() || NVIDIA_MAGPIE_DEFAULT_VOICE;
88399
+ const language = magpieLanguageForVoice(voiceId);
88400
+ const form = new FormData;
88401
+ form.set("text", text4);
88402
+ form.set("language", language);
88403
+ form.set("voice", voiceId);
88404
+ form.set("encoding", "LINEAR_PCM");
88405
+ form.set("sample_rate_hz", "44100");
88406
+ const timeout = AbortSignal.timeout(NVIDIA_MAGPIE_TIMEOUT_MS);
88407
+ const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
88408
+ const response = await fetch(input.endpoint ?? NVIDIA_MAGPIE_SPEECH_URL, {
88409
+ method: "POST",
88410
+ headers: { authorization: `Bearer ${input.apiKey}` },
88411
+ body: form,
88412
+ signal
88413
+ });
88414
+ if (!response.ok) {
88415
+ const detail = await response.text().catch(() => "");
88416
+ throw new Error(`NVIDIA Magpie speech failed (${response.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`);
88417
+ }
88418
+ const audio = new Uint8Array(await response.arrayBuffer());
88419
+ if (audio.byteLength === 0) {
88420
+ throw new Error("NVIDIA Magpie returned no audio.");
88421
+ }
88422
+ const responseContentType = response.headers.get("content-type")?.split(";")[0];
88423
+ return {
88424
+ contentType: responseContentType?.startsWith("audio/") ? responseContentType : "audio/wav",
88425
+ audioBase64: Buffer.from(audio).toString("base64"),
88426
+ modelId: NVIDIA_MAGPIE_MODEL,
88427
+ voiceId,
88428
+ audioBytes: audio.byteLength
88429
+ };
88430
+ }
88431
+ function magpieLanguageForVoice(voiceId) {
88432
+ const locale = magpieLocaleSegment(voiceId);
88433
+ if (!locale)
88434
+ return "en-US";
88435
+ const [language, region] = locale.split("-");
88436
+ return `${language.toLowerCase()}-${region.toUpperCase()}`;
88437
+ }
88438
+ function parseNvidiaMagpieVoices(value) {
88439
+ if (!value || typeof value !== "object" || Array.isArray(value))
88440
+ return [];
88441
+ const ids = new Set;
88442
+ for (const group of Object.values(value)) {
88443
+ if (!group || typeof group !== "object" || Array.isArray(group))
88444
+ continue;
88445
+ const voices = group.voices;
88446
+ if (!Array.isArray(voices))
88447
+ continue;
88448
+ for (const voice of voices) {
88449
+ if (typeof voice !== "string")
88450
+ continue;
88451
+ const id = voice.trim();
88452
+ if (id)
88453
+ ids.add(id);
88454
+ }
88455
+ }
88456
+ return Array.from(ids, voiceDetails).sort((left, right) => left.language.localeCompare(right.language) || left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
88457
+ }
88458
+ function voiceDetails(id) {
88459
+ const parts = id.split(".");
88460
+ const language = magpieLanguageForVoice(id);
88461
+ const localeIndex = parts.findIndex(isLocaleSegment);
88462
+ const nameParts = localeIndex >= 0 ? parts.slice(localeIndex + 1) : [];
88463
+ const name2 = nameParts.length > 0 ? nameParts.join(" \xB7 ") : parts.at(-1) || id;
88464
+ return {
88465
+ id,
88466
+ name: name2,
88467
+ language,
88468
+ isDefault: id === NVIDIA_MAGPIE_DEFAULT_VOICE
88469
+ };
88470
+ }
88471
+ function magpieLocaleSegment(voiceId) {
88472
+ return voiceId.split(".").find(isLocaleSegment);
88473
+ }
88474
+ function isLocaleSegment(value) {
88475
+ return /^[a-z]{2,3}-[a-z]{2}$/i.test(value);
88476
+ }
88477
+ function firstNonEmptyString8(...values) {
88478
+ for (const value of values) {
88479
+ const trimmed = value?.trim();
88480
+ if (trimmed)
88481
+ return trimmed;
88482
+ }
88483
+ return;
88484
+ }
88485
+
87229
88486
  // packages/web/server/scout-voice.ts
87230
88487
  var SCOUT_VOICE_CLIENT_ID = "openscout-web";
87231
88488
  var DEFAULT_SCOUT_VOICE_ASR_URL = "http://127.0.0.1:43115";
@@ -87268,14 +88525,16 @@ async function getScoutSpeechCatalog(input = {}) {
87268
88525
  let models = [];
87269
88526
  let voices = [];
87270
88527
  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 {}
88528
+ if (requestedModelId !== NVIDIA_MAGPIE_MODEL) {
88529
+ try {
88530
+ [models, voices] = await Promise.all([
88531
+ listVoxSpeechModels(input.signal),
88532
+ listVoxSpeechVoices(requestedModelId, input.signal)
88533
+ ]);
88534
+ if (models.length > 0)
88535
+ source = "vox";
88536
+ } catch {}
88537
+ }
87279
88538
  const fallback = fallbackScoutSpeechCatalog(requestedModelId, defaults);
87280
88539
  for (const model2 of fallback.models) {
87281
88540
  if (!models.some((candidate) => candidate.id === model2.id)) {
@@ -87289,6 +88548,25 @@ async function getScoutSpeechCatalog(input = {}) {
87289
88548
  models = models.map((model2) => model2.provider === "openai" ? { ...model2, available: true } : model2);
87290
88549
  voices = voices.map((voice) => voice.provider === "openai" ? { ...voice, available: true } : voice);
87291
88550
  }
88551
+ if (input.directNvidiaAvailable || input.directNvidiaApiKey) {
88552
+ models = models.map((model2) => model2.provider === "nvidia" ? { ...model2, available: true } : model2);
88553
+ voices = voices.map((voice) => voice.provider === "nvidia" ? { ...voice, available: true } : voice);
88554
+ }
88555
+ if (input.directNvidiaApiKey && requestedModelId === NVIDIA_MAGPIE_MODEL) {
88556
+ try {
88557
+ const discovered = await listNvidiaMagpieVoices({
88558
+ apiKey: input.directNvidiaApiKey,
88559
+ signal: input.signal
88560
+ });
88561
+ voices = discovered.map((voice) => ({
88562
+ ...voice,
88563
+ provider: "nvidia",
88564
+ modelId: NVIDIA_MAGPIE_MODEL,
88565
+ available: true
88566
+ }));
88567
+ source = "nvidia-developer-inference";
88568
+ } catch {}
88569
+ }
87292
88570
  return {
87293
88571
  defaultModelId: defaults.modelId,
87294
88572
  ...defaults.voiceId ? { defaultVoiceId: defaults.voiceId } : {},
@@ -87300,7 +88578,8 @@ async function getScoutSpeechCatalog(input = {}) {
87300
88578
  function fallbackScoutSpeechCatalog(modelId2, defaults = resolveScoutSpeechDefaults()) {
87301
88579
  const models = [
87302
88580
  { 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 }
88581
+ { id: "eleven_multilingual_v2", name: "Eleven Multilingual v2", provider: "elevenlabs", available: true },
88582
+ { id: NVIDIA_MAGPIE_MODEL, name: "Magpie TTS Multilingual", provider: "nvidia", available: true }
87304
88583
  ];
87305
88584
  const openAIVoices = ["alloy", "ash", "ballad", "cedar", "coral", "echo", "fable", "marin", "nova", "onyx", "sage", "shimmer", "verse"].map((id) => ({
87306
88585
  id,
@@ -87318,11 +88597,20 @@ function fallbackScoutSpeechCatalog(modelId2, defaults = resolveScoutSpeechDefau
87318
88597
  available: true,
87319
88598
  isDefault: true
87320
88599
  }];
88600
+ const nvidiaVoices = [{
88601
+ id: NVIDIA_MAGPIE_DEFAULT_VOICE,
88602
+ name: "Aria",
88603
+ language: "en-US",
88604
+ provider: "nvidia",
88605
+ modelId: NVIDIA_MAGPIE_MODEL,
88606
+ available: true,
88607
+ isDefault: true
88608
+ }];
87321
88609
  return {
87322
88610
  defaultModelId: defaults.modelId,
87323
88611
  ...defaults.voiceId ? { defaultVoiceId: defaults.voiceId } : {},
87324
88612
  models,
87325
- voices: modelId2 === "eleven_multilingual_v2" ? elevenLabsVoices : openAIVoices,
88613
+ voices: modelId2 === "eleven_multilingual_v2" ? elevenLabsVoices : modelId2 === NVIDIA_MAGPIE_MODEL ? nvidiaVoices : openAIVoices,
87326
88614
  source: "fallback"
87327
88615
  };
87328
88616
  }
@@ -87337,9 +88625,9 @@ function createScoutVoiceAsrClient(probeTimeout) {
87337
88625
  });
87338
88626
  }
87339
88627
  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;
88628
+ return firstNonEmptyString9(env.OPENSCOUT_VOICE_ASR_URL, env.OPENSCOUT_VOICE_BRIDGE_URL, env.VOX_COMPANION_URL) ?? DEFAULT_SCOUT_VOICE_ASR_URL;
87341
88629
  }
87342
- function firstNonEmptyString8(...values) {
88630
+ function firstNonEmptyString9(...values) {
87343
88631
  for (const value of values) {
87344
88632
  const trimmed = value?.trim();
87345
88633
  if (trimmed)
@@ -87528,9 +88816,9 @@ class ScoutRealtimeVoiceAdmission {
87528
88816
  }
87529
88817
  function resolveScoutRealtimeVoiceConfig(env = process.env) {
87530
88818
  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
88819
+ model: firstNonEmptyString10(env.OPENSCOUT_REALTIME_MODEL) ?? DEFAULT_REALTIME_MODEL,
88820
+ voice: firstNonEmptyString10(env.OPENSCOUT_REALTIME_VOICE) ?? DEFAULT_REALTIME_VOICE,
88821
+ instructions: firstNonEmptyString10(env.OPENSCOUT_REALTIME_INSTRUCTIONS) ?? SCOUT_REALTIME_INSTRUCTIONS
87534
88822
  };
87535
88823
  }
87536
88824
  function scoutRealtimeVoiceEnvironmentOverride(env = process.env) {
@@ -87668,7 +88956,7 @@ function positiveInteger3(value, fallback) {
87668
88956
  function secondsUntil(timestamp, now) {
87669
88957
  return Math.max(1, Math.ceil((timestamp - now) / 1000));
87670
88958
  }
87671
- function firstNonEmptyString9(value) {
88959
+ function firstNonEmptyString10(value) {
87672
88960
  const trimmed = value?.trim();
87673
88961
  return trimmed || undefined;
87674
88962
  }
@@ -88035,10 +89323,13 @@ function mountScoutVoiceRoutes(app, deps = {}) {
88035
89323
  const directOpenAIAvailable = Boolean(await deps.resolveOpenAIApiKey?.().catch(() => {
88036
89324
  return;
88037
89325
  }) ?? process.env.OPENAI_API_KEY?.trim());
89326
+ const directNvidiaApiKey = resolveNvidiaApiKey();
88038
89327
  return c.json(await getScoutSpeechCatalog({
88039
89328
  modelId: c.req.query("modelId"),
88040
89329
  signal: c.req.raw.signal,
88041
- directOpenAIAvailable
89330
+ directOpenAIAvailable,
89331
+ directNvidiaAvailable: Boolean(directNvidiaApiKey),
89332
+ directNvidiaApiKey
88042
89333
  }));
88043
89334
  });
88044
89335
  app.post("/api/voice/engage", async (c) => {
@@ -88254,6 +89545,28 @@ function mountScoutVoiceRoutes(app, deps = {}) {
88254
89545
  if (speechTiming === null) {
88255
89546
  return c.json({ error: "speechTiming is invalid" }, 400);
88256
89547
  }
89548
+ const defaults = resolveScoutSpeechDefaults();
89549
+ const requestedModelId = body.modelId ?? defaults.modelId;
89550
+ if (isNvidiaMagpieSpeechModel(requestedModelId)) {
89551
+ const nvidiaApiKey = resolveNvidiaApiKey();
89552
+ if (!nvidiaApiKey) {
89553
+ return c.json({ error: "NV_API_KEY is required for hosted NVIDIA Developer Inference." }, 503);
89554
+ }
89555
+ try {
89556
+ return c.json({
89557
+ ...await synthesizeNvidiaMagpieSpeech({
89558
+ text: text4,
89559
+ apiKey: nvidiaApiKey,
89560
+ voiceId: body.voiceId,
89561
+ signal: c.req.raw.signal
89562
+ }),
89563
+ route: "nvidia-developer-inference"
89564
+ });
89565
+ } catch (error) {
89566
+ const message = error instanceof Error ? error.message : "NVIDIA Magpie speech failed";
89567
+ return c.json({ error: message }, 503);
89568
+ }
89569
+ }
88257
89570
  try {
88258
89571
  return c.json(await synthesizeScoutSpeech({
88259
89572
  text: text4,
@@ -88271,13 +89584,12 @@ function mountScoutVoiceRoutes(app, deps = {}) {
88271
89584
  return;
88272
89585
  }) ?? process.env.OPENAI_API_KEY?.trim();
88273
89586
  if (apiKey && !c.req.raw.signal.aborted) {
88274
- const defaults = resolveScoutSpeechDefaults();
88275
89587
  try {
88276
89588
  return c.json({
88277
89589
  ...await synthesizeOpenAISpeech({
88278
89590
  text: text4,
88279
89591
  apiKey,
88280
- modelId: body.modelId ?? defaults.modelId,
89592
+ modelId: requestedModelId,
88281
89593
  voiceId: body.voiceId ?? defaults.voiceId,
88282
89594
  speed: body.speed,
88283
89595
  instructions: optionalString(body.instructions),
@@ -89300,8 +90612,8 @@ function normalizeExistingPath(value) {
89300
90612
  return null;
89301
90613
  }
89302
90614
  try {
89303
- const stat10 = statSync21(absolute);
89304
- return stat10.isDirectory() ? absolute : dirname37(absolute);
90615
+ const stat11 = statSync21(absolute);
90616
+ return stat11.isDirectory() ? absolute : dirname37(absolute);
89305
90617
  } catch {
89306
90618
  return null;
89307
90619
  }
@@ -90050,8 +91362,8 @@ async function resolveTrustedMaterialPath(work, materialId) {
90050
91362
  if (!pathInsideRoot(realPath, realRoot)) {
90051
91363
  return { ok: false, status: 403, error: "material path is outside its trusted root" };
90052
91364
  }
90053
- const stat10 = statSync21(realPath);
90054
- if (!stat10.isFile()) {
91365
+ const stat11 = statSync21(realPath);
91366
+ if (!stat11.isFile()) {
90055
91367
  return { ok: false, status: 415, error: "material is not a file" };
90056
91368
  }
90057
91369
  return { ok: true, material, realPath };
@@ -90065,12 +91377,12 @@ async function readWorkMaterialRaw(work, materialId) {
90065
91377
  return resolved;
90066
91378
  }
90067
91379
  try {
90068
- const stat10 = statSync21(resolved.realPath);
91380
+ const stat11 = statSync21(resolved.realPath);
90069
91381
  return {
90070
91382
  ok: true,
90071
91383
  realPath: resolved.realPath,
90072
91384
  mediaType: materialMediaType(resolved.realPath),
90073
- sizeBytes: stat10.size
91385
+ sizeBytes: stat11.size
90074
91386
  };
90075
91387
  } catch {
90076
91388
  return { ok: false, status: 404, error: "material could not be read" };
@@ -90100,8 +91412,8 @@ async function readWorkMaterialContent(work, materialId) {
90100
91412
  if (!pathInsideRoot(realPath, realRoot)) {
90101
91413
  return { ok: false, status: 403, error: "material path is outside its trusted root" };
90102
91414
  }
90103
- const stat10 = statSync21(realPath);
90104
- if (!stat10.isFile()) {
91415
+ const stat11 = statSync21(realPath);
91416
+ if (!stat11.isFile()) {
90105
91417
  return { ok: false, status: 415, error: "material is not a file" };
90106
91418
  }
90107
91419
  const buffer = readFileSync29(realPath);
@@ -90133,7 +91445,7 @@ async function readWorkMaterialContent(work, materialId) {
90133
91445
 
90134
91446
  // packages/web/server/plan-documents.ts
90135
91447
  import { createHash as createHash9 } from "crypto";
90136
- import { readdir as readdir4, readFile as readFile17, stat as stat10 } from "fs/promises";
91448
+ import { readdir as readdir5, readFile as readFile17, stat as stat11 } from "fs/promises";
90137
91449
  import { homedir as homedir49 } from "os";
90138
91450
  import { basename as basename25, isAbsolute as isAbsolute10, join as join65, relative as relative8, resolve as resolve27 } from "path";
90139
91451
  var HOME3 = homedir49();
@@ -90216,7 +91528,7 @@ async function walkMarkdownFiles(directory, remaining = { count: MAX_FILES_PER_R
90216
91528
  return [];
90217
91529
  let entries2;
90218
91530
  try {
90219
- entries2 = await readdir4(directory, { withFileTypes: true });
91531
+ entries2 = await readdir5(directory, { withFileTypes: true });
90220
91532
  } catch (error) {
90221
91533
  if (error.code === "ENOENT")
90222
91534
  return [];
@@ -90249,7 +91561,7 @@ async function discoverPlanFiles(root) {
90249
91561
  const fromPlanDirs = (await Promise.all(PLAN_SUBDIRECTORIES.map((subdir) => walkMarkdownFiles(join65(root.root, subdir))))).flat();
90250
91562
  let directPlanFiles = [];
90251
91563
  try {
90252
- const entries2 = await readdir4(root.root, { withFileTypes: true });
91564
+ const entries2 = await readdir5(root.root, { withFileTypes: true });
90253
91565
  directPlanFiles = entries2.filter((entry) => entry.isFile() && /\.plan\.md$/i.test(entry.name)).map((entry) => join65(root.root, entry.name));
90254
91566
  } catch {
90255
91567
  directPlanFiles = [];
@@ -90432,7 +91744,7 @@ function idForPlan(root, filePath) {
90432
91744
  return `plan-doc:${hash}`;
90433
91745
  }
90434
91746
  async function readPlanDocument(root, filePath) {
90435
- const fileStats = await stat10(filePath).catch(() => null);
91747
+ const fileStats = await stat11(filePath).catch(() => null);
90436
91748
  if (!fileStats || !fileStats.isFile() || fileStats.size > MAX_DOCUMENT_BYTES) {
90437
91749
  return null;
90438
91750
  }
@@ -92101,7 +93413,8 @@ function brokerCardAgentsForWeb(broker2) {
92101
93413
  }
92102
93414
  var AGENT_ATTENTION_TTL_MS2 = 1e4;
92103
93415
  var AGENT_BACKGROUND_REFRESH_DELAY_MS = 500;
92104
- var AGENT_BROKER_CONTEXT_TTL_MS = 5000;
93416
+ var AGENT_BROKER_CONTEXT_TTL_MS = 60000;
93417
+ var AGENT_BROKER_CONTEXT_COLD_BUDGET_MS = 75;
92105
93418
  var agentAttentionCache = null;
92106
93419
  var agentAttentionInFlight = null;
92107
93420
  function queryAgentAttentionIndex(broker2, capture = defaultCaptureTmuxPane) {
@@ -92151,8 +93464,12 @@ function createAgentBrokerContextReader() {
92151
93464
  return promise;
92152
93465
  };
92153
93466
  return () => {
92154
- if (!cache)
92155
- return refresh(0);
93467
+ if (!cache) {
93468
+ return Promise.race([
93469
+ refresh(0),
93470
+ delay2(AGENT_BROKER_CONTEXT_COLD_BUDGET_MS).then(() => null)
93471
+ ]);
93472
+ }
92156
93473
  if (Date.now() - cache.at >= AGENT_BROKER_CONTEXT_TTL_MS && !inFlight) {
92157
93474
  refresh(AGENT_BACKGROUND_REFRESH_DELAY_MS);
92158
93475
  }
@@ -92246,24 +93563,37 @@ function withPinnedMeshPeerAgents(agents, broker2, limit) {
92246
93563
  if (!localNodeId)
92247
93564
  return mostRecentAgents(agents, limit);
92248
93565
  const peerNodeIds = pinnedMeshPeerNodeIds(broker2);
92249
- const peerAgents = brokerCardAgentsForWeb(broker2).filter((agent) => agent.homeNodeId && peerNodeIds.has(agent.homeNodeId));
93566
+ const representativeCardByNode = new Map;
93567
+ for (const agent of Object.values(broker2.snapshot.agents ?? {})) {
93568
+ const homeNodeId = agent.homeNodeId;
93569
+ if (!homeNodeId || !peerNodeIds.has(homeNodeId) || !isBrokerAgentVisibleInWeb(agent))
93570
+ continue;
93571
+ const existing = representativeCardByNode.get(homeNodeId);
93572
+ if (!existing || (latestBrokerAgentTimestamp(agent, null) ?? 0) > (latestBrokerAgentTimestamp(existing, null) ?? 0)) {
93573
+ representativeCardByNode.set(homeNodeId, agent);
93574
+ }
93575
+ }
93576
+ 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
93577
  if (peerAgents.length === 0)
92251
93578
  return mostRecentAgents(agents, limit);
92252
93579
  const byId = new Map(agents.map((agent) => [agent.id, agent]));
92253
93580
  for (const peer of peerAgents) {
92254
- byId.set(peer.id, peer);
93581
+ if (!byId.has(peer.id))
93582
+ byId.set(peer.id, peer);
92255
93583
  }
92256
93584
  const merged = [...byId.values()];
92257
93585
  if (limit === undefined)
92258
93586
  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
- }
93587
+ const representatives = peerAgents.map((peer) => byId.get(peer.id) ?? peer).slice(0, limit);
93588
+ const included = new Set(representatives.map((agent) => agent.id));
93589
+ const limited = [...representatives];
93590
+ for (const agent of mostRecentAgents(merged, undefined)) {
93591
+ if (limited.length >= limit)
93592
+ break;
93593
+ if (included.add(agent.id))
93594
+ limited.push(agent);
92265
93595
  }
92266
- return limited;
93596
+ return mostRecentAgents(limited, undefined);
92267
93597
  }
92268
93598
  function agentListSummary(agent) {
92269
93599
  return {
@@ -92313,13 +93643,11 @@ function agentListSummary(agent) {
92313
93643
  createdAt: agent.createdAt
92314
93644
  };
92315
93645
  }
92316
- async function withAgentRepoKeys(agents) {
93646
+ function withAgentRepoKeys(agents) {
92317
93647
  const roots = agents.map((agent) => agent.projectRoot).filter((root) => Boolean(root));
92318
93648
  if (roots.length === 0)
92319
93649
  return agents;
92320
- const keys = await resolveRepoKeysByRoot(roots).catch(() => null);
92321
- if (!keys)
92322
- return agents;
93650
+ const keys = cachedRepoKeysByRoot(roots);
92323
93651
  return agents.map((agent) => {
92324
93652
  const repoKey = agent.projectRoot ? keys.get(agent.projectRoot) ?? null : null;
92325
93653
  return repoKey ? { ...agent, repoKey } : agent;
@@ -92403,11 +93731,11 @@ async function queryAgentsIncludingBrokerCards(limit, includeRichBrokerContext =
92403
93731
  const brokerById2 = new Map(brokerAgents2.map((agent) => [agent.id, agent]));
92404
93732
  const mergedAgents2 = agents.map((agent) => mergeBrokerAgentProjection(agent, brokerById2.get(agent.id)));
92405
93733
  const existingIds2 = new Set(mergedAgents2.map((agent) => agent.id));
92406
- const broker3 = await loadBrokerContext();
92407
- const roster2 = withPinnedMeshPeerAgents([
93734
+ const attention2 = includeAttention ? await queryAgentAttentionIndex(null, capture) : new Map;
93735
+ const roster2 = mostRecentAgents(applyAgentAttention([
92408
93736
  ...mergedAgents2,
92409
93737
  ...brokerAgents2.filter((agent) => !existingIds2.has(agent.id))
92410
- ], broker3, limit);
93738
+ ], attention2), limit);
92411
93739
  return withAgentRepoKeys(roster2);
92412
93740
  }
92413
93741
  const broker2 = await loadBrokerContext();
@@ -94039,13 +95367,11 @@ function dedupeHudRunnerProjects(projects) {
94039
95367
  return result;
94040
95368
  }
94041
95369
  async function buildHudRunnerOptions(currentDirectory, input = {}) {
94042
- const [settingsResult, catalogResult, runtimeCatalogResult] = await Promise.allSettled([
95370
+ const [settingsResult, runtimeCatalogResult] = await Promise.allSettled([
94043
95371
  readOpenScoutSettings({ currentDirectory }),
94044
- loadHarnessCatalogSnapshot(),
94045
95372
  loadBrokerRuntimeCatalog()
94046
95373
  ]);
94047
95374
  const settings = settingsResult.status === "fulfilled" ? settingsResult.value : null;
94048
- const catalog = catalogResult.status === "fulfilled" ? catalogResult.value : null;
94049
95375
  const liveRuntimeCatalog = runtimeCatalogResult.status === "fulfilled" ? runtimeCatalogResult.value : null;
94050
95376
  const runtimeCatalog = liveRuntimeCatalog?.catalog ?? SCOUT_RUNTIME_CATALOG;
94051
95377
  const allAgents = queryAgents(50);
@@ -94058,19 +95384,17 @@ async function buildHudRunnerOptions(currentDirectory, input = {}) {
94058
95384
  const configuredDefaultHarness = settings?.agents.defaultHarness?.trim() ?? "";
94059
95385
  const defaultHarness = isScoutRuntimeHarnessEnabled(configuredDefaultHarness, runtimeCatalog) ? configuredDefaultHarness : scoutRuntimeDefaultHarness(runtimeCatalog) ?? "claude";
94060
95386
  const harnessesById = new Map;
94061
- const readinessByHarness = new Map((catalog?.entries ?? []).map((entry) => [entry.harness, entry]));
94062
95387
  for (const entry of runtimeCatalog.harnesses) {
94063
95388
  if (!entry.enabled || entry.listed === false)
94064
95389
  continue;
94065
- const readiness = readinessByHarness.get(entry.id);
94066
95390
  harnessesById.set(entry.id, {
94067
95391
  id: entry.id,
94068
95392
  name: entry.id,
94069
95393
  label: entry.label,
94070
95394
  description: null,
94071
- state: readiness?.readinessReport.state ?? null,
94072
- ready: readiness?.readinessReport.ready ?? null,
94073
- detail: readiness?.readinessReport.detail ?? null
95395
+ state: null,
95396
+ ready: null,
95397
+ detail: null
94074
95398
  });
94075
95399
  }
94076
95400
  const projectOptions = allAgents.map((agent) => agent.projectRoot ?? agent.cwd).filter((root) => typeof root === "string" && root.trim().length > 0).map((root) => {
@@ -94266,6 +95590,7 @@ function fallbackOpenScoutHostInfo(options, currentDirectory) {
94266
95590
  async function createOpenScoutWebServer(options) {
94267
95591
  const shellTtl = options.shellStateCacheTtlMs ?? 15000;
94268
95592
  const currentDirectory = options.currentDirectory;
95593
+ const lanAccessScope = options.lanAccessScope ?? resolveScoutWebLanAccessScope(process.env);
94269
95594
  const pendingPairRequests = createPendingPairRequestStore({
94270
95595
  statePath: pairRequestStatePath(localConfigHome())
94271
95596
  });
@@ -94281,8 +95606,28 @@ async function createOpenScoutWebServer(options) {
94281
95606
  startGlobalHeuristicsWatcher();
94282
95607
  }
94283
95608
  const app = new Hono2;
95609
+ app.use("*", async (c, next) => {
95610
+ const peerAddress = (options.resolvePeerAddress ?? resolveScoutRequestPeerAddress)(c);
95611
+ if (!isScoutWebRequestAllowedFromPeer(c.req.raw, peerAddress, lanAccessScope)) {
95612
+ return c.text("Not Found", 404);
95613
+ }
95614
+ await next();
95615
+ });
94284
95616
  installHttpsEdgeSecurityHeaders(app, options.publicOrigin);
94285
95617
  const shellStateCache = createCachedSnapshot(loadOpenScoutWebShellState, shellTtl);
95618
+ const readAgentConfigurationSnapshot = coalesce(() => buildAgentConfigurationSnapshot(currentDirectory), 30000);
95619
+ const runnerOptionsReaders = new Map;
95620
+ const readRunnerOptions = (scope, projectRoot) => {
95621
+ const key = `${scope}\x00${projectRoot}`;
95622
+ let read = runnerOptionsReaders.get(key);
95623
+ if (!read) {
95624
+ if (runnerOptionsReaders.size >= 32)
95625
+ runnerOptionsReaders.clear();
95626
+ read = coalesce(() => buildHudRunnerOptions(currentDirectory, { scope, projectRoot }), 30000);
95627
+ runnerOptionsReaders.set(key, read);
95628
+ }
95629
+ return read();
95630
+ };
94286
95631
  const agentBrokerContextReader = createAgentBrokerContextReader();
94287
95632
  const agentBrokerHomeReader = createAgentBrokerHomeReader();
94288
95633
  const dispatchBrokerSnapshotCache = createCachedSnapshot(async () => {
@@ -94339,7 +95684,8 @@ async function createOpenScoutWebServer(options) {
94339
95684
  installScoutApiMiddleware(app, "openscout-web api", {
94340
95685
  trustedHosts: options.trustedHosts,
94341
95686
  trustedOrigins: options.trustedOrigins,
94342
- authToken: options.authToken
95687
+ authToken: options.authToken,
95688
+ resolvePeerAddress: options.resolvePeerAddress
94343
95689
  });
94344
95690
  mountScoutDeckSurfaceRoutes(app, {
94345
95691
  currentDirectory,
@@ -94666,33 +96012,68 @@ async function createOpenScoutWebServer(options) {
94666
96012
  const route = c.req.query("route")?.trim().toLowerCase() ?? null;
94667
96013
  const token = c.req.query("token")?.trim() || null;
94668
96014
  const wantsJson = (c.req.header("accept") ?? "").includes("application/json");
94669
- const state = await loadPairingState(currentDirectory, true);
96015
+ const peerAddress = (options.resolvePeerAddress ?? resolveScoutRequestPeerAddress)(c);
96016
+ const xff = c.req.header("x-forwarded-for");
96017
+ const forwardedIp = (xff ? xff.split(",").at(-1)?.trim() : null) || c.req.header("x-real-ip")?.trim() || null;
96018
+ const loopbackPeer = Boolean(peerAddress && isLoopbackScoutAddress(peerAddress));
96019
+ const sameMacRequest = isSameMacScoutRequest(c.req.raw, peerAddress);
96020
+ const relayedLanRequest = Boolean(loopbackPeer && c.req.header(SCOUT_PAIR_RELAY_HEADER) === "1");
96021
+ const directLanRequest = Boolean(peerAddress && !isLoopbackScoutAddress(peerAddress));
96022
+ const proxiedLanRequest = Boolean(loopbackPeer && !relayedLanRequest && !sameMacRequest);
96023
+ const unidentifiedPairingIngress = !peerAddress;
96024
+ const requiresApproval = relayedLanRequest || directLanRequest || proxiedLanRequest || unidentifiedPairingIngress;
96025
+ const requesterApp = parseScoutPairClient(c.req.header(SCOUT_PAIR_CLIENT_HEADER), c.req.header("user-agent"));
96026
+ const state = await loadPairingState(currentDirectory, false);
94670
96027
  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
96028
  if (token) {
94678
- const req2 = pendingPairRequests.get(token);
94679
- if (!req2) {
96029
+ const request2 = pendingPairRequests.get(token);
96030
+ if (!request2) {
94680
96031
  return wantsJson ? c.json({ status: "expired", token }, 410) : c.text("Pairing request expired.", 410);
94681
96032
  }
94682
- if (req2.status === "denied") {
96033
+ if (request2.status === "denied") {
94683
96034
  return wantsJson ? c.json({ status: "denied", token }, 403) : c.text("Pairing request was denied.", 403);
94684
96035
  }
96036
+ if (location && request2.status === "approved") {
96037
+ if (!pendingPairRequests.fulfill(token, "approved")) {
96038
+ const current = pendingPairRequests.get(token);
96039
+ if (current?.status === "approved" || current?.status === "pending") {
96040
+ return c.json({ status: current.status, token, pollAfterMs: 1200 }, 202);
96041
+ }
96042
+ if (current?.status === "denied") {
96043
+ return wantsJson ? c.json({ status: "denied", token }, 403) : c.text("Pairing request was denied.", 403);
96044
+ }
96045
+ return wantsJson ? c.json({ status: "expired", token }, 410) : c.text("Pairing request expired.", 410);
96046
+ }
96047
+ return c.redirect(location, 302);
96048
+ }
94685
96049
  pendingPairRequests.touch(token);
94686
- return c.json({ status: req2.status, token, pollAfterMs: 1200 }, 202);
96050
+ return c.json({ status: request2.status, token, pollAfterMs: 1200 }, 202);
94687
96051
  }
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);
96052
+ if (location && !requiresApproval) {
96053
+ return c.redirect(location, 302);
96054
+ }
96055
+ const requesterIp = unidentifiedPairingIngress ? null : relayedLanRequest || proxiedLanRequest ? forwardedIp : directLanRequest ? peerAddress ?? null : forwardedIp;
96056
+ if (requiresApproval && !requesterIp) {
96057
+ return wantsJson ? c.json({ status: "unavailable" }, 503) : c.text("Unable to identify pairing requester.", 503);
96058
+ }
96059
+ let request;
96060
+ try {
96061
+ request = pendingPairRequests.create({
96062
+ requesterIp,
96063
+ requesterLabel: c.req.header("x-scout-device-name")?.trim() || null,
96064
+ requesterApp,
96065
+ route
96066
+ });
96067
+ } catch (error) {
96068
+ if (!(error instanceof PairRequestCapacityError))
96069
+ throw error;
96070
+ c.header("retry-after", String(Math.ceil(error.retryAfterMs / 1000)));
96071
+ return wantsJson ? c.json({ status: "busy", retryAfterMs: error.retryAfterMs }, 429) : c.text("Too many pairing requests. Try again shortly.", 429);
96072
+ }
96073
+ if (request.status === "denied") {
96074
+ return wantsJson ? c.json({ status: "denied", token: request.token }, 403) : c.text("Pairing request was denied.", 403);
96075
+ }
96076
+ 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
96077
  });
94697
96078
  app.get("/api/notifications", (c) => {
94698
96079
  const rawType = c.req.query("type") ?? "";
@@ -94701,7 +96082,7 @@ async function createOpenScoutWebServer(options) {
94701
96082
  const notifications = includePairingRequests ? pendingPairRequests.list().filter((request) => request.status === "pending").map((request) => ({
94702
96083
  id: `pairing_request:${request.token}`,
94703
96084
  type: "pairing_request",
94704
- title: `${request.requesterLabel?.trim() || "A device"} wants to pair`,
96085
+ title: `${pairRequesterDisplay(request)} wants to pair`,
94705
96086
  body: `On your network${request.requesterIp ? ` \xB7 ${request.requesterIp}` : ""}.`,
94706
96087
  createdAt: request.createdAt,
94707
96088
  updatedAt: request.updatedAt,
@@ -94791,14 +96172,11 @@ async function createOpenScoutWebServer(options) {
94791
96172
  });
94792
96173
  app.get("/api/shell-state", async (c) => c.json(await shellStateCache.get()));
94793
96174
  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)));
96175
+ app.get("/api/agent-config/snapshot", async (c) => c.json(await readAgentConfigurationSnapshot()));
94795
96176
  app.get("/api/runner/options", async (c) => {
94796
96177
  const requestedScope = c.req.query("scope");
94797
96178
  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
- }));
96179
+ return c.json(await readRunnerOptions(scope, c.req.query("projectRoot") || currentDirectory));
94802
96180
  });
94803
96181
  const agentsResponseCache = new Map;
94804
96182
  const readAgentsResponse = (limit, summary, attentionRequested) => {
@@ -94807,7 +96185,7 @@ async function createOpenScoutWebServer(options) {
94807
96185
  if (!entry) {
94808
96186
  if (agentsResponseCache.size >= 32)
94809
96187
  agentsResponseCache.clear();
94810
- entry = coalesce(() => queryAgentsIncludingBrokerCards(limit, !summary || attentionRequested, attentionRequested, options.captureTmuxPane ?? defaultCaptureTmuxPane, agentBrokerContextReader, agentBrokerHomeReader), 3000);
96188
+ entry = coalesce(() => queryAgentsIncludingBrokerCards(limit, !summary, attentionRequested, options.captureTmuxPane ?? defaultCaptureTmuxPane, agentBrokerContextReader, agentBrokerHomeReader), 3000);
94811
96189
  agentsResponseCache.set(key, entry);
94812
96190
  }
94813
96191
  return entry();
@@ -95580,7 +96958,12 @@ async function createOpenScoutWebServer(options) {
95580
96958
  let messages2;
95581
96959
  try {
95582
96960
  parseMessageHistoryCursor(beforeMessageId);
95583
- const brokerMessages = cId ? await getScoutConversationMessages(cId, limit, beforeMessageId) : null;
96961
+ const brokerContext = cId ? await loadScoutBrokerContext(undefined, {
96962
+ scope: "conversations",
96963
+ waitForInitial: false,
96964
+ initialRefreshDelayMs: 750
96965
+ }).catch(() => null) : null;
96966
+ const brokerMessages = cId ? await getScoutConversationMessages(cId, limit, beforeMessageId, brokerContext) : null;
95584
96967
  if (cId && brokerMessages && brokerMessages.length > 0 && brokerMessages.length < limit) {
95585
96968
  const oldest = brokerMessages[0];
95586
96969
  const seen = new Set(brokerMessages.map((message) => message.id));
@@ -95953,7 +97336,11 @@ async function createOpenScoutWebServer(options) {
95953
97336
  if (flights.length > 0) {
95954
97337
  return c.json(flights);
95955
97338
  }
95956
- const broker2 = await loadScoutBrokerContext().catch(() => null);
97339
+ const broker2 = await loadScoutBrokerContext(undefined, {
97340
+ scope: "conversations",
97341
+ waitForInitial: false,
97342
+ initialRefreshDelayMs: 750
97343
+ }).catch(() => null);
95957
97344
  return c.json(broker2 ? queryBrokerFlightsForWeb(broker2, query) : flights);
95958
97345
  });
95959
97346
  app.get("/api/follow", (c) => {
@@ -95970,7 +97357,7 @@ async function createOpenScoutWebServer(options) {
95970
97357
  targetAgentId: c.req.query("targetAgentId") || undefined
95971
97358
  }));
95972
97359
  });
95973
- const readCommsList = async (c) => {
97360
+ const readCommsList = async (c, options2 = {}) => {
95974
97361
  const rawLimit = Number(c.req.query("limit"));
95975
97362
  const rawKinds = c.req.query("kinds")?.trim();
95976
97363
  const filters = {
@@ -95979,11 +97366,43 @@ async function createOpenScoutWebServer(options) {
95979
97366
  kinds: parseConversationKinds(rawKinds),
95980
97367
  machineId: c.req.query("machineId") || undefined
95981
97368
  };
95982
- const broker2 = await loadScoutBrokerContext(undefined, filters.machineId ? {} : { scope: "conversations" }).catch(() => null);
97369
+ const preferredProjection = options2.preferMaterialized && !filters.machineId ? await readScoutConversationProjection(160) : undefined;
97370
+ if (preferredProjection?.items.some((item) => item.entityKind === "scout_conversation")) {
97371
+ return {
97372
+ items: provisionalScoutConversations(preferredProjection, filters),
97373
+ listReady: true
97374
+ };
97375
+ }
97376
+ const broker2 = await loadScoutBrokerContext(undefined, filters.machineId ? {} : {
97377
+ scope: "conversations",
97378
+ waitForInitial: false,
97379
+ initialRefreshDelayMs: 750
97380
+ }).catch(() => null);
95983
97381
  if (broker2) {
95984
97382
  const items = await getScoutConversations(filters, broker2);
95985
97383
  return { items, listReady: true };
95986
97384
  }
97385
+ if (!filters.machineId) {
97386
+ const launchProjection = preferredProjection === undefined ? await readScoutConversationProjection(Math.min(160, filters.limit ?? 160)) : preferredProjection;
97387
+ if (launchProjection?.items.some((item) => item.entityKind === "scout_conversation")) {
97388
+ return {
97389
+ items: provisionalScoutConversations(launchProjection, filters),
97390
+ listReady: true
97391
+ };
97392
+ }
97393
+ const projected = querySessions(250);
97394
+ if (projected.length > 0) {
97395
+ const query = filters.query?.trim().toLocaleLowerCase() ?? "";
97396
+ const kinds = filters.kinds ? new Set(filters.kinds) : null;
97397
+ const items = projected.filter((item) => !kinds || kinds.has(item.kind)).filter((item) => !query || [
97398
+ item.title,
97399
+ item.alias,
97400
+ item.agentName,
97401
+ item.preview
97402
+ ].some((value) => value?.toLocaleLowerCase().includes(query))).slice(0, filters.limit ?? projected.length);
97403
+ return { items, listReady: true };
97404
+ }
97405
+ }
95987
97406
  const health = await readScoutBrokerHealth(resolveScoutBrokerUrl(), {
95988
97407
  signal: AbortSignal.timeout(2000)
95989
97408
  });
@@ -95996,7 +97415,7 @@ async function createOpenScoutWebServer(options) {
95996
97415
  retryable: true
95997
97416
  }, 503);
95998
97417
  app.get("/api/comms", async (c) => {
95999
- const { items, listReady } = await readCommsList(c);
97418
+ const { items, listReady } = await readCommsList(c, { preferMaterialized: true });
96000
97419
  if (!listReady) {
96001
97420
  return unavailableConversationList(c);
96002
97421
  }
@@ -96176,10 +97595,10 @@ async function createOpenScoutWebServer(options) {
96176
97595
  session: conversation
96177
97596
  });
96178
97597
  }
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
97598
  const payload = await loadSessionRefObservePayload(refId);
96182
97599
  if (payload) {
97600
+ const parsedRef = payload.agentId ? parseSessionRouteRef(refId) : null;
97601
+ const harnessSession = parsedRef ? querySessions(200).find((session3) => sessionHarnessMatches(parsedRef.harness, session3.harness) && parseSessionRouteRef(session3.harnessSessionId)?.refId === parsedRef.refId) : null;
96183
97602
  return c.json({
96184
97603
  kind: "observe",
96185
97604
  refId,
@@ -96204,7 +97623,12 @@ async function createOpenScoutWebServer(options) {
96204
97623
  return c.json({ error: "chatId must be an opaque chat id" }, 400);
96205
97624
  }
96206
97625
  const session3 = querySessionById(resolvedChatId);
96207
- const conversation = await getScoutConversationById(resolvedChatId);
97626
+ const broker2 = session3 ? await loadScoutBrokerContext(undefined, {
97627
+ scope: "conversations",
97628
+ waitForInitial: false,
97629
+ initialRefreshDelayMs: 750
97630
+ }).catch(() => null) : undefined;
97631
+ const conversation = broker2 === null ? null : broker2 ? (await getScoutConversations({ conversationId: resolvedChatId, limit: 1 }, broker2))[0] ?? null : await getScoutConversationById(resolvedChatId);
96208
97632
  if (session3 && conversation) {
96209
97633
  return c.json({ ...session3, ...conversation });
96210
97634
  }
@@ -97093,6 +98517,8 @@ async function createOpenScoutWebServer(options) {
97093
98517
  const limitParam = parseOptionalPositiveInt3(c.req.query("limit"), 500) ?? 500;
97094
98518
  const includeTranscripts = c.req.query("transcripts") === "true" || c.req.query("transcripts") === "1";
97095
98519
  const mode = c.req.query("mode") === "assistant-replies" ? "assistant-replies" : null;
98520
+ const requestedWindowMs = parseOptionalPositiveInt3(c.req.query("windowMs"));
98521
+ const windowMs = requestedWindowMs === undefined ? undefined : Math.min(requestedWindowMs, 86400000);
97096
98522
  const url = new URL(scoutBrokerPaths.v1.tailRecent, resolveScoutBrokerUrl());
97097
98523
  url.searchParams.set("limit", String(limitParam));
97098
98524
  if (includeTranscripts) {
@@ -97101,7 +98527,10 @@ async function createOpenScoutWebServer(options) {
97101
98527
  if (mode) {
97102
98528
  url.searchParams.set("mode", mode);
97103
98529
  }
97104
- const cacheKey = `limit=${limitParam};transcripts=${includeTranscripts ? "1" : "0"};mode=${mode ?? "all"}`;
98530
+ if (windowMs !== undefined) {
98531
+ url.searchParams.set("windowMs", String(windowMs));
98532
+ }
98533
+ const cacheKey = `limit=${limitParam};transcripts=${includeTranscripts ? "1" : "0"};mode=${mode ?? "all"};window=${windowMs ?? "all"}`;
97105
98534
  let cache = tailRecentCaches.get(cacheKey);
97106
98535
  if (!cache) {
97107
98536
  cache = createBrokerJsonCache();
@@ -97811,6 +99240,7 @@ function startProcessParentWatchdog(expectedParentPidRaw, options = {}) {
97811
99240
  process.title = "scout-web";
97812
99241
  var port = Number.parseInt(process.env.OPENSCOUT_WEB_PORT ?? process.env.SCOUT_WEB_PORT ?? String(resolveWebPort()), 10);
97813
99242
  var hostname2 = resolveScoutWebBindHost(process.env);
99243
+ var lanAccessScope = resolveScoutWebLanAccessScope(process.env);
97814
99244
  var webAuthToken = resolveWebAuthToken(process.env);
97815
99245
  var currentDirectory = resolveOpenScoutSetupContextRoot({
97816
99246
  env: process.env,
@@ -97820,6 +99250,7 @@ var shellStateCacheTtlMs = Number.parseInt(process.env.OPENSCOUT_WEB_SHELL_CACHE
97820
99250
  var providerTelemetryBootstrapEnabled = process.env.OPENSCOUT_WEB_PROVIDER_TELEMETRY_BOOTSTRAP?.trim() === "1";
97821
99251
  var startupWarmupEnabled = process.env.OPENSCOUT_WEB_STARTUP_WARMUP?.trim() === "1";
97822
99252
  var routes = resolveOpenScoutWebRoutes(process.env);
99253
+ var requestPeerAddresses = createScoutRequestPeerAddressRegistry();
97823
99254
  function resolveStaticRoot2() {
97824
99255
  if (process.env.OPENSCOUT_WEB_STATIC_ROOT?.trim()) {
97825
99256
  return process.env.OPENSCOUT_WEB_STATIC_ROOT.trim();
@@ -97909,6 +99340,7 @@ var web = await createOpenScoutWebServer({
97909
99340
  trustedHosts: applicationServerIdentity.trustedHosts,
97910
99341
  trustedOrigins: applicationServerIdentity.trustedOrigins,
97911
99342
  authToken: webAuthToken,
99343
+ resolvePeerAddress: requestPeerAddresses.resolve,
97912
99344
  runTerminalCommand: async (request) => {
97913
99345
  const relay = await ensureTerminalRelay();
97914
99346
  if (!relay) {
@@ -97948,6 +99380,9 @@ try {
97948
99380
  async fetch(req, server2) {
97949
99381
  const url = new URL(req.url);
97950
99382
  const peerAddress = server2.requestIP(req)?.address;
99383
+ if (!isScoutWebRequestAllowedFromPeer(req, peerAddress, lanAccessScope)) {
99384
+ return new Response("Not Found", { status: 404 });
99385
+ }
97951
99386
  if (req.headers.get("upgrade")?.toLowerCase() === "websocket") {
97952
99387
  let upstreamUrl = null;
97953
99388
  const guardsOrigin = url.pathname === routes.terminalRelayPath || url.pathname === routes.tailStreamPath || url.pathname === routes.eventsStreamPath;
@@ -98003,6 +99438,7 @@ try {
98003
99438
  return headers2;
98004
99439
  })()
98005
99440
  }) : req;
99441
+ requestPeerAddresses.remember(honoRequest, peerAddress);
98006
99442
  const response = await honoFetch(honoRequest, server2);
98007
99443
  if (!bootstrapEligible)
98008
99444
  return response;