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,2086 @@
1
+ import { a as authAndSetupMachineIfNeeded } from './auth-Cwutmdsa.mjs';
2
+ import { e as createEnvelope, l as BUILTIN_TODO_DISCOVERY, P as PROMPT_QUEUE_CAPABILITY } from './index-qdS1friN.mjs';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { createId } from '@paralleldrive/cuid2';
5
+ import { r as registerAgentAttachmentDownloads } from './agentAttachments-M_B5qEF2.mjs';
6
+ import { C as CLAUDE_ATTACHMENT_KINDS, a as stageClaudeAttachments, b as appendStagedAttachmentsToPrompt } from './attachmentContent-_kxRznNf.mjs';
7
+ import { useStdout, useInput, Box, Text, render } from 'ink';
8
+ import { c as claimSessionOrExit, a as applyServerSnapshot, w as withoutServerSnapshot, m as mergeReconnectMetadata } from './sessionLock-D-EG2CfG.mjs';
9
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
10
+ import { c as connectionState, A as ApiClient } from './api-CLMUr-SX.mjs';
11
+ import { C as CodexAppServerClient } from './codexAppServerClient-I3teiV-N.mjs';
12
+ import { l as logger } from './logger-6HYUEXNk.mjs';
13
+ import { B as BasePermissionHandler } from './BasePermissionHandler-m3WVholL.mjs';
14
+ import { B as BaseReasoningProcessor } from './BaseReasoningProcessor-CYrKBXGm.mjs';
15
+ import { execSync } from 'node:child_process';
16
+ import { readSettings } from './persistence-HGW_ZVs9.mjs';
17
+ import { g as getInitialMachineMetadata } from './machineMetadata-oOzBfDfs.mjs';
18
+ import { c as configuration } from './configuration-RfeNjFmd.mjs';
19
+ import { M as MessageQueue2, r as registerKillSessionHandler } from './registerKillSessionHandler-GYvHdipX.mjs';
20
+ import { p as projectPath } from './projectPath-BTBIMB57.mjs';
21
+ import { join } from 'node:path';
22
+ import { c as createSessionMetadata } from './createSessionMetadata-CVVkGjxV.mjs';
23
+ import { h as hashObject, s as startHappyServer } from './deterministicJson-CzkFJWw1.mjs';
24
+ import { M as MessageBuffer } from './messageBuffer-BWlGgR3A.mjs';
25
+ import { n as notifyDaemonSessionStarted, g as reportSessionEditToDaemon } from './controlClient-BGbDcgWS.mjs';
26
+ import { d as decodeBase64, e as encodeBase64 } from './authRelay-DSAvg1uv.mjs';
27
+ import { s as setupOfflineReconnection } from './setupOfflineReconnection-DpvWTt69.mjs';
28
+ import { t as trimIdent } from './trimIdent-BAjmv_TG.mjs';
29
+ import { p as parseSpecialCommand } from './specialCommands-BSqhYg5X.mjs';
30
+ import { C as CHANGE_TITLE_INSTRUCTION } from './constants-BC4St0xd.mjs';
31
+ import { a as DEFAULT_CODEX_PERMISSION_MODE } from './defaultPermissionMode-BnxJPVHT.mjs';
32
+ import { E as EditReportThrottle, a as extractCodexPatchPaths } from './editPaths-BVfS_FFL.mjs';
33
+ import { a as extractNoSandboxFlag, e as ensureDaemonRunning } from './ensureDaemonRunning-Dqa2pOjY.mjs';
34
+ import 'tweetnacl';
35
+ import 'axios';
36
+ import './time-D0XyHOid.mjs';
37
+ import './browser-jh2P0UVZ.mjs';
38
+ import 'open';
39
+ import 'zod';
40
+ import 'node:fs/promises';
41
+ import './attachmentLimits-yba1uHFO.mjs';
42
+ import 'node:fs';
43
+ import 'node:events';
44
+ import 'socket.io-client';
45
+ import './limits-DIo7rEBR.mjs';
46
+ import './lidState-CeEt442o.mjs';
47
+ import 'os';
48
+ import 'child_process';
49
+ import './registerCommonHandlers-Ccvsz7Ou.mjs';
50
+ import 'util';
51
+ import 'fs/promises';
52
+ import 'crypto';
53
+ import 'path';
54
+ import 'cross-spawn';
55
+ import './sync-C8gyQFMc.mjs';
56
+ import './backgroundTaskActivity-8PdPxrvy.mjs';
57
+ import './contentLogMetadata-BlWI0egu.mjs';
58
+ import 'chalk';
59
+ import 'node:readline';
60
+ import './manager-BQ4flvQZ.mjs';
61
+ import '@anthropic-ai/sandbox-runtime';
62
+ import 'node:os';
63
+ import './package-BhaKlnxz.mjs';
64
+ import 'node:util';
65
+ import './detectCLI-CSGMlV51.mjs';
66
+ import 'fs';
67
+ import './localHappyAgentAuth-C-HQyiv6.mjs';
68
+ import './types-CkqAACXx.mjs';
69
+ import 'url';
70
+ import './client-CeGKqqcJ.mjs';
71
+ import './context-H0Bcb9S_.mjs';
72
+ import './sessionOps-SuHW8Dfe.mjs';
73
+ import './resources-BdFBQi8r.mjs';
74
+ import './tools-DkluDcG4.mjs';
75
+ import './tools-BZtHF_qQ.mjs';
76
+ import './client-ClR0K8zX.mjs';
77
+ import './peerTools-BTqw97Gb.mjs';
78
+ import './ids-DU6sLx4C.mjs';
79
+ import './remoteSessionClient-CYnXRynE.mjs';
80
+ import '@modelcontextprotocol/sdk/server/mcp.js';
81
+ import 'node:http';
82
+ import '@modelcontextprotocol/sdk/server/streamableHttp.js';
83
+ import './assistantTools-D6tuc6o4.mjs';
84
+ import './terminals-Dlg0x3nY.mjs';
85
+ import './terminalTitleSuggest-CeKsX_V6.mjs';
86
+ import './tmuxSocket-B8Yx20sT.mjs';
87
+ import './bootstrap-hxXPCFMx.mjs';
88
+ import './agentGuidance-CQuNlNx8.mjs';
89
+ import './previewPath-CXN4Vag0.mjs';
90
+ import './spawnHappyCLI-B2H5s4B_.mjs';
91
+ import './doctorReadiness-C208SMN9.mjs';
92
+
93
+ function getStartedSubagents(state) {
94
+ return state.startedSubagents ?? /* @__PURE__ */ new Set();
95
+ }
96
+ function getActiveSubagents(state) {
97
+ return state.activeSubagents ?? /* @__PURE__ */ new Set();
98
+ }
99
+ function getProviderSubagentToSessionSubagent(state) {
100
+ return state.providerSubagentToSessionSubagent ?? /* @__PURE__ */ new Map();
101
+ }
102
+ function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes) {
103
+ if (!subagent || startedSubagents.has(subagent)) {
104
+ return;
105
+ }
106
+ envelopes.push(createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
107
+ startedSubagents.add(subagent);
108
+ activeSubagents.add(subagent);
109
+ }
110
+ function emitSubagentStops(opts, startedSubagents, activeSubagents) {
111
+ const envelopes = [];
112
+ for (const subagent of activeSubagents) {
113
+ envelopes.push(createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
114
+ }
115
+ activeSubagents.clear();
116
+ startedSubagents.clear();
117
+ return envelopes;
118
+ }
119
+ function buildEnvelopeOptions(currentTurnId, subagent) {
120
+ return {
121
+ ...currentTurnId ? { turn: currentTurnId } : {},
122
+ ...subagent ? { subagent } : {}
123
+ };
124
+ }
125
+ function pickProviderSubagent(message) {
126
+ const candidates = [message.subagent, message.parent_call_id, message.parentCallId];
127
+ for (const candidate of candidates) {
128
+ if (typeof candidate === "string" && candidate.length > 0) {
129
+ return candidate;
130
+ }
131
+ }
132
+ return void 0;
133
+ }
134
+ function resolveSessionSubagent(message, providerSubagentToSessionSubagent) {
135
+ const providerSubagent = pickProviderSubagent(message);
136
+ if (!providerSubagent) {
137
+ return void 0;
138
+ }
139
+ const existing = providerSubagentToSessionSubagent.get(providerSubagent);
140
+ if (existing) {
141
+ return existing;
142
+ }
143
+ const created = createId();
144
+ providerSubagentToSessionSubagent.set(providerSubagent, created);
145
+ return created;
146
+ }
147
+ function pickCallId(message) {
148
+ const callId = message.call_id ?? message.callId;
149
+ if (typeof callId === "string" && callId.length > 0) {
150
+ return callId;
151
+ }
152
+ return randomUUID();
153
+ }
154
+ function summarizeCommand(command) {
155
+ if (typeof command === "string" && command.trim().length > 0) {
156
+ return command;
157
+ }
158
+ if (Array.isArray(command)) {
159
+ const cmd = command.map((v) => String(v)).join(" ").trim();
160
+ return cmd.length > 0 ? cmd : null;
161
+ }
162
+ return null;
163
+ }
164
+ function commandToTitle(command) {
165
+ if (!command) {
166
+ return "Run command";
167
+ }
168
+ const short = command.length > 80 ? `${command.slice(0, 77)}...` : command;
169
+ return `Run \`${short}\``;
170
+ }
171
+ function turnTimestampMs(turn) {
172
+ const seconds = turn.startedAt ?? turn.completedAt;
173
+ return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
174
+ }
175
+ function completedTimestampMs(turn) {
176
+ const seconds = turn.completedAt ?? turn.startedAt;
177
+ return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
178
+ }
179
+ function textFromInputItems(items) {
180
+ if (!Array.isArray(items)) {
181
+ return null;
182
+ }
183
+ const text = items.filter((item) => Boolean(item) && typeof item === "object" && item.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n").trim();
184
+ return text.length > 0 ? text : null;
185
+ }
186
+ function reasoningText(item) {
187
+ const summary = item.summary;
188
+ const content = item.content;
189
+ const parts = [
190
+ ...Array.isArray(summary) ? summary : [],
191
+ ...Array.isArray(content) ? content : []
192
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
193
+ const text = parts.join("\n").trim();
194
+ return text.length > 0 ? text : null;
195
+ }
196
+ function turnStatus(turn) {
197
+ const status = typeof turn.status === "string" ? turn.status : null;
198
+ if (status === "failed") {
199
+ return "failed";
200
+ }
201
+ if (status === "cancelled" || status === "canceled" || status === "aborted" || status === "interrupted") {
202
+ return "cancelled";
203
+ }
204
+ return "completed";
205
+ }
206
+ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
207
+ const time = turnTimestampMs(turn);
208
+ const opts = { turn: turn.id, time, codexItemId: item.id };
209
+ envelopes.push(createEnvelope("agent", {
210
+ t: "tool-call-start",
211
+ call: item.id,
212
+ name,
213
+ title,
214
+ description: title,
215
+ args
216
+ }, {
217
+ ...opts,
218
+ id: `${item.id}:start`
219
+ }));
220
+ if (output && output.trim().length > 0) {
221
+ envelopes.push(createEnvelope("agent", {
222
+ t: "text",
223
+ text: output,
224
+ thinking: true
225
+ }, {
226
+ ...opts,
227
+ id: `${item.id}:output`
228
+ }));
229
+ }
230
+ if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
231
+ envelopes.push(createEnvelope("agent", {
232
+ t: "tool-call-end",
233
+ call: item.id
234
+ }, {
235
+ ...opts,
236
+ id: `${item.id}:end`,
237
+ time: completedTimestampMs(turn)
238
+ }));
239
+ }
240
+ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
241
+ if (item.type === "subAgentActivity" && typeof item.agentThreadId === "string") {
242
+ item = { ...item, type: "collabAgentToolCall", tool: item.kind, status: "completed", receiverThreadIds: [item.agentThreadId] };
243
+ }
244
+ if (item.type !== "collabAgentToolCall" || typeof item.id !== "string" || typeof item.tool !== "string") return [];
245
+ const args = {
246
+ operation: item.tool,
247
+ ...typeof item.prompt === "string" ? { prompt: item.prompt } : {},
248
+ ...typeof item.model === "string" ? { model: item.model } : {},
249
+ receiverThreadIds: Array.isArray(item.receiverThreadIds) ? item.receiverThreadIds : [],
250
+ agentsStates: item.agentsStates ?? {}
251
+ };
252
+ const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
253
+ const envelopes = [createEnvelope("agent", {
254
+ t: "tool-call-start",
255
+ call: item.id,
256
+ name: "CodexCollaboration",
257
+ title: `Codex \xB7 ${item.tool}`,
258
+ description: `Codex \xB7 ${item.tool}`,
259
+ args
260
+ }, { ...opts, id: startId })];
261
+ if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
262
+ envelopes.push(createEnvelope("agent", {
263
+ t: "tool-call-end",
264
+ call: item.id,
265
+ result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
266
+ }, { ...opts, id: `${item.id}:end` }));
267
+ }
268
+ return envelopes;
269
+ }
270
+ function mapCodexThreadToSessionEnvelopes(thread) {
271
+ const envelopes = [];
272
+ for (const turn of thread.turns ?? []) {
273
+ const startedAt = turnTimestampMs(turn);
274
+ const completedAt = completedTimestampMs(turn);
275
+ envelopes.push(createEnvelope("agent", { t: "turn-start" }, {
276
+ id: `${turn.id}:start`,
277
+ turn: turn.id,
278
+ time: startedAt
279
+ }));
280
+ for (const item of turn.items ?? []) {
281
+ switch (item.type) {
282
+ case "subAgentActivity":
283
+ case "collabAgentToolCall": {
284
+ envelopes.push(...mapCodexCollaborationItem(item, { turn: turn.id, time: startedAt, codexItemId: item.id }));
285
+ break;
286
+ }
287
+ case "userMessage": {
288
+ const text = textFromInputItems(item.content);
289
+ if (text) {
290
+ envelopes.push(createEnvelope("user", { t: "text", text }, {
291
+ id: item.id,
292
+ time: startedAt,
293
+ codexItemId: item.id
294
+ }));
295
+ }
296
+ break;
297
+ }
298
+ case "agentMessage": {
299
+ const text = typeof item.text === "string" ? item.text.trim() : "";
300
+ if (text.length > 0) {
301
+ envelopes.push(createEnvelope("agent", { t: "text", text }, {
302
+ id: item.id,
303
+ turn: turn.id,
304
+ time: completedAt,
305
+ codexItemId: item.id
306
+ }));
307
+ }
308
+ break;
309
+ }
310
+ case "reasoning": {
311
+ const text = reasoningText(item);
312
+ if (text) {
313
+ envelopes.push(createEnvelope("agent", { t: "text", text, thinking: true }, {
314
+ id: item.id,
315
+ turn: turn.id,
316
+ time: startedAt,
317
+ codexItemId: item.id
318
+ }));
319
+ }
320
+ break;
321
+ }
322
+ case "commandExecution": {
323
+ const command = typeof item.command === "string" ? item.command : "";
324
+ emitHistoricalToolCall(
325
+ envelopes,
326
+ turn,
327
+ item,
328
+ "CodexBash",
329
+ commandToTitle(command),
330
+ { command, cwd: item.cwd },
331
+ typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : null
332
+ );
333
+ break;
334
+ }
335
+ case "fileChange": {
336
+ const title = "Apply patch";
337
+ emitHistoricalToolCall(
338
+ envelopes,
339
+ turn,
340
+ item,
341
+ "CodexPatch",
342
+ title,
343
+ { changes: item.changes, status: item.status },
344
+ null
345
+ );
346
+ break;
347
+ }
348
+ case "mcpToolCall": {
349
+ const title = `${item.server}.${item.tool}`;
350
+ const output = item.error !== void 0 && item.error !== null ? String(item.error) : item.result !== void 0 && item.result !== null ? String(item.result) : null;
351
+ emitHistoricalToolCall(
352
+ envelopes,
353
+ turn,
354
+ item,
355
+ "McpTool",
356
+ title,
357
+ {
358
+ server: item.server,
359
+ tool: item.tool,
360
+ arguments: item.arguments
361
+ },
362
+ output
363
+ );
364
+ break;
365
+ }
366
+ }
367
+ }
368
+ envelopes.push(createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
369
+ id: `${turn.id}:end`,
370
+ turn: turn.id,
371
+ time: completedAt
372
+ }));
373
+ }
374
+ return envelopes;
375
+ }
376
+ function patchDescription(changes) {
377
+ if (!changes || typeof changes !== "object") {
378
+ return "Applying patch";
379
+ }
380
+ const fileCount = Object.keys(changes).length;
381
+ if (fileCount === 1) {
382
+ return "Applying patch to 1 file";
383
+ }
384
+ return `Applying patch to ${fileCount} files`;
385
+ }
386
+ function pickTurnEndStatus(message, type) {
387
+ const rawStatus = message.status;
388
+ if (rawStatus === "completed" || rawStatus === "failed" || rawStatus === "cancelled") {
389
+ return rawStatus;
390
+ }
391
+ if (rawStatus === "canceled") {
392
+ return "cancelled";
393
+ }
394
+ if (type === "turn_aborted") {
395
+ const reason = message.reason;
396
+ const error = message.error;
397
+ if (typeof reason === "string" && /(fail|error)/i.test(reason) || typeof error === "string" && error.length > 0 || error !== void 0 && error !== null && typeof error === "object") {
398
+ return "failed";
399
+ }
400
+ return "cancelled";
401
+ }
402
+ if (message.error !== void 0 && message.error !== null) {
403
+ return "failed";
404
+ }
405
+ return "completed";
406
+ }
407
+ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
408
+ const type = message.type;
409
+ const startedSubagents = getStartedSubagents(state);
410
+ const activeSubagents = getActiveSubagents(state);
411
+ const providerSubagentToSessionSubagent = getProviderSubagentToSessionSubagent(state);
412
+ if (type === "collab_agent_item") {
413
+ return {
414
+ currentTurnId: state.currentTurnId,
415
+ startedSubagents,
416
+ activeSubagents,
417
+ providerSubagentToSessionSubagent,
418
+ envelopes: mapCodexCollaborationItem(message.item, buildEnvelopeOptions(state.currentTurnId), "live")
419
+ };
420
+ }
421
+ if (type === "task_started") {
422
+ const turnId = createId();
423
+ const turnStart = createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
424
+ startedSubagents.clear();
425
+ activeSubagents.clear();
426
+ providerSubagentToSessionSubagent.clear();
427
+ return {
428
+ currentTurnId: turnId,
429
+ startedSubagents,
430
+ activeSubagents,
431
+ providerSubagentToSessionSubagent,
432
+ envelopes: [turnStart]
433
+ };
434
+ }
435
+ if (type === "task_complete" || type === "turn_aborted") {
436
+ if (!state.currentTurnId) {
437
+ return {
438
+ currentTurnId: null,
439
+ startedSubagents,
440
+ activeSubagents,
441
+ providerSubagentToSessionSubagent,
442
+ envelopes: []
443
+ };
444
+ }
445
+ const lifecycleOpts = { turn: state.currentTurnId };
446
+ providerSubagentToSessionSubagent.clear();
447
+ return {
448
+ currentTurnId: null,
449
+ startedSubagents,
450
+ activeSubagents,
451
+ providerSubagentToSessionSubagent,
452
+ envelopes: [
453
+ ...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
454
+ createEnvelope("agent", {
455
+ t: "turn-end",
456
+ status: pickTurnEndStatus(message, type)
457
+ }, lifecycleOpts)
458
+ ]
459
+ };
460
+ }
461
+ if (type === "token_count") {
462
+ return {
463
+ currentTurnId: state.currentTurnId,
464
+ startedSubagents,
465
+ activeSubagents,
466
+ providerSubagentToSessionSubagent,
467
+ envelopes: []
468
+ };
469
+ }
470
+ const subagent = resolveSessionSubagent(message, providerSubagentToSessionSubagent);
471
+ const opts = buildEnvelopeOptions(state.currentTurnId, subagent);
472
+ if (type === "agent_message") {
473
+ if (typeof message.message !== "string") {
474
+ return {
475
+ currentTurnId: state.currentTurnId,
476
+ startedSubagents,
477
+ activeSubagents,
478
+ providerSubagentToSessionSubagent,
479
+ envelopes: []
480
+ };
481
+ }
482
+ const envelopes = [];
483
+ maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
484
+ envelopes.push(createEnvelope("agent", { t: "text", text: message.message }, opts));
485
+ return {
486
+ currentTurnId: state.currentTurnId,
487
+ startedSubagents,
488
+ activeSubagents,
489
+ providerSubagentToSessionSubagent,
490
+ envelopes
491
+ };
492
+ }
493
+ if (type === "agent_reasoning" || type === "agent_reasoning_delta") {
494
+ const text = typeof message.text === "string" ? message.text : typeof message.delta === "string" ? message.delta : null;
495
+ if (!text) {
496
+ return {
497
+ currentTurnId: state.currentTurnId,
498
+ startedSubagents,
499
+ activeSubagents,
500
+ providerSubagentToSessionSubagent,
501
+ envelopes: []
502
+ };
503
+ }
504
+ const envelopes = [];
505
+ maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
506
+ envelopes.push(createEnvelope("agent", { t: "text", text, thinking: true }, opts));
507
+ return {
508
+ currentTurnId: state.currentTurnId,
509
+ startedSubagents,
510
+ activeSubagents,
511
+ providerSubagentToSessionSubagent,
512
+ envelopes
513
+ };
514
+ }
515
+ if (type === "exec_command_begin") {
516
+ const call = pickCallId(message);
517
+ const { call_id: _callIdSnake, callId: _callIdCamel, type: _type, ...args } = message;
518
+ const command = summarizeCommand(args.command);
519
+ const description = typeof args.description === "string" ? args.description : command ?? "Execute command";
520
+ const envelopes = [];
521
+ maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
522
+ envelopes.push(
523
+ createEnvelope("agent", {
524
+ t: "tool-call-start",
525
+ call,
526
+ name: "CodexBash",
527
+ title: commandToTitle(command),
528
+ description,
529
+ args
530
+ }, opts)
531
+ );
532
+ return {
533
+ currentTurnId: state.currentTurnId,
534
+ startedSubagents,
535
+ activeSubagents,
536
+ providerSubagentToSessionSubagent,
537
+ envelopes
538
+ };
539
+ }
540
+ if (type === "exec_command_end") {
541
+ const call = pickCallId(message);
542
+ const envelopes = [];
543
+ maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
544
+ envelopes.push(createEnvelope("agent", { t: "tool-call-end", call }, opts));
545
+ return {
546
+ currentTurnId: state.currentTurnId,
547
+ startedSubagents,
548
+ activeSubagents,
549
+ providerSubagentToSessionSubagent,
550
+ envelopes
551
+ };
552
+ }
553
+ if (type === "patch_apply_begin") {
554
+ const call = pickCallId(message);
555
+ const autoApproved = message.auto_approved;
556
+ const changes = message.changes;
557
+ const envelopes = [];
558
+ maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
559
+ envelopes.push(
560
+ createEnvelope("agent", {
561
+ t: "tool-call-start",
562
+ call,
563
+ name: "CodexPatch",
564
+ title: "Apply patch",
565
+ description: patchDescription(changes),
566
+ args: {
567
+ auto_approved: autoApproved,
568
+ changes
569
+ }
570
+ }, opts)
571
+ );
572
+ return {
573
+ currentTurnId: state.currentTurnId,
574
+ startedSubagents,
575
+ activeSubagents,
576
+ providerSubagentToSessionSubagent,
577
+ envelopes
578
+ };
579
+ }
580
+ if (type === "patch_apply_end") {
581
+ const call = pickCallId(message);
582
+ const envelopes = [];
583
+ maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
584
+ envelopes.push(createEnvelope("agent", { t: "tool-call-end", call }, opts));
585
+ return {
586
+ currentTurnId: state.currentTurnId,
587
+ startedSubagents,
588
+ activeSubagents,
589
+ providerSubagentToSessionSubagent,
590
+ envelopes
591
+ };
592
+ }
593
+ return {
594
+ currentTurnId: state.currentTurnId,
595
+ startedSubagents,
596
+ activeSubagents,
597
+ providerSubagentToSessionSubagent,
598
+ envelopes: []
599
+ };
600
+ }
601
+ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
602
+ const toolLikeMessage = message;
603
+ const opts = buildEnvelopeOptions(state.currentTurnId);
604
+ if (message.type === "reasoning") {
605
+ return [createEnvelope("agent", {
606
+ t: "text",
607
+ text: message.message,
608
+ thinking: true
609
+ }, opts)];
610
+ }
611
+ if (message.type === "tool-call") {
612
+ const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
613
+ return [createEnvelope("agent", {
614
+ t: "tool-call-start",
615
+ call: toolLikeMessage.callId,
616
+ name: toolLikeMessage.name || "unknown",
617
+ title,
618
+ description: title,
619
+ args: toolLikeMessage.input && typeof toolLikeMessage.input === "object" ? toolLikeMessage.input : {}
620
+ }, opts)];
621
+ }
622
+ if (message.type === "tool-call-result") {
623
+ const envelopes = [];
624
+ const content = toolLikeMessage.output?.content;
625
+ if (typeof content === "string" && content.trim().length > 0) {
626
+ envelopes.push(createEnvelope("agent", {
627
+ t: "text",
628
+ text: content,
629
+ thinking: true
630
+ }, opts));
631
+ }
632
+ envelopes.push(createEnvelope("agent", {
633
+ t: "tool-call-end",
634
+ call: toolLikeMessage.callId
635
+ }, opts));
636
+ return envelopes;
637
+ }
638
+ return [];
639
+ }
640
+
641
+ async function readChildThread(client, childId) {
642
+ if (!client.threadId || typeof childId !== "string" || !childId) throw new Error("Invalid child thread");
643
+ const parentId = client.threadId;
644
+ const { thread: parent } = await client.readThread({ threadId: parentId, includeTurns: true });
645
+ const belongs = parent.id === parentId && parent.turns?.some((turn) => turn.items.some((item) => item.type === "collabAgentToolCall" && Array.isArray(item.receiverThreadIds) && item.receiverThreadIds.includes(childId) || item.type === "subAgentActivity" && item.agentThreadId === childId));
646
+ if (!belongs) throw new Error("Thread is not a child of this session");
647
+ const { thread } = await client.readThread({ threadId: childId, includeTurns: true });
648
+ if (thread.id !== childId) throw new Error("Child thread mismatch");
649
+ return { envelopes: mapCodexThreadToSessionEnvelopes(thread) };
650
+ }
651
+
652
+ class CodexPermissionHandler extends BasePermissionHandler {
653
+ // Exact tool names that should always be auto-approved. Include the bare
654
+ // form (used by Codex elicitation messages like `tool "change_title"`)
655
+ // and the MCP-qualified form for defense in depth.
656
+ static ALWAYS_AUTO_APPROVE_NAMES = /* @__PURE__ */ new Set([
657
+ "change_title",
658
+ "mcp__happy__change_title"
659
+ ]);
660
+ // Tool-call IDs that should auto-approve when they exactly match one of
661
+ // these values or start with `<name>-` (e.g. `change_title-1765385846663`).
662
+ // Substring matching was a bypass vector — any tool whose ID happened to
663
+ // contain `change_title` as a substring would be silently approved.
664
+ static ALWAYS_AUTO_APPROVE_ID_PREFIXES = [
665
+ "change_title"
666
+ ];
667
+ constructor(session) {
668
+ super(session);
669
+ }
670
+ getLogPrefix() {
671
+ return "[Codex]";
672
+ }
673
+ shouldAutoApprove(toolName, toolCallId) {
674
+ if (CodexPermissionHandler.ALWAYS_AUTO_APPROVE_NAMES.has(toolName)) {
675
+ return true;
676
+ }
677
+ for (const prefix of CodexPermissionHandler.ALWAYS_AUTO_APPROVE_ID_PREFIXES) {
678
+ if (toolCallId === prefix || toolCallId.startsWith(`${prefix}-`)) {
679
+ return true;
680
+ }
681
+ }
682
+ return false;
683
+ }
684
+ /**
685
+ * Handle a tool permission request
686
+ * @param toolCallId - The unique ID of the tool call
687
+ * @param toolName - The name of the tool being called
688
+ * @param input - The input parameters for the tool
689
+ * @returns Promise resolving to permission result
690
+ */
691
+ async handleToolCall(toolCallId, toolName, input) {
692
+ if (this.shouldAutoApprove(toolName, toolCallId)) {
693
+ logger.debug(`${this.getLogPrefix()} Auto-approving tool ${toolName} (${toolCallId})`);
694
+ this.session.updateAgentState((currentState) => ({
695
+ ...currentState,
696
+ completedRequests: {
697
+ ...currentState.completedRequests,
698
+ [toolCallId]: {
699
+ tool: toolName,
700
+ arguments: input,
701
+ createdAt: Date.now(),
702
+ completedAt: Date.now(),
703
+ status: "approved",
704
+ decision: "approved"
705
+ }
706
+ }
707
+ }));
708
+ return { decision: "approved" };
709
+ }
710
+ return new Promise((resolve, reject) => {
711
+ this.pendingRequests.set(toolCallId, {
712
+ resolve,
713
+ reject,
714
+ toolName,
715
+ input
716
+ });
717
+ this.addPendingRequestToState(toolCallId, toolName, input);
718
+ logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})`);
719
+ });
720
+ }
721
+ }
722
+
723
+ class ReasoningProcessor extends BaseReasoningProcessor {
724
+ getToolName() {
725
+ return "CodexReasoning";
726
+ }
727
+ getLogPrefix() {
728
+ return "[ReasoningProcessor]";
729
+ }
730
+ /**
731
+ * Process a reasoning delta and accumulate content.
732
+ */
733
+ processDelta(delta) {
734
+ this.processInput(delta);
735
+ }
736
+ /**
737
+ * Complete the reasoning section with final text.
738
+ */
739
+ complete(fullText) {
740
+ this.completeReasoning(fullText);
741
+ }
742
+ }
743
+
744
+ class DiffProcessor {
745
+ previousDiff = null;
746
+ onMessage = null;
747
+ constructor(onMessage) {
748
+ this.onMessage = onMessage || null;
749
+ }
750
+ /**
751
+ * Process a turn_diff message and check if the unified_diff has changed
752
+ */
753
+ processDiff(unifiedDiff) {
754
+ if (this.previousDiff !== unifiedDiff) {
755
+ logger.debug("[DiffProcessor] Unified diff changed, sending CodexDiff tool call");
756
+ const callId = randomUUID();
757
+ const toolCall = {
758
+ type: "tool-call",
759
+ name: "CodexDiff",
760
+ callId,
761
+ input: {
762
+ unified_diff: unifiedDiff
763
+ },
764
+ id: randomUUID()
765
+ };
766
+ this.onMessage?.(toolCall);
767
+ const toolResult = {
768
+ type: "tool-call-result",
769
+ callId,
770
+ output: {
771
+ status: "completed"
772
+ },
773
+ id: randomUUID()
774
+ };
775
+ this.onMessage?.(toolResult);
776
+ }
777
+ this.previousDiff = unifiedDiff;
778
+ logger.debug("[DiffProcessor] Updated stored diff");
779
+ }
780
+ /**
781
+ * Reset the processor state (called on task_complete or turn_aborted)
782
+ */
783
+ reset() {
784
+ logger.debug("[DiffProcessor] Resetting diff state");
785
+ this.previousDiff = null;
786
+ }
787
+ /**
788
+ * Set the message callback for sending messages directly
789
+ */
790
+ setMessageCallback(callback) {
791
+ this.onMessage = callback;
792
+ }
793
+ /**
794
+ * Get the current diff value
795
+ */
796
+ getCurrentDiff() {
797
+ return this.previousDiff;
798
+ }
799
+ }
800
+
801
+ const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
802
+ const [messages, setMessages] = useState([]);
803
+ const [confirmationMode, setConfirmationMode] = useState(false);
804
+ const [actionInProgress, setActionInProgress] = useState(false);
805
+ const confirmationTimeoutRef = useRef(null);
806
+ const { stdout } = useStdout();
807
+ const terminalWidth = stdout.columns || 80;
808
+ const terminalHeight = stdout.rows || 24;
809
+ useEffect(() => {
810
+ setMessages(messageBuffer.getMessages());
811
+ const unsubscribe = messageBuffer.onUpdate((newMessages) => {
812
+ setMessages(newMessages);
813
+ });
814
+ return () => {
815
+ unsubscribe();
816
+ if (confirmationTimeoutRef.current) {
817
+ clearTimeout(confirmationTimeoutRef.current);
818
+ }
819
+ };
820
+ }, [messageBuffer]);
821
+ const resetConfirmation = useCallback(() => {
822
+ setConfirmationMode(false);
823
+ if (confirmationTimeoutRef.current) {
824
+ clearTimeout(confirmationTimeoutRef.current);
825
+ confirmationTimeoutRef.current = null;
826
+ }
827
+ }, []);
828
+ const setConfirmationWithTimeout = useCallback(() => {
829
+ setConfirmationMode(true);
830
+ if (confirmationTimeoutRef.current) {
831
+ clearTimeout(confirmationTimeoutRef.current);
832
+ }
833
+ confirmationTimeoutRef.current = setTimeout(() => {
834
+ resetConfirmation();
835
+ }, 15e3);
836
+ }, [resetConfirmation]);
837
+ useInput(useCallback(async (input, key) => {
838
+ if (actionInProgress) return;
839
+ if (key.ctrl && input === "c") {
840
+ if (confirmationMode) {
841
+ resetConfirmation();
842
+ setActionInProgress(true);
843
+ await new Promise((resolve) => setTimeout(resolve, 100));
844
+ onExit?.();
845
+ } else {
846
+ setConfirmationWithTimeout();
847
+ }
848
+ return;
849
+ }
850
+ if (confirmationMode) {
851
+ resetConfirmation();
852
+ }
853
+ }, [confirmationMode, actionInProgress, onExit, setConfirmationWithTimeout, resetConfirmation]));
854
+ const getMessageColor = (type) => {
855
+ switch (type) {
856
+ case "user":
857
+ return "magenta";
858
+ case "assistant":
859
+ return "cyan";
860
+ case "system":
861
+ return "blue";
862
+ case "tool":
863
+ return "yellow";
864
+ case "result":
865
+ return "green";
866
+ case "status":
867
+ return "gray";
868
+ default:
869
+ return "white";
870
+ }
871
+ };
872
+ const formatMessage = (msg) => {
873
+ const lines = msg.content.split("\n");
874
+ const maxLineLength = terminalWidth - 10;
875
+ return lines.map((line) => {
876
+ if (line.length <= maxLineLength) return line;
877
+ const chunks = [];
878
+ for (let i = 0; i < line.length; i += maxLineLength) {
879
+ chunks.push(line.slice(i, i + maxLineLength));
880
+ }
881
+ return chunks.join("\n");
882
+ }).join("\n");
883
+ };
884
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: terminalWidth, height: terminalHeight }, /* @__PURE__ */ React.createElement(
885
+ Box,
886
+ {
887
+ flexDirection: "column",
888
+ width: terminalWidth,
889
+ height: terminalHeight - 4,
890
+ borderStyle: "round",
891
+ borderColor: "gray",
892
+ paddingX: 1,
893
+ overflow: "hidden"
894
+ },
895
+ /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", bold: true }, "\u{1F916} Codex Agent Messages"), /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "\u2500".repeat(Math.min(terminalWidth - 4, 60)))),
896
+ /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", height: terminalHeight - 10, overflow: "hidden" }, messages.length === 0 ? /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "Waiting for messages...") : (
897
+ // Show only the last messages that fit in the available space
898
+ messages.slice(-Math.max(1, terminalHeight - 10)).map((msg) => /* @__PURE__ */ React.createElement(Box, { key: msg.id, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: getMessageColor(msg.type), dimColor: true }, formatMessage(msg))))
899
+ ))
900
+ ), /* @__PURE__ */ React.createElement(
901
+ Box,
902
+ {
903
+ width: terminalWidth,
904
+ borderStyle: "round",
905
+ borderColor: actionInProgress ? "gray" : confirmationMode ? "red" : "green",
906
+ paddingX: 2,
907
+ justifyContent: "center",
908
+ alignItems: "center",
909
+ flexDirection: "column"
910
+ },
911
+ /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", alignItems: "center" }, actionInProgress ? /* @__PURE__ */ React.createElement(Text, { color: "gray", bold: true }, "Exiting agent...") : confirmationMode ? /* @__PURE__ */ React.createElement(Text, { color: "red", bold: true }, "\u26A0\uFE0F Press Ctrl-C again to exit the agent") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: "green", bold: true }, "\u{1F916} Codex Agent Running \u2022 Ctrl-C to exit")), process.env.DEBUG && logPath && /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "Debug logs: ", logPath))
912
+ ));
913
+ };
914
+
915
+ function resolveCodexExecutionPolicy(permissionMode, sandboxManagedByHappy) {
916
+ if (sandboxManagedByHappy) {
917
+ return {
918
+ approvalPolicy: "never",
919
+ sandbox: "danger-full-access"
920
+ };
921
+ }
922
+ const approvalPolicy = (() => {
923
+ switch (permissionMode) {
924
+ // Codex native modes
925
+ case "default":
926
+ return "untrusted";
927
+ // Ask for non-trusted commands
928
+ case "read-only":
929
+ return "never";
930
+ // Never ask, read-only enforced by sandbox
931
+ case "safe-yolo":
932
+ return "on-request";
933
+ // Workspace auto-run; request approval for escalation (0.153 removed on-failure)
934
+ case "yolo":
935
+ return "never";
936
+ // Full YOLO: never interrupt for approvals
937
+ // Defensive fallback for Claude-specific modes (backward compatibility)
938
+ case "bypassPermissions":
939
+ return "never";
940
+ // Full access: map to yolo behavior
941
+ case "acceptEdits":
942
+ return "on-request";
943
+ // Let model decide (closest to auto-approve edits)
944
+ case "plan":
945
+ return "untrusted";
946
+ // Conservative: ask for non-trusted
947
+ default:
948
+ return "untrusted";
949
+ }
950
+ })();
951
+ const sandbox = (() => {
952
+ switch (permissionMode) {
953
+ // Codex native modes
954
+ case "default":
955
+ return "workspace-write";
956
+ // Can write in workspace
957
+ case "read-only":
958
+ return "read-only";
959
+ // Read-only filesystem
960
+ case "safe-yolo":
961
+ return "workspace-write";
962
+ // Can write in workspace
963
+ case "yolo":
964
+ return "danger-full-access";
965
+ // Full system access
966
+ // Defensive fallback for Claude-specific modes
967
+ case "bypassPermissions":
968
+ return "danger-full-access";
969
+ // Full access: map to yolo
970
+ case "acceptEdits":
971
+ return "workspace-write";
972
+ // Can edit files in workspace
973
+ case "plan":
974
+ return "workspace-write";
975
+ // Can write for planning
976
+ default:
977
+ return "workspace-write";
978
+ }
979
+ })();
980
+ return { approvalPolicy, sandbox };
981
+ }
982
+
983
+ function unsupportedEffortReason(catalog, model, effort) {
984
+ if (!effort) return null;
985
+ const selected = catalog.find((entry) => model ? entry.model === model : entry.isDefault);
986
+ if (!selected) return null;
987
+ if (selected.supportedReasoningEfforts.some((option) => option.reasoningEffort === effort)) return null;
988
+ return `Reasoning effort "${effort}" is not supported by ${model ?? "the default model"}. Choose a supported level.`;
989
+ }
990
+
991
+ function readableCodexErrorText(text) {
992
+ const trimmed = text.trim();
993
+ if (!trimmed.startsWith("{")) return trimmed;
994
+ try {
995
+ const body = JSON.parse(trimmed);
996
+ const inner = typeof body.error === "object" && body.error ? body.error.message : void 0;
997
+ const message = typeof inner === "string" && inner.trim() ? inner : typeof body.error === "string" && body.error.trim() ? body.error : typeof body.message === "string" && body.message.trim() ? body.message : null;
998
+ return message ? message.trim() : trimmed;
999
+ } catch {
1000
+ return trimmed;
1001
+ }
1002
+ }
1003
+ function describeCodexFailure(msg) {
1004
+ const hasFailure = msg?.status === "failed" || msg?.error !== void 0 && msg?.error !== null;
1005
+ if (!hasFailure) return null;
1006
+ const err = msg.error;
1007
+ if (typeof err === "string" && err.length > 0) return readableCodexErrorText(err);
1008
+ if (err && typeof err === "object" && typeof err.message === "string" && err.message.length > 0) {
1009
+ return readableCodexErrorText(err.message);
1010
+ }
1011
+ return "Unknown error";
1012
+ }
1013
+ const TRANSPORT_LOSS = /^Codex process (exited|disconnected)\b|stdin not writable| timed out after \d+ms/;
1014
+ function describeThrownTurnError(error) {
1015
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
1016
+ if (!message.trim() || TRANSPORT_LOSS.test(message)) return null;
1017
+ return readableCodexErrorText(message);
1018
+ }
1019
+
1020
+ async function resumeExistingThread(opts) {
1021
+ try {
1022
+ const resumedThread = await opts.client.resumeThread({
1023
+ threadId: opts.threadId,
1024
+ cwd: opts.cwd,
1025
+ mcpServers: opts.mcpServers
1026
+ });
1027
+ opts.session.updateMetadata((currentMetadata) => ({
1028
+ ...currentMetadata,
1029
+ codexThreadId: resumedThread.threadId
1030
+ }));
1031
+ opts.messageBuffer.addMessage(`Resumed thread ${trimIdent(resumedThread.threadId)}`, "status");
1032
+ opts.session.sendSessionEvent({
1033
+ type: "message",
1034
+ message: `Resumed Codex thread ${resumedThread.threadId}`
1035
+ });
1036
+ return resumedThread;
1037
+ } catch (error) {
1038
+ const reason = error instanceof Error ? error.message : String(error);
1039
+ throw new Error(`Failed to resume Codex thread ${opts.threadId}: ${reason}`);
1040
+ }
1041
+ }
1042
+
1043
+ const IMPORT_CODEX_THREAD_ENV = "HAPPY_IMPORT_CODEX_THREAD_ID";
1044
+ async function importCodexThread(opts) {
1045
+ let forked;
1046
+ try {
1047
+ forked = await opts.client.forkThread({
1048
+ threadId: opts.threadId,
1049
+ cwd: opts.cwd,
1050
+ mcpServers: opts.mcpServers
1051
+ });
1052
+ } catch (error) {
1053
+ const reason = error instanceof Error ? error.message : String(error);
1054
+ throw new Error(`Failed to import Codex thread ${opts.threadId}: ${reason}`);
1055
+ }
1056
+ if (!forked.threadId || forked.threadId === opts.threadId) {
1057
+ throw new Error(`Failed to import Codex thread ${opts.threadId}: Codex did not create an independent copy`);
1058
+ }
1059
+ opts.session.updateMetadata((currentMetadata) => ({
1060
+ ...currentMetadata,
1061
+ codexThreadId: forked.threadId,
1062
+ importedFromCodexThreadId: opts.threadId
1063
+ }));
1064
+ let thread = forked.thread;
1065
+ if (!Array.isArray(thread?.turns)) {
1066
+ thread = (await opts.client.readThread({ threadId: forked.threadId, includeTurns: true })).thread;
1067
+ }
1068
+ const envelopes = mapCodexThreadToSessionEnvelopes(thread);
1069
+ for (const envelope of envelopes) {
1070
+ opts.session.sendSessionProtocolMessage(envelope);
1071
+ }
1072
+ opts.messageBuffer.addMessage(`Imported thread ${trimIdent(opts.threadId)} as ${trimIdent(forked.threadId)}`, "status");
1073
+ opts.session.sendSessionEvent({
1074
+ type: "message",
1075
+ message: `Imported Codex thread ${opts.threadId} (continuing as ${forked.threadId})`
1076
+ });
1077
+ return { threadId: forked.threadId, model: forked.model, replayed: envelopes.length };
1078
+ }
1079
+
1080
+ function emitReadyIfIdle({ pending, queueSize, shouldExit, sendReady, notify }) {
1081
+ if (shouldExit) {
1082
+ return false;
1083
+ }
1084
+ if (pending) {
1085
+ return false;
1086
+ }
1087
+ if (queueSize() > 0) {
1088
+ return false;
1089
+ }
1090
+ sendReady();
1091
+ notify?.();
1092
+ return true;
1093
+ }
1094
+
1095
+ const DEFAULT_CODEX_IDLE_RELEASE_MS = 5 * 60 * 1e3;
1096
+ const CODEX_IDLE_RELEASE_ENV = "HAPPY_CODEX_IDLE_RELEASE_MS";
1097
+ function resolveCodexIdleReleaseMs(raw) {
1098
+ if (raw === void 0 || raw.trim() === "") return DEFAULT_CODEX_IDLE_RELEASE_MS;
1099
+ const parsed = Number(raw);
1100
+ if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_CODEX_IDLE_RELEASE_MS;
1101
+ return Math.floor(parsed);
1102
+ }
1103
+ function shouldReleaseIdleCodex(state) {
1104
+ if (state.shouldExit) return false;
1105
+ if (state.turnActive) return false;
1106
+ if (state.abortInProgress) return false;
1107
+ if (state.queueSize > 0) return false;
1108
+ return true;
1109
+ }
1110
+ class CodexIdleReleaseTimer {
1111
+ constructor(opts) {
1112
+ this.opts = opts;
1113
+ }
1114
+ timer = null;
1115
+ releasing = null;
1116
+ get enabled() {
1117
+ return this.opts.delayMs > 0;
1118
+ }
1119
+ get armed() {
1120
+ return this.timer !== null;
1121
+ }
1122
+ arm() {
1123
+ this.cancel();
1124
+ if (!this.enabled) return;
1125
+ const timer = setTimeout(() => {
1126
+ if (this.timer !== timer) return;
1127
+ this.timer = null;
1128
+ void this.fire();
1129
+ }, this.opts.delayMs);
1130
+ timer.unref?.();
1131
+ this.timer = timer;
1132
+ }
1133
+ cancel() {
1134
+ if (this.timer) {
1135
+ clearTimeout(this.timer);
1136
+ this.timer = null;
1137
+ }
1138
+ }
1139
+ /** Resolves once an in-flight release (if any) has finished. Test hook. */
1140
+ async settle() {
1141
+ await this.releasing;
1142
+ }
1143
+ async fire() {
1144
+ if (!this.opts.isIdle()) return;
1145
+ const run = (async () => {
1146
+ try {
1147
+ await this.opts.release();
1148
+ } catch (error) {
1149
+ this.opts.onError?.(error);
1150
+ }
1151
+ })();
1152
+ this.releasing = run;
1153
+ try {
1154
+ await run;
1155
+ } finally {
1156
+ if (this.releasing === run) this.releasing = null;
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
+ function isCodexClearText(text) {
1162
+ return parseSpecialCommand(text).type === "clear";
1163
+ }
1164
+ function enqueueCodexUserText(opts) {
1165
+ if (isCodexClearText(opts.text)) {
1166
+ opts.queue.pushIsolateAndClear(opts.text, opts.mode, opts.attachments, opts.sourceId);
1167
+ return "clear";
1168
+ }
1169
+ opts.queue.push(opts.text, opts.mode, opts.attachments, opts.sourceId);
1170
+ return "queued";
1171
+ }
1172
+
1173
+ function hashCodexEnhancedMode(mode) {
1174
+ return hashObject({
1175
+ permissionMode: mode.permissionMode,
1176
+ model: mode.model,
1177
+ appendSystemPrompt: mode.appendSystemPrompt,
1178
+ effort: mode.effort
1179
+ });
1180
+ }
1181
+ function buildCodexTurnPrompt(opts) {
1182
+ const parts = [];
1183
+ if (opts.includeAppendSystemPrompt && opts.mode.appendSystemPrompt) {
1184
+ parts.push(opts.mode.appendSystemPrompt);
1185
+ }
1186
+ parts.push(opts.message);
1187
+ if (opts.includeTitleInstruction) {
1188
+ parts.push(CHANGE_TITLE_INSTRUCTION);
1189
+ parts.push(BUILTIN_TODO_DISCOVERY);
1190
+ }
1191
+ return parts.join("\n\n");
1192
+ }
1193
+
1194
+ function byteLength(value) {
1195
+ return Buffer.byteLength(value, "utf8");
1196
+ }
1197
+ function safeTypeToken(value) {
1198
+ if (typeof value !== "string") return typeof value;
1199
+ return /^[a-z][a-z0-9_.-]{0,63}$/i.test(value) ? value : "unknown";
1200
+ }
1201
+ function logValueMetadata(value) {
1202
+ return typeof value === "string" ? { valueType: "string", valueBytes: byteLength(value) } : { valueType: value === null ? "null" : typeof value };
1203
+ }
1204
+ function codexEventLogMetadata(value) {
1205
+ const event = value && typeof value === "object" ? value : {};
1206
+ let payloadBytes;
1207
+ try {
1208
+ const encoded = JSON.stringify(value);
1209
+ if (encoded !== void 0) payloadBytes = byteLength(encoded);
1210
+ } catch {
1211
+ }
1212
+ return {
1213
+ eventType: safeTypeToken(event.type),
1214
+ ...payloadBytes === void 0 ? {} : { payloadBytes },
1215
+ hasId: typeof event.id === "string" || typeof event.turn_id === "string" || typeof event.callId === "string"
1216
+ };
1217
+ }
1218
+ function safeCodexErrorMetadata(value) {
1219
+ if (!value || typeof value !== "object") return { errorType: typeof value };
1220
+ const error = value;
1221
+ const status = error.status ?? error.response?.status;
1222
+ const safeCode = typeof error.code === "string" && /^[a-z0-9_.-]{1,64}$/i.test(error.code) ? { code: error.code } : typeof error.code === "number" ? { code: error.code } : typeof error.code === "string" ? { codeBytes: byteLength(error.code) } : {};
1223
+ return {
1224
+ errorType: safeTypeToken(error.name ?? value.constructor?.name),
1225
+ ...safeCode,
1226
+ ...typeof status === "number" ? { status } : {}
1227
+ };
1228
+ }
1229
+
1230
+ const DEFAULT_CODEX_MODEL = "gpt-5.5";
1231
+ const DEFAULT_CODEX_EFFORT = "medium";
1232
+ async function runCodex(opts) {
1233
+ try {
1234
+ execSync("codex --version", { encoding: "utf8", stdio: "pipe", windowsHide: true });
1235
+ } catch {
1236
+ console.error("\n\x1B[1m\x1B[33mCodex CLI is not installed\x1B[0m\n");
1237
+ console.error("Please install Codex CLI using one of these methods:\n");
1238
+ console.error("\x1B[1mOption 1 - npm (recommended):\x1B[0m");
1239
+ console.error(" \x1B[36mnpm install -g @openai/codex\x1B[0m\n");
1240
+ console.error("\x1B[1mOption 2 - Homebrew (macOS):\x1B[0m");
1241
+ console.error(" \x1B[36mbrew install --cask codex\x1B[0m\n");
1242
+ console.error("Alternatively, use Claude Code:");
1243
+ console.error(" \x1B[36mvery-happy claude\x1B[0m\n");
1244
+ process.exit(1);
1245
+ }
1246
+ const sessionTag = randomUUID();
1247
+ connectionState.setBackend("Codex");
1248
+ const api = await ApiClient.create(opts.credentials);
1249
+ logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
1250
+ const settings = await readSettings();
1251
+ let machineId = settings?.machineId;
1252
+ const sandboxConfig = opts.noSandbox ? void 0 : settings?.sandboxConfig;
1253
+ if (!machineId) {
1254
+ 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`);
1255
+ process.exit(1);
1256
+ }
1257
+ logger.debug(`Using machineId: ${machineId}`);
1258
+ await api.getOrCreateMachine({
1259
+ machineId,
1260
+ metadata: getInitialMachineMetadata()
1261
+ });
1262
+ const initialPermissionMode = opts.permissionMode ?? DEFAULT_CODEX_PERMISSION_MODE;
1263
+ const forkedFromSessionId = process.env.HAPPY_FORKED_FROM_SESSION_ID;
1264
+ const forkedFromMessageId = process.env.HAPPY_FORKED_FROM_MESSAGE_ID;
1265
+ const { state, metadata } = createSessionMetadata({
1266
+ flavor: "codex",
1267
+ ...process.env.VH_TEAM_OPERATION_ID ? { teamOperationId: process.env.VH_TEAM_OPERATION_ID } : {},
1268
+ machineId,
1269
+ startedBy: opts.startedBy,
1270
+ sandbox: sandboxConfig,
1271
+ dangerouslySkipPermissions: initialPermissionMode === "yolo" || initialPermissionMode === "bypassPermissions",
1272
+ ...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
1273
+ ...forkedFromMessageId ? { forkedFromMessageId } : {},
1274
+ capabilities: [PROMPT_QUEUE_CAPABILITY]
1275
+ });
1276
+ metadata.attachmentKinds = [...CLAUDE_ATTACHMENT_KINDS];
1277
+ const importCodexThreadId = process.env[IMPORT_CODEX_THREAD_ENV];
1278
+ if (importCodexThreadId) {
1279
+ metadata.importedFromCodexThreadId = importCodexThreadId;
1280
+ const importTitle = process.env.HAPPY_IMPORT_TITLE;
1281
+ if (importTitle) metadata.summary = { text: importTitle, updatedAt: Date.now() };
1282
+ }
1283
+ const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
1284
+ const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
1285
+ const reconnectVariant = process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT;
1286
+ const reconnectSeq = process.env.HAPPY_RECONNECT_SEQ;
1287
+ const reconnectMetadataVersion = process.env.HAPPY_RECONNECT_METADATA_VERSION;
1288
+ const reconnectAgentStateVersion = process.env.HAPPY_RECONNECT_AGENT_STATE_VERSION;
1289
+ let response;
1290
+ if (reconnectSessionId && reconnectKeyBase64 && reconnectVariant) {
1291
+ logger.debug(`[START] Reconnecting to existing session ${reconnectSessionId}`);
1292
+ response = {
1293
+ id: reconnectSessionId,
1294
+ seq: parseInt(reconnectSeq || "0", 10),
1295
+ encryptionKey: decodeBase64(reconnectKeyBase64),
1296
+ encryptionVariant: reconnectVariant,
1297
+ metadata,
1298
+ metadataVersion: parseInt(reconnectMetadataVersion || "0", 10),
1299
+ agentState: state,
1300
+ agentStateVersion: parseInt(reconnectAgentStateVersion || "0", 10)
1301
+ };
1302
+ } else {
1303
+ response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
1304
+ }
1305
+ if (response) {
1306
+ await claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "codex" });
1307
+ }
1308
+ if (reconnectSessionId && response && !await api.reactivateSession(response.id)) {
1309
+ throw new Error(`Failed to reactivate archived session ${response.id}`);
1310
+ }
1311
+ let reconnectSeeded = false;
1312
+ if (reconnectSessionId && response) {
1313
+ const snapshot = await api.getSession(response.id, response.encryptionKey, response.encryptionVariant);
1314
+ if (snapshot.ok) {
1315
+ response = applyServerSnapshot(response, snapshot.session);
1316
+ reconnectSeeded = true;
1317
+ logger.debug(`[START] Reconnect seeded from server: seq=${snapshot.session.seq} metadataVersion=${snapshot.session.metadataVersion}`);
1318
+ } else {
1319
+ response = withoutServerSnapshot(response);
1320
+ logger.debug(`[START] Reconnect without server snapshot (${snapshot.reason}); skipping existing messages`);
1321
+ }
1322
+ }
1323
+ let session;
1324
+ let permissionHandler;
1325
+ let client;
1326
+ let reasoningProcessor;
1327
+ let abortInProgress = null;
1328
+ let turnActive = false;
1329
+ const { session: initialSession, reconnectionHandle } = setupOfflineReconnection({
1330
+ api,
1331
+ sessionTag,
1332
+ metadata,
1333
+ state,
1334
+ response,
1335
+ sessionClientOptions: reconnectSeeded && response ? { initialSeq: response.seq } : void 0,
1336
+ onSessionSwap: (newSession) => {
1337
+ session = newSession;
1338
+ if (permissionHandler) {
1339
+ permissionHandler.updateSession(newSession);
1340
+ }
1341
+ attachPromptQueueDrain(newSession);
1342
+ }
1343
+ });
1344
+ session = initialSession;
1345
+ if (reconnectSessionId) {
1346
+ session.suppressNextArchiveSignal();
1347
+ if (!reconnectSeeded) session.skipExistingMessages();
1348
+ void session.cancelUndeliveredQueuedInputs();
1349
+ session.updateMetadata((meta) => mergeReconnectMetadata(meta, metadata, Date.now()));
1350
+ }
1351
+ if (response) {
1352
+ try {
1353
+ logger.debug(`[START] Reporting session ${response.id} to daemon`);
1354
+ const result = await notifyDaemonSessionStarted(response.id, metadata, {
1355
+ encryptionKey: encodeBase64(response.encryptionKey),
1356
+ encryptionVariant: response.encryptionVariant,
1357
+ seq: response.seq,
1358
+ metadataVersion: response.metadataVersion,
1359
+ agentStateVersion: response.agentStateVersion
1360
+ });
1361
+ if (result.error) {
1362
+ logger.debug("[START] Failed to report to daemon (may not be running)", safeCodexErrorMetadata(result.error));
1363
+ } else {
1364
+ logger.debug(`[START] Reported session ${response.id} to daemon`);
1365
+ }
1366
+ } catch (error) {
1367
+ logger.debug("[START] Failed to report to daemon (may not be running)", safeCodexErrorMetadata(error));
1368
+ }
1369
+ }
1370
+ const messageQueue = new MessageQueue2(hashCodexEnhancedMode);
1371
+ function attachPromptQueueDrain(client2) {
1372
+ const drain = client2.attachPromptQueueDrain({
1373
+ isIdle: () => messageQueue.size() === 0 && messageQueue.isWaiting()
1374
+ });
1375
+ messageQueue.setOnWait(() => drain.onIdle());
1376
+ }
1377
+ attachPromptQueueDrain(session);
1378
+ messageQueue.setOnDiscard((entries) => {
1379
+ const keys = entries.map((entry) => entry.sourceId).filter((key) => typeof key === "string");
1380
+ if (keys.length > 0) session.sendQueueCancelReason(keys, entries[0].reason);
1381
+ });
1382
+ let currentPermissionMode = initialPermissionMode;
1383
+ let currentModel = DEFAULT_CODEX_MODEL;
1384
+ let currentEffort = DEFAULT_CODEX_EFFORT;
1385
+ let currentAppendSystemPrompt = void 0;
1386
+ const resetCurrentModeDefaults = () => {
1387
+ currentPermissionMode = DEFAULT_CODEX_PERMISSION_MODE;
1388
+ currentModel = DEFAULT_CODEX_MODEL;
1389
+ currentEffort = DEFAULT_CODEX_EFFORT;
1390
+ currentAppendSystemPrompt = void 0;
1391
+ logger.debug("[Codex] Reset current mode defaults after abort");
1392
+ };
1393
+ const VALID_REMOTE_PERMISSION_MODES = [
1394
+ "default",
1395
+ "read-only",
1396
+ "safe-yolo",
1397
+ "yolo"
1398
+ ];
1399
+ let discoveredModels = [];
1400
+ registerAgentAttachmentDownloads(session);
1401
+ let userMessageDelivery = Promise.resolve();
1402
+ session.onUserMessage((message) => {
1403
+ const downloads = session.drainAttachmentsForUserMessage();
1404
+ userMessageDelivery = userMessageDelivery.then(async () => {
1405
+ const attachments = await downloads;
1406
+ let messagePermissionMode = currentPermissionMode;
1407
+ if (message.meta?.permissionMode) {
1408
+ const incoming = message.meta.permissionMode;
1409
+ if (VALID_REMOTE_PERMISSION_MODES.includes(incoming)) {
1410
+ messagePermissionMode = incoming;
1411
+ currentPermissionMode = messagePermissionMode;
1412
+ logger.debug("[Codex] Valid permission mode override applied");
1413
+ } else {
1414
+ logger.debug("[Codex] Ignoring invalid permission mode override", logValueMetadata(message.meta.permissionMode));
1415
+ }
1416
+ } else {
1417
+ logger.debug("[Codex] User message received without a permission mode override");
1418
+ }
1419
+ let messageModel = currentModel;
1420
+ if (message.meta?.hasOwnProperty("model")) {
1421
+ messageModel = message.meta.model || void 0;
1422
+ currentModel = messageModel;
1423
+ logger.debug("[Codex] Model override updated", logValueMetadata(message.meta.model));
1424
+ } else {
1425
+ logger.debug("[Codex] User message received without a model override");
1426
+ }
1427
+ let messageEffort = currentEffort;
1428
+ if (message.meta?.hasOwnProperty("effort")) {
1429
+ const incoming = message.meta.effort;
1430
+ if (incoming === null || incoming === void 0) {
1431
+ messageEffort = void 0;
1432
+ currentEffort = void 0;
1433
+ logger.debug(`[Codex] Effort reset to default`);
1434
+ } else if (typeof incoming === "string") {
1435
+ messageEffort = incoming;
1436
+ currentEffort = messageEffort;
1437
+ logger.debug("[Codex] Valid effort override applied");
1438
+ } else {
1439
+ logger.debug("[Codex] Ignoring invalid effort override", logValueMetadata(incoming));
1440
+ }
1441
+ } else {
1442
+ logger.debug("[Codex] User message received without an effort override");
1443
+ }
1444
+ let messageAppendSystemPrompt = currentAppendSystemPrompt;
1445
+ if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
1446
+ messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
1447
+ currentAppendSystemPrompt = messageAppendSystemPrompt;
1448
+ logger.debug("[Codex] Append system prompt override updated", logValueMetadata(message.meta.appendSystemPrompt));
1449
+ } else {
1450
+ logger.debug("[Codex] User message received without an append system prompt override");
1451
+ }
1452
+ const enhancedMode = {
1453
+ permissionMode: messagePermissionMode || "default",
1454
+ model: messageModel,
1455
+ appendSystemPrompt: messageAppendSystemPrompt,
1456
+ effort: messageEffort
1457
+ };
1458
+ const enqueueResult = enqueueCodexUserText({
1459
+ text: message.content.text,
1460
+ mode: enhancedMode,
1461
+ queue: messageQueue,
1462
+ attachments,
1463
+ sourceId: message.localKey
1464
+ });
1465
+ if (enqueueResult === "clear") {
1466
+ logger.debug("[Codex] /clear command pushed to isolated queue");
1467
+ }
1468
+ }).catch((error) => {
1469
+ logger.warn("[Codex] Failed to prepare user attachments", safeCodexErrorMetadata(error));
1470
+ session.sendSessionEvent({ type: "message", message: "Could not prepare this message. Please send it again." });
1471
+ });
1472
+ });
1473
+ let thinking = false;
1474
+ let currentTurnId = null;
1475
+ let codexStartedSubagents = /* @__PURE__ */ new Set();
1476
+ let codexActiveSubagents = /* @__PURE__ */ new Set();
1477
+ let codexProviderSubagentToSessionSubagent = /* @__PURE__ */ new Map();
1478
+ session.keepAlive(thinking, "remote");
1479
+ const keepAliveInterval = setInterval(() => {
1480
+ session.keepAlive(thinking, "remote");
1481
+ }, 2e3);
1482
+ const sendReady = () => {
1483
+ session.sendSessionEvent({ type: "ready" });
1484
+ try {
1485
+ api.push().sendSessionNotification({
1486
+ kind: "done",
1487
+ metadata: session.getMetadata(),
1488
+ data: {
1489
+ sessionId: session.sessionId,
1490
+ type: "ready",
1491
+ provider: "codex"
1492
+ }
1493
+ });
1494
+ } catch (pushError) {
1495
+ logger.debug("[Codex] Failed to send ready push", safeCodexErrorMetadata(pushError));
1496
+ }
1497
+ };
1498
+ function logActiveHandles(tag) {
1499
+ if (!process.env.DEBUG) return;
1500
+ const anyProc = process;
1501
+ const handles = typeof anyProc._getActiveHandles === "function" ? anyProc._getActiveHandles() : [];
1502
+ const requests = typeof anyProc._getActiveRequests === "function" ? anyProc._getActiveRequests() : [];
1503
+ logger.debug(`[codex][handles] ${tag}: handles=${handles.length} requests=${requests.length}`);
1504
+ try {
1505
+ const kinds = handles.map((h) => h && h.constructor ? h.constructor.name : typeof h);
1506
+ logger.debug(`[codex][handles] kinds=${JSON.stringify(kinds)}`);
1507
+ } catch {
1508
+ }
1509
+ }
1510
+ let abortController = new AbortController();
1511
+ let shouldExit = false;
1512
+ async function handleAbort() {
1513
+ if (abortInProgress) {
1514
+ await abortInProgress;
1515
+ return;
1516
+ }
1517
+ logger.debug("[Codex] Abort requested - stopping current task");
1518
+ abortInProgress = (async () => {
1519
+ try {
1520
+ if (permissionHandler) {
1521
+ permissionHandler.abortAll();
1522
+ }
1523
+ if (client) {
1524
+ const abortResult = await client.abortTurnWithFallback({
1525
+ gracePeriodMs: 3e3,
1526
+ forceRestartOnTimeout: true
1527
+ });
1528
+ if (abortResult.forcedRestart) {
1529
+ logger.warn("[Codex] Forced app-server restart after interrupt timeout");
1530
+ session.sendSessionEvent({
1531
+ type: "message",
1532
+ message: abortResult.resumedThread ? "Force-stopped active task after interrupt timeout. Codex backend was restarted and the previous thread was resumed." : "Force-stopped active task after interrupt timeout. Codex backend was restarted, but the previous thread could not be resumed."
1533
+ });
1534
+ }
1535
+ }
1536
+ if (reasoningProcessor) {
1537
+ reasoningProcessor.abort();
1538
+ }
1539
+ logger.debug("[Codex] Abort completed - session remains active");
1540
+ } catch (error) {
1541
+ logger.debug("[Codex] Error during abort", safeCodexErrorMetadata(error));
1542
+ } finally {
1543
+ resetCurrentModeDefaults();
1544
+ abortController.abort();
1545
+ abortController = new AbortController();
1546
+ }
1547
+ })();
1548
+ await abortInProgress;
1549
+ abortInProgress = null;
1550
+ }
1551
+ const handleKillSession = async () => {
1552
+ logger.debug("[Codex] Kill session requested - terminating process");
1553
+ await handleAbort();
1554
+ logger.debug("[Codex] Abort completed, proceeding with termination");
1555
+ try {
1556
+ if (session) {
1557
+ session.updateMetadata((currentMetadata) => ({
1558
+ ...currentMetadata,
1559
+ lifecycleState: "archived",
1560
+ lifecycleStateSince: Date.now(),
1561
+ archivedBy: "cli",
1562
+ archiveReason: "User terminated"
1563
+ }));
1564
+ session.sendSessionDeath();
1565
+ await session.flush();
1566
+ await session.close();
1567
+ }
1568
+ try {
1569
+ await client.disconnect();
1570
+ } catch (e) {
1571
+ logger.debug("[Codex] Error disconnecting Codex during termination", safeCodexErrorMetadata(e));
1572
+ }
1573
+ happyServer.stop();
1574
+ logger.debug("[Codex] Session termination complete, exiting");
1575
+ process.exit(0);
1576
+ } catch (error) {
1577
+ logger.debug("[Codex] Error during session termination", safeCodexErrorMetadata(error));
1578
+ process.exit(1);
1579
+ }
1580
+ };
1581
+ session.rpcHandlerManager.registerHandler("codex-child-read", async (args) => {
1582
+ try {
1583
+ return await readChildThread(client, args?.threadId);
1584
+ } catch (error) {
1585
+ return { error: error instanceof Error ? error.message : "Child read failed" };
1586
+ }
1587
+ });
1588
+ session.rpcHandlerManager.registerHandler("abort", handleAbort);
1589
+ const disposeTermination = registerKillSessionHandler(session.rpcHandlerManager, handleKillSession, session, { processSignals: true });
1590
+ const messageBuffer = new MessageBuffer();
1591
+ const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
1592
+ let inkInstance = null;
1593
+ if (hasTTY) {
1594
+ console.clear();
1595
+ inkInstance = render(React.createElement(CodexDisplay, {
1596
+ messageBuffer,
1597
+ logPath: process.env.DEBUG ? logger.getLogPath() : void 0,
1598
+ onExit: async () => {
1599
+ logger.debug("[codex]: Exiting agent via Ctrl-C");
1600
+ shouldExit = true;
1601
+ await handleAbort();
1602
+ }
1603
+ }), {
1604
+ exitOnCtrlC: false,
1605
+ patchConsole: false
1606
+ });
1607
+ }
1608
+ if (hasTTY) {
1609
+ process.stdin.resume();
1610
+ if (process.stdin.isTTY) {
1611
+ process.stdin.setRawMode(true);
1612
+ }
1613
+ process.stdin.setEncoding("utf8");
1614
+ }
1615
+ client = new CodexAppServerClient(sandboxConfig);
1616
+ const idleRelease = new CodexIdleReleaseTimer({
1617
+ delayMs: resolveCodexIdleReleaseMs(process.env[CODEX_IDLE_RELEASE_ENV]),
1618
+ isIdle: () => shouldReleaseIdleCodex({
1619
+ turnActive,
1620
+ queueSize: messageQueue.size(),
1621
+ shouldExit,
1622
+ abortInProgress: abortInProgress !== null
1623
+ }),
1624
+ release: async () => {
1625
+ const released = await client.releaseIdleProcess();
1626
+ if (released) {
1627
+ logger.debug("[Codex] Released idle app-server; thread kept for resume on next message");
1628
+ }
1629
+ },
1630
+ onError: (error) => logger.debug("[Codex] Idle app-server release failed", safeCodexErrorMetadata(error))
1631
+ });
1632
+ permissionHandler = new CodexPermissionHandler(session);
1633
+ permissionHandler.reset("Previous CLI process exited before responding");
1634
+ reasoningProcessor = new ReasoningProcessor((message) => {
1635
+ const envelopes = mapCodexProcessorMessageToSessionEnvelopes(message, { currentTurnId });
1636
+ for (const envelope of envelopes) {
1637
+ session.sendSessionProtocolMessage(envelope);
1638
+ }
1639
+ });
1640
+ const diffProcessor = new DiffProcessor((message) => {
1641
+ const envelopes = mapCodexProcessorMessageToSessionEnvelopes(message, { currentTurnId });
1642
+ for (const envelope of envelopes) {
1643
+ session.sendSessionProtocolMessage(envelope);
1644
+ }
1645
+ });
1646
+ client.setApprovalHandler(async (params) => {
1647
+ const toolName = params.type === "exec" ? "CodexBash" : params.type === "patch" ? "CodexPatch" : params.toolName ?? "McpTool";
1648
+ const input = params.type === "exec" ? { command: params.command, cwd: params.cwd } : params.type === "patch" ? { changes: params.fileChanges } : params.input ?? {};
1649
+ try {
1650
+ const result = await permissionHandler.handleToolCall(params.callId, toolName, input);
1651
+ logger.debug("[Codex] Permission request resolved");
1652
+ return result.decision;
1653
+ } catch (error) {
1654
+ logger.debug("[Codex] Error handling permission", safeCodexErrorMetadata(error));
1655
+ return "denied";
1656
+ }
1657
+ });
1658
+ const editThrottle = new EditReportThrottle();
1659
+ client.setEventHandler((msg) => {
1660
+ if (msg.type === "token_count") {
1661
+ session.sendAgentUsageSnapshot("codex", msg);
1662
+ }
1663
+ logger.debug("[Codex] Event metadata", codexEventLogMetadata(msg));
1664
+ if (msg.type === "agent_message") {
1665
+ messageBuffer.addMessage(msg.message, "assistant");
1666
+ } else if (msg.type === "agent_reasoning_delta") ; else if (msg.type === "agent_reasoning") {
1667
+ messageBuffer.addMessage(`[Thinking] ${msg.text.substring(0, 100)}...`, "system");
1668
+ } else if (msg.type === "exec_command_begin") {
1669
+ messageBuffer.addMessage(`Executing: ${msg.command}`, "tool");
1670
+ } else if (msg.type === "exec_command_end") {
1671
+ const output = msg.output || msg.error || "Command completed";
1672
+ const truncatedOutput = output.substring(0, 200);
1673
+ messageBuffer.addMessage(
1674
+ `Result: ${truncatedOutput}${output.length > 200 ? "..." : ""}`,
1675
+ "result"
1676
+ );
1677
+ } else if (msg.type === "task_started") {
1678
+ messageBuffer.addMessage("Starting task...", "status");
1679
+ } else if (msg.type === "task_complete") {
1680
+ const failure = describeCodexFailure(msg);
1681
+ if (failure) {
1682
+ messageBuffer.addMessage(`Task failed: ${failure}`, "status");
1683
+ session.sendSessionEvent({ type: "message", message: `Codex error: ${failure}` });
1684
+ } else {
1685
+ messageBuffer.addMessage("Task completed", "status");
1686
+ }
1687
+ } else if (msg.type === "turn_aborted") {
1688
+ const failure = describeCodexFailure(msg);
1689
+ if (failure) {
1690
+ messageBuffer.addMessage(`Turn aborted: ${failure}`, "status");
1691
+ session.sendSessionEvent({ type: "message", message: `Codex error: ${failure}` });
1692
+ } else {
1693
+ messageBuffer.addMessage("Turn aborted", "status");
1694
+ }
1695
+ }
1696
+ if (msg.type === "task_started") {
1697
+ if (!thinking) {
1698
+ logger.debug("thinking started");
1699
+ thinking = true;
1700
+ session.keepAlive(thinking, "remote");
1701
+ }
1702
+ }
1703
+ if (msg.type === "task_complete" || msg.type === "turn_aborted") {
1704
+ if (thinking) {
1705
+ logger.debug("thinking completed");
1706
+ thinking = false;
1707
+ session.keepAlive(thinking, "remote");
1708
+ }
1709
+ diffProcessor.reset();
1710
+ }
1711
+ if (msg.type === "agent_reasoning_section_break") {
1712
+ reasoningProcessor.handleSectionBreak();
1713
+ }
1714
+ if (msg.type === "agent_reasoning_delta") {
1715
+ reasoningProcessor.processDelta(msg.delta);
1716
+ }
1717
+ if (msg.type === "agent_reasoning") {
1718
+ reasoningProcessor.complete(msg.text);
1719
+ }
1720
+ if (msg.type === "patch_apply_begin") {
1721
+ const { changes } = msg;
1722
+ for (const edit of editThrottle.take(extractCodexPatchPaths(changes))) {
1723
+ reportSessionEditToDaemon({ sessionId: session.sessionId, path: edit.path, tool: edit.tool, cwd: process.cwd() });
1724
+ }
1725
+ const changeCount = Object.keys(changes).length;
1726
+ const filesMsg = changeCount === 1 ? "1 file" : `${changeCount} files`;
1727
+ messageBuffer.addMessage(`Modifying ${filesMsg}...`, "tool");
1728
+ }
1729
+ if (msg.type === "patch_apply_end") {
1730
+ const { stdout, stderr, success } = msg;
1731
+ if (success) {
1732
+ const message = stdout || "Files modified successfully";
1733
+ messageBuffer.addMessage(message.substring(0, 200), "result");
1734
+ } else {
1735
+ const errorMsg = stderr || "Failed to modify files";
1736
+ messageBuffer.addMessage(`Error: ${errorMsg.substring(0, 200)}`, "result");
1737
+ }
1738
+ }
1739
+ if (msg.type === "turn_diff") {
1740
+ if (msg.unified_diff) {
1741
+ diffProcessor.processDiff(msg.unified_diff);
1742
+ }
1743
+ }
1744
+ if (msg.type !== "agent_reasoning_delta" && msg.type !== "agent_reasoning" && msg.type !== "agent_reasoning_section_break" && msg.type !== "turn_diff") {
1745
+ const mapped = mapCodexMcpMessageToSessionEnvelopes(msg, {
1746
+ currentTurnId,
1747
+ startedSubagents: codexStartedSubagents,
1748
+ activeSubagents: codexActiveSubagents,
1749
+ providerSubagentToSessionSubagent: codexProviderSubagentToSessionSubagent
1750
+ });
1751
+ currentTurnId = mapped.currentTurnId;
1752
+ codexStartedSubagents = mapped.startedSubagents;
1753
+ codexActiveSubagents = mapped.activeSubagents;
1754
+ codexProviderSubagentToSessionSubagent = mapped.providerSubagentToSessionSubagent;
1755
+ for (const envelope of mapped.envelopes) {
1756
+ session.sendSessionProtocolMessage(envelope);
1757
+ }
1758
+ }
1759
+ });
1760
+ const happyServer = await startHappyServer(session);
1761
+ const bridgeEntrypoint = join(projectPath(), "bin", "very-happy-mcp.mjs");
1762
+ const mcpServers = {
1763
+ happy: {
1764
+ command: process.execPath,
1765
+ args: ["--no-warnings", "--no-deprecation", bridgeEntrypoint, "--url", happyServer.url]
1766
+ }
1767
+ };
1768
+ let first = true;
1769
+ let appendSystemPromptInjected = false;
1770
+ process.env.VH_PEER_SESSION_ID = session.sessionId;
1771
+ try {
1772
+ logger.debug("[codex]: client.connect begin");
1773
+ await client.connect();
1774
+ logger.debug("[codex]: client.connect done");
1775
+ try {
1776
+ discoveredModels = await client.listModels();
1777
+ if (discoveredModels.length > 0) {
1778
+ session.updateMetadata((metadata2) => ({
1779
+ ...metadata2,
1780
+ models: discoveredModels.map((model) => ({
1781
+ code: model.model,
1782
+ value: model.displayName,
1783
+ description: model.description,
1784
+ reasoningEfforts: model.supportedReasoningEfforts.map((option) => option.reasoningEffort),
1785
+ defaultReasoningEffort: model.defaultReasoningEffort
1786
+ })),
1787
+ defaultModelCode: discoveredModels.find((model) => model.isDefault)?.model
1788
+ }));
1789
+ }
1790
+ } catch (error) {
1791
+ logger.debug("[Codex] Model discovery unavailable; using compatibility fallback", safeCodexErrorMetadata(error));
1792
+ }
1793
+ if (opts.resumeThreadId) {
1794
+ await resumeExistingThread({
1795
+ client,
1796
+ session,
1797
+ messageBuffer,
1798
+ threadId: opts.resumeThreadId,
1799
+ cwd: process.cwd(),
1800
+ mcpServers
1801
+ });
1802
+ first = false;
1803
+ appendSystemPromptInjected = true;
1804
+ }
1805
+ if (!reconnectSessionId && !opts.resumeThreadId && importCodexThreadId) {
1806
+ await importCodexThread({
1807
+ client,
1808
+ session,
1809
+ messageBuffer,
1810
+ threadId: importCodexThreadId,
1811
+ cwd: process.cwd(),
1812
+ mcpServers
1813
+ });
1814
+ first = false;
1815
+ appendSystemPromptInjected = true;
1816
+ }
1817
+ const forkCodexThreadId = process.env.HAPPY_FORK_CODEX_THREAD_ID;
1818
+ if (!reconnectSessionId && forkCodexThreadId) {
1819
+ try {
1820
+ const { thread } = await client.readThread({
1821
+ threadId: forkCodexThreadId,
1822
+ includeTurns: true
1823
+ });
1824
+ const envelopes = mapCodexThreadToSessionEnvelopes(thread);
1825
+ for (const envelope of envelopes) {
1826
+ session.sendSessionProtocolMessage(envelope);
1827
+ }
1828
+ session.updateMetadata((currentMetadata) => ({
1829
+ ...currentMetadata,
1830
+ codexThreadId: forkCodexThreadId
1831
+ }));
1832
+ logger.debug(`[CODEX FORK BACKFILL] Replayed ${envelopes.length} historical envelopes from thread ${forkCodexThreadId}`);
1833
+ } catch (error) {
1834
+ logger.debug(`[CODEX FORK BACKFILL] Failed to read thread ${forkCodexThreadId}`, safeCodexErrorMetadata(error));
1835
+ }
1836
+ }
1837
+ let pending = null;
1838
+ idleRelease.arm();
1839
+ while (!shouldExit) {
1840
+ logActiveHandles("loop-top");
1841
+ let message = pending;
1842
+ pending = null;
1843
+ if (!message) {
1844
+ const waitSignal = abortController.signal;
1845
+ const batch = await messageQueue.waitForMessagesAndGetAsString(waitSignal);
1846
+ if (!batch) {
1847
+ if (waitSignal.aborted && !shouldExit) {
1848
+ logger.debug("[codex]: Wait aborted while idle; ignoring and continuing");
1849
+ continue;
1850
+ }
1851
+ logger.debug(`[codex]: batch=${!!batch}, shouldExit=${shouldExit}`);
1852
+ break;
1853
+ }
1854
+ message = batch;
1855
+ }
1856
+ if (!message) {
1857
+ break;
1858
+ }
1859
+ idleRelease.cancel();
1860
+ turnActive = true;
1861
+ if (isCodexClearText(message.message)) {
1862
+ logger.debug("[Codex] Handling /clear command - resetting Codex thread state");
1863
+ client.clearThreadState();
1864
+ currentTurnId = null;
1865
+ codexStartedSubagents = /* @__PURE__ */ new Set();
1866
+ codexActiveSubagents = /* @__PURE__ */ new Set();
1867
+ codexProviderSubagentToSessionSubagent = /* @__PURE__ */ new Map();
1868
+ permissionHandler.reset();
1869
+ reasoningProcessor.abort();
1870
+ diffProcessor.reset();
1871
+ appendSystemPromptInjected = false;
1872
+ thinking = false;
1873
+ session.keepAlive(thinking, "remote");
1874
+ messageBuffer.addMessage("Context was reset", "status");
1875
+ session.sendSessionEvent({ type: "message", message: "Context was reset" });
1876
+ session.updateMetadata((currentMetadata) => {
1877
+ const nextMetadata = { ...currentMetadata };
1878
+ delete nextMetadata.codexThreadId;
1879
+ return nextMetadata;
1880
+ });
1881
+ emitReadyIfIdle({
1882
+ pending,
1883
+ // B-509: server-queued prompts count as pending input (no ready/push per item).
1884
+ queueSize: () => messageQueue.size() + (session.promptQueueHasPending() ? 1 : 0),
1885
+ shouldExit,
1886
+ sendReady
1887
+ });
1888
+ turnActive = false;
1889
+ idleRelease.arm();
1890
+ continue;
1891
+ }
1892
+ messageBuffer.addMessage(message.message, "user");
1893
+ try {
1894
+ await client.ensureConnected();
1895
+ const sandboxManagedByHappy = client.sandboxEnabled;
1896
+ const executionPolicy = resolveCodexExecutionPolicy(
1897
+ message.mode.permissionMode,
1898
+ sandboxManagedByHappy
1899
+ );
1900
+ const effortRefusal = unsupportedEffortReason(discoveredModels, message.mode.model, message.mode.effort);
1901
+ if (effortRefusal) {
1902
+ throw new Error(effortRefusal);
1903
+ }
1904
+ if (!client.hasActiveThread()) {
1905
+ const startedThread = await client.startThread({
1906
+ model: message.mode.model,
1907
+ cwd: process.cwd(),
1908
+ approvalPolicy: executionPolicy.approvalPolicy,
1909
+ sandbox: executionPolicy.sandbox,
1910
+ mcpServers
1911
+ });
1912
+ session.updateMetadata((currentMetadata) => ({
1913
+ ...currentMetadata,
1914
+ codexThreadId: startedThread.threadId
1915
+ }));
1916
+ }
1917
+ const includeAppendSystemPrompt = Boolean(
1918
+ message.mode.appendSystemPrompt && !appendSystemPromptInjected
1919
+ );
1920
+ const turnPrompt = buildCodexTurnPrompt({
1921
+ message: message.message,
1922
+ mode: message.mode,
1923
+ includeAppendSystemPrompt,
1924
+ includeTitleInstruction: first
1925
+ });
1926
+ const staged = message.attachments?.length ? await stageClaudeAttachments(message.attachments, {
1927
+ happyHomeDir: configuration.happyHomeDir,
1928
+ sessionId: session.sessionId
1929
+ }) : [];
1930
+ const result = await client.sendTurnAndWait(appendStagedAttachmentsToPrompt(turnPrompt, staged), {
1931
+ images: staged.filter((file) => ["image/jpeg", "image/png", "image/gif", "image/webp"].includes(file.mimeType)).map((file) => file.path),
1932
+ model: message.mode.model,
1933
+ approvalPolicy: executionPolicy.approvalPolicy,
1934
+ sandbox: executionPolicy.sandbox,
1935
+ effort: message.mode.effort
1936
+ });
1937
+ first = false;
1938
+ if (includeAppendSystemPrompt) {
1939
+ appendSystemPromptInjected = true;
1940
+ }
1941
+ if (result.aborted) {
1942
+ logger.debug("[Codex] Turn aborted");
1943
+ }
1944
+ } catch (error) {
1945
+ logger.warn("Error in codex session", safeCodexErrorMetadata(error));
1946
+ const reason = describeThrownTurnError(error);
1947
+ if (reason) {
1948
+ messageBuffer.addMessage(`Task failed: ${reason}`, "status");
1949
+ session.sendSessionEvent({ type: "message", message: `Codex error: ${reason}` });
1950
+ } else {
1951
+ messageBuffer.addMessage("Process exited unexpectedly", "status");
1952
+ session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
1953
+ }
1954
+ } finally {
1955
+ permissionHandler.reset();
1956
+ reasoningProcessor.abort();
1957
+ diffProcessor.reset();
1958
+ thinking = false;
1959
+ session.keepAlive(thinking, "remote");
1960
+ emitReadyIfIdle({
1961
+ pending,
1962
+ // B-509: server-queued prompts count as pending input (no ready/push per item).
1963
+ queueSize: () => messageQueue.size() + (session.promptQueueHasPending() ? 1 : 0),
1964
+ shouldExit,
1965
+ sendReady
1966
+ });
1967
+ turnActive = false;
1968
+ idleRelease.arm();
1969
+ logActiveHandles("after-turn");
1970
+ }
1971
+ }
1972
+ } finally {
1973
+ disposeTermination?.();
1974
+ session.closePromptQueueDrain();
1975
+ logger.debug("[codex]: Final cleanup start");
1976
+ logActiveHandles("cleanup-start");
1977
+ if (reconnectionHandle) {
1978
+ logger.debug("[codex]: Cancelling offline reconnection");
1979
+ reconnectionHandle.cancel();
1980
+ }
1981
+ try {
1982
+ logger.debug("[codex]: sendSessionDeath");
1983
+ session.sendSessionDeath();
1984
+ logger.debug("[codex]: flush begin");
1985
+ await session.flush();
1986
+ logger.debug("[codex]: flush done");
1987
+ logger.debug("[codex]: session.close begin");
1988
+ await session.close();
1989
+ logger.debug("[codex]: session.close done");
1990
+ } catch (e) {
1991
+ logger.debug("[codex]: Error while closing session", safeCodexErrorMetadata(e));
1992
+ }
1993
+ idleRelease.cancel();
1994
+ logger.debug("[codex]: client.disconnect begin");
1995
+ await client.disconnect();
1996
+ logger.debug("[codex]: client.disconnect done");
1997
+ logger.debug("[codex]: happyServer.stop");
1998
+ happyServer.stop();
1999
+ if (process.stdin.isTTY) {
2000
+ logger.debug("[codex]: setRawMode(false)");
2001
+ try {
2002
+ process.stdin.setRawMode(false);
2003
+ } catch {
2004
+ }
2005
+ }
2006
+ if (hasTTY) {
2007
+ logger.debug("[codex]: stdin.pause()");
2008
+ try {
2009
+ process.stdin.pause();
2010
+ } catch {
2011
+ }
2012
+ }
2013
+ logger.debug("[codex]: clearInterval(keepAlive)");
2014
+ clearInterval(keepAliveInterval);
2015
+ if (inkInstance) {
2016
+ logger.debug("[codex]: inkInstance.unmount()");
2017
+ inkInstance.unmount();
2018
+ }
2019
+ messageBuffer.clear();
2020
+ logActiveHandles("cleanup-end");
2021
+ logger.debug("[codex]: Final cleanup completed");
2022
+ }
2023
+ }
2024
+
2025
+ function extractCodexResumeFlag(args) {
2026
+ const remainingArgs = [];
2027
+ let resumeThreadId = null;
2028
+ for (let i = 0; i < args.length; i++) {
2029
+ const arg = args[i];
2030
+ if (arg === "--resume" || arg === "-r") {
2031
+ if (resumeThreadId !== null) {
2032
+ throw new Error("Codex resume flag can only be provided once.");
2033
+ }
2034
+ const nextArg = args[i + 1];
2035
+ if (!nextArg || nextArg.startsWith("-")) {
2036
+ throw new Error("Codex resume requires a thread ID: very-happy codex --resume <thread-id>");
2037
+ }
2038
+ resumeThreadId = nextArg;
2039
+ i++;
2040
+ continue;
2041
+ }
2042
+ if (arg.startsWith("--resume=")) {
2043
+ if (resumeThreadId !== null) {
2044
+ throw new Error("Codex resume flag can only be provided once.");
2045
+ }
2046
+ const value = arg.slice("--resume=".length).trim();
2047
+ if (!value) {
2048
+ throw new Error("Codex resume requires a thread ID: very-happy codex --resume <thread-id>");
2049
+ }
2050
+ resumeThreadId = value;
2051
+ continue;
2052
+ }
2053
+ remainingArgs.push(arg);
2054
+ }
2055
+ return {
2056
+ resumeThreadId,
2057
+ args: remainingArgs
2058
+ };
2059
+ }
2060
+
2061
+ async function handleCodexCommand(args) {
2062
+ let startedBy = void 0;
2063
+ let permissionMode = void 0;
2064
+ const sandboxArgs = extractNoSandboxFlag(args);
2065
+ const codexArgs = extractCodexResumeFlag(sandboxArgs.args);
2066
+ for (let i = 0; i < codexArgs.args.length; i++) {
2067
+ if (codexArgs.args[i] === "--started-by") {
2068
+ startedBy = codexArgs.args[++i];
2069
+ } else if (codexArgs.args[i] === "--permission-mode") {
2070
+ permissionMode = codexArgs.args[++i];
2071
+ } else if (codexArgs.args[i] === "--yolo") {
2072
+ permissionMode = "yolo";
2073
+ }
2074
+ }
2075
+ const { credentials } = await authAndSetupMachineIfNeeded();
2076
+ await ensureDaemonRunning();
2077
+ await runCodex({
2078
+ credentials,
2079
+ startedBy,
2080
+ noSandbox: sandboxArgs.noSandbox,
2081
+ resumeThreadId: codexArgs.resumeThreadId ?? void 0,
2082
+ permissionMode
2083
+ });
2084
+ }
2085
+
2086
+ export { handleCodexCommand };