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.
- package/dist/{AcpBackend-mzv8Dhba.mjs → AcpBackend--TmFHgRH.mjs} +3 -3
- package/dist/{AcpBackend-Bm2rdke8.cjs → AcpBackend-9asD_DL7.cjs} +3 -3
- package/dist/{AcpSessionManager-CoMWFVw3.mjs → AcpSessionManager-BSIBnWUb.mjs} +1 -1
- package/dist/{AcpSessionManager-BAY1Pggx.cjs → AcpSessionManager-BvIuBzoO.cjs} +1 -1
- package/dist/{BasePermissionHandler-CFHAxumV.cjs → BasePermissionHandler-D4B-NNu2.cjs} +1 -1
- package/dist/{BasePermissionHandler-m3WVholL.mjs → BasePermissionHandler-DUBySe0f.mjs} +1 -1
- package/dist/{BaseReasoningProcessor-CYrKBXGm.mjs → BaseReasoningProcessor-9TybUGTq.mjs} +1 -1
- package/dist/{BaseReasoningProcessor-BFa9_rZA.cjs → BaseReasoningProcessor-CqLl-ikP.cjs} +1 -1
- package/dist/{agentHome-CboCeKWq.cjs → agentHome-BohNUYnE.cjs} +4 -4
- package/dist/{agentHome-Dj1C43aJ.mjs → agentHome-DQrq_FG1.mjs} +4 -4
- package/dist/{api-CgundNNX.cjs → api-CQnLOqRS.cjs} +10 -10
- package/dist/{api-CLMUr-SX.mjs → api-D57dsd6_.mjs} +10 -10
- package/dist/{apiMachine-B9dLW_p5.mjs → apiMachine-78Aqzg--.mjs} +21 -14
- package/dist/{apiMachine-Bn8482VM.cjs → apiMachine-fFFaWxYy.cjs} +33 -26
- package/dist/{assistantTools-BTqPnlFl.cjs → assistantTools-CwESq24n.cjs} +6 -6
- package/dist/{assistantTools-D6tuc6o4.mjs → assistantTools-mEz6uNqL.mjs} +6 -6
- package/dist/{attachmentContent-DpiTJZb4.cjs → attachmentContent-GHOk6Muq.cjs} +2 -2
- package/dist/{attachmentContent-_kxRznNf.mjs → attachmentContent-s1xPha4Q.mjs} +2 -2
- package/dist/{attachmentLimits-yba1uHFO.mjs → attachmentLimits-D5pgmASK.mjs} +1 -1
- package/dist/{attachmentLimits-CzVrqgLR.cjs → attachmentLimits-DzlBPyJP.cjs} +1 -1
- package/dist/{auth-BDOVh5h5.cjs → auth-B8EagV6G.cjs} +8 -8
- package/dist/{auth-PYHZQNPp.mjs → auth-BOaHWquu.mjs} +8 -8
- package/dist/{auth-ba4WMYrN.cjs → auth-BbSdXMQ_.cjs} +5 -5
- package/dist/{auth-Cwutmdsa.mjs → auth-C8V7-oUT.mjs} +5 -5
- package/dist/{auto-BOX_fgmR.cjs → auto-Bw_h0yVc.cjs} +15 -14
- package/dist/{auto-BXu14yP_.mjs → auto-CWDOX35m.mjs} +15 -14
- package/dist/{bootstrap-hxXPCFMx.mjs → bootstrap-BKmsYSwh.mjs} +2 -2
- package/dist/{bootstrap-B5G1TijC.cjs → bootstrap-DvZjEa9_.cjs} +2 -2
- package/dist/{browser-Y6Bl8L_O.cjs → browser-CBZj-1Wx.cjs} +1 -1
- package/dist/{browser-jh2P0UVZ.mjs → browser-CrMAr5fI.mjs} +1 -1
- package/dist/{claudeLocal-CRGG4Pea.cjs → claudeLocal-Bpw-u9et.cjs} +5 -5
- package/dist/{claudeLocal-BaY4fZKi.mjs → claudeLocal-dhdlcQ6F.mjs} +5 -5
- package/dist/{claudeLocalLauncher-D4T7MTqd.mjs → claudeLocalLauncher-BFfGT4eY.mjs} +10 -10
- package/dist/{claudeLocalLauncher-DyEbVzUN.cjs → claudeLocalLauncher-DWFWuLhY.cjs} +10 -10
- package/dist/{codexThreadFork-BH9IZPqD.cjs → claudeSessionFork-D9-mANmu.cjs} +1 -137
- package/dist/{codexThreadFork-slzAseLx.mjs → claudeSessionFork-_DUrXaqd.mjs} +2 -134
- package/dist/{client-BPNbmtvO.cjs → client-CVQJWqjx.cjs} +2 -2
- package/dist/{client-3Ac-O4pw.cjs → client-DiBTHWNU.cjs} +3 -3
- package/dist/{client-CeGKqqcJ.mjs → client-Dk7x-s-O.mjs} +3 -3
- package/dist/{client-ClR0K8zX.mjs → client-DwqHDsLb.mjs} +2 -2
- package/dist/codex/happyMcpStdioBridge.cjs +15 -15
- package/dist/codex/happyMcpStdioBridge.mjs +15 -15
- package/dist/{codexAppServerClient-I3teiV-N.mjs → codexAppServerClient-C2ybs2mZ.mjs} +2 -2
- package/dist/{codexAppServerClient-q2FjnRcK.cjs → codexAppServerClient-DlKv5TjG.cjs} +2 -2
- package/dist/{codexCommand-4zcccIoq.cjs → codexCommand-4vLE31fj.cjs} +45 -40
- package/dist/{codexCommand-Cc9gcbdX.mjs → codexCommand-DnmFr9Qx.mjs} +45 -40
- package/dist/codexThreadFork-DNekT4nQ.cjs +138 -0
- package/dist/codexThreadFork-R76RU5H0.mjs +133 -0
- package/dist/{config-Ci9Kpgwa.cjs → config-Btd4B05_.cjs} +3 -3
- package/dist/{config-CTx_jb-C.mjs → config-CSOG_ZcP.mjs} +3 -3
- package/dist/{configuration-RfeNjFmd.mjs → configuration-A5gW_0GE.mjs} +1 -1
- package/dist/{configuration-BSYpcPwE.cjs → configuration-UsI0sxEv.cjs} +1 -1
- package/dist/{connect-C2CiOUFs.cjs → connect-C9yxp8Om.cjs} +13 -13
- package/dist/{connect-BpOdzNbE.mjs → connect-DWYj8LP_.mjs} +13 -13
- package/dist/{context-DsDr6E2t.cjs → context-0FjjA4vJ.cjs} +1 -1
- package/dist/{context-H0Bcb9S_.mjs → context-BXnxalWW.mjs} +1 -1
- package/dist/{controlClient-Bcwv3D21.cjs → controlClient-BXktrQ4z.cjs} +3 -3
- package/dist/{controlClient-BGbDcgWS.mjs → controlClient-CYzHrh3e.mjs} +3 -3
- package/dist/{createSessionMetadata-CVVkGjxV.mjs → createSessionMetadata-Dyg33ZlU.mjs} +2 -2
- package/dist/{createSessionMetadata-BJxEnM-o.cjs → createSessionMetadata-EX1eAlHx.cjs} +2 -2
- package/dist/{deterministicJson-CzkFJWw1.mjs → deterministicJson-DiR7EOMO.mjs} +9 -9
- package/dist/{deterministicJson-C5CYTVpm.cjs → deterministicJson-eha_oMZ2.cjs} +9 -9
- package/dist/{doctor-D2YJ-Yn2.cjs → doctor-DunLBgVi.cjs} +6 -6
- package/dist/{doctor-DOvByjbk.mjs → doctor-FuCvKdRa.mjs} +6 -6
- package/dist/{ensureDaemonRunning-BCwNrMyv.cjs → ensureDaemonRunning-B4dZFmUy.cjs} +5 -5
- package/dist/{ensureDaemonRunning-Dqa2pOjY.mjs → ensureDaemonRunning-EZkb-CAF.mjs} +5 -5
- package/dist/{handleResumeCommand-C2cKchpX.cjs → handleResumeCommand-CKMIw4pE.cjs} +5 -5
- package/dist/{handleResumeCommand-BvXYf5El.mjs → handleResumeCommand-CsXendKb.mjs} +5 -5
- package/dist/{index-DMZqWwsP.mjs → index-9L39wqYR.mjs} +37 -37
- package/dist/{index-qdS1friN.mjs → index-BBg9j0XK.mjs} +8 -1
- package/dist/{index-B6pAdvJx.cjs → index-BOhgL3rh.cjs} +36 -36
- package/dist/{index-Bu5Zi_a9.cjs → index-CZWAiydF.cjs} +4 -4
- package/dist/{index-DVkMGPyS.mjs → index-CvXcVQ06.mjs} +3 -3
- package/dist/{index-DA7yINuk.cjs → index-DJ-oV52i.cjs} +8 -1
- package/dist/{index-DKLU_fD0.mjs → index-DK2JpTJf.mjs} +4 -4
- package/dist/{index-DDeHXrIX.cjs → index-DRhWHEuW.cjs} +3 -3
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{install-CFY3tYtJ.mjs → install-B2duVo9a.mjs} +3 -3
- package/dist/{install-BgoC2e_2.cjs → install-C8WyZI9K.cjs} +3 -3
- package/dist/lib.cjs +12 -12
- package/dist/lib.d.cts +5 -0
- package/dist/lib.d.mts +5 -0
- package/dist/lib.mjs +12 -12
- package/dist/{lidState-CeEt442o.mjs → lidState-CigXWxhe.mjs} +1 -1
- package/dist/{lidState-BeDfxxEX.cjs → lidState-Ds-6ytZV.cjs} +1 -1
- package/dist/{localHappyAgentAuth-C-HQyiv6.mjs → localHappyAgentAuth-CaCyKzYp.mjs} +1 -1
- package/dist/{localHappyAgentAuth-D6rFhaoc.cjs → localHappyAgentAuth-l0Na-5Ys.cjs} +1 -1
- package/dist/{logger-CS0mw3fa.cjs → logger-7ucWm9wp.cjs} +2 -2
- package/dist/{logger-6HYUEXNk.mjs → logger-CGE4C8ho.mjs} +2 -2
- package/dist/{machineMetadata-DyDbODYd.cjs → machineMetadata-lMPtpF16.cjs} +3 -3
- package/dist/{machineMetadata-oOzBfDfs.mjs → machineMetadata-ue1nkowa.mjs} +3 -3
- package/dist/{main-BwNIAI1A.mjs → main-3fIFXD-t.mjs} +44 -44
- package/dist/{main-BkxJP6JQ.cjs → main-CFI0datL.cjs} +44 -44
- package/dist/{mcp-B1A4I3TK.mjs → mcp-DARQBZ7l.mjs} +14 -14
- package/dist/{mcp-Hszd7qpS.cjs → mcp-DfxLAc1G.cjs} +14 -14
- package/dist/{package-BwMrPwqH.cjs → package-D1A-8ZJy.cjs} +7 -7
- package/dist/{package-BhaKlnxz.mjs → package-DQsvY1XN.mjs} +7 -7
- package/dist/{peerTools-BTqw97Gb.mjs → peerTools-D19hhSQF.mjs} +4 -4
- package/dist/{peerTools-BvKtHXj8.cjs → peerTools-DLwNaEEV.cjs} +4 -4
- package/dist/{permissionMode-Cr5JCbB7.mjs → permissionMode-CH4FoZfU.mjs} +1 -1
- package/dist/{persistence-HGW_ZVs9.mjs → persistence-Cw8aLoyL.mjs} +3 -3
- package/dist/{persistence-yKTH3-5X.cjs → persistence-i0nQMLlk.cjs} +3 -3
- package/dist/{piRunnerArgs-BuX1UFWy.mjs → piRunnerArgs-58t454gw.mjs} +1 -1
- package/dist/{piRunnerArgs-BNloJCCg.cjs → piRunnerArgs-BUyxPxjZ.cjs} +1 -1
- package/dist/{piRuntime-CSGkwCKQ.mjs → piRuntime-DVPixFjl.mjs} +1 -1
- package/dist/{piRuntime-C_H7MJCM.cjs → piRuntime-Dff73XCb.cjs} +1 -1
- package/dist/{piTerminal-DNCZ6yTM.cjs → piTerminal-Dz1RrO70.cjs} +16 -16
- package/dist/{piTerminal-C8a1OrNQ.mjs → piTerminal-gHWFAyzq.mjs} +16 -16
- package/dist/{registerCommonHandlers-DskirN-_.cjs → registerCommonHandlers-CMROPdJz.cjs} +2 -2
- package/dist/{registerCommonHandlers-Ccvsz7Ou.mjs → registerCommonHandlers-Ckv5boQI.mjs} +2 -2
- package/dist/{registerKillSessionHandler-DZeLXWcB.cjs → registerKillSessionHandler-CA9-oelM.cjs} +19 -10
- package/dist/{registerKillSessionHandler-GYvHdipX.mjs → registerKillSessionHandler-Cll4-o8Y.mjs} +19 -10
- package/dist/{remoteSessionClient-CYnXRynE.mjs → remoteSessionClient-BMjsbuBh.mjs} +5 -5
- package/dist/{remoteSessionClient-B0LraSB9.cjs → remoteSessionClient-DX9jHLeW.cjs} +5 -5
- package/dist/{run-CJzxoHVh.cjs → run-CjbGo2wb.cjs} +36 -32
- package/dist/{run-D3ifupU3.mjs → run-CrunzDlt.mjs} +35 -31
- package/dist/{runClaude-Da3RirzE.cjs → runClaude-KN-4Y4LH.cjs} +184 -140
- package/dist/{runClaude-Ba01qT-u.mjs → runClaude-WDBWRtnF.mjs} +185 -141
- package/dist/{runClaudeDeps-DOzEBMls.cjs → runClaudeDeps-DW-8lnHv.cjs} +821 -125
- package/dist/{runClaudeDeps-DOt79DjU.mjs → runClaudeDeps-QaEx8y5r.mjs} +811 -129
- package/dist/{runGemini-DSr9LIxL.cjs → runGemini-Djmwjdv1.cjs} +32 -32
- package/dist/{runGemini-BGeTz57m.mjs → runGemini-Nf6SKBpw.mjs} +32 -32
- package/dist/{runOpenClaw-CogSedIc.mjs → runOpenClaw-9nHtmYbN.mjs} +18 -18
- package/dist/{runOpenClaw-B1mTxN35.cjs → runOpenClaw-CAqP5KFH.cjs} +18 -18
- package/dist/{sandbox-CG7IyDTU.cjs → sandbox-C8Dd8GPM.cjs} +4 -4
- package/dist/{sandbox-DTe6d54R.mjs → sandbox-SP0THMt0.mjs} +4 -4
- package/dist/selfCheck-7FaZtVu2.mjs +84 -0
- package/dist/{selfCheck-Dc-mNSLb.cjs → selfCheck-D9ZXrK7h.cjs} +34 -34
- package/dist/{send-CBXJKc6O.mjs → send-DOx9aQsx.mjs} +8 -8
- package/dist/{send-B7J_eaG3.cjs → send-LPk6Q5HI.cjs} +8 -8
- package/dist/{server-DcVBv04l.mjs → server-BZNi8Hq_.mjs} +4 -4
- package/dist/{server-CZTP_iOn.cjs → server-CxT8jNyn.cjs} +6 -6
- package/dist/{sessionLock-D-EG2CfG.mjs → sessionLock-CfLqJfeI.mjs} +2 -2
- package/dist/{sessionLock-Bys1PNSo.cjs → sessionLock-y9rnA4LK.cjs} +2 -2
- package/dist/{sessionModeFile-RSm0NQkH.cjs → sessionModeFile-B5YeoQJh.cjs} +1 -1
- package/dist/{sessionModeFile-DMl3dw3r.mjs → sessionModeFile-BKxgNH3P.mjs} +1 -1
- package/dist/{sessionOps-SuHW8Dfe.mjs → sessionOps-CdwVPSZz.mjs} +4 -4
- package/dist/{sessionOps-Dn8z85h_.cjs → sessionOps-GsELm2PZ.cjs} +4 -4
- package/dist/{sessionScanner-CX3onqXh.mjs → sessionScanner-Cdukzo70.mjs} +3 -3
- package/dist/{sessionScanner-BOrGjMXL.cjs → sessionScanner-aLEv7mD3.cjs} +3 -3
- package/dist/{sessions-D74aJbhL.cjs → sessions-CfG4WGN0.cjs} +9 -9
- package/dist/{sessions-BEG_HTSi.mjs → sessions-Dne-OiRL.mjs} +9 -9
- package/dist/{setupOfflineReconnection-ByElY60m.cjs → setupOfflineReconnection-CFwltTcP.cjs} +2 -2
- package/dist/{setupOfflineReconnection-DpvWTt69.mjs → setupOfflineReconnection-DiQma2Cc.mjs} +2 -2
- package/dist/{spawn-DgR0RKRs.cjs → spawn-BI_sdgWs.cjs} +24 -15
- package/dist/{spawn-B7yro0b2.mjs → spawn-DkgP2BGF.mjs} +22 -14
- package/dist/{spawnAgents-DZzZ9jMI.mjs → spawnAgents-Y1LjnCt9.mjs} +1 -1
- package/dist/{spawnAgents-BGQiEF-w.cjs → spawnAgents-iSu1Xzs5.cjs} +1 -1
- package/dist/{spawnHappyCLI-B2H5s4B_.mjs → spawnHappyCLI-9mx4lPxh.mjs} +1 -1
- package/dist/{spawnHappyCLI-DBhwZT7O.cjs → spawnHappyCLI-BRdx0sAX.cjs} +1 -1
- package/dist/{startFileWatcher-B4VsLgjD.cjs → startFileWatcher-C2HC2_Dg.cjs} +1 -1
- package/dist/{startFileWatcher-DErVWZE4.mjs → startFileWatcher-COD5UKNs.mjs} +1 -1
- package/dist/{streamRelay-FPVbHpbM.mjs → streamRelay-BLgU9X3R.mjs} +1 -1
- package/dist/{streamRelay-BiKw59Cc.cjs → streamRelay-DnObXZZj.cjs} +1 -1
- package/dist/{sync-C8gyQFMc.mjs → sync-BpLasLu2.mjs} +1 -1
- package/dist/{sync-DccIKZ5_.cjs → sync-Dj92ZJ8Y.cjs} +1 -1
- package/dist/{systemPrompt-BwTPWfMu.cjs → systemPrompt-CyEz_IfR.cjs} +2 -2
- package/dist/{systemPrompt-DX521JJz.mjs → systemPrompt-DsQoUgQw.mjs} +2 -2
- package/dist/{teams-C32F1hqd.mjs → teams-BETNMjpZ.mjs} +6 -6
- package/dist/{teams-Dl8g8kTI.cjs → teams-Ctx9zXn4.cjs} +6 -6
- package/dist/{time-DCuTyook.cjs → time-CLwVAz6s.cjs} +1 -1
- package/dist/{time-D0XyHOid.mjs → time-CVvxdEDh.mjs} +1 -1
- package/dist/{titleGenerator-CQ8CoGhg.mjs → titleGenerator-B4AW3dLr.mjs} +12 -2
- package/dist/{titleGenerator-ChzMJsyg.cjs → titleGenerator-FDpdizn7.cjs} +13 -3
- package/dist/{todo-BvUOTa8G.cjs → todo-C3tyFeq1.cjs} +6 -6
- package/dist/{todo-CZ6cFrrn.mjs → todo-Jk4Vs7hP.mjs} +6 -6
- package/dist/{tools-BZtHF_qQ.mjs → tools-BA1qkiV1.mjs} +2 -2
- package/dist/{tools-DQj0AeLk.cjs → tools-CGxq3I52.cjs} +2 -2
- package/dist/{tools-Be3AoAwu.cjs → tools-O5beDK-s.cjs} +1 -1
- package/dist/{tools-DkluDcG4.mjs → tools-_kFe4m_l.mjs} +1 -1
- package/dist/{uninstall-BkLzaJ6I.mjs → uninstall-C_mdAZlm.mjs} +3 -3
- package/dist/{uninstall-x-TUtf5H.cjs → uninstall-DHKF90lo.cjs} +3 -3
- package/package.json +7 -7
- package/scripts/session_hook_forwarder.cjs +9 -2
- package/dist/selfCheck-C9mVrQwf.mjs +0 -84
|
@@ -1,48 +1,50 @@
|
|
|
1
|
-
import { l as logger } from './logger-
|
|
2
|
-
import { k as notifyDaemonSessionEvent, m as notifyDaemonClaudeAuthFailed } from './controlClient-
|
|
3
|
-
export { g as reportSessionEditToDaemon } from './controlClient-
|
|
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-
|
|
10
|
-
import { c as claudeCheckSession, s as systemPrompt } from './systemPrompt-
|
|
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-
|
|
16
|
+
import { A as AsyncLock } from './attachmentLimits-D5pgmASK.mjs';
|
|
17
17
|
import 'tweetnacl';
|
|
18
|
-
import { c as configuration, e as ensurePrivateDirectory } from './configuration-
|
|
19
|
-
import './index-
|
|
20
|
-
import './registerCommonHandlers-
|
|
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-
|
|
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-
|
|
31
|
-
import {
|
|
32
|
-
|
|
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-
|
|
35
|
-
import { C as CLAUDE_ATTACHMENT_KINDS, c as chatAttachmentDirectory, a as stageClaudeAttachments, b as appendStagedAttachmentsToPrompt } from './attachmentContent-
|
|
36
|
-
|
|
37
|
-
import {
|
|
38
|
-
export {
|
|
39
|
-
export { g as getEnvironmentInfo } from './doctor-
|
|
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-
|
|
45
|
-
export { T as TitleGenerator } from './titleGenerator-
|
|
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-
|
|
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 './
|
|
61
|
-
import './
|
|
62
|
-
import './
|
|
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-
|
|
67
|
-
import './tools-
|
|
68
|
-
import './client-
|
|
69
|
-
import './peerTools-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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
|
-
|
|
799
|
-
|
|
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
|
|
809
|
-
|
|
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 (
|
|
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 =
|
|
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-
|
|
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
|
|
2925
|
-
return
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|