very-happy-cli 0.2.160 → 0.2.162

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 (176) hide show
  1. package/dist/{AcpBackend-mzv8Dhba.mjs → AcpBackend--TmFHgRH.mjs} +3 -3
  2. package/dist/{AcpBackend-Bm2rdke8.cjs → AcpBackend-9asD_DL7.cjs} +3 -3
  3. package/dist/{AcpSessionManager-CoMWFVw3.mjs → AcpSessionManager-BSIBnWUb.mjs} +1 -1
  4. package/dist/{AcpSessionManager-BAY1Pggx.cjs → AcpSessionManager-BvIuBzoO.cjs} +1 -1
  5. package/dist/{BasePermissionHandler-CFHAxumV.cjs → BasePermissionHandler-D4B-NNu2.cjs} +1 -1
  6. package/dist/{BasePermissionHandler-m3WVholL.mjs → BasePermissionHandler-DUBySe0f.mjs} +1 -1
  7. package/dist/{BaseReasoningProcessor-CYrKBXGm.mjs → BaseReasoningProcessor-9TybUGTq.mjs} +1 -1
  8. package/dist/{BaseReasoningProcessor-BFa9_rZA.cjs → BaseReasoningProcessor-CqLl-ikP.cjs} +1 -1
  9. package/dist/{agentHome-CboCeKWq.cjs → agentHome-BohNUYnE.cjs} +4 -4
  10. package/dist/{agentHome-Dj1C43aJ.mjs → agentHome-DQrq_FG1.mjs} +4 -4
  11. package/dist/{api-CgundNNX.cjs → api-CQnLOqRS.cjs} +10 -10
  12. package/dist/{api-CLMUr-SX.mjs → api-D57dsd6_.mjs} +10 -10
  13. package/dist/{apiMachine-B9dLW_p5.mjs → apiMachine-78Aqzg--.mjs} +21 -14
  14. package/dist/{apiMachine-Bn8482VM.cjs → apiMachine-fFFaWxYy.cjs} +33 -26
  15. package/dist/{assistantTools-BTqPnlFl.cjs → assistantTools-CwESq24n.cjs} +6 -6
  16. package/dist/{assistantTools-D6tuc6o4.mjs → assistantTools-mEz6uNqL.mjs} +6 -6
  17. package/dist/{attachmentContent-DpiTJZb4.cjs → attachmentContent-GHOk6Muq.cjs} +2 -2
  18. package/dist/{attachmentContent-_kxRznNf.mjs → attachmentContent-s1xPha4Q.mjs} +2 -2
  19. package/dist/{attachmentLimits-yba1uHFO.mjs → attachmentLimits-D5pgmASK.mjs} +1 -1
  20. package/dist/{attachmentLimits-CzVrqgLR.cjs → attachmentLimits-DzlBPyJP.cjs} +1 -1
  21. package/dist/{auth-BDOVh5h5.cjs → auth-B8EagV6G.cjs} +8 -8
  22. package/dist/{auth-PYHZQNPp.mjs → auth-BOaHWquu.mjs} +8 -8
  23. package/dist/{auth-ba4WMYrN.cjs → auth-BbSdXMQ_.cjs} +5 -5
  24. package/dist/{auth-Cwutmdsa.mjs → auth-C8V7-oUT.mjs} +5 -5
  25. package/dist/{auto-BOX_fgmR.cjs → auto-Bw_h0yVc.cjs} +15 -14
  26. package/dist/{auto-BXu14yP_.mjs → auto-CWDOX35m.mjs} +15 -14
  27. package/dist/{bootstrap-hxXPCFMx.mjs → bootstrap-BKmsYSwh.mjs} +2 -2
  28. package/dist/{bootstrap-B5G1TijC.cjs → bootstrap-DvZjEa9_.cjs} +2 -2
  29. package/dist/{browser-Y6Bl8L_O.cjs → browser-CBZj-1Wx.cjs} +1 -1
  30. package/dist/{browser-jh2P0UVZ.mjs → browser-CrMAr5fI.mjs} +1 -1
  31. package/dist/{claudeLocal-CRGG4Pea.cjs → claudeLocal-Bpw-u9et.cjs} +5 -5
  32. package/dist/{claudeLocal-BaY4fZKi.mjs → claudeLocal-dhdlcQ6F.mjs} +5 -5
  33. package/dist/{claudeLocalLauncher-D4T7MTqd.mjs → claudeLocalLauncher-BFfGT4eY.mjs} +10 -10
  34. package/dist/{claudeLocalLauncher-DyEbVzUN.cjs → claudeLocalLauncher-DWFWuLhY.cjs} +10 -10
  35. package/dist/{codexThreadFork-BH9IZPqD.cjs → claudeSessionFork-D9-mANmu.cjs} +1 -137
  36. package/dist/{codexThreadFork-slzAseLx.mjs → claudeSessionFork-_DUrXaqd.mjs} +2 -134
  37. package/dist/{client-BPNbmtvO.cjs → client-CVQJWqjx.cjs} +2 -2
  38. package/dist/{client-3Ac-O4pw.cjs → client-DiBTHWNU.cjs} +3 -3
  39. package/dist/{client-CeGKqqcJ.mjs → client-Dk7x-s-O.mjs} +3 -3
  40. package/dist/{client-ClR0K8zX.mjs → client-DwqHDsLb.mjs} +2 -2
  41. package/dist/codex/happyMcpStdioBridge.cjs +15 -15
  42. package/dist/codex/happyMcpStdioBridge.mjs +15 -15
  43. package/dist/{codexAppServerClient-I3teiV-N.mjs → codexAppServerClient-C2ybs2mZ.mjs} +2 -2
  44. package/dist/{codexAppServerClient-q2FjnRcK.cjs → codexAppServerClient-DlKv5TjG.cjs} +2 -2
  45. package/dist/{codexCommand-4zcccIoq.cjs → codexCommand-4vLE31fj.cjs} +45 -40
  46. package/dist/{codexCommand-Cc9gcbdX.mjs → codexCommand-DnmFr9Qx.mjs} +45 -40
  47. package/dist/codexThreadFork-DNekT4nQ.cjs +138 -0
  48. package/dist/codexThreadFork-R76RU5H0.mjs +133 -0
  49. package/dist/{config-Ci9Kpgwa.cjs → config-Btd4B05_.cjs} +3 -3
  50. package/dist/{config-CTx_jb-C.mjs → config-CSOG_ZcP.mjs} +3 -3
  51. package/dist/{configuration-RfeNjFmd.mjs → configuration-A5gW_0GE.mjs} +1 -1
  52. package/dist/{configuration-BSYpcPwE.cjs → configuration-UsI0sxEv.cjs} +1 -1
  53. package/dist/{connect-C2CiOUFs.cjs → connect-C9yxp8Om.cjs} +13 -13
  54. package/dist/{connect-BpOdzNbE.mjs → connect-DWYj8LP_.mjs} +13 -13
  55. package/dist/{context-DsDr6E2t.cjs → context-0FjjA4vJ.cjs} +1 -1
  56. package/dist/{context-H0Bcb9S_.mjs → context-BXnxalWW.mjs} +1 -1
  57. package/dist/{controlClient-Bcwv3D21.cjs → controlClient-BXktrQ4z.cjs} +3 -3
  58. package/dist/{controlClient-BGbDcgWS.mjs → controlClient-CYzHrh3e.mjs} +3 -3
  59. package/dist/{createSessionMetadata-CVVkGjxV.mjs → createSessionMetadata-Dyg33ZlU.mjs} +2 -2
  60. package/dist/{createSessionMetadata-BJxEnM-o.cjs → createSessionMetadata-EX1eAlHx.cjs} +2 -2
  61. package/dist/{deterministicJson-CzkFJWw1.mjs → deterministicJson-DiR7EOMO.mjs} +9 -9
  62. package/dist/{deterministicJson-C5CYTVpm.cjs → deterministicJson-eha_oMZ2.cjs} +9 -9
  63. package/dist/{doctor-D2YJ-Yn2.cjs → doctor-DunLBgVi.cjs} +6 -6
  64. package/dist/{doctor-DOvByjbk.mjs → doctor-FuCvKdRa.mjs} +6 -6
  65. package/dist/{ensureDaemonRunning-BCwNrMyv.cjs → ensureDaemonRunning-B4dZFmUy.cjs} +5 -5
  66. package/dist/{ensureDaemonRunning-Dqa2pOjY.mjs → ensureDaemonRunning-EZkb-CAF.mjs} +5 -5
  67. package/dist/{handleResumeCommand-C2cKchpX.cjs → handleResumeCommand-CKMIw4pE.cjs} +5 -5
  68. package/dist/{handleResumeCommand-BvXYf5El.mjs → handleResumeCommand-CsXendKb.mjs} +5 -5
  69. package/dist/{index-DMZqWwsP.mjs → index-9L39wqYR.mjs} +37 -37
  70. package/dist/{index-qdS1friN.mjs → index-BBg9j0XK.mjs} +8 -1
  71. package/dist/{index-B6pAdvJx.cjs → index-BOhgL3rh.cjs} +36 -36
  72. package/dist/{index-Bu5Zi_a9.cjs → index-CZWAiydF.cjs} +4 -4
  73. package/dist/{index-DVkMGPyS.mjs → index-CvXcVQ06.mjs} +3 -3
  74. package/dist/{index-DA7yINuk.cjs → index-DJ-oV52i.cjs} +8 -1
  75. package/dist/{index-DKLU_fD0.mjs → index-DK2JpTJf.mjs} +4 -4
  76. package/dist/{index-DDeHXrIX.cjs → index-DRhWHEuW.cjs} +3 -3
  77. package/dist/index.cjs +2 -2
  78. package/dist/index.mjs +2 -2
  79. package/dist/{install-CFY3tYtJ.mjs → install-B2duVo9a.mjs} +3 -3
  80. package/dist/{install-BgoC2e_2.cjs → install-C8WyZI9K.cjs} +3 -3
  81. package/dist/lib.cjs +12 -12
  82. package/dist/lib.d.cts +5 -0
  83. package/dist/lib.d.mts +5 -0
  84. package/dist/lib.mjs +12 -12
  85. package/dist/{lidState-CeEt442o.mjs → lidState-CigXWxhe.mjs} +1 -1
  86. package/dist/{lidState-BeDfxxEX.cjs → lidState-Ds-6ytZV.cjs} +1 -1
  87. package/dist/{localHappyAgentAuth-C-HQyiv6.mjs → localHappyAgentAuth-CaCyKzYp.mjs} +1 -1
  88. package/dist/{localHappyAgentAuth-D6rFhaoc.cjs → localHappyAgentAuth-l0Na-5Ys.cjs} +1 -1
  89. package/dist/{logger-CS0mw3fa.cjs → logger-7ucWm9wp.cjs} +2 -2
  90. package/dist/{logger-6HYUEXNk.mjs → logger-CGE4C8ho.mjs} +2 -2
  91. package/dist/{machineMetadata-DyDbODYd.cjs → machineMetadata-lMPtpF16.cjs} +3 -3
  92. package/dist/{machineMetadata-oOzBfDfs.mjs → machineMetadata-ue1nkowa.mjs} +3 -3
  93. package/dist/{main-BwNIAI1A.mjs → main-3fIFXD-t.mjs} +44 -44
  94. package/dist/{main-BkxJP6JQ.cjs → main-CFI0datL.cjs} +44 -44
  95. package/dist/{mcp-B1A4I3TK.mjs → mcp-DARQBZ7l.mjs} +14 -14
  96. package/dist/{mcp-Hszd7qpS.cjs → mcp-DfxLAc1G.cjs} +14 -14
  97. package/dist/{package-BwMrPwqH.cjs → package-D1A-8ZJy.cjs} +7 -7
  98. package/dist/{package-BhaKlnxz.mjs → package-DQsvY1XN.mjs} +7 -7
  99. package/dist/{peerTools-BTqw97Gb.mjs → peerTools-D19hhSQF.mjs} +4 -4
  100. package/dist/{peerTools-BvKtHXj8.cjs → peerTools-DLwNaEEV.cjs} +4 -4
  101. package/dist/{permissionMode-Cr5JCbB7.mjs → permissionMode-CH4FoZfU.mjs} +1 -1
  102. package/dist/{persistence-HGW_ZVs9.mjs → persistence-Cw8aLoyL.mjs} +3 -3
  103. package/dist/{persistence-yKTH3-5X.cjs → persistence-i0nQMLlk.cjs} +3 -3
  104. package/dist/{piRunnerArgs-BuX1UFWy.mjs → piRunnerArgs-58t454gw.mjs} +1 -1
  105. package/dist/{piRunnerArgs-BNloJCCg.cjs → piRunnerArgs-BUyxPxjZ.cjs} +1 -1
  106. package/dist/{piRuntime-CSGkwCKQ.mjs → piRuntime-DVPixFjl.mjs} +1 -1
  107. package/dist/{piRuntime-C_H7MJCM.cjs → piRuntime-Dff73XCb.cjs} +1 -1
  108. package/dist/{piTerminal-DNCZ6yTM.cjs → piTerminal-Dz1RrO70.cjs} +16 -16
  109. package/dist/{piTerminal-C8a1OrNQ.mjs → piTerminal-gHWFAyzq.mjs} +16 -16
  110. package/dist/{registerCommonHandlers-DskirN-_.cjs → registerCommonHandlers-CMROPdJz.cjs} +2 -2
  111. package/dist/{registerCommonHandlers-Ccvsz7Ou.mjs → registerCommonHandlers-Ckv5boQI.mjs} +2 -2
  112. package/dist/{registerKillSessionHandler-DZeLXWcB.cjs → registerKillSessionHandler-CA9-oelM.cjs} +19 -10
  113. package/dist/{registerKillSessionHandler-GYvHdipX.mjs → registerKillSessionHandler-Cll4-o8Y.mjs} +19 -10
  114. package/dist/{remoteSessionClient-CYnXRynE.mjs → remoteSessionClient-BMjsbuBh.mjs} +5 -5
  115. package/dist/{remoteSessionClient-B0LraSB9.cjs → remoteSessionClient-DX9jHLeW.cjs} +5 -5
  116. package/dist/{run-CJzxoHVh.cjs → run-CjbGo2wb.cjs} +36 -32
  117. package/dist/{run-D3ifupU3.mjs → run-CrunzDlt.mjs} +35 -31
  118. package/dist/{runClaude-Da3RirzE.cjs → runClaude-KN-4Y4LH.cjs} +184 -140
  119. package/dist/{runClaude-Ba01qT-u.mjs → runClaude-WDBWRtnF.mjs} +185 -141
  120. package/dist/{runClaudeDeps-DOzEBMls.cjs → runClaudeDeps-DW-8lnHv.cjs} +821 -125
  121. package/dist/{runClaudeDeps-DOt79DjU.mjs → runClaudeDeps-QaEx8y5r.mjs} +811 -129
  122. package/dist/{runGemini-DSr9LIxL.cjs → runGemini-Djmwjdv1.cjs} +32 -32
  123. package/dist/{runGemini-BGeTz57m.mjs → runGemini-Nf6SKBpw.mjs} +32 -32
  124. package/dist/{runOpenClaw-CogSedIc.mjs → runOpenClaw-9nHtmYbN.mjs} +18 -18
  125. package/dist/{runOpenClaw-B1mTxN35.cjs → runOpenClaw-CAqP5KFH.cjs} +18 -18
  126. package/dist/{sandbox-CG7IyDTU.cjs → sandbox-C8Dd8GPM.cjs} +4 -4
  127. package/dist/{sandbox-DTe6d54R.mjs → sandbox-SP0THMt0.mjs} +4 -4
  128. package/dist/selfCheck-7FaZtVu2.mjs +84 -0
  129. package/dist/{selfCheck-Dc-mNSLb.cjs → selfCheck-D9ZXrK7h.cjs} +34 -34
  130. package/dist/{send-CBXJKc6O.mjs → send-DOx9aQsx.mjs} +8 -8
  131. package/dist/{send-B7J_eaG3.cjs → send-LPk6Q5HI.cjs} +8 -8
  132. package/dist/{server-DcVBv04l.mjs → server-BZNi8Hq_.mjs} +4 -4
  133. package/dist/{server-CZTP_iOn.cjs → server-CxT8jNyn.cjs} +6 -6
  134. package/dist/{sessionLock-D-EG2CfG.mjs → sessionLock-CfLqJfeI.mjs} +2 -2
  135. package/dist/{sessionLock-Bys1PNSo.cjs → sessionLock-y9rnA4LK.cjs} +2 -2
  136. package/dist/{sessionModeFile-RSm0NQkH.cjs → sessionModeFile-B5YeoQJh.cjs} +1 -1
  137. package/dist/{sessionModeFile-DMl3dw3r.mjs → sessionModeFile-BKxgNH3P.mjs} +1 -1
  138. package/dist/{sessionOps-SuHW8Dfe.mjs → sessionOps-CdwVPSZz.mjs} +4 -4
  139. package/dist/{sessionOps-Dn8z85h_.cjs → sessionOps-GsELm2PZ.cjs} +4 -4
  140. package/dist/{sessionScanner-CX3onqXh.mjs → sessionScanner-Cdukzo70.mjs} +3 -3
  141. package/dist/{sessionScanner-BOrGjMXL.cjs → sessionScanner-aLEv7mD3.cjs} +3 -3
  142. package/dist/{sessions-D74aJbhL.cjs → sessions-CfG4WGN0.cjs} +9 -9
  143. package/dist/{sessions-BEG_HTSi.mjs → sessions-Dne-OiRL.mjs} +9 -9
  144. package/dist/{setupOfflineReconnection-ByElY60m.cjs → setupOfflineReconnection-CFwltTcP.cjs} +2 -2
  145. package/dist/{setupOfflineReconnection-DpvWTt69.mjs → setupOfflineReconnection-DiQma2Cc.mjs} +2 -2
  146. package/dist/{spawn-DgR0RKRs.cjs → spawn-BI_sdgWs.cjs} +24 -15
  147. package/dist/{spawn-B7yro0b2.mjs → spawn-DkgP2BGF.mjs} +22 -14
  148. package/dist/{spawnAgents-DZzZ9jMI.mjs → spawnAgents-Y1LjnCt9.mjs} +1 -1
  149. package/dist/{spawnAgents-BGQiEF-w.cjs → spawnAgents-iSu1Xzs5.cjs} +1 -1
  150. package/dist/{spawnHappyCLI-B2H5s4B_.mjs → spawnHappyCLI-9mx4lPxh.mjs} +1 -1
  151. package/dist/{spawnHappyCLI-DBhwZT7O.cjs → spawnHappyCLI-BRdx0sAX.cjs} +1 -1
  152. package/dist/{startFileWatcher-B4VsLgjD.cjs → startFileWatcher-C2HC2_Dg.cjs} +1 -1
  153. package/dist/{startFileWatcher-DErVWZE4.mjs → startFileWatcher-COD5UKNs.mjs} +1 -1
  154. package/dist/{streamRelay-FPVbHpbM.mjs → streamRelay-BLgU9X3R.mjs} +1 -1
  155. package/dist/{streamRelay-BiKw59Cc.cjs → streamRelay-DnObXZZj.cjs} +1 -1
  156. package/dist/{sync-C8gyQFMc.mjs → sync-BpLasLu2.mjs} +1 -1
  157. package/dist/{sync-DccIKZ5_.cjs → sync-Dj92ZJ8Y.cjs} +1 -1
  158. package/dist/{systemPrompt-BwTPWfMu.cjs → systemPrompt-CyEz_IfR.cjs} +2 -2
  159. package/dist/{systemPrompt-DX521JJz.mjs → systemPrompt-DsQoUgQw.mjs} +2 -2
  160. package/dist/{teams-C32F1hqd.mjs → teams-BETNMjpZ.mjs} +6 -6
  161. package/dist/{teams-Dl8g8kTI.cjs → teams-Ctx9zXn4.cjs} +6 -6
  162. package/dist/{time-DCuTyook.cjs → time-CLwVAz6s.cjs} +1 -1
  163. package/dist/{time-D0XyHOid.mjs → time-CVvxdEDh.mjs} +1 -1
  164. package/dist/{titleGenerator-CQ8CoGhg.mjs → titleGenerator-B4AW3dLr.mjs} +12 -2
  165. package/dist/{titleGenerator-ChzMJsyg.cjs → titleGenerator-FDpdizn7.cjs} +13 -3
  166. package/dist/{todo-BvUOTa8G.cjs → todo-C3tyFeq1.cjs} +6 -6
  167. package/dist/{todo-CZ6cFrrn.mjs → todo-Jk4Vs7hP.mjs} +6 -6
  168. package/dist/{tools-BZtHF_qQ.mjs → tools-BA1qkiV1.mjs} +2 -2
  169. package/dist/{tools-DQj0AeLk.cjs → tools-CGxq3I52.cjs} +2 -2
  170. package/dist/{tools-Be3AoAwu.cjs → tools-O5beDK-s.cjs} +1 -1
  171. package/dist/{tools-DkluDcG4.mjs → tools-_kFe4m_l.mjs} +1 -1
  172. package/dist/{uninstall-BkLzaJ6I.mjs → uninstall-C_mdAZlm.mjs} +3 -3
  173. package/dist/{uninstall-x-TUtf5H.cjs → uninstall-DHKF90lo.cjs} +3 -3
  174. package/package.json +7 -7
  175. package/scripts/session_hook_forwarder.cjs +9 -2
  176. package/dist/selfCheck-C9mVrQwf.mjs +0 -84
@@ -1,48 +1,50 @@
1
- import { l as logger } from './logger-6HYUEXNk.mjs';
2
- import { k as notifyDaemonSessionEvent, m as notifyDaemonClaudeAuthFailed } from './controlClient-BGbDcgWS.mjs';
3
- export { g as reportSessionEditToDaemon } from './controlClient-BGbDcgWS.mjs';
1
+ import { l as logger } from './logger-CGE4C8ho.mjs';
2
+ import { k as notifyDaemonSessionEvent, m as notifyDaemonClaudeAuthFailed } from './controlClient-CYzHrh3e.mjs';
3
+ export { g as reportSessionEditToDaemon } from './controlClient-CYzHrh3e.mjs';
4
4
  import { s as sameBackgroundTasks, c as createBackgroundTaskTracker } from './backgroundTaskActivity-8PdPxrvy.mjs';
5
5
  import { randomInt, randomUUID, createHash } from 'node:crypto';
6
6
  import { M as MessageBuffer } from './messageBuffer-BWlGgR3A.mjs';
7
7
  import { query } from './index-B6UGRQ7h.mjs';
8
8
  import { AbortError } from '@anthropic-ai/claude-agent-sdk';
9
- import { m as mapToClaudeMode, p as parseClaudePermissionMode } from './permissionMode-Cr5JCbB7.mjs';
10
- import { c as claudeCheckSession, s as systemPrompt } from './systemPrompt-DX521JJz.mjs';
9
+ import { m as mapToClaudeMode, p as parseClaudePermissionMode, c as resolveRemoteClaudePermissionMode } from './permissionMode-CH4FoZfU.mjs';
10
+ import { c as claudeCheckSession, s as systemPrompt } from './systemPrompt-DsQoUgQw.mjs';
11
11
  import { join, resolve, dirname } from 'node:path';
12
12
  import { p as parseSpecialCommand } from './specialCommands-BSqhYg5X.mjs';
13
13
  import 'axios';
14
14
  import 'node:events';
15
15
  import 'socket.io-client';
16
- import { A as AsyncLock } from './attachmentLimits-yba1uHFO.mjs';
16
+ import { A as AsyncLock } from './attachmentLimits-D5pgmASK.mjs';
17
17
  import 'tweetnacl';
18
- import { c as configuration, e as ensurePrivateDirectory } from './configuration-RfeNjFmd.mjs';
19
- import './index-qdS1friN.mjs';
20
- import './registerCommonHandlers-Ccvsz7Ou.mjs';
18
+ import { c as configuration, e as ensurePrivateDirectory } from './configuration-A5gW_0GE.mjs';
19
+ import './index-BBg9j0XK.mjs';
20
+ import './registerCommonHandlers-Ckv5boQI.mjs';
21
21
  import 'os';
22
22
  import 'child_process';
23
23
  import '@paralleldrive/cuid2';
24
- import './persistence-HGW_ZVs9.mjs';
24
+ import './persistence-Cw8aLoyL.mjs';
25
25
  import { c as contentLogMetadata } from './contentLogMetadata-BlWI0egu.mjs';
26
26
  import 'chalk';
27
27
  export { R as RawJSONLinesSchema } from './types-CkqAACXx.mjs';
28
28
  import { g as getProjectPath } from './path-D2wdaNSk.mjs';
29
29
  import { access } from 'fs/promises';
30
- import { d as delay } from './time-D0XyHOid.mjs';
31
- import { F as Future } from './sessionScanner-CX3onqXh.mjs';
32
- export { c as createSessionScanner } from './sessionScanner-CX3onqXh.mjs';
30
+ import { d as delay } from './time-CVvxdEDh.mjs';
31
+ import { mkdirSync, writeFileSync, readFileSync, unlinkSync, renameSync, statSync, existsSync } from 'node:fs';
32
+ import { h as hashObject, i as isSelfReportFresh } from './deterministicJson-DiR7EOMO.mjs';
33
+ export { c as createSelfReportState, s as startHappyServer } from './deterministicJson-DiR7EOMO.mjs';
34
+ import { F as Future } from './sessionScanner-Cdukzo70.mjs';
35
+ export { c as createSessionScanner } from './sessionScanner-Cdukzo70.mjs';
33
36
  import { execSync } from 'node:child_process';
34
- import { S as StreamRelay } from './streamRelay-FPVbHpbM.mjs';
35
- import { C as CLAUDE_ATTACHMENT_KINDS, c as chatAttachmentDirectory, a as stageClaudeAttachments, b as appendStagedAttachmentsToPrompt } from './attachmentContent-_kxRznNf.mjs';
36
- export { M as MessageQueue2, r as registerKillSessionHandler } from './registerKillSessionHandler-GYvHdipX.mjs';
37
- import { h as hashObject, i as isSelfReportFresh } from './deterministicJson-CzkFJWw1.mjs';
38
- export { c as createSelfReportState, s as startHappyServer } from './deterministicJson-CzkFJWw1.mjs';
39
- export { g as getEnvironmentInfo } from './doctor-DOvByjbk.mjs';
37
+ import { S as StreamRelay } from './streamRelay-BLgU9X3R.mjs';
38
+ import { C as CLAUDE_ATTACHMENT_KINDS, c as chatAttachmentDirectory, a as stageClaudeAttachments, b as appendStagedAttachmentsToPrompt } from './attachmentContent-s1xPha4Q.mjs';
39
+ import { readFile, writeFile, rename, unlink } from 'node:fs/promises';
40
+ import { F as ForkSourceMissingError } from './claudeSessionFork-_DUrXaqd.mjs';
41
+ export { M as MessageQueue2, r as registerKillSessionHandler } from './registerKillSessionHandler-Cll4-o8Y.mjs';
42
+ export { g as getEnvironmentInfo } from './doctor-FuCvKdRa.mjs';
40
43
  import { createServer } from 'node:http';
41
44
  export { E as EditReportThrottle, b as extractClaudeEditPaths } from './editPaths-BVfS_FFL.mjs';
42
- import { existsSync, unlinkSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
43
45
  import { p as projectPath } from './projectPath-BTBIMB57.mjs';
44
- import { r as resolveClaudeBinary, a as runClaudeOneShot } from './titleGenerator-CQ8CoGhg.mjs';
45
- export { T as TitleGenerator } from './titleGenerator-CQ8CoGhg.mjs';
46
+ import { r as resolveClaudeBinary, a as runClaudeOneShot } from './titleGenerator-B4AW3dLr.mjs';
47
+ export { T as TitleGenerator } from './titleGenerator-B4AW3dLr.mjs';
46
48
  import { B as BOARD_ANALYZER_PROMPT_PREFIX } from './oneShotPrompts-DT4p6RNd.mjs';
47
49
  import 'node:util';
48
50
  import './smallFastModel-BLNgMrwR.mjs';
@@ -50,34 +52,34 @@ import './trimIdent-BAjmv_TG.mjs';
50
52
  import 'node:os';
51
53
  import './agentGuidance-CQuNlNx8.mjs';
52
54
  import 'zod';
53
- import './package-BhaKlnxz.mjs';
54
- import 'node:fs/promises';
55
+ import './package-DQsvY1XN.mjs';
55
56
  import 'util';
56
57
  import 'crypto';
57
58
  import 'path';
58
59
  import 'cross-spawn';
59
60
  import './authRelay-DSAvg1uv.mjs';
60
- import './sync-C8gyQFMc.mjs';
61
- import './startFileWatcher-DErVWZE4.mjs';
62
- import './client-CeGKqqcJ.mjs';
63
- import './context-H0Bcb9S_.mjs';
64
- import './sessionOps-SuHW8Dfe.mjs';
61
+ import './client-Dk7x-s-O.mjs';
62
+ import './context-BXnxalWW.mjs';
63
+ import './sessionOps-CdwVPSZz.mjs';
65
64
  import './resources-BdFBQi8r.mjs';
66
- import './tools-DkluDcG4.mjs';
67
- import './tools-BZtHF_qQ.mjs';
68
- import './client-ClR0K8zX.mjs';
69
- import './peerTools-BTqw97Gb.mjs';
65
+ import './tools-_kFe4m_l.mjs';
66
+ import './tools-BA1qkiV1.mjs';
67
+ import './client-DwqHDsLb.mjs';
68
+ import './peerTools-D19hhSQF.mjs';
70
69
  import './ids-DU6sLx4C.mjs';
71
- import './remoteSessionClient-CYnXRynE.mjs';
70
+ import './remoteSessionClient-BMjsbuBh.mjs';
72
71
  import '@modelcontextprotocol/sdk/server/mcp.js';
73
72
  import '@modelcontextprotocol/sdk/server/streamableHttp.js';
74
73
  import './limits-DIo7rEBR.mjs';
75
- import './assistantTools-D6tuc6o4.mjs';
74
+ import './assistantTools-mEz6uNqL.mjs';
76
75
  import './terminals-Dlg0x3nY.mjs';
77
76
  import './terminalTitleSuggest-CeKsX_V6.mjs';
78
77
  import './tmuxSocket-B8Yx20sT.mjs';
79
- import './bootstrap-hxXPCFMx.mjs';
78
+ import './bootstrap-BKmsYSwh.mjs';
80
79
  import './previewPath-CXN4Vag0.mjs';
80
+ import './sync-BpLasLu2.mjs';
81
+ import './startFileWatcher-COD5UKNs.mjs';
82
+ import 'node:readline';
81
83
  import './doctor-BPkYWSuq.mjs';
82
84
  import 'ps-list';
83
85
  import './doctorReadiness-C208SMN9.mjs';
@@ -145,6 +147,20 @@ class Session {
145
147
  steerHandler = null;
146
148
  /** B-482: the live remote Query's in-process side-question entry; null between Queries and in local mode. */
147
149
  sideQuestionLive = null;
150
+ /** B-515: consumed by the first remote launch; later launches never prewarm. */
151
+ claudePrewarm = null;
152
+ /** Hand the prewarm lease to the first remote launch — once per process. */
153
+ takeClaudePrewarm = () => {
154
+ const factory = this.claudePrewarm;
155
+ this.claudePrewarm = null;
156
+ if (!factory || this.sessionId) return null;
157
+ try {
158
+ return factory();
159
+ } catch (error) {
160
+ logger.debug("[Session] Claude prewarm setup failed:", error);
161
+ return null;
162
+ }
163
+ };
148
164
  /** Callbacks to be notified when session ID is found/changed */
149
165
  sessionFoundCallbacks = [];
150
166
  /** Keep alive interval reference for cleanup */
@@ -165,6 +181,7 @@ class Session {
165
181
  this._onAbort = opts.onAbort;
166
182
  this.hookSettingsPath = opts.hookSettingsPath;
167
183
  this.jsRuntime = opts.jsRuntime ?? "node";
184
+ this.claudePrewarm = opts.claudePrewarm ?? null;
168
185
  try {
169
186
  this.notificationProducer = opts.api.notificationProducer(
170
187
  opts.client.sessionId,
@@ -699,6 +716,285 @@ function modelSwitchFailureNotice(model, error) {
699
716
  return `\u26A0\uFE0F Could not switch to model "${name}" \u2014 continuing with the previous model. ${detail}`;
700
717
  }
701
718
 
719
+ const SDK_TIMING_FIELDS = ["ttft_ms", "time_to_request_ms", "time_to_request_from_spawn_ms"];
720
+ function delta(from, to) {
721
+ if (typeof from !== "number" || typeof to !== "number" || to < from) return "-";
722
+ return `${Math.round(to - from)}ms`;
723
+ }
724
+ function sdkField(result, key) {
725
+ if (!result || typeof result !== "object") return "-";
726
+ const value = result[key];
727
+ return typeof value === "number" && Number.isFinite(value) ? `${Math.round(value)}` : "-";
728
+ }
729
+ function formatClaudeTimingLine(input) {
730
+ const m = input.marks;
731
+ const sdk = SDK_TIMING_FIELDS.map((key) => `${key}=${sdkField(input.result, key)}`).join(",");
732
+ return [
733
+ "[CLAUDE TIMING]",
734
+ `turn=${input.turn}`,
735
+ `firstTurnOfProcess=${input.firstTurnOfProcess}`,
736
+ `pushed\u2192spawn=${delta(m.pushedAt, m.spawnAt)}`,
737
+ `spawn\u2192handshake=${delta(m.spawnAt, m.handshakeAt)}`,
738
+ `handshake\u2192init=${delta(m.handshakeAt, m.initAt)}`,
739
+ `init\u2192firstAssistant=${delta(m.initAt, m.firstAssistantAt)}`,
740
+ `pushed\u2192firstAssistant=${delta(m.pushedAt, m.firstAssistantAt)}`,
741
+ `sdk{${sdk}}`,
742
+ ...input.prewarm ? [`prewarm=${input.prewarm}`] : []
743
+ ].join(" ");
744
+ }
745
+
746
+ function claudeModeHash(mode) {
747
+ return hashObject({
748
+ isPlan: mode.permissionMode === "plan",
749
+ fallbackModel: mode.fallbackModel,
750
+ customSystemPrompt: mode.customSystemPrompt,
751
+ appendSystemPrompt: mode.appendSystemPrompt,
752
+ allowedTools: mode.allowedTools,
753
+ disallowedTools: mode.disallowedTools,
754
+ effort: mode.effort
755
+ });
756
+ }
757
+
758
+ const CLAUDE_PREWARM_IDLE_MS = 15 * 60 * 1e3;
759
+ const CLAUDE_PREWARM_MAX_CONCURRENT = 2;
760
+ const CLAUDE_PREWARM_ADOPT_TIMEOUT_MS = 5e3;
761
+ const PREWARM_HOOK_SOURCE_PREFIX = "prewarm-";
762
+ const BAD_AUTH_STATUSES = /* @__PURE__ */ new Set(["not-logged-in", "error", "claude-missing"]);
763
+ const RESUME_ARGS = /* @__PURE__ */ new Set(["--resume", "-r", "--continue", "-c", "--fork-session", "--session-id"]);
764
+ const CLAUDE_PREWARM_MAX_CONSECUTIVE_MISSES = 5;
765
+ function claudePrewarmSessionEligibility(input) {
766
+ const { env } = input;
767
+ const no = (reason) => ({ eligible: false, reason });
768
+ if (env.HAPPY_CLAUDE_PREWARM === "0") return no("env-off");
769
+ if (input.setting === "off") return no("setting-off");
770
+ if (input.startedBy !== "daemon") return no("not-daemon");
771
+ if (input.startingMode !== "remote") return no("not-remote");
772
+ if (env.HAPPY_RECONNECT_SESSION_ID) return no("reconnect");
773
+ if (env.HAPPY_FORK_CLAUDE_SESSION_ID || env.HAPPY_FORKED_FROM_SESSION_ID) return no("fork");
774
+ if (env.HAPPY_IMPORTED_FROM_CLAUDE_SESSION_ID) return no("import");
775
+ if (env.HAPPY_SESSION_VARIANT === "assistant") return no("assistant");
776
+ if (env.VH_TEAM_OPERATION_ID) return no("teams");
777
+ if (env.VH_AUTOMATION_RUN_ID) return no("automation");
778
+ if (env.HAPPY_SPAWNED_BY) return no("spawned-by");
779
+ if (env.HAPPY_FIRST_MESSAGE_FROM_CLI === "1") return no("cli-first-message");
780
+ if (input.claudeArgs?.some((arg) => RESUME_ARGS.has(arg))) return no("resume-args");
781
+ if (env.HAPPY_CLAUDE_AUTH_STATUS && BAD_AUTH_STATUSES.has(env.HAPPY_CLAUDE_AUTH_STATUS)) return no("claude-auth");
782
+ return { eligible: true };
783
+ }
784
+ function claudePrewarmEligibility(input) {
785
+ const session = claudePrewarmSessionEligibility(input);
786
+ if (!session.eligible) return session;
787
+ if (!input.cache?.appendSystemPrompt) return { eligible: false, reason: "no-cached-system-prompt" };
788
+ if (input.cache.consecutiveMisses >= CLAUDE_PREWARM_MAX_CONSECUTIVE_MISSES) return { eligible: false, reason: "recent-misses" };
789
+ return { eligible: true };
790
+ }
791
+ function prewarmCacheFile(happyHomeDir) {
792
+ return join(happyHomeDir, "claude-prewarm.json");
793
+ }
794
+ function readPrewarmCache(file) {
795
+ try {
796
+ const raw = JSON.parse(readFileSync(file, "utf8"));
797
+ if (typeof raw?.appendSystemPrompt !== "string" || raw.appendSystemPrompt.length === 0) return null;
798
+ const misses = typeof raw.consecutiveMisses === "number" && Number.isFinite(raw.consecutiveMisses) && raw.consecutiveMisses > 0 ? Math.floor(raw.consecutiveMisses) : 0;
799
+ return {
800
+ appendSystemPrompt: raw.appendSystemPrompt,
801
+ ...typeof raw.effort === "string" || raw.effort === null ? { effort: raw.effort } : {},
802
+ consecutiveMisses: misses
803
+ };
804
+ } catch {
805
+ return null;
806
+ }
807
+ }
808
+ function writePrewarmCache(file, cache) {
809
+ try {
810
+ const tmp = `${file}.${process.pid}.tmp`;
811
+ writeFileSync(tmp, JSON.stringify({ ...cache, updatedAt: Date.now() }), { mode: 384 });
812
+ renameSync(tmp, file);
813
+ return true;
814
+ } catch {
815
+ return false;
816
+ }
817
+ }
818
+ function prewarmPredictionMeta(cache) {
819
+ return {
820
+ appendSystemPrompt: cache.appendSystemPrompt,
821
+ ...cache.effort !== void 0 ? { effort: cache.effort } : {}
822
+ };
823
+ }
824
+ function evaluatePrewarmPrediction(input) {
825
+ const outcome = input.predicted === null ? "no-prediction" : claudeModeHash(input.predicted) === claudeModeHash(input.actual) ? "hit" : "miss";
826
+ const append = input.meta?.appendSystemPrompt;
827
+ const misses = outcome === "miss" ? (input.previous?.consecutiveMisses ?? 0) + 1 : 0;
828
+ if (typeof append !== "string" || append.length === 0) {
829
+ return { outcome, next: input.previous ? { ...input.previous, consecutiveMisses: misses } : null };
830
+ }
831
+ const hasEffort = !!input.meta && Object.prototype.hasOwnProperty.call(input.meta, "effort");
832
+ return {
833
+ outcome,
834
+ next: {
835
+ appendSystemPrompt: append,
836
+ ...hasEffort ? { effort: input.meta.effort ?? null } : {},
837
+ consecutiveMisses: misses
838
+ }
839
+ };
840
+ }
841
+ function prewarmSlotDir(happyHomeDir) {
842
+ return join(happyHomeDir, "tmp", "claude-prewarm");
843
+ }
844
+ function acquirePrewarmSlot(dir, opts = {}) {
845
+ const max = opts.max ?? CLAUDE_PREWARM_MAX_CONCURRENT;
846
+ const pid = opts.pid ?? process.pid;
847
+ const isAlive = opts.isAlive ?? pidAlive;
848
+ const now = opts.now ?? Date.now();
849
+ const staleMs = opts.staleMs ?? CLAUDE_PREWARM_IDLE_MS + 6e4;
850
+ try {
851
+ mkdirSync(dir, { recursive: true, mode: 448 });
852
+ } catch {
853
+ return null;
854
+ }
855
+ for (let i = 0; i < max; i++) {
856
+ const path = join(dir, `slot-${i}.lock`);
857
+ for (let attempt = 0; attempt < 2; attempt++) {
858
+ try {
859
+ writeFileSync(path, String(pid), { flag: "wx", mode: 384 });
860
+ let released = false;
861
+ return {
862
+ path,
863
+ release: () => {
864
+ if (released) return;
865
+ released = true;
866
+ try {
867
+ if (readFileSync(path, "utf8").trim() === String(pid)) unlinkSync(path);
868
+ } catch {
869
+ }
870
+ }
871
+ };
872
+ } catch (error) {
873
+ if (error?.code !== "EEXIST") return null;
874
+ if (!slotIsStale(path, { isAlive, now, staleMs }) || attempt > 0) break;
875
+ try {
876
+ unlinkSync(path);
877
+ } catch {
878
+ }
879
+ }
880
+ }
881
+ }
882
+ return null;
883
+ }
884
+ function slotIsStale(path, opts) {
885
+ try {
886
+ const owner = Number(readFileSync(path, "utf8").trim());
887
+ if (!Number.isInteger(owner) || owner <= 0 || !opts.isAlive(owner)) return true;
888
+ return opts.now - statSync(path).mtimeMs > opts.staleMs;
889
+ } catch {
890
+ return !existsSync(path);
891
+ }
892
+ }
893
+ function pidAlive(pid) {
894
+ try {
895
+ process.kill(pid, 0);
896
+ return true;
897
+ } catch (error) {
898
+ return error?.code === "EPERM";
899
+ }
900
+ }
901
+ class PrewarmHookGate {
902
+ next = 0;
903
+ adopted = /* @__PURE__ */ new Set();
904
+ newTag() {
905
+ return `${PREWARM_HOOK_SOURCE_PREFIX}${process.pid}-${this.next++}`;
906
+ }
907
+ adopt(tag) {
908
+ this.adopted.add(tag);
909
+ }
910
+ /** Untagged hooks (the ordinary per-process settings file) always pass. */
911
+ accepts(source) {
912
+ if (!source || !source.startsWith(PREWARM_HOOK_SOURCE_PREFIX)) return true;
913
+ return this.adopted.has(source);
914
+ }
915
+ }
916
+ function decideWarmAdoption(input) {
917
+ if (input.specialCommand === "clear") return { adopt: false, reason: "clear" };
918
+ if (input.specialCommand === "compact") return { adopt: false, reason: "compact" };
919
+ if (claudeModeHash(input.predicted) !== claudeModeHash(input.actual)) return { adopt: false, reason: "mode-mismatch" };
920
+ const warmPermission = mapToClaudeMode(input.predicted.permissionMode);
921
+ const wantedPermission = mapToClaudeMode(input.actual.permissionMode);
922
+ return {
923
+ adopt: true,
924
+ ...warmPermission !== wantedPermission ? { setPermissionMode: wantedPermission } : {},
925
+ switchModel: needsModelSwitch(input.predicted.model, input.actual.model)
926
+ };
927
+ }
928
+ function formatPrewarmLine(event, detail) {
929
+ if (event === "discarded" || event === "skipped") {
930
+ return `[CLAUDE PREWARM] ${event}(${typeof detail === "string" ? detail : "unknown"})`;
931
+ }
932
+ const fields = detail && typeof detail === "object" ? Object.entries(detail).filter(([, v]) => v !== void 0).map(([k, v]) => ` ${k}=${v}`).join("") : "";
933
+ return `[CLAUDE PREWARM] ${event}${fields}`;
934
+ }
935
+ const liveLeases = /* @__PURE__ */ new Set();
936
+ let exitHookInstalled = false;
937
+ function disposeAllClaudePrewarms(reason) {
938
+ for (const lease of [...liveLeases]) lease.discard(reason);
939
+ }
940
+ function createClaudePrewarmLease(input) {
941
+ let state = "warm";
942
+ let teardown = null;
943
+ const lease = {
944
+ mode: input.mode,
945
+ hookSettingsPath: input.hookSettingsPath,
946
+ tag: input.tag,
947
+ idleMs: input.idleMs ?? CLAUDE_PREWARM_IDLE_MS,
948
+ adoptTimeoutMs: input.adoptTimeoutMs ?? CLAUDE_PREWARM_ADOPT_TIMEOUT_MS,
949
+ adopt: () => {
950
+ if (state !== "warm") return;
951
+ state = "adopted";
952
+ teardown = null;
953
+ input.gate.adopt(input.tag);
954
+ input.slot.release();
955
+ liveLeases.delete(lease);
956
+ },
957
+ discard: (reason) => {
958
+ if (state !== "warm") return;
959
+ state = "closed";
960
+ const t = teardown;
961
+ teardown = null;
962
+ liveLeases.delete(lease);
963
+ try {
964
+ t?.(reason);
965
+ } catch {
966
+ }
967
+ input.slot.release();
968
+ input.cleanupHookSettings();
969
+ input.log(formatPrewarmLine("discarded", reason));
970
+ },
971
+ setTeardown: (next) => {
972
+ if (state === "warm") teardown = next;
973
+ }
974
+ };
975
+ liveLeases.add(lease);
976
+ if (!exitHookInstalled) {
977
+ exitHookInstalled = true;
978
+ process.once("exit", () => disposeAllClaudePrewarms("exit"));
979
+ }
980
+ return lease;
981
+ }
982
+
983
+ function withTimeout(promise, ms) {
984
+ promise.catch(() => {
985
+ });
986
+ let timer;
987
+ const timeout = new Promise((_, reject) => {
988
+ timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);
989
+ });
990
+ return Promise.race([promise, timeout]).finally(() => {
991
+ if (timer) clearTimeout(timer);
992
+ });
993
+ }
994
+ const PROMPT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
995
+ function promptUuid(uuid) {
996
+ return uuid && PROMPT_UUID_RE.test(uuid) ? { uuid } : {};
997
+ }
702
998
  async function claudeRemote(opts) {
703
999
  let startFrom = opts.sessionId;
704
1000
  if (opts.sessionId && !claudeCheckSession(opts.sessionId, opts.path)) {
@@ -729,13 +1025,153 @@ async function claudeRemote(opts) {
729
1025
  process.env[key] = value;
730
1026
  });
731
1027
  }
732
- const initial = await opts.nextMessage();
1028
+ let thinking = false;
1029
+ const updateThinking = (newThinking) => {
1030
+ if (thinking !== newThinking) {
1031
+ thinking = newThinking;
1032
+ logger.debug(`[claudeRemote] Thinking state changed to: ${thinking}`);
1033
+ if (opts.onThinkingChange) {
1034
+ opts.onThinkingChange(thinking);
1035
+ }
1036
+ }
1037
+ };
1038
+ let callbackDepth = 0;
1039
+ const guardCallback = (callback) => (async (...args) => {
1040
+ callbackDepth++;
1041
+ try {
1042
+ return await callback(...args);
1043
+ } finally {
1044
+ setImmediate(() => {
1045
+ callbackDepth--;
1046
+ });
1047
+ }
1048
+ });
1049
+ let mode = opts.prewarm?.mode ?? { permissionMode: "default" };
1050
+ const buildSdkOptions = (creationMode, io) => {
1051
+ const sdkOptions = {
1052
+ cwd: opts.path,
1053
+ resume: startFrom ?? void 0,
1054
+ mcpServers: opts.mcpServers,
1055
+ permissionMode: mapToClaudeMode(creationMode.permissionMode),
1056
+ // This is only the SDK safety opt-in; permissionMode/canUseTool still
1057
+ // enforce the selected policy. It must be enabled at Query creation so
1058
+ // a later explicit live switch to bypassPermissions can succeed.
1059
+ allowDangerouslySkipPermissions: true,
1060
+ model: creationMode.model,
1061
+ fallbackModel: creationMode.fallbackModel,
1062
+ customSystemPrompt: creationMode.customSystemPrompt ? creationMode.customSystemPrompt + "\n\n" + systemPrompt : void 0,
1063
+ appendSystemPrompt: creationMode.appendSystemPrompt ? creationMode.appendSystemPrompt + "\n\n" + systemPrompt : systemPrompt,
1064
+ allowedTools: creationMode.allowedTools ? creationMode.allowedTools.concat(opts.allowedTools) : opts.allowedTools,
1065
+ additionalDirectories: opts.additionalDirectories,
1066
+ disallowedTools: creationMode.disallowedTools,
1067
+ effort: creationMode.effort,
1068
+ canCallTool: (toolName, input, options) => opts.canCallTool(toolName, input, mode, options),
1069
+ onElicitation: opts.onElicitation,
1070
+ onUserDialog: opts.onUserDialog,
1071
+ supportedDialogKinds: opts.onUserDialog ? ["refusal_fallback_prompt"] : void 0,
1072
+ abort: io.abort,
1073
+ settingsPath: io.settingsPath,
1074
+ // B-309: token-level partials. Without this the SDK emits nothing
1075
+ // between "turn started" and "entire assistant message", which is
1076
+ // exactly why the web used to show a spinner where the terminal shows
1077
+ // the model thinking out loud. The partials do NOT enter the
1078
+ // transcript; they are split off below and relayed on a side channel.
1079
+ // Only pay for them when someone is listening.
1080
+ includePartialMessages: opts.onStreamFrame ? true : void 0
1081
+ };
1082
+ if (sdkOptions.canCallTool) sdkOptions.canCallTool = guardCallback(sdkOptions.canCallTool);
1083
+ if (sdkOptions.onElicitation) sdkOptions.onElicitation = guardCallback(sdkOptions.onElicitation);
1084
+ if (sdkOptions.onUserDialog) sdkOptions.onUserDialog = guardCallback(sdkOptions.onUserDialog);
1085
+ return sdkOptions;
1086
+ };
1087
+ const discoverModels = async (q) => {
1088
+ if (typeof q.supportedModels !== "function") return void 0;
1089
+ return (await q.supportedModels()).map((model) => ({
1090
+ code: model.value,
1091
+ value: model.displayName,
1092
+ description: model.description,
1093
+ resolvedModel: model.resolvedModel,
1094
+ ...model.supportsEffort === false ? { reasoningEfforts: [] } : model.supportedEffortLevels ? { reasoningEfforts: model.supportedEffortLevels } : {}
1095
+ }));
1096
+ };
1097
+ let warm = null;
1098
+ if (opts.prewarm && startFrom) {
1099
+ opts.prewarm.discard("resume");
1100
+ } else if (opts.prewarm && opts.signal?.aborted) {
1101
+ opts.prewarm.discard("aborted");
1102
+ } else if (opts.prewarm) {
1103
+ const lease = opts.prewarm;
1104
+ const abort = new AbortController();
1105
+ const onOuterAbort = () => abort.abort();
1106
+ opts.signal?.addEventListener("abort", onOuterAbort, { once: true });
1107
+ const messages2 = new PushableAsyncIterable();
1108
+ const startedAt = Date.now();
1109
+ try {
1110
+ const response2 = query({
1111
+ prompt: messages2,
1112
+ options: buildSdkOptions(lease.mode, { settingsPath: lease.hookSettingsPath, abort: abort.signal })
1113
+ });
1114
+ const handshake = discoverModels(response2).then(
1115
+ (models2) => ({ ok: true, models: models2, at: Date.now() }),
1116
+ () => ({ ok: false, at: Date.now() })
1117
+ );
1118
+ const w = { response: response2, messages: messages2, lease, startedAt, handshake };
1119
+ lease.setTeardown(() => {
1120
+ if (w.idleTimer) clearTimeout(w.idleTimer);
1121
+ try {
1122
+ w.offExit?.();
1123
+ } catch {
1124
+ }
1125
+ opts.signal?.removeEventListener("abort", onOuterAbort);
1126
+ if (warm === w) warm = null;
1127
+ messages2.end();
1128
+ abort.abort();
1129
+ });
1130
+ w.idleTimer = setTimeout(() => lease.discard("idle"), lease.idleMs);
1131
+ w.idleTimer.unref?.();
1132
+ void handshake.then((result) => {
1133
+ if (!result.ok) {
1134
+ lease.discard("handshake-failed");
1135
+ return;
1136
+ }
1137
+ if (warm !== w) return;
1138
+ try {
1139
+ const transport = response2.transport;
1140
+ const off = transport?.onExit?.(() => {
1141
+ if (warm === w) lease.discard("exited");
1142
+ });
1143
+ if (typeof off === "function") w.offExit = off;
1144
+ } catch {
1145
+ }
1146
+ });
1147
+ warm = w;
1148
+ logger.debug(formatPrewarmLine("started", { tag: lease.tag }));
1149
+ } catch (error) {
1150
+ opts.signal?.removeEventListener("abort", onOuterAbort);
1151
+ logger.debug(`[claudeRemote] prewarm query() threw: ${error instanceof Error ? error.message : String(error)}`);
1152
+ lease.discard("spawn-failed");
1153
+ }
1154
+ }
1155
+ const discardWarm = (reason) => {
1156
+ warm?.lease.discard(reason);
1157
+ };
1158
+ let initial;
1159
+ try {
1160
+ initial = await opts.nextMessage();
1161
+ } catch (error) {
1162
+ discardWarm("error");
1163
+ throw error;
1164
+ }
733
1165
  if (!initial) {
1166
+ discardWarm("exit");
734
1167
  return;
735
1168
  }
1169
+ let timingTurn = 1;
1170
+ let timingMarks = { pushedAt: initial.enqueuedAt ?? Date.now() };
736
1171
  const initialText = typeof initial.message === "string" ? initial.message : initial.message.find((b) => b.type === "text")?.text ?? "";
737
1172
  const specialCommand = parseSpecialCommand(initialText);
738
1173
  if (specialCommand.type === "clear") {
1174
+ discardWarm("clear");
739
1175
  if (opts.onCompletionEvent) {
740
1176
  opts.onCompletionEvent("Context was reset");
741
1177
  }
@@ -753,90 +1189,95 @@ async function claudeRemote(opts) {
753
1189
  opts.onCompletionEvent("Compaction started");
754
1190
  }
755
1191
  }
756
- let mode = initial.mode;
757
- const sdkOptions = {
758
- cwd: opts.path,
759
- resume: startFrom ?? void 0,
760
- mcpServers: opts.mcpServers,
761
- permissionMode: mapToClaudeMode(initial.mode.permissionMode),
762
- // This is only the SDK safety opt-in; permissionMode/canUseTool still
763
- // enforce the selected policy. It must be enabled at Query creation so
764
- // a later explicit live switch to bypassPermissions can succeed.
765
- allowDangerouslySkipPermissions: true,
766
- model: initial.mode.model,
767
- fallbackModel: initial.mode.fallbackModel,
768
- customSystemPrompt: initial.mode.customSystemPrompt ? initial.mode.customSystemPrompt + "\n\n" + systemPrompt : void 0,
769
- appendSystemPrompt: initial.mode.appendSystemPrompt ? initial.mode.appendSystemPrompt + "\n\n" + systemPrompt : systemPrompt,
770
- allowedTools: initial.mode.allowedTools ? initial.mode.allowedTools.concat(opts.allowedTools) : opts.allowedTools,
771
- additionalDirectories: opts.additionalDirectories,
772
- disallowedTools: initial.mode.disallowedTools,
773
- effort: initial.mode.effort,
774
- canCallTool: (toolName, input, options) => opts.canCallTool(toolName, input, mode, options),
775
- onElicitation: opts.onElicitation,
776
- onUserDialog: opts.onUserDialog,
777
- supportedDialogKinds: opts.onUserDialog ? ["refusal_fallback_prompt"] : void 0,
778
- abort: opts.signal,
779
- settingsPath: opts.hookSettingsPath,
780
- // B-309: token-level partials. Without this the SDK emits nothing
781
- // between "turn started" and "entire assistant message", which is
782
- // exactly why the web used to show a spinner where the terminal shows
783
- // the model thinking out loud. The partials do NOT enter the
784
- // transcript; they are split off below and relayed on a side channel.
785
- // Only pay for them when someone is listening.
786
- includePartialMessages: opts.onStreamFrame ? true : void 0
787
- };
788
- let thinking = false;
789
- const updateThinking = (newThinking) => {
790
- if (thinking !== newThinking) {
791
- thinking = newThinking;
792
- logger.debug(`[claudeRemote] Thinking state changed to: ${thinking}`);
793
- if (opts.onThinkingChange) {
794
- opts.onThinkingChange(thinking);
1192
+ let adopted = null;
1193
+ if (warm) {
1194
+ const w = warm;
1195
+ const adoptStart = Date.now();
1196
+ const decision = decideWarmAdoption({ predicted: w.lease.mode, actual: initial.mode, specialCommand: specialCommand.type });
1197
+ let reason = decision.adopt ? null : decision.reason;
1198
+ const hs = reason ? null : await w.handshake;
1199
+ if (!reason) {
1200
+ if (warm !== w) {
1201
+ reason = "closed";
1202
+ } else if (!hs?.ok) {
1203
+ reason = "handshake-failed";
1204
+ } else if (decision.adopt) {
1205
+ try {
1206
+ if (decision.setPermissionMode) {
1207
+ await withTimeout(w.response.setPermissionMode(decision.setPermissionMode), w.lease.adoptTimeoutMs);
1208
+ } else {
1209
+ await withTimeout(w.response.mcpServerStatus(), w.lease.adoptTimeoutMs);
1210
+ }
1211
+ } catch {
1212
+ reason = decision.setPermissionMode ? "set-permission-failed" : "not-alive";
1213
+ }
1214
+ if (!reason && decision.switchModel) {
1215
+ try {
1216
+ await withTimeout(w.response.setModel(modelTarget(initial.mode.model)), w.lease.adoptTimeoutMs);
1217
+ } catch {
1218
+ reason = "set-model-failed";
1219
+ }
1220
+ }
1221
+ if (!reason && warm !== w) reason = "closed";
795
1222
  }
796
1223
  }
797
- };
798
- let messages = new PushableAsyncIterable();
799
- messages.push({
1224
+ if (reason || !hs?.ok) {
1225
+ w.lease.discard(reason ?? "handshake-failed");
1226
+ warm = null;
1227
+ } else {
1228
+ if (w.idleTimer) clearTimeout(w.idleTimer);
1229
+ try {
1230
+ w.offExit?.();
1231
+ } catch {
1232
+ }
1233
+ warm = null;
1234
+ w.lease.adopt();
1235
+ adopted = { response: w.response, messages: w.messages, models: hs.models };
1236
+ logger.debug(formatPrewarmLine("adopted", {
1237
+ warmForMs: adoptStart - w.startedAt,
1238
+ handshakeWaitMs: Math.max(0, hs.at - adoptStart),
1239
+ adoptMs: Date.now() - adoptStart,
1240
+ setPermissionMode: decision.adopt ? decision.setPermissionMode : void 0,
1241
+ setModel: decision.adopt && decision.switchModel ? true : void 0
1242
+ }));
1243
+ }
1244
+ }
1245
+ mode = initial.mode;
1246
+ const initialUserMessage = {
800
1247
  type: "user",
801
1248
  parent_tool_use_id: null,
802
1249
  origin: { kind: "human" },
1250
+ ...promptUuid(initial.uuid),
803
1251
  message: {
804
1252
  role: "user",
805
1253
  content: initial.message
806
1254
  }
807
- });
808
- let callbackDepth = 0;
809
- const guardCallback = (callback) => (async (...args) => {
810
- callbackDepth++;
811
- try {
812
- return await callback(...args);
813
- } finally {
814
- setImmediate(() => {
815
- callbackDepth--;
816
- });
817
- }
818
- });
819
- if (sdkOptions.canCallTool) sdkOptions.canCallTool = guardCallback(sdkOptions.canCallTool);
820
- if (sdkOptions.onElicitation) sdkOptions.onElicitation = guardCallback(sdkOptions.onElicitation);
821
- if (sdkOptions.onUserDialog) sdkOptions.onUserDialog = guardCallback(sdkOptions.onUserDialog);
822
- const response = query({
823
- prompt: messages,
824
- options: sdkOptions
825
- });
1255
+ };
1256
+ let messages;
1257
+ let response;
826
1258
  let models;
827
- if (typeof response.supportedModels === "function") {
1259
+ if (adopted) {
1260
+ messages = adopted.messages;
1261
+ response = adopted.response;
1262
+ models = adopted.models;
1263
+ timingMarks.handshakeAt = Date.now();
1264
+ messages.push(initialUserMessage);
1265
+ } else {
1266
+ messages = new PushableAsyncIterable();
1267
+ messages.push(initialUserMessage);
1268
+ timingMarks.spawnAt = Date.now();
1269
+ response = query({
1270
+ prompt: messages,
1271
+ options: buildSdkOptions(initial.mode, { settingsPath: opts.hookSettingsPath, abort: opts.signal })
1272
+ });
828
1273
  try {
829
- models = (await response.supportedModels()).map((model) => ({
830
- code: model.value,
831
- value: model.displayName,
832
- description: model.description,
833
- resolvedModel: model.resolvedModel,
834
- ...model.supportsEffort === false ? { reasoningEfforts: [] } : model.supportedEffortLevels ? { reasoningEfforts: model.supportedEffortLevels } : {}
835
- }));
1274
+ models = await discoverModels(response);
836
1275
  } catch (error) {
837
1276
  logger.debug("[claudeRemote] Model capability discovery unavailable");
838
1277
  }
1278
+ timingMarks.handshakeAt = Date.now();
839
1279
  }
1280
+ const prewarmTiming = adopted ? "adopted" : void 0;
840
1281
  if (opts.onQueryReady) {
841
1282
  opts.onQueryReady({
842
1283
  canControl: () => callbackDepth === 0,
@@ -888,6 +1329,11 @@ async function claudeRemote(opts) {
888
1329
  if (message.type === "assistant" && message.error) {
889
1330
  lastAssistantError = message.error;
890
1331
  }
1332
+ if (message.type === "assistant" && timingMarks.firstAssistantAt === void 0) {
1333
+ timingMarks.firstAssistantAt = Date.now();
1334
+ } else if (message.type === "system" && message.subtype === "init" && timingMarks.initAt === void 0) {
1335
+ timingMarks.initAt = Date.now();
1336
+ }
891
1337
  if (opts.onStreamFrame && message.type === "system") {
892
1338
  const subtype = message.subtype;
893
1339
  if (subtype === "thinking_tokens") {
@@ -937,6 +1383,15 @@ async function claudeRemote(opts) {
937
1383
  if (message.type === "result") {
938
1384
  updateThinking(false);
939
1385
  logger.debug("[claudeRemote] Result received");
1386
+ logger.debug(formatClaudeTimingLine({
1387
+ turn: timingTurn,
1388
+ firstTurnOfProcess: timingTurn === 1,
1389
+ marks: timingMarks,
1390
+ result: message,
1391
+ prewarm: timingTurn === 1 ? prewarmTiming : void 0
1392
+ }));
1393
+ timingTurn++;
1394
+ timingMarks = {};
940
1395
  const denials = message.permission_denials;
941
1396
  if (denials && denials.length > 0) {
942
1397
  logger.warn(`[claudeRemote] ${denials.length} tool call(s) auto-denied by Claude Code policy: ${denials.map((d) => d.tool_name ?? "?").join(", ")}`);
@@ -976,10 +1431,12 @@ async function claudeRemote(opts) {
976
1431
  }
977
1432
  mode = next.mode;
978
1433
  updateThinking(true);
1434
+ timingMarks = { pushedAt: next.enqueuedAt ?? Date.now() };
979
1435
  messages.push({
980
1436
  type: "user",
981
1437
  parent_tool_use_id: null,
982
1438
  origin: { kind: "human" },
1439
+ ...promptUuid(next.uuid),
983
1440
  message: { role: "user", content: next.message }
984
1441
  });
985
1442
  }).catch(() => {
@@ -2357,6 +2814,146 @@ class LaunchModeGate {
2357
2814
  }
2358
2815
  }
2359
2816
 
2817
+ class ConversationRewindError extends Error {
2818
+ constructor(code, message) {
2819
+ super(message);
2820
+ this.code = code;
2821
+ this.name = "ConversationRewindError";
2822
+ }
2823
+ }
2824
+ function transcriptPromptText(parsed) {
2825
+ if (parsed?.type !== "user" || parsed.isSidechain || parsed.isMeta || parsed.isSynthetic) return null;
2826
+ const content = parsed.message?.content;
2827
+ if (typeof content === "string") return content.trim() ? content : null;
2828
+ if (!Array.isArray(content) || content.some((block) => block?.type === "tool_result")) return null;
2829
+ const text = content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
2830
+ if (!text.trim()) return content.length > 0 ? "" : null;
2831
+ return text;
2832
+ }
2833
+ function isConversationEntry(parsed) {
2834
+ return !parsed?.isSidechain && !parsed?.isMeta && !parsed?.isSynthetic && (parsed?.type === "user" || parsed?.type === "assistant");
2835
+ }
2836
+ function resolveRewindTarget(entries, target) {
2837
+ for (const uuid of target.uuids) {
2838
+ const index = entries.findIndex((entry) => entry.parsed?.uuid === uuid && transcriptPromptText(entry.parsed) !== null);
2839
+ if (index >= 0) return index;
2840
+ }
2841
+ const wanted = target.text?.trim();
2842
+ if (!wanted) throw new ConversationRewindError("not-found", "This message is not in the agent conversation");
2843
+ const matches = [];
2844
+ entries.forEach((entry, index) => {
2845
+ if (transcriptPromptText(entry.parsed)?.trim() === wanted) matches.push(index);
2846
+ });
2847
+ if (matches.length === 1) return matches[0];
2848
+ if (matches.length === 0) throw new ConversationRewindError("not-found", "This message is not in the agent conversation");
2849
+ throw new ConversationRewindError("ambiguous", "Several earlier messages have the same text; cannot tell which one to change");
2850
+ }
2851
+ function rewindTranscriptLines(lines, action, target) {
2852
+ const entries = [];
2853
+ for (const line of lines) {
2854
+ if (!line) continue;
2855
+ let parsed = null;
2856
+ try {
2857
+ parsed = JSON.parse(line);
2858
+ } catch {
2859
+ }
2860
+ entries.push({ line, parsed });
2861
+ }
2862
+ const cut = resolveRewindTarget(entries, target);
2863
+ let end = entries.length;
2864
+ for (let i = cut + 1; i < entries.length; i++) {
2865
+ if (transcriptPromptText(entries[i].parsed) !== null) {
2866
+ end = i;
2867
+ break;
2868
+ }
2869
+ }
2870
+ const before = entries.slice(0, cut);
2871
+ const after = action === "delete" ? entries.slice(end) : [];
2872
+ if (![...before, ...after].some((entry) => isConversationEntry(entry.parsed))) return null;
2873
+ if (before.some((entry) => isConversationEntry(entry.parsed)) && !before.some((entry) => transcriptPromptText(entry.parsed) !== null)) {
2874
+ throw new ConversationRewindError("unsafe", "Cannot safely rewind: earlier history has no user prompt");
2875
+ }
2876
+ if (after.length === 0) return before.map((entry) => entry.line);
2877
+ const dropped = /* @__PURE__ */ new Set();
2878
+ for (const entry of entries.slice(cut, end)) {
2879
+ if (typeof entry.parsed?.uuid === "string") dropped.add(entry.parsed.uuid);
2880
+ }
2881
+ const reparentTo = typeof entries[cut].parsed?.parentUuid === "string" ? entries[cut].parsed.parentUuid : null;
2882
+ const relinked = after.map((entry) => {
2883
+ const parsed = entry.parsed;
2884
+ if (!parsed || typeof parsed !== "object") return entry.line;
2885
+ let next = parsed;
2886
+ for (const key of ["parentUuid", "logicalParentUuid"]) {
2887
+ if (typeof parsed[key] === "string" && dropped.has(parsed[key])) {
2888
+ next = next === parsed ? { ...parsed } : next;
2889
+ next[key] = reparentTo;
2890
+ }
2891
+ }
2892
+ return next === parsed ? entry.line : JSON.stringify(next);
2893
+ });
2894
+ return [...before.map((entry) => entry.line), ...relinked];
2895
+ }
2896
+ async function rewindClaudeTranscript(projectDir, claudeSessionId, action, target) {
2897
+ const source = join(projectDir, `${claudeSessionId}.jsonl`);
2898
+ let raw;
2899
+ try {
2900
+ raw = await readFile(source, "utf-8");
2901
+ } catch (error) {
2902
+ if (error.code === "ENOENT") throw new ForkSourceMissingError(source);
2903
+ throw error;
2904
+ }
2905
+ const kept = rewindTranscriptLines(raw.split("\n"), action, target);
2906
+ if (kept === null) return null;
2907
+ const newId = randomUUID();
2908
+ const destination = join(projectDir, `${newId}.jsonl`);
2909
+ const temporary = `${destination}.tmp-${process.pid}`;
2910
+ try {
2911
+ await writeFile(temporary, kept.join("\n") + "\n", { encoding: "utf-8", flag: "wx" });
2912
+ await rename(temporary, destination);
2913
+ } catch (error) {
2914
+ await unlink(temporary).catch(() => void 0);
2915
+ throw error;
2916
+ }
2917
+ return newId;
2918
+ }
2919
+
2920
+ const STOP_WAIT_MS = 8e3;
2921
+ function createConversationRewind(deps) {
2922
+ let inFlight = false;
2923
+ const rewrite = deps.rewrite ?? rewindClaudeTranscript;
2924
+ return async (input) => {
2925
+ const request = input && typeof input === "object" ? input : {};
2926
+ const action = request.action;
2927
+ const sourceId = typeof request.sourceId === "string" && request.sourceId.length > 0 && request.sourceId.length <= 200 ? request.sourceId : void 0;
2928
+ const text = typeof request.text === "string" && request.text.length <= 2e5 ? request.text : void 0;
2929
+ if (action !== "edit" && action !== "delete" || !sourceId && !text?.trim()) {
2930
+ return { ok: false, code: "invalid", error: "Invalid rewind request" };
2931
+ }
2932
+ if (inFlight) return { ok: false, code: "busy", error: "Another edit is still being applied" };
2933
+ inFlight = true;
2934
+ try {
2935
+ if (deps.isThinking()) {
2936
+ await deps.stopTurn();
2937
+ if (!await deps.waitIdle(STOP_WAIT_MS)) {
2938
+ return { ok: false, code: "running", error: "The agent did not stop; try again after it finishes" };
2939
+ }
2940
+ }
2941
+ const current = deps.claudeSessionId();
2942
+ if (!current) return { ok: false, code: "no-conversation", error: "The agent has no conversation to change yet" };
2943
+ const uuids = sourceId ? [sourceId, ...deps.aliases(sourceId)] : [];
2944
+ const next = await rewrite(deps.projectDir(), current, action, { uuids, text });
2945
+ await deps.switchConversation(next);
2946
+ return { ok: true, action, freshConversation: next === null };
2947
+ } catch (error) {
2948
+ if (error instanceof ConversationRewindError) return { ok: false, code: error.code, error: error.message };
2949
+ if (error instanceof ForkSourceMissingError) return { ok: false, code: "no-conversation", error: "The agent conversation file is missing on this machine" };
2950
+ throw error;
2951
+ } finally {
2952
+ inFlight = false;
2953
+ }
2954
+ };
2955
+ }
2956
+
2360
2957
  const ABORT_INTERRUPT_GRACE_MS = 4e3;
2361
2958
  async function claudeRemoteLauncher(session, onPermissionModeChange, onEffectivePermissionMode, onPromptFinalized) {
2362
2959
  logger.debug("[claudeRemoteLauncher] Starting remote launcher");
@@ -2451,6 +3048,31 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2451
3048
  await turnSteering.steer();
2452
3049
  });
2453
3050
  session.client.rpcHandlerManager.registerHandler("switch", doSwitch);
3051
+ const promptAliases = /* @__PURE__ */ new Map();
3052
+ const promptUuidFor = (sourceIds) => {
3053
+ const [first, ...rest] = sourceIds ?? [];
3054
+ if (first) for (const id of rest) promptAliases.set(id, first);
3055
+ if (promptAliases.size > 2e3) promptAliases.delete(promptAliases.keys().next().value);
3056
+ return first;
3057
+ };
3058
+ let silentRelaunch = false;
3059
+ session.client.rpcHandlerManager.registerHandler("conversation-rewind", createConversationRewind({
3060
+ claudeSessionId: () => session.sessionId,
3061
+ projectDir: () => getProjectPath(session.path),
3062
+ isThinking: () => session.thinking,
3063
+ stopTurn: doAbort,
3064
+ waitIdle: waitForThinkingToStop,
3065
+ aliases: (sourceId) => {
3066
+ const alias = promptAliases.get(sourceId);
3067
+ return alias ? [alias] : [];
3068
+ },
3069
+ switchConversation: async (claudeSessionId) => {
3070
+ if (claudeSessionId) session.onSessionFound(claudeSessionId);
3071
+ else session.clearSessionId();
3072
+ silentRelaunch = true;
3073
+ await abort();
3074
+ }
3075
+ }));
2454
3076
  let livePermissionModeHandler = null;
2455
3077
  let idlePermissionModeHandler = null;
2456
3078
  session.client.rpcHandlerManager.registerHandler(
@@ -2636,10 +3258,13 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2636
3258
  permissionHandler.setOnModeChanged((nextMode) => {
2637
3259
  commitPermissionMode(nextMode);
2638
3260
  });
3261
+ let launchPrewarm = null;
2639
3262
  try {
2640
3263
  const attachmentDirectory = chatAttachmentDirectory(configuration.happyHomeDir, session.client.sessionId);
2641
3264
  await ensurePrivateDirectory(attachmentDirectory);
3265
+ launchPrewarm = session.takeClaudePrewarm();
2642
3266
  const remoteResult = await claudeRemote({
3267
+ prewarm: launchPrewarm ?? void 0,
2643
3268
  sessionId: session.sessionId,
2644
3269
  path: session.path,
2645
3270
  additionalDirectories: [attachmentDirectory],
@@ -2671,7 +3296,8 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2671
3296
  }) : [];
2672
3297
  modeGate.park({
2673
3298
  message: parkedStaged.length > 0 ? appendStagedAttachmentsToPrompt(msg.message, parkedStaged) : msg.message,
2674
- mode: msg.mode
3299
+ mode: msg.mode,
3300
+ uuid: promptUuidFor(msg.sourceIds)
2675
3301
  });
2676
3302
  return null;
2677
3303
  }
@@ -2688,13 +3314,17 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2688
3314
  onPromptFinalized?.(withAttachments);
2689
3315
  return {
2690
3316
  message: withAttachments,
2691
- mode: msg.mode
3317
+ mode: msg.mode,
3318
+ enqueuedAt: msg.enqueuedAt,
3319
+ uuid: promptUuidFor(msg.sourceIds)
2692
3320
  };
2693
3321
  }
2694
3322
  onPromptFinalized?.(msg.message);
2695
3323
  return {
2696
3324
  message: msg.message,
2697
- mode: msg.mode
3325
+ mode: msg.mode,
3326
+ enqueuedAt: msg.enqueuedAt,
3327
+ uuid: promptUuidFor(msg.sourceIds)
2698
3328
  };
2699
3329
  }
2700
3330
  return null;
@@ -2795,7 +3425,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2795
3425
  signal: abortController.signal
2796
3426
  });
2797
3427
  session.consumeOneTimeFlags();
2798
- if (!exitReason && abortController.signal.aborted) {
3428
+ if (!exitReason && abortController.signal.aborted && !silentRelaunch) {
2799
3429
  session.client.closeClaudeSessionTurn("cancelled");
2800
3430
  session.client.sendSessionEvent({ type: "message", message: "Aborted by user" });
2801
3431
  }
@@ -2812,6 +3442,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2812
3442
  continue;
2813
3443
  }
2814
3444
  } finally {
3445
+ launchPrewarm?.discard("launch-ended");
2815
3446
  runtimeControls.setQuery(null);
2816
3447
  backgroundTasks.reset();
2817
3448
  session.setBackgroundTasks([]);
@@ -2837,6 +3468,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2837
3468
  streamRelay.endTurn();
2838
3469
  streamRelay.dispose();
2839
3470
  abortController = null;
3471
+ silentRelaunch = false;
2840
3472
  abortFuture?.resolve(void 0);
2841
3473
  abortFuture = null;
2842
3474
  logger.debug("[remote]: launch done");
@@ -2885,7 +3517,8 @@ async function loop(opts) {
2885
3517
  onModeChange: opts.onModeChange,
2886
3518
  onAbort: opts.onAbort,
2887
3519
  hookSettingsPath: opts.hookSettingsPath,
2888
- jsRuntime: opts.jsRuntime
3520
+ jsRuntime: opts.jsRuntime,
3521
+ claudePrewarm: opts.claudePrewarm
2889
3522
  });
2890
3523
  opts.onSessionReady?.(session);
2891
3524
  let mode = opts.startingMode ?? "local";
@@ -2893,7 +3526,7 @@ async function loop(opts) {
2893
3526
  logger.debug(`[loop] Iteration with mode: ${mode}`);
2894
3527
  switch (mode) {
2895
3528
  case "local": {
2896
- const { claudeLocalLauncher } = await import('./claudeLocalLauncher-D4T7MTqd.mjs');
3529
+ const { claudeLocalLauncher } = await import('./claudeLocalLauncher-BFfGT4eY.mjs');
2897
3530
  const result = await claudeLocalLauncher(session);
2898
3531
  switch (result.type) {
2899
3532
  case "switch":
@@ -2921,18 +3554,18 @@ async function loop(opts) {
2921
3554
  }
2922
3555
  }
2923
3556
 
2924
- function claudeModeHash(mode) {
2925
- return hashObject({
2926
- isPlan: mode.permissionMode === "plan",
2927
- fallbackModel: mode.fallbackModel,
2928
- customSystemPrompt: mode.customSystemPrompt,
2929
- appendSystemPrompt: mode.appendSystemPrompt,
2930
- allowedTools: mode.allowedTools,
2931
- disallowedTools: mode.disallowedTools,
2932
- effort: mode.effort
2933
- });
3557
+ function parseSessionHookTarget(url) {
3558
+ if (!url) return null;
3559
+ let parsed;
3560
+ try {
3561
+ parsed = new URL(url, "http://127.0.0.1");
3562
+ } catch {
3563
+ return null;
3564
+ }
3565
+ if (parsed.pathname !== "/hook/session-start") return null;
3566
+ const source = parsed.searchParams.get("source");
3567
+ return source && /^[A-Za-z0-9_.-]{1,64}$/.test(source) ? { source } : {};
2934
3568
  }
2935
-
2936
3569
  function hookRequestLogMetadata(body, data) {
2937
3570
  return {
2938
3571
  bodyBytes: Buffer.byteLength(body, "utf8"),
@@ -2954,7 +3587,8 @@ async function startHookServer(options) {
2954
3587
  const { onSessionHook } = options;
2955
3588
  return new Promise((resolve, reject) => {
2956
3589
  const server = createServer(async (req, res) => {
2957
- if (req.method === "POST" && req.url === "/hook/session-start") {
3590
+ const hookMeta = req.method === "POST" ? parseSessionHookTarget(req.url) : null;
3591
+ if (hookMeta) {
2958
3592
  const timeout = setTimeout(() => {
2959
3593
  if (!res.headersSent) {
2960
3594
  logger.debug("[hookServer] Request timeout");
@@ -2978,7 +3612,7 @@ async function startHookServer(options) {
2978
3612
  const sessionId = data?.session_id || data?.sessionId;
2979
3613
  if (sessionId && data) {
2980
3614
  logger.debug(`[hookServer] Session hook received session ID: ${sessionId}`);
2981
- onSessionHook(sessionId, data);
3615
+ onSessionHook(sessionId, data, hookMeta);
2982
3616
  } else {
2983
3617
  logger.debug("[hookServer] Session hook received but no session_id found in data");
2984
3618
  }
@@ -3017,13 +3651,16 @@ async function startHookServer(options) {
3017
3651
  });
3018
3652
  }
3019
3653
 
3020
- function generateHookSettingsFile(port) {
3654
+ function generateHookSettingsFile(port, source) {
3021
3655
  const hooksDir = join(configuration.happyHomeDir, "tmp", "hooks");
3022
3656
  mkdirSync(hooksDir, { recursive: true });
3023
- const filename = `session-hook-${process.pid}.json`;
3657
+ const filename = source ? `session-hook-${process.pid}-${source}.json` : `session-hook-${process.pid}.json`;
3024
3658
  const filepath = join(hooksDir, filename);
3025
3659
  const forwarderScript = resolve(projectPath(), "scripts", "session_hook_forwarder.cjs");
3026
- const hookCommand = `node "${forwarderScript}" ${port}`;
3660
+ if (source !== void 0 && !/^[A-Za-z0-9_.-]{1,64}$/.test(source)) {
3661
+ throw new Error("invalid hook source tag");
3662
+ }
3663
+ const hookCommand = source ? `node "${forwarderScript}" ${port} ${source}` : `node "${forwarderScript}" ${port}`;
3027
3664
  const settings = {
3028
3665
  hooks: {
3029
3666
  SessionStart: [
@@ -3621,4 +4258,49 @@ function withAssistantDenylist(current, isAssistant) {
3621
4258
  return [...merged];
3622
4259
  }
3623
4260
 
3624
- export { BoardAnalyzer, FileRateLimiter, SessionExitGate, claudeCheckSession, claudeModeHash, cleanupHookSettingsFile, contentLogMetadata, exitIntentFromArchiveFlag, generateHookSettingsFile, getProjectPath, loop, parseSpecialCommand, registerSideQuestionHandler, settleSessionOnExit, startHookServer, withAssistantDenylist, writeSideQuestionSettingsFile };
4261
+ const VALID_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
4262
+ const has = (meta, key) => !!meta && Object.prototype.hasOwnProperty.call(meta, key);
4263
+ function resolveMessageMode(current, meta, ctx) {
4264
+ const next = { ...current };
4265
+ const result = {};
4266
+ let permissionMode = current.permissionMode;
4267
+ if (!ctx.staleModeSnapshot && meta?.permissionMode) {
4268
+ permissionMode = resolveRemoteClaudePermissionMode(current.permissionMode, meta.permissionMode, ctx.sandboxEnabled);
4269
+ next.permissionMode = permissionMode;
4270
+ result.publishPermissionMode = permissionMode;
4271
+ }
4272
+ if (has(meta, "model")) next.model = meta.model || void 0;
4273
+ if (has(meta, "customSystemPrompt")) next.customSystemPrompt = meta.customSystemPrompt || void 0;
4274
+ if (has(meta, "fallbackModel")) next.fallbackModel = meta.fallbackModel || void 0;
4275
+ if (has(meta, "appendSystemPrompt")) next.appendSystemPrompt = meta.appendSystemPrompt || void 0;
4276
+ if (has(meta, "allowedTools")) next.allowedTools = meta.allowedTools || void 0;
4277
+ if (has(meta, "disallowedTools")) {
4278
+ next.disallowedTools = withAssistantDenylist(meta.disallowedTools || void 0, ctx.isAssistantVariant);
4279
+ }
4280
+ if (has(meta, "effort")) {
4281
+ const incoming = meta.effort;
4282
+ if (incoming === null || incoming === void 0) {
4283
+ next.effort = void 0;
4284
+ } else if (typeof incoming === "string" && VALID_EFFORTS.has(incoming)) {
4285
+ next.effort = incoming;
4286
+ } else {
4287
+ result.ignoredEffort = String(incoming);
4288
+ }
4289
+ }
4290
+ return {
4291
+ ...result,
4292
+ next,
4293
+ mode: {
4294
+ permissionMode: permissionMode || "default",
4295
+ model: next.model,
4296
+ fallbackModel: next.fallbackModel,
4297
+ customSystemPrompt: next.customSystemPrompt,
4298
+ appendSystemPrompt: next.appendSystemPrompt,
4299
+ allowedTools: next.allowedTools,
4300
+ disallowedTools: next.disallowedTools,
4301
+ effort: next.effort
4302
+ }
4303
+ };
4304
+ }
4305
+
4306
+ export { BoardAnalyzer, FileRateLimiter, PrewarmHookGate, SessionExitGate, acquirePrewarmSlot, claudeCheckSession, claudeModeHash, claudePrewarmEligibility, claudePrewarmSessionEligibility, cleanupHookSettingsFile, contentLogMetadata, createClaudePrewarmLease, disposeAllClaudePrewarms, evaluatePrewarmPrediction, exitIntentFromArchiveFlag, formatPrewarmLine, generateHookSettingsFile, getProjectPath, loop, parseSpecialCommand, prewarmCacheFile, prewarmPredictionMeta, prewarmSlotDir, readPrewarmCache, registerSideQuestionHandler, resolveMessageMode, settleSessionOnExit, startHookServer, withAssistantDenylist, writePrewarmCache, writeSideQuestionSettingsFile };