very-happy-cli 0.2.165 → 0.2.167

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 (198) hide show
  1. package/README.md +1 -1
  2. package/dist/{AcpBackend-DCfNt9bD.cjs → AcpBackend-D1IKJGz_.cjs} +3 -3
  3. package/dist/{AcpBackend-C11FnVw5.mjs → AcpBackend-kFZlee5E.mjs} +3 -3
  4. package/dist/{AcpSessionManager-BQnDJvct.mjs → AcpSessionManager-B7FIrsys.mjs} +1 -1
  5. package/dist/{AcpSessionManager-9yN2iH4b.cjs → AcpSessionManager-Zf00QnlL.cjs} +1 -1
  6. package/dist/{BasePermissionHandler-BwQHxl6M.cjs → BasePermissionHandler-DHxYAK5L.cjs} +1 -1
  7. package/dist/{BasePermissionHandler-CwJ48yiB.mjs → BasePermissionHandler-DS0kRTNI.mjs} +1 -1
  8. package/dist/{BaseReasoningProcessor-ClF2g1qz.mjs → BaseReasoningProcessor-BRtCCt7q.mjs} +1 -1
  9. package/dist/{BaseReasoningProcessor-81lf_M8G.cjs → BaseReasoningProcessor-CsjVdt_H.cjs} +1 -1
  10. package/dist/{agentGuidance-CQuNlNx8.mjs → agentGuidance-BDYt2Qy2.mjs} +4 -2
  11. package/dist/{agentGuidance-B5NATuFa.cjs → agentGuidance-BtBx2xxE.cjs} +4 -1
  12. package/dist/{agentHome-DvR176E5.mjs → agentHome-Ct-VXFeG.mjs} +4 -4
  13. package/dist/{agentHome-D3Nn82kI.cjs → agentHome-DeAWzhSe.cjs} +4 -4
  14. package/dist/{api-AzdzTKFs.cjs → api-BZYLRDM9.cjs} +93 -13
  15. package/dist/{api-DTVPUE7g.mjs → api-CReZazeT.mjs} +93 -13
  16. package/dist/{apiMachine-B_cJldfa.cjs → apiMachine-BgMxGPsR.cjs} +2173 -66
  17. package/dist/{apiMachine-BQ-b4Lu3.mjs → apiMachine-CWndf1Vn.mjs} +2176 -69
  18. package/dist/{attachmentContent-muDCe4Gy.cjs → attachmentContent-HcJqFQW8.cjs} +2 -2
  19. package/dist/{attachmentContent-dzIs-wis.mjs → attachmentContent-j875mwU7.mjs} +2 -2
  20. package/dist/{attachmentLimits-eJZ4ZBjF.cjs → attachmentLimits-BEYEEBm8.cjs} +40 -2
  21. package/dist/{attachmentLimits-B8nzhw-i.mjs → attachmentLimits-CxG3T18E.mjs} +40 -2
  22. package/dist/{auth-Jtmvh1w4.mjs → auth-BIHuc7vY.mjs} +10 -10
  23. package/dist/{auth-BY5ujhGn.mjs → auth-CFS9Lfgv.mjs} +5 -5
  24. package/dist/{auth-DtP-B4gf.cjs → auth-Myr2Q42x.cjs} +10 -10
  25. package/dist/{auth-CfLd3phE.cjs → auth-VzWpbo5P.cjs} +5 -5
  26. package/dist/{auto-ClNnbSoF.cjs → auto-Bf6Dr7_o.cjs} +85 -21
  27. package/dist/{auto-DjWfR1UB.mjs → auto-Df9-zmuN.mjs} +84 -22
  28. package/dist/{bootstrap-D_ExGrY2.mjs → bootstrap-BSOxYpaP.mjs} +2 -2
  29. package/dist/{bootstrap-B1dMZA-u.cjs → bootstrap-Dj8WBiR2.cjs} +2 -2
  30. package/dist/{browser-mwzpOmcw.mjs → browser-BCFIHOvo.mjs} +1 -1
  31. package/dist/{browser-_gV7tcPs.cjs → browser-BoCV40yC.cjs} +1 -1
  32. package/dist/{claudeLocal-G5UmJ81F.cjs → claudeLocal-CGsq7Kul.cjs} +7 -7
  33. package/dist/{claudeLocal-BeHToEK5.mjs → claudeLocal-D5-eA9SC.mjs} +7 -7
  34. package/dist/{claudeLocalLauncher-Cq6bqAT4.mjs → claudeLocalLauncher-C9Cc438l.mjs} +15 -12
  35. package/dist/{claudeLocalLauncher-B4y2BsmO.cjs → claudeLocalLauncher-Cf9QeYDT.cjs} +15 -12
  36. package/dist/{claudeSessionFork-CDJq_b9r.cjs → claudeSessionFork-Byd8zD1U.cjs} +1 -1
  37. package/dist/{claudeSessionFork-CokUhS4r.mjs → claudeSessionFork-CMzAva8G.mjs} +1 -1
  38. package/dist/{client-Do7cGPEq.cjs → client-BKnKRLSt.cjs} +3 -3
  39. package/dist/{client-CJZKBZCE.mjs → client-C7FYMer0.mjs} +2 -2
  40. package/dist/{client-uN9d6Git.cjs → client-D0T5-6Ds.cjs} +2 -2
  41. package/dist/{client-BwaWdlCm.mjs → client-Dsfy_SAb.mjs} +3 -3
  42. package/dist/codex/happyMcpStdioBridge.cjs +23 -18
  43. package/dist/codex/happyMcpStdioBridge.mjs +22 -17
  44. package/dist/{codexAppServerClient-B0QaMQsC.mjs → codexAppServerClient-Dd_4TxHv.mjs} +48 -4
  45. package/dist/{codexAppServerClient-BuSdeUxT.cjs → codexAppServerClient-TiW9OBk4.cjs} +47 -3
  46. package/dist/{codexCommand-C3gdjMAw.mjs → codexCommand-BZSr4MKI.mjs} +170 -69
  47. package/dist/{codexCommand-YdYmykAR.cjs → codexCommand-Bxryqh90.cjs} +170 -69
  48. package/dist/{config-CGCIFRCN.mjs → config-C_Pj7yxV.mjs} +3 -3
  49. package/dist/{config-ChoHFnfO.cjs → config-DXPash-r.cjs} +3 -3
  50. package/dist/{configuration-BE61XNsg.cjs → configuration-C3piBtxY.cjs} +1 -1
  51. package/dist/{configuration-L1qcGTFM.mjs → configuration-WspKd2C2.mjs} +2 -2
  52. package/dist/{connect-D5IKfcfd.cjs → connect-BN-IBElA.cjs} +14 -14
  53. package/dist/{connect-CFD0Em6a.mjs → connect-DGPZs5px.mjs} +14 -14
  54. package/dist/{context-DaCCB4cm.mjs → context-Cr6a3NTg.mjs} +1 -1
  55. package/dist/{context-3WVw8qPz.cjs → context-Jrq4-eCZ.cjs} +1 -1
  56. package/dist/{controlClient-RWTlQA4_.cjs → controlClient-BI6Fw3fQ.cjs} +28 -5
  57. package/dist/{controlClient-Ci7HTMII.mjs → controlClient-DTheOlvC.mjs} +27 -6
  58. package/dist/{createSessionMetadata-CWR-AB2A.mjs → createSessionMetadata-BEZ4DIDA.mjs} +57 -5
  59. package/dist/{createSessionMetadata-CPGTztXP.cjs → createSessionMetadata-zQSz3hgk.cjs} +56 -3
  60. package/dist/defaultPermissionMode-Cif_RHGb.mjs +191 -0
  61. package/dist/defaultPermissionMode-DNKNzk7l.cjs +197 -0
  62. package/dist/{deterministicJson-BBwG-Ase.mjs → deterministicJson-BMMifViN.mjs} +36 -52
  63. package/dist/{deterministicJson-C4Jo2RWy.cjs → deterministicJson-C7O2ulKY.cjs} +41 -59
  64. package/dist/{doctor-ZOQLE5AH.cjs → doctor-BykBaMTr.cjs} +6 -6
  65. package/dist/{doctor-CAC6qY9Z.mjs → doctor-D1KKyPzB.mjs} +6 -6
  66. package/dist/{assistantTools-BsrG3PD-.cjs → downloadTarget-CWTb2-EM.cjs} +44 -6
  67. package/dist/{assistantTools-DPGga5lM.mjs → downloadTarget-Cp48jrIP.mjs} +45 -8
  68. package/dist/{ensureDaemonRunning-b1pvEFcS.mjs → ensureDaemonRunning-Ba5fYoxb.mjs} +5 -5
  69. package/dist/{ensureDaemonRunning-mIgX2vnc.cjs → ensureDaemonRunning-CCxX7MoP.cjs} +5 -5
  70. package/dist/{handleResumeCommand-CTGbZkCL.mjs → handleResumeCommand-BTUGlNjB.mjs} +5 -5
  71. package/dist/{handleResumeCommand-DGNhbv-3.cjs → handleResumeCommand-yPdDQHqz.cjs} +5 -5
  72. package/dist/{index-vVWpCoTr.mjs → index-B0HEEYGh.mjs} +22 -5
  73. package/dist/{index-BzIxE0Eu.cjs → index-BjhKqDa0.cjs} +3 -3
  74. package/dist/{index-BgzjPv07.mjs → index-C3_83bqS.mjs} +3 -3
  75. package/dist/{index-DqlyNgt4.cjs → index-C9hI8a10.cjs} +26 -4
  76. package/dist/{index-DjtBengz.mjs → index-CE2GgPbr.mjs} +40 -39
  77. package/dist/{index-Diu6HdWH.mjs → index-D8m8Ripb.mjs} +4 -4
  78. package/dist/{index-DrSPR59N.cjs → index-DifM62wX.cjs} +39 -38
  79. package/dist/{index-DIodyAqF.cjs → index-PNMQPKhz.cjs} +4 -4
  80. package/dist/index.cjs +2 -2
  81. package/dist/index.mjs +2 -2
  82. package/dist/{install-BO_W7VhL.mjs → install-BdvKiOQw.mjs} +2 -0
  83. package/dist/{install-Cp_aB-NQ.mjs → install-C0-QFn9s.mjs} +3 -3
  84. package/dist/{install-Cs5qAPrB.cjs → install-Cf6AFSWC.cjs} +2 -0
  85. package/dist/{install-DrUQk2DJ.cjs → install-CqZVeze8.cjs} +3 -3
  86. package/dist/lib.cjs +12 -12
  87. package/dist/lib.d.cts +116 -2
  88. package/dist/lib.d.mts +116 -2
  89. package/dist/lib.mjs +12 -12
  90. package/dist/{lidState-BLHLmhwd.mjs → lidState-B2ZxdREe.mjs} +1 -1
  91. package/dist/{lidState-z9ekRpZu.cjs → lidState-Bc2K9H9l.cjs} +1 -1
  92. package/dist/{localHappyAgentAuth-BWvmc_Hg.mjs → localHappyAgentAuth-B5XPEMwk.mjs} +1 -1
  93. package/dist/{localHappyAgentAuth-BCx0y1qW.cjs → localHappyAgentAuth-T5GOvRha.cjs} +1 -1
  94. package/dist/{logger-D0hkk7Vo.cjs → logger-B_MWPbZz.cjs} +2 -2
  95. package/dist/{logger-CknSVkIe.mjs → logger-CbBv3q_b.mjs} +2 -2
  96. package/dist/{machineMetadata-BDCYOXQg.cjs → machineMetadata-BafS4JY8.cjs} +4 -3
  97. package/dist/{machineMetadata-Cj1DoFot.mjs → machineMetadata-DHg238U4.mjs} +4 -3
  98. package/dist/{main-COAPGIso.cjs → main-DNrA0qBy.cjs} +45 -45
  99. package/dist/{main-CKUfkVG_.mjs → main-DXAK0n7x.mjs} +45 -45
  100. package/dist/{manager-BQ4flvQZ.mjs → manager-BEsI4qt3.mjs} +9 -3
  101. package/dist/{manager-Cly8xVIR.cjs → manager-HrEj442o.cjs} +8 -2
  102. package/dist/{mcp-BILnw4si.cjs → mcp-AvPOhDXy.cjs} +24 -19
  103. package/dist/{mcp-DeYUfpo8.mjs → mcp-BotAjCdD.mjs} +23 -18
  104. package/dist/mirrorProtocol-BdcN6QlN.cjs +1270 -0
  105. package/dist/mirrorProtocol-BwHm8odn.mjs +1247 -0
  106. package/dist/{package-UkmvSFsg.cjs → package-C9ZP9bpX.cjs} +7 -7
  107. package/dist/{package-VIUXrYZ1.mjs → package-DU1DxhH9.mjs} +7 -7
  108. package/dist/{peerTools-CttfOxti.cjs → peerTools-Dkbu7gag.cjs} +4 -4
  109. package/dist/{peerTools-SWITnkqz.mjs → peerTools-os2DVjA0.mjs} +4 -4
  110. package/dist/{persistence-C6ztVW1W.cjs → persistence-BYGl0QYw.cjs} +147 -25
  111. package/dist/{persistence-DujtUH94.mjs → persistence-ON0zpHfb.mjs} +123 -12
  112. package/dist/{piRunnerArgs-BVJHIjlk.mjs → piRunnerArgs-CYsnT9sV.mjs} +1 -1
  113. package/dist/{piRunnerArgs-tBQg6xDw.cjs → piRunnerArgs-DpbvstUX.cjs} +1 -1
  114. package/dist/{piRuntime-DZzjF9It.cjs → piRuntime-CDnILlCd.cjs} +1 -1
  115. package/dist/{piRuntime-DKFA8u8t.mjs → piRuntime-CmEid-wu.mjs} +1 -1
  116. package/dist/{piTerminal-BTSaFlpV.mjs → piTerminal-0eNlXSlL.mjs} +17 -17
  117. package/dist/{piTerminal-t5pQHCPT.cjs → piTerminal-B57TgMON.cjs} +17 -17
  118. package/dist/precheckCompat-CWOi5c9L.cjs +29 -0
  119. package/dist/precheckCompat-DeEbilbr.mjs +26 -0
  120. package/dist/rateLimitWindows-CusGIqlq.cjs +258 -0
  121. package/dist/rateLimitWindows-vZ9Bo7aT.mjs +248 -0
  122. package/dist/{registerCommonHandlers-BR0J4g64.mjs → registerCommonHandlers-B0PVff2U.mjs} +2 -2
  123. package/dist/{registerCommonHandlers--8ds2auE.cjs → registerCommonHandlers-DVYB3iVn.cjs} +2 -2
  124. package/dist/{registerKillSessionHandler-Czz0tdki.mjs → registerKillSessionHandler-BJMHJHXe.mjs} +15 -10
  125. package/dist/{registerKillSessionHandler-CI23l8aJ.cjs → registerKillSessionHandler-BwXGKtg_.cjs} +15 -10
  126. package/dist/{remoteSessionClient-DCAUHgPz.mjs → remoteSessionClient-BzPXSC21.mjs} +5 -5
  127. package/dist/{remoteSessionClient-BAkIwM2S.cjs → remoteSessionClient-PyUAIhwK.cjs} +5 -5
  128. package/dist/{tools-BVRoGcQH.mjs → reportProgressTool-BvB13EwC.mjs} +67 -10
  129. package/dist/{tools-mWqsE6ji.cjs → reportProgressTool-D4KB_4cP.cjs} +73 -9
  130. package/dist/{run-CkKujjn8.cjs → run-BC8jzq_h.cjs} +558 -122
  131. package/dist/{run-B75qm2I6.mjs → run-O2M8LsWf.mjs} +556 -120
  132. package/dist/{runClaude-B1T9DLLi.cjs → runClaude-C7jPa8VP.cjs} +93 -26
  133. package/dist/{runClaude-aBfBweec.mjs → runClaude-DGkr5E7s.mjs} +93 -26
  134. package/dist/{runClaudeDeps-BGjNGfuZ.cjs → runClaudeDeps-5kzncqME.cjs} +113 -35
  135. package/dist/{runClaudeDeps-BOrPP4J1.mjs → runClaudeDeps-CX_TxS78.mjs} +116 -37
  136. package/dist/{runGemini-BuAA_OQb.cjs → runGemini-BfYA4Uqc.cjs} +43 -36
  137. package/dist/{runGemini-a-V7JE-J.mjs → runGemini-CwmGBH3L.mjs} +43 -36
  138. package/dist/{runOpenClaw-DUSwXg1A.mjs → runOpenClaw-BYAPYFyL.mjs} +18 -18
  139. package/dist/{runOpenClaw-BBVffIh9.cjs → runOpenClaw-C72jut7v.cjs} +18 -18
  140. package/dist/{sandbox-Fd2G5yaq.cjs → sandbox-Bqkp8IT7.cjs} +4 -4
  141. package/dist/{sandbox-Brb_5wzc.mjs → sandbox-DlVnK1Vi.mjs} +4 -4
  142. package/dist/{selfCheck-NuJKMInJ.cjs → selfCheck-B4lLZplk.cjs} +34 -34
  143. package/dist/selfCheck-DYTGBdIO.mjs +84 -0
  144. package/dist/{send-DEQJiq_x.mjs → send-B5bWW9Tl.mjs} +8 -8
  145. package/dist/{send-4dgHKC3u.cjs → send-BCwS6n_s.cjs} +8 -8
  146. package/dist/{server-C6AlUFkw.mjs → server-CEJfRQfj.mjs} +4 -4
  147. package/dist/{server-DWGcDmHG.cjs → server-CFWZehnC.cjs} +6 -6
  148. package/dist/{sessionLock-Dm8QyGo4.mjs → sessionLock-AwOYsKT7.mjs} +12 -4
  149. package/dist/{sessionLock-B1GgaO1q.cjs → sessionLock-LBWNjpHf.cjs} +12 -3
  150. package/dist/{sessionModeFile-BvOEJ4Fl.cjs → sessionModeFile-CyPuZRIU.cjs} +1 -1
  151. package/dist/{sessionModeFile-CuNAWCKL.mjs → sessionModeFile-fvFn1JVG.mjs} +1 -1
  152. package/dist/{sessionOps-DG5MMEp_.mjs → sessionOps-DZRH4-hV.mjs} +4 -4
  153. package/dist/{sessionOps-JP64uJmc.cjs → sessionOps-DzbZI3Eq.cjs} +4 -4
  154. package/dist/{sessionScanner-DqSk5Ol_.mjs → sessionScanner-Q_Ei2flB.mjs} +75 -19
  155. package/dist/{sessionScanner-BBPEgJ5S.cjs → sessionScanner-i-cgwFVw.cjs} +75 -19
  156. package/dist/{sessions-4isbBAWi.mjs → sessions-DAmjMAZN.mjs} +9 -9
  157. package/dist/{sessions-DgJuw_i0.cjs → sessions-DYK5hD4T.cjs} +9 -9
  158. package/dist/{setupOfflineReconnection-_MoZtsro.mjs → setupOfflineReconnection-BfDBxNdo.mjs} +2 -2
  159. package/dist/{setupOfflineReconnection-CKJKVIUS.cjs → setupOfflineReconnection-C2LO3hSP.cjs} +2 -2
  160. package/dist/{spawn-BTOG4tTm.mjs → spawn-BmjG5j5Y.mjs} +15 -14
  161. package/dist/{spawn-CshERWbY.cjs → spawn-GK_sXE8h.cjs} +15 -14
  162. package/dist/{spawnAgents-Czi2VDzj.mjs → spawnAgents-HDaE3C0-.mjs} +1 -1
  163. package/dist/{spawnAgents-McixdtV1.cjs → spawnAgents-vglexX9N.cjs} +1 -1
  164. package/dist/{spawnHappyCLI-Dc4xYm8P.mjs → spawnHappyCLI-B2h0P4b5.mjs} +1 -1
  165. package/dist/{spawnHappyCLI-Dd5_8Vex.cjs → spawnHappyCLI-J4iEhfyZ.cjs} +1 -1
  166. package/dist/specialCommands-DisVxagO.mjs +93 -0
  167. package/dist/specialCommands-DmJMKspE.cjs +96 -0
  168. package/dist/{startFileWatcher-B0VlG-Fn.mjs → startFileWatcher-CeK1k5gt.mjs} +1 -1
  169. package/dist/{startFileWatcher-1LedLBY-.cjs → startFileWatcher-xKo8AoYl.cjs} +1 -1
  170. package/dist/{streamRelay-C0AffuX6.cjs → streamRelay-Ce9sgcOV.cjs} +1 -1
  171. package/dist/{streamRelay-BzMEEvJU.mjs → streamRelay-Iogsh51a.mjs} +1 -1
  172. package/dist/{sync-DwUUGGFz.cjs → sync-Bvc-vh6C.cjs} +1 -1
  173. package/dist/{sync-Dfm6K2te.mjs → sync-CCwYpJMS.mjs} +1 -1
  174. package/dist/{systemPrompt-D41Zw1a3.mjs → systemPrompt-BNIkP3tR.mjs} +3 -3
  175. package/dist/{systemPrompt-CB1Ls0AE.cjs → systemPrompt-DwOddbuG.cjs} +3 -3
  176. package/dist/{teams-D8Ly3i6u.mjs → teams-DRrLBcys.mjs} +7 -7
  177. package/dist/{teams-NWv2ji16.cjs → teams-GHMCRZOw.cjs} +7 -7
  178. package/dist/{time-BbzJ7PJQ.cjs → time-BXvtjhe9.cjs} +1 -1
  179. package/dist/{time-v1Y_qEJp.mjs → time-PG5Dfwdv.mjs} +1 -1
  180. package/dist/{titleGenerator-cH6IkPRi.cjs → titleGenerator-B07bZA0V.cjs} +2 -2
  181. package/dist/{titleGenerator-BhoblAQY.mjs → titleGenerator-CIzfpfZW.mjs} +1 -1
  182. package/dist/{todo-D1hgWV6d.cjs → todo-B0aeoNu3.cjs} +6 -6
  183. package/dist/{todo-CuNct-fw.mjs → todo-CfL7SBFc.mjs} +6 -6
  184. package/dist/{tools-jof0Bu54.mjs → tools-D91mq5Ib.mjs} +1 -1
  185. package/dist/{tools-c2fb0CF5.cjs → tools-DJIANHSh.cjs} +1 -1
  186. package/dist/transcriptCursor-B7T_gkQ0.mjs +102 -0
  187. package/dist/transcriptCursor-DcXGLeSc.cjs +106 -0
  188. package/dist/{uninstall-BB7WYYAk.mjs → uninstall-5xz3Xvhf.mjs} +3 -3
  189. package/dist/{uninstall-VPH5GfrA.cjs → uninstall-AtzOfdrj.cjs} +3 -3
  190. package/package.json +7 -7
  191. package/scripts/terminal_status_forwarder.cjs +89 -0
  192. package/dist/defaultPermissionMode-BnxJPVHT.mjs +0 -4
  193. package/dist/defaultPermissionMode-CxmTXUQR.cjs +0 -7
  194. package/dist/mirrorProtocol-BNLtoWjO.mjs +0 -170
  195. package/dist/mirrorProtocol-BUtmg1ws.cjs +0 -186
  196. package/dist/selfCheck-BgwJ3NGS.mjs +0 -84
  197. package/dist/specialCommands-BSqhYg5X.mjs +0 -52
  198. package/dist/specialCommands-DSsTeRej.cjs +0 -54
package/README.md CHANGED
@@ -161,7 +161,7 @@ The web client URL follows the same precedence (`HAPPY_WEBAPP_URL`, then
161
161
 
162
162
  Base managed Claude sessions receive `change_title`, `copy_to_clipboard`,
163
163
  `open_preview`, and `report_progress`. The managed Codex, Gemini, and ACP bridge
164
- exposes the first three except progress. For a plain `claude`, the standalone
164
+ exposes the same four. For a plain `claude`, the standalone
165
165
  command is intentionally clipboard-only:
166
166
 
167
167
  ```bash
@@ -3,10 +3,10 @@
3
3
  var node_child_process = require('node:child_process');
4
4
  var sdk = require('@agentclientprotocol/sdk');
5
5
  var node_crypto = require('node:crypto');
6
- var logger = require('./logger-D0hkk7Vo.cjs');
7
- var time = require('./time-BbzJ7PJQ.cjs');
6
+ var logger = require('./logger-B_MWPbZz.cjs');
7
+ var time = require('./time-BXvtjhe9.cjs');
8
8
  var contentLogMetadata = require('./contentLogMetadata-ZSFk_NjQ.cjs');
9
- var _package = require('./package-UkmvSFsg.cjs');
9
+ var _package = require('./package-C9ZP9bpX.cjs');
10
10
 
11
11
  function acpImageBlocks(attachments, supportsImages) {
12
12
  return attachments.filter((file) => ["image/jpeg", "image/png", "image/gif", "image/webp"].includes(file.mimeType)).map((file) => {
@@ -1,10 +1,10 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { l as logger } from './logger-CknSVkIe.mjs';
5
- import { d as delay } from './time-v1Y_qEJp.mjs';
4
+ import { l as logger } from './logger-CbBv3q_b.mjs';
5
+ import { d as delay } from './time-PG5Dfwdv.mjs';
6
6
  import { c as contentLogMetadata, e as errorLogMetadata } from './contentLogMetadata-BlWI0egu.mjs';
7
- import { p as packageJson } from './package-VIUXrYZ1.mjs';
7
+ import { p as packageJson } from './package-DU1DxhH9.mjs';
8
8
 
9
9
  function acpImageBlocks(attachments, supportsImages) {
10
10
  return attachments.filter((file) => ["image/jpeg", "image/png", "image/gif", "image/webp"].includes(file.mimeType)).map((file) => {
@@ -1,5 +1,5 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { s as streamKeyOf, e as createEnvelope } from './index-vVWpCoTr.mjs';
2
+ import { s as streamKeyOf, c as createEnvelope } from './index-B0HEEYGh.mjs';
3
3
 
4
4
  function turnOptions(turnId, time) {
5
5
  return turnId ? { turn: turnId, time } : { time };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var cuid2 = require('@paralleldrive/cuid2');
4
- var index = require('./index-DqlyNgt4.cjs');
4
+ var index = require('./index-C9hI8a10.cjs');
5
5
 
6
6
  function turnOptions(turnId, time) {
7
7
  return turnId ? { turn: turnId, time } : { time };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var logger = require('./logger-D0hkk7Vo.cjs');
3
+ var logger = require('./logger-B_MWPbZz.cjs');
4
4
 
5
5
  class BasePermissionHandler {
6
6
  pendingRequests = /* @__PURE__ */ new Map();
@@ -1,4 +1,4 @@
1
- import { l as logger } from './logger-CknSVkIe.mjs';
1
+ import { l as logger } from './logger-CbBv3q_b.mjs';
2
2
 
3
3
  class BasePermissionHandler {
4
4
  pendingRequests = /* @__PURE__ */ new Map();
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { l as logger } from './logger-CknSVkIe.mjs';
2
+ import { l as logger } from './logger-CbBv3q_b.mjs';
3
3
 
4
4
  class BaseReasoningProcessor {
5
5
  accumulator = "";
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var node_crypto = require('node:crypto');
4
- var logger = require('./logger-D0hkk7Vo.cjs');
4
+ var logger = require('./logger-B_MWPbZz.cjs');
5
5
 
6
6
  class BaseReasoningProcessor {
7
7
  accumulator = "";
@@ -5,6 +5,7 @@ const AGENT_GUIDANCE = (() => trimIdent(`
5
5
 
6
6
  - Terminal output is not a good delivery channel. When the user asks for a piece of text ("copy that", "give me X", "\u590D\u5236\u7ED9\u6211", "\u53D1\u6211"), hand it over with mcp__happy__copy_to_clipboard instead of printing it for them to select by hand.
7
7
  - When you produce or want to show a FILE (a document you wrote, a report, an image, a PDF, a diff), call mcp__happy__open_preview with its path instead of cat-ing it. Their client opens the rendered file.
8
+ - If the user wants to GET a file (any type: zip, docx, binary), pass download: true to mcp__happy__open_preview. Zip a directory first.
8
9
 
9
10
  Judgement, not reflex: use these when the user's goal is to RECEIVE something. If they are asking a question you can just answer, answer it. Do not preview a file you only read for your own reasoning, and do not preview the same file repeatedly within a turn.
10
11
 
@@ -12,9 +13,10 @@ const AGENT_GUIDANCE = (() => trimIdent(`
12
13
  `))();
13
14
  const PREVIEW_TOOL_NAME = "open_preview";
14
15
  const PREVIEW_TOOL_TITLE = "Open File Preview for the User";
15
- const PREVIEW_TOOL_DESCRIPTION = 'Open a file in the preview panel of every web client the user currently has open. Use this whenever you produce or want to show the user a file: a document or report you just wrote, a generated image or diagram, a PDF, a data file, or a source file you want them to look at. Trigger phrases include: "write me a doc", "show me that file", "let me see it", "\u5199\u4E2A\u6587\u6863", "\u7ED9\u6211\u770B\u770B\u8FD9\u4E2A\u6587\u4EF6", "\u6253\u5F00\u770B\u4E0B". Pass an absolute path (or one starting with ~). The file is read on this machine and rendered in their browser \u2014 markdown is rendered, images and PDFs are displayed, other text is syntax-highlighted. Prefer this over printing file contents to the terminal, which the user is probably not looking at. NOTE: this only requests that their client open the file; it cannot confirm the user actually looked at it, so do not assume they have seen it.';
16
+ const PREVIEW_TOOL_DESCRIPTION = 'Open a file in the preview panel of every web client the user currently has open. Use this whenever you produce or want to show the user a file: a document or report you just wrote, a generated image or diagram, a PDF, a data file, or a source file you want them to look at. Trigger phrases include: "write me a doc", "show me that file", "let me see it", "\u5199\u4E2A\u6587\u6863", "\u7ED9\u6211\u770B\u770B\u8FD9\u4E2A\u6587\u4EF6", "\u6253\u5F00\u770B\u4E0B". Pass an absolute path (or one starting with ~). The file is read on this machine and rendered in their browser \u2014 markdown is rendered, images and PDFs are displayed, other text is syntax-highlighted. Prefer this over printing file contents to the terminal, which the user is probably not looking at. Set download: true when the user wants to GET the file rather than read it \u2014 any type, including zip archives, Word/Excel/PowerPoint documents, binaries and installers ("send me the file", "let me download it", "\u7ED9\u6211\u4E0B\u8F7D", "\u53D1\u6211\u8FD9\u4E2A\u6587\u4EF6"). Their client then shows a prominent download button (and may start the download on desktop). Only single regular files can be downloaded: zip a directory first and pass the archive. The web download limit is 50 MiB; compress or split anything larger. NOTE: this only requests that their client open the file; it cannot confirm the user actually looked at it or downloaded it, so do not assume they have.';
17
+ const PREVIEW_DOWNLOAD_PARAM_DESCRIPTION = "true when the user wants to download/receive the file itself (any type) rather than just read it";
16
18
  const REPORT_PROGRESS_TOOL_NAME = "report_progress";
17
19
  const REPORT_PROGRESS_TOOL_TITLE = "Report Progress to the Task Board";
18
20
  const REPORT_PROGRESS_TOOL_DESCRIPTION = `Report what you are doing to the user's task board, so they can see the state of this session at a glance without reading the transcript. Call it at meaningful milestones only: when you start substantive work, when you get blocked and need the user, when something is ready for their review, and when you are done. Do NOT call it after every tool call or every file edit \u2014 it is a status line, not a log. Set attention to "blocked" when you cannot proceed without the user, "review" when you want them to look at something, and "none" for ordinary progress. Keep progress to one short line describing the current state (the user reads it on a card).`;
19
21
 
20
- export { AGENT_GUIDANCE as A, PREVIEW_TOOL_NAME as P, REPORT_PROGRESS_TOOL_NAME as R, PREVIEW_TOOL_TITLE as a, PREVIEW_TOOL_DESCRIPTION as b, REPORT_PROGRESS_TOOL_TITLE as c, REPORT_PROGRESS_TOOL_DESCRIPTION as d };
22
+ export { AGENT_GUIDANCE as A, PREVIEW_TOOL_NAME as P, REPORT_PROGRESS_TOOL_NAME as R, PREVIEW_TOOL_TITLE as a, PREVIEW_TOOL_DESCRIPTION as b, PREVIEW_DOWNLOAD_PARAM_DESCRIPTION as c, REPORT_PROGRESS_TOOL_TITLE as d, REPORT_PROGRESS_TOOL_DESCRIPTION as e };
@@ -7,6 +7,7 @@ const AGENT_GUIDANCE = (() => trimIdent.trimIdent(`
7
7
 
8
8
  - Terminal output is not a good delivery channel. When the user asks for a piece of text ("copy that", "give me X", "\u590D\u5236\u7ED9\u6211", "\u53D1\u6211"), hand it over with mcp__happy__copy_to_clipboard instead of printing it for them to select by hand.
9
9
  - When you produce or want to show a FILE (a document you wrote, a report, an image, a PDF, a diff), call mcp__happy__open_preview with its path instead of cat-ing it. Their client opens the rendered file.
10
+ - If the user wants to GET a file (any type: zip, docx, binary), pass download: true to mcp__happy__open_preview. Zip a directory first.
10
11
 
11
12
  Judgement, not reflex: use these when the user's goal is to RECEIVE something. If they are asking a question you can just answer, answer it. Do not preview a file you only read for your own reasoning, and do not preview the same file repeatedly within a turn.
12
13
 
@@ -14,12 +15,14 @@ const AGENT_GUIDANCE = (() => trimIdent.trimIdent(`
14
15
  `))();
15
16
  const PREVIEW_TOOL_NAME = "open_preview";
16
17
  const PREVIEW_TOOL_TITLE = "Open File Preview for the User";
17
- const PREVIEW_TOOL_DESCRIPTION = 'Open a file in the preview panel of every web client the user currently has open. Use this whenever you produce or want to show the user a file: a document or report you just wrote, a generated image or diagram, a PDF, a data file, or a source file you want them to look at. Trigger phrases include: "write me a doc", "show me that file", "let me see it", "\u5199\u4E2A\u6587\u6863", "\u7ED9\u6211\u770B\u770B\u8FD9\u4E2A\u6587\u4EF6", "\u6253\u5F00\u770B\u4E0B". Pass an absolute path (or one starting with ~). The file is read on this machine and rendered in their browser \u2014 markdown is rendered, images and PDFs are displayed, other text is syntax-highlighted. Prefer this over printing file contents to the terminal, which the user is probably not looking at. NOTE: this only requests that their client open the file; it cannot confirm the user actually looked at it, so do not assume they have seen it.';
18
+ const PREVIEW_TOOL_DESCRIPTION = 'Open a file in the preview panel of every web client the user currently has open. Use this whenever you produce or want to show the user a file: a document or report you just wrote, a generated image or diagram, a PDF, a data file, or a source file you want them to look at. Trigger phrases include: "write me a doc", "show me that file", "let me see it", "\u5199\u4E2A\u6587\u6863", "\u7ED9\u6211\u770B\u770B\u8FD9\u4E2A\u6587\u4EF6", "\u6253\u5F00\u770B\u4E0B". Pass an absolute path (or one starting with ~). The file is read on this machine and rendered in their browser \u2014 markdown is rendered, images and PDFs are displayed, other text is syntax-highlighted. Prefer this over printing file contents to the terminal, which the user is probably not looking at. Set download: true when the user wants to GET the file rather than read it \u2014 any type, including zip archives, Word/Excel/PowerPoint documents, binaries and installers ("send me the file", "let me download it", "\u7ED9\u6211\u4E0B\u8F7D", "\u53D1\u6211\u8FD9\u4E2A\u6587\u4EF6"). Their client then shows a prominent download button (and may start the download on desktop). Only single regular files can be downloaded: zip a directory first and pass the archive. The web download limit is 50 MiB; compress or split anything larger. NOTE: this only requests that their client open the file; it cannot confirm the user actually looked at it or downloaded it, so do not assume they have.';
19
+ const PREVIEW_DOWNLOAD_PARAM_DESCRIPTION = "true when the user wants to download/receive the file itself (any type) rather than just read it";
18
20
  const REPORT_PROGRESS_TOOL_NAME = "report_progress";
19
21
  const REPORT_PROGRESS_TOOL_TITLE = "Report Progress to the Task Board";
20
22
  const REPORT_PROGRESS_TOOL_DESCRIPTION = `Report what you are doing to the user's task board, so they can see the state of this session at a glance without reading the transcript. Call it at meaningful milestones only: when you start substantive work, when you get blocked and need the user, when something is ready for their review, and when you are done. Do NOT call it after every tool call or every file edit \u2014 it is a status line, not a log. Set attention to "blocked" when you cannot proceed without the user, "review" when you want them to look at something, and "none" for ordinary progress. Keep progress to one short line describing the current state (the user reads it on a card).`;
21
23
 
22
24
  exports.AGENT_GUIDANCE = AGENT_GUIDANCE;
25
+ exports.PREVIEW_DOWNLOAD_PARAM_DESCRIPTION = PREVIEW_DOWNLOAD_PARAM_DESCRIPTION;
23
26
  exports.PREVIEW_TOOL_DESCRIPTION = PREVIEW_TOOL_DESCRIPTION;
24
27
  exports.PREVIEW_TOOL_NAME = PREVIEW_TOOL_NAME;
25
28
  exports.PREVIEW_TOOL_TITLE = PREVIEW_TOOL_TITLE;
@@ -1,13 +1,13 @@
1
1
  import chalk from 'chalk';
2
2
  import { homedir } from 'node:os';
3
- import { readSettings, updateSettings } from './persistence-DujtUH94.mjs';
3
+ import { r as readSettings, u as updateSettings } from './persistence-ON0zpHfb.mjs';
4
4
  import 'node:fs';
5
- import './logger-CknSVkIe.mjs';
5
+ import './logger-CbBv3q_b.mjs';
6
6
  import { d as describeAgentHome, p as probeLoginShellEnv, r as resolveAgentHomes, e as expandHomePath, A as AGENT_HOME_ENV_NAMES } from './loginShellEnv-Dw8M7tkI.mjs';
7
7
  import 'node:fs/promises';
8
- import './configuration-L1qcGTFM.mjs';
8
+ import './configuration-WspKd2C2.mjs';
9
9
  import 'node:path';
10
- import './package-VIUXrYZ1.mjs';
10
+ import './package-DU1DxhH9.mjs';
11
11
  import 'zod';
12
12
  import './authRelay-DSAvg1uv.mjs';
13
13
  import 'node:crypto';
@@ -2,14 +2,14 @@
2
2
 
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
- var persistence = require('./persistence-C6ztVW1W.cjs');
5
+ var persistence = require('./persistence-BYGl0QYw.cjs');
6
6
  require('node:fs');
7
- require('./logger-D0hkk7Vo.cjs');
7
+ require('./logger-B_MWPbZz.cjs');
8
8
  var loginShellEnv = require('./loginShellEnv-BkuYtExZ.cjs');
9
9
  require('node:fs/promises');
10
- require('./configuration-BE61XNsg.cjs');
10
+ require('./configuration-C3piBtxY.cjs');
11
11
  require('node:path');
12
- require('./package-UkmvSFsg.cjs');
12
+ require('./package-C9ZP9bpX.cjs');
13
13
  require('zod');
14
14
  require('./authRelay-BdYkC1Po.cjs');
15
15
  require('node:crypto');
@@ -1,22 +1,22 @@
1
1
  'use strict';
2
2
 
3
3
  var axios = require('axios');
4
- var logger = require('./logger-D0hkk7Vo.cjs');
5
- var lidState = require('./lidState-z9ekRpZu.cjs');
4
+ var logger = require('./logger-B_MWPbZz.cjs');
5
+ var lidState = require('./lidState-Bc2K9H9l.cjs');
6
6
  var node_events = require('node:events');
7
7
  var socket_ioClient = require('socket.io-client');
8
- var attachmentLimits = require('./attachmentLimits-eJZ4ZBjF.cjs');
8
+ var attachmentLimits = require('./attachmentLimits-BEYEEBm8.cjs');
9
9
  var authRelay = require('./authRelay-BdYkC1Po.cjs');
10
10
  var limits = require('./limits-C-a7gr04.cjs');
11
- var time = require('./time-BbzJ7PJQ.cjs');
12
- var configuration = require('./configuration-BE61XNsg.cjs');
11
+ var time = require('./time-BXvtjhe9.cjs');
12
+ var configuration = require('./configuration-C3piBtxY.cjs');
13
13
  var node_crypto = require('node:crypto');
14
- var index = require('./index-DqlyNgt4.cjs');
15
- var registerCommonHandlers = require('./registerCommonHandlers--8ds2auE.cjs');
14
+ var index = require('./index-C9hI8a10.cjs');
15
+ var registerCommonHandlers = require('./registerCommonHandlers-DVYB3iVn.cjs');
16
16
  var cuid2 = require('@paralleldrive/cuid2');
17
- var sync = require('./sync-DwUUGGFz.cjs');
17
+ var sync = require('./sync-Bvc-vh6C.cjs');
18
18
  var backgroundTaskActivity = require('./backgroundTaskActivity-CBvUdj39.cjs');
19
- var controlClient = require('./controlClient-RWTlQA4_.cjs');
19
+ var controlClient = require('./controlClient-BI6Fw3fQ.cjs');
20
20
  var contentLogMetadata = require('./contentLogMetadata-ZSFk_NjQ.cjs');
21
21
  var chalk = require('chalk');
22
22
 
@@ -1590,6 +1590,14 @@ class ApiSessionClient extends node_events.EventEmitter {
1590
1590
  }
1591
1591
  }
1592
1592
  routeIncomingMessage(message, sourceLocalId) {
1593
+ if (this.inboundFrozen) {
1594
+ logger.logger.debug(`[API] Hibernating: holding inbound ${sourceLocalId ?? "(no localId)"}`);
1595
+ this.frozenInbound.push({ message, sourceLocalId });
1596
+ this.inboundRoutedDuringCommit = true;
1597
+ return;
1598
+ }
1599
+ this.inboundRoutedDuringCommit = true;
1600
+ this.noteActivity();
1593
1601
  if (sourceLocalId) this.promptQueueDrain?.onInbound(sourceLocalId);
1594
1602
  const messageWithSource = sourceLocalId && typeof message === "object" && message !== null ? { ...message, localKey: message.localKey ?? sourceLocalId } : message;
1595
1603
  const userResult = attachmentLimits.UserMessageSchema.safeParse(messageWithSource);
@@ -1835,6 +1843,7 @@ class ApiSessionClient extends node_events.EventEmitter {
1835
1843
  }
1836
1844
  }
1837
1845
  enqueueMessage(content, invalidate = true, localId) {
1846
+ this.noteActivity();
1838
1847
  const encrypted = authRelay.encodeBase64(authRelay.encrypt(this.encryptionKey, this.encryptionVariant, content));
1839
1848
  this.pendingOutbox.push({
1840
1849
  content: encrypted,
@@ -2011,6 +2020,52 @@ class ApiSessionClient extends node_events.EventEmitter {
2011
2020
  };
2012
2021
  this.enqueueMessage(content);
2013
2022
  }
2023
+ // ── B-587 hibernation support ──────────────────────────────────────────
2024
+ /** Last user/agent activity this wrapper saw (spec §2.1). */
2025
+ lastActivity = Date.now();
2026
+ inboundFrozen = false;
2027
+ frozenInbound = [];
2028
+ heartbeatsMuted = false;
2029
+ lastHeartbeat = 0;
2030
+ get lastHeartbeatAt() {
2031
+ return this.lastHeartbeat;
2032
+ }
2033
+ muteHeartbeats() {
2034
+ this.heartbeatsMuted = true;
2035
+ }
2036
+ unmuteHeartbeats() {
2037
+ this.heartbeatsMuted = false;
2038
+ }
2039
+ inboundRoutedDuringCommit = false;
2040
+ noteActivity(now = Date.now()) {
2041
+ if (now > this.lastActivity) this.lastActivity = now;
2042
+ }
2043
+ get lastActivityAt() {
2044
+ return this.lastActivity;
2045
+ }
2046
+ /** The message cursor (highest seq this wrapper has seen). */
2047
+ get messageCursor() {
2048
+ return this.lastSeq;
2049
+ }
2050
+ /** Start watching for inbound routed during the hibernation commit window. */
2051
+ beginHibernationCommit() {
2052
+ this.inboundRoutedDuringCommit = false;
2053
+ }
2054
+ /** Was any inbound message routed since beginHibernationCommit()? */
2055
+ inboundRoutedSinceCommit() {
2056
+ return this.inboundRoutedDuringCommit;
2057
+ }
2058
+ /** Stop routing inbound messages; the next wrapper replays them (spec §3/§4). */
2059
+ freezeInbound() {
2060
+ this.inboundFrozen = true;
2061
+ }
2062
+ /** An aborted hibernation: route whatever arrived while frozen, in order. */
2063
+ unfreezeInbound() {
2064
+ this.inboundFrozen = false;
2065
+ const held = this.frozenInbound;
2066
+ this.frozenInbound = [];
2067
+ for (const item of held) this.routeIncomingMessage(item.message, item.sourceLocalId);
2068
+ }
2014
2069
  /**
2015
2070
  * Send a ping message to keep the connection alive
2016
2071
  */
@@ -2029,11 +2084,14 @@ class ApiSessionClient extends node_events.EventEmitter {
2029
2084
  }));
2030
2085
  });
2031
2086
  keepAlive(thinking, mode, backgroundTasks) {
2087
+ if (this.heartbeatsMuted) return;
2088
+ this.lastHeartbeat = Date.now();
2032
2089
  if (process.env.DEBUG) {
2033
2090
  logger.logger.debug(`[API] Sending keep alive message: ${thinking}`);
2034
2091
  }
2035
2092
  this.turnReporter.observe(thinking);
2036
2093
  if (backgroundTasks) this.backgroundTaskReporter.observe(backgroundTasks);
2094
+ if (thinking || (backgroundTasks?.length ?? 0) > 0) this.noteActivity();
2037
2095
  this.socket.volatile.emit("session-alive", {
2038
2096
  sid: this.sessionId,
2039
2097
  time: Date.now(),
@@ -2112,14 +2170,16 @@ class ApiSessionClient extends node_events.EventEmitter {
2112
2170
  * payload on this channel is (clipboard, fs RPC) — and a path leaks
2113
2171
  * project/client names and directory structure.
2114
2172
  */
2115
- pushFilePreview(path, mode = "file") {
2173
+ pushFilePreview(path, mode = "file", intent) {
2116
2174
  if (!this.socket.connected) {
2117
2175
  return { delivered: false, error: "not connected to the server" };
2118
2176
  }
2119
2177
  this.socket.emit("file-preview-push", {
2120
2178
  payload: authRelay.encodeBase64(authRelay.encrypt(this.encryptionKey, this.encryptionVariant, path)),
2121
2179
  enc: true,
2122
- mode
2180
+ mode,
2181
+ // B-562: only present for downloads, so a plain preview keeps the old wire shape.
2182
+ ...intent ? { intent } : {}
2123
2183
  });
2124
2184
  return { delivered: true };
2125
2185
  }
@@ -2224,7 +2284,26 @@ class ApiSessionClient extends node_events.EventEmitter {
2224
2284
  });
2225
2285
  }
2226
2286
  updateMetadata(handler) {
2227
- this.metadataLock.inLock(async () => {
2287
+ void this.writeMetadata(handler);
2288
+ }
2289
+ /**
2290
+ * B-587: the same write, awaitable with a ceiling — the hibernation stamp
2291
+ * must land before `session-end`. Resolves false on timeout (the write
2292
+ * itself keeps retrying in the background, exactly like updateMetadata).
2293
+ */
2294
+ async updateMetadataAndWait(handler, timeoutMs = 1e4) {
2295
+ let timer;
2296
+ const timeout = new Promise((resolve) => {
2297
+ timer = setTimeout(() => resolve(false), timeoutMs);
2298
+ });
2299
+ try {
2300
+ return await Promise.race([this.writeMetadata(handler).then(() => true), timeout]);
2301
+ } finally {
2302
+ if (timer) clearTimeout(timer);
2303
+ }
2304
+ }
2305
+ writeMetadata(handler) {
2306
+ return this.metadataLock.inLock(async () => {
2228
2307
  let rateRefusals = 0;
2229
2308
  await time.backoff(async () => {
2230
2309
  let updated = handler(this.metadata);
@@ -2270,6 +2349,7 @@ class ApiSessionClient extends node_events.EventEmitter {
2270
2349
  this.agentState = answer.agentState ? authRelay.decrypt(this.encryptionKey, this.encryptionVariant, authRelay.decodeBase64(answer.agentState)) : null;
2271
2350
  this.agentStateVersion = answer.version;
2272
2351
  rateRefusals = 0;
2352
+ if (Object.keys(this.agentState?.requests ?? {}).length > 0) this.noteActivity();
2273
2353
  logger.logger.debug("Agent state updated", this.agentState);
2274
2354
  } else if (answer.result === "version-mismatch") {
2275
2355
  if (answer.version > this.agentStateVersion) {
@@ -3086,7 +3166,7 @@ class ApiClient {
3086
3166
  return new ApiSessionClient(this.credential.token, session, opts);
3087
3167
  }
3088
3168
  async machineSyncClient(machine) {
3089
- const { ApiMachineClient } = await Promise.resolve().then(function () { return require('./apiMachine-B_cJldfa.cjs'); });
3169
+ const { ApiMachineClient } = await Promise.resolve().then(function () { return require('./apiMachine-BgMxGPsR.cjs'); });
3090
3170
  return new ApiMachineClient(this.credential.token, machine);
3091
3171
  }
3092
3172
  push() {
@@ -1,20 +1,20 @@
1
1
  import axios from 'axios';
2
- import { l as logger, r as redactLogValue } from './logger-CknSVkIe.mjs';
3
- import { R as RpcHandlerManager, a as answerSocketRequest, c as connectGuarded, s as socketConnectGuard, d as describeSocketAckDrop, b as safeAck, i as isRateQuotaCode, p as pauseForRateQuota, e as shouldReconnect } from './lidState-BLHLmhwd.mjs';
2
+ import { l as logger, r as redactLogValue } from './logger-CbBv3q_b.mjs';
3
+ import { R as RpcHandlerManager, a as answerSocketRequest, c as connectGuarded, s as socketConnectGuard, d as describeSocketAckDrop, b as safeAck, i as isRateQuotaCode, p as pauseForRateQuota, e as shouldReconnect } from './lidState-B2ZxdREe.mjs';
4
4
  import { EventEmitter } from 'node:events';
5
5
  import { io } from 'socket.io-client';
6
- import { A as AsyncLock, a as MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES, U as UserMessageSchema, F as FileEventMessageSchema } from './attachmentLimits-B8nzhw-i.mjs';
6
+ import { A as AsyncLock, a as MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES, U as UserMessageSchema, F as FileEventMessageSchema } from './attachmentLimits-CxG3T18E.mjs';
7
7
  import { h as decrypt, d as decodeBase64, i as decryptBlob, e as encodeBase64, g as encrypt, l as libsodiumEncryptForPublicKey, j as libsodiumPublicKeyFromSecretKey, k as getRandomBytes } from './authRelay-DSAvg1uv.mjs';
8
8
  import { p as prepareClipboardText, d as prepareClipboardHistoryText } from './limits-DIo7rEBR.mjs';
9
- import { b as backoff, d as delay, e as exponentialBackoffDelay } from './time-v1Y_qEJp.mjs';
10
- import { c as configuration } from './configuration-L1qcGTFM.mjs';
9
+ import { b as backoff, d as delay, e as exponentialBackoffDelay } from './time-PG5Dfwdv.mjs';
10
+ import { c as configuration } from './configuration-WspKd2C2.mjs';
11
11
  import { createHmac, randomUUID } from 'node:crypto';
12
- import { e as createEnvelope, s as streamKeyOf, R as ReleaseDrainNoticeSchema, m as RelayAssignmentResponseSchema } from './index-vVWpCoTr.mjs';
13
- import { r as registerCommonHandlers } from './registerCommonHandlers-BR0J4g64.mjs';
12
+ import { c as createEnvelope, s as streamKeyOf, R as ReleaseDrainNoticeSchema, q as RelayAssignmentResponseSchema } from './index-B0HEEYGh.mjs';
13
+ import { r as registerCommonHandlers } from './registerCommonHandlers-B0PVff2U.mjs';
14
14
  import { createId } from '@paralleldrive/cuid2';
15
- import { I as InvalidateSync } from './sync-Dfm6K2te.mjs';
15
+ import { I as InvalidateSync } from './sync-CCwYpJMS.mjs';
16
16
  import { B as BackgroundTaskReporter } from './backgroundTaskActivity-8PdPxrvy.mjs';
17
- import { h as notifyDaemonTurnEvent, j as notifyDaemonBackgroundTasks } from './controlClient-Ci7HTMII.mjs';
17
+ import { k as notifyDaemonTurnEvent, m as notifyDaemonBackgroundTasks } from './controlClient-DTheOlvC.mjs';
18
18
  import { c as contentLogMetadata } from './contentLogMetadata-BlWI0egu.mjs';
19
19
  import chalk from 'chalk';
20
20
 
@@ -1588,6 +1588,14 @@ class ApiSessionClient extends EventEmitter {
1588
1588
  }
1589
1589
  }
1590
1590
  routeIncomingMessage(message, sourceLocalId) {
1591
+ if (this.inboundFrozen) {
1592
+ logger.debug(`[API] Hibernating: holding inbound ${sourceLocalId ?? "(no localId)"}`);
1593
+ this.frozenInbound.push({ message, sourceLocalId });
1594
+ this.inboundRoutedDuringCommit = true;
1595
+ return;
1596
+ }
1597
+ this.inboundRoutedDuringCommit = true;
1598
+ this.noteActivity();
1591
1599
  if (sourceLocalId) this.promptQueueDrain?.onInbound(sourceLocalId);
1592
1600
  const messageWithSource = sourceLocalId && typeof message === "object" && message !== null ? { ...message, localKey: message.localKey ?? sourceLocalId } : message;
1593
1601
  const userResult = UserMessageSchema.safeParse(messageWithSource);
@@ -1833,6 +1841,7 @@ class ApiSessionClient extends EventEmitter {
1833
1841
  }
1834
1842
  }
1835
1843
  enqueueMessage(content, invalidate = true, localId) {
1844
+ this.noteActivity();
1836
1845
  const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
1837
1846
  this.pendingOutbox.push({
1838
1847
  content: encrypted,
@@ -2009,6 +2018,52 @@ class ApiSessionClient extends EventEmitter {
2009
2018
  };
2010
2019
  this.enqueueMessage(content);
2011
2020
  }
2021
+ // ── B-587 hibernation support ──────────────────────────────────────────
2022
+ /** Last user/agent activity this wrapper saw (spec §2.1). */
2023
+ lastActivity = Date.now();
2024
+ inboundFrozen = false;
2025
+ frozenInbound = [];
2026
+ heartbeatsMuted = false;
2027
+ lastHeartbeat = 0;
2028
+ get lastHeartbeatAt() {
2029
+ return this.lastHeartbeat;
2030
+ }
2031
+ muteHeartbeats() {
2032
+ this.heartbeatsMuted = true;
2033
+ }
2034
+ unmuteHeartbeats() {
2035
+ this.heartbeatsMuted = false;
2036
+ }
2037
+ inboundRoutedDuringCommit = false;
2038
+ noteActivity(now = Date.now()) {
2039
+ if (now > this.lastActivity) this.lastActivity = now;
2040
+ }
2041
+ get lastActivityAt() {
2042
+ return this.lastActivity;
2043
+ }
2044
+ /** The message cursor (highest seq this wrapper has seen). */
2045
+ get messageCursor() {
2046
+ return this.lastSeq;
2047
+ }
2048
+ /** Start watching for inbound routed during the hibernation commit window. */
2049
+ beginHibernationCommit() {
2050
+ this.inboundRoutedDuringCommit = false;
2051
+ }
2052
+ /** Was any inbound message routed since beginHibernationCommit()? */
2053
+ inboundRoutedSinceCommit() {
2054
+ return this.inboundRoutedDuringCommit;
2055
+ }
2056
+ /** Stop routing inbound messages; the next wrapper replays them (spec §3/§4). */
2057
+ freezeInbound() {
2058
+ this.inboundFrozen = true;
2059
+ }
2060
+ /** An aborted hibernation: route whatever arrived while frozen, in order. */
2061
+ unfreezeInbound() {
2062
+ this.inboundFrozen = false;
2063
+ const held = this.frozenInbound;
2064
+ this.frozenInbound = [];
2065
+ for (const item of held) this.routeIncomingMessage(item.message, item.sourceLocalId);
2066
+ }
2012
2067
  /**
2013
2068
  * Send a ping message to keep the connection alive
2014
2069
  */
@@ -2027,11 +2082,14 @@ class ApiSessionClient extends EventEmitter {
2027
2082
  }));
2028
2083
  });
2029
2084
  keepAlive(thinking, mode, backgroundTasks) {
2085
+ if (this.heartbeatsMuted) return;
2086
+ this.lastHeartbeat = Date.now();
2030
2087
  if (process.env.DEBUG) {
2031
2088
  logger.debug(`[API] Sending keep alive message: ${thinking}`);
2032
2089
  }
2033
2090
  this.turnReporter.observe(thinking);
2034
2091
  if (backgroundTasks) this.backgroundTaskReporter.observe(backgroundTasks);
2092
+ if (thinking || (backgroundTasks?.length ?? 0) > 0) this.noteActivity();
2035
2093
  this.socket.volatile.emit("session-alive", {
2036
2094
  sid: this.sessionId,
2037
2095
  time: Date.now(),
@@ -2110,14 +2168,16 @@ class ApiSessionClient extends EventEmitter {
2110
2168
  * payload on this channel is (clipboard, fs RPC) — and a path leaks
2111
2169
  * project/client names and directory structure.
2112
2170
  */
2113
- pushFilePreview(path, mode = "file") {
2171
+ pushFilePreview(path, mode = "file", intent) {
2114
2172
  if (!this.socket.connected) {
2115
2173
  return { delivered: false, error: "not connected to the server" };
2116
2174
  }
2117
2175
  this.socket.emit("file-preview-push", {
2118
2176
  payload: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, path)),
2119
2177
  enc: true,
2120
- mode
2178
+ mode,
2179
+ // B-562: only present for downloads, so a plain preview keeps the old wire shape.
2180
+ ...intent ? { intent } : {}
2121
2181
  });
2122
2182
  return { delivered: true };
2123
2183
  }
@@ -2222,7 +2282,26 @@ class ApiSessionClient extends EventEmitter {
2222
2282
  });
2223
2283
  }
2224
2284
  updateMetadata(handler) {
2225
- this.metadataLock.inLock(async () => {
2285
+ void this.writeMetadata(handler);
2286
+ }
2287
+ /**
2288
+ * B-587: the same write, awaitable with a ceiling — the hibernation stamp
2289
+ * must land before `session-end`. Resolves false on timeout (the write
2290
+ * itself keeps retrying in the background, exactly like updateMetadata).
2291
+ */
2292
+ async updateMetadataAndWait(handler, timeoutMs = 1e4) {
2293
+ let timer;
2294
+ const timeout = new Promise((resolve) => {
2295
+ timer = setTimeout(() => resolve(false), timeoutMs);
2296
+ });
2297
+ try {
2298
+ return await Promise.race([this.writeMetadata(handler).then(() => true), timeout]);
2299
+ } finally {
2300
+ if (timer) clearTimeout(timer);
2301
+ }
2302
+ }
2303
+ writeMetadata(handler) {
2304
+ return this.metadataLock.inLock(async () => {
2226
2305
  let rateRefusals = 0;
2227
2306
  await backoff(async () => {
2228
2307
  let updated = handler(this.metadata);
@@ -2268,6 +2347,7 @@ class ApiSessionClient extends EventEmitter {
2268
2347
  this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
2269
2348
  this.agentStateVersion = answer.version;
2270
2349
  rateRefusals = 0;
2350
+ if (Object.keys(this.agentState?.requests ?? {}).length > 0) this.noteActivity();
2271
2351
  logger.debug("Agent state updated", this.agentState);
2272
2352
  } else if (answer.result === "version-mismatch") {
2273
2353
  if (answer.version > this.agentStateVersion) {
@@ -3084,7 +3164,7 @@ class ApiClient {
3084
3164
  return new ApiSessionClient(this.credential.token, session, opts);
3085
3165
  }
3086
3166
  async machineSyncClient(machine) {
3087
- const { ApiMachineClient } = await import('./apiMachine-BQ-b4Lu3.mjs');
3167
+ const { ApiMachineClient } = await import('./apiMachine-CWndf1Vn.mjs');
3088
3168
  return new ApiMachineClient(this.credential.token, machine);
3089
3169
  }
3090
3170
  push() {