very-happy-cli 0.2.173 → 0.2.175

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 (178) hide show
  1. package/dist/{AcpBackend-DG5AHZXF.mjs → AcpBackend-BZrLVcHb.mjs} +3 -3
  2. package/dist/{AcpBackend-ByLLiXvl.cjs → AcpBackend-Sb6N_HcB.cjs} +3 -3
  3. package/dist/{AcpSessionManager-Zf00QnlL.cjs → AcpSessionManager-C2MqOirs.cjs} +1 -1
  4. package/dist/{AcpSessionManager-B7FIrsys.mjs → AcpSessionManager-y1Iy1mzc.mjs} +1 -1
  5. package/dist/{BasePermissionHandler-kD4FK-7K.cjs → BasePermissionHandler-BbLYwd13.cjs} +1 -1
  6. package/dist/{BasePermissionHandler-CgPa--v2.mjs → BasePermissionHandler-DriZLITp.mjs} +1 -1
  7. package/dist/{BaseReasoningProcessor-Cnmvd83F.mjs → BaseReasoningProcessor-DnykPlPo.mjs} +1 -1
  8. package/dist/{BaseReasoningProcessor-CeMRo-8g.cjs → BaseReasoningProcessor-_S1PbSvH.cjs} +1 -1
  9. package/dist/{agentHome-DVLed_WJ.cjs → agentHome-BoYfpemd.cjs} +4 -4
  10. package/dist/{agentHome-b9H3fCBZ.mjs → agentHome-BtuRubHG.mjs} +4 -4
  11. package/dist/{api-DDz_YyoH.mjs → api-D08IEaWa.mjs} +10 -10
  12. package/dist/{api-BBoamFqd.cjs → api-KZmVqSKO.cjs} +10 -10
  13. package/dist/{apiMachine-DqQ8WeIe.mjs → apiMachine-Cx9Zn-78.mjs} +14 -14
  14. package/dist/{apiMachine-DRxPV0j2.cjs → apiMachine-Dct2GDAq.cjs} +14 -14
  15. package/dist/{attachmentContent-CmR86NxQ.cjs → attachmentContent-4L9dO8E3.cjs} +2 -2
  16. package/dist/{attachmentContent-DNsACM9b.mjs → attachmentContent-PrNJg8gG.mjs} +2 -2
  17. package/dist/{attachmentLimits-CxG3T18E.mjs → attachmentLimits-BLbji11b.mjs} +1 -1
  18. package/dist/{attachmentLimits-BEYEEBm8.cjs → attachmentLimits-D6bT2jU1.cjs} +1 -1
  19. package/dist/{auth-3UXAs__6.cjs → auth-BGmM3Vlm.cjs} +5 -5
  20. package/dist/{auth-BF8QvXR1.cjs → auth-BI0CsMcy.cjs} +8 -8
  21. package/dist/{auth-DvB0u-4H.mjs → auth-D1qHU-i_.mjs} +5 -5
  22. package/dist/{auth-BVqHWH_r.mjs → auth-PY_HGlzz.mjs} +8 -8
  23. package/dist/{auto-D0Ai-s17.mjs → auto-COMphIfD.mjs} +15 -15
  24. package/dist/{auto-D3PSMTni.cjs → auto-DM4_FeKb.cjs} +15 -15
  25. package/dist/{bootstrap-Cg_t3W-0.mjs → bootstrap-CWjpC5-J.mjs} +2 -2
  26. package/dist/{bootstrap-BsXIVhrG.cjs → bootstrap-DTJ6-vYT.cjs} +2 -2
  27. package/dist/{browser-Cbyo0qny.cjs → browser-DmgVoopB.cjs} +1 -1
  28. package/dist/{browser-f6xCPkkN.mjs → browser-xuW4fiHo.mjs} +1 -1
  29. package/dist/{claudeLocal-DqJUowRE.mjs → claudeLocal-B0P-wVJJ.mjs} +5 -5
  30. package/dist/{claudeLocal-AByQMi4D.cjs → claudeLocal-CtZVz3ee.cjs} +5 -5
  31. package/dist/{claudeLocalLauncher-BFxrneBS.mjs → claudeLocalLauncher-D4vmjI3c.mjs} +10 -10
  32. package/dist/{claudeLocalLauncher-CSnWv8cG.cjs → claudeLocalLauncher-Dnv4CH8r.cjs} +10 -10
  33. package/dist/{claudeSessionFork-DsmA1Jv6.mjs → claudeSessionFork-CxjOBoBh.mjs} +1 -1
  34. package/dist/{claudeSessionFork-DEuvbqD3.cjs → claudeSessionFork-DQk5-dzR.cjs} +1 -1
  35. package/dist/{client-CuadUoga.cjs → client-BHZVyQwi.cjs} +3 -3
  36. package/dist/{client-W3gPBBN2.cjs → client-BhLhjj_x.cjs} +2 -2
  37. package/dist/{client-CzNWPOBI.mjs → client-C4ddTzrD.mjs} +2 -2
  38. package/dist/{client-mnqGeUey.mjs → client-eQjOvC55.mjs} +3 -3
  39. package/dist/codex/happyMcpStdioBridge.cjs +15 -15
  40. package/dist/codex/happyMcpStdioBridge.mjs +15 -15
  41. package/dist/{codexAppServerClient-BCAPEmbn.mjs → codexAppServerClient-CppPl_R6.mjs} +205 -28
  42. package/dist/{codexAppServerClient-BgNcgkVT.cjs → codexAppServerClient-D-9z-lWP.cjs} +205 -28
  43. package/dist/{codexCommand-B-xQ7uoD.cjs → codexCommand-AgVJYnFU.cjs} +87 -45
  44. package/dist/{codexCommand-3ZsmFNCE.mjs → codexCommand-BdrBNbZu.mjs} +87 -45
  45. package/dist/{config-q8FBDbqS.mjs → config-Cr3VUzbE.mjs} +3 -3
  46. package/dist/{config-CJTzYT71.cjs → config-D89sE3wK.cjs} +3 -3
  47. package/dist/{configuration-2Eck1oZf.mjs → configuration-BNGPXg07.mjs} +1 -1
  48. package/dist/{configuration-C_-2io9T.cjs → configuration-UV4EE5ui.cjs} +1 -1
  49. package/dist/{connect-ch6-M_fR.cjs → connect-B35YXbdZ.cjs} +13 -13
  50. package/dist/{connect-CJc2q7Zb.mjs → connect-C0iLkCiY.mjs} +13 -13
  51. package/dist/{context--xjJuO00.cjs → context-BmH2BKip.cjs} +1 -1
  52. package/dist/{context-C1tA72Pn.mjs → context-Ptdnlcv6.mjs} +1 -1
  53. package/dist/{controlClient-DCH7YKU2.cjs → controlClient-CtsQMw--.cjs} +3 -3
  54. package/dist/{controlClient-Dn0x8DWU.mjs → controlClient-DqtC0pq1.mjs} +3 -3
  55. package/dist/{createSessionMetadata-BeY_Ct51.cjs → createSessionMetadata-2IuH5nlA.cjs} +2 -2
  56. package/dist/{createSessionMetadata-IMDmdFLj.mjs → createSessionMetadata-CtNMvfth.mjs} +2 -2
  57. package/dist/{defaultPermissionMode-CP-RNe7S.cjs → defaultPermissionMode-Bn4HcU8S.cjs} +2 -2
  58. package/dist/{defaultPermissionMode-C7be9Tg9.mjs → defaultPermissionMode-DYihjV2g.mjs} +2 -2
  59. package/dist/{deterministicJson-Cun6TLIF.mjs → deterministicJson-DF4xv6MH.mjs} +9 -9
  60. package/dist/{deterministicJson-3OOoCWI0.cjs → deterministicJson-je21npNx.cjs} +9 -9
  61. package/dist/{doctor-DE9dpmhV.cjs → doctor-BXKcYVGU.cjs} +7 -7
  62. package/dist/{doctor-B3krtkx6.mjs → doctor-Ba92L7-b.mjs} +7 -7
  63. package/dist/{downloadTarget-BBAA6-Wy.mjs → downloadTarget-CUJg8iJV.mjs} +6 -6
  64. package/dist/{downloadTarget-nKU9KvK-.cjs → downloadTarget-JC7FZl7W.cjs} +6 -6
  65. package/dist/{ensureDaemonRunning-bshCT05q.mjs → ensureDaemonRunning-JPA9tHNE.mjs} +5 -5
  66. package/dist/{ensureDaemonRunning-D3F9Wn4o.cjs → ensureDaemonRunning-bIp6-vgi.cjs} +5 -5
  67. package/dist/{handleResumeCommand-DSYAc7pz.cjs → handleResumeCommand-B6b9kIYf.cjs} +5 -5
  68. package/dist/{handleResumeCommand-DqQ2rB_w.mjs → handleResumeCommand-B6lE5DtU.mjs} +5 -5
  69. package/dist/{index-BzM5uj8R.cjs → index-B1ABQRrB.cjs} +4 -4
  70. package/dist/{index-DzqTd9GX.cjs → index-B2KL0aPc.cjs} +36 -36
  71. package/dist/{index-C9hI8a10.cjs → index-BZdhwBKa.cjs} +3 -1
  72. package/dist/{index-C7IYgnoC.mjs → index-CZLwo1FS.mjs} +3 -3
  73. package/dist/{index-CLTdBqtl.mjs → index-CjE4kBKx.mjs} +37 -37
  74. package/dist/{index-I6GVU3_G.cjs → index-D759ViMU.cjs} +3 -3
  75. package/dist/{index-DtDYoGpm.mjs → index-DW1ONaGc.mjs} +4 -4
  76. package/dist/{index-B0HEEYGh.mjs → index-boE-d8CM.mjs} +3 -1
  77. package/dist/index.cjs +2 -2
  78. package/dist/index.mjs +2 -2
  79. package/dist/{install-BFd6bwk2.mjs → install-CmojqnLK.mjs} +3 -3
  80. package/dist/{install-D8XM9y0N.cjs → install-KjIsK_EA.cjs} +3 -3
  81. package/dist/lib.cjs +12 -12
  82. package/dist/lib.mjs +12 -12
  83. package/dist/{lidState-hKB4aKcf.cjs → lidState-BmJp2pLt.cjs} +1 -1
  84. package/dist/{lidState-B1KB4bLv.mjs → lidState-CL1NdKfQ.mjs} +1 -1
  85. package/dist/{localHappyAgentAuth-B_ItamJZ.cjs → localHappyAgentAuth-9SP7hdiw.cjs} +1 -1
  86. package/dist/{localHappyAgentAuth-B94JIex_.mjs → localHappyAgentAuth-B7f4Ysn0.mjs} +1 -1
  87. package/dist/{logger-DYejzaY9.cjs → logger-C7dRaCkE.cjs} +2 -2
  88. package/dist/{logger-CKCRe63e.mjs → logger-D5EzMy30.mjs} +2 -2
  89. package/dist/{machineMetadata-C7Slozib.cjs → machineMetadata-DYYuK_0c.cjs} +3 -3
  90. package/dist/{machineMetadata-CiemL6uU.mjs → machineMetadata-DqplCWue.mjs} +3 -3
  91. package/dist/{main-BQBSqrW3.mjs → main-CnV62agu.mjs} +44 -44
  92. package/dist/{main-CJCss2TE.cjs → main-kgCQgknK.cjs} +44 -44
  93. package/dist/{mcp-cq0hhROT.cjs → mcp-BV0vxHtz.cjs} +14 -14
  94. package/dist/{mcp-BRtLFMHZ.mjs → mcp-bDPaj-tM.mjs} +14 -14
  95. package/dist/{mirrorProtocol-pDuMxeQj.cjs → mirrorProtocol-BSHVZ8Nf.cjs} +3 -3
  96. package/dist/{mirrorProtocol-hPlbcL2P.mjs → mirrorProtocol-ClKhNXYe.mjs} +3 -3
  97. package/dist/{package-JFU2Ry9r.mjs → package-D0dxZ-W8.mjs} +7 -7
  98. package/dist/{package-DwxuhtoO.cjs → package-VqWv7EI5.cjs} +7 -7
  99. package/dist/{peerTools-D-yf7SlL.mjs → peerTools-B_eiNB3P.mjs} +4 -4
  100. package/dist/{peerTools-DiBZOaPT.cjs → peerTools-BktGmlpd.cjs} +4 -4
  101. package/dist/{persistence-CG158J29.mjs → persistence-CIIhP0Uw.mjs} +2 -2
  102. package/dist/{persistence-Bi1BTtHq.cjs → persistence-DweHi8xa.cjs} +2 -2
  103. package/dist/{piRunnerArgs-DkCs_Ma1.cjs → piRunnerArgs-BNzWTNRV.cjs} +1 -1
  104. package/dist/{piRunnerArgs-C-8jIqUE.mjs → piRunnerArgs-cQafsadv.mjs} +1 -1
  105. package/dist/{piRuntime-C4Ei1y-P.mjs → piRuntime-Bel5lOLb.mjs} +1 -1
  106. package/dist/{piRuntime-CHsld2lz.cjs → piRuntime-VZLSwW-4.cjs} +1 -1
  107. package/dist/{piTerminal-yp98lIpx.mjs → piTerminal-CS5SDi_H.mjs} +16 -16
  108. package/dist/{piTerminal-D4Hy_mmZ.cjs → piTerminal-DkR8sPv-.cjs} +16 -16
  109. package/dist/{rateLimitWindows-C36y0Ldw.mjs → rateLimitWindows-Bo6XVnie.mjs} +1 -1
  110. package/dist/{rateLimitWindows-Blhk1RyR.cjs → rateLimitWindows-Bp4T21RI.cjs} +1 -1
  111. package/dist/{registerCommonHandlers-Ct5XkkNU.cjs → registerCommonHandlers-B7QZxkCL.cjs} +2 -2
  112. package/dist/{registerCommonHandlers-ni8jeOJc.mjs → registerCommonHandlers-CR5jpiSx.mjs} +2 -2
  113. package/dist/{registerKillSessionHandler-DoR8z8b6.cjs → registerKillSessionHandler-CcX84g-T.cjs} +6 -6
  114. package/dist/{registerKillSessionHandler-B0LoAR3_.mjs → registerKillSessionHandler-DLiu24CQ.mjs} +6 -6
  115. package/dist/{remoteSessionClient-B7YBUGMI.cjs → remoteSessionClient-CL3akj0K.cjs} +5 -5
  116. package/dist/{remoteSessionClient-B5vn4Ghn.mjs → remoteSessionClient-CUjw1COc.mjs} +5 -5
  117. package/dist/{reportProgressTool-25DIibtH.mjs → reportProgressTool-BLLwMQrN.mjs} +2 -2
  118. package/dist/{reportProgressTool-BVTrYVsY.cjs → reportProgressTool-DzMi4mvH.cjs} +2 -2
  119. package/dist/{run-Buxf1ATU.cjs → run-5pmOElaW.cjs} +34 -34
  120. package/dist/{run-D4qgzSft.mjs → run-DGokR_6q.mjs} +33 -33
  121. package/dist/{runClaude-CRkWhQ5T.mjs → runClaude-BFlpZQds.mjs} +21 -21
  122. package/dist/{runClaude-D4B0SSd5.cjs → runClaude-EnYNvkV0.cjs} +21 -21
  123. package/dist/{runClaudeDeps-BKYHcQkO.cjs → runClaudeDeps-CUDebE9e.cjs} +35 -35
  124. package/dist/{runClaudeDeps-CbyQIbNw.mjs → runClaudeDeps-D1M1dzlZ.mjs} +40 -40
  125. package/dist/{runGemini-OgmDK1Lq.cjs → runGemini-Bu69Zu1u.cjs} +32 -32
  126. package/dist/{runGemini-Dn6yFpnO.mjs → runGemini-Bub8T5cM.mjs} +32 -32
  127. package/dist/{runOpenClaw-DFD--ZW9.cjs → runOpenClaw-Cob5F3FC.cjs} +18 -18
  128. package/dist/{runOpenClaw-CDU1UOZ1.mjs → runOpenClaw-DVu73F_i.mjs} +18 -18
  129. package/dist/{sandbox-DojsNXsy.mjs → sandbox-9EKOFyQF.mjs} +4 -4
  130. package/dist/{sandbox-ryBV4t4z.cjs → sandbox-GDqMagSF.cjs} +4 -4
  131. package/dist/selfCheck-CNo4uzGB.mjs +84 -0
  132. package/dist/{selfCheck-B69om90v.cjs → selfCheck-DiNo34da.cjs} +34 -34
  133. package/dist/{send-Bi7MDDHN.cjs → send-ThC80164.cjs} +8 -8
  134. package/dist/{send-DyRupwU5.mjs → send-wgtHphq8.mjs} +8 -8
  135. package/dist/{server-B4SxszzQ.mjs → server-Bb8v2fOj.mjs} +4 -4
  136. package/dist/{server-DpJPHOAb.cjs → server-bidHnstE.cjs} +6 -6
  137. package/dist/{sessionLock-DdoHaNAf.cjs → sessionLock-BWBcBZKh.cjs} +3 -3
  138. package/dist/{sessionLock-ZtySCwjt.mjs → sessionLock-gb32sVPR.mjs} +3 -3
  139. package/dist/{sessionModeFile-B-z4OPU6.mjs → sessionModeFile-C-fr1M1h.mjs} +1 -1
  140. package/dist/{sessionModeFile-Bkzcsv_J.cjs → sessionModeFile-DhuTNcJr.cjs} +1 -1
  141. package/dist/{sessionOps-BUzvciA5.cjs → sessionOps-9MChM9Op.cjs} +4 -4
  142. package/dist/{sessionOps-BtqGzSz1.mjs → sessionOps-ByZJbkMq.mjs} +4 -4
  143. package/dist/{sessionScanner-DOdHh5CA.cjs → sessionScanner-BpegdnXK.cjs} +3 -3
  144. package/dist/{sessionScanner-60sqJeks.mjs → sessionScanner-CxXPAVyS.mjs} +3 -3
  145. package/dist/{sessions-OCFh0naE.cjs → sessions-C1X4z_Lm.cjs} +9 -9
  146. package/dist/{sessions-AM5DxBgS.mjs → sessions-l-UgJGjV.mjs} +9 -9
  147. package/dist/{setupOfflineReconnection-BcHl9VGI.cjs → setupOfflineReconnection-CS_8Da8R.cjs} +2 -2
  148. package/dist/{setupOfflineReconnection-VK1e2LME.mjs → setupOfflineReconnection-CgtXlV74.mjs} +2 -2
  149. package/dist/{spawn-CjtSsVs5.cjs → spawn-D2BlNdZu.cjs} +13 -13
  150. package/dist/{spawn-CG1ckxEZ.mjs → spawn-FyoHqYWo.mjs} +13 -13
  151. package/dist/{spawnAgents-HDaE3C0-.mjs → spawnAgents-BhsbJ253.mjs} +1 -1
  152. package/dist/{spawnAgents-vglexX9N.cjs → spawnAgents-CgMKuOhZ.cjs} +1 -1
  153. package/dist/{spawnHappyCLI-A9QYQdFL.cjs → spawnHappyCLI-DByQcqEN.cjs} +1 -1
  154. package/dist/{spawnHappyCLI-XVh-9eez.mjs → spawnHappyCLI-DsC256j_.mjs} +1 -1
  155. package/dist/{specialCommands-BK2gc9vz.cjs → specialCommands-DGUeZqdm.cjs} +3 -3
  156. package/dist/{specialCommands-VaHfF5iC.mjs → specialCommands-DUUBqzvd.mjs} +3 -3
  157. package/dist/{startFileWatcher-CwkqU0fA.cjs → startFileWatcher-BtBaahTn.cjs} +1 -1
  158. package/dist/{startFileWatcher-Dls9MDuo.mjs → startFileWatcher-CaYRObJu.mjs} +1 -1
  159. package/dist/{streamRelay-Iogsh51a.mjs → streamRelay-C8zI7OnR.mjs} +1 -1
  160. package/dist/{streamRelay-Ce9sgcOV.cjs → streamRelay-DqPFsCgx.cjs} +1 -1
  161. package/dist/{sync-BN5jPiq4.cjs → sync-BK-IBkuB.cjs} +1 -1
  162. package/dist/{sync-CU7N-dAQ.mjs → sync-D5YqUVjS.mjs} +1 -1
  163. package/dist/{systemPrompt-bo6KFoMZ.cjs → systemPrompt-DaTKLEb-.cjs} +2 -2
  164. package/dist/{systemPrompt-W9opO926.mjs → systemPrompt-e-qeag_h.mjs} +2 -2
  165. package/dist/{teams-hotQxTHy.mjs → teams-ClrppvKu.mjs} +6 -6
  166. package/dist/{teams--pI0Ri5A.cjs → teams-D9xwa_d3.cjs} +6 -6
  167. package/dist/{time-BDZ6VD5o.cjs → time-0szhiPo4.cjs} +1 -1
  168. package/dist/{time-jR_TKQyj.mjs → time-CvrpeD3S.mjs} +1 -1
  169. package/dist/{titleGenerator-Dp0eIi1b.mjs → titleGenerator-BZ5770wq.mjs} +1 -1
  170. package/dist/{titleGenerator-CQ6ZqGb2.cjs → titleGenerator-Dz5P7sE1.cjs} +2 -2
  171. package/dist/{todo-DVCyX6N9.cjs → todo-CkmNJQey.cjs} +6 -6
  172. package/dist/{todo-CwW7byJY.mjs → todo-o-miKcN2.mjs} +6 -6
  173. package/dist/{tools-C5HNddiJ.cjs → tools-C77-Ssan.cjs} +1 -1
  174. package/dist/{tools-BzD8JYIx.mjs → tools-Cp0qJeBq.mjs} +1 -1
  175. package/dist/{uninstall-G9lEyEVW.cjs → uninstall-DGUPERDX.cjs} +3 -3
  176. package/dist/{uninstall-HRcEekvo.mjs → uninstall-jNJDCLD_.mjs} +3 -3
  177. package/package.json +7 -7
  178. package/dist/selfCheck-jA7PDBkO.mjs +0 -84
@@ -1,11 +1,11 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { spawn } from 'cross-spawn';
3
3
  import { createInterface } from 'node:readline';
4
- import { l as logger } from './logger-CKCRe63e.mjs';
4
+ import { l as logger } from './logger-D5EzMy30.mjs';
5
5
  import { e as errorLogMetadata, c as contentLogMetadata } from './contentLogMetadata-BlWI0egu.mjs';
6
6
  import { i as initializeSandbox, w as wrapForMcpTransport } from './manager-BEsI4qt3.mjs';
7
- import { p as packageJson } from './package-JFU2Ry9r.mjs';
8
- import { s as shouldReadCodexRateLimits, i as isUnsupportedCodexMethodError } from './rateLimitWindows-C36y0Ldw.mjs';
7
+ import { p as packageJson } from './package-D0dxZ-W8.mjs';
8
+ import { s as shouldReadCodexRateLimits, i as isUnsupportedCodexMethodError } from './rateLimitWindows-Bo6XVnie.mjs';
9
9
 
10
10
  function isAppServerAvailable() {
11
11
  try {
@@ -89,7 +89,6 @@ class CodexAppServerClient {
89
89
  _turnId = null;
90
90
  threadDefaults = null;
91
91
  // Turn completion tracking for the currently active sendTurnAndWait call.
92
- // A completion event only resolves once we have seen task_started for this turn.
93
92
  pendingTurnCompletion = null;
94
93
  // Tracks in-flight interruptTurn() RPCs so sendTurnAndWait can wait for them
95
94
  // before starting a new turn (prevents stale turn/interrupt from aborting the next turn).
@@ -99,7 +98,29 @@ class CodexAppServerClient {
99
98
  idleRelease = null;
100
99
  notificationProtocol = "unknown";
101
100
  completedTurnIds = /* @__PURE__ */ new Set();
101
+ /** B-625: turns whose failure has already been forwarded, so a second failed frame is not. */
102
+ failedTurnIds = /* @__PURE__ */ new Set();
103
+ // B-625: the app-server speaks for every thread it hosts — the session's own and the
104
+ // children `spawn_agent` opens — so a lifecycle frame (turn/started, turn/completed,
105
+ // thread/status/changed, error, thread/closed) is the session's only when its `threadId`
106
+ // is ours; a frame naming no thread belongs to the thread it was asked on (Orca's rule,
107
+ // src/main/codex/codex-structured-turn-open-wait.ts). Before this, a child's
108
+ // `turn/completed` ended the parent's turn in the UI and a child's `turn/started`
109
+ // retargeted `turn/interrupt` at the child.
110
+ /** The own thread's last turn-ending `error` (willRetry=false), for a turn end that arrives without a verdict. */
111
+ lastTurnError = null;
112
+ /** Own thread reported idle/systemError while a turn was pending: Codex's `turn/completed`
113
+ * follows within a millisecond; this ends the turn if it does not (see armThreadStopGrace). */
114
+ threadStopGrace = null;
115
+ /** Last few hundred chars of the app-server's stderr: Codex's own last words when it dies. */
116
+ stderrRing = "";
102
117
  rawFileChangesByItemId = /* @__PURE__ */ new Map();
118
+ // B-622: inactivity watchdog for the pending turn — re-armed by every
119
+ // notification, never a cap on how long a turn may run (see sendTurnAndWait).
120
+ turnWatchdog = null;
121
+ // Approval / elicitation requests we are still answering: a human may take
122
+ // longer than the watchdog to decide, and that is not Codex going silent.
123
+ inflightServerRequests = 0;
103
124
  // Handlers set by the consumer (runCodex.ts)
104
125
  eventHandler = null;
105
126
  approvalHandler = null;
@@ -161,8 +182,21 @@ class CodexAppServerClient {
161
182
  const status = params?.turn?.status ?? params?.status ?? null;
162
183
  return typeof status === "string" && status.length > 0 ? status : null;
163
184
  }
185
+ extractThreadId(params) {
186
+ const threadId = params?.threadId ?? params?.thread_id ?? params?.thread?.id ?? null;
187
+ return typeof threadId === "string" && threadId.length > 0 ? threadId : null;
188
+ }
189
+ /** B-625: whether a lifecycle frame is about the session's own thread (see the field comment). */
190
+ isOwnThreadFrame(params) {
191
+ const threadId = this.extractThreadId(params);
192
+ return threadId === null || this._threadId === null || threadId === this._threadId;
193
+ }
194
+ /** Codex's last stderr lines, one line, for a message a person reads. */
195
+ stderrTail() {
196
+ return this.stderrRing.replace(/\x1b\[[0-9;]*m/g, "").replace(/\s+/g, " ").trim();
197
+ }
164
198
  shouldHandleRawNotification(method) {
165
- const isRawNotification = method === "thread/started" || method === "turn/started" || method === "turn/completed" || method === "thread/status/changed" || method === "thread/tokenUsage/updated" || method.startsWith("item/");
199
+ const isRawNotification = method === "thread/started" || method === "turn/started" || method === "turn/completed" || method === "thread/status/changed" || method === "thread/closed" || method === "thread/tokenUsage/updated" || method.startsWith("item/");
166
200
  if (!isRawNotification) {
167
201
  return false;
168
202
  }
@@ -176,14 +210,20 @@ class CodexAppServerClient {
176
210
  }
177
211
  emitRawTurnCompletion(turnId, status, error, source) {
178
212
  const aborted = status === "cancelled" || status === "canceled" || status === "aborted" || status === "interrupted";
213
+ const failed = status === "failed" || error !== void 0 && error !== null;
214
+ this.clearThreadStopGrace();
179
215
  this.tryResolvePendingTurn(aborted, turnId, source);
180
216
  this._turnId = null;
181
217
  this.maybeRefreshRateLimits();
218
+ if (this.lastTurnError && (!turnId || !this.lastTurnError.turnId || this.lastTurnError.turnId === turnId)) {
219
+ this.lastTurnError = null;
220
+ }
182
221
  if (turnId && this.completedTurnIds.has(turnId)) {
183
- return;
222
+ if (!failed || this.failedTurnIds.has(turnId)) return;
184
223
  }
185
224
  if (turnId) {
186
225
  this.completedTurnIds.add(turnId);
226
+ if (failed) this.failedTurnIds.add(turnId);
187
227
  }
188
228
  if (aborted) {
189
229
  this.eventHandler?.({
@@ -206,6 +246,10 @@ class CodexAppServerClient {
206
246
  return false;
207
247
  }
208
248
  if (method === "turn/started") {
249
+ if (!this.isOwnThreadFrame(params)) {
250
+ logger.debug(`[CodexAppServer] Child thread ${this.extractThreadId(params)} started a turn`);
251
+ return true;
252
+ }
209
253
  const turnId = this.extractTurnId(params);
210
254
  if (turnId) {
211
255
  this._turnId = turnId;
@@ -218,6 +262,10 @@ class CodexAppServerClient {
218
262
  return true;
219
263
  }
220
264
  if (method === "turn/completed") {
265
+ if (!this.isOwnThreadFrame(params)) {
266
+ logger.debug(`[CodexAppServer] Child thread ${this.extractThreadId(params)} ended a turn`);
267
+ return true;
268
+ }
221
269
  this.emitRawTurnCompletion(
222
270
  this.extractTurnId(params),
223
271
  this.extractTurnStatus(params),
@@ -227,9 +275,23 @@ class CodexAppServerClient {
227
275
  return true;
228
276
  }
229
277
  if (method === "thread/status/changed") {
278
+ if (!this.isOwnThreadFrame(params)) return true;
230
279
  const statusType = params?.status?.type;
231
- if (statusType === "idle" && this.pendingTurnCompletion) {
232
- this.emitRawTurnCompletion(this._turnId, "completed", null, method);
280
+ if ((statusType === "idle" || statusType === "systemError") && this.pendingTurnCompletion?.opened) {
281
+ this.armThreadStopGrace(statusType);
282
+ } else if (statusType === "active") {
283
+ this.clearThreadStopGrace();
284
+ }
285
+ return true;
286
+ }
287
+ if (method === "thread/closed") {
288
+ if (this.isOwnThreadFrame(params) && this.pendingTurnCompletion) {
289
+ this.emitRawTurnCompletion(
290
+ this.pendingTurnCompletion.turnId ?? this._turnId,
291
+ "aborted",
292
+ { message: "Codex closed the thread before the turn ended." },
293
+ method
294
+ );
233
295
  }
234
296
  return true;
235
297
  }
@@ -316,14 +378,6 @@ class CodexAppServerClient {
316
378
  phase: item.phase
317
379
  });
318
380
  }
319
- if (item.phase === "final_answer" && this.pendingTurnCompletion) {
320
- this.emitRawTurnCompletion(
321
- this.extractTurnId(params),
322
- "completed",
323
- null,
324
- `${method}:final_answer`
325
- );
326
- }
327
381
  return true;
328
382
  }
329
383
  return method.startsWith("item/");
@@ -383,17 +437,35 @@ class CodexAppServerClient {
383
437
  return;
384
438
  }
385
439
  this.connected = false;
440
+ const tail = this.stderrTail();
441
+ const exited = `Codex process exited (code=${code ?? "null"}${signal ? `, signal=${signal}` : ""})`;
386
442
  for (const [id, req] of this.pending) {
387
443
  if (req.epoch !== epoch) continue;
388
- req.reject(new Error(`Codex process exited (code=${code}) while waiting for ${req.method}`));
444
+ const error = new Error(`${exited} while waiting for ${req.method}`);
445
+ error.codexStderrTail = tail;
446
+ req.reject(error);
389
447
  this.pending.delete(id);
390
448
  }
449
+ if (this.pendingTurnCompletion) {
450
+ const turnId = this.pendingTurnCompletion.turnId ?? this._turnId;
451
+ this.eventHandler?.({
452
+ type: "turn_aborted",
453
+ reason: "process-exit",
454
+ ...turnId ? { turn_id: turnId } : {},
455
+ error: { message: `${exited}${tail ? `: ${tail}` : ""}` }
456
+ });
457
+ }
391
458
  this.resolvePendingTurn(true);
392
459
  });
460
+ this.stderrRing = "";
393
461
  proc.stderr?.on("data", (chunk) => {
394
462
  if (this.process !== proc || this.processEpoch !== epoch) return;
395
463
  const text = chunk.toString().trim();
396
- if (text) logger.debug("[CodexAppServer:stderr]", contentLogMetadata(text));
464
+ if (text) {
465
+ logger.debug("[CodexAppServer:stderr]", contentLogMetadata(text));
466
+ this.stderrRing = `${this.stderrRing}
467
+ ${text}`.slice(-400);
468
+ }
397
469
  });
398
470
  this.readline = createInterface({ input: proc.stdout });
399
471
  this.readline.on("line", (line) => {
@@ -444,6 +516,9 @@ class CodexAppServerClient {
444
516
  this._turnId = null;
445
517
  this.notificationProtocol = "unknown";
446
518
  this.completedTurnIds.clear();
519
+ this.failedTurnIds.clear();
520
+ this.lastTurnError = null;
521
+ this.clearThreadStopGrace();
447
522
  if (!opts?.preserveThreadState) {
448
523
  this._threadId = null;
449
524
  this.threadDefaults = null;
@@ -661,15 +736,50 @@ class CodexAppServerClient {
661
736
  }
662
737
  resolvePendingTurn(aborted) {
663
738
  if (!this.pendingTurnCompletion) return;
739
+ this.clearTurnWatchdog();
740
+ this.clearThreadStopGrace();
664
741
  this.pendingTurnCompletion.resolve(aborted);
665
742
  this.pendingTurnCompletion = null;
666
743
  }
667
744
  markPendingTurnStarted(turnId) {
668
745
  if (!this.pendingTurnCompletion) return;
746
+ this.pendingTurnCompletion.opened = true;
669
747
  if (turnId) {
670
748
  this.pendingTurnCompletion.turnId = turnId;
671
749
  }
672
750
  }
751
+ /** Default wait for Codex's `turn/completed` after its thread reported idle/systemError. */
752
+ static THREAD_STOP_GRACE_MS = 1500;
753
+ /**
754
+ * B-625: the own thread stopped (idle / systemError) while a turn was pending and
755
+ * had opened. Codex's `turn/completed` follows at once in every probed release
756
+ * (0.154.0: 1 ms); this is the net under a release that does not send it, so the
757
+ * session never waits on the silence watchdog for a turn Codex has already left.
758
+ */
759
+ armThreadStopGrace(statusType) {
760
+ this.clearThreadStopGrace();
761
+ const graceMs = this.pendingTurnCompletion?.threadStopGraceMs ?? CodexAppServerClient.THREAD_STOP_GRACE_MS;
762
+ this.threadStopGrace = setTimeout(() => {
763
+ this.threadStopGrace = null;
764
+ const pending = this.pendingTurnCompletion;
765
+ if (!pending) return;
766
+ const turnId = pending.turnId ?? this._turnId;
767
+ const lastError = this.lastTurnError && (!turnId || !this.lastTurnError.turnId || this.lastTurnError.turnId === turnId) ? this.lastTurnError.error : null;
768
+ logger.warn(`[CodexAppServer] Thread went ${statusType} and no turn/completed followed for turn ${turnId ?? "?"}; ending the turn`);
769
+ const failed = statusType === "systemError" || lastError !== null;
770
+ this.emitRawTurnCompletion(
771
+ turnId,
772
+ failed ? "failed" : "completed",
773
+ lastError ?? (failed ? { message: "Codex reported a system error and ended the turn without a verdict." } : null),
774
+ `thread/status/changed:${statusType}`
775
+ );
776
+ }, graceMs);
777
+ }
778
+ clearThreadStopGrace() {
779
+ if (!this.threadStopGrace) return;
780
+ clearTimeout(this.threadStopGrace);
781
+ this.threadStopGrace = null;
782
+ }
673
783
  tryResolvePendingTurn(aborted, turnId, source) {
674
784
  const pending = this.pendingTurnCompletion;
675
785
  if (!pending) return;
@@ -771,6 +881,8 @@ class CodexAppServerClient {
771
881
  }
772
882
  /** Default timeout for waiting on turn completion (ms). 10 minutes. */
773
883
  static TURN_TIMEOUT_MS = 10 * 60 * 1e3;
884
+ /** How much of the app-server's stderr is kept for its exit message. */
885
+ static STDERR_TAIL_CHARS = 400;
774
886
  /**
775
887
  * Send a user turn and wait for it to complete (task_complete or turn_aborted).
776
888
  * Returns { aborted: true } if the turn was aborted (user cancel, permission reject, etc.).
@@ -782,30 +894,58 @@ class CodexAppServerClient {
782
894
  }
783
895
  await this.ensureConnected();
784
896
  const timeoutMs = opts?.turnTimeoutMs ?? CodexAppServerClient.TURN_TIMEOUT_MS;
785
- let timer = null;
786
897
  const completion = new Promise((resolve) => {
787
898
  this.pendingTurnCompletion = {
788
899
  resolve,
789
- turnId: null
900
+ turnId: null,
901
+ opened: false,
902
+ threadStopGraceMs: opts?.threadStopGraceMs ?? CodexAppServerClient.THREAD_STOP_GRACE_MS
790
903
  };
791
- timer = setTimeout(() => {
792
- if (this.pendingTurnCompletion) {
793
- logger.warn(`[CodexAppServer] Turn timed out after ${timeoutMs}ms \u2014 treating as abort`);
794
- this.resolvePendingTurn(true);
795
- }
796
- }, timeoutMs);
904
+ this.lastTurnError = null;
905
+ this.armTurnWatchdog(timeoutMs);
797
906
  });
798
907
  try {
799
908
  await this.sendTurn(prompt, opts);
800
909
  } catch (err) {
801
- if (timer) clearTimeout(timer);
910
+ this.clearTurnWatchdog();
802
911
  this.pendingTurnCompletion = null;
803
912
  throw err;
804
913
  }
805
914
  const aborted = await completion;
806
- if (timer) clearTimeout(timer);
915
+ this.clearTurnWatchdog();
807
916
  return { aborted };
808
917
  }
918
+ armTurnWatchdog(timeoutMs) {
919
+ this.clearTurnWatchdog();
920
+ const timer = setTimeout(() => {
921
+ this.turnWatchdog = null;
922
+ if (!this.pendingTurnCompletion) return;
923
+ if (this.inflightServerRequests > 0) {
924
+ this.armTurnWatchdog(timeoutMs);
925
+ return;
926
+ }
927
+ logger.warn(`[CodexAppServer] No notification from Codex for ${timeoutMs}ms \u2014 giving the turn up as aborted`);
928
+ const turnId = this.pendingTurnCompletion.turnId ?? this._turnId;
929
+ this.eventHandler?.({
930
+ type: "turn_aborted",
931
+ reason: "timeout",
932
+ ...turnId ? { turn_id: turnId } : {},
933
+ error: { message: `Codex sent nothing for ${Math.max(1, Math.round(timeoutMs / 6e4))} minutes; this turn was given up. Send the message again, or restart the session if it keeps happening.` }
934
+ });
935
+ this.resolvePendingTurn(true);
936
+ }, timeoutMs);
937
+ this.turnWatchdog = { timer, timeoutMs };
938
+ }
939
+ /** Any sign of life from the app-server while a turn is pending restarts the clock. */
940
+ touchTurnWatchdog() {
941
+ if (!this.turnWatchdog || !this.pendingTurnCompletion) return;
942
+ this.armTurnWatchdog(this.turnWatchdog.timeoutMs);
943
+ }
944
+ clearTurnWatchdog() {
945
+ if (!this.turnWatchdog) return;
946
+ clearTimeout(this.turnWatchdog.timer);
947
+ this.turnWatchdog = null;
948
+ }
809
949
  async interruptTurn() {
810
950
  if (!this._threadId) return;
811
951
  if (!this._turnId) {
@@ -841,6 +981,8 @@ class CodexAppServerClient {
841
981
  this._turnId = null;
842
982
  this.threadDefaults = null;
843
983
  this.completedTurnIds.clear();
984
+ this.failedTurnIds.clear();
985
+ this.lastTurnError = null;
844
986
  this.rawFileChangesByItemId.clear();
845
987
  }
846
988
  // ─── JSON-RPC transport ─────────────────────────────────────
@@ -1013,6 +1155,15 @@ class CodexAppServerClient {
1013
1155
  };
1014
1156
  }
1015
1157
  async handleServerRequest(id, method, params) {
1158
+ this.inflightServerRequests += 1;
1159
+ try {
1160
+ await this.handleServerRequestInner(id, method, params);
1161
+ } finally {
1162
+ this.inflightServerRequests -= 1;
1163
+ this.touchTurnWatchdog();
1164
+ }
1165
+ }
1166
+ async handleServerRequestInner(id, method, params) {
1016
1167
  if (method === "mcpServer/elicitation/request") {
1017
1168
  const toolName = this.parseToolNameFromElicitationMessage(params?.message) ?? params?.serverName ?? "McpTool";
1018
1169
  const decision = await this.handleApproval({
@@ -1066,6 +1217,7 @@ class CodexAppServerClient {
1066
1217
  return "denied";
1067
1218
  }
1068
1219
  handleNotification(method, params) {
1220
+ this.touchTurnWatchdog();
1069
1221
  if (method === "codex/event" || method.startsWith("codex/event/")) {
1070
1222
  this.notificationProtocol = "legacy";
1071
1223
  const msg = params?.msg;
@@ -1099,6 +1251,7 @@ class CodexAppServerClient {
1099
1251
  }
1100
1252
  if (method === "thread/started" || method === "turn/started" || method === "turn/completed" || method === "thread/status/changed") {
1101
1253
  logger.debug(`[CodexAppServer] Lifecycle notification: ${method}`);
1254
+ if (!this.isOwnThreadFrame(params)) return;
1102
1255
  if (method === "turn/started") {
1103
1256
  const turnId = this.extractTurnId(params);
1104
1257
  if (turnId) {
@@ -1125,6 +1278,30 @@ class CodexAppServerClient {
1125
1278
  logger.debug(`[CodexAppServer] mcpServer startup status:`, params);
1126
1279
  return;
1127
1280
  }
1281
+ if (method === "error") {
1282
+ if (!this.isOwnThreadFrame(params)) return;
1283
+ const error = params?.error ?? {};
1284
+ const turnId = typeof params?.turnId === "string" && params.turnId.length > 0 ? params.turnId : null;
1285
+ if (params?.willRetry === true) {
1286
+ const message = typeof error.message === "string" ? error.message : "";
1287
+ const info = error.codexErrorInfo;
1288
+ const httpStatus = info && typeof info === "object" ? info.responseStreamDisconnected?.httpStatusCode ?? info.httpStatusCode ?? null : null;
1289
+ this.eventHandler?.({
1290
+ type: "turn_retry",
1291
+ message,
1292
+ ...typeof httpStatus === "number" ? { http_status: httpStatus } : {},
1293
+ ...turnId ? { turn_id: turnId } : {},
1294
+ ...typeof error.additionalDetails === "string" ? { details: error.additionalDetails } : {}
1295
+ });
1296
+ return;
1297
+ }
1298
+ this.lastTurnError = { turnId, error };
1299
+ const pending = this.pendingTurnCompletion;
1300
+ if (pending && !pending.opened && (turnId === null || pending.turnId === null || pending.turnId === turnId)) {
1301
+ this.emitRawTurnCompletion(turnId ?? pending.turnId, "failed", error, "error:unopened-turn");
1302
+ }
1303
+ return;
1304
+ }
1128
1305
  logger.debug(`[CodexAppServer] Notification: ${method}`);
1129
1306
  }
1130
1307
  }