very-happy-cli 0.2.160 → 0.2.161

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 (162) hide show
  1. package/dist/{AcpBackend-mzv8Dhba.mjs → AcpBackend-6U6pQgx1.mjs} +3 -3
  2. package/dist/{AcpBackend-Bm2rdke8.cjs → AcpBackend-Ba98QDif.cjs} +3 -3
  3. package/dist/{BasePermissionHandler-CFHAxumV.cjs → BasePermissionHandler-2R0uH2CI.cjs} +1 -1
  4. package/dist/{BasePermissionHandler-m3WVholL.mjs → BasePermissionHandler-BvvhBTja.mjs} +1 -1
  5. package/dist/{BaseReasoningProcessor-CYrKBXGm.mjs → BaseReasoningProcessor-DyhO1uoU.mjs} +1 -1
  6. package/dist/{BaseReasoningProcessor-BFa9_rZA.cjs → BaseReasoningProcessor-fsVNmCEF.cjs} +1 -1
  7. package/dist/{agentHome-Dj1C43aJ.mjs → agentHome-Ck1i1TQB.mjs} +4 -4
  8. package/dist/{agentHome-CboCeKWq.cjs → agentHome-DvNnGphb.cjs} +4 -4
  9. package/dist/{api-CgundNNX.cjs → api-B2vLMS-z.cjs} +8 -8
  10. package/dist/{api-CLMUr-SX.mjs → api-SffjRQ1b.mjs} +8 -8
  11. package/dist/{apiMachine-B9dLW_p5.mjs → apiMachine-BpW9t8MO.mjs} +11 -11
  12. package/dist/{apiMachine-Bn8482VM.cjs → apiMachine-gFA54X7y.cjs} +11 -11
  13. package/dist/{assistantTools-BTqPnlFl.cjs → assistantTools-BfA0R3_m.cjs} +6 -6
  14. package/dist/{assistantTools-D6tuc6o4.mjs → assistantTools-CgiTbnWb.mjs} +6 -6
  15. package/dist/{attachmentContent-_kxRznNf.mjs → attachmentContent-CSGxr71v.mjs} +1 -1
  16. package/dist/{attachmentContent-DpiTJZb4.cjs → attachmentContent-DMMZzevR.cjs} +1 -1
  17. package/dist/{auth-ba4WMYrN.cjs → auth-Bt4ceBBy.cjs} +5 -5
  18. package/dist/{auth-Cwutmdsa.mjs → auth-CYHjQ8oQ.mjs} +5 -5
  19. package/dist/{auth-PYHZQNPp.mjs → auth-DdyVZSo6.mjs} +8 -8
  20. package/dist/{auth-BDOVh5h5.cjs → auth-x4CEoysH.cjs} +8 -8
  21. package/dist/{auto-BOX_fgmR.cjs → auto-BDXDUnjB.cjs} +12 -12
  22. package/dist/{auto-BXu14yP_.mjs → auto-CXLlRi3a.mjs} +12 -12
  23. package/dist/{bootstrap-hxXPCFMx.mjs → bootstrap-BNQq05jR.mjs} +2 -2
  24. package/dist/{bootstrap-B5G1TijC.cjs → bootstrap-CkgIq8N3.cjs} +2 -2
  25. package/dist/{browser-jh2P0UVZ.mjs → browser-9pyhQr1R.mjs} +1 -1
  26. package/dist/{browser-Y6Bl8L_O.cjs → browser-BDOlKWYe.cjs} +1 -1
  27. package/dist/{claudeLocal-CRGG4Pea.cjs → claudeLocal-BDuGFnjJ.cjs} +4 -4
  28. package/dist/{claudeLocal-BaY4fZKi.mjs → claudeLocal-CQa8qnwq.mjs} +4 -4
  29. package/dist/{claudeLocalLauncher-DyEbVzUN.cjs → claudeLocalLauncher-Bd5I2-j6.cjs} +9 -9
  30. package/dist/{claudeLocalLauncher-D4T7MTqd.mjs → claudeLocalLauncher-CDqQI9ak.mjs} +9 -9
  31. package/dist/{client-CeGKqqcJ.mjs → client-Brn-UwVk.mjs} +3 -3
  32. package/dist/{client-ClR0K8zX.mjs → client-CXb1VJui.mjs} +2 -2
  33. package/dist/{client-3Ac-O4pw.cjs → client-C_axwRry.cjs} +3 -3
  34. package/dist/{client-BPNbmtvO.cjs → client-DOBWJL3u.cjs} +2 -2
  35. package/dist/codex/happyMcpStdioBridge.cjs +14 -14
  36. package/dist/codex/happyMcpStdioBridge.mjs +14 -14
  37. package/dist/{codexAppServerClient-q2FjnRcK.cjs → codexAppServerClient-ZzhUTWPo.cjs} +2 -2
  38. package/dist/{codexAppServerClient-I3teiV-N.mjs → codexAppServerClient-oeQKD6Qf.mjs} +2 -2
  39. package/dist/{codexCommand-4zcccIoq.cjs → codexCommand-C7H0NpBe.cjs} +35 -35
  40. package/dist/{codexCommand-Cc9gcbdX.mjs → codexCommand-CJ82bl3l.mjs} +35 -35
  41. package/dist/{codexThreadFork-slzAseLx.mjs → codexThreadFork-BCaEMBiM.mjs} +1 -1
  42. package/dist/{codexThreadFork-BH9IZPqD.cjs → codexThreadFork-BZqF-9bX.cjs} +1 -1
  43. package/dist/{config-CTx_jb-C.mjs → config-BglnktIo.mjs} +3 -3
  44. package/dist/{config-Ci9Kpgwa.cjs → config-CYpL1Ahd.cjs} +3 -3
  45. package/dist/{configuration-RfeNjFmd.mjs → configuration-DoWFJUPm.mjs} +1 -1
  46. package/dist/{configuration-BSYpcPwE.cjs → configuration-v9f8LM9T.cjs} +1 -1
  47. package/dist/{connect-BpOdzNbE.mjs → connect-hgcNjyAB.mjs} +11 -11
  48. package/dist/{connect-C2CiOUFs.cjs → connect-yEvjdRvx.cjs} +11 -11
  49. package/dist/{context-DsDr6E2t.cjs → context-B45vMtSZ.cjs} +1 -1
  50. package/dist/{context-H0Bcb9S_.mjs → context-C9GHPLEm.mjs} +1 -1
  51. package/dist/{controlClient-BGbDcgWS.mjs → controlClient-DjoKTtgK.mjs} +3 -3
  52. package/dist/{controlClient-Bcwv3D21.cjs → controlClient-fEHE85bH.cjs} +3 -3
  53. package/dist/{createSessionMetadata-BJxEnM-o.cjs → createSessionMetadata-BRLB9CNO.cjs} +2 -2
  54. package/dist/{createSessionMetadata-CVVkGjxV.mjs → createSessionMetadata-mIeehclp.mjs} +2 -2
  55. package/dist/{deterministicJson-C5CYTVpm.cjs → deterministicJson-DKDP_mM-.cjs} +8 -8
  56. package/dist/{deterministicJson-CzkFJWw1.mjs → deterministicJson-p835AdAU.mjs} +8 -8
  57. package/dist/{doctor-DOvByjbk.mjs → doctor-BeUuQXFs.mjs} +6 -6
  58. package/dist/{doctor-D2YJ-Yn2.cjs → doctor-hVNnSZJn.cjs} +6 -6
  59. package/dist/{ensureDaemonRunning-Dqa2pOjY.mjs → ensureDaemonRunning-CO7d1gOC.mjs} +5 -5
  60. package/dist/{ensureDaemonRunning-BCwNrMyv.cjs → ensureDaemonRunning-Dg9aZfjs.cjs} +5 -5
  61. package/dist/{handleResumeCommand-BvXYf5El.mjs → handleResumeCommand-BiTPK0qd.mjs} +5 -5
  62. package/dist/{handleResumeCommand-C2cKchpX.cjs → handleResumeCommand-DnVmy7a3.cjs} +5 -5
  63. package/dist/{index-DMZqWwsP.mjs → index-CAFwetLk.mjs} +33 -33
  64. package/dist/{index-DVkMGPyS.mjs → index-Cqx-Raq6.mjs} +3 -3
  65. package/dist/{index-B6pAdvJx.cjs → index-LIn6nCcq.cjs} +32 -32
  66. package/dist/{index-Bu5Zi_a9.cjs → index-Pf2cV9cn.cjs} +4 -4
  67. package/dist/{index-DKLU_fD0.mjs → index-SPGPenK5.mjs} +4 -4
  68. package/dist/{index-DDeHXrIX.cjs → index-gNTWtSKN.cjs} +3 -3
  69. package/dist/index.cjs +2 -2
  70. package/dist/index.mjs +2 -2
  71. package/dist/{install-BgoC2e_2.cjs → install-Bu85vlHD.cjs} +3 -3
  72. package/dist/{install-CFY3tYtJ.mjs → install-ClUUl3yp.mjs} +3 -3
  73. package/dist/lib.cjs +10 -10
  74. package/dist/lib.mjs +10 -10
  75. package/dist/{lidState-CeEt442o.mjs → lidState-C2dI0FAN.mjs} +1 -1
  76. package/dist/{lidState-BeDfxxEX.cjs → lidState-KHHu1015.cjs} +1 -1
  77. package/dist/{localHappyAgentAuth-D6rFhaoc.cjs → localHappyAgentAuth-BXiHPXdC.cjs} +1 -1
  78. package/dist/{localHappyAgentAuth-C-HQyiv6.mjs → localHappyAgentAuth-DCsetiyG.mjs} +1 -1
  79. package/dist/{logger-CS0mw3fa.cjs → logger-Bc8-u1w0.cjs} +2 -2
  80. package/dist/{logger-6HYUEXNk.mjs → logger-fXoNzNuB.mjs} +2 -2
  81. package/dist/{machineMetadata-oOzBfDfs.mjs → machineMetadata-BHjehxJY.mjs} +3 -3
  82. package/dist/{machineMetadata-DyDbODYd.cjs → machineMetadata-BibDMFVx.cjs} +3 -3
  83. package/dist/{main-BwNIAI1A.mjs → main-BktM4A1K.mjs} +44 -44
  84. package/dist/{main-BkxJP6JQ.cjs → main-CLaX-t-b.cjs} +44 -44
  85. package/dist/{mcp-B1A4I3TK.mjs → mcp-CeUd9KDr.mjs} +14 -14
  86. package/dist/{mcp-Hszd7qpS.cjs → mcp-GBfE70nk.cjs} +14 -14
  87. package/dist/{package-BwMrPwqH.cjs → package-Cl5BEP5x.cjs} +7 -7
  88. package/dist/{package-BhaKlnxz.mjs → package-uOg9SCKi.mjs} +7 -7
  89. package/dist/{peerTools-BvKtHXj8.cjs → peerTools-CIeiy9wn.cjs} +4 -4
  90. package/dist/{peerTools-BTqw97Gb.mjs → peerTools-Dz3_7Bkk.mjs} +4 -4
  91. package/dist/{permissionMode-Cr5JCbB7.mjs → permissionMode-CH4FoZfU.mjs} +1 -1
  92. package/dist/{persistence-yKTH3-5X.cjs → persistence-CBNMzSa8.cjs} +3 -3
  93. package/dist/{persistence-HGW_ZVs9.mjs → persistence-oET5n61f.mjs} +3 -3
  94. package/dist/{piRunnerArgs-BNloJCCg.cjs → piRunnerArgs-Bf7RUF__.cjs} +1 -1
  95. package/dist/{piRunnerArgs-BuX1UFWy.mjs → piRunnerArgs-Dk1ncwHF.mjs} +1 -1
  96. package/dist/{piRuntime-C_H7MJCM.cjs → piRuntime-B_6TE9Eb.cjs} +1 -1
  97. package/dist/{piRuntime-CSGkwCKQ.mjs → piRuntime-BwRdhK_N.mjs} +1 -1
  98. package/dist/{piTerminal-C8a1OrNQ.mjs → piTerminal-Bap3R8Re.mjs} +16 -16
  99. package/dist/{piTerminal-DNCZ6yTM.cjs → piTerminal-CagmEoez.cjs} +16 -16
  100. package/dist/{registerCommonHandlers-DskirN-_.cjs → registerCommonHandlers-BjSV2Skz.cjs} +2 -2
  101. package/dist/{registerCommonHandlers-Ccvsz7Ou.mjs → registerCommonHandlers-CrgvJsOy.mjs} +2 -2
  102. package/dist/{registerKillSessionHandler-DZeLXWcB.cjs → registerKillSessionHandler-6TC2S3lf.cjs} +13 -8
  103. package/dist/{registerKillSessionHandler-GYvHdipX.mjs → registerKillSessionHandler-Cbc8Rgtd.mjs} +13 -8
  104. package/dist/{remoteSessionClient-B0LraSB9.cjs → remoteSessionClient-BNx78NLb.cjs} +5 -5
  105. package/dist/{remoteSessionClient-CYnXRynE.mjs → remoteSessionClient-CO7qOjR0.mjs} +5 -5
  106. package/dist/{run-D3ifupU3.mjs → run-2l5vSt2h.mjs} +32 -28
  107. package/dist/{run-CJzxoHVh.cjs → run-D5xAEWSB.cjs} +33 -29
  108. package/dist/{runClaude-Da3RirzE.cjs → runClaude-BX6ixjtW.cjs} +182 -138
  109. package/dist/{runClaude-Ba01qT-u.mjs → runClaude-BgN6xo05.mjs} +183 -139
  110. package/dist/{runClaudeDeps-DOzEBMls.cjs → runClaudeDeps-Bp20MScE.cjs} +638 -119
  111. package/dist/{runClaudeDeps-DOt79DjU.mjs → runClaudeDeps-COBBhD9o.mjs} +628 -123
  112. package/dist/{runGemini-DSr9LIxL.cjs → runGemini-CSMPkt0p.cjs} +30 -30
  113. package/dist/{runGemini-BGeTz57m.mjs → runGemini-Cc2jq-uh.mjs} +30 -30
  114. package/dist/{runOpenClaw-CogSedIc.mjs → runOpenClaw-CIlCwdW2.mjs} +15 -15
  115. package/dist/{runOpenClaw-B1mTxN35.cjs → runOpenClaw-YBwua4Rf.cjs} +15 -15
  116. package/dist/{sandbox-CG7IyDTU.cjs → sandbox-C4gXLP1p.cjs} +4 -4
  117. package/dist/{sandbox-DTe6d54R.mjs → sandbox-CNkwOtHI.mjs} +4 -4
  118. package/dist/{selfCheck-Dc-mNSLb.cjs → selfCheck-CU8xSDsl.cjs} +34 -34
  119. package/dist/selfCheck-hJ6PCh7v.mjs +84 -0
  120. package/dist/{send-CBXJKc6O.mjs → send-CGHcKZ-w.mjs} +8 -8
  121. package/dist/{send-B7J_eaG3.cjs → send-DY9Nr-x4.cjs} +8 -8
  122. package/dist/{server-CZTP_iOn.cjs → server-II51Mimf.cjs} +6 -6
  123. package/dist/{server-DcVBv04l.mjs → server-m2b5pamZ.mjs} +4 -4
  124. package/dist/{sessionLock-Bys1PNSo.cjs → sessionLock-7ePAYDOg.cjs} +2 -2
  125. package/dist/{sessionLock-D-EG2CfG.mjs → sessionLock-Cw6FmbMv.mjs} +2 -2
  126. package/dist/{sessionModeFile-RSm0NQkH.cjs → sessionModeFile-BVCV5f7E.cjs} +1 -1
  127. package/dist/{sessionModeFile-DMl3dw3r.mjs → sessionModeFile-CDmDyRkD.mjs} +1 -1
  128. package/dist/{sessionOps-SuHW8Dfe.mjs → sessionOps-C_f1wKOr.mjs} +4 -4
  129. package/dist/{sessionOps-Dn8z85h_.cjs → sessionOps-DJdExmBy.cjs} +4 -4
  130. package/dist/{sessionScanner-CX3onqXh.mjs → sessionScanner-CaAHLyNV.mjs} +3 -3
  131. package/dist/{sessionScanner-BOrGjMXL.cjs → sessionScanner-D1goKdd3.cjs} +3 -3
  132. package/dist/{sessions-D74aJbhL.cjs → sessions-DGs6ALNo.cjs} +9 -9
  133. package/dist/{sessions-BEG_HTSi.mjs → sessions-DnvmWUel.mjs} +9 -9
  134. package/dist/{setupOfflineReconnection-DpvWTt69.mjs → setupOfflineReconnection-CVViqZft.mjs} +2 -2
  135. package/dist/{setupOfflineReconnection-ByElY60m.cjs → setupOfflineReconnection-R7XuR_sj.cjs} +2 -2
  136. package/dist/{spawn-DgR0RKRs.cjs → spawn-CXTH7qg1.cjs} +19 -11
  137. package/dist/{spawn-B7yro0b2.mjs → spawn-DNYSvFtC.mjs} +19 -12
  138. package/dist/{spawnHappyCLI-DBhwZT7O.cjs → spawnHappyCLI-BhYj84rv.cjs} +1 -1
  139. package/dist/{spawnHappyCLI-B2H5s4B_.mjs → spawnHappyCLI-cqWQbwsb.mjs} +1 -1
  140. package/dist/{startFileWatcher-B4VsLgjD.cjs → startFileWatcher-CKKx-8ZO.cjs} +1 -1
  141. package/dist/{startFileWatcher-DErVWZE4.mjs → startFileWatcher-CdegMbJj.mjs} +1 -1
  142. package/dist/{sync-DccIKZ5_.cjs → sync-CVk4QERO.cjs} +1 -1
  143. package/dist/{sync-C8gyQFMc.mjs → sync-CxD-w_40.mjs} +1 -1
  144. package/dist/{systemPrompt-BwTPWfMu.cjs → systemPrompt-BygJcmbi.cjs} +1 -1
  145. package/dist/{systemPrompt-DX521JJz.mjs → systemPrompt-DmtypUc7.mjs} +1 -1
  146. package/dist/{teams-C32F1hqd.mjs → teams-BOC6nIPL.mjs} +6 -6
  147. package/dist/{teams-Dl8g8kTI.cjs → teams-BsvpOBfn.cjs} +6 -6
  148. package/dist/{time-D0XyHOid.mjs → time-BZNdNiIV.mjs} +1 -1
  149. package/dist/{time-DCuTyook.cjs → time-CNRE4ChD.cjs} +1 -1
  150. package/dist/{titleGenerator-ChzMJsyg.cjs → titleGenerator-CFRhU-xq.cjs} +2 -2
  151. package/dist/{titleGenerator-CQ8CoGhg.mjs → titleGenerator-ox6ptCsp.mjs} +1 -1
  152. package/dist/{todo-BvUOTa8G.cjs → todo-B-KB7oAB.cjs} +5 -5
  153. package/dist/{todo-CZ6cFrrn.mjs → todo-C5KBVbUw.mjs} +5 -5
  154. package/dist/{tools-BZtHF_qQ.mjs → tools-BZabNIid.mjs} +1 -1
  155. package/dist/{tools-DkluDcG4.mjs → tools-BmQQUATY.mjs} +1 -1
  156. package/dist/{tools-DQj0AeLk.cjs → tools-CsSovyNp.cjs} +1 -1
  157. package/dist/{tools-Be3AoAwu.cjs → tools-lMDra4Pv.cjs} +1 -1
  158. package/dist/{uninstall-BkLzaJ6I.mjs → uninstall-B1WLtC1d.mjs} +3 -3
  159. package/dist/{uninstall-x-TUtf5H.cjs → uninstall-fvA5aWpU.cjs} +3 -3
  160. package/package.json +7 -7
  161. package/scripts/session_hook_forwarder.cjs +9 -2
  162. package/dist/selfCheck-C9mVrQwf.mjs +0 -84
@@ -1,13 +1,13 @@
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-fXoNzNuB.mjs';
2
+ import { k as notifyDaemonSessionEvent, m as notifyDaemonClaudeAuthFailed } from './controlClient-DjoKTtgK.mjs';
3
+ export { g as reportSessionEditToDaemon } from './controlClient-DjoKTtgK.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-DmtypUc7.mjs';
11
11
  import { join, resolve, dirname } from 'node:path';
12
12
  import { p as parseSpecialCommand } from './specialCommands-BSqhYg5X.mjs';
13
13
  import 'axios';
@@ -15,34 +15,34 @@ import 'node:events';
15
15
  import 'socket.io-client';
16
16
  import { A as AsyncLock } from './attachmentLimits-yba1uHFO.mjs';
17
17
  import 'tweetnacl';
18
- import { c as configuration, e as ensurePrivateDirectory } from './configuration-RfeNjFmd.mjs';
18
+ import { c as configuration, e as ensurePrivateDirectory } from './configuration-DoWFJUPm.mjs';
19
19
  import './index-qdS1friN.mjs';
20
- import './registerCommonHandlers-Ccvsz7Ou.mjs';
20
+ import './registerCommonHandlers-CrgvJsOy.mjs';
21
21
  import 'os';
22
22
  import 'child_process';
23
23
  import '@paralleldrive/cuid2';
24
- import './persistence-HGW_ZVs9.mjs';
24
+ import './persistence-oET5n61f.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-BZNdNiIV.mjs';
31
+ import { mkdirSync, writeFileSync, readFileSync, unlinkSync, renameSync, statSync, existsSync } from 'node:fs';
32
+ import { h as hashObject, i as isSelfReportFresh } from './deterministicJson-p835AdAU.mjs';
33
+ export { c as createSelfReportState, s as startHappyServer } from './deterministicJson-p835AdAU.mjs';
34
+ import { F as Future } from './sessionScanner-CaAHLyNV.mjs';
35
+ export { c as createSessionScanner } from './sessionScanner-CaAHLyNV.mjs';
33
36
  import { execSync } from 'node:child_process';
34
37
  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';
38
+ import { C as CLAUDE_ATTACHMENT_KINDS, c as chatAttachmentDirectory, a as stageClaudeAttachments, b as appendStagedAttachmentsToPrompt } from './attachmentContent-CSGxr71v.mjs';
39
+ export { M as MessageQueue2, r as registerKillSessionHandler } from './registerKillSessionHandler-Cbc8Rgtd.mjs';
40
+ export { g as getEnvironmentInfo } from './doctor-BeUuQXFs.mjs';
40
41
  import { createServer } from 'node:http';
41
42
  export { E as EditReportThrottle, b as extractClaudeEditPaths } from './editPaths-BVfS_FFL.mjs';
42
- import { existsSync, unlinkSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
43
43
  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';
44
+ import { r as resolveClaudeBinary, a as runClaudeOneShot } from './titleGenerator-ox6ptCsp.mjs';
45
+ export { T as TitleGenerator } from './titleGenerator-ox6ptCsp.mjs';
46
46
  import { B as BOARD_ANALYZER_PROMPT_PREFIX } from './oneShotPrompts-DT4p6RNd.mjs';
47
47
  import 'node:util';
48
48
  import './smallFastModel-BLNgMrwR.mjs';
@@ -50,34 +50,34 @@ import './trimIdent-BAjmv_TG.mjs';
50
50
  import 'node:os';
51
51
  import './agentGuidance-CQuNlNx8.mjs';
52
52
  import 'zod';
53
- import './package-BhaKlnxz.mjs';
53
+ import './package-uOg9SCKi.mjs';
54
54
  import 'node:fs/promises';
55
55
  import 'util';
56
56
  import 'crypto';
57
57
  import 'path';
58
58
  import 'cross-spawn';
59
59
  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';
60
+ import './client-Brn-UwVk.mjs';
61
+ import './context-C9GHPLEm.mjs';
62
+ import './sessionOps-C_f1wKOr.mjs';
65
63
  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';
64
+ import './tools-BmQQUATY.mjs';
65
+ import './tools-BZabNIid.mjs';
66
+ import './client-CXb1VJui.mjs';
67
+ import './peerTools-Dz3_7Bkk.mjs';
70
68
  import './ids-DU6sLx4C.mjs';
71
- import './remoteSessionClient-CYnXRynE.mjs';
69
+ import './remoteSessionClient-CO7qOjR0.mjs';
72
70
  import '@modelcontextprotocol/sdk/server/mcp.js';
73
71
  import '@modelcontextprotocol/sdk/server/streamableHttp.js';
74
72
  import './limits-DIo7rEBR.mjs';
75
- import './assistantTools-D6tuc6o4.mjs';
73
+ import './assistantTools-CgiTbnWb.mjs';
76
74
  import './terminals-Dlg0x3nY.mjs';
77
75
  import './terminalTitleSuggest-CeKsX_V6.mjs';
78
76
  import './tmuxSocket-B8Yx20sT.mjs';
79
- import './bootstrap-hxXPCFMx.mjs';
77
+ import './bootstrap-BNQq05jR.mjs';
80
78
  import './previewPath-CXN4Vag0.mjs';
79
+ import './sync-CxD-w_40.mjs';
80
+ import './startFileWatcher-CdegMbJj.mjs';
81
81
  import './doctor-BPkYWSuq.mjs';
82
82
  import 'ps-list';
83
83
  import './doctorReadiness-C208SMN9.mjs';
@@ -145,6 +145,20 @@ class Session {
145
145
  steerHandler = null;
146
146
  /** B-482: the live remote Query's in-process side-question entry; null between Queries and in local mode. */
147
147
  sideQuestionLive = null;
148
+ /** B-515: consumed by the first remote launch; later launches never prewarm. */
149
+ claudePrewarm = null;
150
+ /** Hand the prewarm lease to the first remote launch — once per process. */
151
+ takeClaudePrewarm = () => {
152
+ const factory = this.claudePrewarm;
153
+ this.claudePrewarm = null;
154
+ if (!factory || this.sessionId) return null;
155
+ try {
156
+ return factory();
157
+ } catch (error) {
158
+ logger.debug("[Session] Claude prewarm setup failed:", error);
159
+ return null;
160
+ }
161
+ };
148
162
  /** Callbacks to be notified when session ID is found/changed */
149
163
  sessionFoundCallbacks = [];
150
164
  /** Keep alive interval reference for cleanup */
@@ -165,6 +179,7 @@ class Session {
165
179
  this._onAbort = opts.onAbort;
166
180
  this.hookSettingsPath = opts.hookSettingsPath;
167
181
  this.jsRuntime = opts.jsRuntime ?? "node";
182
+ this.claudePrewarm = opts.claudePrewarm ?? null;
168
183
  try {
169
184
  this.notificationProducer = opts.api.notificationProducer(
170
185
  opts.client.sessionId,
@@ -699,6 +714,281 @@ function modelSwitchFailureNotice(model, error) {
699
714
  return `\u26A0\uFE0F Could not switch to model "${name}" \u2014 continuing with the previous model. ${detail}`;
700
715
  }
701
716
 
717
+ const SDK_TIMING_FIELDS = ["ttft_ms", "time_to_request_ms", "time_to_request_from_spawn_ms"];
718
+ function delta(from, to) {
719
+ if (typeof from !== "number" || typeof to !== "number" || to < from) return "-";
720
+ return `${Math.round(to - from)}ms`;
721
+ }
722
+ function sdkField(result, key) {
723
+ if (!result || typeof result !== "object") return "-";
724
+ const value = result[key];
725
+ return typeof value === "number" && Number.isFinite(value) ? `${Math.round(value)}` : "-";
726
+ }
727
+ function formatClaudeTimingLine(input) {
728
+ const m = input.marks;
729
+ const sdk = SDK_TIMING_FIELDS.map((key) => `${key}=${sdkField(input.result, key)}`).join(",");
730
+ return [
731
+ "[CLAUDE TIMING]",
732
+ `turn=${input.turn}`,
733
+ `firstTurnOfProcess=${input.firstTurnOfProcess}`,
734
+ `pushed\u2192spawn=${delta(m.pushedAt, m.spawnAt)}`,
735
+ `spawn\u2192handshake=${delta(m.spawnAt, m.handshakeAt)}`,
736
+ `handshake\u2192init=${delta(m.handshakeAt, m.initAt)}`,
737
+ `init\u2192firstAssistant=${delta(m.initAt, m.firstAssistantAt)}`,
738
+ `pushed\u2192firstAssistant=${delta(m.pushedAt, m.firstAssistantAt)}`,
739
+ `sdk{${sdk}}`,
740
+ ...input.prewarm ? [`prewarm=${input.prewarm}`] : []
741
+ ].join(" ");
742
+ }
743
+
744
+ function claudeModeHash(mode) {
745
+ return hashObject({
746
+ isPlan: mode.permissionMode === "plan",
747
+ fallbackModel: mode.fallbackModel,
748
+ customSystemPrompt: mode.customSystemPrompt,
749
+ appendSystemPrompt: mode.appendSystemPrompt,
750
+ allowedTools: mode.allowedTools,
751
+ disallowedTools: mode.disallowedTools,
752
+ effort: mode.effort
753
+ });
754
+ }
755
+
756
+ const CLAUDE_PREWARM_IDLE_MS = 15 * 60 * 1e3;
757
+ const CLAUDE_PREWARM_MAX_CONCURRENT = 2;
758
+ const CLAUDE_PREWARM_ADOPT_TIMEOUT_MS = 5e3;
759
+ const PREWARM_HOOK_SOURCE_PREFIX = "prewarm-";
760
+ const BAD_AUTH_STATUSES = /* @__PURE__ */ new Set(["not-logged-in", "error", "claude-missing"]);
761
+ const RESUME_ARGS = /* @__PURE__ */ new Set(["--resume", "-r", "--continue", "-c", "--fork-session", "--session-id"]);
762
+ const CLAUDE_PREWARM_MAX_CONSECUTIVE_MISSES = 5;
763
+ function claudePrewarmSessionEligibility(input) {
764
+ const { env } = input;
765
+ const no = (reason) => ({ eligible: false, reason });
766
+ if (env.HAPPY_CLAUDE_PREWARM === "0") return no("env-off");
767
+ if (input.setting === "off") return no("setting-off");
768
+ if (input.startedBy !== "daemon") return no("not-daemon");
769
+ if (input.startingMode !== "remote") return no("not-remote");
770
+ if (env.HAPPY_RECONNECT_SESSION_ID) return no("reconnect");
771
+ if (env.HAPPY_FORK_CLAUDE_SESSION_ID || env.HAPPY_FORKED_FROM_SESSION_ID) return no("fork");
772
+ if (env.HAPPY_IMPORTED_FROM_CLAUDE_SESSION_ID) return no("import");
773
+ if (env.HAPPY_SESSION_VARIANT === "assistant") return no("assistant");
774
+ if (env.VH_TEAM_OPERATION_ID) return no("teams");
775
+ if (env.VH_AUTOMATION_RUN_ID) return no("automation");
776
+ if (env.HAPPY_SPAWNED_BY) return no("spawned-by");
777
+ if (env.HAPPY_FIRST_MESSAGE_FROM_CLI === "1") return no("cli-first-message");
778
+ if (input.claudeArgs?.some((arg) => RESUME_ARGS.has(arg))) return no("resume-args");
779
+ if (env.HAPPY_CLAUDE_AUTH_STATUS && BAD_AUTH_STATUSES.has(env.HAPPY_CLAUDE_AUTH_STATUS)) return no("claude-auth");
780
+ return { eligible: true };
781
+ }
782
+ function claudePrewarmEligibility(input) {
783
+ const session = claudePrewarmSessionEligibility(input);
784
+ if (!session.eligible) return session;
785
+ if (!input.cache?.appendSystemPrompt) return { eligible: false, reason: "no-cached-system-prompt" };
786
+ if (input.cache.consecutiveMisses >= CLAUDE_PREWARM_MAX_CONSECUTIVE_MISSES) return { eligible: false, reason: "recent-misses" };
787
+ return { eligible: true };
788
+ }
789
+ function prewarmCacheFile(happyHomeDir) {
790
+ return join(happyHomeDir, "claude-prewarm.json");
791
+ }
792
+ function readPrewarmCache(file) {
793
+ try {
794
+ const raw = JSON.parse(readFileSync(file, "utf8"));
795
+ if (typeof raw?.appendSystemPrompt !== "string" || raw.appendSystemPrompt.length === 0) return null;
796
+ const misses = typeof raw.consecutiveMisses === "number" && Number.isFinite(raw.consecutiveMisses) && raw.consecutiveMisses > 0 ? Math.floor(raw.consecutiveMisses) : 0;
797
+ return {
798
+ appendSystemPrompt: raw.appendSystemPrompt,
799
+ ...typeof raw.effort === "string" || raw.effort === null ? { effort: raw.effort } : {},
800
+ consecutiveMisses: misses
801
+ };
802
+ } catch {
803
+ return null;
804
+ }
805
+ }
806
+ function writePrewarmCache(file, cache) {
807
+ try {
808
+ const tmp = `${file}.${process.pid}.tmp`;
809
+ writeFileSync(tmp, JSON.stringify({ ...cache, updatedAt: Date.now() }), { mode: 384 });
810
+ renameSync(tmp, file);
811
+ return true;
812
+ } catch {
813
+ return false;
814
+ }
815
+ }
816
+ function prewarmPredictionMeta(cache) {
817
+ return {
818
+ appendSystemPrompt: cache.appendSystemPrompt,
819
+ ...cache.effort !== void 0 ? { effort: cache.effort } : {}
820
+ };
821
+ }
822
+ function evaluatePrewarmPrediction(input) {
823
+ const outcome = input.predicted === null ? "no-prediction" : claudeModeHash(input.predicted) === claudeModeHash(input.actual) ? "hit" : "miss";
824
+ const append = input.meta?.appendSystemPrompt;
825
+ const misses = outcome === "miss" ? (input.previous?.consecutiveMisses ?? 0) + 1 : 0;
826
+ if (typeof append !== "string" || append.length === 0) {
827
+ return { outcome, next: input.previous ? { ...input.previous, consecutiveMisses: misses } : null };
828
+ }
829
+ const hasEffort = !!input.meta && Object.prototype.hasOwnProperty.call(input.meta, "effort");
830
+ return {
831
+ outcome,
832
+ next: {
833
+ appendSystemPrompt: append,
834
+ ...hasEffort ? { effort: input.meta.effort ?? null } : {},
835
+ consecutiveMisses: misses
836
+ }
837
+ };
838
+ }
839
+ function prewarmSlotDir(happyHomeDir) {
840
+ return join(happyHomeDir, "tmp", "claude-prewarm");
841
+ }
842
+ function acquirePrewarmSlot(dir, opts = {}) {
843
+ const max = opts.max ?? CLAUDE_PREWARM_MAX_CONCURRENT;
844
+ const pid = opts.pid ?? process.pid;
845
+ const isAlive = opts.isAlive ?? pidAlive;
846
+ const now = opts.now ?? Date.now();
847
+ const staleMs = opts.staleMs ?? CLAUDE_PREWARM_IDLE_MS + 6e4;
848
+ try {
849
+ mkdirSync(dir, { recursive: true, mode: 448 });
850
+ } catch {
851
+ return null;
852
+ }
853
+ for (let i = 0; i < max; i++) {
854
+ const path = join(dir, `slot-${i}.lock`);
855
+ for (let attempt = 0; attempt < 2; attempt++) {
856
+ try {
857
+ writeFileSync(path, String(pid), { flag: "wx", mode: 384 });
858
+ let released = false;
859
+ return {
860
+ path,
861
+ release: () => {
862
+ if (released) return;
863
+ released = true;
864
+ try {
865
+ if (readFileSync(path, "utf8").trim() === String(pid)) unlinkSync(path);
866
+ } catch {
867
+ }
868
+ }
869
+ };
870
+ } catch (error) {
871
+ if (error?.code !== "EEXIST") return null;
872
+ if (!slotIsStale(path, { isAlive, now, staleMs }) || attempt > 0) break;
873
+ try {
874
+ unlinkSync(path);
875
+ } catch {
876
+ }
877
+ }
878
+ }
879
+ }
880
+ return null;
881
+ }
882
+ function slotIsStale(path, opts) {
883
+ try {
884
+ const owner = Number(readFileSync(path, "utf8").trim());
885
+ if (!Number.isInteger(owner) || owner <= 0 || !opts.isAlive(owner)) return true;
886
+ return opts.now - statSync(path).mtimeMs > opts.staleMs;
887
+ } catch {
888
+ return !existsSync(path);
889
+ }
890
+ }
891
+ function pidAlive(pid) {
892
+ try {
893
+ process.kill(pid, 0);
894
+ return true;
895
+ } catch (error) {
896
+ return error?.code === "EPERM";
897
+ }
898
+ }
899
+ class PrewarmHookGate {
900
+ next = 0;
901
+ adopted = /* @__PURE__ */ new Set();
902
+ newTag() {
903
+ return `${PREWARM_HOOK_SOURCE_PREFIX}${process.pid}-${this.next++}`;
904
+ }
905
+ adopt(tag) {
906
+ this.adopted.add(tag);
907
+ }
908
+ /** Untagged hooks (the ordinary per-process settings file) always pass. */
909
+ accepts(source) {
910
+ if (!source || !source.startsWith(PREWARM_HOOK_SOURCE_PREFIX)) return true;
911
+ return this.adopted.has(source);
912
+ }
913
+ }
914
+ function decideWarmAdoption(input) {
915
+ if (input.specialCommand === "clear") return { adopt: false, reason: "clear" };
916
+ if (input.specialCommand === "compact") return { adopt: false, reason: "compact" };
917
+ if (claudeModeHash(input.predicted) !== claudeModeHash(input.actual)) return { adopt: false, reason: "mode-mismatch" };
918
+ const warmPermission = mapToClaudeMode(input.predicted.permissionMode);
919
+ const wantedPermission = mapToClaudeMode(input.actual.permissionMode);
920
+ return {
921
+ adopt: true,
922
+ ...warmPermission !== wantedPermission ? { setPermissionMode: wantedPermission } : {},
923
+ switchModel: needsModelSwitch(input.predicted.model, input.actual.model)
924
+ };
925
+ }
926
+ function formatPrewarmLine(event, detail) {
927
+ if (event === "discarded" || event === "skipped") {
928
+ return `[CLAUDE PREWARM] ${event}(${typeof detail === "string" ? detail : "unknown"})`;
929
+ }
930
+ const fields = detail && typeof detail === "object" ? Object.entries(detail).filter(([, v]) => v !== void 0).map(([k, v]) => ` ${k}=${v}`).join("") : "";
931
+ return `[CLAUDE PREWARM] ${event}${fields}`;
932
+ }
933
+ const liveLeases = /* @__PURE__ */ new Set();
934
+ let exitHookInstalled = false;
935
+ function disposeAllClaudePrewarms(reason) {
936
+ for (const lease of [...liveLeases]) lease.discard(reason);
937
+ }
938
+ function createClaudePrewarmLease(input) {
939
+ let state = "warm";
940
+ let teardown = null;
941
+ const lease = {
942
+ mode: input.mode,
943
+ hookSettingsPath: input.hookSettingsPath,
944
+ tag: input.tag,
945
+ idleMs: input.idleMs ?? CLAUDE_PREWARM_IDLE_MS,
946
+ adoptTimeoutMs: input.adoptTimeoutMs ?? CLAUDE_PREWARM_ADOPT_TIMEOUT_MS,
947
+ adopt: () => {
948
+ if (state !== "warm") return;
949
+ state = "adopted";
950
+ teardown = null;
951
+ input.gate.adopt(input.tag);
952
+ input.slot.release();
953
+ liveLeases.delete(lease);
954
+ },
955
+ discard: (reason) => {
956
+ if (state !== "warm") return;
957
+ state = "closed";
958
+ const t = teardown;
959
+ teardown = null;
960
+ liveLeases.delete(lease);
961
+ try {
962
+ t?.(reason);
963
+ } catch {
964
+ }
965
+ input.slot.release();
966
+ input.cleanupHookSettings();
967
+ input.log(formatPrewarmLine("discarded", reason));
968
+ },
969
+ setTeardown: (next) => {
970
+ if (state === "warm") teardown = next;
971
+ }
972
+ };
973
+ liveLeases.add(lease);
974
+ if (!exitHookInstalled) {
975
+ exitHookInstalled = true;
976
+ process.once("exit", () => disposeAllClaudePrewarms("exit"));
977
+ }
978
+ return lease;
979
+ }
980
+
981
+ function withTimeout(promise, ms) {
982
+ promise.catch(() => {
983
+ });
984
+ let timer;
985
+ const timeout = new Promise((_, reject) => {
986
+ timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);
987
+ });
988
+ return Promise.race([promise, timeout]).finally(() => {
989
+ if (timer) clearTimeout(timer);
990
+ });
991
+ }
702
992
  async function claudeRemote(opts) {
703
993
  let startFrom = opts.sessionId;
704
994
  if (opts.sessionId && !claudeCheckSession(opts.sessionId, opts.path)) {
@@ -729,13 +1019,153 @@ async function claudeRemote(opts) {
729
1019
  process.env[key] = value;
730
1020
  });
731
1021
  }
732
- const initial = await opts.nextMessage();
1022
+ let thinking = false;
1023
+ const updateThinking = (newThinking) => {
1024
+ if (thinking !== newThinking) {
1025
+ thinking = newThinking;
1026
+ logger.debug(`[claudeRemote] Thinking state changed to: ${thinking}`);
1027
+ if (opts.onThinkingChange) {
1028
+ opts.onThinkingChange(thinking);
1029
+ }
1030
+ }
1031
+ };
1032
+ let callbackDepth = 0;
1033
+ const guardCallback = (callback) => (async (...args) => {
1034
+ callbackDepth++;
1035
+ try {
1036
+ return await callback(...args);
1037
+ } finally {
1038
+ setImmediate(() => {
1039
+ callbackDepth--;
1040
+ });
1041
+ }
1042
+ });
1043
+ let mode = opts.prewarm?.mode ?? { permissionMode: "default" };
1044
+ const buildSdkOptions = (creationMode, io) => {
1045
+ const sdkOptions = {
1046
+ cwd: opts.path,
1047
+ resume: startFrom ?? void 0,
1048
+ mcpServers: opts.mcpServers,
1049
+ permissionMode: mapToClaudeMode(creationMode.permissionMode),
1050
+ // This is only the SDK safety opt-in; permissionMode/canUseTool still
1051
+ // enforce the selected policy. It must be enabled at Query creation so
1052
+ // a later explicit live switch to bypassPermissions can succeed.
1053
+ allowDangerouslySkipPermissions: true,
1054
+ model: creationMode.model,
1055
+ fallbackModel: creationMode.fallbackModel,
1056
+ customSystemPrompt: creationMode.customSystemPrompt ? creationMode.customSystemPrompt + "\n\n" + systemPrompt : void 0,
1057
+ appendSystemPrompt: creationMode.appendSystemPrompt ? creationMode.appendSystemPrompt + "\n\n" + systemPrompt : systemPrompt,
1058
+ allowedTools: creationMode.allowedTools ? creationMode.allowedTools.concat(opts.allowedTools) : opts.allowedTools,
1059
+ additionalDirectories: opts.additionalDirectories,
1060
+ disallowedTools: creationMode.disallowedTools,
1061
+ effort: creationMode.effort,
1062
+ canCallTool: (toolName, input, options) => opts.canCallTool(toolName, input, mode, options),
1063
+ onElicitation: opts.onElicitation,
1064
+ onUserDialog: opts.onUserDialog,
1065
+ supportedDialogKinds: opts.onUserDialog ? ["refusal_fallback_prompt"] : void 0,
1066
+ abort: io.abort,
1067
+ settingsPath: io.settingsPath,
1068
+ // B-309: token-level partials. Without this the SDK emits nothing
1069
+ // between "turn started" and "entire assistant message", which is
1070
+ // exactly why the web used to show a spinner where the terminal shows
1071
+ // the model thinking out loud. The partials do NOT enter the
1072
+ // transcript; they are split off below and relayed on a side channel.
1073
+ // Only pay for them when someone is listening.
1074
+ includePartialMessages: opts.onStreamFrame ? true : void 0
1075
+ };
1076
+ if (sdkOptions.canCallTool) sdkOptions.canCallTool = guardCallback(sdkOptions.canCallTool);
1077
+ if (sdkOptions.onElicitation) sdkOptions.onElicitation = guardCallback(sdkOptions.onElicitation);
1078
+ if (sdkOptions.onUserDialog) sdkOptions.onUserDialog = guardCallback(sdkOptions.onUserDialog);
1079
+ return sdkOptions;
1080
+ };
1081
+ const discoverModels = async (q) => {
1082
+ if (typeof q.supportedModels !== "function") return void 0;
1083
+ return (await q.supportedModels()).map((model) => ({
1084
+ code: model.value,
1085
+ value: model.displayName,
1086
+ description: model.description,
1087
+ resolvedModel: model.resolvedModel,
1088
+ ...model.supportsEffort === false ? { reasoningEfforts: [] } : model.supportedEffortLevels ? { reasoningEfforts: model.supportedEffortLevels } : {}
1089
+ }));
1090
+ };
1091
+ let warm = null;
1092
+ if (opts.prewarm && startFrom) {
1093
+ opts.prewarm.discard("resume");
1094
+ } else if (opts.prewarm && opts.signal?.aborted) {
1095
+ opts.prewarm.discard("aborted");
1096
+ } else if (opts.prewarm) {
1097
+ const lease = opts.prewarm;
1098
+ const abort = new AbortController();
1099
+ const onOuterAbort = () => abort.abort();
1100
+ opts.signal?.addEventListener("abort", onOuterAbort, { once: true });
1101
+ const messages2 = new PushableAsyncIterable();
1102
+ const startedAt = Date.now();
1103
+ try {
1104
+ const response2 = query({
1105
+ prompt: messages2,
1106
+ options: buildSdkOptions(lease.mode, { settingsPath: lease.hookSettingsPath, abort: abort.signal })
1107
+ });
1108
+ const handshake = discoverModels(response2).then(
1109
+ (models2) => ({ ok: true, models: models2, at: Date.now() }),
1110
+ () => ({ ok: false, at: Date.now() })
1111
+ );
1112
+ const w = { response: response2, messages: messages2, lease, startedAt, handshake };
1113
+ lease.setTeardown(() => {
1114
+ if (w.idleTimer) clearTimeout(w.idleTimer);
1115
+ try {
1116
+ w.offExit?.();
1117
+ } catch {
1118
+ }
1119
+ opts.signal?.removeEventListener("abort", onOuterAbort);
1120
+ if (warm === w) warm = null;
1121
+ messages2.end();
1122
+ abort.abort();
1123
+ });
1124
+ w.idleTimer = setTimeout(() => lease.discard("idle"), lease.idleMs);
1125
+ w.idleTimer.unref?.();
1126
+ void handshake.then((result) => {
1127
+ if (!result.ok) {
1128
+ lease.discard("handshake-failed");
1129
+ return;
1130
+ }
1131
+ if (warm !== w) return;
1132
+ try {
1133
+ const transport = response2.transport;
1134
+ const off = transport?.onExit?.(() => {
1135
+ if (warm === w) lease.discard("exited");
1136
+ });
1137
+ if (typeof off === "function") w.offExit = off;
1138
+ } catch {
1139
+ }
1140
+ });
1141
+ warm = w;
1142
+ logger.debug(formatPrewarmLine("started", { tag: lease.tag }));
1143
+ } catch (error) {
1144
+ opts.signal?.removeEventListener("abort", onOuterAbort);
1145
+ logger.debug(`[claudeRemote] prewarm query() threw: ${error instanceof Error ? error.message : String(error)}`);
1146
+ lease.discard("spawn-failed");
1147
+ }
1148
+ }
1149
+ const discardWarm = (reason) => {
1150
+ warm?.lease.discard(reason);
1151
+ };
1152
+ let initial;
1153
+ try {
1154
+ initial = await opts.nextMessage();
1155
+ } catch (error) {
1156
+ discardWarm("error");
1157
+ throw error;
1158
+ }
733
1159
  if (!initial) {
1160
+ discardWarm("exit");
734
1161
  return;
735
1162
  }
1163
+ let timingTurn = 1;
1164
+ let timingMarks = { pushedAt: initial.enqueuedAt ?? Date.now() };
736
1165
  const initialText = typeof initial.message === "string" ? initial.message : initial.message.find((b) => b.type === "text")?.text ?? "";
737
1166
  const specialCommand = parseSpecialCommand(initialText);
738
1167
  if (specialCommand.type === "clear") {
1168
+ discardWarm("clear");
739
1169
  if (opts.onCompletionEvent) {
740
1170
  opts.onCompletionEvent("Context was reset");
741
1171
  }
@@ -753,50 +1183,61 @@ async function claudeRemote(opts) {
753
1183
  opts.onCompletionEvent("Compaction started");
754
1184
  }
755
1185
  }
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);
1186
+ let adopted = null;
1187
+ if (warm) {
1188
+ const w = warm;
1189
+ const adoptStart = Date.now();
1190
+ const decision = decideWarmAdoption({ predicted: w.lease.mode, actual: initial.mode, specialCommand: specialCommand.type });
1191
+ let reason = decision.adopt ? null : decision.reason;
1192
+ const hs = reason ? null : await w.handshake;
1193
+ if (!reason) {
1194
+ if (warm !== w) {
1195
+ reason = "closed";
1196
+ } else if (!hs?.ok) {
1197
+ reason = "handshake-failed";
1198
+ } else if (decision.adopt) {
1199
+ try {
1200
+ if (decision.setPermissionMode) {
1201
+ await withTimeout(w.response.setPermissionMode(decision.setPermissionMode), w.lease.adoptTimeoutMs);
1202
+ } else {
1203
+ await withTimeout(w.response.mcpServerStatus(), w.lease.adoptTimeoutMs);
1204
+ }
1205
+ } catch {
1206
+ reason = decision.setPermissionMode ? "set-permission-failed" : "not-alive";
1207
+ }
1208
+ if (!reason && decision.switchModel) {
1209
+ try {
1210
+ await withTimeout(w.response.setModel(modelTarget(initial.mode.model)), w.lease.adoptTimeoutMs);
1211
+ } catch {
1212
+ reason = "set-model-failed";
1213
+ }
1214
+ }
1215
+ if (!reason && warm !== w) reason = "closed";
795
1216
  }
796
1217
  }
797
- };
798
- let messages = new PushableAsyncIterable();
799
- messages.push({
1218
+ if (reason || !hs?.ok) {
1219
+ w.lease.discard(reason ?? "handshake-failed");
1220
+ warm = null;
1221
+ } else {
1222
+ if (w.idleTimer) clearTimeout(w.idleTimer);
1223
+ try {
1224
+ w.offExit?.();
1225
+ } catch {
1226
+ }
1227
+ warm = null;
1228
+ w.lease.adopt();
1229
+ adopted = { response: w.response, messages: w.messages, models: hs.models };
1230
+ logger.debug(formatPrewarmLine("adopted", {
1231
+ warmForMs: adoptStart - w.startedAt,
1232
+ handshakeWaitMs: Math.max(0, hs.at - adoptStart),
1233
+ adoptMs: Date.now() - adoptStart,
1234
+ setPermissionMode: decision.adopt ? decision.setPermissionMode : void 0,
1235
+ setModel: decision.adopt && decision.switchModel ? true : void 0
1236
+ }));
1237
+ }
1238
+ }
1239
+ mode = initial.mode;
1240
+ const initialUserMessage = {
800
1241
  type: "user",
801
1242
  parent_tool_use_id: null,
802
1243
  origin: { kind: "human" },
@@ -804,39 +1245,32 @@ async function claudeRemote(opts) {
804
1245
  role: "user",
805
1246
  content: initial.message
806
1247
  }
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
- });
1248
+ };
1249
+ let messages;
1250
+ let response;
826
1251
  let models;
827
- if (typeof response.supportedModels === "function") {
1252
+ if (adopted) {
1253
+ messages = adopted.messages;
1254
+ response = adopted.response;
1255
+ models = adopted.models;
1256
+ timingMarks.handshakeAt = Date.now();
1257
+ messages.push(initialUserMessage);
1258
+ } else {
1259
+ messages = new PushableAsyncIterable();
1260
+ messages.push(initialUserMessage);
1261
+ timingMarks.spawnAt = Date.now();
1262
+ response = query({
1263
+ prompt: messages,
1264
+ options: buildSdkOptions(initial.mode, { settingsPath: opts.hookSettingsPath, abort: opts.signal })
1265
+ });
828
1266
  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
- }));
1267
+ models = await discoverModels(response);
836
1268
  } catch (error) {
837
1269
  logger.debug("[claudeRemote] Model capability discovery unavailable");
838
1270
  }
1271
+ timingMarks.handshakeAt = Date.now();
839
1272
  }
1273
+ const prewarmTiming = adopted ? "adopted" : void 0;
840
1274
  if (opts.onQueryReady) {
841
1275
  opts.onQueryReady({
842
1276
  canControl: () => callbackDepth === 0,
@@ -888,6 +1322,11 @@ async function claudeRemote(opts) {
888
1322
  if (message.type === "assistant" && message.error) {
889
1323
  lastAssistantError = message.error;
890
1324
  }
1325
+ if (message.type === "assistant" && timingMarks.firstAssistantAt === void 0) {
1326
+ timingMarks.firstAssistantAt = Date.now();
1327
+ } else if (message.type === "system" && message.subtype === "init" && timingMarks.initAt === void 0) {
1328
+ timingMarks.initAt = Date.now();
1329
+ }
891
1330
  if (opts.onStreamFrame && message.type === "system") {
892
1331
  const subtype = message.subtype;
893
1332
  if (subtype === "thinking_tokens") {
@@ -937,6 +1376,15 @@ async function claudeRemote(opts) {
937
1376
  if (message.type === "result") {
938
1377
  updateThinking(false);
939
1378
  logger.debug("[claudeRemote] Result received");
1379
+ logger.debug(formatClaudeTimingLine({
1380
+ turn: timingTurn,
1381
+ firstTurnOfProcess: timingTurn === 1,
1382
+ marks: timingMarks,
1383
+ result: message,
1384
+ prewarm: timingTurn === 1 ? prewarmTiming : void 0
1385
+ }));
1386
+ timingTurn++;
1387
+ timingMarks = {};
940
1388
  const denials = message.permission_denials;
941
1389
  if (denials && denials.length > 0) {
942
1390
  logger.warn(`[claudeRemote] ${denials.length} tool call(s) auto-denied by Claude Code policy: ${denials.map((d) => d.tool_name ?? "?").join(", ")}`);
@@ -976,6 +1424,7 @@ async function claudeRemote(opts) {
976
1424
  }
977
1425
  mode = next.mode;
978
1426
  updateThinking(true);
1427
+ timingMarks = { pushedAt: next.enqueuedAt ?? Date.now() };
979
1428
  messages.push({
980
1429
  type: "user",
981
1430
  parent_tool_use_id: null,
@@ -2636,10 +3085,13 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2636
3085
  permissionHandler.setOnModeChanged((nextMode) => {
2637
3086
  commitPermissionMode(nextMode);
2638
3087
  });
3088
+ let launchPrewarm = null;
2639
3089
  try {
2640
3090
  const attachmentDirectory = chatAttachmentDirectory(configuration.happyHomeDir, session.client.sessionId);
2641
3091
  await ensurePrivateDirectory(attachmentDirectory);
3092
+ launchPrewarm = session.takeClaudePrewarm();
2642
3093
  const remoteResult = await claudeRemote({
3094
+ prewarm: launchPrewarm ?? void 0,
2643
3095
  sessionId: session.sessionId,
2644
3096
  path: session.path,
2645
3097
  additionalDirectories: [attachmentDirectory],
@@ -2688,13 +3140,15 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2688
3140
  onPromptFinalized?.(withAttachments);
2689
3141
  return {
2690
3142
  message: withAttachments,
2691
- mode: msg.mode
3143
+ mode: msg.mode,
3144
+ enqueuedAt: msg.enqueuedAt
2692
3145
  };
2693
3146
  }
2694
3147
  onPromptFinalized?.(msg.message);
2695
3148
  return {
2696
3149
  message: msg.message,
2697
- mode: msg.mode
3150
+ mode: msg.mode,
3151
+ enqueuedAt: msg.enqueuedAt
2698
3152
  };
2699
3153
  }
2700
3154
  return null;
@@ -2812,6 +3266,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
2812
3266
  continue;
2813
3267
  }
2814
3268
  } finally {
3269
+ launchPrewarm?.discard("launch-ended");
2815
3270
  runtimeControls.setQuery(null);
2816
3271
  backgroundTasks.reset();
2817
3272
  session.setBackgroundTasks([]);
@@ -2885,7 +3340,8 @@ async function loop(opts) {
2885
3340
  onModeChange: opts.onModeChange,
2886
3341
  onAbort: opts.onAbort,
2887
3342
  hookSettingsPath: opts.hookSettingsPath,
2888
- jsRuntime: opts.jsRuntime
3343
+ jsRuntime: opts.jsRuntime,
3344
+ claudePrewarm: opts.claudePrewarm
2889
3345
  });
2890
3346
  opts.onSessionReady?.(session);
2891
3347
  let mode = opts.startingMode ?? "local";
@@ -2893,7 +3349,7 @@ async function loop(opts) {
2893
3349
  logger.debug(`[loop] Iteration with mode: ${mode}`);
2894
3350
  switch (mode) {
2895
3351
  case "local": {
2896
- const { claudeLocalLauncher } = await import('./claudeLocalLauncher-D4T7MTqd.mjs');
3352
+ const { claudeLocalLauncher } = await import('./claudeLocalLauncher-CDqQI9ak.mjs');
2897
3353
  const result = await claudeLocalLauncher(session);
2898
3354
  switch (result.type) {
2899
3355
  case "switch":
@@ -2921,18 +3377,18 @@ async function loop(opts) {
2921
3377
  }
2922
3378
  }
2923
3379
 
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
- });
3380
+ function parseSessionHookTarget(url) {
3381
+ if (!url) return null;
3382
+ let parsed;
3383
+ try {
3384
+ parsed = new URL(url, "http://127.0.0.1");
3385
+ } catch {
3386
+ return null;
3387
+ }
3388
+ if (parsed.pathname !== "/hook/session-start") return null;
3389
+ const source = parsed.searchParams.get("source");
3390
+ return source && /^[A-Za-z0-9_.-]{1,64}$/.test(source) ? { source } : {};
2934
3391
  }
2935
-
2936
3392
  function hookRequestLogMetadata(body, data) {
2937
3393
  return {
2938
3394
  bodyBytes: Buffer.byteLength(body, "utf8"),
@@ -2954,7 +3410,8 @@ async function startHookServer(options) {
2954
3410
  const { onSessionHook } = options;
2955
3411
  return new Promise((resolve, reject) => {
2956
3412
  const server = createServer(async (req, res) => {
2957
- if (req.method === "POST" && req.url === "/hook/session-start") {
3413
+ const hookMeta = req.method === "POST" ? parseSessionHookTarget(req.url) : null;
3414
+ if (hookMeta) {
2958
3415
  const timeout = setTimeout(() => {
2959
3416
  if (!res.headersSent) {
2960
3417
  logger.debug("[hookServer] Request timeout");
@@ -2978,7 +3435,7 @@ async function startHookServer(options) {
2978
3435
  const sessionId = data?.session_id || data?.sessionId;
2979
3436
  if (sessionId && data) {
2980
3437
  logger.debug(`[hookServer] Session hook received session ID: ${sessionId}`);
2981
- onSessionHook(sessionId, data);
3438
+ onSessionHook(sessionId, data, hookMeta);
2982
3439
  } else {
2983
3440
  logger.debug("[hookServer] Session hook received but no session_id found in data");
2984
3441
  }
@@ -3017,13 +3474,16 @@ async function startHookServer(options) {
3017
3474
  });
3018
3475
  }
3019
3476
 
3020
- function generateHookSettingsFile(port) {
3477
+ function generateHookSettingsFile(port, source) {
3021
3478
  const hooksDir = join(configuration.happyHomeDir, "tmp", "hooks");
3022
3479
  mkdirSync(hooksDir, { recursive: true });
3023
- const filename = `session-hook-${process.pid}.json`;
3480
+ const filename = source ? `session-hook-${process.pid}-${source}.json` : `session-hook-${process.pid}.json`;
3024
3481
  const filepath = join(hooksDir, filename);
3025
3482
  const forwarderScript = resolve(projectPath(), "scripts", "session_hook_forwarder.cjs");
3026
- const hookCommand = `node "${forwarderScript}" ${port}`;
3483
+ if (source !== void 0 && !/^[A-Za-z0-9_.-]{1,64}$/.test(source)) {
3484
+ throw new Error("invalid hook source tag");
3485
+ }
3486
+ const hookCommand = source ? `node "${forwarderScript}" ${port} ${source}` : `node "${forwarderScript}" ${port}`;
3027
3487
  const settings = {
3028
3488
  hooks: {
3029
3489
  SessionStart: [
@@ -3621,4 +4081,49 @@ function withAssistantDenylist(current, isAssistant) {
3621
4081
  return [...merged];
3622
4082
  }
3623
4083
 
3624
- export { BoardAnalyzer, FileRateLimiter, SessionExitGate, claudeCheckSession, claudeModeHash, cleanupHookSettingsFile, contentLogMetadata, exitIntentFromArchiveFlag, generateHookSettingsFile, getProjectPath, loop, parseSpecialCommand, registerSideQuestionHandler, settleSessionOnExit, startHookServer, withAssistantDenylist, writeSideQuestionSettingsFile };
4084
+ const VALID_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
4085
+ const has = (meta, key) => !!meta && Object.prototype.hasOwnProperty.call(meta, key);
4086
+ function resolveMessageMode(current, meta, ctx) {
4087
+ const next = { ...current };
4088
+ const result = {};
4089
+ let permissionMode = current.permissionMode;
4090
+ if (!ctx.staleModeSnapshot && meta?.permissionMode) {
4091
+ permissionMode = resolveRemoteClaudePermissionMode(current.permissionMode, meta.permissionMode, ctx.sandboxEnabled);
4092
+ next.permissionMode = permissionMode;
4093
+ result.publishPermissionMode = permissionMode;
4094
+ }
4095
+ if (has(meta, "model")) next.model = meta.model || void 0;
4096
+ if (has(meta, "customSystemPrompt")) next.customSystemPrompt = meta.customSystemPrompt || void 0;
4097
+ if (has(meta, "fallbackModel")) next.fallbackModel = meta.fallbackModel || void 0;
4098
+ if (has(meta, "appendSystemPrompt")) next.appendSystemPrompt = meta.appendSystemPrompt || void 0;
4099
+ if (has(meta, "allowedTools")) next.allowedTools = meta.allowedTools || void 0;
4100
+ if (has(meta, "disallowedTools")) {
4101
+ next.disallowedTools = withAssistantDenylist(meta.disallowedTools || void 0, ctx.isAssistantVariant);
4102
+ }
4103
+ if (has(meta, "effort")) {
4104
+ const incoming = meta.effort;
4105
+ if (incoming === null || incoming === void 0) {
4106
+ next.effort = void 0;
4107
+ } else if (typeof incoming === "string" && VALID_EFFORTS.has(incoming)) {
4108
+ next.effort = incoming;
4109
+ } else {
4110
+ result.ignoredEffort = String(incoming);
4111
+ }
4112
+ }
4113
+ return {
4114
+ ...result,
4115
+ next,
4116
+ mode: {
4117
+ permissionMode: permissionMode || "default",
4118
+ model: next.model,
4119
+ fallbackModel: next.fallbackModel,
4120
+ customSystemPrompt: next.customSystemPrompt,
4121
+ appendSystemPrompt: next.appendSystemPrompt,
4122
+ allowedTools: next.allowedTools,
4123
+ disallowedTools: next.disallowedTools,
4124
+ effort: next.effort
4125
+ }
4126
+ };
4127
+ }
4128
+
4129
+ 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 };