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.
- package/dist/{AcpBackend-mzv8Dhba.mjs → AcpBackend-6U6pQgx1.mjs} +3 -3
- package/dist/{AcpBackend-Bm2rdke8.cjs → AcpBackend-Ba98QDif.cjs} +3 -3
- package/dist/{BasePermissionHandler-CFHAxumV.cjs → BasePermissionHandler-2R0uH2CI.cjs} +1 -1
- package/dist/{BasePermissionHandler-m3WVholL.mjs → BasePermissionHandler-BvvhBTja.mjs} +1 -1
- package/dist/{BaseReasoningProcessor-CYrKBXGm.mjs → BaseReasoningProcessor-DyhO1uoU.mjs} +1 -1
- package/dist/{BaseReasoningProcessor-BFa9_rZA.cjs → BaseReasoningProcessor-fsVNmCEF.cjs} +1 -1
- package/dist/{agentHome-Dj1C43aJ.mjs → agentHome-Ck1i1TQB.mjs} +4 -4
- package/dist/{agentHome-CboCeKWq.cjs → agentHome-DvNnGphb.cjs} +4 -4
- package/dist/{api-CgundNNX.cjs → api-B2vLMS-z.cjs} +8 -8
- package/dist/{api-CLMUr-SX.mjs → api-SffjRQ1b.mjs} +8 -8
- package/dist/{apiMachine-B9dLW_p5.mjs → apiMachine-BpW9t8MO.mjs} +11 -11
- package/dist/{apiMachine-Bn8482VM.cjs → apiMachine-gFA54X7y.cjs} +11 -11
- package/dist/{assistantTools-BTqPnlFl.cjs → assistantTools-BfA0R3_m.cjs} +6 -6
- package/dist/{assistantTools-D6tuc6o4.mjs → assistantTools-CgiTbnWb.mjs} +6 -6
- package/dist/{attachmentContent-_kxRznNf.mjs → attachmentContent-CSGxr71v.mjs} +1 -1
- package/dist/{attachmentContent-DpiTJZb4.cjs → attachmentContent-DMMZzevR.cjs} +1 -1
- package/dist/{auth-ba4WMYrN.cjs → auth-Bt4ceBBy.cjs} +5 -5
- package/dist/{auth-Cwutmdsa.mjs → auth-CYHjQ8oQ.mjs} +5 -5
- package/dist/{auth-PYHZQNPp.mjs → auth-DdyVZSo6.mjs} +8 -8
- package/dist/{auth-BDOVh5h5.cjs → auth-x4CEoysH.cjs} +8 -8
- package/dist/{auto-BOX_fgmR.cjs → auto-BDXDUnjB.cjs} +12 -12
- package/dist/{auto-BXu14yP_.mjs → auto-CXLlRi3a.mjs} +12 -12
- package/dist/{bootstrap-hxXPCFMx.mjs → bootstrap-BNQq05jR.mjs} +2 -2
- package/dist/{bootstrap-B5G1TijC.cjs → bootstrap-CkgIq8N3.cjs} +2 -2
- package/dist/{browser-jh2P0UVZ.mjs → browser-9pyhQr1R.mjs} +1 -1
- package/dist/{browser-Y6Bl8L_O.cjs → browser-BDOlKWYe.cjs} +1 -1
- package/dist/{claudeLocal-CRGG4Pea.cjs → claudeLocal-BDuGFnjJ.cjs} +4 -4
- package/dist/{claudeLocal-BaY4fZKi.mjs → claudeLocal-CQa8qnwq.mjs} +4 -4
- package/dist/{claudeLocalLauncher-DyEbVzUN.cjs → claudeLocalLauncher-Bd5I2-j6.cjs} +9 -9
- package/dist/{claudeLocalLauncher-D4T7MTqd.mjs → claudeLocalLauncher-CDqQI9ak.mjs} +9 -9
- package/dist/{client-CeGKqqcJ.mjs → client-Brn-UwVk.mjs} +3 -3
- package/dist/{client-ClR0K8zX.mjs → client-CXb1VJui.mjs} +2 -2
- package/dist/{client-3Ac-O4pw.cjs → client-C_axwRry.cjs} +3 -3
- package/dist/{client-BPNbmtvO.cjs → client-DOBWJL3u.cjs} +2 -2
- package/dist/codex/happyMcpStdioBridge.cjs +14 -14
- package/dist/codex/happyMcpStdioBridge.mjs +14 -14
- package/dist/{codexAppServerClient-q2FjnRcK.cjs → codexAppServerClient-ZzhUTWPo.cjs} +2 -2
- package/dist/{codexAppServerClient-I3teiV-N.mjs → codexAppServerClient-oeQKD6Qf.mjs} +2 -2
- package/dist/{codexCommand-4zcccIoq.cjs → codexCommand-C7H0NpBe.cjs} +35 -35
- package/dist/{codexCommand-Cc9gcbdX.mjs → codexCommand-CJ82bl3l.mjs} +35 -35
- package/dist/{codexThreadFork-slzAseLx.mjs → codexThreadFork-BCaEMBiM.mjs} +1 -1
- package/dist/{codexThreadFork-BH9IZPqD.cjs → codexThreadFork-BZqF-9bX.cjs} +1 -1
- package/dist/{config-CTx_jb-C.mjs → config-BglnktIo.mjs} +3 -3
- package/dist/{config-Ci9Kpgwa.cjs → config-CYpL1Ahd.cjs} +3 -3
- package/dist/{configuration-RfeNjFmd.mjs → configuration-DoWFJUPm.mjs} +1 -1
- package/dist/{configuration-BSYpcPwE.cjs → configuration-v9f8LM9T.cjs} +1 -1
- package/dist/{connect-BpOdzNbE.mjs → connect-hgcNjyAB.mjs} +11 -11
- package/dist/{connect-C2CiOUFs.cjs → connect-yEvjdRvx.cjs} +11 -11
- package/dist/{context-DsDr6E2t.cjs → context-B45vMtSZ.cjs} +1 -1
- package/dist/{context-H0Bcb9S_.mjs → context-C9GHPLEm.mjs} +1 -1
- package/dist/{controlClient-BGbDcgWS.mjs → controlClient-DjoKTtgK.mjs} +3 -3
- package/dist/{controlClient-Bcwv3D21.cjs → controlClient-fEHE85bH.cjs} +3 -3
- package/dist/{createSessionMetadata-BJxEnM-o.cjs → createSessionMetadata-BRLB9CNO.cjs} +2 -2
- package/dist/{createSessionMetadata-CVVkGjxV.mjs → createSessionMetadata-mIeehclp.mjs} +2 -2
- package/dist/{deterministicJson-C5CYTVpm.cjs → deterministicJson-DKDP_mM-.cjs} +8 -8
- package/dist/{deterministicJson-CzkFJWw1.mjs → deterministicJson-p835AdAU.mjs} +8 -8
- package/dist/{doctor-DOvByjbk.mjs → doctor-BeUuQXFs.mjs} +6 -6
- package/dist/{doctor-D2YJ-Yn2.cjs → doctor-hVNnSZJn.cjs} +6 -6
- package/dist/{ensureDaemonRunning-Dqa2pOjY.mjs → ensureDaemonRunning-CO7d1gOC.mjs} +5 -5
- package/dist/{ensureDaemonRunning-BCwNrMyv.cjs → ensureDaemonRunning-Dg9aZfjs.cjs} +5 -5
- package/dist/{handleResumeCommand-BvXYf5El.mjs → handleResumeCommand-BiTPK0qd.mjs} +5 -5
- package/dist/{handleResumeCommand-C2cKchpX.cjs → handleResumeCommand-DnVmy7a3.cjs} +5 -5
- package/dist/{index-DMZqWwsP.mjs → index-CAFwetLk.mjs} +33 -33
- package/dist/{index-DVkMGPyS.mjs → index-Cqx-Raq6.mjs} +3 -3
- package/dist/{index-B6pAdvJx.cjs → index-LIn6nCcq.cjs} +32 -32
- package/dist/{index-Bu5Zi_a9.cjs → index-Pf2cV9cn.cjs} +4 -4
- package/dist/{index-DKLU_fD0.mjs → index-SPGPenK5.mjs} +4 -4
- package/dist/{index-DDeHXrIX.cjs → index-gNTWtSKN.cjs} +3 -3
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{install-BgoC2e_2.cjs → install-Bu85vlHD.cjs} +3 -3
- package/dist/{install-CFY3tYtJ.mjs → install-ClUUl3yp.mjs} +3 -3
- package/dist/lib.cjs +10 -10
- package/dist/lib.mjs +10 -10
- package/dist/{lidState-CeEt442o.mjs → lidState-C2dI0FAN.mjs} +1 -1
- package/dist/{lidState-BeDfxxEX.cjs → lidState-KHHu1015.cjs} +1 -1
- package/dist/{localHappyAgentAuth-D6rFhaoc.cjs → localHappyAgentAuth-BXiHPXdC.cjs} +1 -1
- package/dist/{localHappyAgentAuth-C-HQyiv6.mjs → localHappyAgentAuth-DCsetiyG.mjs} +1 -1
- package/dist/{logger-CS0mw3fa.cjs → logger-Bc8-u1w0.cjs} +2 -2
- package/dist/{logger-6HYUEXNk.mjs → logger-fXoNzNuB.mjs} +2 -2
- package/dist/{machineMetadata-oOzBfDfs.mjs → machineMetadata-BHjehxJY.mjs} +3 -3
- package/dist/{machineMetadata-DyDbODYd.cjs → machineMetadata-BibDMFVx.cjs} +3 -3
- package/dist/{main-BwNIAI1A.mjs → main-BktM4A1K.mjs} +44 -44
- package/dist/{main-BkxJP6JQ.cjs → main-CLaX-t-b.cjs} +44 -44
- package/dist/{mcp-B1A4I3TK.mjs → mcp-CeUd9KDr.mjs} +14 -14
- package/dist/{mcp-Hszd7qpS.cjs → mcp-GBfE70nk.cjs} +14 -14
- package/dist/{package-BwMrPwqH.cjs → package-Cl5BEP5x.cjs} +7 -7
- package/dist/{package-BhaKlnxz.mjs → package-uOg9SCKi.mjs} +7 -7
- package/dist/{peerTools-BvKtHXj8.cjs → peerTools-CIeiy9wn.cjs} +4 -4
- package/dist/{peerTools-BTqw97Gb.mjs → peerTools-Dz3_7Bkk.mjs} +4 -4
- package/dist/{permissionMode-Cr5JCbB7.mjs → permissionMode-CH4FoZfU.mjs} +1 -1
- package/dist/{persistence-yKTH3-5X.cjs → persistence-CBNMzSa8.cjs} +3 -3
- package/dist/{persistence-HGW_ZVs9.mjs → persistence-oET5n61f.mjs} +3 -3
- package/dist/{piRunnerArgs-BNloJCCg.cjs → piRunnerArgs-Bf7RUF__.cjs} +1 -1
- package/dist/{piRunnerArgs-BuX1UFWy.mjs → piRunnerArgs-Dk1ncwHF.mjs} +1 -1
- package/dist/{piRuntime-C_H7MJCM.cjs → piRuntime-B_6TE9Eb.cjs} +1 -1
- package/dist/{piRuntime-CSGkwCKQ.mjs → piRuntime-BwRdhK_N.mjs} +1 -1
- package/dist/{piTerminal-C8a1OrNQ.mjs → piTerminal-Bap3R8Re.mjs} +16 -16
- package/dist/{piTerminal-DNCZ6yTM.cjs → piTerminal-CagmEoez.cjs} +16 -16
- package/dist/{registerCommonHandlers-DskirN-_.cjs → registerCommonHandlers-BjSV2Skz.cjs} +2 -2
- package/dist/{registerCommonHandlers-Ccvsz7Ou.mjs → registerCommonHandlers-CrgvJsOy.mjs} +2 -2
- package/dist/{registerKillSessionHandler-DZeLXWcB.cjs → registerKillSessionHandler-6TC2S3lf.cjs} +13 -8
- package/dist/{registerKillSessionHandler-GYvHdipX.mjs → registerKillSessionHandler-Cbc8Rgtd.mjs} +13 -8
- package/dist/{remoteSessionClient-B0LraSB9.cjs → remoteSessionClient-BNx78NLb.cjs} +5 -5
- package/dist/{remoteSessionClient-CYnXRynE.mjs → remoteSessionClient-CO7qOjR0.mjs} +5 -5
- package/dist/{run-D3ifupU3.mjs → run-2l5vSt2h.mjs} +32 -28
- package/dist/{run-CJzxoHVh.cjs → run-D5xAEWSB.cjs} +33 -29
- package/dist/{runClaude-Da3RirzE.cjs → runClaude-BX6ixjtW.cjs} +182 -138
- package/dist/{runClaude-Ba01qT-u.mjs → runClaude-BgN6xo05.mjs} +183 -139
- package/dist/{runClaudeDeps-DOzEBMls.cjs → runClaudeDeps-Bp20MScE.cjs} +638 -119
- package/dist/{runClaudeDeps-DOt79DjU.mjs → runClaudeDeps-COBBhD9o.mjs} +628 -123
- package/dist/{runGemini-DSr9LIxL.cjs → runGemini-CSMPkt0p.cjs} +30 -30
- package/dist/{runGemini-BGeTz57m.mjs → runGemini-Cc2jq-uh.mjs} +30 -30
- package/dist/{runOpenClaw-CogSedIc.mjs → runOpenClaw-CIlCwdW2.mjs} +15 -15
- package/dist/{runOpenClaw-B1mTxN35.cjs → runOpenClaw-YBwua4Rf.cjs} +15 -15
- package/dist/{sandbox-CG7IyDTU.cjs → sandbox-C4gXLP1p.cjs} +4 -4
- package/dist/{sandbox-DTe6d54R.mjs → sandbox-CNkwOtHI.mjs} +4 -4
- package/dist/{selfCheck-Dc-mNSLb.cjs → selfCheck-CU8xSDsl.cjs} +34 -34
- package/dist/selfCheck-hJ6PCh7v.mjs +84 -0
- package/dist/{send-CBXJKc6O.mjs → send-CGHcKZ-w.mjs} +8 -8
- package/dist/{send-B7J_eaG3.cjs → send-DY9Nr-x4.cjs} +8 -8
- package/dist/{server-CZTP_iOn.cjs → server-II51Mimf.cjs} +6 -6
- package/dist/{server-DcVBv04l.mjs → server-m2b5pamZ.mjs} +4 -4
- package/dist/{sessionLock-Bys1PNSo.cjs → sessionLock-7ePAYDOg.cjs} +2 -2
- package/dist/{sessionLock-D-EG2CfG.mjs → sessionLock-Cw6FmbMv.mjs} +2 -2
- package/dist/{sessionModeFile-RSm0NQkH.cjs → sessionModeFile-BVCV5f7E.cjs} +1 -1
- package/dist/{sessionModeFile-DMl3dw3r.mjs → sessionModeFile-CDmDyRkD.mjs} +1 -1
- package/dist/{sessionOps-SuHW8Dfe.mjs → sessionOps-C_f1wKOr.mjs} +4 -4
- package/dist/{sessionOps-Dn8z85h_.cjs → sessionOps-DJdExmBy.cjs} +4 -4
- package/dist/{sessionScanner-CX3onqXh.mjs → sessionScanner-CaAHLyNV.mjs} +3 -3
- package/dist/{sessionScanner-BOrGjMXL.cjs → sessionScanner-D1goKdd3.cjs} +3 -3
- package/dist/{sessions-D74aJbhL.cjs → sessions-DGs6ALNo.cjs} +9 -9
- package/dist/{sessions-BEG_HTSi.mjs → sessions-DnvmWUel.mjs} +9 -9
- package/dist/{setupOfflineReconnection-DpvWTt69.mjs → setupOfflineReconnection-CVViqZft.mjs} +2 -2
- package/dist/{setupOfflineReconnection-ByElY60m.cjs → setupOfflineReconnection-R7XuR_sj.cjs} +2 -2
- package/dist/{spawn-DgR0RKRs.cjs → spawn-CXTH7qg1.cjs} +19 -11
- package/dist/{spawn-B7yro0b2.mjs → spawn-DNYSvFtC.mjs} +19 -12
- package/dist/{spawnHappyCLI-DBhwZT7O.cjs → spawnHappyCLI-BhYj84rv.cjs} +1 -1
- package/dist/{spawnHappyCLI-B2H5s4B_.mjs → spawnHappyCLI-cqWQbwsb.mjs} +1 -1
- package/dist/{startFileWatcher-B4VsLgjD.cjs → startFileWatcher-CKKx-8ZO.cjs} +1 -1
- package/dist/{startFileWatcher-DErVWZE4.mjs → startFileWatcher-CdegMbJj.mjs} +1 -1
- package/dist/{sync-DccIKZ5_.cjs → sync-CVk4QERO.cjs} +1 -1
- package/dist/{sync-C8gyQFMc.mjs → sync-CxD-w_40.mjs} +1 -1
- package/dist/{systemPrompt-BwTPWfMu.cjs → systemPrompt-BygJcmbi.cjs} +1 -1
- package/dist/{systemPrompt-DX521JJz.mjs → systemPrompt-DmtypUc7.mjs} +1 -1
- package/dist/{teams-C32F1hqd.mjs → teams-BOC6nIPL.mjs} +6 -6
- package/dist/{teams-Dl8g8kTI.cjs → teams-BsvpOBfn.cjs} +6 -6
- package/dist/{time-D0XyHOid.mjs → time-BZNdNiIV.mjs} +1 -1
- package/dist/{time-DCuTyook.cjs → time-CNRE4ChD.cjs} +1 -1
- package/dist/{titleGenerator-ChzMJsyg.cjs → titleGenerator-CFRhU-xq.cjs} +2 -2
- package/dist/{titleGenerator-CQ8CoGhg.mjs → titleGenerator-ox6ptCsp.mjs} +1 -1
- package/dist/{todo-BvUOTa8G.cjs → todo-B-KB7oAB.cjs} +5 -5
- package/dist/{todo-CZ6cFrrn.mjs → todo-C5KBVbUw.mjs} +5 -5
- package/dist/{tools-BZtHF_qQ.mjs → tools-BZabNIid.mjs} +1 -1
- package/dist/{tools-DkluDcG4.mjs → tools-BmQQUATY.mjs} +1 -1
- package/dist/{tools-DQj0AeLk.cjs → tools-CsSovyNp.cjs} +1 -1
- package/dist/{tools-Be3AoAwu.cjs → tools-lMDra4Pv.cjs} +1 -1
- package/dist/{uninstall-BkLzaJ6I.mjs → uninstall-B1WLtC1d.mjs} +3 -3
- package/dist/{uninstall-x-TUtf5H.cjs → uninstall-fvA5aWpU.cjs} +3 -3
- package/package.json +7 -7
- package/scripts/session_hook_forwarder.cjs +9 -2
- package/dist/selfCheck-C9mVrQwf.mjs +0 -84
|
@@ -1,13 +1,13 @@
|
|
|
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-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-
|
|
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-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-
|
|
18
|
+
import { c as configuration, e as ensurePrivateDirectory } from './configuration-DoWFJUPm.mjs';
|
|
19
19
|
import './index-qdS1friN.mjs';
|
|
20
|
-
import './registerCommonHandlers-
|
|
20
|
+
import './registerCommonHandlers-CrgvJsOy.mjs';
|
|
21
21
|
import 'os';
|
|
22
22
|
import 'child_process';
|
|
23
23
|
import '@paralleldrive/cuid2';
|
|
24
|
-
import './persistence-
|
|
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-
|
|
31
|
-
import {
|
|
32
|
-
|
|
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-
|
|
36
|
-
export { M as MessageQueue2, r as registerKillSessionHandler } from './registerKillSessionHandler-
|
|
37
|
-
|
|
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-
|
|
45
|
-
export { T as TitleGenerator } from './titleGenerator-
|
|
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-
|
|
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 './
|
|
61
|
-
import './
|
|
62
|
-
import './
|
|
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-
|
|
67
|
-
import './tools-
|
|
68
|
-
import './client-
|
|
69
|
-
import './peerTools-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
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);
|
|
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
|
-
|
|
799
|
-
|
|
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
|
|
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
|
-
});
|
|
1248
|
+
};
|
|
1249
|
+
let messages;
|
|
1250
|
+
let response;
|
|
826
1251
|
let models;
|
|
827
|
-
if (
|
|
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 =
|
|
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-
|
|
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
|
|
2925
|
-
return
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|