very-happy-cli 0.2.159 → 0.2.160

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 (258) hide show
  1. package/dist/{AcpBackend-DUYFlyfN.cjs → AcpBackend-Bm2rdke8.cjs} +78 -76
  2. package/dist/{AcpBackend-VsfkZiVF.mjs → AcpBackend-mzv8Dhba.mjs} +4 -2
  3. package/dist/{AcpSessionManager-Bdv8LWXh.cjs → AcpSessionManager-BAY1Pggx.cjs} +9 -9
  4. package/dist/{AcpSessionManager-rYtVb0kN.mjs → AcpSessionManager-CoMWFVw3.mjs} +1 -1
  5. package/dist/BasePermissionHandler-CFHAxumV.cjs +157 -0
  6. package/dist/BasePermissionHandler-m3WVholL.mjs +155 -0
  7. package/dist/BaseReasoningProcessor-BFa9_rZA.cjs +200 -0
  8. package/dist/BaseReasoningProcessor-CYrKBXGm.mjs +198 -0
  9. package/dist/RemoteModeDisplay-D-HewyXg.mjs +150 -0
  10. package/dist/RemoteModeDisplay-NC1gPHtj.cjs +153 -0
  11. package/dist/agentAttachments-Dbw8iMCZ.cjs +20 -0
  12. package/dist/agentAttachments-M_B5qEF2.mjs +18 -0
  13. package/dist/agentGuidance-B5NATuFa.cjs +28 -0
  14. package/dist/agentGuidance-CQuNlNx8.mjs +20 -0
  15. package/dist/{agentHome-CHh4lmjM.cjs → agentHome-CboCeKWq.cjs} +14 -28
  16. package/dist/{agentHome-CvsJyeQB.mjs → agentHome-Dj1C43aJ.mjs} +8 -22
  17. package/dist/api-CLMUr-SX.mjs +3339 -0
  18. package/dist/api-CgundNNX.cjs +3346 -0
  19. package/dist/{types-Dh-s4Lqs.mjs → apiMachine-B9dLW_p5.mjs} +9116 -16205
  20. package/dist/{types-BbvK1Tr_.cjs → apiMachine-Bn8482VM.cjs} +9104 -16247
  21. package/dist/assistantTools-BTqPnlFl.cjs +380 -0
  22. package/dist/assistantTools-D6tuc6o4.mjs +376 -0
  23. package/dist/attachmentContent-DpiTJZb4.cjs +88 -0
  24. package/dist/attachmentContent-_kxRznNf.mjs +82 -0
  25. package/dist/attachmentLimits-CzVrqgLR.cjs +354 -0
  26. package/dist/attachmentLimits-yba1uHFO.mjs +348 -0
  27. package/dist/auth-BDOVh5h5.cjs +220 -0
  28. package/dist/auth-Cwutmdsa.mjs +225 -0
  29. package/dist/auth-PYHZQNPp.mjs +218 -0
  30. package/dist/auth-ba4WMYrN.cjs +227 -0
  31. package/dist/authRelay-BdYkC1Po.cjs +156 -0
  32. package/dist/authRelay-DSAvg1uv.mjs +143 -0
  33. package/dist/{auto-DKmEls8Z.cjs → auto-BOX_fgmR.cjs} +70 -79
  34. package/dist/{auto-nb8GhRRy.mjs → auto-BXu14yP_.mjs} +27 -36
  35. package/dist/backgroundTaskActivity-8PdPxrvy.mjs +149 -0
  36. package/dist/backgroundTaskActivity-CBvUdj39.cjs +157 -0
  37. package/dist/bootstrap-B5G1TijC.cjs +116 -0
  38. package/dist/bootstrap-hxXPCFMx.mjs +112 -0
  39. package/dist/browser-Y6Bl8L_O.cjs +22 -0
  40. package/dist/browser-jh2P0UVZ.mjs +20 -0
  41. package/dist/claudeCliPath-BqkaH2oR.mjs +8 -0
  42. package/dist/claudeCliPath-U4HHmV4r.cjs +10 -0
  43. package/dist/claudeLocal-BaY4fZKi.mjs +339 -0
  44. package/dist/claudeLocal-CRGG4Pea.cjs +343 -0
  45. package/dist/claudeLocalLauncher-D4T7MTqd.mjs +152 -0
  46. package/dist/claudeLocalLauncher-DyEbVzUN.cjs +154 -0
  47. package/dist/cliUpdate-DsKXPFYp.cjs +120 -0
  48. package/dist/cliUpdate-Y34VGe9Y.mjs +112 -0
  49. package/dist/client-3Ac-O4pw.cjs +104 -0
  50. package/dist/client-BPNbmtvO.cjs +119 -0
  51. package/dist/client-CeGKqqcJ.mjs +100 -0
  52. package/dist/client-ClR0K8zX.mjs +112 -0
  53. package/dist/codex/happyMcpStdioBridge.cjs +33 -14
  54. package/dist/codex/happyMcpStdioBridge.mjs +26 -7
  55. package/dist/codexAppServerClient-I3teiV-N.mjs +1088 -0
  56. package/dist/codexAppServerClient-q2FjnRcK.cjs +1090 -0
  57. package/dist/codexCommand-4zcccIoq.cjs +2088 -0
  58. package/dist/codexCommand-Cc9gcbdX.mjs +2086 -0
  59. package/dist/codexThreadFork-BH9IZPqD.cjs +350 -0
  60. package/dist/codexThreadFork-slzAseLx.mjs +338 -0
  61. package/dist/{config-ClLtM6F3.mjs → config-CTx_jb-C.mjs} +7 -40
  62. package/dist/{config-DLx-vW3I.cjs → config-Ci9Kpgwa.cjs} +26 -59
  63. package/dist/configuration-BSYpcPwE.cjs +181 -0
  64. package/dist/configuration-RfeNjFmd.mjs +170 -0
  65. package/dist/connect-BpOdzNbE.mjs +682 -0
  66. package/dist/connect-C2CiOUFs.cjs +684 -0
  67. package/dist/constants-BC4St0xd.mjs +11 -0
  68. package/dist/constants-VJ0N6WuG.cjs +17 -0
  69. package/dist/contentLogMetadata-BlWI0egu.mjs +51 -0
  70. package/dist/contentLogMetadata-ZSFk_NjQ.cjs +54 -0
  71. package/dist/context-DsDr6E2t.cjs +69 -0
  72. package/dist/context-H0Bcb9S_.mjs +65 -0
  73. package/dist/controlClient-BGbDcgWS.mjs +260 -0
  74. package/dist/controlClient-Bcwv3D21.cjs +279 -0
  75. package/dist/createSessionMetadata-BJxEnM-o.cjs +58 -0
  76. package/dist/createSessionMetadata-CVVkGjxV.mjs +54 -0
  77. package/dist/defaultPermissionMode-BnxJPVHT.mjs +4 -0
  78. package/dist/defaultPermissionMode-CxmTXUQR.cjs +7 -0
  79. package/dist/detectCLI-CSGMlV51.mjs +58 -0
  80. package/dist/detectCLI-DR9ZaN6B.cjs +61 -0
  81. package/dist/deterministicJson-C5CYTVpm.cjs +507 -0
  82. package/dist/deterministicJson-CzkFJWw1.mjs +502 -0
  83. package/dist/doctor-BPkYWSuq.mjs +75 -0
  84. package/dist/doctor-D2YJ-Yn2.cjs +395 -0
  85. package/dist/doctor-DOvByjbk.mjs +391 -0
  86. package/dist/doctor-u46qYYKX.cjs +79 -0
  87. package/dist/doctorReadiness-BCZGcN0a.cjs +151 -0
  88. package/dist/doctorReadiness-C208SMN9.mjs +142 -0
  89. package/dist/editPaths-BVfS_FFL.mjs +68 -0
  90. package/dist/editPaths-Bqp4OFPw.cjs +73 -0
  91. package/dist/ensureDaemonRunning-BCwNrMyv.cjs +64 -0
  92. package/dist/ensureDaemonRunning-Dqa2pOjY.mjs +61 -0
  93. package/dist/handleResumeCommand-BvXYf5El.mjs +240 -0
  94. package/dist/handleResumeCommand-C2cKchpX.cjs +245 -0
  95. package/dist/ids-B6ZC0HGt.cjs +13 -0
  96. package/dist/ids-DU6sLx4C.mjs +10 -0
  97. package/dist/index-B6UGRQ7h.mjs +102 -0
  98. package/dist/{index-BEOMECtE.cjs → index-B6pAdvJx.cjs} +121 -91
  99. package/dist/index-Bu5Zi_a9.cjs +182 -0
  100. package/dist/index-D3kI0hDR.cjs +107 -0
  101. package/dist/index-DA7yINuk.cjs +994 -0
  102. package/dist/index-DDeHXrIX.cjs +71 -0
  103. package/dist/index-DKLU_fD0.mjs +158 -0
  104. package/dist/{index-2mCaeKyf.mjs → index-DMZqWwsP.mjs} +68 -38
  105. package/dist/index-DVkMGPyS.mjs +68 -0
  106. package/dist/index-qdS1friN.mjs +949 -0
  107. package/dist/index.cjs +5 -46
  108. package/dist/index.mjs +5 -46
  109. package/dist/{install-BPhJppvd.mjs → install-BO_W7VhL.mjs} +37 -37
  110. package/dist/install-BgoC2e_2.cjs +88 -0
  111. package/dist/install-CFY3tYtJ.mjs +86 -0
  112. package/dist/{install-BOHrUaFM.cjs → install-Cs5qAPrB.cjs} +38 -38
  113. package/dist/{installPiTools-DuZ6jZvX.cjs → installPiTools-BKnXjrVu.cjs} +2 -3
  114. package/dist/{installPiTools-Cj7yU9Fc.mjs → installPiTools-B_CdM5_2.mjs} +1 -2
  115. package/dist/{installTerminalHooks-Cou3WUf3.cjs → installTerminalHooks-BQZbvxsZ.cjs} +5 -40
  116. package/dist/{installTerminalHooks-WeznjkuC.mjs → installTerminalHooks-By7gBMEF.mjs} +3 -38
  117. package/dist/lib.cjs +28 -19
  118. package/dist/lib.d.cts +12 -2
  119. package/dist/lib.d.mts +12 -2
  120. package/dist/lib.mjs +23 -14
  121. package/dist/lidState-BeDfxxEX.cjs +305 -0
  122. package/dist/lidState-CeEt442o.mjs +297 -0
  123. package/dist/limits-C-a7gr04.cjs +43 -0
  124. package/dist/limits-DIo7rEBR.mjs +36 -0
  125. package/dist/localHappyAgentAuth-C-HQyiv6.mjs +82 -0
  126. package/dist/localHappyAgentAuth-D6rFhaoc.cjs +86 -0
  127. package/dist/logger-6HYUEXNk.mjs +271 -0
  128. package/dist/logger-CS0mw3fa.cjs +275 -0
  129. package/dist/loginShellEnv-BkuYtExZ.cjs +182 -0
  130. package/dist/loginShellEnv-Dw8M7tkI.mjs +167 -0
  131. package/dist/machineMetadata-DyDbODYd.cjs +41 -0
  132. package/dist/machineMetadata-oOzBfDfs.mjs +38 -0
  133. package/dist/main-BkxJP6JQ.cjs +941 -0
  134. package/dist/main-BwNIAI1A.mjs +939 -0
  135. package/dist/manager-BQ4flvQZ.mjs +101 -0
  136. package/dist/manager-Cly8xVIR.cjs +105 -0
  137. package/dist/{mcp-Dy3f_K0h.mjs → mcp-B1A4I3TK.mjs} +27 -33
  138. package/dist/{mcp-CPYbtsYG.cjs → mcp-Hszd7qpS.cjs} +42 -48
  139. package/dist/messageBuffer-BWlGgR3A.mjs +71 -0
  140. package/dist/messageBuffer-fJMH6uY8.cjs +73 -0
  141. package/dist/mirrorProtocol-BNLtoWjO.mjs +170 -0
  142. package/dist/mirrorProtocol-BUtmg1ws.cjs +186 -0
  143. package/dist/oneShotPrompts-CL0dip9C.cjs +16 -0
  144. package/dist/oneShotPrompts-DT4p6RNd.mjs +12 -0
  145. package/dist/package-BhaKlnxz.mjs +171 -0
  146. package/dist/package-BwMrPwqH.cjs +173 -0
  147. package/dist/path-B7zW-WwE.cjs +18 -0
  148. package/dist/path-D2wdaNSk.mjs +15 -0
  149. package/dist/peerTools-BTqw97Gb.mjs +272 -0
  150. package/dist/peerTools-BvKtHXj8.cjs +283 -0
  151. package/dist/permissionMode-Cr5JCbB7.mjs +80 -0
  152. package/dist/permissionMode-CrV-XuMs.cjs +87 -0
  153. package/dist/persistence-HGW_ZVs9.mjs +372 -0
  154. package/dist/persistence-yKTH3-5X.cjs +414 -0
  155. package/dist/piExtension.mjs +2 -1
  156. package/dist/piRunnerArgs-BNloJCCg.cjs +66 -0
  157. package/dist/piRunnerArgs-BuX1UFWy.mjs +59 -0
  158. package/dist/{piRuntime-kLZNpAAl.mjs → piRuntime-CSGkwCKQ.mjs} +2 -2
  159. package/dist/{piRuntime-NtF0YqjH.cjs → piRuntime-C_H7MJCM.cjs} +4 -4
  160. package/dist/{piTerminal-B0fEOV_2.mjs → piTerminal-C8a1OrNQ.mjs} +32 -35
  161. package/dist/{piTerminal-9J0GAKzb.cjs → piTerminal-DNCZ6yTM.cjs} +32 -35
  162. package/dist/previewPath-B0rBdhy1.cjs +83 -0
  163. package/dist/previewPath-CXN4Vag0.mjs +81 -0
  164. package/dist/projectPath-BTBIMB57.mjs +10 -0
  165. package/dist/projectPath-ChLhdOyt.cjs +13 -0
  166. package/dist/registerCommonHandlers-Ccvsz7Ou.mjs +536 -0
  167. package/dist/registerCommonHandlers-DskirN-_.cjs +538 -0
  168. package/dist/registerKillSessionHandler-DZeLXWcB.cjs +361 -0
  169. package/dist/registerKillSessionHandler-GYvHdipX.mjs +358 -0
  170. package/dist/remoteSessionClient-B0LraSB9.cjs +634 -0
  171. package/dist/remoteSessionClient-CYnXRynE.mjs +612 -0
  172. package/dist/resources-BdFBQi8r.mjs +117 -0
  173. package/dist/resources-JDPugcDq.cjs +120 -0
  174. package/dist/run-CJzxoHVh.cjs +5762 -0
  175. package/dist/run-D3ifupU3.mjs +5739 -0
  176. package/dist/runClaude-Ba01qT-u.mjs +848 -0
  177. package/dist/runClaude-Da3RirzE.cjs +850 -0
  178. package/dist/runClaudeDeps-DOt79DjU.mjs +3624 -0
  179. package/dist/runClaudeDeps-DOzEBMls.cjs +3649 -0
  180. package/dist/{runGemini-DV6Ouadt.mjs → runGemini-BGeTz57m.mjs} +58 -31
  181. package/dist/{runGemini-F-mqx3_K.cjs → runGemini-DSr9LIxL.cjs} +202 -175
  182. package/dist/{runOpenClaw-Qqo9j63M.cjs → runOpenClaw-B1mTxN35.cjs} +64 -65
  183. package/dist/{runOpenClaw-D7MlPS5n.mjs → runOpenClaw-CogSedIc.mjs} +31 -32
  184. package/dist/sandbox-CG7IyDTU.cjs +187 -0
  185. package/dist/sandbox-DTe6d54R.mjs +180 -0
  186. package/dist/selfCheck-C9mVrQwf.mjs +84 -0
  187. package/dist/selfCheck-Dc-mNSLb.cjs +87 -0
  188. package/dist/{send-4qJTCAYW.cjs → send-B7J_eaG3.cjs} +16 -9
  189. package/dist/{send-CLhYXNgy.mjs → send-CBXJKc6O.mjs} +12 -5
  190. package/dist/server-CZTP_iOn.cjs +407 -0
  191. package/dist/server-DcVBv04l.mjs +404 -0
  192. package/dist/sessionLock-Bys1PNSo.cjs +205 -0
  193. package/dist/sessionLock-D-EG2CfG.mjs +197 -0
  194. package/dist/sessionModeFile-DMl3dw3r.mjs +32 -0
  195. package/dist/sessionModeFile-RSm0NQkH.cjs +36 -0
  196. package/dist/sessionOps-Dn8z85h_.cjs +783 -0
  197. package/dist/sessionOps-SuHW8Dfe.mjs +760 -0
  198. package/dist/sessionScanner-BOrGjMXL.cjs +209 -0
  199. package/dist/sessionScanner-CX3onqXh.mjs +206 -0
  200. package/dist/{sessions-CzIH2rGp.mjs → sessions-BEG_HTSi.mjs} +17 -8
  201. package/dist/{sessions-Bh1pyddi.cjs → sessions-D74aJbhL.cjs} +37 -28
  202. package/dist/setupOfflineReconnection-ByElY60m.cjs +74 -0
  203. package/dist/setupOfflineReconnection-DpvWTt69.mjs +72 -0
  204. package/dist/smallFastModel-BLNgMrwR.mjs +18 -0
  205. package/dist/smallFastModel-D_uxGMo3.cjs +21 -0
  206. package/dist/{spawn-CPIWlPOM.mjs → spawn-B7yro0b2.mjs} +26 -38
  207. package/dist/{spawn-CmFOSVdW.cjs → spawn-DgR0RKRs.cjs} +52 -64
  208. package/dist/spawnAgents-BGQiEF-w.cjs +52 -0
  209. package/dist/spawnAgents-DZzZ9jMI.mjs +45 -0
  210. package/dist/spawnHappyCLI-B2H5s4B_.mjs +64 -0
  211. package/dist/spawnHappyCLI-DBhwZT7O.cjs +66 -0
  212. package/dist/specialCommands-BSqhYg5X.mjs +52 -0
  213. package/dist/specialCommands-DSsTeRej.cjs +54 -0
  214. package/dist/startFileWatcher-B4VsLgjD.cjs +71 -0
  215. package/dist/startFileWatcher-DErVWZE4.mjs +69 -0
  216. package/dist/startupPlan-BLMbfQIH.cjs +12 -0
  217. package/dist/startupPlan-DFMyNgAs.mjs +10 -0
  218. package/dist/streamRelay-BiKw59Cc.cjs +233 -0
  219. package/dist/streamRelay-FPVbHpbM.mjs +231 -0
  220. package/dist/sync-C8gyQFMc.mjs +69 -0
  221. package/dist/sync-DccIKZ5_.cjs +71 -0
  222. package/dist/systemPrompt-BwTPWfMu.cjs +97 -0
  223. package/dist/systemPrompt-DX521JJz.mjs +94 -0
  224. package/dist/{teams-CEahj4cm.mjs → teams-C32F1hqd.mjs} +9 -40
  225. package/dist/{teams-CB1R9-Z9.cjs → teams-Dl8g8kTI.cjs} +14 -45
  226. package/dist/template-BaHAmH2K.cjs +129 -0
  227. package/dist/template-CiOPa2rd.mjs +121 -0
  228. package/dist/{terminalTitleSuggest-DKgX3osW.mjs → terminalTitleSuggest-CeKsX_V6.mjs} +1 -9
  229. package/dist/{terminalTitleSuggest-Dji8I0Jy.cjs → terminalTitleSuggest-DhvRFX3H.cjs} +0 -10
  230. package/dist/terminals-B_bfC9c2.cjs +250 -0
  231. package/dist/terminals-Dlg0x3nY.mjs +235 -0
  232. package/dist/time-D0XyHOid.mjs +38 -0
  233. package/dist/time-DCuTyook.cjs +42 -0
  234. package/dist/titleGenerator-CQ8CoGhg.mjs +166 -0
  235. package/dist/titleGenerator-ChzMJsyg.cjs +172 -0
  236. package/dist/tmuxSocket-B8Yx20sT.mjs +19 -0
  237. package/dist/tmuxSocket-D-Xt6Vn-.cjs +24 -0
  238. package/dist/{todo-DqyRI4wP.cjs → todo-BvUOTa8G.cjs} +15 -12
  239. package/dist/{todo-Cknp5D4U.mjs → todo-CZ6cFrrn.mjs} +11 -8
  240. package/dist/tools-BZtHF_qQ.mjs +177 -0
  241. package/dist/tools-Be3AoAwu.cjs +63 -0
  242. package/dist/tools-DQj0AeLk.cjs +180 -0
  243. package/dist/tools-DkluDcG4.mjs +60 -0
  244. package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
  245. package/dist/trimIdent-BAjmv_TG.mjs +20 -0
  246. package/dist/types-CRS-srZK.cjs +66 -0
  247. package/dist/types-CkqAACXx.mjs +64 -0
  248. package/dist/uninstall-BkLzaJ6I.mjs +47 -0
  249. package/dist/uninstall-x-TUtf5H.cjs +49 -0
  250. package/package.json +7 -7
  251. package/dist/agentGuidance-BRDN1NoZ.cjs +0 -2217
  252. package/dist/agentGuidance-KgvYuF3R.mjs +0 -2145
  253. package/dist/index-BF6WfgV5.mjs +0 -19624
  254. package/dist/index-C-JazrMB.mjs +0 -52
  255. package/dist/index-blDSadUR.cjs +0 -58
  256. package/dist/index-rH3DrQ5B.cjs +0 -19688
  257. package/dist/limits-BH5CV5vR.cjs +0 -2683
  258. package/dist/limits-CjRj8IX5.mjs +0 -2558
@@ -0,0 +1,850 @@
1
+ 'use strict';
2
+
3
+ var os = require('node:os');
4
+ var sessionLock = require('./sessionLock-Bys1PNSo.cjs');
5
+ var node_crypto = require('node:crypto');
6
+ var api = require('./api-CgundNNX.cjs');
7
+ var logger = require('./logger-CS0mw3fa.cjs');
8
+ var _package = require('./package-BwMrPwqH.cjs');
9
+ var persistence = require('./persistence-yKTH3-5X.cjs');
10
+ var createSessionMetadata = require('./createSessionMetadata-BJxEnM-o.cjs');
11
+ var index = require('./index-DA7yINuk.cjs');
12
+ var configuration = require('./configuration-BSYpcPwE.cjs');
13
+ var controlClient = require('./controlClient-Bcwv3D21.cjs');
14
+ var machineMetadata = require('./machineMetadata-DyDbODYd.cjs');
15
+ var projectPath = require('./projectPath-ChLhdOyt.cjs');
16
+ var path = require('node:path');
17
+ var permissionMode = require('./permissionMode-CrV-XuMs.cjs');
18
+ var authRelay = require('./authRelay-BdYkC1Po.cjs');
19
+ var promises = require('node:fs/promises');
20
+ var defaultPermissionMode = require('./defaultPermissionMode-CxmTXUQR.cjs');
21
+ var attachmentContent = require('./attachmentContent-DpiTJZb4.cjs');
22
+ var startupPlan = require('./startupPlan-BLMbfQIH.cjs');
23
+ require('node:fs');
24
+ require('axios');
25
+ require('node:events');
26
+ require('socket.io-client');
27
+ require('./attachmentLimits-CzVrqgLR.cjs');
28
+ require('zod');
29
+ require('./limits-C-a7gr04.cjs');
30
+ require('./time-DCuTyook.cjs');
31
+ require('./lidState-BeDfxxEX.cjs');
32
+ require('os');
33
+ require('child_process');
34
+ require('./registerCommonHandlers-DskirN-_.cjs');
35
+ require('util');
36
+ require('fs/promises');
37
+ require('crypto');
38
+ require('path');
39
+ require('cross-spawn');
40
+ require('@paralleldrive/cuid2');
41
+ require('./sync-DccIKZ5_.cjs');
42
+ require('./backgroundTaskActivity-CBvUdj39.cjs');
43
+ require('./contentLogMetadata-ZSFk_NjQ.cjs');
44
+ require('chalk');
45
+ require('node:util');
46
+ require('./detectCLI-DR9ZaN6B.cjs');
47
+ require('fs');
48
+ require('./localHappyAgentAuth-D6rFhaoc.cjs');
49
+ require('tweetnacl');
50
+ require('url');
51
+
52
+ const DEFAULT_CLAUDE_MODEL = void 0;
53
+ const DEFAULT_CLAUDE_EFFORT = void 0;
54
+ async function runClaude(credentials, options = {}) {
55
+ logger.logger.debug(`[CLAUDE] ===== CLAUDE MODE STARTING =====`);
56
+ logger.logger.debug(`[CLAUDE] This is the Claude agent, NOT Gemini`);
57
+ options = { ...options, claudeEnvVars: { ...options.claudeEnvVars, HAPPY_MANAGED: "1" } };
58
+ const workingDirectory = process.cwd();
59
+ const isAssistantVariant = process.env.HAPPY_SESSION_VARIANT === "assistant";
60
+ if (isAssistantVariant) {
61
+ try {
62
+ const { bootstrapAssistantHome } = await Promise.resolve().then(function () { return require('./bootstrap-B5G1TijC.cjs'); });
63
+ await bootstrapAssistantHome();
64
+ } catch (error) {
65
+ logger.logger.debug("[START] Assistant home bootstrap failed:", error);
66
+ }
67
+ }
68
+ logger.logger.debug(`[START] Happy process started pid=${process.pid} node=${process.version} cwd=${workingDirectory}`);
69
+ logger.logger.debug(`[START] Options: startedBy=${options.startedBy}, startingMode=${options.startingMode}`);
70
+ if (options.startedBy === "daemon" && options.startingMode === "local") {
71
+ throw new Error("Daemon-spawned sessions cannot use local/interactive mode. Use --happy-starting-mode remote or spawn sessions directly from terminal.");
72
+ }
73
+ const depsLoad = Promise.resolve().then(function () { return require('./runClaudeDeps-DOzEBMls.cjs'); });
74
+ depsLoad.catch(() => {
75
+ });
76
+ const reconnectRequested = Boolean(
77
+ process.env.HAPPY_RECONNECT_SESSION_ID && process.env.HAPPY_RECONNECT_ENCRYPTION_KEY && process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT
78
+ );
79
+ const startupPlan$1 = startupPlan.planClaudeStartup({ startedBy: options.startedBy, reconnect: reconnectRequested });
80
+ api.connectionState.setBackend("Claude");
81
+ const api$1 = await api.ApiClient.create(credentials);
82
+ let state = {};
83
+ const settings = await persistence.readSettings();
84
+ let machineId = settings?.machineId;
85
+ const sandboxConfig = options.noSandbox ? void 0 : settings?.sandboxConfig;
86
+ const sandboxEnabled = Boolean(sandboxConfig?.enabled);
87
+ const initialPermissionMode = permissionMode.applySandboxPermissionPolicy(
88
+ permissionMode.resolveInitialClaudePermissionMode(options.permissionMode ?? defaultPermissionMode.DEFAULT_CLAUDE_PERMISSION_MODE, options.claudeArgs),
89
+ sandboxEnabled
90
+ );
91
+ const dangerouslySkipPermissions = initialPermissionMode === "bypassPermissions" || initialPermissionMode === "yolo" || sandboxEnabled || Boolean(options.claudeArgs?.includes("--dangerously-skip-permissions"));
92
+ if (!machineId) {
93
+ console.error(`[START] No machine ID found in settings, which is unexpected since authAndSetupMachineIfNeeded should have created it. Please report this issue on https://github.com/Mereithhh/very-happy/issues`);
94
+ process.exit(1);
95
+ }
96
+ logger.logger.debug(`Using machineId: ${machineId}`);
97
+ const sessionTag = node_crypto.randomUUID();
98
+ if (startupPlan$1.registerMachine) {
99
+ await api$1.getOrCreateMachine({
100
+ machineId,
101
+ metadata: machineMetadata.getInitialMachineMetadata()
102
+ });
103
+ }
104
+ const forkedFromSessionId = process.env.HAPPY_FORKED_FROM_SESSION_ID;
105
+ const forkedFromMessageId = process.env.HAPPY_FORKED_FROM_MESSAGE_ID;
106
+ const importedFromClaudeSessionId = process.env.HAPPY_IMPORTED_FROM_CLAUDE_SESSION_ID;
107
+ const importTitle = process.env.HAPPY_IMPORT_TITLE;
108
+ const originTags = createSessionMetadata.spawnOriginTags(process.env, "claude");
109
+ let metadata = {
110
+ path: workingDirectory,
111
+ host: os.hostname(),
112
+ version: _package.packageJson.version,
113
+ os: os.platform(),
114
+ machineId,
115
+ homeDir: os.homedir(),
116
+ happyHomeDir: configuration.configuration.happyHomeDir,
117
+ happyLibDir: projectPath.projectPath(),
118
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
119
+ startedFromDaemon: options.startedBy === "daemon",
120
+ hostPid: process.pid,
121
+ startedBy: options.startedBy || "terminal",
122
+ // Initialize lifecycle state
123
+ lifecycleState: "running",
124
+ lifecycleStateSince: Date.now(),
125
+ flavor: "claude",
126
+ ...process.env.VH_TEAM_OPERATION_ID ? { teamOperationId: process.env.VH_TEAM_OPERATION_ID } : {},
127
+ // Advertise composer support before the first SDK response. Otherwise
128
+ // a brand-new session cannot pick a PDF until after sending once.
129
+ attachmentKinds: [...attachmentContent.CLAUDE_ATTACHMENT_KINDS],
130
+ queueCancellation: true,
131
+ capabilities: ["claude-steer-v1", "claude-live-permission-v1", "claude-live-permission-v2", "claude-btw-v1", "claude-runtime-controls-v1", "claude-opus-5-5-v1", index.PROMPT_QUEUE_CAPABILITY],
132
+ // Effective mode this process enforces. Kept current by
133
+ // publishPermissionMode below; the web renders it instead of guessing.
134
+ permissionMode: permissionMode.mapToClaudeMode(initialPermissionMode ?? "default"),
135
+ sandbox: sandboxConfig?.enabled ? sandboxConfig : null,
136
+ dangerouslySkipPermissions,
137
+ ...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
138
+ ...forkedFromMessageId ? { forkedFromMessageId } : {},
139
+ ...importedFromClaudeSessionId ? { importedFromClaudeSessionId } : {},
140
+ ...importTitle ? { summary: { text: importTitle, updatedAt: Date.now() } } : {},
141
+ ...isAssistantVariant ? { variant: "assistant" } : {},
142
+ // B-091/B-303: dispatched sessions (daemon spawn RPC exports
143
+ // HAPPY_SPAWNED_BY, B-069) are born tagged with their spawn origin so
144
+ // every list shows where the work came from. NOT on the meta-agent
145
+ // itself — that one is the variant above and never joins the lists at
146
+ // all. (Shared helper: the other flavors get the same tag via
147
+ // createSessionMetadata.)
148
+ ...originTags ? { tags: originTags } : {}
149
+ };
150
+ const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
151
+ const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
152
+ const reconnectVariant = process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT;
153
+ const reconnectSeq = process.env.HAPPY_RECONNECT_SEQ;
154
+ const reconnectMetadataVersion = process.env.HAPPY_RECONNECT_METADATA_VERSION;
155
+ const reconnectAgentStateVersion = process.env.HAPPY_RECONNECT_AGENT_STATE_VERSION;
156
+ let response;
157
+ if (reconnectSessionId && reconnectKeyBase64 && reconnectVariant) {
158
+ logger.logger.debug(`[START] Reconnecting to existing session ${reconnectSessionId}`);
159
+ response = {
160
+ id: reconnectSessionId,
161
+ seq: parseInt(reconnectSeq || "0", 10),
162
+ encryptionKey: authRelay.decodeBase64(reconnectKeyBase64),
163
+ encryptionVariant: reconnectVariant,
164
+ metadata,
165
+ metadataVersion: parseInt(reconnectMetadataVersion || "0", 10),
166
+ agentState: state,
167
+ agentStateVersion: parseInt(reconnectAgentStateVersion || "0", 10)
168
+ };
169
+ } else {
170
+ response = await api$1.getOrCreateSession({ tag: sessionTag, metadata, state });
171
+ }
172
+ if (!response) {
173
+ let offlineSessionId = null;
174
+ const { claudeLocal } = await Promise.resolve().then(function () { return require('./claudeLocal-CRGG4Pea.cjs'); });
175
+ const { createSessionScanner: createSessionScanner2 } = await depsLoad;
176
+ const reconnection = api.startOfflineReconnection({
177
+ serverUrl: configuration.configuration.serverUrl,
178
+ onReconnected: async () => {
179
+ const resp = await api$1.getOrCreateSession({ tag: node_crypto.randomUUID(), metadata, state });
180
+ if (!resp) throw new Error("Server unavailable");
181
+ const session2 = api$1.sessionSyncClient(resp);
182
+ const scanner = await createSessionScanner2({
183
+ sessionId: null,
184
+ workingDirectory,
185
+ onMessage: (msg) => session2.sendClaudeSessionMessage(msg)
186
+ });
187
+ if (offlineSessionId) scanner.onNewSession(offlineSessionId);
188
+ return { session: session2, scanner };
189
+ },
190
+ onNotify: console.log,
191
+ onCleanup: () => {
192
+ }
193
+ });
194
+ try {
195
+ await claudeLocal({
196
+ path: workingDirectory,
197
+ sessionId: null,
198
+ onSessionFound: (id) => {
199
+ offlineSessionId = id;
200
+ },
201
+ onThinkingChange: () => {
202
+ },
203
+ abort: new AbortController().signal,
204
+ claudeEnvVars: options.claudeEnvVars,
205
+ claudeArgs: options.claudeArgs,
206
+ mcpServers: {},
207
+ allowedTools: [],
208
+ sandboxConfig
209
+ });
210
+ } finally {
211
+ reconnection.cancel();
212
+ }
213
+ process.exit(0);
214
+ }
215
+ logger.logger.debug(`Session created: ${response.id}`);
216
+ await sessionLock.claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "claude" });
217
+ if (reconnectSessionId && !await api$1.reactivateSession(response.id)) {
218
+ throw new Error(`Failed to reactivate archived session ${response.id}`);
219
+ }
220
+ let reconnectSeeded = false;
221
+ if (reconnectSessionId) {
222
+ const snapshot = await api$1.getSession(response.id, response.encryptionKey, response.encryptionVariant);
223
+ if (snapshot.ok) {
224
+ response = sessionLock.applyServerSnapshot(response, snapshot.session);
225
+ reconnectSeeded = true;
226
+ logger.logger.debug(`[START] Reconnect seeded from server: seq=${snapshot.session.seq} metadataVersion=${snapshot.session.metadataVersion}`);
227
+ } else {
228
+ response = sessionLock.withoutServerSnapshot(response);
229
+ logger.logger.debug(`[START] Reconnect without server snapshot (${snapshot.reason}); skipping existing messages`);
230
+ }
231
+ }
232
+ const earlySession = startupPlan$1.sessionClientBeforeWebhook ? api$1.sessionSyncClient(response) : null;
233
+ try {
234
+ logger.logger.debug(`[START] Reporting session ${response.id} to daemon`);
235
+ const result = await controlClient.notifyDaemonSessionStarted(response.id, metadata, {
236
+ encryptionKey: authRelay.encodeBase64(response.encryptionKey),
237
+ encryptionVariant: response.encryptionVariant,
238
+ seq: response.seq,
239
+ metadataVersion: response.metadataVersion,
240
+ agentStateVersion: response.agentStateVersion
241
+ });
242
+ if (result.error) {
243
+ logger.logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
244
+ } else {
245
+ logger.logger.debug(`[START] Reported session ${response.id} to daemon`);
246
+ }
247
+ } catch (error) {
248
+ logger.logger.debug("[START] Failed to report to daemon (may not be running):", error);
249
+ }
250
+ if (!startupPlan$1.registerMachine) {
251
+ void Promise.resolve().then(() => api$1.getOrCreateMachine({ machineId, metadata: machineMetadata.getInitialMachineMetadata() })).catch((error) => logger.logger.debug("[START] Background machine registration failed:", error));
252
+ }
253
+ let deps;
254
+ try {
255
+ deps = await depsLoad;
256
+ } catch (error) {
257
+ logger.logger.warn(`[START] Failed to load the Claude runner modules for session ${response.id}; marking it offline and exiting: ${error instanceof Error ? error.message : String(error)}`);
258
+ try {
259
+ await earlySession?.close();
260
+ } catch {
261
+ }
262
+ try {
263
+ await api$1.deactivateSession(response.id);
264
+ } catch {
265
+ }
266
+ process.exit(1);
267
+ }
268
+ const {
269
+ loop,
270
+ MessageQueue2,
271
+ claudeModeHash,
272
+ parseSpecialCommand,
273
+ getEnvironmentInfo,
274
+ startHappyServer,
275
+ startHookServer,
276
+ EditReportThrottle,
277
+ extractClaudeEditPaths,
278
+ reportSessionEditToDaemon,
279
+ generateHookSettingsFile,
280
+ cleanupHookSettingsFile,
281
+ registerKillSessionHandler,
282
+ SessionExitGate,
283
+ exitIntentFromArchiveFlag,
284
+ settleSessionOnExit,
285
+ registerSideQuestionHandler,
286
+ writeSideQuestionSettingsFile,
287
+ claudeCheckSession,
288
+ createSessionScanner,
289
+ getProjectPath,
290
+ RawJSONLinesSchema,
291
+ TitleGenerator,
292
+ BoardAnalyzer,
293
+ FileRateLimiter,
294
+ createSelfReportState,
295
+ withAssistantDenylist,
296
+ contentLogMetadata
297
+ } = deps;
298
+ logger.logger.debugLargeJson("[START] Happy process started", getEnvironmentInfo());
299
+ const session = earlySession ?? api$1.sessionSyncClient(response, reconnectSeeded ? { initialSeq: response.seq } : void 0);
300
+ if (reconnectSessionId) {
301
+ session.suppressNextArchiveSignal();
302
+ if (!reconnectSeeded) session.skipExistingMessages();
303
+ void session.cancelUndeliveredQueuedInputs();
304
+ session.updateMetadata((meta) => ({
305
+ ...sessionLock.mergeReconnectMetadata(meta, metadata, Date.now()),
306
+ queueCancellation: true,
307
+ // B-262 batch 2 (B5): a reconnected process re-publishes the mode
308
+ // IT enforces; the server copy may still carry the previous
309
+ // process's value.
310
+ permissionMode: permissionMode.mapToClaudeMode(initialPermissionMode ?? "default")
311
+ }));
312
+ }
313
+ const forkClaudeSessionId = process.env.HAPPY_FORK_CLAUDE_SESSION_ID;
314
+ if (!reconnectSessionId && forkClaudeSessionId) {
315
+ const jsonlPath = path.join(getProjectPath(workingDirectory), `${forkClaudeSessionId}.jsonl`);
316
+ try {
317
+ const file = await promises.readFile(jsonlPath, "utf-8");
318
+ const lines = file.split("\n");
319
+ let backfilled = 0;
320
+ for (const line of lines) {
321
+ if (line.trim().length === 0) continue;
322
+ let parsed;
323
+ try {
324
+ parsed = JSON.parse(line);
325
+ } catch {
326
+ continue;
327
+ }
328
+ const result = RawJSONLinesSchema.safeParse(parsed);
329
+ if (!result.success) continue;
330
+ session.sendClaudeSessionMessage(result.data);
331
+ backfilled += 1;
332
+ }
333
+ logger.logger.debug(`[FORK BACKFILL] Replayed ${backfilled} historical messages from ${jsonlPath}`);
334
+ session.updateMetadata((meta) => ({ ...meta, claudeSessionId: forkClaudeSessionId }));
335
+ } catch (error) {
336
+ logger.logger.debug(`[FORK BACKFILL] Failed to read ${jsonlPath}:`, error);
337
+ }
338
+ }
339
+ const recentAppPromptsMaxAgeMs = 5 * 60 * 1e3;
340
+ const recentAppPrompts = [];
341
+ const promptDedupeKey = (text) => attachmentContent.stripAttachmentManifest(text).trim();
342
+ const recordAppPrompt = (text) => {
343
+ const now = Date.now();
344
+ recentAppPrompts.push({ text: promptDedupeKey(text), addedAt: now });
345
+ const cutoff = now - recentAppPromptsMaxAgeMs;
346
+ while (recentAppPrompts.length > 0 && recentAppPrompts[0].addedAt < cutoff) {
347
+ recentAppPrompts.shift();
348
+ }
349
+ };
350
+ const consumeAppPrompt = (text) => {
351
+ const cutoff = Date.now() - recentAppPromptsMaxAgeMs;
352
+ const key = promptDedupeKey(text);
353
+ for (let i = 0; i < recentAppPrompts.length; i++) {
354
+ const entry = recentAppPrompts[i];
355
+ if (entry.addedAt < cutoff) continue;
356
+ if (entry.text === key) {
357
+ recentAppPrompts.splice(i, 1);
358
+ return true;
359
+ }
360
+ }
361
+ return false;
362
+ };
363
+ let currentRunMode = options.startingMode ?? "local";
364
+ const titleGenerator = new TitleGenerator(session);
365
+ const boardSelfReport = createSelfReportState();
366
+ const boardAnalyzer = new BoardAnalyzer(session, {
367
+ enabled: settings?.boardLlm === true,
368
+ selfReportState: boardSelfReport,
369
+ fetchTasks: async () => {
370
+ const item = await api$1.kvGet("vh.board-tasks.v1");
371
+ if (!item) return null;
372
+ try {
373
+ const parsed = JSON.parse(Buffer.from(item.value, "base64").toString("utf8"));
374
+ if (!Array.isArray(parsed.tasks)) return null;
375
+ return parsed.tasks.filter((t) => typeof t?.id === "string" && typeof t?.title === "string" && t.status === "open").map((t) => ({ id: t.id, title: t.title }));
376
+ } catch {
377
+ return null;
378
+ }
379
+ },
380
+ rateLimiter: new FileRateLimiter(path.resolve(configuration.configuration.happyHomeDir, "board-analyzer-rate.json"))
381
+ });
382
+ session.on("claude-session-message", (body) => boardAnalyzer.noteClaudeMessage(body));
383
+ session.on("turn-ended", () => boardAnalyzer.onTurnEnd());
384
+ const editThrottle = new EditReportThrottle();
385
+ session.on("claude-session-message", (body) => {
386
+ try {
387
+ for (const edit of editThrottle.take(extractClaudeEditPaths(body))) {
388
+ reportSessionEditToDaemon({ sessionId: response.id, path: edit.path, tool: edit.tool, cwd: workingDirectory, ...edit.at !== void 0 ? { at: edit.at } : {} });
389
+ }
390
+ } catch (error) {
391
+ logger.logger.debug("[START] edit report failed:", error);
392
+ }
393
+ });
394
+ options = { ...options, claudeEnvVars: { ...options.claudeEnvVars, VH_PEER_SESSION_ID: response.id } };
395
+ const initialScannerSessionId = forkClaudeSessionId ?? (metadata.claudeSessionId ?? null);
396
+ const remoteScanner = await createSessionScanner({
397
+ sessionId: initialScannerSessionId,
398
+ workingDirectory,
399
+ onMessage: (raw) => {
400
+ if (currentRunMode !== "remote") return;
401
+ if (raw.type !== "user") return;
402
+ if (raw.isSidechain) return;
403
+ const content = raw.message?.content;
404
+ if (typeof content !== "string") return;
405
+ if (content.trim().length === 0) return;
406
+ if (consumeAppPrompt(content)) return;
407
+ titleGenerator.maybeGenerate(content);
408
+ boardAnalyzer.noteUserMessage(content);
409
+ session.sendClaudeSessionMessage(raw);
410
+ }
411
+ });
412
+ const happyServer = await startHappyServer(session, { assistant: isAssistantVariant, selfReportState: boardSelfReport });
413
+ logger.logger.debug(`[START] Happy MCP server started at ${happyServer.url}`);
414
+ let currentSession = null;
415
+ const hookServer = await startHookServer({
416
+ onSessionHook: (sessionId, data) => {
417
+ logger.logger.debug(`[START] Session hook received: ${sessionId}`, contentLogMetadata(data));
418
+ remoteScanner.onNewSession(sessionId, { treatExistingAsProcessed: true });
419
+ if (currentSession) {
420
+ const previousSessionId = currentSession.sessionId;
421
+ if (previousSessionId !== sessionId) {
422
+ logger.logger.debug(`[START] Claude session ID changed: ${previousSessionId} -> ${sessionId}`);
423
+ currentSession.onSessionFound(sessionId);
424
+ }
425
+ }
426
+ }
427
+ });
428
+ logger.logger.debug(`[START] Hook server started on port ${hookServer.port}`);
429
+ const hookSettingsPath = generateHookSettingsFile(hookServer.port);
430
+ logger.logger.debug(`[START] Generated hook settings file: ${hookSettingsPath}`);
431
+ const logPath = logger.logger.logFilePath;
432
+ logger.logger.infoDeveloper(`Session: ${response.id}`);
433
+ logger.logger.infoDeveloper(`Logs: ${logPath}`);
434
+ session.updateAgentState((currentState) => ({
435
+ ...currentState,
436
+ controlledByUser: options.startingMode !== "remote"
437
+ }));
438
+ const messageQueue = new MessageQueue2(claudeModeHash);
439
+ const promptQueueDrain = session.attachPromptQueueDrain({
440
+ isIdle: () => messageQueue.size() === 0 && messageQueue.isWaiting()
441
+ });
442
+ messageQueue.setOnWait(() => promptQueueDrain.onIdle());
443
+ messageQueue.setOnDiscard((entries) => {
444
+ const byReason = /* @__PURE__ */ new Map();
445
+ for (const entry of entries) {
446
+ if (!entry.sourceId) continue;
447
+ const list = byReason.get(entry.reason) ?? [];
448
+ list.push(entry.sourceId);
449
+ byReason.set(entry.reason, list);
450
+ }
451
+ for (const [reason, keys] of byReason) session.sendQueueCancelReason(keys, reason);
452
+ });
453
+ session.rpcHandlerManager.registerHandler("cancelQueuedMessage", async (params) => ({
454
+ removed: messageQueue.cancelPending(
455
+ typeof params?.localKey === "string" ? params.localKey : void 0,
456
+ typeof params?.text === "string" ? params.text : void 0
457
+ )
458
+ }));
459
+ let currentPermissionMode = initialPermissionMode;
460
+ let publishedPermissionMode = permissionMode.mapToClaudeMode(initialPermissionMode ?? "default");
461
+ const publishPermissionMode = (mode) => {
462
+ currentPermissionMode = mode;
463
+ const published = permissionMode.mapToClaudeMode(mode ?? "default");
464
+ if (published === publishedPermissionMode) return;
465
+ publishedPermissionMode = published;
466
+ session.updateMetadata((meta) => ({ ...meta, permissionMode: published }));
467
+ };
468
+ const publishEffectivePermissionMode = (effective) => {
469
+ const decision = permissionMode.reconcilePublishedPermissionMode({
470
+ intent: permissionMode.mapToClaudeMode(currentPermissionMode ?? "default"),
471
+ published: publishedPermissionMode,
472
+ effective
473
+ });
474
+ if (!decision) return;
475
+ if (decision.mismatch) {
476
+ logger.logger.warn(`[loop] Claude Code enforces permission mode '${effective}' but this process asked for '${permissionMode.mapToClaudeMode(currentPermissionMode ?? "default")}' \u2014 check ~/.claude settings (permissions.deny/ask/defaultMode/disableBypassPermissionsMode). Publishing the effective mode.`);
477
+ }
478
+ if (decision.publish !== publishedPermissionMode) {
479
+ publishedPermissionMode = decision.publish;
480
+ session.updateMetadata((meta) => ({ ...meta, permissionMode: decision.publish }));
481
+ }
482
+ };
483
+ let lastExplicitModeSwitchAt = 0;
484
+ let currentModel = options.model ?? DEFAULT_CLAUDE_MODEL;
485
+ let currentFallbackModel = void 0;
486
+ let currentCustomSystemPrompt = void 0;
487
+ let currentAppendSystemPrompt = void 0;
488
+ let currentAllowedTools = void 0;
489
+ let currentDisallowedTools = withAssistantDenylist(void 0, isAssistantVariant);
490
+ let currentEffort = DEFAULT_CLAUDE_EFFORT;
491
+ registerSideQuestionHandler(session.rpcHandlerManager, {
492
+ getClaudeSessionId: () => {
493
+ const id = currentSession?.sessionId ?? null;
494
+ return id && claudeCheckSession(id, workingDirectory) ? id : null;
495
+ },
496
+ getModel: () => currentModel,
497
+ cwd: workingDirectory,
498
+ getEnv: () => options.claudeEnvVars,
499
+ settingsPath: writeSideQuestionSettingsFile(configuration.configuration.happyHomeDir),
500
+ // B-482: in remote mode the launcher parks the live Query's /btw entry
501
+ // on the session while the Query is alive; local mode never sets it.
502
+ getLiveQuery: () => currentSession?.getSideQuestionLive() ?? null
503
+ });
504
+ const resetCurrentModeDefaults = () => {
505
+ currentModel = options.model ?? DEFAULT_CLAUDE_MODEL;
506
+ currentFallbackModel = void 0;
507
+ currentCustomSystemPrompt = void 0;
508
+ currentAppendSystemPrompt = void 0;
509
+ currentAllowedTools = void 0;
510
+ currentDisallowedTools = withAssistantDenylist(void 0, isAssistantVariant);
511
+ currentEffort = DEFAULT_CLAUDE_EFFORT;
512
+ logger.logger.debug("[loop] Reset current mode defaults after abort");
513
+ };
514
+ session.onFileEvent((fileEvent) => {
515
+ const ev = fileEvent.content.data.ev;
516
+ logger.logger.debug(`[loop] File event received: ${ev.name} (${ev.size} bytes, ref: ${ev.ref})`);
517
+ const downloadPromise = (async () => {
518
+ try {
519
+ const decrypted = await session.downloadAndDecryptAttachment(ev.ref);
520
+ if (!decrypted) {
521
+ logger.logger.debug(`[loop] Failed to decrypt attachment: ${ev.name}`);
522
+ return null;
523
+ }
524
+ logger.logger.debug(`[loop] Attachment decrypted: ${ev.name} (${decrypted.length} bytes)`);
525
+ return { data: decrypted, mimeType: ev.mimeType ?? "image/jpeg", name: ev.name };
526
+ } catch (error) {
527
+ logger.logger.debug(`[loop] Failed to download attachment: ${ev.name}`, { error });
528
+ return null;
529
+ }
530
+ })();
531
+ session.trackAttachmentDownload(downloadPromise);
532
+ });
533
+ session.onUserMessage(async (message) => {
534
+ if (message?.content?.text) {
535
+ titleGenerator.maybeGenerate(message.content.text);
536
+ boardAnalyzer.noteUserMessage(message.content.text);
537
+ }
538
+ const attachmentsForThisMessage = await session.drainAttachmentsForUserMessage();
539
+ let messagePermissionMode = currentPermissionMode;
540
+ const messageCreatedAt = message.createdAt;
541
+ const staleModeSnapshot = message.meta?.permissionMode !== void 0 && typeof messageCreatedAt === "number" && messageCreatedAt < lastExplicitModeSwitchAt;
542
+ if (staleModeSnapshot) {
543
+ logger.logger.debug(`[loop] Ignoring stale meta.permissionMode=${message.meta?.permissionMode} (message ${messageCreatedAt} predates explicit switch ${lastExplicitModeSwitchAt}); keeping ${currentPermissionMode}`);
544
+ } else if (message.meta?.permissionMode) {
545
+ const previousPermissionMode = currentPermissionMode;
546
+ messagePermissionMode = permissionMode.resolveRemoteClaudePermissionMode(
547
+ currentPermissionMode,
548
+ message.meta.permissionMode,
549
+ sandboxEnabled
550
+ );
551
+ publishPermissionMode(messagePermissionMode);
552
+ const ignoredDefaultDowngrade = (previousPermissionMode === "bypassPermissions" || previousPermissionMode === "yolo") && message.meta.permissionMode === "default" && currentPermissionMode === previousPermissionMode;
553
+ if (ignoredDefaultDowngrade) {
554
+ logger.logger.debug(`[loop] Ignoring permission mode downgrade from ${previousPermissionMode} to default`);
555
+ } else {
556
+ logger.logger.debug(`[loop] Permission mode updated from user message to: ${currentPermissionMode}`);
557
+ }
558
+ } else {
559
+ logger.logger.debug(`[loop] User message received with no permission mode override, using current: ${currentPermissionMode}`);
560
+ }
561
+ let messageModel = currentModel;
562
+ if (message.meta?.hasOwnProperty("model")) {
563
+ messageModel = message.meta.model || void 0;
564
+ currentModel = messageModel;
565
+ logger.logger.debug(`[loop] Model updated from user message: ${messageModel || "reset to default"}`);
566
+ } else {
567
+ logger.logger.debug(`[loop] User message received with no model override, using current: ${currentModel || "default"}`);
568
+ }
569
+ let messageCustomSystemPrompt = currentCustomSystemPrompt;
570
+ if (message.meta?.hasOwnProperty("customSystemPrompt")) {
571
+ messageCustomSystemPrompt = message.meta.customSystemPrompt || void 0;
572
+ currentCustomSystemPrompt = messageCustomSystemPrompt;
573
+ logger.logger.debug(`[loop] Custom system prompt updated from user message: ${messageCustomSystemPrompt ? "set" : "reset to none"}`);
574
+ } else {
575
+ logger.logger.debug(`[loop] User message received with no custom system prompt override, using current: ${currentCustomSystemPrompt ? "set" : "none"}`);
576
+ }
577
+ let messageFallbackModel = currentFallbackModel;
578
+ if (message.meta?.hasOwnProperty("fallbackModel")) {
579
+ messageFallbackModel = message.meta.fallbackModel || void 0;
580
+ currentFallbackModel = messageFallbackModel;
581
+ logger.logger.debug(`[loop] Fallback model updated from user message: ${messageFallbackModel || "reset to none"}`);
582
+ } else {
583
+ logger.logger.debug(`[loop] User message received with no fallback model override, using current: ${currentFallbackModel || "none"}`);
584
+ }
585
+ let messageAppendSystemPrompt = currentAppendSystemPrompt;
586
+ if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
587
+ messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
588
+ currentAppendSystemPrompt = messageAppendSystemPrompt;
589
+ logger.logger.debug(`[loop] Append system prompt updated from user message: ${messageAppendSystemPrompt ? "set" : "reset to none"}`);
590
+ } else {
591
+ logger.logger.debug(`[loop] User message received with no append system prompt override, using current: ${currentAppendSystemPrompt ? "set" : "none"}`);
592
+ }
593
+ let messageAllowedTools = currentAllowedTools;
594
+ if (message.meta?.hasOwnProperty("allowedTools")) {
595
+ messageAllowedTools = message.meta.allowedTools || void 0;
596
+ currentAllowedTools = messageAllowedTools;
597
+ logger.logger.debug(`[loop] Allowed tools updated from user message: ${messageAllowedTools ? messageAllowedTools.join(", ") : "reset to none"}`);
598
+ } else {
599
+ logger.logger.debug(`[loop] User message received with no allowed tools override, using current: ${currentAllowedTools ? currentAllowedTools.join(", ") : "none"}`);
600
+ }
601
+ let messageDisallowedTools = currentDisallowedTools;
602
+ if (message.meta?.hasOwnProperty("disallowedTools")) {
603
+ messageDisallowedTools = withAssistantDenylist(message.meta.disallowedTools || void 0, isAssistantVariant);
604
+ currentDisallowedTools = messageDisallowedTools;
605
+ logger.logger.debug(`[loop] Disallowed tools updated from user message: ${messageDisallowedTools ? messageDisallowedTools.join(", ") : "reset to none"}`);
606
+ } else {
607
+ logger.logger.debug(`[loop] User message received with no disallowed tools override, using current: ${currentDisallowedTools ? currentDisallowedTools.join(", ") : "none"}`);
608
+ }
609
+ let messageEffort = currentEffort;
610
+ const VALID_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
611
+ if (message.meta?.hasOwnProperty("effort")) {
612
+ const incoming = message.meta.effort;
613
+ if (incoming === null || incoming === void 0) {
614
+ messageEffort = void 0;
615
+ currentEffort = void 0;
616
+ logger.logger.debug(`[loop] Effort reset to default`);
617
+ } else if (typeof incoming === "string" && VALID_EFFORTS.has(incoming)) {
618
+ messageEffort = incoming;
619
+ currentEffort = messageEffort;
620
+ logger.logger.debug(`[loop] Effort updated from user message: ${messageEffort}`);
621
+ } else {
622
+ logger.logger.debug(`[loop] Ignoring invalid effort from user message: ${String(incoming)}`);
623
+ }
624
+ } else {
625
+ logger.logger.debug(`[loop] User message received with no effort override, using current: ${currentEffort ?? "default"}`);
626
+ }
627
+ const specialCommand = parseSpecialCommand(message.content.text);
628
+ if (specialCommand.type === "compact") {
629
+ logger.logger.debug("[start] Detected /compact command");
630
+ const enhancedMode2 = {
631
+ permissionMode: messagePermissionMode || "default",
632
+ model: messageModel,
633
+ fallbackModel: messageFallbackModel,
634
+ customSystemPrompt: messageCustomSystemPrompt,
635
+ appendSystemPrompt: messageAppendSystemPrompt,
636
+ allowedTools: messageAllowedTools,
637
+ disallowedTools: messageDisallowedTools,
638
+ effort: messageEffort
639
+ };
640
+ messageQueue.pushIsolateAndClear(specialCommand.originalMessage || message.content.text, enhancedMode2, attachmentsForThisMessage, message.localKey);
641
+ logger.logger.debug("[start] /compact command pushed to queue:", {
642
+ ...contentLogMetadata(message.content.text),
643
+ attachmentCount: attachmentsForThisMessage?.length ?? 0,
644
+ hasMetadata: Boolean(message.meta)
645
+ });
646
+ return;
647
+ }
648
+ if (specialCommand.type === "clear") {
649
+ logger.logger.debug("[start] Detected /clear command");
650
+ const enhancedMode2 = {
651
+ permissionMode: messagePermissionMode || "default",
652
+ model: messageModel,
653
+ fallbackModel: messageFallbackModel,
654
+ customSystemPrompt: messageCustomSystemPrompt,
655
+ appendSystemPrompt: messageAppendSystemPrompt,
656
+ allowedTools: messageAllowedTools,
657
+ disallowedTools: messageDisallowedTools,
658
+ effort: messageEffort
659
+ };
660
+ messageQueue.pushIsolateAndClear(specialCommand.originalMessage || message.content.text, enhancedMode2, attachmentsForThisMessage, message.localKey);
661
+ logger.logger.debug("[start] /clear command pushed to queue:", {
662
+ ...contentLogMetadata(message.content.text),
663
+ attachmentCount: attachmentsForThisMessage?.length ?? 0,
664
+ hasMetadata: Boolean(message.meta)
665
+ });
666
+ return;
667
+ }
668
+ if (specialCommand.type === "mcp" || specialCommand.type === "skills") {
669
+ if (currentRunMode === "local") {
670
+ logger.logger.debug(`[start] /${specialCommand.type} in local mode \u2014 passing through to Claude Code`);
671
+ } else {
672
+ logger.logger.debug(`[start] Detected /${specialCommand.type} command in remote mode`);
673
+ const metadata2 = session.getMetadata();
674
+ let responseText;
675
+ if (specialCommand.type === "mcp") {
676
+ const servers = metadata2?.mcpServers;
677
+ if (servers && servers.length > 0) {
678
+ responseText = "**MCP Servers**\n\n" + servers.map((s) => `- **${s.name}** \u2014 ${s.status}`).join("\n");
679
+ } else {
680
+ responseText = "No MCP servers configured. Session may still be initializing \u2014 try again after sending a message.";
681
+ }
682
+ } else {
683
+ const skills = metadata2?.skills ?? metadata2?.slashCommands;
684
+ if (skills && skills.length > 0) {
685
+ responseText = "**Available Skills**\n\n" + skills.map((s) => `- /${s}`).join("\n");
686
+ } else {
687
+ responseText = "No skills available. Session may still be initializing \u2014 try again after sending a message.";
688
+ }
689
+ }
690
+ session.sendClaudeSessionMessage({
691
+ type: "assistant",
692
+ uuid: node_crypto.randomUUID(),
693
+ parentUuid: null,
694
+ isSidechain: false,
695
+ sessionId: session.sessionId || "unknown",
696
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
697
+ message: {
698
+ role: "assistant",
699
+ model: "system",
700
+ content: [{ type: "text", text: responseText }]
701
+ }
702
+ });
703
+ return;
704
+ }
705
+ }
706
+ const enhancedMode = {
707
+ permissionMode: messagePermissionMode || "default",
708
+ model: messageModel,
709
+ fallbackModel: messageFallbackModel,
710
+ customSystemPrompt: messageCustomSystemPrompt,
711
+ appendSystemPrompt: messageAppendSystemPrompt,
712
+ allowedTools: messageAllowedTools,
713
+ disallowedTools: messageDisallowedTools,
714
+ effort: messageEffort
715
+ };
716
+ if (message.meta?.delivery === "steer" && currentRunMode === "remote") {
717
+ const accepted = await currentSession?.trySteer({
718
+ message: message.content.text,
719
+ mode: enhancedMode,
720
+ attachments: attachmentsForThisMessage
721
+ }) === true;
722
+ if (accepted) {
723
+ logger.logger.debug("User message steered into current turn:", {
724
+ ...contentLogMetadata(message.content.text),
725
+ attachmentCount: attachmentsForThisMessage?.length ?? 0
726
+ });
727
+ return;
728
+ }
729
+ logger.logger.debug("[start] Steer arrived without a compatible live turn; falling back to queue");
730
+ }
731
+ messageQueue.push(message.content.text, enhancedMode, attachmentsForThisMessage, message.localKey);
732
+ logger.logger.debug("User message pushed to queue:", {
733
+ ...contentLogMetadata(message.content.text),
734
+ attachmentCount: attachmentsForThisMessage?.length ?? 0,
735
+ hasMetadata: Boolean(message.meta)
736
+ });
737
+ });
738
+ const exitGate = new SessionExitGate();
739
+ const cleanup = async (opts = { archive: true }) => {
740
+ const intent = exitGate.claim(exitIntentFromArchiveFlag(opts.archive));
741
+ if (intent === null) {
742
+ logger.logger.debug(`[START] Termination (archive=${opts.archive ?? true}) ignored: cleanup already running as ${exitGate.current}`);
743
+ return;
744
+ }
745
+ logger.logger.debug(`[START] Received termination signal, cleaning up (archive=${intent === "archive"})...`);
746
+ promptQueueDrain.close();
747
+ try {
748
+ if (session) {
749
+ currentSession?.cleanup();
750
+ await settleSessionOnExit(session.sessionId, intent, {
751
+ updateMetadata: (handler) => session.updateMetadata(handler),
752
+ sendSessionDeath: () => session.sendSessionDeath(),
753
+ archiveSession: (id) => api$1.archiveSession(id),
754
+ deactivateSession: (id) => api$1.deactivateSession(id),
755
+ log: (message, ...args) => logger.logger.debug(message, ...args)
756
+ });
757
+ await session.flush();
758
+ await session.close();
759
+ }
760
+ happyServer.stop();
761
+ hookServer.stop();
762
+ cleanupHookSettingsFile(hookSettingsPath);
763
+ await remoteScanner.cleanup();
764
+ logger.logger.debug("[START] Cleanup complete, exiting");
765
+ process.exit(0);
766
+ } catch (error) {
767
+ logger.logger.debug("[START] Error during cleanup:", error);
768
+ process.exit(1);
769
+ }
770
+ };
771
+ process.on("SIGTERM", () => {
772
+ void cleanup({ archive: false });
773
+ });
774
+ process.on("SIGINT", () => {
775
+ void cleanup({ archive: false });
776
+ });
777
+ process.on("uncaughtException", (error) => {
778
+ logger.logger.debug("[START] Uncaught exception:", error);
779
+ if (currentSession?.isAborting()) {
780
+ logger.logger.debug("[START] Uncaught exception during abort teardown \u2014 swallowing, not archiving");
781
+ return;
782
+ }
783
+ void cleanup({ archive: true });
784
+ });
785
+ process.on("unhandledRejection", (reason) => {
786
+ logger.logger.debug("[START] Unhandled rejection:", reason);
787
+ if (currentSession?.isAborting()) {
788
+ logger.logger.debug("[START] Unhandled rejection during abort teardown \u2014 swallowing, not archiving");
789
+ return;
790
+ }
791
+ void cleanup({ archive: true });
792
+ });
793
+ registerKillSessionHandler(session.rpcHandlerManager, () => cleanup({ archive: true }), session);
794
+ const exitCode = await loop({
795
+ path: workingDirectory,
796
+ model: options.model,
797
+ permissionMode: initialPermissionMode,
798
+ startingMode: options.startingMode,
799
+ messageQueue,
800
+ api: api$1,
801
+ allowedTools: happyServer.toolNames.map((toolName) => `mcp__happy__${toolName}`),
802
+ onModeChange: (newMode) => {
803
+ currentRunMode = newMode;
804
+ session.sendSessionEvent({ type: "switch", mode: newMode });
805
+ session.updateAgentState((currentState) => ({
806
+ ...currentState,
807
+ controlledByUser: newMode === "local"
808
+ }));
809
+ },
810
+ onSessionReady: (sessionInstance) => {
811
+ currentSession = sessionInstance;
812
+ },
813
+ // B-355: remember exactly what the SDK is about to write to the Claude
814
+ // transcript, so the JSONL scanner below recognises it as ours.
815
+ onPromptFinalized: recordAppPrompt,
816
+ onAbort: resetCurrentModeDefaults,
817
+ onPermissionModeChange: (mode) => {
818
+ lastExplicitModeSwitchAt = Date.now();
819
+ publishPermissionMode(mode);
820
+ logger.logger.debug(`[loop] Permission mode updated from session RPC / approval to: ${mode}`);
821
+ },
822
+ onEffectivePermissionMode: (mode) => publishEffectivePermissionMode(mode),
823
+ mcpServers: {
824
+ "happy": {
825
+ type: "http",
826
+ url: happyServer.url
827
+ }
828
+ },
829
+ session,
830
+ claudeEnvVars: options.claudeEnvVars,
831
+ claudeArgs: options.claudeArgs,
832
+ sandboxConfig,
833
+ hookSettingsPath,
834
+ jsRuntime: options.jsRuntime
835
+ });
836
+ currentSession?.cleanup();
837
+ session.sendSessionDeath();
838
+ logger.logger.debug("Waiting for socket to flush...");
839
+ await session.flush();
840
+ logger.logger.debug("Closing session...");
841
+ await session.close();
842
+ happyServer.stop();
843
+ logger.logger.debug("Stopped Happy MCP server");
844
+ hookServer.stop();
845
+ cleanupHookSettingsFile(hookSettingsPath);
846
+ logger.logger.debug("Stopped Hook server and cleaned up settings file");
847
+ process.exit(exitCode);
848
+ }
849
+
850
+ exports.runClaude = runClaude;