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