very-happy-cli 0.2.178 → 0.2.180

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 (217) hide show
  1. package/dist/{AcpBackend-DCQXHRzT.mjs → AcpBackend-B8Zi30Zh.mjs} +135 -16
  2. package/dist/{AcpBackend-CDkAQXYf.cjs → AcpBackend-DfRDtMcG.cjs} +135 -16
  3. package/dist/{AcpSessionManager-DckmUZza.cjs → AcpSessionManager-CASKOnOj.cjs} +38 -4
  4. package/dist/{AcpSessionManager-BXrQcqwC.mjs → AcpSessionManager-i8Rab0xT.mjs} +38 -4
  5. package/dist/{BasePermissionHandler-BVlrEhIi.mjs → BasePermissionHandler-26fKbRCm.mjs} +5 -2
  6. package/dist/{BasePermissionHandler-DyLmKKtO.cjs → BasePermissionHandler-BlM_tfej.cjs} +5 -2
  7. package/dist/acpSessionResume-B9l54P-X.mjs +67 -0
  8. package/dist/acpSessionResume-CR5pmgVo.cjs +74 -0
  9. package/dist/{agentHome-CEp9RMyK.cjs → agentHome-C7Sb7-Tz.cjs} +4 -4
  10. package/dist/{agentHome-C1xi37as.mjs → agentHome-CmGy0NWv.mjs} +4 -4
  11. package/dist/{api-JANhFdZV.cjs → api-DM7b1OKj.cjs} +38 -262
  12. package/dist/{api-Dib0dmqC.mjs → api-Hov8p7CA.mjs} +32 -256
  13. package/dist/{apiMachine-DNTJidYH.mjs → apiMachine-CwGEhQDT.mjs} +62 -15
  14. package/dist/{apiMachine-QhK-sKcr.cjs → apiMachine-DkGRj-Ai.cjs} +64 -17
  15. package/dist/{attachmentContent-bpQTBJ0y.cjs → attachmentContent-CEm13_gF.cjs} +4 -4
  16. package/dist/{attachmentContent-DkkdpSSJ.mjs → attachmentContent-CIn4iPEk.mjs} +2 -2
  17. package/dist/{auth-BRjd2esb.mjs → auth-BG1DY0gj.mjs} +5 -5
  18. package/dist/{auth-DgIV0Cvt.cjs → auth-CBXnm-Mj.cjs} +5 -5
  19. package/dist/{auth-omWDtgR8.cjs → auth-Dawj7pP5.cjs} +8 -8
  20. package/dist/{auth-DNct7VUi.mjs → auth-rggxMpEE.mjs} +8 -8
  21. package/dist/{auto-DKGFOJBo.mjs → auto-CXXLXGeL.mjs} +19 -18
  22. package/dist/{auto-CI16FZmX.cjs → auto-CbYOfoPS.cjs} +19 -18
  23. package/dist/{backgroundTaskActivity-BVwJBl02.mjs → backgroundTaskActivity-8PdPxrvy.mjs} +1 -1
  24. package/dist/{bootstrap-DCQ87Aan.cjs → bootstrap-DoA7okld.cjs} +2 -2
  25. package/dist/{bootstrap-BUagGC_-.mjs → bootstrap-Dt5I1SOl.mjs} +2 -2
  26. package/dist/{browser-Dj_DYQEh.mjs → browser-DUNetFT4.mjs} +1 -1
  27. package/dist/{browser-CSKhc0Bl.cjs → browser-DZ6qRtfH.cjs} +1 -1
  28. package/dist/{claudeCliPath-U4HHmV4r.cjs → claudeCliPath-GaP3UUz1.cjs} +1 -1
  29. package/dist/{claudeLocal-CdIVnOZp.cjs → claudeLocal--DNyMOSv.cjs} +8 -7
  30. package/dist/{claudeLocal-CCCvAeMd.mjs → claudeLocal-OHWQigCl.mjs} +6 -5
  31. package/dist/{claudeLocalLauncher-BmrpaRYJ.mjs → claudeLocalLauncher-BStvfa7M.mjs} +11 -10
  32. package/dist/{claudeLocalLauncher-21-xh5gc.cjs → claudeLocalLauncher-BhvPbcyH.cjs} +13 -12
  33. package/dist/{claudeSessionFork-kYCP1IJB.cjs → claudeSessionFork-Be16vzGc.cjs} +1 -1
  34. package/dist/{claudeSessionFork-DvxMKAmY.mjs → claudeSessionFork-GAVWhiKG.mjs} +1 -1
  35. package/dist/{client-C4zKGjiJ.cjs → client-BWjaHKtj.cjs} +2 -2
  36. package/dist/client-Ca5Pcgy_.mjs +71 -0
  37. package/dist/{client-CnlkkrZO.mjs → client-CnB7kRCT.mjs} +2 -2
  38. package/dist/{client-BTX9hsbr.mjs → client-D4uPb1uu.mjs} +3 -3
  39. package/dist/client-DJplzPjD.cjs +76 -0
  40. package/dist/{client-Dnza1hgN.cjs → client-D_loZ9IK.cjs} +3 -3
  41. package/dist/codex/happyMcpStdioBridge.cjs +24 -15
  42. package/dist/codex/happyMcpStdioBridge.mjs +25 -16
  43. package/dist/{codexAppServerClient-DvkpQgyv.mjs → codexAppServerClient-CO4G6OS4.mjs} +3 -3
  44. package/dist/{codexAppServerClient-CRlBWGn3.cjs → codexAppServerClient-PE-12AsW.cjs} +3 -3
  45. package/dist/{codexCommand-DzxIWXIT.cjs → codexCommand-BNoD1t1C.cjs} +127 -116
  46. package/dist/{codexCommand-BxjogBzU.mjs → codexCommand-B_xUH79E.mjs} +77 -66
  47. package/dist/{codexTermination-C28FzlLb.cjs → codexTermination-BmBbizFK.cjs} +7 -2
  48. package/dist/{codexTermination-ySDNMEhd.mjs → codexTermination-Dx48ma6e.mjs} +7 -2
  49. package/dist/{config-BFB5uJ4t.cjs → config-CtwEUxUP.cjs} +26 -26
  50. package/dist/{config-DwfEpFr3.mjs → config-DSu0Blos.mjs} +3 -3
  51. package/dist/{configuration-4TGMrjul.cjs → configuration-CYO-kPOS.cjs} +1 -1
  52. package/dist/{configuration-DVrb61aY.mjs → configuration-PxzEJumr.mjs} +1 -1
  53. package/dist/{connect-C7FPY0PI.mjs → connect-BQrg3TB6.mjs} +15 -15
  54. package/dist/{connect-QH2VJGBG.cjs → connect-Dq7wndzR.cjs} +28 -28
  55. package/dist/{context-JoKu0V54.cjs → context-6TP-iz_c.cjs} +1 -1
  56. package/dist/{context-B3XPVJlj.mjs → context-CJGRRGWW.mjs} +1 -1
  57. package/dist/{controlClient-BTaZlbC6.cjs → controlClient-Bqv5v5OM.cjs} +3 -3
  58. package/dist/{controlClient-BYKyGyxj.mjs → controlClient-DoeYovwP.mjs} +4 -4
  59. package/dist/{createSessionMetadata-8Fuj2-IV.mjs → createSessionMetadata-DDIChCx8.mjs} +2 -2
  60. package/dist/{createSessionMetadata-B2NynaWZ.cjs → createSessionMetadata-xz767pcG.cjs} +3 -3
  61. package/dist/{defaultPermissionMode-BEWCqJU-.cjs → defaultPermissionMode-PkZGsQDk.cjs} +2 -2
  62. package/dist/{defaultPermissionMode-B8kJG9uI.mjs → defaultPermissionMode-i5KzdK09.mjs} +2 -2
  63. package/dist/{detectCLI-DR9ZaN6B.cjs → detectCLI-BuYR_Qxe.cjs} +4 -4
  64. package/dist/{deterministicJson-C5ZwZ7hu.mjs → deterministicJson-CTEJDqtS.mjs} +217 -12
  65. package/dist/{deterministicJson-BjNQsAjO.cjs → deterministicJson-xqA6DaOm.cjs} +222 -16
  66. package/dist/{doctor-BshRnjLi.cjs → doctor-BrgSlfXn.cjs} +9 -9
  67. package/dist/{doctor-0BiGwJXk.mjs → doctor-IctjVh9T.mjs} +8 -8
  68. package/dist/{doctorReadiness-BCZGcN0a.cjs → doctorReadiness-Dl6yRQCy.cjs} +1 -1
  69. package/dist/{downloadTarget-BN5vZt3K.cjs → downloadTarget-CEb9che-.cjs} +6 -6
  70. package/dist/{downloadTarget-DJPwAZlu.mjs → downloadTarget-jbOz1egC.mjs} +6 -6
  71. package/dist/{ensureDaemonRunning-C5QfToCe.cjs → ensureDaemonRunning-D-HyjGIX.cjs} +6 -6
  72. package/dist/{ensureDaemonRunning-CSmb_mqt.mjs → ensureDaemonRunning-b1e8LCSM.mjs} +5 -5
  73. package/dist/{handleResumeCommand-BwnUZmbo.cjs → handleResumeCommand-BvWVuRwH.cjs} +23 -7
  74. package/dist/{handleResumeCommand-CgjpsdDe.mjs → handleResumeCommand-DpaZvm2c.mjs} +22 -6
  75. package/dist/{index-piJPsTsm.cjs → index-B2p2Mut7.cjs} +259 -53
  76. package/dist/{index-BehVbAsA.mjs → index-BUNnpGFF.mjs} +256 -50
  77. package/dist/{index-Bvldtdny.mjs → index-Bsvbgh9e.mjs} +4 -4
  78. package/dist/{index-BYOZ_8WZ.cjs → index-C64Ufj4D.cjs} +4 -4
  79. package/dist/{index-Clqlu17M.mjs → index-C6Vn5Eac.mjs} +155 -34
  80. package/dist/index-CMf5VbzG.mjs +18 -0
  81. package/dist/index-DmTlDRSL.cjs +24 -0
  82. package/dist/{index-_l1Sdmbi.cjs → index-Dv0Q48P3.cjs} +156 -33
  83. package/dist/{index-BRClDe07.cjs → index-F2UejqYs.cjs} +3 -3
  84. package/dist/{index-BYgfern_.mjs → index-wCbtF4BK.mjs} +3 -3
  85. package/dist/index.cjs +2 -2
  86. package/dist/index.mjs +2 -2
  87. package/dist/{install-BdvKiOQw.mjs → install-BX3lv3NO.mjs} +1 -1
  88. package/dist/{install-Cf6AFSWC.cjs → install-BXvf2E3o.cjs} +1 -1
  89. package/dist/{install-TO4taKW7.cjs → install-C0ANcEAW.cjs} +7 -7
  90. package/dist/{install-fYWHrPcC.mjs → install-CdzSV7qO.mjs} +3 -3
  91. package/dist/{installPiTools-BKnXjrVu.cjs → installPiTools-CL_axh_6.cjs} +1 -1
  92. package/dist/{installTerminalHooks-BQZbvxsZ.cjs → installTerminalHooks-BETLKAtd.cjs} +3 -3
  93. package/dist/lib.cjs +14 -14
  94. package/dist/lib.d.cts +239 -2
  95. package/dist/lib.d.mts +239 -2
  96. package/dist/lib.mjs +14 -14
  97. package/dist/{lidState-BeOwj6eW.cjs → lidState-DWGnm2H4.cjs} +1 -1
  98. package/dist/{lidState-DNMlIhX0.mjs → lidState-bBrmaQd2.mjs} +1 -1
  99. package/dist/{localHappyAgentAuth-Br-mgogg.cjs → localHappyAgentAuth-DZHQnIW9.cjs} +1 -1
  100. package/dist/{localHappyAgentAuth-BezKSFDi.mjs → localHappyAgentAuth-Dn1jYDpt.mjs} +1 -1
  101. package/dist/{logger--vAVtXB_.mjs → logger-BUngm5Xg.mjs} +2 -2
  102. package/dist/{logger-DXTWlPzP.cjs → logger-CphW5nUE.cjs} +2 -2
  103. package/dist/machineMetadata-BQm9YTcI.cjs +79 -0
  104. package/dist/machineMetadata-BXN_bKU3.mjs +68 -0
  105. package/dist/{main-DoMFJ2Cl.cjs → main-B2UV51nK.cjs} +112 -132
  106. package/dist/{main-CshDb5-1.mjs → main-DGkdYfEq.mjs} +106 -126
  107. package/dist/{mcp-pNc1EbQf.cjs → mcp-BFvAEIRz.cjs} +15 -15
  108. package/dist/{mcp-DKT_lrvQ.mjs → mcp-CbTtbs0f.mjs} +15 -15
  109. package/dist/{mirrorProtocol-6IFGPpjJ.mjs → mirrorProtocol-BR4luBC0.mjs} +148 -7
  110. package/dist/{mirrorProtocol-qKflWTX_.cjs → mirrorProtocol-HKPivs5o.cjs} +150 -4
  111. package/dist/{package-B7B03ejW.mjs → package-7Ld1YMhh.mjs} +8 -7
  112. package/dist/{package-DY4zTbKS.cjs → package-KhLqBDX9.cjs} +8 -7
  113. package/dist/panels-C7IVqpJk.mjs +137 -0
  114. package/dist/panels-Njf6GelF.cjs +142 -0
  115. package/dist/{peerTools-3R9RUARX.mjs → peerTools-BOlDRyhi.mjs} +60 -10
  116. package/dist/{peerTools-iy3_6Yc5.cjs → peerTools-DQNlrcqM.cjs} +60 -9
  117. package/dist/{persistence-BfX72dne.cjs → persistence-9BMYspxG.cjs} +2 -2
  118. package/dist/{persistence-DTk4sNpd.mjs → persistence-BFUOHW5h.mjs} +3 -3
  119. package/dist/{piRunnerArgs-CRQVR9o-.cjs → piRunnerArgs-DGaq6VYM.cjs} +8 -2
  120. package/dist/{piRunnerArgs-AT8QJY7x.mjs → piRunnerArgs-H2pgpA5z.mjs} +7 -1
  121. package/dist/{piRuntime-BHRT5MKO.cjs → piRuntime-CT5xv_3h.cjs} +2 -2
  122. package/dist/{piRuntime-E44qTVQ4.mjs → piRuntime-CdN0JAsa.mjs} +2 -2
  123. package/dist/{piTerminal-hI4WFdWm.cjs → piTerminal-0FBtXTHK.cjs} +18 -18
  124. package/dist/{piTerminal-De8nbciM.mjs → piTerminal-C4m9YXAV.mjs} +18 -18
  125. package/dist/processTreeSnapshot-CuLJDNC-.mjs +101 -0
  126. package/dist/processTreeSnapshot-mDbnuojn.cjs +105 -0
  127. package/dist/projectPath-C3ioB4Bu.cjs +13 -0
  128. package/dist/prompt-BOouDaTM.mjs +99 -0
  129. package/dist/prompt-lgF9IE82.cjs +105 -0
  130. package/dist/{attachmentLimits-UyFn8fkQ.mjs → pushNotifications-DCiBEVeC.mjs} +307 -3
  131. package/dist/{attachmentLimits-SqeAHrVC.cjs → pushNotifications-DI92havG.cjs} +308 -2
  132. package/dist/{index-DFPT4HOO.cjs → query-BETKBrnz.cjs} +25 -5
  133. package/dist/{index-CEjJnq05.mjs → query-ywZbQUzR.mjs} +26 -3
  134. package/dist/{rateLimitWindows-XrJiiBpU.mjs → rateLimitWindows-BkfuUB1a.mjs} +1 -1
  135. package/dist/{rateLimitWindows-Czb7_7cK.cjs → rateLimitWindows-YRIYVdo4.cjs} +1 -1
  136. package/dist/{registerCommonHandlers-C0_fyBQ0.cjs → registerCommonHandlers-C7a_dUWs.cjs} +30 -30
  137. package/dist/{registerCommonHandlers-CvIedVRf.mjs → registerCommonHandlers-CTkCvjxt.mjs} +2 -2
  138. package/dist/{registerKillSessionHandler-DYl7l4Ts.cjs → registerKillSessionHandler-CIsvBrVe.cjs} +6 -6
  139. package/dist/{registerKillSessionHandler-gwJ5lTf0.mjs → registerKillSessionHandler-CaqqRYq6.mjs} +6 -6
  140. package/dist/{remoteSessionClient-BRTIOX5b.cjs → remoteSessionClient-D843fkzL.cjs} +5 -5
  141. package/dist/{remoteSessionClient-DRb2Jr5s.mjs → remoteSessionClient-PP-mOKmw.mjs} +5 -5
  142. package/dist/{reportProgressTool-B9nCzbkL.cjs → reportProgressTool-14AXr959.cjs} +84 -2
  143. package/dist/{reportProgressTool-D5tEoXMW.mjs → reportProgressTool-ZOCNjWHr.mjs} +82 -3
  144. package/dist/{resources-JDPugcDq.cjs → resources-CvIlkzpP.cjs} +30 -0
  145. package/dist/{resources-BdFBQi8r.mjs → resources-y9jfyO-_.mjs} +30 -0
  146. package/dist/{run-CKe_WeLW.cjs → run-CAOCYYfT.cjs} +2806 -110
  147. package/dist/{run-BQ_FGaOL.mjs → run-Dajw2E5j.mjs} +2787 -91
  148. package/dist/{runClaude-zrrNcQC_.mjs → runClaude-C6oxWYbp.mjs} +49 -47
  149. package/dist/{runClaude-MHI5IKM-.cjs → runClaude-CRRKt-tm.cjs} +50 -48
  150. package/dist/{runClaudeDeps-RRw-Nusc.cjs → runClaudeDeps-B1ysqTvx.cjs} +310 -237
  151. package/dist/{runClaudeDeps-CVBCokOS.mjs → runClaudeDeps-BcZ68cX3.mjs} +297 -226
  152. package/dist/{runGemini-kipag2ag.mjs → runGemini-D7yKc4wD.mjs} +61 -55
  153. package/dist/{runGemini-D7Lg9yCr.cjs → runGemini-DtKFXqf-.cjs} +89 -83
  154. package/dist/{runOpenClaw-C_lRY3Do.mjs → runOpenClaw-C8ilyLP9.mjs} +19 -19
  155. package/dist/{runOpenClaw-DCcQ0L2N.cjs → runOpenClaw-Cq29mryb.cjs} +20 -20
  156. package/dist/{sandbox-BSyXFVFp.mjs → sandbox-CbCutma4.mjs} +4 -4
  157. package/dist/{sandbox-dMwPTmjm.cjs → sandbox-gIj-EuF4.cjs} +4 -4
  158. package/dist/{selfCheck-DkzDDUsE.cjs → selfCheck-BTGBVthv.cjs} +39 -38
  159. package/dist/selfCheck-Smt-5Xiq.mjs +85 -0
  160. package/dist/{send-dxC0miIA.cjs → send-CZf5lLw_.cjs} +8 -8
  161. package/dist/{send-C-k9nsd3.mjs → send-YZRMQvMJ.mjs} +9 -9
  162. package/dist/{server-DxoTYNlw.mjs → server-BqwXRzFZ.mjs} +4 -4
  163. package/dist/{server-B-W6P7Qr.cjs → server-CknFq6xG.cjs} +6 -6
  164. package/dist/sessionExitLifecycle-CzkLIgNZ.mjs +142 -0
  165. package/dist/sessionExitLifecycle-DFltxXU8.cjs +148 -0
  166. package/dist/{sessionLock-DWX6BbiU.cjs → sessionLock-DRZUcKHT.cjs} +38 -5
  167. package/dist/{sessionLock-DBdtZGOL.mjs → sessionLock-nBXdJxJm.mjs} +36 -5
  168. package/dist/{sessionOps-BAnr7n8t.cjs → sessionOps-BrGiU9-s.cjs} +10 -5
  169. package/dist/{sessionOps-BVoXe12O.mjs → sessionOps-C58iDeD5.mjs} +11 -6
  170. package/dist/{sessionScanner-BtxoeluY.mjs → sessionScanner-Bm57LEXe.mjs} +3 -3
  171. package/dist/{sessionScanner-CDOYsEUD.cjs → sessionScanner-CaO8bLaG.cjs} +3 -3
  172. package/dist/{sessions-C3sZJkzI.mjs → sessions-BXyBHo8n.mjs} +40 -18
  173. package/dist/{sessions-hAitoN-m.cjs → sessions-uzqPCc37.cjs} +39 -16
  174. package/dist/{setupOfflineReconnection-_eD4cuUT.mjs → setupOfflineReconnection-1Qlek3wl.mjs} +2 -2
  175. package/dist/{setupOfflineReconnection-C2EeYTFH.cjs → setupOfflineReconnection-DvvVX3TB.cjs} +2 -2
  176. package/dist/{spawn-Bc9v7pIf.cjs → spawn-DBkodJEO.cjs} +49 -18
  177. package/dist/{spawn-qNrB2CD_.mjs → spawn-vn93PhYL.mjs} +49 -18
  178. package/dist/spawnAgents-CgPMWexP.mjs +79 -0
  179. package/dist/{spawnAgents-BVEaz35W.cjs → spawnAgents-DLNMlhWJ.cjs} +37 -1
  180. package/dist/{spawnHappyCLI-C2stUioM.mjs → spawnHappyCLI-BjFZc_7V.mjs} +1 -1
  181. package/dist/{spawnHappyCLI-fzUagkKV.cjs → spawnHappyCLI-DnUZv0X2.cjs} +2 -2
  182. package/dist/{specialCommands-BJ-nqY4s.cjs → specialCommands-BRufxlqe.cjs} +3 -3
  183. package/dist/{specialCommands-C6W-rVyh.mjs → specialCommands-D3ND9bdg.mjs} +3 -3
  184. package/dist/{startFileWatcher-Bqlqjt93.cjs → startFileWatcher-D9cYzZOq.cjs} +1 -1
  185. package/dist/{startFileWatcher-CR09Snmz.mjs → startFileWatcher-DXJ7yB5v.mjs} +1 -1
  186. package/dist/startupSessionCreate-kdr3m1uX.cjs +26 -0
  187. package/dist/startupSessionCreate-uZ6BM0ty.mjs +24 -0
  188. package/dist/{streamRelay-CJoMk_d2.mjs → streamRelay-DnMCMMfV.mjs} +1 -1
  189. package/dist/{streamRelay-9kBPpbvU.cjs → streamRelay-Dy6amlzx.cjs} +1 -1
  190. package/dist/{sync-Dhmtk66S.cjs → sync-Bn-idAoy.cjs} +1 -1
  191. package/dist/{sync-DbW07iQS.mjs → sync-BvVhDO40.mjs} +1 -1
  192. package/dist/{systemPrompt-CQcuUmVp.mjs → systemPrompt-CIZcJuJF.mjs} +4 -3
  193. package/dist/{systemPrompt-3PYi3EUY.cjs → systemPrompt-DlyEk35T.cjs} +4 -3
  194. package/dist/{teams-DpxLcUU_.cjs → teams-BLeRpDxv.cjs} +8 -8
  195. package/dist/{teams-CwF4Z2fA.mjs → teams-DLY3B5yi.mjs} +8 -8
  196. package/dist/{time-A6zR3Iy6.cjs → time-Cjv0zxQO.cjs} +1 -1
  197. package/dist/{time-BuGWTVP6.mjs → time-D3ETL7Jb.mjs} +1 -1
  198. package/dist/{titleGenerator-D26gvNaS.cjs → titleGenerator-Cgp5ihFb.cjs} +10 -9
  199. package/dist/{titleGenerator-BGEzNWC9.mjs → titleGenerator-Uzbd4utr.mjs} +9 -8
  200. package/dist/{todo-DrZc76H5.mjs → todo-C9Ga-oEM.mjs} +6 -6
  201. package/dist/{todo-73g3NCIO.cjs → todo-pOMcEECE.cjs} +6 -6
  202. package/dist/{tools-CxJp4T1T.cjs → tools-BL7Byl_x.cjs} +1 -1
  203. package/dist/{tools-BtsrQnNS.mjs → tools-Cho7iIWf.mjs} +1 -1
  204. package/dist/{uninstall-DWNYvewQ.cjs → uninstall-CbayEVoV.cjs} +6 -6
  205. package/dist/{uninstall-DOb2mTwV.mjs → uninstall-CvZHVY9C.mjs} +3 -3
  206. package/dist/webPanels-B2_C2-3g.mjs +49 -0
  207. package/dist/webPanels-FCgFKvVe.cjs +53 -0
  208. package/package.json +8 -7
  209. package/dist/machineMetadata-B1H9gNbN.mjs +0 -39
  210. package/dist/machineMetadata-DcUupMje.cjs +0 -42
  211. package/dist/messageBuffer-BWlGgR3A.mjs +0 -71
  212. package/dist/messageBuffer-fJMH6uY8.cjs +0 -73
  213. package/dist/projectPath-ChLhdOyt.cjs +0 -13
  214. package/dist/selfCheck-CF7XGJPb.mjs +0 -84
  215. package/dist/sessionModeFile-ByLbXTbg.mjs +0 -32
  216. package/dist/sessionModeFile-CLLdidiJ.cjs +0 -36
  217. package/dist/spawnAgents-Bw6j4tqz.mjs +0 -45
@@ -1,11 +1,19 @@
1
1
  import { z } from 'zod';
2
- import { m as AgentVersionsSchema } from './index-Clqlu17M.mjs';
2
+ import { p as AgentVersionsSchema } from './index-C6Vn5Eac.mjs';
3
+ import axios from 'axios';
4
+ import { l as logger } from './logger-BUngm5Xg.mjs';
5
+ import { c as configuration } from './configuration-PxzEJumr.mjs';
6
+ import { c as contentLogMetadata } from './contentLogMetadata-BlWI0egu.mjs';
3
7
 
4
8
  z.object({
5
9
  teamsVersion: z.number().int().positive().optional(),
6
10
  teamLaunchVersion: z.number().int().positive().optional(),
11
+ /** B-689: daemon acts on the owner's team `retry-operation`. Absent on old daemons. */
12
+ teamRetryVersion: z.number().int().positive().optional(),
7
13
  /** B-586: daemon serves the `worktree-*` RPCs. Absent on old daemons. */
8
14
  worktreeVersion: z.number().int().positive().optional(),
15
+ /** B-688: daemon serves the `model-config-*` RPCs (harness model/provider config). */
16
+ modelConfigVersion: z.number().int().positive().optional(),
9
17
  host: z.string(),
10
18
  platform: z.string(),
11
19
  happyCliVersion: z.string(),
@@ -254,7 +262,21 @@ z.object({
254
262
  idleMs: z.number(),
255
263
  rpcAvailable: z.boolean(),
256
264
  detectedAt: z.number()
257
- }).passthrough().optional()
265
+ }).passthrough().optional(),
266
+ /**
267
+ * B-510: web panels declared in this machine's local settings.json
268
+ * (`webPanels`), normalized by webPanels/webPanels.ts. Read-only mirror —
269
+ * nothing on the web/server side can write the local declaration.
270
+ */
271
+ webPanels: z.object({
272
+ panels: z.array(z.object({
273
+ id: z.string(),
274
+ title: z.string(),
275
+ url: z.string(),
276
+ embed: z.boolean()
277
+ })),
278
+ updatedAt: z.number()
279
+ }).optional()
258
280
  });
259
281
  const MessageMetaSchema = z.object({
260
282
  sentFrom: z.string().optional(),
@@ -383,4 +405,286 @@ const MAX_CHAT_ATTACHMENT_SOURCE_BYTES = 50 * 1024 * 1024;
383
405
  const CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES = 40;
384
406
  const MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES = MAX_CHAT_ATTACHMENT_SOURCE_BYTES + CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES;
385
407
 
386
- export { AsyncLock as A, FileEventMessageSchema as F, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, UserMessageSchema as U, MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES as a };
408
+ function getSessionTitle(metadata) {
409
+ const summaryText = metadata?.summary?.text?.trim();
410
+ if (summaryText) {
411
+ return summaryText;
412
+ }
413
+ const path = metadata?.path?.trim();
414
+ if (!path) {
415
+ return "Session";
416
+ }
417
+ const segments = path.split(/[\\/]/).filter(Boolean);
418
+ return segments[segments.length - 1] || "Session";
419
+ }
420
+ function getSessionNotificationUrl(data) {
421
+ const sessionId = data?.sessionId;
422
+ if (typeof sessionId !== "string") {
423
+ return null;
424
+ }
425
+ const trimmedSessionId = sessionId.trim();
426
+ if (!trimmedSessionId) {
427
+ return null;
428
+ }
429
+ return `/session/${encodeURIComponent(trimmedSessionId)}`;
430
+ }
431
+ function getSessionNotificationTitle(kind) {
432
+ switch (kind) {
433
+ case "done":
434
+ return "It's ready!";
435
+ case "permission":
436
+ return "Permission request";
437
+ case "question":
438
+ return "Clarification needed";
439
+ case "attention":
440
+ return "Needs you";
441
+ }
442
+ }
443
+ function getSessionNotificationBody(metadata) {
444
+ return getSessionTitle(metadata);
445
+ }
446
+ function getSessionNotificationCopy(kind, metadata) {
447
+ return {
448
+ title: getSessionNotificationTitle(kind),
449
+ body: getSessionNotificationBody(metadata)
450
+ };
451
+ }
452
+ class PushNotificationClient {
453
+ token;
454
+ baseUrl;
455
+ expoLoad = null;
456
+ constructor(token, baseUrl = "https://api.cluster-fluster.com") {
457
+ this.token = token;
458
+ this.baseUrl = baseUrl;
459
+ }
460
+ loadExpo() {
461
+ if (!this.expoLoad) {
462
+ const load = import('expo-server-sdk').then(({ Expo }) => ({ Expo, expo: new Expo() }));
463
+ load.catch(() => {
464
+ if (this.expoLoad === load) this.expoLoad = null;
465
+ });
466
+ this.expoLoad = load;
467
+ }
468
+ return this.expoLoad;
469
+ }
470
+ /**
471
+ * Fetch all push tokens for the authenticated user.
472
+ * Retries up to 3 times with exponential backoff on transient errors.
473
+ */
474
+ async fetchPushTokens() {
475
+ const maxAttempts = 3;
476
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
477
+ try {
478
+ const response = await axios.get(
479
+ `${this.baseUrl}/v1/push-tokens`,
480
+ {
481
+ headers: {
482
+ "Authorization": `Bearer ${this.token}`,
483
+ "Content-Type": "application/json",
484
+ "X-Happy-Client": `cli-daemon/${configuration.currentCliVersion}`
485
+ }
486
+ }
487
+ );
488
+ logger.debug(`Fetched ${response.data.tokens.length} push tokens`);
489
+ response.data.tokens.forEach((token, index) => {
490
+ logger.debug(`[PUSH] Token ${index + 1}: id=${token.id}, created=${new Date(token.createdAt).toISOString()}, updated=${new Date(token.updatedAt).toISOString()}`);
491
+ });
492
+ return response.data.tokens;
493
+ } catch (error) {
494
+ logger.debug(`[PUSH] [ERROR] Failed to fetch push tokens (attempt ${attempt}/${maxAttempts}):`, error);
495
+ if (attempt < maxAttempts) {
496
+ const delay = 1e3 * Math.pow(2, attempt - 1);
497
+ await new Promise((resolve) => setTimeout(resolve, delay));
498
+ }
499
+ }
500
+ }
501
+ logger.debug("[PUSH] [ERROR] All push token fetch attempts failed");
502
+ return [];
503
+ }
504
+ /**
505
+ * Send push notification via Expo Push API with retry
506
+ * @param messages - Array of push messages to send
507
+ */
508
+ async sendPushNotifications(messages) {
509
+ logger.debug(`Sending ${messages.length} push notifications`);
510
+ const { Expo, expo } = await this.loadExpo();
511
+ const validMessages = messages.filter((message) => {
512
+ if (Array.isArray(message.to)) {
513
+ return message.to.every((token) => Expo.isExpoPushToken(token));
514
+ }
515
+ return Expo.isExpoPushToken(message.to);
516
+ });
517
+ if (validMessages.length === 0) {
518
+ logger.debug("No valid Expo push tokens found");
519
+ return;
520
+ }
521
+ const chunks = expo.chunkPushNotifications(validMessages);
522
+ for (const chunk of chunks) {
523
+ const startTime = Date.now();
524
+ const timeout = 3e5;
525
+ let attempt = 0;
526
+ while (true) {
527
+ try {
528
+ const ticketChunk = await expo.sendPushNotificationsAsync(chunk);
529
+ const errors = ticketChunk.filter((ticket) => ticket.status === "error");
530
+ if (errors.length > 0) {
531
+ const errorDetails = errors.map((e) => ({ message: e.message, details: e.details }));
532
+ logger.debug("[PUSH] Some notifications failed:", errorDetails);
533
+ }
534
+ if (errors.length === ticketChunk.length) {
535
+ throw new Error("All push notifications in chunk failed");
536
+ }
537
+ break;
538
+ } catch (error) {
539
+ const elapsed = Date.now() - startTime;
540
+ if (elapsed >= timeout) {
541
+ logger.debug("[PUSH] Timeout reached after 5 minutes, giving up on chunk");
542
+ break;
543
+ }
544
+ attempt++;
545
+ const delay = Math.min(1e3 * Math.pow(2, attempt), 3e4);
546
+ const remainingTime = timeout - elapsed;
547
+ const waitTime = Math.min(delay, remainingTime);
548
+ if (waitTime > 0) {
549
+ logger.debug(`[PUSH] Retrying in ${waitTime}ms (attempt ${attempt})`);
550
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
551
+ }
552
+ }
553
+ }
554
+ }
555
+ logger.debug(`Push notifications sent successfully`);
556
+ }
557
+ /**
558
+ * Send a push notification to all registered devices for the user
559
+ * @param title - Notification title
560
+ * @param body - Notification body
561
+ * @param data - Additional data to send with the notification
562
+ */
563
+ sendToAllDevices(title, body, data) {
564
+ logger.debug("[PUSH] sendToAllDevices called:", {
565
+ title: contentLogMetadata(title),
566
+ body: contentLogMetadata(body),
567
+ dataKeyCount: data ? Object.keys(data).length : 0
568
+ });
569
+ (async () => {
570
+ try {
571
+ logger.debug("[PUSH] Fetching push tokens...");
572
+ const tokens = await this.fetchPushTokens();
573
+ logger.debug(`[PUSH] Fetched ${tokens.length} push tokens`);
574
+ tokens.forEach((token, index) => {
575
+ logger.debug(`[PUSH] Using token ${index + 1}: id=${token.id}`);
576
+ });
577
+ if (tokens.length === 0) {
578
+ logger.debug("No push tokens found for user");
579
+ return;
580
+ }
581
+ const messages = tokens.map((token, index) => {
582
+ logger.debug(`[PUSH] Creating message ${index + 1} for token`);
583
+ return {
584
+ to: token.token,
585
+ title,
586
+ body: body && body.length > 0 ? body : void 0,
587
+ data,
588
+ // TODO: For brutalist session artwork, attach rich media via a public HTTPS image URL.
589
+ // Bundled app asset paths / require(...) / local file paths will not work in push payloads.
590
+ // iOS also needs a Notification Service Extension to render richContent.image reliably.
591
+ sound: "default",
592
+ priority: "high"
593
+ };
594
+ });
595
+ logger.debug(`[PUSH] Sending ${messages.length} push notifications...`);
596
+ await this.sendPushNotifications(messages);
597
+ logger.debug("[PUSH] Push notifications sent successfully");
598
+ } catch (error) {
599
+ logger.debug("[PUSH] Error sending to all devices:", error);
600
+ }
601
+ })();
602
+ }
603
+ /**
604
+ * Routes session-event pushes through the server so it can apply
605
+ * presence-based suppression (active desktop/web, mobile foreground).
606
+ * Falls back to direct Expo send only when sessionId is missing — that
607
+ * shouldn't happen for session notifications but guards against regressions.
608
+ */
609
+ sendSessionNotification(params) {
610
+ const { title, body } = getSessionNotificationCopy(params.kind, params.metadata);
611
+ const sessionTitle = getSessionNotificationBody(params.metadata);
612
+ const url = getSessionNotificationUrl(params.data);
613
+ const payloadData = {
614
+ ...params.data,
615
+ kind: params.kind,
616
+ sessionTitle,
617
+ ...url ? { url } : {}
618
+ };
619
+ const sessionId = typeof params.data?.sessionId === "string" ? params.data.sessionId : null;
620
+ if (!sessionId) {
621
+ logger.debug("[PUSH] sendSessionNotification: missing sessionId, falling back to direct send");
622
+ this.sendToAllDevices(title, body, payloadData);
623
+ return;
624
+ }
625
+ void (async () => {
626
+ try {
627
+ await axios.post(
628
+ `${this.baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/push-event`,
629
+ {
630
+ kind: params.kind,
631
+ title,
632
+ body,
633
+ data: payloadData
634
+ },
635
+ {
636
+ headers: {
637
+ "Authorization": `Bearer ${this.token}`,
638
+ "Content-Type": "application/json",
639
+ "X-Happy-Client": `cli-daemon/${configuration.currentCliVersion}`
640
+ },
641
+ timeout: 15e3
642
+ }
643
+ );
644
+ logger.debug(`[PUSH] sendSessionNotification dispatched via server (kind=${params.kind})`);
645
+ } catch (error) {
646
+ logger.debug("[PUSH] sendSessionNotification failed:", error);
647
+ }
648
+ })();
649
+ }
650
+ /**
651
+ * B-510 `request_human` / `very-happy attention --session`: an awaited
652
+ * push-event `attention` (title "Needs you", body = the reason, data.url =
653
+ * panel deep link or the session). A server that predates the kind answers
654
+ * 400; then the same push goes out as `question`, so the person is still
655
+ * reached (compat matrix in specs/2026-09-machine-web-panels.md).
656
+ */
657
+ async sendAttention(params) {
658
+ const post = (kind) => axios.post(
659
+ `${this.baseUrl}/v1/sessions/${encodeURIComponent(params.sessionId)}/push-event`,
660
+ {
661
+ kind,
662
+ title: getSessionNotificationTitle(kind),
663
+ body: params.reason,
664
+ data: { sessionId: params.sessionId, kind, url: params.url, reason: params.reason, ...params.sessionTitle ? { sessionTitle: params.sessionTitle } : {} }
665
+ },
666
+ {
667
+ headers: {
668
+ "Authorization": `Bearer ${this.token}`,
669
+ "Content-Type": "application/json",
670
+ "X-Happy-Client": `cli-daemon/${configuration.currentCliVersion}`
671
+ },
672
+ timeout: 15e3
673
+ }
674
+ );
675
+ try {
676
+ await post("attention");
677
+ return { sent: true, kind: "attention" };
678
+ } catch (error) {
679
+ if (error?.response?.status !== 400) return { sent: false, error: error?.response ? `HTTP ${error.response.status}` : String(error?.message ?? error) };
680
+ }
681
+ try {
682
+ await post("question");
683
+ return { sent: true, kind: "question" };
684
+ } catch (error) {
685
+ return { sent: false, error: error?.response ? `HTTP ${error.response.status}` : String(error?.message ?? error) };
686
+ }
687
+ }
688
+ }
689
+
690
+ export { AsyncLock as A, FileEventMessageSchema as F, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, PushNotificationClient as P, UserMessageSchema as U, MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES as a, getSessionNotificationBody as g };
@@ -1,13 +1,21 @@
1
1
  'use strict';
2
2
 
3
3
  var z = require('zod');
4
- var index = require('./index-_l1Sdmbi.cjs');
4
+ var index = require('./index-Dv0Q48P3.cjs');
5
+ var axios = require('axios');
6
+ var logger = require('./logger-CphW5nUE.cjs');
7
+ var configuration = require('./configuration-CYO-kPOS.cjs');
8
+ var contentLogMetadata = require('./contentLogMetadata-ZSFk_NjQ.cjs');
5
9
 
6
10
  z.z.object({
7
11
  teamsVersion: z.z.number().int().positive().optional(),
8
12
  teamLaunchVersion: z.z.number().int().positive().optional(),
13
+ /** B-689: daemon acts on the owner's team `retry-operation`. Absent on old daemons. */
14
+ teamRetryVersion: z.z.number().int().positive().optional(),
9
15
  /** B-586: daemon serves the `worktree-*` RPCs. Absent on old daemons. */
10
16
  worktreeVersion: z.z.number().int().positive().optional(),
17
+ /** B-688: daemon serves the `model-config-*` RPCs (harness model/provider config). */
18
+ modelConfigVersion: z.z.number().int().positive().optional(),
11
19
  host: z.z.string(),
12
20
  platform: z.z.string(),
13
21
  happyCliVersion: z.z.string(),
@@ -256,7 +264,21 @@ z.z.object({
256
264
  idleMs: z.z.number(),
257
265
  rpcAvailable: z.z.boolean(),
258
266
  detectedAt: z.z.number()
259
- }).passthrough().optional()
267
+ }).passthrough().optional(),
268
+ /**
269
+ * B-510: web panels declared in this machine's local settings.json
270
+ * (`webPanels`), normalized by webPanels/webPanels.ts. Read-only mirror —
271
+ * nothing on the web/server side can write the local declaration.
272
+ */
273
+ webPanels: z.z.object({
274
+ panels: z.z.array(z.z.object({
275
+ id: z.z.string(),
276
+ title: z.z.string(),
277
+ url: z.z.string(),
278
+ embed: z.z.boolean()
279
+ })),
280
+ updatedAt: z.z.number()
281
+ }).optional()
260
282
  });
261
283
  const MessageMetaSchema = z.z.object({
262
284
  sentFrom: z.z.string().optional(),
@@ -385,8 +407,292 @@ const MAX_CHAT_ATTACHMENT_SOURCE_BYTES = 50 * 1024 * 1024;
385
407
  const CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES = 40;
386
408
  const MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES = MAX_CHAT_ATTACHMENT_SOURCE_BYTES + CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES;
387
409
 
410
+ function getSessionTitle(metadata) {
411
+ const summaryText = metadata?.summary?.text?.trim();
412
+ if (summaryText) {
413
+ return summaryText;
414
+ }
415
+ const path = metadata?.path?.trim();
416
+ if (!path) {
417
+ return "Session";
418
+ }
419
+ const segments = path.split(/[\\/]/).filter(Boolean);
420
+ return segments[segments.length - 1] || "Session";
421
+ }
422
+ function getSessionNotificationUrl(data) {
423
+ const sessionId = data?.sessionId;
424
+ if (typeof sessionId !== "string") {
425
+ return null;
426
+ }
427
+ const trimmedSessionId = sessionId.trim();
428
+ if (!trimmedSessionId) {
429
+ return null;
430
+ }
431
+ return `/session/${encodeURIComponent(trimmedSessionId)}`;
432
+ }
433
+ function getSessionNotificationTitle(kind) {
434
+ switch (kind) {
435
+ case "done":
436
+ return "It's ready!";
437
+ case "permission":
438
+ return "Permission request";
439
+ case "question":
440
+ return "Clarification needed";
441
+ case "attention":
442
+ return "Needs you";
443
+ }
444
+ }
445
+ function getSessionNotificationBody(metadata) {
446
+ return getSessionTitle(metadata);
447
+ }
448
+ function getSessionNotificationCopy(kind, metadata) {
449
+ return {
450
+ title: getSessionNotificationTitle(kind),
451
+ body: getSessionNotificationBody(metadata)
452
+ };
453
+ }
454
+ class PushNotificationClient {
455
+ token;
456
+ baseUrl;
457
+ expoLoad = null;
458
+ constructor(token, baseUrl = "https://api.cluster-fluster.com") {
459
+ this.token = token;
460
+ this.baseUrl = baseUrl;
461
+ }
462
+ loadExpo() {
463
+ if (!this.expoLoad) {
464
+ const load = import('expo-server-sdk').then(({ Expo }) => ({ Expo, expo: new Expo() }));
465
+ load.catch(() => {
466
+ if (this.expoLoad === load) this.expoLoad = null;
467
+ });
468
+ this.expoLoad = load;
469
+ }
470
+ return this.expoLoad;
471
+ }
472
+ /**
473
+ * Fetch all push tokens for the authenticated user.
474
+ * Retries up to 3 times with exponential backoff on transient errors.
475
+ */
476
+ async fetchPushTokens() {
477
+ const maxAttempts = 3;
478
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
479
+ try {
480
+ const response = await axios.get(
481
+ `${this.baseUrl}/v1/push-tokens`,
482
+ {
483
+ headers: {
484
+ "Authorization": `Bearer ${this.token}`,
485
+ "Content-Type": "application/json",
486
+ "X-Happy-Client": `cli-daemon/${configuration.configuration.currentCliVersion}`
487
+ }
488
+ }
489
+ );
490
+ logger.logger.debug(`Fetched ${response.data.tokens.length} push tokens`);
491
+ response.data.tokens.forEach((token, index) => {
492
+ logger.logger.debug(`[PUSH] Token ${index + 1}: id=${token.id}, created=${new Date(token.createdAt).toISOString()}, updated=${new Date(token.updatedAt).toISOString()}`);
493
+ });
494
+ return response.data.tokens;
495
+ } catch (error) {
496
+ logger.logger.debug(`[PUSH] [ERROR] Failed to fetch push tokens (attempt ${attempt}/${maxAttempts}):`, error);
497
+ if (attempt < maxAttempts) {
498
+ const delay = 1e3 * Math.pow(2, attempt - 1);
499
+ await new Promise((resolve) => setTimeout(resolve, delay));
500
+ }
501
+ }
502
+ }
503
+ logger.logger.debug("[PUSH] [ERROR] All push token fetch attempts failed");
504
+ return [];
505
+ }
506
+ /**
507
+ * Send push notification via Expo Push API with retry
508
+ * @param messages - Array of push messages to send
509
+ */
510
+ async sendPushNotifications(messages) {
511
+ logger.logger.debug(`Sending ${messages.length} push notifications`);
512
+ const { Expo, expo } = await this.loadExpo();
513
+ const validMessages = messages.filter((message) => {
514
+ if (Array.isArray(message.to)) {
515
+ return message.to.every((token) => Expo.isExpoPushToken(token));
516
+ }
517
+ return Expo.isExpoPushToken(message.to);
518
+ });
519
+ if (validMessages.length === 0) {
520
+ logger.logger.debug("No valid Expo push tokens found");
521
+ return;
522
+ }
523
+ const chunks = expo.chunkPushNotifications(validMessages);
524
+ for (const chunk of chunks) {
525
+ const startTime = Date.now();
526
+ const timeout = 3e5;
527
+ let attempt = 0;
528
+ while (true) {
529
+ try {
530
+ const ticketChunk = await expo.sendPushNotificationsAsync(chunk);
531
+ const errors = ticketChunk.filter((ticket) => ticket.status === "error");
532
+ if (errors.length > 0) {
533
+ const errorDetails = errors.map((e) => ({ message: e.message, details: e.details }));
534
+ logger.logger.debug("[PUSH] Some notifications failed:", errorDetails);
535
+ }
536
+ if (errors.length === ticketChunk.length) {
537
+ throw new Error("All push notifications in chunk failed");
538
+ }
539
+ break;
540
+ } catch (error) {
541
+ const elapsed = Date.now() - startTime;
542
+ if (elapsed >= timeout) {
543
+ logger.logger.debug("[PUSH] Timeout reached after 5 minutes, giving up on chunk");
544
+ break;
545
+ }
546
+ attempt++;
547
+ const delay = Math.min(1e3 * Math.pow(2, attempt), 3e4);
548
+ const remainingTime = timeout - elapsed;
549
+ const waitTime = Math.min(delay, remainingTime);
550
+ if (waitTime > 0) {
551
+ logger.logger.debug(`[PUSH] Retrying in ${waitTime}ms (attempt ${attempt})`);
552
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
553
+ }
554
+ }
555
+ }
556
+ }
557
+ logger.logger.debug(`Push notifications sent successfully`);
558
+ }
559
+ /**
560
+ * Send a push notification to all registered devices for the user
561
+ * @param title - Notification title
562
+ * @param body - Notification body
563
+ * @param data - Additional data to send with the notification
564
+ */
565
+ sendToAllDevices(title, body, data) {
566
+ logger.logger.debug("[PUSH] sendToAllDevices called:", {
567
+ title: contentLogMetadata.contentLogMetadata(title),
568
+ body: contentLogMetadata.contentLogMetadata(body),
569
+ dataKeyCount: data ? Object.keys(data).length : 0
570
+ });
571
+ (async () => {
572
+ try {
573
+ logger.logger.debug("[PUSH] Fetching push tokens...");
574
+ const tokens = await this.fetchPushTokens();
575
+ logger.logger.debug(`[PUSH] Fetched ${tokens.length} push tokens`);
576
+ tokens.forEach((token, index) => {
577
+ logger.logger.debug(`[PUSH] Using token ${index + 1}: id=${token.id}`);
578
+ });
579
+ if (tokens.length === 0) {
580
+ logger.logger.debug("No push tokens found for user");
581
+ return;
582
+ }
583
+ const messages = tokens.map((token, index) => {
584
+ logger.logger.debug(`[PUSH] Creating message ${index + 1} for token`);
585
+ return {
586
+ to: token.token,
587
+ title,
588
+ body: body && body.length > 0 ? body : void 0,
589
+ data,
590
+ // TODO: For brutalist session artwork, attach rich media via a public HTTPS image URL.
591
+ // Bundled app asset paths / require(...) / local file paths will not work in push payloads.
592
+ // iOS also needs a Notification Service Extension to render richContent.image reliably.
593
+ sound: "default",
594
+ priority: "high"
595
+ };
596
+ });
597
+ logger.logger.debug(`[PUSH] Sending ${messages.length} push notifications...`);
598
+ await this.sendPushNotifications(messages);
599
+ logger.logger.debug("[PUSH] Push notifications sent successfully");
600
+ } catch (error) {
601
+ logger.logger.debug("[PUSH] Error sending to all devices:", error);
602
+ }
603
+ })();
604
+ }
605
+ /**
606
+ * Routes session-event pushes through the server so it can apply
607
+ * presence-based suppression (active desktop/web, mobile foreground).
608
+ * Falls back to direct Expo send only when sessionId is missing — that
609
+ * shouldn't happen for session notifications but guards against regressions.
610
+ */
611
+ sendSessionNotification(params) {
612
+ const { title, body } = getSessionNotificationCopy(params.kind, params.metadata);
613
+ const sessionTitle = getSessionNotificationBody(params.metadata);
614
+ const url = getSessionNotificationUrl(params.data);
615
+ const payloadData = {
616
+ ...params.data,
617
+ kind: params.kind,
618
+ sessionTitle,
619
+ ...url ? { url } : {}
620
+ };
621
+ const sessionId = typeof params.data?.sessionId === "string" ? params.data.sessionId : null;
622
+ if (!sessionId) {
623
+ logger.logger.debug("[PUSH] sendSessionNotification: missing sessionId, falling back to direct send");
624
+ this.sendToAllDevices(title, body, payloadData);
625
+ return;
626
+ }
627
+ void (async () => {
628
+ try {
629
+ await axios.post(
630
+ `${this.baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/push-event`,
631
+ {
632
+ kind: params.kind,
633
+ title,
634
+ body,
635
+ data: payloadData
636
+ },
637
+ {
638
+ headers: {
639
+ "Authorization": `Bearer ${this.token}`,
640
+ "Content-Type": "application/json",
641
+ "X-Happy-Client": `cli-daemon/${configuration.configuration.currentCliVersion}`
642
+ },
643
+ timeout: 15e3
644
+ }
645
+ );
646
+ logger.logger.debug(`[PUSH] sendSessionNotification dispatched via server (kind=${params.kind})`);
647
+ } catch (error) {
648
+ logger.logger.debug("[PUSH] sendSessionNotification failed:", error);
649
+ }
650
+ })();
651
+ }
652
+ /**
653
+ * B-510 `request_human` / `very-happy attention --session`: an awaited
654
+ * push-event `attention` (title "Needs you", body = the reason, data.url =
655
+ * panel deep link or the session). A server that predates the kind answers
656
+ * 400; then the same push goes out as `question`, so the person is still
657
+ * reached (compat matrix in specs/2026-09-machine-web-panels.md).
658
+ */
659
+ async sendAttention(params) {
660
+ const post = (kind) => axios.post(
661
+ `${this.baseUrl}/v1/sessions/${encodeURIComponent(params.sessionId)}/push-event`,
662
+ {
663
+ kind,
664
+ title: getSessionNotificationTitle(kind),
665
+ body: params.reason,
666
+ data: { sessionId: params.sessionId, kind, url: params.url, reason: params.reason, ...params.sessionTitle ? { sessionTitle: params.sessionTitle } : {} }
667
+ },
668
+ {
669
+ headers: {
670
+ "Authorization": `Bearer ${this.token}`,
671
+ "Content-Type": "application/json",
672
+ "X-Happy-Client": `cli-daemon/${configuration.configuration.currentCliVersion}`
673
+ },
674
+ timeout: 15e3
675
+ }
676
+ );
677
+ try {
678
+ await post("attention");
679
+ return { sent: true, kind: "attention" };
680
+ } catch (error) {
681
+ if (error?.response?.status !== 400) return { sent: false, error: error?.response ? `HTTP ${error.response.status}` : String(error?.message ?? error) };
682
+ }
683
+ try {
684
+ await post("question");
685
+ return { sent: true, kind: "question" };
686
+ } catch (error) {
687
+ return { sent: false, error: error?.response ? `HTTP ${error.response.status}` : String(error?.message ?? error) };
688
+ }
689
+ }
690
+ }
691
+
388
692
  exports.AsyncLock = AsyncLock;
389
693
  exports.FileEventMessageSchema = FileEventMessageSchema;
390
694
  exports.MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES = MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES;
391
695
  exports.MAX_CHAT_ATTACHMENT_SOURCE_BYTES = MAX_CHAT_ATTACHMENT_SOURCE_BYTES;
696
+ exports.PushNotificationClient = PushNotificationClient;
392
697
  exports.UserMessageSchema = UserMessageSchema;
698
+ exports.getSessionNotificationBody = getSessionNotificationBody;