very-happy-cli 0.2.177 → 0.2.179

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 (203) hide show
  1. package/dist/{AcpBackend-B0K0WrtW.mjs → AcpBackend-CHp3y7cb.mjs} +135 -16
  2. package/dist/{AcpBackend-B-ny68XM.cjs → AcpBackend-DFipFF7-.cjs} +135 -16
  3. package/dist/{AcpSessionManager-CjdJLofq.cjs → AcpSessionManager-0zpvRJXc.cjs} +38 -4
  4. package/dist/{AcpSessionManager-DykjzBM4.mjs → AcpSessionManager-d2-rKWWa.mjs} +38 -4
  5. package/dist/{BasePermissionHandler-DR6nxuEn.cjs → BasePermissionHandler-CH3zuEkA.cjs} +5 -2
  6. package/dist/{BasePermissionHandler-hdY7kTqX.mjs → BasePermissionHandler-DdZBYIR4.mjs} +5 -2
  7. package/dist/acpSessionResume-DTGmAPLt.mjs +67 -0
  8. package/dist/acpSessionResume-oiCBnAVA.cjs +74 -0
  9. package/dist/{agentHome-BdTXEUPe.cjs → agentHome-BWcz8mz0.cjs} +5 -5
  10. package/dist/{agentHome-GRN2VfHv.mjs → agentHome-CghLvL_1.mjs} +5 -5
  11. package/dist/{api-BWX10o97.cjs → api-Cmjfr8Z7.cjs} +29 -261
  12. package/dist/{api-CKBViqI3.mjs → api-CzwFpJQc.mjs} +22 -254
  13. package/dist/{apiMachine-DttO4UFj.mjs → apiMachine-DJkTEiqy.mjs} +53 -16
  14. package/dist/{apiMachine-BYYFGgFs.cjs → apiMachine-G6OogT08.cjs} +53 -16
  15. package/dist/{attachmentContent-cPVF463_.mjs → attachmentContent-B2os4HbA.mjs} +2 -2
  16. package/dist/{attachmentContent-kJKXpkMX.cjs → attachmentContent-Bo0UAsSu.cjs} +4 -4
  17. package/dist/{auth-BmESeKuM.mjs → auth-BS6PaOOG.mjs} +6 -6
  18. package/dist/{auth-CTECt2Oz.cjs → auth-D6bBUwZa.cjs} +6 -6
  19. package/dist/{auth-CKdXb9zp.mjs → auth-DMX0xPNK.mjs} +9 -9
  20. package/dist/{auth-DNxVmVbn.cjs → auth-HEhRaNgL.cjs} +9 -9
  21. package/dist/{authRelay-DSAvg1uv.mjs → authRelay-DhQhkBvX.mjs} +1 -1
  22. package/dist/{authRelay-BdYkC1Po.cjs → authRelay-Dy1qU9td.cjs} +1 -0
  23. package/dist/{auto-D1ypjjfx.cjs → auto-C6I55iOh.cjs} +19 -18
  24. package/dist/{auto-Ch3203qd.mjs → auto-Cl1SEDcD.mjs} +19 -18
  25. package/dist/{bootstrap-6OI698q9.cjs → bootstrap-exVucYav.cjs} +2 -2
  26. package/dist/{bootstrap-C0s2bPFQ.mjs → bootstrap-iuyufnp3.mjs} +2 -2
  27. package/dist/{browser-DG6Uof5O.cjs → browser-BohJK12g.cjs} +1 -1
  28. package/dist/{browser-BlLWZUMx.mjs → browser-CSYum7xH.mjs} +1 -1
  29. package/dist/{claudeLocal-CcWcKnoo.mjs → claudeLocal-Dq_R3Ax_.mjs} +6 -5
  30. package/dist/{claudeLocal-CemsIArT.cjs → claudeLocal-re4yjSTx.cjs} +6 -5
  31. package/dist/{claudeLocalLauncher-A4VuevIi.cjs → claudeLocalLauncher-B4hJHpCA.cjs} +11 -10
  32. package/dist/{claudeLocalLauncher-7p6y88nj.mjs → claudeLocalLauncher-CN67lN7-.mjs} +11 -10
  33. package/dist/{claudeSessionFork-BtROOjqf.mjs → claudeSessionFork-BihfD0Ik.mjs} +1 -1
  34. package/dist/{claudeSessionFork-CbDfm8Fz.cjs → claudeSessionFork-DiSMYerV.cjs} +1 -1
  35. package/dist/{client-DyvBTYee.cjs → client-BKcK1Bi-.cjs} +3 -3
  36. package/dist/client-BhoEBnuN.mjs +71 -0
  37. package/dist/{client-COmgDDy0.cjs → client-CuVdN6PP.cjs} +2 -2
  38. package/dist/{client-BPMWutfs.mjs → client-DuTDwWSQ.mjs} +3 -3
  39. package/dist/client-ZjOsn6nZ.cjs +76 -0
  40. package/dist/{client-DG5ndWLw.mjs → client-wo9nbxuf.mjs} +2 -2
  41. package/dist/codex/happyMcpStdioBridge.cjs +25 -16
  42. package/dist/codex/happyMcpStdioBridge.mjs +25 -16
  43. package/dist/{codexAppServerClient-D-Z8qSfI.mjs → codexAppServerClient-BK3zra_1.mjs} +3 -3
  44. package/dist/{codexAppServerClient-R24WZZfg.cjs → codexAppServerClient-Dt5zxrun.cjs} +3 -3
  45. package/dist/{codexCommand-CVTkZJ1o.mjs → codexCommand-B6pY7CMh.mjs} +66 -61
  46. package/dist/{codexCommand-6hW19gsq.cjs → codexCommand-CAZ1SLjg.cjs} +89 -84
  47. package/dist/{codexTermination-CCIfwzcx.mjs → codexTermination-Cd1Eon_4.mjs} +1 -1
  48. package/dist/{codexTermination-BBU7e92y.cjs → codexTermination-DmGbHxcD.cjs} +1 -1
  49. package/dist/{config-CvsNChKX.mjs → config-2oMG_fEI.mjs} +3 -3
  50. package/dist/{config-DhdrvOLV.cjs → config-B0uK6K7n.cjs} +3 -3
  51. package/dist/{configuration-DZPy0T9Z.cjs → configuration-CMHZnp6G.cjs} +1 -1
  52. package/dist/{configuration-D55F0t16.mjs → configuration-WMi4idBB.mjs} +1 -1
  53. package/dist/{connect-CwCXMRmD.mjs → connect-BLVHhHzA.mjs} +15 -15
  54. package/dist/{connect-Wz-XZYXW.cjs → connect-uaglGrde.cjs} +15 -15
  55. package/dist/{context-BKbrZ55r.cjs → context-DXzU5bFM.cjs} +1 -1
  56. package/dist/{context-CCRpVXFo.mjs → context-DploEcoN.mjs} +1 -1
  57. package/dist/{controlClient-C4xB5J6O.mjs → controlClient-BWUJrj3t.mjs} +3 -3
  58. package/dist/{controlClient-SX8gSza2.cjs → controlClient-C-5qCt8W.cjs} +3 -3
  59. package/dist/{createSessionMetadata-Bi-m0Y_O.mjs → createSessionMetadata-3uXrJgOW.mjs} +2 -2
  60. package/dist/{createSessionMetadata-BCFmYlBo.cjs → createSessionMetadata-BkIzHALd.cjs} +2 -2
  61. package/dist/{defaultPermissionMode-wBcbBezm.mjs → defaultPermissionMode-BzxLh20a.mjs} +2 -2
  62. package/dist/{defaultPermissionMode-DMBEnXll.cjs → defaultPermissionMode-pS3vmiSg.cjs} +2 -2
  63. package/dist/{deterministicJson-ChGfQEF6.cjs → deterministicJson-B8z2GPi9.cjs} +220 -14
  64. package/dist/{deterministicJson-BkQETt8_.mjs → deterministicJson-D_whDMFd.mjs} +217 -12
  65. package/dist/{doctor-BsN3JZJn.cjs → doctor-Br1Kcy2J.cjs} +8 -8
  66. package/dist/{doctor-B9c2C9dF.mjs → doctor-CmtSsR2-.mjs} +8 -8
  67. package/dist/{downloadTarget-Dj3WoVJa.cjs → downloadTarget-BQJK_6oH.cjs} +6 -6
  68. package/dist/{downloadTarget-ByXzbDK4.mjs → downloadTarget-BoNwTIH_.mjs} +6 -6
  69. package/dist/{ensureDaemonRunning-7Wb8M7Lv.cjs → ensureDaemonRunning-BRUmsq6J.cjs} +5 -5
  70. package/dist/{ensureDaemonRunning--HYzs5Ce.mjs → ensureDaemonRunning-Czo9IHJQ.mjs} +5 -5
  71. package/dist/{handleResumeCommand-DHddBkNJ.mjs → handleResumeCommand-C8thKjqf.mjs} +22 -6
  72. package/dist/{handleResumeCommand-OmuUGYjQ.cjs → handleResumeCommand-tKD_Hpxc.cjs} +22 -6
  73. package/dist/{index-CrR4JISa.cjs → index-B0UjHlAe.cjs} +5 -5
  74. package/dist/{index-BzJNDsJq.mjs → index-BTxHphpV.mjs} +254 -49
  75. package/dist/index-BiyC7cZ3.mjs +18 -0
  76. package/dist/{index-De8L5lFK.cjs → index-C6O2vVjm.cjs} +156 -34
  77. package/dist/index-CUuc6iCn.cjs +24 -0
  78. package/dist/{index-Dhl6_bw7.mjs → index-CdQI0C9p.mjs} +5 -5
  79. package/dist/{index-D9NoYc5I.cjs → index-D09T1m8Y.cjs} +3 -3
  80. package/dist/{index-CEm0MYiS.mjs → index-D3bidoG8.mjs} +3 -3
  81. package/dist/{index-BRJzH4sc.cjs → index-DMqDMT1Q.cjs} +257 -52
  82. package/dist/{index-DMy3OCBe.mjs → index-DPOa4NhL.mjs} +154 -35
  83. package/dist/index.cjs +2 -2
  84. package/dist/index.mjs +2 -2
  85. package/dist/{install-BdvKiOQw.mjs → install-BX3lv3NO.mjs} +1 -1
  86. package/dist/{install-Cf6AFSWC.cjs → install-BXvf2E3o.cjs} +1 -1
  87. package/dist/{install-1qn-D9FS.cjs → install-CYRZxS87.cjs} +3 -3
  88. package/dist/{install-BDIUtPXi.mjs → install-hnve_9vq.mjs} +3 -3
  89. package/dist/lib.cjs +14 -14
  90. package/dist/lib.d.cts +65 -1
  91. package/dist/lib.d.mts +65 -1
  92. package/dist/lib.mjs +14 -14
  93. package/dist/{lidState-BqVsw7H1.mjs → lidState-Bb8fgXg3.mjs} +2 -2
  94. package/dist/{lidState-DxbK0qfu.cjs → lidState-Dv2wwFTj.cjs} +2 -2
  95. package/dist/{localHappyAgentAuth-D5tANdRD.cjs → localHappyAgentAuth-DjYvkXvV.cjs} +2 -2
  96. package/dist/{localHappyAgentAuth-Ce8kO2yc.mjs → localHappyAgentAuth-NEU7fax7.mjs} +2 -2
  97. package/dist/{logger-13cBr_ew.mjs → logger-BtVhHnD8.mjs} +2 -2
  98. package/dist/{logger-LvwVeATU.cjs → logger-C1GWx3gi.cjs} +2 -2
  99. package/dist/{machineMetadata-29h7vgf4.cjs → machineMetadata-CLHS9sv_.cjs} +3 -3
  100. package/dist/{machineMetadata-dMida2qq.mjs → machineMetadata-D-g--oU_.mjs} +3 -3
  101. package/dist/{main-x4nAhdH5.mjs → main-B2LWV8DI.mjs} +59 -46
  102. package/dist/{main-DpM5jaFH.cjs → main-CWhSsbtU.cjs} +59 -46
  103. package/dist/{mcp-DQv3caXs.cjs → mcp-CARUsXHV.cjs} +15 -15
  104. package/dist/{mcp-DMiFGYMT.mjs → mcp-ext92q3Y.mjs} +15 -15
  105. package/dist/{mirrorProtocol-Bz8tEabR.mjs → mirrorProtocol-BwYibqry.mjs} +3 -3
  106. package/dist/{mirrorProtocol-BfrERW7Z.cjs → mirrorProtocol-DEy2g1CB.cjs} +3 -3
  107. package/dist/{package-CjlsAaRe.mjs → package-BPPebGQF.mjs} +7 -7
  108. package/dist/{package-Ch9WGDV_.cjs → package-SgqTYsjn.cjs} +7 -7
  109. package/dist/panels-BZ0_Tt7B.mjs +137 -0
  110. package/dist/panels-D1c7q2dD.cjs +142 -0
  111. package/dist/{peerTools-CxmrKnfV.mjs → peerTools-CWKzcQ7u.mjs} +60 -10
  112. package/dist/{peerTools-DtGKWli5.cjs → peerTools-DRQETXaz.cjs} +60 -9
  113. package/dist/{persistence-DXbKOHxl.cjs → persistence-BegE7NPa.cjs} +14 -3
  114. package/dist/{persistence-C_tYtOqK.mjs → persistence-Cr-wutZ4.mjs} +14 -4
  115. package/dist/{piRunnerArgs-BVRZe2AR.cjs → piRunnerArgs-Cs-378Vz.cjs} +8 -2
  116. package/dist/{piRunnerArgs-BpHc-JZl.mjs → piRunnerArgs-DwvM_yWA.mjs} +7 -1
  117. package/dist/{piRuntime-D7eio8Ix.cjs → piRuntime-BG-1td_n.cjs} +2 -2
  118. package/dist/{piRuntime-DOdGF395.mjs → piRuntime-Bue5mnlc.mjs} +2 -2
  119. package/dist/{piTerminal-dpxz4Q8D.mjs → piTerminal-B5x9X5yV.mjs} +18 -18
  120. package/dist/{piTerminal-y_PRo2NY.cjs → piTerminal-G4z-xVyy.cjs} +18 -18
  121. package/dist/processTreeSnapshot-CuLJDNC-.mjs +101 -0
  122. package/dist/processTreeSnapshot-mDbnuojn.cjs +105 -0
  123. package/dist/prompt-BOouDaTM.mjs +99 -0
  124. package/dist/prompt-lgF9IE82.cjs +105 -0
  125. package/dist/{attachmentLimits-Di1Z93lN.mjs → pushNotifications-BdFjDjQe.mjs} +303 -3
  126. package/dist/{attachmentLimits-DtFqqiwR.cjs → pushNotifications-DaLQUyi0.cjs} +304 -2
  127. package/dist/{index-DFPT4HOO.cjs → query-Ctfg-5gy.cjs} +25 -5
  128. package/dist/{index-CEjJnq05.mjs → query-DYP_W1wp.mjs} +26 -3
  129. package/dist/{rateLimitWindows-DYHDGuzQ.mjs → rateLimitWindows-CuEqk_0g.mjs} +1 -1
  130. package/dist/{rateLimitWindows-DFnMU2Vt.cjs → rateLimitWindows-Dj_8voBK.cjs} +1 -1
  131. package/dist/{registerCommonHandlers-D8OMpBbQ.mjs → registerCommonHandlers-BAlP7SGc.mjs} +2 -2
  132. package/dist/{registerCommonHandlers-60VuTkdg.cjs → registerCommonHandlers-Cj084Sg7.cjs} +2 -2
  133. package/dist/{registerKillSessionHandler-Drhf7-Vz.mjs → registerKillSessionHandler-PD0jWtQo.mjs} +6 -6
  134. package/dist/{registerKillSessionHandler-pqChBKwO.cjs → registerKillSessionHandler-qMju0ZfF.cjs} +6 -6
  135. package/dist/{remoteSessionClient-B7NvjHD3.cjs → remoteSessionClient-B7i7MkYV.cjs} +5 -5
  136. package/dist/{remoteSessionClient-D01Hso2-.mjs → remoteSessionClient-Bn2qu5C5.mjs} +5 -5
  137. package/dist/{reportProgressTool-Du_zAK4h.cjs → reportProgressTool-CM1JFAHg.cjs} +84 -2
  138. package/dist/{reportProgressTool-B203EZCR.mjs → reportProgressTool-gHItlSzz.mjs} +82 -3
  139. package/dist/{resources-JDPugcDq.cjs → resources-CvIlkzpP.cjs} +30 -0
  140. package/dist/{resources-BdFBQi8r.mjs → resources-y9jfyO-_.mjs} +30 -0
  141. package/dist/{run-DX4Zs3Ug.mjs → run-BM4TxiyN.mjs} +249 -45
  142. package/dist/{run-DZ5jU53c.cjs → run-CNXwIe0A.cjs} +263 -59
  143. package/dist/{runClaude-ClRhHpwR.cjs → runClaude-CWhj2V4Q.cjs} +37 -41
  144. package/dist/{runClaude-CgPHXP0L.mjs → runClaude-C_3I8hvR.mjs} +37 -41
  145. package/dist/{runClaudeDeps-D2v_WDSL.cjs → runClaudeDeps-B81cOKnH.cjs} +295 -157
  146. package/dist/{runClaudeDeps-Dz2iydq4.mjs → runClaudeDeps-QGrOeDe2.mjs} +295 -160
  147. package/dist/{runGemini-DBXxpoY8.mjs → runGemini-BEdWEZLR.mjs} +61 -55
  148. package/dist/{runGemini-BtZqGFkp.cjs → runGemini-CgRLYbt5.cjs} +61 -55
  149. package/dist/{runOpenClaw-D8q8G57u.cjs → runOpenClaw-C1NVmzOL.cjs} +19 -19
  150. package/dist/{runOpenClaw-BOHpzCEU.mjs → runOpenClaw-DdQZAYj1.mjs} +19 -19
  151. package/dist/{sandbox-C452Vrsc.mjs → sandbox-DjQUdCMG.mjs} +5 -5
  152. package/dist/{sandbox-Cu9mMzjx.cjs → sandbox-nocIjliq.cjs} +5 -5
  153. package/dist/selfCheck-CjCF4C3v.mjs +85 -0
  154. package/dist/{selfCheck-gKa3uVr1.cjs → selfCheck-DCt4Uzbq.cjs} +36 -35
  155. package/dist/{send-B8HARi7x.mjs → send-Ct7X4pPU.mjs} +9 -9
  156. package/dist/{send-2Vb5eppI.cjs → send-Dydf_hwZ.cjs} +9 -9
  157. package/dist/{server-Db9Wuam3.cjs → server-CosABFbd.cjs} +7 -7
  158. package/dist/{server-BBWCIZyS.mjs → server-DTQGZMxh.mjs} +5 -5
  159. package/dist/{sessionLock-6qL_N6Rn.cjs → sessionLock-DokGab2R.cjs} +16 -3
  160. package/dist/{sessionLock-ZfvFVdUi.mjs → sessionLock-WYIyaVO4.mjs} +16 -4
  161. package/dist/{sessionOps-CdgbEJgf.mjs → sessionOps-BIGgRube.mjs} +11 -6
  162. package/dist/{sessionOps-Dmt7WUUs.cjs → sessionOps-S-qPBLFf.cjs} +11 -6
  163. package/dist/{sessionScanner-CQL_WSaC.cjs → sessionScanner-CIMM2hvZ.cjs} +3 -3
  164. package/dist/{sessionScanner-CNVaSNOn.mjs → sessionScanner-o5hnMi5e.mjs} +3 -3
  165. package/dist/{sessions-DTomHfFg.mjs → sessions-BJHEZpi2.mjs} +40 -18
  166. package/dist/{sessions-BJblG_Hx.cjs → sessions-JchI1w3z.cjs} +40 -17
  167. package/dist/{setupOfflineReconnection-DVA0VpQL.cjs → setupOfflineReconnection-CNiAt5ZP.cjs} +2 -2
  168. package/dist/{setupOfflineReconnection-C5trsV-j.mjs → setupOfflineReconnection-DMiBO2y9.mjs} +2 -2
  169. package/dist/{spawn-DRyNAqD8.cjs → spawn-6PD7nnoC.cjs} +49 -18
  170. package/dist/{spawn-CQJFgoOq.mjs → spawn-DgvUNYEo.mjs} +49 -18
  171. package/dist/{spawnAgents-CCjJifAW.cjs → spawnAgents-DsXgegHM.cjs} +37 -1
  172. package/dist/spawnAgents-P006Hqio.mjs +79 -0
  173. package/dist/{spawnHappyCLI-B_0RWxD4.mjs → spawnHappyCLI-CCOrEtf9.mjs} +1 -1
  174. package/dist/{spawnHappyCLI-ChniczJ5.cjs → spawnHappyCLI-Dywezwk6.cjs} +1 -1
  175. package/dist/{specialCommands-DkdFJi4N.mjs → specialCommands-DPcd9edP.mjs} +3 -3
  176. package/dist/{specialCommands-DbntVs0d.cjs → specialCommands-QxPpkGKf.cjs} +3 -3
  177. package/dist/{startFileWatcher-UQyTSRAP.mjs → startFileWatcher-DghDOCO4.mjs} +1 -1
  178. package/dist/{startFileWatcher-C292fhra.cjs → startFileWatcher-RECH5qY8.cjs} +1 -1
  179. package/dist/{streamRelay-_qCd873P.mjs → streamRelay-DMV_TXfS.mjs} +1 -1
  180. package/dist/{streamRelay-Cx01enfe.cjs → streamRelay-DlUWKUpo.cjs} +1 -1
  181. package/dist/{sync-C3p7-KLn.mjs → sync-CSe0Rf45.mjs} +1 -1
  182. package/dist/{sync-C7sxoUYG.cjs → sync-D23jbqVa.cjs} +1 -1
  183. package/dist/{systemPrompt-DtibzbRU.cjs → systemPrompt-BOy4kU4P.cjs} +4 -3
  184. package/dist/{systemPrompt-CdHOlyVW.mjs → systemPrompt-CFNKnh65.mjs} +4 -3
  185. package/dist/{teams-ujXr3AFn.mjs → teams-DyY669f0.mjs} +9 -9
  186. package/dist/{teams-D7O7TpVU.cjs → teams-Ov2BDT-A.cjs} +9 -9
  187. package/dist/{time-yEL6wHUF.mjs → time-65DcU6Jx.mjs} +1 -1
  188. package/dist/{time-Dg5tVB8S.cjs → time-CrwIMHta.cjs} +1 -1
  189. package/dist/{titleGenerator-BMEaDTwU.cjs → titleGenerator-CS_rh2_J.cjs} +9 -8
  190. package/dist/{titleGenerator-Dr-BtjzO.mjs → titleGenerator-CiAMxf7s.mjs} +9 -8
  191. package/dist/{todo-CvJe4M2C.mjs → todo-BDONw0sK.mjs} +7 -7
  192. package/dist/{todo-CUcFamGs.cjs → todo-KY-okGii.cjs} +7 -7
  193. package/dist/{tools-BrM3bZW5.mjs → tools-D8-PnZEG.mjs} +1 -1
  194. package/dist/{tools-DDKTB5aQ.cjs → tools-_w52DZG6.cjs} +1 -1
  195. package/dist/{uninstall-7oLuhYIu.cjs → uninstall-8vnLN-IE.cjs} +3 -3
  196. package/dist/{uninstall-CEcMMQGz.mjs → uninstall-_fjGRP9b.mjs} +3 -3
  197. package/dist/webPanels-B2_C2-3g.mjs +49 -0
  198. package/dist/webPanels-FCgFKvVe.cjs +53 -0
  199. package/package.json +7 -7
  200. package/dist/selfCheck-BAvqBwBR.mjs +0 -84
  201. package/dist/sessionModeFile-CWVq8AT9.mjs +0 -32
  202. package/dist/sessionModeFile-CsLtn_PL.cjs +0 -36
  203. package/dist/spawnAgents-BjtmjBvP.mjs +0 -45
@@ -1,28 +1,126 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-De8L5lFK.cjs');
3
+ var logger = require('./logger-C1GWx3gi.cjs');
4
+ var prompt = require('./prompt-lgF9IE82.cjs');
5
+ var client = require('./client-ZjOsn6nZ.cjs');
6
+ var reportProgressTool = require('./reportProgressTool-CM1JFAHg.cjs');
7
+ var index = require('./index-C6O2vVjm.cjs');
4
8
  var promises = require('node:fs/promises');
5
9
  var node_crypto = require('node:crypto');
6
10
  var node_fs = require('node:fs');
7
11
  var path = require('node:path');
8
12
  var z = require('zod');
9
- var client = require('./client-DyvBTYee.cjs');
10
- var context = require('./context-BKbrZ55r.cjs');
11
- var sessionOps = require('./sessionOps-Dmt7WUUs.cjs');
12
- var resources = require('./resources-JDPugcDq.cjs');
13
- var tools = require('./tools-DDKTB5aQ.cjs');
14
- var reportProgressTool = require('./reportProgressTool-Du_zAK4h.cjs');
15
- var peerTools = require('./peerTools-DtGKWli5.cjs');
13
+ var client$1 = require('./client-BKcK1Bi-.cjs');
14
+ var context = require('./context-DXzU5bFM.cjs');
15
+ var sessionOps = require('./sessionOps-S-qPBLFf.cjs');
16
+ var resources = require('./resources-CvIlkzpP.cjs');
17
+ var tools = require('./tools-_w52DZG6.cjs');
18
+ var peerTools = require('./peerTools-DRQETXaz.cjs');
16
19
  var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
17
20
  var node_http = require('node:http');
18
21
  var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
19
- var logger = require('./logger-LvwVeATU.cjs');
20
22
  var limits = require('./limits-C-a7gr04.cjs');
21
- var downloadTarget = require('./downloadTarget-Dj3WoVJa.cjs');
23
+ var downloadTarget = require('./downloadTarget-BQJK_6oH.cjs');
22
24
  var agentGuidance = require('./agentGuidance-BtBx2xxE.cjs');
25
+ var webPanels = require('./webPanels-FCgFKvVe.cjs');
26
+ var persistence = require('./persistence-BegE7NPa.cjs');
27
+ var pushNotifications = require('./pushNotifications-DaLQUyi0.cjs');
23
28
  var previewPath = require('./previewPath-B0rBdhy1.cjs');
24
29
  var crypto = require('crypto');
25
30
 
31
+ function createProjectInjector(deps) {
32
+ const timeoutMs = deps.timeoutMs ?? 1500;
33
+ const now = deps.now ?? Date.now;
34
+ const presenceEvery = deps.presenceMinIntervalMs ?? 3e4;
35
+ const state = { projectId: null, prefixSent: false, cursor: 0, injectedVersion: null };
36
+ const titles = /* @__PURE__ */ new Map();
37
+ let lastPresenceAt = 0;
38
+ let lastPresenceVersion = null;
39
+ const label = (id) => titles.get(id) ?? id.slice(0, 8);
40
+ function withTimeout(p) {
41
+ return Promise.race([p, new Promise((_, reject) => setTimeout(() => reject(new Error("project injection timeout")), timeoutMs).unref?.())]);
42
+ }
43
+ function reportPresence(force) {
44
+ const sid = deps.sessionId();
45
+ const meta = deps.metadata?.() ?? null;
46
+ const machineId = typeof meta?.machineId === "string" ? meta.machineId : null;
47
+ if (!sid || !machineId || !state.projectId) return;
48
+ const t = now();
49
+ if (!force && state.injectedVersion === lastPresenceVersion && t - lastPresenceAt < presenceEvery) return;
50
+ lastPresenceAt = t;
51
+ lastPresenceVersion = state.injectedVersion;
52
+ const entry = {
53
+ sessionId: sid,
54
+ agent: deps.agent,
55
+ state: "running",
56
+ injectedInstructionsVersion: state.injectedVersion,
57
+ ...typeof meta?.summary?.text === "string" ? { title: String(meta.summary.text).slice(0, 200) } : {},
58
+ ...typeof meta?.board?.progress === "string" ? { doing: String(meta.board.progress).slice(0, 200) } : {},
59
+ ...typeof meta?.worktree?.branch === "string" ? { branch: String(meta.worktree.branch).slice(0, 200) } : {},
60
+ ...typeof meta?.host === "string" ? { machine: String(meta.host).slice(0, 200) } : {},
61
+ ...Array.isArray(meta?.capabilities) ? { capabilities: meta.capabilities.filter((c) => typeof c === "string").slice(0, 32) } : {}
62
+ };
63
+ void deps.client.presence(machineId, [entry]).catch((error) => logger.logger.debug(`[projects] presence write skipped: ${String(error)}`));
64
+ }
65
+ function markDelivered(sid, ids) {
66
+ if (ids.length) void deps.client.delivered(sid, ids).catch((error) => logger.logger.debug(`[projects] delivered ack skipped: ${String(error)}`));
67
+ }
68
+ async function firstTurn(sid, message) {
69
+ const ctx = await withTimeout(deps.client.context(sid, {}, timeoutMs));
70
+ for (const s of ctx.sessions) if (s.title) titles.set(s.sessionId, s.title);
71
+ const built = prompt.buildFirstTurnPrefix(ctx, { label });
72
+ if (!built || !ctx.project) {
73
+ state.projectId = null;
74
+ return message;
75
+ }
76
+ state.projectId = ctx.project.id;
77
+ state.prefixSent = true;
78
+ state.cursor = ctx.asOf;
79
+ state.injectedVersion = built.instructionsVersion;
80
+ markDelivered(sid, ctx.unread.map((u) => u.id));
81
+ reportPresence(true);
82
+ return prompt.prependToMessage(message, built.prefix);
83
+ }
84
+ async function laterTurn(sid, message) {
85
+ const d = await withTimeout(deps.client.digest(sid, state.cursor, timeoutMs));
86
+ if (!d.projectId) {
87
+ state.projectId = null;
88
+ state.prefixSent = false;
89
+ state.injectedVersion = null;
90
+ return message;
91
+ }
92
+ if (d.projectId !== state.projectId) {
93
+ state.prefixSent = false;
94
+ return firstTurn(sid, message);
95
+ }
96
+ state.cursor = d.asOf;
97
+ if (d.instructions) state.injectedVersion = d.instructions.version;
98
+ markDelivered(sid, d.messages.map((m) => m.id));
99
+ reportPresence(!!d.instructions);
100
+ return prompt.prependToMessage(message, prompt.buildDigestLine(d, { label }));
101
+ }
102
+ return {
103
+ state: () => ({ ...state }),
104
+ /** A fresh agent with no memory of earlier turns (ACP backend restart, new Claude conversation) needs P again. */
105
+ reset() {
106
+ state.prefixSent = false;
107
+ state.cursor = 0;
108
+ },
109
+ async decorate(message) {
110
+ if (typeof message !== "string") return message;
111
+ const sid = deps.sessionId();
112
+ if (!sid || deps.client.isDisabled()) return message;
113
+ try {
114
+ const out = state.prefixSent ? await laterTurn(sid, message) : await firstTurn(sid, message);
115
+ return out;
116
+ } catch (error) {
117
+ if (!(error instanceof client.ProjectsUnsupportedError)) logger.logger.debug(`[projects] injection skipped: ${String(error)}`);
118
+ return message;
119
+ }
120
+ }
121
+ };
122
+ }
123
+
26
124
  const TEAMS_ADOPT_CAPABILITY = "teams-adopt-v1";
27
125
  const TeamAdoptSchema = z.z.object({
28
126
  requestId: z.z.string().min(1).max(128),
@@ -78,7 +176,7 @@ async function retainTeam(path, input, resolvedTeamId, guidanceDelivered = false
78
176
  }
79
177
  }
80
178
  function createTeamAdoption(sessionId) {
81
- const client$1 = client.createTeamsClient(sessionId);
179
+ const client = client$1.createTeamsClient(sessionId);
82
180
  let current;
83
181
  return (raw) => {
84
182
  const parsed = TeamAdoptSchema.safeParse(raw);
@@ -107,7 +205,7 @@ function createTeamAdoption(sessionId) {
107
205
  let intent;
108
206
  try {
109
207
  intent = await retainIntent(path, input);
110
- const connected = await client$1.initialize(input, { resumeAdoption: true, expectedTeamId: intent.resolvedTeamId, onTeamResolved: (teamId) => retainTeam(path, input, teamId, intent?.guidanceDelivered) });
208
+ const connected = await client.initialize(input, { resumeAdoption: true, expectedTeamId: intent.resolvedTeamId, onTeamResolved: (teamId) => retainTeam(path, input, teamId, intent?.guidanceDelivered) });
111
209
  const instructions = `${resources.TEAM_SKILL}
112
210
 
113
211
  This session is now connected as the team lead. Call team_inspect to read the authoritative state before continuing the user's goal. Do not call team_create or team_join again.`;
@@ -118,7 +216,7 @@ This session is now connected as the team lead. Call team_inspect to read the au
118
216
  }
119
217
  operation.result = { status: "connected", teamId: connected.team.id, botId: connected.botId, instructions };
120
218
  } catch (error) {
121
- if (error instanceof client.TeamConnectionPreflightError && intent?.fresh && !intent.resolvedTeamId) {
219
+ if (error instanceof client$1.TeamConnectionPreflightError && intent?.fresh && !intent.resolvedTeamId) {
122
220
  await promises.unlink(path).catch(() => {
123
221
  });
124
222
  operation.safeToChange = true;
@@ -130,6 +228,41 @@ This session is now connected as the team lead. Call team_inspect to read the au
130
228
  };
131
229
  }
132
230
 
231
+ const REQUEST_HUMAN_TOOL_NAME = "request_human";
232
+ const REQUEST_HUMAN_REASON_MAX = 200;
233
+ const REQUEST_HUMAN_DEDUPE_MS = 6e4;
234
+ const REQUEST_HUMAN_TOOL_DESCRIPTION = "Ask the user to step in when you cannot continue on your own: a captcha, 2FA code, QR scan, OAuth consent page, or a decision only they can make. Sends a push notification and marks the task board as blocked. Give a short reason (no secrets; the server can read it). If this machine declares a web panel showing what you are working in (e.g. a remote desktop or browser dashboard), pass its id as `panel` so the notification opens it. Afterwards, poll for your own completion condition (the page changed, the element disappeared) instead of waiting for a reply.";
235
+ function createRequestHuman(deps) {
236
+ const now = deps.now ?? Date.now;
237
+ let last = null;
238
+ return async (rawReason, panel) => {
239
+ const reason = rawReason.replace(/\s+/g, " ").trim().slice(0, REQUEST_HUMAN_REASON_MAX);
240
+ if (!reason) return { notified: false, error: "reason must be a non-empty string" };
241
+ await deps.reportBlocked(reason);
242
+ const at = now();
243
+ if (last && last.reason === reason && at - last.at < REQUEST_HUMAN_DEDUPE_MS) {
244
+ return { notified: true, deduped: true };
245
+ }
246
+ let url = `/session/${encodeURIComponent(deps.sessionId)}`;
247
+ let unknownPanel;
248
+ if (panel) {
249
+ const machineId = deps.machineId();
250
+ const { panels } = webPanels.normalizeWebPanels(await deps.readPanels().catch(() => void 0));
251
+ if (machineId && panels.some((p) => p.id === panel)) url = webPanels.webPanelPath(machineId, panel);
252
+ else unknownPanel = panel;
253
+ }
254
+ const sent = await deps.sendAttention({ sessionId: deps.sessionId, reason, url });
255
+ if (sent.sent) last = { reason, at };
256
+ return { notified: sent.sent, url, ...unknownPanel ? { unknownPanel } : {}, ...sent.error ? { error: sent.error } : {} };
257
+ };
258
+ }
259
+ function requestHumanResultText(r) {
260
+ if (r.deduped) return "Already asked for this in the last minute; the board still shows it. Keep polling your completion condition.";
261
+ if (!r.notified) return `Could not notify the user: ${r.error ?? "unknown error"}. The task board is marked blocked.`;
262
+ const panelNote = r.unknownPanel ? ` (panel "${r.unknownPanel}" is not declared on this machine, so the notification opens the session instead)` : "";
263
+ return `Notified the user${panelNote}. This does not mean they have seen it. Poll your own completion condition (URL changed, element gone) instead of waiting for a reply.`;
264
+ }
265
+
133
266
  function createMcpServer(handlers, options, sessionId, client) {
134
267
  const mcp = new mcp_js.McpServer({
135
268
  name: "Very Happy Tools",
@@ -138,6 +271,7 @@ function createMcpServer(handlers, options, sessionId, client) {
138
271
  tools.registerTeamsTools(mcp, sessionId);
139
272
  reportProgressTool.registerAutomationTools(mcp);
140
273
  if (client) peerTools.registerSessionPeerTools(mcp, peerTools.createSessionPeerToolExecutor(client));
274
+ if (client) reportProgressTool.registerProjectTools(mcp, reportProgressTool.createProjectToolExecutor(() => client.sessionId));
141
275
  mcp.registerTool("change_title", {
142
276
  description: "Change the title of the current chat session",
143
277
  title: "Change Chat Title",
@@ -224,6 +358,18 @@ function createMcpServer(handlers, options, sessionId, client) {
224
358
  isError: true
225
359
  };
226
360
  });
361
+ mcp.registerTool(REQUEST_HUMAN_TOOL_NAME, {
362
+ description: REQUEST_HUMAN_TOOL_DESCRIPTION,
363
+ title: "Ask the user to step in",
364
+ inputSchema: {
365
+ reason: z.z.string().describe("What you need from the user, one short line (no secrets)"),
366
+ panel: z.z.string().optional().describe("Id of this machine's web panel that shows what you are working in (from ~/.happy/settings.json webPanels)")
367
+ }
368
+ }, async (args) => {
369
+ const response = await handlers.requestHuman(String(args.reason ?? ""), args.panel);
370
+ logger.logger.debug("[happyMCP] request_human response:", response);
371
+ return { content: [{ type: "text", text: requestHumanResultText(response) }], isError: response.error === "reason must be a non-empty string" };
372
+ });
227
373
  reportProgressTool.registerReportProgressTool(mcp, async (args) => {
228
374
  const response = await handlers.reportProgress(String(args.progress ?? ""), reportProgressTool.normalizeAttention(args.attention), reportProgressTool.normalizePhase(args.phase));
229
375
  logger.logger.debug("[happyMCP] report_progress response:", response);
@@ -321,10 +467,66 @@ async function startHappyServer(client, options) {
321
467
  } catch (error) {
322
468
  return { delivered: false, truncated: false, totalBytes: 0, error: String(error) };
323
469
  }
324
- }
470
+ },
471
+ requestHuman: createRequestHuman({
472
+ sessionId: client.sessionId,
473
+ machineId: () => client.getMetadata()?.machineId,
474
+ readPanels: async () => (await persistence.readSettings()).webPanels,
475
+ // Same board write (and attention-change throttle bypass) as report_progress.
476
+ reportBlocked: (reason) => handlers.reportProgress(reason, "blocked"),
477
+ sendAttention: (input) => options?.push ? options.push.sendAttention({ ...input, sessionTitle: pushNotifications.getSessionNotificationBody(client.getMetadata()) }) : Promise.resolve({ sent: false, error: "push is not available in this runner" })
478
+ })
325
479
  };
326
480
  const contextPath = "/runtime-context/" + node_crypto.randomUUID();
481
+ const steerPath = "/runtime-steer/" + node_crypto.randomUUID();
327
482
  const server = node_http.createServer(async (req, res) => {
483
+ if (req.url?.startsWith("/runtime-steer/")) {
484
+ const steer = options?.piSteer;
485
+ if (!steer || req.headers.origin) {
486
+ res.writeHead(403).end();
487
+ return;
488
+ }
489
+ if (req.method === "GET" && req.url === steerPath) {
490
+ const gone = new AbortController();
491
+ res.on("close", () => gone.abort());
492
+ const item = await steer.take(25e3, gone.signal);
493
+ if (res.writableEnded || gone.signal.aborted) {
494
+ return;
495
+ }
496
+ if (!item) {
497
+ res.writeHead(204).end();
498
+ return;
499
+ }
500
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(item));
501
+ return;
502
+ }
503
+ if (req.method === "POST" && (req.url === steerPath + "/hello" || req.url === steerPath + "/ack")) {
504
+ let body = "";
505
+ for await (const chunk of req) {
506
+ body += chunk.toString();
507
+ if (Buffer.byteLength(body) > 1024) {
508
+ res.writeHead(413).end();
509
+ return;
510
+ }
511
+ }
512
+ if (req.url.endsWith("/hello")) {
513
+ steer.hello();
514
+ res.writeHead(204).end();
515
+ return;
516
+ }
517
+ try {
518
+ const ack = JSON.parse(body);
519
+ if (typeof ack.id !== "number") throw new Error("id");
520
+ steer.ack(ack.id, ack.delivered === true);
521
+ res.writeHead(204).end();
522
+ } catch {
523
+ res.writeHead(400).end();
524
+ }
525
+ return;
526
+ }
527
+ res.writeHead(403).end();
528
+ return;
529
+ }
328
530
  if (req.url?.startsWith("/runtime-context/")) {
329
531
  if (!options?.onContextUsage || req.url !== contextPath || req.method !== "POST" || req.headers.origin) {
330
532
  res.writeHead(403).end();
@@ -381,14 +583,17 @@ async function startHappyServer(client, options) {
381
583
  return {
382
584
  url: baseUrl.toString(),
383
585
  contextUsageUrl: options?.onContextUsage ? new URL(contextPath, baseUrl).toString() : void 0,
586
+ steerUrl: options?.piSteer ? new URL(steerPath, baseUrl).toString() : void 0,
384
587
  toolNames: [
385
588
  "change_title",
386
589
  limits.CLIPBOARD_TOOL_NAME,
387
590
  agentGuidance.PREVIEW_TOOL_NAME,
388
591
  agentGuidance.REPORT_PROGRESS_TOOL_NAME,
592
+ REQUEST_HUMAN_TOOL_NAME,
389
593
  ...tools.TEAM_TOOL_NAMES,
390
594
  ...reportProgressTool.AUTOMATION_TOOL_NAMES,
391
595
  ...peerTools.SESSION_PEER_TOOL_NAMES,
596
+ ...reportProgressTool.PROJECT_TOOL_NAMES,
392
597
  ...options?.assistant && !process.env.VH_TEAM_SCOPE_FILE ? downloadTarget.ASSISTANT_TOOL_NAMES : []
393
598
  ],
394
599
  stop: () => {
@@ -485,5 +690,6 @@ function hashObject(obj, options, encoding = "hex") {
485
690
  return crypto.createHash("sha256").update(jsonString).digest(encoding);
486
691
  }
487
692
 
693
+ exports.createProjectInjector = createProjectInjector;
488
694
  exports.hashObject = hashObject;
489
695
  exports.startHappyServer = startHappyServer;
@@ -1,26 +1,124 @@
1
- import { C as ContextUsageSchema } from './index-DMy3OCBe.mjs';
1
+ import { l as logger } from './logger-BtVhHnD8.mjs';
2
+ import { p as prependToMessage, b as buildDigestLine, a as buildFirstTurnPrefix } from './prompt-BOouDaTM.mjs';
3
+ import { P as ProjectsUnsupportedError } from './client-BhoEBnuN.mjs';
4
+ import { c as createSelfReportState, A as AUTOMATION_TOOL_NAMES, P as PROJECT_TOOL_NAMES, r as registerAutomationTools, a as registerProjectTools, d as createProjectToolExecutor, b as registerReportProgressTool, n as normalizeAttention, e as normalizePhase, f as normalizeProgress, s as shouldAcceptSelfReport } from './reportProgressTool-gHItlSzz.mjs';
5
+ import { C as ContextUsageSchema } from './index-DPOa4NhL.mjs';
2
6
  import { unlink, mkdir, open, rename } from 'node:fs/promises';
3
7
  import { randomUUID } from 'node:crypto';
4
8
  import { constants } from 'node:fs';
5
9
  import { dirname } from 'node:path';
6
10
  import { z } from 'zod';
7
- import { T as TeamConnectionPreflightError, c as createTeamsClient } from './client-BPMWutfs.mjs';
8
- import { t as teamScopePath } from './context-CCRpVXFo.mjs';
9
- import { g as waitForSessionKey, e as sendUserMessage } from './sessionOps-CdgbEJgf.mjs';
10
- import { T as TEAM_SKILL } from './resources-BdFBQi8r.mjs';
11
- import { T as TEAM_TOOL_NAMES, r as registerTeamsTools } from './tools-BrM3bZW5.mjs';
12
- import { c as createSelfReportState, A as AUTOMATION_TOOL_NAMES, r as registerAutomationTools, a as registerReportProgressTool, n as normalizeAttention, b as normalizePhase, d as normalizeProgress, s as shouldAcceptSelfReport } from './reportProgressTool-B203EZCR.mjs';
13
- import { S as SESSION_PEER_TOOL_NAMES, r as registerSessionPeerTools, a as createSessionPeerToolExecutor } from './peerTools-CxmrKnfV.mjs';
11
+ import { T as TeamConnectionPreflightError, c as createTeamsClient } from './client-DuTDwWSQ.mjs';
12
+ import { t as teamScopePath } from './context-DploEcoN.mjs';
13
+ import { g as waitForSessionKey, e as sendUserMessage } from './sessionOps-BIGgRube.mjs';
14
+ import { T as TEAM_SKILL } from './resources-y9jfyO-_.mjs';
15
+ import { T as TEAM_TOOL_NAMES, r as registerTeamsTools } from './tools-D8-PnZEG.mjs';
16
+ import { S as SESSION_PEER_TOOL_NAMES, r as registerSessionPeerTools, a as createSessionPeerToolExecutor } from './peerTools-CWKzcQ7u.mjs';
14
17
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
15
18
  import { createServer } from 'node:http';
16
19
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
17
- import { l as logger } from './logger-13cBr_ew.mjs';
18
20
  import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, c as CLIPBOARD_MAX_BYTES } from './limits-DIo7rEBR.mjs';
19
- import { A as ASSISTANT_TOOL_NAMES, a as registerAssistantTools, c as checkDownloadTarget } from './downloadTarget-ByXzbDK4.mjs';
21
+ import { A as ASSISTANT_TOOL_NAMES, a as registerAssistantTools, c as checkDownloadTarget } from './downloadTarget-BoNwTIH_.mjs';
20
22
  import { P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, c as PREVIEW_DOWNLOAD_PARAM_DESCRIPTION } from './agentGuidance-BDYt2Qy2.mjs';
23
+ import { n as normalizeWebPanels, w as webPanelPath } from './webPanels-B2_C2-3g.mjs';
24
+ import { r as readSettings } from './persistence-Cr-wutZ4.mjs';
25
+ import { g as getSessionNotificationBody } from './pushNotifications-BdFjDjQe.mjs';
21
26
  import { c as checkPreviewPath } from './previewPath-CXN4Vag0.mjs';
22
27
  import { createHash } from 'crypto';
23
28
 
29
+ function createProjectInjector(deps) {
30
+ const timeoutMs = deps.timeoutMs ?? 1500;
31
+ const now = deps.now ?? Date.now;
32
+ const presenceEvery = deps.presenceMinIntervalMs ?? 3e4;
33
+ const state = { projectId: null, prefixSent: false, cursor: 0, injectedVersion: null };
34
+ const titles = /* @__PURE__ */ new Map();
35
+ let lastPresenceAt = 0;
36
+ let lastPresenceVersion = null;
37
+ const label = (id) => titles.get(id) ?? id.slice(0, 8);
38
+ function withTimeout(p) {
39
+ return Promise.race([p, new Promise((_, reject) => setTimeout(() => reject(new Error("project injection timeout")), timeoutMs).unref?.())]);
40
+ }
41
+ function reportPresence(force) {
42
+ const sid = deps.sessionId();
43
+ const meta = deps.metadata?.() ?? null;
44
+ const machineId = typeof meta?.machineId === "string" ? meta.machineId : null;
45
+ if (!sid || !machineId || !state.projectId) return;
46
+ const t = now();
47
+ if (!force && state.injectedVersion === lastPresenceVersion && t - lastPresenceAt < presenceEvery) return;
48
+ lastPresenceAt = t;
49
+ lastPresenceVersion = state.injectedVersion;
50
+ const entry = {
51
+ sessionId: sid,
52
+ agent: deps.agent,
53
+ state: "running",
54
+ injectedInstructionsVersion: state.injectedVersion,
55
+ ...typeof meta?.summary?.text === "string" ? { title: String(meta.summary.text).slice(0, 200) } : {},
56
+ ...typeof meta?.board?.progress === "string" ? { doing: String(meta.board.progress).slice(0, 200) } : {},
57
+ ...typeof meta?.worktree?.branch === "string" ? { branch: String(meta.worktree.branch).slice(0, 200) } : {},
58
+ ...typeof meta?.host === "string" ? { machine: String(meta.host).slice(0, 200) } : {},
59
+ ...Array.isArray(meta?.capabilities) ? { capabilities: meta.capabilities.filter((c) => typeof c === "string").slice(0, 32) } : {}
60
+ };
61
+ void deps.client.presence(machineId, [entry]).catch((error) => logger.debug(`[projects] presence write skipped: ${String(error)}`));
62
+ }
63
+ function markDelivered(sid, ids) {
64
+ if (ids.length) void deps.client.delivered(sid, ids).catch((error) => logger.debug(`[projects] delivered ack skipped: ${String(error)}`));
65
+ }
66
+ async function firstTurn(sid, message) {
67
+ const ctx = await withTimeout(deps.client.context(sid, {}, timeoutMs));
68
+ for (const s of ctx.sessions) if (s.title) titles.set(s.sessionId, s.title);
69
+ const built = buildFirstTurnPrefix(ctx, { label });
70
+ if (!built || !ctx.project) {
71
+ state.projectId = null;
72
+ return message;
73
+ }
74
+ state.projectId = ctx.project.id;
75
+ state.prefixSent = true;
76
+ state.cursor = ctx.asOf;
77
+ state.injectedVersion = built.instructionsVersion;
78
+ markDelivered(sid, ctx.unread.map((u) => u.id));
79
+ reportPresence(true);
80
+ return prependToMessage(message, built.prefix);
81
+ }
82
+ async function laterTurn(sid, message) {
83
+ const d = await withTimeout(deps.client.digest(sid, state.cursor, timeoutMs));
84
+ if (!d.projectId) {
85
+ state.projectId = null;
86
+ state.prefixSent = false;
87
+ state.injectedVersion = null;
88
+ return message;
89
+ }
90
+ if (d.projectId !== state.projectId) {
91
+ state.prefixSent = false;
92
+ return firstTurn(sid, message);
93
+ }
94
+ state.cursor = d.asOf;
95
+ if (d.instructions) state.injectedVersion = d.instructions.version;
96
+ markDelivered(sid, d.messages.map((m) => m.id));
97
+ reportPresence(!!d.instructions);
98
+ return prependToMessage(message, buildDigestLine(d, { label }));
99
+ }
100
+ return {
101
+ state: () => ({ ...state }),
102
+ /** A fresh agent with no memory of earlier turns (ACP backend restart, new Claude conversation) needs P again. */
103
+ reset() {
104
+ state.prefixSent = false;
105
+ state.cursor = 0;
106
+ },
107
+ async decorate(message) {
108
+ if (typeof message !== "string") return message;
109
+ const sid = deps.sessionId();
110
+ if (!sid || deps.client.isDisabled()) return message;
111
+ try {
112
+ const out = state.prefixSent ? await laterTurn(sid, message) : await firstTurn(sid, message);
113
+ return out;
114
+ } catch (error) {
115
+ if (!(error instanceof ProjectsUnsupportedError)) logger.debug(`[projects] injection skipped: ${String(error)}`);
116
+ return message;
117
+ }
118
+ }
119
+ };
120
+ }
121
+
24
122
  const TEAMS_ADOPT_CAPABILITY = "teams-adopt-v1";
25
123
  const TeamAdoptSchema = z.object({
26
124
  requestId: z.string().min(1).max(128),
@@ -128,6 +226,41 @@ This session is now connected as the team lead. Call team_inspect to read the au
128
226
  };
129
227
  }
130
228
 
229
+ const REQUEST_HUMAN_TOOL_NAME = "request_human";
230
+ const REQUEST_HUMAN_REASON_MAX = 200;
231
+ const REQUEST_HUMAN_DEDUPE_MS = 6e4;
232
+ const REQUEST_HUMAN_TOOL_DESCRIPTION = "Ask the user to step in when you cannot continue on your own: a captcha, 2FA code, QR scan, OAuth consent page, or a decision only they can make. Sends a push notification and marks the task board as blocked. Give a short reason (no secrets; the server can read it). If this machine declares a web panel showing what you are working in (e.g. a remote desktop or browser dashboard), pass its id as `panel` so the notification opens it. Afterwards, poll for your own completion condition (the page changed, the element disappeared) instead of waiting for a reply.";
233
+ function createRequestHuman(deps) {
234
+ const now = deps.now ?? Date.now;
235
+ let last = null;
236
+ return async (rawReason, panel) => {
237
+ const reason = rawReason.replace(/\s+/g, " ").trim().slice(0, REQUEST_HUMAN_REASON_MAX);
238
+ if (!reason) return { notified: false, error: "reason must be a non-empty string" };
239
+ await deps.reportBlocked(reason);
240
+ const at = now();
241
+ if (last && last.reason === reason && at - last.at < REQUEST_HUMAN_DEDUPE_MS) {
242
+ return { notified: true, deduped: true };
243
+ }
244
+ let url = `/session/${encodeURIComponent(deps.sessionId)}`;
245
+ let unknownPanel;
246
+ if (panel) {
247
+ const machineId = deps.machineId();
248
+ const { panels } = normalizeWebPanels(await deps.readPanels().catch(() => void 0));
249
+ if (machineId && panels.some((p) => p.id === panel)) url = webPanelPath(machineId, panel);
250
+ else unknownPanel = panel;
251
+ }
252
+ const sent = await deps.sendAttention({ sessionId: deps.sessionId, reason, url });
253
+ if (sent.sent) last = { reason, at };
254
+ return { notified: sent.sent, url, ...unknownPanel ? { unknownPanel } : {}, ...sent.error ? { error: sent.error } : {} };
255
+ };
256
+ }
257
+ function requestHumanResultText(r) {
258
+ if (r.deduped) return "Already asked for this in the last minute; the board still shows it. Keep polling your completion condition.";
259
+ if (!r.notified) return `Could not notify the user: ${r.error ?? "unknown error"}. The task board is marked blocked.`;
260
+ const panelNote = r.unknownPanel ? ` (panel "${r.unknownPanel}" is not declared on this machine, so the notification opens the session instead)` : "";
261
+ return `Notified the user${panelNote}. This does not mean they have seen it. Poll your own completion condition (URL changed, element gone) instead of waiting for a reply.`;
262
+ }
263
+
131
264
  function createMcpServer(handlers, options, sessionId, client) {
132
265
  const mcp = new McpServer({
133
266
  name: "Very Happy Tools",
@@ -136,6 +269,7 @@ function createMcpServer(handlers, options, sessionId, client) {
136
269
  registerTeamsTools(mcp, sessionId);
137
270
  registerAutomationTools(mcp);
138
271
  if (client) registerSessionPeerTools(mcp, createSessionPeerToolExecutor(client));
272
+ if (client) registerProjectTools(mcp, createProjectToolExecutor(() => client.sessionId));
139
273
  mcp.registerTool("change_title", {
140
274
  description: "Change the title of the current chat session",
141
275
  title: "Change Chat Title",
@@ -222,6 +356,18 @@ function createMcpServer(handlers, options, sessionId, client) {
222
356
  isError: true
223
357
  };
224
358
  });
359
+ mcp.registerTool(REQUEST_HUMAN_TOOL_NAME, {
360
+ description: REQUEST_HUMAN_TOOL_DESCRIPTION,
361
+ title: "Ask the user to step in",
362
+ inputSchema: {
363
+ reason: z.string().describe("What you need from the user, one short line (no secrets)"),
364
+ panel: z.string().optional().describe("Id of this machine's web panel that shows what you are working in (from ~/.happy/settings.json webPanels)")
365
+ }
366
+ }, async (args) => {
367
+ const response = await handlers.requestHuman(String(args.reason ?? ""), args.panel);
368
+ logger.debug("[happyMCP] request_human response:", response);
369
+ return { content: [{ type: "text", text: requestHumanResultText(response) }], isError: response.error === "reason must be a non-empty string" };
370
+ });
225
371
  registerReportProgressTool(mcp, async (args) => {
226
372
  const response = await handlers.reportProgress(String(args.progress ?? ""), normalizeAttention(args.attention), normalizePhase(args.phase));
227
373
  logger.debug("[happyMCP] report_progress response:", response);
@@ -319,10 +465,66 @@ async function startHappyServer(client, options) {
319
465
  } catch (error) {
320
466
  return { delivered: false, truncated: false, totalBytes: 0, error: String(error) };
321
467
  }
322
- }
468
+ },
469
+ requestHuman: createRequestHuman({
470
+ sessionId: client.sessionId,
471
+ machineId: () => client.getMetadata()?.machineId,
472
+ readPanels: async () => (await readSettings()).webPanels,
473
+ // Same board write (and attention-change throttle bypass) as report_progress.
474
+ reportBlocked: (reason) => handlers.reportProgress(reason, "blocked"),
475
+ sendAttention: (input) => options?.push ? options.push.sendAttention({ ...input, sessionTitle: getSessionNotificationBody(client.getMetadata()) }) : Promise.resolve({ sent: false, error: "push is not available in this runner" })
476
+ })
323
477
  };
324
478
  const contextPath = "/runtime-context/" + randomUUID();
479
+ const steerPath = "/runtime-steer/" + randomUUID();
325
480
  const server = createServer(async (req, res) => {
481
+ if (req.url?.startsWith("/runtime-steer/")) {
482
+ const steer = options?.piSteer;
483
+ if (!steer || req.headers.origin) {
484
+ res.writeHead(403).end();
485
+ return;
486
+ }
487
+ if (req.method === "GET" && req.url === steerPath) {
488
+ const gone = new AbortController();
489
+ res.on("close", () => gone.abort());
490
+ const item = await steer.take(25e3, gone.signal);
491
+ if (res.writableEnded || gone.signal.aborted) {
492
+ return;
493
+ }
494
+ if (!item) {
495
+ res.writeHead(204).end();
496
+ return;
497
+ }
498
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(item));
499
+ return;
500
+ }
501
+ if (req.method === "POST" && (req.url === steerPath + "/hello" || req.url === steerPath + "/ack")) {
502
+ let body = "";
503
+ for await (const chunk of req) {
504
+ body += chunk.toString();
505
+ if (Buffer.byteLength(body) > 1024) {
506
+ res.writeHead(413).end();
507
+ return;
508
+ }
509
+ }
510
+ if (req.url.endsWith("/hello")) {
511
+ steer.hello();
512
+ res.writeHead(204).end();
513
+ return;
514
+ }
515
+ try {
516
+ const ack = JSON.parse(body);
517
+ if (typeof ack.id !== "number") throw new Error("id");
518
+ steer.ack(ack.id, ack.delivered === true);
519
+ res.writeHead(204).end();
520
+ } catch {
521
+ res.writeHead(400).end();
522
+ }
523
+ return;
524
+ }
525
+ res.writeHead(403).end();
526
+ return;
527
+ }
326
528
  if (req.url?.startsWith("/runtime-context/")) {
327
529
  if (!options?.onContextUsage || req.url !== contextPath || req.method !== "POST" || req.headers.origin) {
328
530
  res.writeHead(403).end();
@@ -379,14 +581,17 @@ async function startHappyServer(client, options) {
379
581
  return {
380
582
  url: baseUrl.toString(),
381
583
  contextUsageUrl: options?.onContextUsage ? new URL(contextPath, baseUrl).toString() : void 0,
584
+ steerUrl: options?.piSteer ? new URL(steerPath, baseUrl).toString() : void 0,
382
585
  toolNames: [
383
586
  "change_title",
384
587
  CLIPBOARD_TOOL_NAME,
385
588
  PREVIEW_TOOL_NAME,
386
589
  REPORT_PROGRESS_TOOL_NAME,
590
+ REQUEST_HUMAN_TOOL_NAME,
387
591
  ...TEAM_TOOL_NAMES,
388
592
  ...AUTOMATION_TOOL_NAMES,
389
593
  ...SESSION_PEER_TOOL_NAMES,
594
+ ...PROJECT_TOOL_NAMES,
390
595
  ...options?.assistant && !process.env.VH_TEAM_SCOPE_FILE ? ASSISTANT_TOOL_NAMES : []
391
596
  ],
392
597
  stop: () => {
@@ -483,4 +688,4 @@ function hashObject(obj, options, encoding = "hex") {
483
688
  return createHash("sha256").update(jsonString).digest(encoding);
484
689
  }
485
690
 
486
- export { hashObject as h, startHappyServer as s };
691
+ export { createProjectInjector as c, hashObject as h, startHappyServer as s };
@@ -1,19 +1,19 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
- var configuration = require('./configuration-DZPy0T9Z.cjs');
5
- var persistence = require('./persistence-DXbKOHxl.cjs');
6
- var controlClient = require('./controlClient-SX8gSza2.cjs');
4
+ var configuration = require('./configuration-CMHZnp6G.cjs');
5
+ var persistence = require('./persistence-BegE7NPa.cjs');
6
+ var controlClient = require('./controlClient-C-5qCt8W.cjs');
7
7
  var doctor$1 = require('./doctor-u46qYYKX.cjs');
8
8
  var node_fs = require('node:fs');
9
9
  var path = require('node:path');
10
10
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
11
- var _package = require('./package-Ch9WGDV_.cjs');
11
+ var _package = require('./package-SgqTYsjn.cjs');
12
12
  var doctorReadiness = require('./doctorReadiness-BCZGcN0a.cjs');
13
- var authRelay = require('./authRelay-BdYkC1Po.cjs');
13
+ var authRelay = require('./authRelay-Dy1qU9td.cjs');
14
14
  var cliUpdate = require('./cliUpdate-DsKXPFYp.cjs');
15
15
  var node_child_process = require('node:child_process');
16
- var piRunnerArgs = require('./piRunnerArgs-BVRZe2AR.cjs');
16
+ var piRunnerArgs = require('./piRunnerArgs-Cs-378Vz.cjs');
17
17
 
18
18
  function shareSafeProcessLine(process2) {
19
19
  return `PID ${process2.pid}: command arguments hidden for privacy`;
@@ -313,8 +313,8 @@ ${typeLabels[type] || type}:`));
313
313
  }
314
314
  console.log(chalk.gray(" An external claude command is only needed for native terminal/mirror use."));
315
315
  try {
316
- const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-BdTXEUPe.cjs'); });
317
- const { describeAgentHome } = await Promise.resolve().then(function () { return require('./index-CrR4JISa.cjs'); });
316
+ const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-BWcz8mz0.cjs'); });
317
+ const { describeAgentHome } = await Promise.resolve().then(function () { return require('./index-B0UjHlAe.cjs'); });
318
318
  const homes = await resolveAgentHomesForCli();
319
319
  console.log(chalk.green(`\u2713 Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}`));
320
320
  console.log(chalk.green(`\u2713 Codex home: ${describeAgentHome(homes.codexHome)}`));