very-happy-cli 0.2.158 → 0.2.160
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-DIDRig5G.cjs → AcpBackend-Bm2rdke8.cjs} +78 -76
- package/dist/{AcpBackend-vDVLHuYJ.mjs → AcpBackend-mzv8Dhba.mjs} +4 -2
- package/dist/{AcpSessionManager-C44U99CC.cjs → AcpSessionManager-BAY1Pggx.cjs} +9 -9
- package/dist/{AcpSessionManager-CmRjfhn3.mjs → AcpSessionManager-CoMWFVw3.mjs} +1 -1
- package/dist/BasePermissionHandler-CFHAxumV.cjs +157 -0
- package/dist/BasePermissionHandler-m3WVholL.mjs +155 -0
- package/dist/BaseReasoningProcessor-BFa9_rZA.cjs +200 -0
- package/dist/BaseReasoningProcessor-CYrKBXGm.mjs +198 -0
- package/dist/RemoteModeDisplay-D-HewyXg.mjs +150 -0
- package/dist/RemoteModeDisplay-NC1gPHtj.cjs +153 -0
- package/dist/agentAttachments-Dbw8iMCZ.cjs +20 -0
- package/dist/agentAttachments-M_B5qEF2.mjs +18 -0
- package/dist/agentGuidance-B5NATuFa.cjs +28 -0
- package/dist/agentGuidance-CQuNlNx8.mjs +20 -0
- package/dist/{agentHome-Dw8Jx_CJ.cjs → agentHome-CboCeKWq.cjs} +14 -28
- package/dist/{agentHome-CyLmGH5L.mjs → agentHome-Dj1C43aJ.mjs} +8 -22
- package/dist/api-CLMUr-SX.mjs +3339 -0
- package/dist/api-CgundNNX.cjs +3346 -0
- package/dist/{types-DIBF4U0Y.mjs → apiMachine-B9dLW_p5.mjs} +9116 -16015
- package/dist/{types-DnrPeN80.cjs → apiMachine-Bn8482VM.cjs} +9104 -16057
- package/dist/assistantTools-BTqPnlFl.cjs +380 -0
- package/dist/assistantTools-D6tuc6o4.mjs +376 -0
- package/dist/attachmentContent-DpiTJZb4.cjs +88 -0
- package/dist/attachmentContent-_kxRznNf.mjs +82 -0
- package/dist/attachmentLimits-CzVrqgLR.cjs +354 -0
- package/dist/attachmentLimits-yba1uHFO.mjs +348 -0
- package/dist/auth-BDOVh5h5.cjs +220 -0
- package/dist/auth-Cwutmdsa.mjs +225 -0
- package/dist/auth-PYHZQNPp.mjs +218 -0
- package/dist/auth-ba4WMYrN.cjs +227 -0
- package/dist/authRelay-BdYkC1Po.cjs +156 -0
- package/dist/authRelay-DSAvg1uv.mjs +143 -0
- package/dist/{auto-DZbNcTXx.cjs → auto-BOX_fgmR.cjs} +70 -79
- package/dist/{auto-BlMZPwOk.mjs → auto-BXu14yP_.mjs} +27 -36
- package/dist/backgroundTaskActivity-8PdPxrvy.mjs +149 -0
- package/dist/backgroundTaskActivity-CBvUdj39.cjs +157 -0
- package/dist/bootstrap-B5G1TijC.cjs +116 -0
- package/dist/bootstrap-hxXPCFMx.mjs +112 -0
- package/dist/browser-Y6Bl8L_O.cjs +22 -0
- package/dist/browser-jh2P0UVZ.mjs +20 -0
- package/dist/claudeCliPath-BqkaH2oR.mjs +8 -0
- package/dist/claudeCliPath-U4HHmV4r.cjs +10 -0
- package/dist/claudeLocal-BaY4fZKi.mjs +339 -0
- package/dist/claudeLocal-CRGG4Pea.cjs +343 -0
- package/dist/claudeLocalLauncher-D4T7MTqd.mjs +152 -0
- package/dist/claudeLocalLauncher-DyEbVzUN.cjs +154 -0
- package/dist/cliUpdate-DsKXPFYp.cjs +120 -0
- package/dist/cliUpdate-Y34VGe9Y.mjs +112 -0
- package/dist/client-3Ac-O4pw.cjs +104 -0
- package/dist/client-BPNbmtvO.cjs +119 -0
- package/dist/client-CeGKqqcJ.mjs +100 -0
- package/dist/client-ClR0K8zX.mjs +112 -0
- package/dist/codex/happyMcpStdioBridge.cjs +33 -14
- package/dist/codex/happyMcpStdioBridge.mjs +26 -7
- package/dist/codexAppServerClient-I3teiV-N.mjs +1088 -0
- package/dist/codexAppServerClient-q2FjnRcK.cjs +1090 -0
- package/dist/codexCommand-4zcccIoq.cjs +2088 -0
- package/dist/codexCommand-Cc9gcbdX.mjs +2086 -0
- package/dist/codexThreadFork-BH9IZPqD.cjs +350 -0
- package/dist/codexThreadFork-slzAseLx.mjs +338 -0
- package/dist/{config-BtPArqRH.mjs → config-CTx_jb-C.mjs} +7 -40
- package/dist/{config-DG84bjwF.cjs → config-Ci9Kpgwa.cjs} +26 -59
- package/dist/configuration-BSYpcPwE.cjs +181 -0
- package/dist/configuration-RfeNjFmd.mjs +170 -0
- package/dist/connect-BpOdzNbE.mjs +682 -0
- package/dist/connect-C2CiOUFs.cjs +684 -0
- package/dist/constants-BC4St0xd.mjs +11 -0
- package/dist/constants-VJ0N6WuG.cjs +17 -0
- package/dist/contentLogMetadata-BlWI0egu.mjs +51 -0
- package/dist/contentLogMetadata-ZSFk_NjQ.cjs +54 -0
- package/dist/context-DsDr6E2t.cjs +69 -0
- package/dist/context-H0Bcb9S_.mjs +65 -0
- package/dist/controlClient-BGbDcgWS.mjs +260 -0
- package/dist/controlClient-Bcwv3D21.cjs +279 -0
- package/dist/createSessionMetadata-BJxEnM-o.cjs +58 -0
- package/dist/createSessionMetadata-CVVkGjxV.mjs +54 -0
- package/dist/defaultPermissionMode-BnxJPVHT.mjs +4 -0
- package/dist/defaultPermissionMode-CxmTXUQR.cjs +7 -0
- package/dist/detectCLI-CSGMlV51.mjs +58 -0
- package/dist/detectCLI-DR9ZaN6B.cjs +61 -0
- package/dist/deterministicJson-C5CYTVpm.cjs +507 -0
- package/dist/deterministicJson-CzkFJWw1.mjs +502 -0
- package/dist/doctor-BPkYWSuq.mjs +75 -0
- package/dist/doctor-D2YJ-Yn2.cjs +395 -0
- package/dist/doctor-DOvByjbk.mjs +391 -0
- package/dist/doctor-u46qYYKX.cjs +79 -0
- package/dist/doctorReadiness-BCZGcN0a.cjs +151 -0
- package/dist/doctorReadiness-C208SMN9.mjs +142 -0
- package/dist/editPaths-BVfS_FFL.mjs +68 -0
- package/dist/editPaths-Bqp4OFPw.cjs +73 -0
- package/dist/ensureDaemonRunning-BCwNrMyv.cjs +64 -0
- package/dist/ensureDaemonRunning-Dqa2pOjY.mjs +61 -0
- package/dist/handleResumeCommand-BvXYf5El.mjs +240 -0
- package/dist/handleResumeCommand-C2cKchpX.cjs +245 -0
- package/dist/ids-B6ZC0HGt.cjs +13 -0
- package/dist/ids-DU6sLx4C.mjs +10 -0
- package/dist/index-B6UGRQ7h.mjs +102 -0
- package/dist/{index-DPCsWlWK.cjs → index-B6pAdvJx.cjs} +131 -91
- package/dist/index-Bu5Zi_a9.cjs +182 -0
- package/dist/index-D3kI0hDR.cjs +107 -0
- package/dist/index-DA7yINuk.cjs +994 -0
- package/dist/index-DDeHXrIX.cjs +71 -0
- package/dist/index-DKLU_fD0.mjs +158 -0
- package/dist/{index-ZChGsuB7.mjs → index-DMZqWwsP.mjs} +79 -39
- package/dist/index-DVkMGPyS.mjs +68 -0
- package/dist/index-qdS1friN.mjs +949 -0
- package/dist/index.cjs +5 -46
- package/dist/index.mjs +5 -46
- package/dist/{install-DarS_Wco.mjs → install-BO_W7VhL.mjs} +37 -37
- package/dist/install-BgoC2e_2.cjs +88 -0
- package/dist/install-CFY3tYtJ.mjs +86 -0
- package/dist/{install-wKQGgXvN.cjs → install-Cs5qAPrB.cjs} +38 -38
- package/dist/{installPiTools-DuZ6jZvX.cjs → installPiTools-BKnXjrVu.cjs} +2 -3
- package/dist/{installPiTools-Cj7yU9Fc.mjs → installPiTools-B_CdM5_2.mjs} +1 -2
- package/dist/{installTerminalHooks-DcQfee_O.cjs → installTerminalHooks-BQZbvxsZ.cjs} +5 -40
- package/dist/{installTerminalHooks-CpNbRFPz.mjs → installTerminalHooks-By7gBMEF.mjs} +3 -38
- package/dist/lib.cjs +28 -19
- package/dist/lib.d.cts +126 -2
- package/dist/lib.d.mts +126 -2
- package/dist/lib.mjs +23 -14
- package/dist/lidState-BeDfxxEX.cjs +305 -0
- package/dist/lidState-CeEt442o.mjs +297 -0
- package/dist/limits-C-a7gr04.cjs +43 -0
- package/dist/limits-DIo7rEBR.mjs +36 -0
- package/dist/localHappyAgentAuth-C-HQyiv6.mjs +82 -0
- package/dist/localHappyAgentAuth-D6rFhaoc.cjs +86 -0
- package/dist/logger-6HYUEXNk.mjs +271 -0
- package/dist/logger-CS0mw3fa.cjs +275 -0
- package/dist/loginShellEnv-BkuYtExZ.cjs +182 -0
- package/dist/loginShellEnv-Dw8M7tkI.mjs +167 -0
- package/dist/machineMetadata-DyDbODYd.cjs +41 -0
- package/dist/machineMetadata-oOzBfDfs.mjs +38 -0
- package/dist/main-BkxJP6JQ.cjs +941 -0
- package/dist/main-BwNIAI1A.mjs +939 -0
- package/dist/manager-BQ4flvQZ.mjs +101 -0
- package/dist/manager-Cly8xVIR.cjs +105 -0
- package/dist/{mcp-BN6bKIGb.mjs → mcp-B1A4I3TK.mjs} +27 -33
- package/dist/{mcp-DGdeUUDJ.cjs → mcp-Hszd7qpS.cjs} +42 -48
- package/dist/messageBuffer-BWlGgR3A.mjs +71 -0
- package/dist/messageBuffer-fJMH6uY8.cjs +73 -0
- package/dist/mirrorProtocol-BNLtoWjO.mjs +170 -0
- package/dist/mirrorProtocol-BUtmg1ws.cjs +186 -0
- package/dist/oneShotPrompts-CL0dip9C.cjs +16 -0
- package/dist/oneShotPrompts-DT4p6RNd.mjs +12 -0
- package/dist/package-BhaKlnxz.mjs +171 -0
- package/dist/package-BwMrPwqH.cjs +173 -0
- package/dist/path-B7zW-WwE.cjs +18 -0
- package/dist/path-D2wdaNSk.mjs +15 -0
- package/dist/peerTools-BTqw97Gb.mjs +272 -0
- package/dist/peerTools-BvKtHXj8.cjs +283 -0
- package/dist/permissionMode-Cr5JCbB7.mjs +80 -0
- package/dist/permissionMode-CrV-XuMs.cjs +87 -0
- package/dist/persistence-HGW_ZVs9.mjs +372 -0
- package/dist/persistence-yKTH3-5X.cjs +414 -0
- package/dist/piExtension.mjs +2 -1
- package/dist/piRunnerArgs-BNloJCCg.cjs +66 -0
- package/dist/piRunnerArgs-BuX1UFWy.mjs +59 -0
- package/dist/{piRuntime-bkw2NAZU.mjs → piRuntime-CSGkwCKQ.mjs} +2 -2
- package/dist/{piRuntime-3qtbKujX.cjs → piRuntime-C_H7MJCM.cjs} +4 -4
- package/dist/{piTerminal-DilKwM7B.mjs → piTerminal-C8a1OrNQ.mjs} +32 -35
- package/dist/{piTerminal-BYxp3LMr.cjs → piTerminal-DNCZ6yTM.cjs} +32 -35
- package/dist/previewPath-B0rBdhy1.cjs +83 -0
- package/dist/previewPath-CXN4Vag0.mjs +81 -0
- package/dist/projectPath-BTBIMB57.mjs +10 -0
- package/dist/projectPath-ChLhdOyt.cjs +13 -0
- package/dist/registerCommonHandlers-Ccvsz7Ou.mjs +536 -0
- package/dist/registerCommonHandlers-DskirN-_.cjs +538 -0
- package/dist/registerKillSessionHandler-DZeLXWcB.cjs +361 -0
- package/dist/registerKillSessionHandler-GYvHdipX.mjs +358 -0
- package/dist/remoteSessionClient-B0LraSB9.cjs +634 -0
- package/dist/remoteSessionClient-CYnXRynE.mjs +612 -0
- package/dist/resources-BdFBQi8r.mjs +117 -0
- package/dist/resources-JDPugcDq.cjs +120 -0
- package/dist/run-CJzxoHVh.cjs +5762 -0
- package/dist/run-D3ifupU3.mjs +5739 -0
- package/dist/runClaude-Ba01qT-u.mjs +848 -0
- package/dist/runClaude-Da3RirzE.cjs +850 -0
- package/dist/runClaudeDeps-DOt79DjU.mjs +3624 -0
- package/dist/runClaudeDeps-DOzEBMls.cjs +3649 -0
- package/dist/{runGemini-Cjii069p.mjs → runGemini-BGeTz57m.mjs} +58 -31
- package/dist/{runGemini-28O2sqyI.cjs → runGemini-DSr9LIxL.cjs} +202 -175
- package/dist/{runOpenClaw-CiMZad-H.cjs → runOpenClaw-B1mTxN35.cjs} +64 -65
- package/dist/{runOpenClaw-jaLr7KF1.mjs → runOpenClaw-CogSedIc.mjs} +31 -32
- package/dist/sandbox-CG7IyDTU.cjs +187 -0
- package/dist/sandbox-DTe6d54R.mjs +180 -0
- package/dist/selfCheck-C9mVrQwf.mjs +84 -0
- package/dist/selfCheck-Dc-mNSLb.cjs +87 -0
- package/dist/{send-D5ejHPXG.cjs → send-B7J_eaG3.cjs} +16 -9
- package/dist/{send-C9mOtChi.mjs → send-CBXJKc6O.mjs} +12 -5
- package/dist/server-CZTP_iOn.cjs +407 -0
- package/dist/server-DcVBv04l.mjs +404 -0
- package/dist/sessionLock-Bys1PNSo.cjs +205 -0
- package/dist/sessionLock-D-EG2CfG.mjs +197 -0
- package/dist/sessionModeFile-DMl3dw3r.mjs +32 -0
- package/dist/sessionModeFile-RSm0NQkH.cjs +36 -0
- package/dist/sessionOps-Dn8z85h_.cjs +783 -0
- package/dist/sessionOps-SuHW8Dfe.mjs +760 -0
- package/dist/sessionScanner-BOrGjMXL.cjs +209 -0
- package/dist/sessionScanner-CX3onqXh.mjs +206 -0
- package/dist/{sessions-W6771g3X.mjs → sessions-BEG_HTSi.mjs} +17 -8
- package/dist/{sessions-BNMPQoLE.cjs → sessions-D74aJbhL.cjs} +37 -28
- package/dist/setupOfflineReconnection-ByElY60m.cjs +74 -0
- package/dist/setupOfflineReconnection-DpvWTt69.mjs +72 -0
- package/dist/smallFastModel-BLNgMrwR.mjs +18 -0
- package/dist/smallFastModel-D_uxGMo3.cjs +21 -0
- package/dist/{spawn-D0QVhzRb.mjs → spawn-B7yro0b2.mjs} +26 -38
- package/dist/{spawn-Ddtom1cb.cjs → spawn-DgR0RKRs.cjs} +52 -64
- package/dist/spawnAgents-BGQiEF-w.cjs +52 -0
- package/dist/spawnAgents-DZzZ9jMI.mjs +45 -0
- package/dist/spawnHappyCLI-B2H5s4B_.mjs +64 -0
- package/dist/spawnHappyCLI-DBhwZT7O.cjs +66 -0
- package/dist/specialCommands-BSqhYg5X.mjs +52 -0
- package/dist/specialCommands-DSsTeRej.cjs +54 -0
- package/dist/startFileWatcher-B4VsLgjD.cjs +71 -0
- package/dist/startFileWatcher-DErVWZE4.mjs +69 -0
- package/dist/startupPlan-BLMbfQIH.cjs +12 -0
- package/dist/startupPlan-DFMyNgAs.mjs +10 -0
- package/dist/streamRelay-BiKw59Cc.cjs +233 -0
- package/dist/streamRelay-FPVbHpbM.mjs +231 -0
- package/dist/sync-C8gyQFMc.mjs +69 -0
- package/dist/sync-DccIKZ5_.cjs +71 -0
- package/dist/systemPrompt-BwTPWfMu.cjs +97 -0
- package/dist/systemPrompt-DX521JJz.mjs +94 -0
- package/dist/{teams-CqSwtw2O.mjs → teams-C32F1hqd.mjs} +9 -40
- package/dist/{teams-BZ1tayg_.cjs → teams-Dl8g8kTI.cjs} +14 -45
- package/dist/template-BaHAmH2K.cjs +129 -0
- package/dist/template-CiOPa2rd.mjs +121 -0
- package/dist/{terminalTitleSuggest-DKgX3osW.mjs → terminalTitleSuggest-CeKsX_V6.mjs} +1 -9
- package/dist/{terminalTitleSuggest-Dji8I0Jy.cjs → terminalTitleSuggest-DhvRFX3H.cjs} +0 -10
- package/dist/terminals-B_bfC9c2.cjs +250 -0
- package/dist/terminals-Dlg0x3nY.mjs +235 -0
- package/dist/time-D0XyHOid.mjs +38 -0
- package/dist/time-DCuTyook.cjs +42 -0
- package/dist/titleGenerator-CQ8CoGhg.mjs +166 -0
- package/dist/titleGenerator-ChzMJsyg.cjs +172 -0
- package/dist/tmuxSocket-B8Yx20sT.mjs +19 -0
- package/dist/tmuxSocket-D-Xt6Vn-.cjs +24 -0
- package/dist/{todo-BYMg0fR0.cjs → todo-BvUOTa8G.cjs} +15 -12
- package/dist/{todo-BbusB1zM.mjs → todo-CZ6cFrrn.mjs} +11 -8
- package/dist/tools-BZtHF_qQ.mjs +177 -0
- package/dist/tools-Be3AoAwu.cjs +63 -0
- package/dist/tools-DQj0AeLk.cjs +180 -0
- package/dist/tools-DkluDcG4.mjs +60 -0
- package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
- package/dist/trimIdent-BAjmv_TG.mjs +20 -0
- package/dist/types-CRS-srZK.cjs +66 -0
- package/dist/types-CkqAACXx.mjs +64 -0
- package/dist/uninstall-BkLzaJ6I.mjs +47 -0
- package/dist/uninstall-x-TUtf5H.cjs +49 -0
- package/package.json +7 -7
- package/dist/agentGuidance-CrR9l62U.mjs +0 -2145
- package/dist/agentGuidance-DwOWbX55.cjs +0 -2217
- package/dist/index-BG932eCh.cjs +0 -19655
- package/dist/index-CVCt3kSt.mjs +0 -19591
- package/dist/index-D5ScEErM.cjs +0 -58
- package/dist/index-DgO0FHOx.mjs +0 -52
- package/dist/limits-D2uN090G.mjs +0 -2528
- package/dist/limits-kqFqhfsh.cjs +0 -2652
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { c as claimSessionOrExit, a as applyServerSnapshot, w as withoutServerSnapshot, m as mergeReconnectMetadata } from './sessionLock-D-EG2CfG.mjs';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { c as connectionState, A as ApiClient, s as startOfflineReconnection } from './api-CLMUr-SX.mjs';
|
|
5
|
+
import { l as logger } from './logger-6HYUEXNk.mjs';
|
|
6
|
+
import { p as packageJson } from './package-BhaKlnxz.mjs';
|
|
7
|
+
import { readSettings } from './persistence-HGW_ZVs9.mjs';
|
|
8
|
+
import { s as spawnOriginTags } from './createSessionMetadata-CVVkGjxV.mjs';
|
|
9
|
+
import { P as PROMPT_QUEUE_CAPABILITY } from './index-qdS1friN.mjs';
|
|
10
|
+
import { c as configuration } from './configuration-RfeNjFmd.mjs';
|
|
11
|
+
import { n as notifyDaemonSessionStarted } from './controlClient-BGbDcgWS.mjs';
|
|
12
|
+
import { g as getInitialMachineMetadata } from './machineMetadata-oOzBfDfs.mjs';
|
|
13
|
+
import { p as projectPath } from './projectPath-BTBIMB57.mjs';
|
|
14
|
+
import { resolve, join } from 'node:path';
|
|
15
|
+
import { a as applySandboxPermissionPolicy, r as resolveInitialClaudePermissionMode, m as mapToClaudeMode, b as resolveRemoteClaudePermissionMode, c as reconcilePublishedPermissionMode } from './permissionMode-Cr5JCbB7.mjs';
|
|
16
|
+
import { d as decodeBase64, e as encodeBase64 } from './authRelay-DSAvg1uv.mjs';
|
|
17
|
+
import { readFile } from 'node:fs/promises';
|
|
18
|
+
import { D as DEFAULT_CLAUDE_PERMISSION_MODE } from './defaultPermissionMode-BnxJPVHT.mjs';
|
|
19
|
+
import { C as CLAUDE_ATTACHMENT_KINDS, s as stripAttachmentManifest } from './attachmentContent-_kxRznNf.mjs';
|
|
20
|
+
import { p as planClaudeStartup } from './startupPlan-DFMyNgAs.mjs';
|
|
21
|
+
import 'node:fs';
|
|
22
|
+
import 'axios';
|
|
23
|
+
import 'node:events';
|
|
24
|
+
import 'socket.io-client';
|
|
25
|
+
import './attachmentLimits-yba1uHFO.mjs';
|
|
26
|
+
import 'zod';
|
|
27
|
+
import './limits-DIo7rEBR.mjs';
|
|
28
|
+
import './time-D0XyHOid.mjs';
|
|
29
|
+
import './lidState-CeEt442o.mjs';
|
|
30
|
+
import 'os';
|
|
31
|
+
import 'child_process';
|
|
32
|
+
import './registerCommonHandlers-Ccvsz7Ou.mjs';
|
|
33
|
+
import 'util';
|
|
34
|
+
import 'fs/promises';
|
|
35
|
+
import 'crypto';
|
|
36
|
+
import 'path';
|
|
37
|
+
import 'cross-spawn';
|
|
38
|
+
import '@paralleldrive/cuid2';
|
|
39
|
+
import './sync-C8gyQFMc.mjs';
|
|
40
|
+
import './backgroundTaskActivity-8PdPxrvy.mjs';
|
|
41
|
+
import './contentLogMetadata-BlWI0egu.mjs';
|
|
42
|
+
import 'chalk';
|
|
43
|
+
import 'node:util';
|
|
44
|
+
import './detectCLI-CSGMlV51.mjs';
|
|
45
|
+
import 'fs';
|
|
46
|
+
import './localHappyAgentAuth-C-HQyiv6.mjs';
|
|
47
|
+
import 'tweetnacl';
|
|
48
|
+
import 'url';
|
|
49
|
+
|
|
50
|
+
const DEFAULT_CLAUDE_MODEL = void 0;
|
|
51
|
+
const DEFAULT_CLAUDE_EFFORT = void 0;
|
|
52
|
+
async function runClaude(credentials, options = {}) {
|
|
53
|
+
logger.debug(`[CLAUDE] ===== CLAUDE MODE STARTING =====`);
|
|
54
|
+
logger.debug(`[CLAUDE] This is the Claude agent, NOT Gemini`);
|
|
55
|
+
options = { ...options, claudeEnvVars: { ...options.claudeEnvVars, HAPPY_MANAGED: "1" } };
|
|
56
|
+
const workingDirectory = process.cwd();
|
|
57
|
+
const isAssistantVariant = process.env.HAPPY_SESSION_VARIANT === "assistant";
|
|
58
|
+
if (isAssistantVariant) {
|
|
59
|
+
try {
|
|
60
|
+
const { bootstrapAssistantHome } = await import('./bootstrap-hxXPCFMx.mjs');
|
|
61
|
+
await bootstrapAssistantHome();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger.debug("[START] Assistant home bootstrap failed:", error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
logger.debug(`[START] Happy process started pid=${process.pid} node=${process.version} cwd=${workingDirectory}`);
|
|
67
|
+
logger.debug(`[START] Options: startedBy=${options.startedBy}, startingMode=${options.startingMode}`);
|
|
68
|
+
if (options.startedBy === "daemon" && options.startingMode === "local") {
|
|
69
|
+
throw new Error("Daemon-spawned sessions cannot use local/interactive mode. Use --happy-starting-mode remote or spawn sessions directly from terminal.");
|
|
70
|
+
}
|
|
71
|
+
const depsLoad = import('./runClaudeDeps-DOt79DjU.mjs');
|
|
72
|
+
depsLoad.catch(() => {
|
|
73
|
+
});
|
|
74
|
+
const reconnectRequested = Boolean(
|
|
75
|
+
process.env.HAPPY_RECONNECT_SESSION_ID && process.env.HAPPY_RECONNECT_ENCRYPTION_KEY && process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT
|
|
76
|
+
);
|
|
77
|
+
const startupPlan = planClaudeStartup({ startedBy: options.startedBy, reconnect: reconnectRequested });
|
|
78
|
+
connectionState.setBackend("Claude");
|
|
79
|
+
const api = await ApiClient.create(credentials);
|
|
80
|
+
let state = {};
|
|
81
|
+
const settings = await readSettings();
|
|
82
|
+
let machineId = settings?.machineId;
|
|
83
|
+
const sandboxConfig = options.noSandbox ? void 0 : settings?.sandboxConfig;
|
|
84
|
+
const sandboxEnabled = Boolean(sandboxConfig?.enabled);
|
|
85
|
+
const initialPermissionMode = applySandboxPermissionPolicy(
|
|
86
|
+
resolveInitialClaudePermissionMode(options.permissionMode ?? DEFAULT_CLAUDE_PERMISSION_MODE, options.claudeArgs),
|
|
87
|
+
sandboxEnabled
|
|
88
|
+
);
|
|
89
|
+
const dangerouslySkipPermissions = initialPermissionMode === "bypassPermissions" || initialPermissionMode === "yolo" || sandboxEnabled || Boolean(options.claudeArgs?.includes("--dangerously-skip-permissions"));
|
|
90
|
+
if (!machineId) {
|
|
91
|
+
console.error(`[START] No machine ID found in settings, which is unexpected since authAndSetupMachineIfNeeded should have created it. Please report this issue on https://github.com/Mereithhh/very-happy/issues`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
logger.debug(`Using machineId: ${machineId}`);
|
|
95
|
+
const sessionTag = randomUUID();
|
|
96
|
+
if (startupPlan.registerMachine) {
|
|
97
|
+
await api.getOrCreateMachine({
|
|
98
|
+
machineId,
|
|
99
|
+
metadata: getInitialMachineMetadata()
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const forkedFromSessionId = process.env.HAPPY_FORKED_FROM_SESSION_ID;
|
|
103
|
+
const forkedFromMessageId = process.env.HAPPY_FORKED_FROM_MESSAGE_ID;
|
|
104
|
+
const importedFromClaudeSessionId = process.env.HAPPY_IMPORTED_FROM_CLAUDE_SESSION_ID;
|
|
105
|
+
const importTitle = process.env.HAPPY_IMPORT_TITLE;
|
|
106
|
+
const originTags = spawnOriginTags(process.env, "claude");
|
|
107
|
+
let metadata = {
|
|
108
|
+
path: workingDirectory,
|
|
109
|
+
host: os.hostname(),
|
|
110
|
+
version: packageJson.version,
|
|
111
|
+
os: os.platform(),
|
|
112
|
+
machineId,
|
|
113
|
+
homeDir: os.homedir(),
|
|
114
|
+
happyHomeDir: configuration.happyHomeDir,
|
|
115
|
+
happyLibDir: projectPath(),
|
|
116
|
+
happyToolsDir: resolve(projectPath(), "tools", "unpacked"),
|
|
117
|
+
startedFromDaemon: options.startedBy === "daemon",
|
|
118
|
+
hostPid: process.pid,
|
|
119
|
+
startedBy: options.startedBy || "terminal",
|
|
120
|
+
// Initialize lifecycle state
|
|
121
|
+
lifecycleState: "running",
|
|
122
|
+
lifecycleStateSince: Date.now(),
|
|
123
|
+
flavor: "claude",
|
|
124
|
+
...process.env.VH_TEAM_OPERATION_ID ? { teamOperationId: process.env.VH_TEAM_OPERATION_ID } : {},
|
|
125
|
+
// Advertise composer support before the first SDK response. Otherwise
|
|
126
|
+
// a brand-new session cannot pick a PDF until after sending once.
|
|
127
|
+
attachmentKinds: [...CLAUDE_ATTACHMENT_KINDS],
|
|
128
|
+
queueCancellation: true,
|
|
129
|
+
capabilities: ["claude-steer-v1", "claude-live-permission-v1", "claude-live-permission-v2", "claude-btw-v1", "claude-runtime-controls-v1", "claude-opus-5-5-v1", PROMPT_QUEUE_CAPABILITY],
|
|
130
|
+
// Effective mode this process enforces. Kept current by
|
|
131
|
+
// publishPermissionMode below; the web renders it instead of guessing.
|
|
132
|
+
permissionMode: mapToClaudeMode(initialPermissionMode ?? "default"),
|
|
133
|
+
sandbox: sandboxConfig?.enabled ? sandboxConfig : null,
|
|
134
|
+
dangerouslySkipPermissions,
|
|
135
|
+
...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
|
|
136
|
+
...forkedFromMessageId ? { forkedFromMessageId } : {},
|
|
137
|
+
...importedFromClaudeSessionId ? { importedFromClaudeSessionId } : {},
|
|
138
|
+
...importTitle ? { summary: { text: importTitle, updatedAt: Date.now() } } : {},
|
|
139
|
+
...isAssistantVariant ? { variant: "assistant" } : {},
|
|
140
|
+
// B-091/B-303: dispatched sessions (daemon spawn RPC exports
|
|
141
|
+
// HAPPY_SPAWNED_BY, B-069) are born tagged with their spawn origin so
|
|
142
|
+
// every list shows where the work came from. NOT on the meta-agent
|
|
143
|
+
// itself — that one is the variant above and never joins the lists at
|
|
144
|
+
// all. (Shared helper: the other flavors get the same tag via
|
|
145
|
+
// createSessionMetadata.)
|
|
146
|
+
...originTags ? { tags: originTags } : {}
|
|
147
|
+
};
|
|
148
|
+
const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
|
|
149
|
+
const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
|
|
150
|
+
const reconnectVariant = process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT;
|
|
151
|
+
const reconnectSeq = process.env.HAPPY_RECONNECT_SEQ;
|
|
152
|
+
const reconnectMetadataVersion = process.env.HAPPY_RECONNECT_METADATA_VERSION;
|
|
153
|
+
const reconnectAgentStateVersion = process.env.HAPPY_RECONNECT_AGENT_STATE_VERSION;
|
|
154
|
+
let response;
|
|
155
|
+
if (reconnectSessionId && reconnectKeyBase64 && reconnectVariant) {
|
|
156
|
+
logger.debug(`[START] Reconnecting to existing session ${reconnectSessionId}`);
|
|
157
|
+
response = {
|
|
158
|
+
id: reconnectSessionId,
|
|
159
|
+
seq: parseInt(reconnectSeq || "0", 10),
|
|
160
|
+
encryptionKey: decodeBase64(reconnectKeyBase64),
|
|
161
|
+
encryptionVariant: reconnectVariant,
|
|
162
|
+
metadata,
|
|
163
|
+
metadataVersion: parseInt(reconnectMetadataVersion || "0", 10),
|
|
164
|
+
agentState: state,
|
|
165
|
+
agentStateVersion: parseInt(reconnectAgentStateVersion || "0", 10)
|
|
166
|
+
};
|
|
167
|
+
} else {
|
|
168
|
+
response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
169
|
+
}
|
|
170
|
+
if (!response) {
|
|
171
|
+
let offlineSessionId = null;
|
|
172
|
+
const { claudeLocal } = await import('./claudeLocal-BaY4fZKi.mjs');
|
|
173
|
+
const { createSessionScanner: createSessionScanner2 } = await depsLoad;
|
|
174
|
+
const reconnection = startOfflineReconnection({
|
|
175
|
+
serverUrl: configuration.serverUrl,
|
|
176
|
+
onReconnected: async () => {
|
|
177
|
+
const resp = await api.getOrCreateSession({ tag: randomUUID(), metadata, state });
|
|
178
|
+
if (!resp) throw new Error("Server unavailable");
|
|
179
|
+
const session2 = api.sessionSyncClient(resp);
|
|
180
|
+
const scanner = await createSessionScanner2({
|
|
181
|
+
sessionId: null,
|
|
182
|
+
workingDirectory,
|
|
183
|
+
onMessage: (msg) => session2.sendClaudeSessionMessage(msg)
|
|
184
|
+
});
|
|
185
|
+
if (offlineSessionId) scanner.onNewSession(offlineSessionId);
|
|
186
|
+
return { session: session2, scanner };
|
|
187
|
+
},
|
|
188
|
+
onNotify: console.log,
|
|
189
|
+
onCleanup: () => {
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
try {
|
|
193
|
+
await claudeLocal({
|
|
194
|
+
path: workingDirectory,
|
|
195
|
+
sessionId: null,
|
|
196
|
+
onSessionFound: (id) => {
|
|
197
|
+
offlineSessionId = id;
|
|
198
|
+
},
|
|
199
|
+
onThinkingChange: () => {
|
|
200
|
+
},
|
|
201
|
+
abort: new AbortController().signal,
|
|
202
|
+
claudeEnvVars: options.claudeEnvVars,
|
|
203
|
+
claudeArgs: options.claudeArgs,
|
|
204
|
+
mcpServers: {},
|
|
205
|
+
allowedTools: [],
|
|
206
|
+
sandboxConfig
|
|
207
|
+
});
|
|
208
|
+
} finally {
|
|
209
|
+
reconnection.cancel();
|
|
210
|
+
}
|
|
211
|
+
process.exit(0);
|
|
212
|
+
}
|
|
213
|
+
logger.debug(`Session created: ${response.id}`);
|
|
214
|
+
await claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "claude" });
|
|
215
|
+
if (reconnectSessionId && !await api.reactivateSession(response.id)) {
|
|
216
|
+
throw new Error(`Failed to reactivate archived session ${response.id}`);
|
|
217
|
+
}
|
|
218
|
+
let reconnectSeeded = false;
|
|
219
|
+
if (reconnectSessionId) {
|
|
220
|
+
const snapshot = await api.getSession(response.id, response.encryptionKey, response.encryptionVariant);
|
|
221
|
+
if (snapshot.ok) {
|
|
222
|
+
response = applyServerSnapshot(response, snapshot.session);
|
|
223
|
+
reconnectSeeded = true;
|
|
224
|
+
logger.debug(`[START] Reconnect seeded from server: seq=${snapshot.session.seq} metadataVersion=${snapshot.session.metadataVersion}`);
|
|
225
|
+
} else {
|
|
226
|
+
response = withoutServerSnapshot(response);
|
|
227
|
+
logger.debug(`[START] Reconnect without server snapshot (${snapshot.reason}); skipping existing messages`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const earlySession = startupPlan.sessionClientBeforeWebhook ? api.sessionSyncClient(response) : null;
|
|
231
|
+
try {
|
|
232
|
+
logger.debug(`[START] Reporting session ${response.id} to daemon`);
|
|
233
|
+
const result = await notifyDaemonSessionStarted(response.id, metadata, {
|
|
234
|
+
encryptionKey: encodeBase64(response.encryptionKey),
|
|
235
|
+
encryptionVariant: response.encryptionVariant,
|
|
236
|
+
seq: response.seq,
|
|
237
|
+
metadataVersion: response.metadataVersion,
|
|
238
|
+
agentStateVersion: response.agentStateVersion
|
|
239
|
+
});
|
|
240
|
+
if (result.error) {
|
|
241
|
+
logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
|
|
242
|
+
} else {
|
|
243
|
+
logger.debug(`[START] Reported session ${response.id} to daemon`);
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
logger.debug("[START] Failed to report to daemon (may not be running):", error);
|
|
247
|
+
}
|
|
248
|
+
if (!startupPlan.registerMachine) {
|
|
249
|
+
void Promise.resolve().then(() => api.getOrCreateMachine({ machineId, metadata: getInitialMachineMetadata() })).catch((error) => logger.debug("[START] Background machine registration failed:", error));
|
|
250
|
+
}
|
|
251
|
+
let deps;
|
|
252
|
+
try {
|
|
253
|
+
deps = await depsLoad;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logger.warn(`[START] Failed to load the Claude runner modules for session ${response.id}; marking it offline and exiting: ${error instanceof Error ? error.message : String(error)}`);
|
|
256
|
+
try {
|
|
257
|
+
await earlySession?.close();
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
await api.deactivateSession(response.id);
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
const {
|
|
267
|
+
loop,
|
|
268
|
+
MessageQueue2,
|
|
269
|
+
claudeModeHash,
|
|
270
|
+
parseSpecialCommand,
|
|
271
|
+
getEnvironmentInfo,
|
|
272
|
+
startHappyServer,
|
|
273
|
+
startHookServer,
|
|
274
|
+
EditReportThrottle,
|
|
275
|
+
extractClaudeEditPaths,
|
|
276
|
+
reportSessionEditToDaemon,
|
|
277
|
+
generateHookSettingsFile,
|
|
278
|
+
cleanupHookSettingsFile,
|
|
279
|
+
registerKillSessionHandler,
|
|
280
|
+
SessionExitGate,
|
|
281
|
+
exitIntentFromArchiveFlag,
|
|
282
|
+
settleSessionOnExit,
|
|
283
|
+
registerSideQuestionHandler,
|
|
284
|
+
writeSideQuestionSettingsFile,
|
|
285
|
+
claudeCheckSession,
|
|
286
|
+
createSessionScanner,
|
|
287
|
+
getProjectPath,
|
|
288
|
+
RawJSONLinesSchema,
|
|
289
|
+
TitleGenerator,
|
|
290
|
+
BoardAnalyzer,
|
|
291
|
+
FileRateLimiter,
|
|
292
|
+
createSelfReportState,
|
|
293
|
+
withAssistantDenylist,
|
|
294
|
+
contentLogMetadata
|
|
295
|
+
} = deps;
|
|
296
|
+
logger.debugLargeJson("[START] Happy process started", getEnvironmentInfo());
|
|
297
|
+
const session = earlySession ?? api.sessionSyncClient(response, reconnectSeeded ? { initialSeq: response.seq } : void 0);
|
|
298
|
+
if (reconnectSessionId) {
|
|
299
|
+
session.suppressNextArchiveSignal();
|
|
300
|
+
if (!reconnectSeeded) session.skipExistingMessages();
|
|
301
|
+
void session.cancelUndeliveredQueuedInputs();
|
|
302
|
+
session.updateMetadata((meta) => ({
|
|
303
|
+
...mergeReconnectMetadata(meta, metadata, Date.now()),
|
|
304
|
+
queueCancellation: true,
|
|
305
|
+
// B-262 batch 2 (B5): a reconnected process re-publishes the mode
|
|
306
|
+
// IT enforces; the server copy may still carry the previous
|
|
307
|
+
// process's value.
|
|
308
|
+
permissionMode: mapToClaudeMode(initialPermissionMode ?? "default")
|
|
309
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
const forkClaudeSessionId = process.env.HAPPY_FORK_CLAUDE_SESSION_ID;
|
|
312
|
+
if (!reconnectSessionId && forkClaudeSessionId) {
|
|
313
|
+
const jsonlPath = join(getProjectPath(workingDirectory), `${forkClaudeSessionId}.jsonl`);
|
|
314
|
+
try {
|
|
315
|
+
const file = await readFile(jsonlPath, "utf-8");
|
|
316
|
+
const lines = file.split("\n");
|
|
317
|
+
let backfilled = 0;
|
|
318
|
+
for (const line of lines) {
|
|
319
|
+
if (line.trim().length === 0) continue;
|
|
320
|
+
let parsed;
|
|
321
|
+
try {
|
|
322
|
+
parsed = JSON.parse(line);
|
|
323
|
+
} catch {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const result = RawJSONLinesSchema.safeParse(parsed);
|
|
327
|
+
if (!result.success) continue;
|
|
328
|
+
session.sendClaudeSessionMessage(result.data);
|
|
329
|
+
backfilled += 1;
|
|
330
|
+
}
|
|
331
|
+
logger.debug(`[FORK BACKFILL] Replayed ${backfilled} historical messages from ${jsonlPath}`);
|
|
332
|
+
session.updateMetadata((meta) => ({ ...meta, claudeSessionId: forkClaudeSessionId }));
|
|
333
|
+
} catch (error) {
|
|
334
|
+
logger.debug(`[FORK BACKFILL] Failed to read ${jsonlPath}:`, error);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const recentAppPromptsMaxAgeMs = 5 * 60 * 1e3;
|
|
338
|
+
const recentAppPrompts = [];
|
|
339
|
+
const promptDedupeKey = (text) => stripAttachmentManifest(text).trim();
|
|
340
|
+
const recordAppPrompt = (text) => {
|
|
341
|
+
const now = Date.now();
|
|
342
|
+
recentAppPrompts.push({ text: promptDedupeKey(text), addedAt: now });
|
|
343
|
+
const cutoff = now - recentAppPromptsMaxAgeMs;
|
|
344
|
+
while (recentAppPrompts.length > 0 && recentAppPrompts[0].addedAt < cutoff) {
|
|
345
|
+
recentAppPrompts.shift();
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const consumeAppPrompt = (text) => {
|
|
349
|
+
const cutoff = Date.now() - recentAppPromptsMaxAgeMs;
|
|
350
|
+
const key = promptDedupeKey(text);
|
|
351
|
+
for (let i = 0; i < recentAppPrompts.length; i++) {
|
|
352
|
+
const entry = recentAppPrompts[i];
|
|
353
|
+
if (entry.addedAt < cutoff) continue;
|
|
354
|
+
if (entry.text === key) {
|
|
355
|
+
recentAppPrompts.splice(i, 1);
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
};
|
|
361
|
+
let currentRunMode = options.startingMode ?? "local";
|
|
362
|
+
const titleGenerator = new TitleGenerator(session);
|
|
363
|
+
const boardSelfReport = createSelfReportState();
|
|
364
|
+
const boardAnalyzer = new BoardAnalyzer(session, {
|
|
365
|
+
enabled: settings?.boardLlm === true,
|
|
366
|
+
selfReportState: boardSelfReport,
|
|
367
|
+
fetchTasks: async () => {
|
|
368
|
+
const item = await api.kvGet("vh.board-tasks.v1");
|
|
369
|
+
if (!item) return null;
|
|
370
|
+
try {
|
|
371
|
+
const parsed = JSON.parse(Buffer.from(item.value, "base64").toString("utf8"));
|
|
372
|
+
if (!Array.isArray(parsed.tasks)) return null;
|
|
373
|
+
return parsed.tasks.filter((t) => typeof t?.id === "string" && typeof t?.title === "string" && t.status === "open").map((t) => ({ id: t.id, title: t.title }));
|
|
374
|
+
} catch {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
rateLimiter: new FileRateLimiter(resolve(configuration.happyHomeDir, "board-analyzer-rate.json"))
|
|
379
|
+
});
|
|
380
|
+
session.on("claude-session-message", (body) => boardAnalyzer.noteClaudeMessage(body));
|
|
381
|
+
session.on("turn-ended", () => boardAnalyzer.onTurnEnd());
|
|
382
|
+
const editThrottle = new EditReportThrottle();
|
|
383
|
+
session.on("claude-session-message", (body) => {
|
|
384
|
+
try {
|
|
385
|
+
for (const edit of editThrottle.take(extractClaudeEditPaths(body))) {
|
|
386
|
+
reportSessionEditToDaemon({ sessionId: response.id, path: edit.path, tool: edit.tool, cwd: workingDirectory, ...edit.at !== void 0 ? { at: edit.at } : {} });
|
|
387
|
+
}
|
|
388
|
+
} catch (error) {
|
|
389
|
+
logger.debug("[START] edit report failed:", error);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
options = { ...options, claudeEnvVars: { ...options.claudeEnvVars, VH_PEER_SESSION_ID: response.id } };
|
|
393
|
+
const initialScannerSessionId = forkClaudeSessionId ?? (metadata.claudeSessionId ?? null);
|
|
394
|
+
const remoteScanner = await createSessionScanner({
|
|
395
|
+
sessionId: initialScannerSessionId,
|
|
396
|
+
workingDirectory,
|
|
397
|
+
onMessage: (raw) => {
|
|
398
|
+
if (currentRunMode !== "remote") return;
|
|
399
|
+
if (raw.type !== "user") return;
|
|
400
|
+
if (raw.isSidechain) return;
|
|
401
|
+
const content = raw.message?.content;
|
|
402
|
+
if (typeof content !== "string") return;
|
|
403
|
+
if (content.trim().length === 0) return;
|
|
404
|
+
if (consumeAppPrompt(content)) return;
|
|
405
|
+
titleGenerator.maybeGenerate(content);
|
|
406
|
+
boardAnalyzer.noteUserMessage(content);
|
|
407
|
+
session.sendClaudeSessionMessage(raw);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
const happyServer = await startHappyServer(session, { assistant: isAssistantVariant, selfReportState: boardSelfReport });
|
|
411
|
+
logger.debug(`[START] Happy MCP server started at ${happyServer.url}`);
|
|
412
|
+
let currentSession = null;
|
|
413
|
+
const hookServer = await startHookServer({
|
|
414
|
+
onSessionHook: (sessionId, data) => {
|
|
415
|
+
logger.debug(`[START] Session hook received: ${sessionId}`, contentLogMetadata(data));
|
|
416
|
+
remoteScanner.onNewSession(sessionId, { treatExistingAsProcessed: true });
|
|
417
|
+
if (currentSession) {
|
|
418
|
+
const previousSessionId = currentSession.sessionId;
|
|
419
|
+
if (previousSessionId !== sessionId) {
|
|
420
|
+
logger.debug(`[START] Claude session ID changed: ${previousSessionId} -> ${sessionId}`);
|
|
421
|
+
currentSession.onSessionFound(sessionId);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
logger.debug(`[START] Hook server started on port ${hookServer.port}`);
|
|
427
|
+
const hookSettingsPath = generateHookSettingsFile(hookServer.port);
|
|
428
|
+
logger.debug(`[START] Generated hook settings file: ${hookSettingsPath}`);
|
|
429
|
+
const logPath = logger.logFilePath;
|
|
430
|
+
logger.infoDeveloper(`Session: ${response.id}`);
|
|
431
|
+
logger.infoDeveloper(`Logs: ${logPath}`);
|
|
432
|
+
session.updateAgentState((currentState) => ({
|
|
433
|
+
...currentState,
|
|
434
|
+
controlledByUser: options.startingMode !== "remote"
|
|
435
|
+
}));
|
|
436
|
+
const messageQueue = new MessageQueue2(claudeModeHash);
|
|
437
|
+
const promptQueueDrain = session.attachPromptQueueDrain({
|
|
438
|
+
isIdle: () => messageQueue.size() === 0 && messageQueue.isWaiting()
|
|
439
|
+
});
|
|
440
|
+
messageQueue.setOnWait(() => promptQueueDrain.onIdle());
|
|
441
|
+
messageQueue.setOnDiscard((entries) => {
|
|
442
|
+
const byReason = /* @__PURE__ */ new Map();
|
|
443
|
+
for (const entry of entries) {
|
|
444
|
+
if (!entry.sourceId) continue;
|
|
445
|
+
const list = byReason.get(entry.reason) ?? [];
|
|
446
|
+
list.push(entry.sourceId);
|
|
447
|
+
byReason.set(entry.reason, list);
|
|
448
|
+
}
|
|
449
|
+
for (const [reason, keys] of byReason) session.sendQueueCancelReason(keys, reason);
|
|
450
|
+
});
|
|
451
|
+
session.rpcHandlerManager.registerHandler("cancelQueuedMessage", async (params) => ({
|
|
452
|
+
removed: messageQueue.cancelPending(
|
|
453
|
+
typeof params?.localKey === "string" ? params.localKey : void 0,
|
|
454
|
+
typeof params?.text === "string" ? params.text : void 0
|
|
455
|
+
)
|
|
456
|
+
}));
|
|
457
|
+
let currentPermissionMode = initialPermissionMode;
|
|
458
|
+
let publishedPermissionMode = mapToClaudeMode(initialPermissionMode ?? "default");
|
|
459
|
+
const publishPermissionMode = (mode) => {
|
|
460
|
+
currentPermissionMode = mode;
|
|
461
|
+
const published = mapToClaudeMode(mode ?? "default");
|
|
462
|
+
if (published === publishedPermissionMode) return;
|
|
463
|
+
publishedPermissionMode = published;
|
|
464
|
+
session.updateMetadata((meta) => ({ ...meta, permissionMode: published }));
|
|
465
|
+
};
|
|
466
|
+
const publishEffectivePermissionMode = (effective) => {
|
|
467
|
+
const decision = reconcilePublishedPermissionMode({
|
|
468
|
+
intent: mapToClaudeMode(currentPermissionMode ?? "default"),
|
|
469
|
+
published: publishedPermissionMode,
|
|
470
|
+
effective
|
|
471
|
+
});
|
|
472
|
+
if (!decision) return;
|
|
473
|
+
if (decision.mismatch) {
|
|
474
|
+
logger.warn(`[loop] Claude Code enforces permission mode '${effective}' but this process asked for '${mapToClaudeMode(currentPermissionMode ?? "default")}' \u2014 check ~/.claude settings (permissions.deny/ask/defaultMode/disableBypassPermissionsMode). Publishing the effective mode.`);
|
|
475
|
+
}
|
|
476
|
+
if (decision.publish !== publishedPermissionMode) {
|
|
477
|
+
publishedPermissionMode = decision.publish;
|
|
478
|
+
session.updateMetadata((meta) => ({ ...meta, permissionMode: decision.publish }));
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
let lastExplicitModeSwitchAt = 0;
|
|
482
|
+
let currentModel = options.model ?? DEFAULT_CLAUDE_MODEL;
|
|
483
|
+
let currentFallbackModel = void 0;
|
|
484
|
+
let currentCustomSystemPrompt = void 0;
|
|
485
|
+
let currentAppendSystemPrompt = void 0;
|
|
486
|
+
let currentAllowedTools = void 0;
|
|
487
|
+
let currentDisallowedTools = withAssistantDenylist(void 0, isAssistantVariant);
|
|
488
|
+
let currentEffort = DEFAULT_CLAUDE_EFFORT;
|
|
489
|
+
registerSideQuestionHandler(session.rpcHandlerManager, {
|
|
490
|
+
getClaudeSessionId: () => {
|
|
491
|
+
const id = currentSession?.sessionId ?? null;
|
|
492
|
+
return id && claudeCheckSession(id, workingDirectory) ? id : null;
|
|
493
|
+
},
|
|
494
|
+
getModel: () => currentModel,
|
|
495
|
+
cwd: workingDirectory,
|
|
496
|
+
getEnv: () => options.claudeEnvVars,
|
|
497
|
+
settingsPath: writeSideQuestionSettingsFile(configuration.happyHomeDir),
|
|
498
|
+
// B-482: in remote mode the launcher parks the live Query's /btw entry
|
|
499
|
+
// on the session while the Query is alive; local mode never sets it.
|
|
500
|
+
getLiveQuery: () => currentSession?.getSideQuestionLive() ?? null
|
|
501
|
+
});
|
|
502
|
+
const resetCurrentModeDefaults = () => {
|
|
503
|
+
currentModel = options.model ?? DEFAULT_CLAUDE_MODEL;
|
|
504
|
+
currentFallbackModel = void 0;
|
|
505
|
+
currentCustomSystemPrompt = void 0;
|
|
506
|
+
currentAppendSystemPrompt = void 0;
|
|
507
|
+
currentAllowedTools = void 0;
|
|
508
|
+
currentDisallowedTools = withAssistantDenylist(void 0, isAssistantVariant);
|
|
509
|
+
currentEffort = DEFAULT_CLAUDE_EFFORT;
|
|
510
|
+
logger.debug("[loop] Reset current mode defaults after abort");
|
|
511
|
+
};
|
|
512
|
+
session.onFileEvent((fileEvent) => {
|
|
513
|
+
const ev = fileEvent.content.data.ev;
|
|
514
|
+
logger.debug(`[loop] File event received: ${ev.name} (${ev.size} bytes, ref: ${ev.ref})`);
|
|
515
|
+
const downloadPromise = (async () => {
|
|
516
|
+
try {
|
|
517
|
+
const decrypted = await session.downloadAndDecryptAttachment(ev.ref);
|
|
518
|
+
if (!decrypted) {
|
|
519
|
+
logger.debug(`[loop] Failed to decrypt attachment: ${ev.name}`);
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
logger.debug(`[loop] Attachment decrypted: ${ev.name} (${decrypted.length} bytes)`);
|
|
523
|
+
return { data: decrypted, mimeType: ev.mimeType ?? "image/jpeg", name: ev.name };
|
|
524
|
+
} catch (error) {
|
|
525
|
+
logger.debug(`[loop] Failed to download attachment: ${ev.name}`, { error });
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
})();
|
|
529
|
+
session.trackAttachmentDownload(downloadPromise);
|
|
530
|
+
});
|
|
531
|
+
session.onUserMessage(async (message) => {
|
|
532
|
+
if (message?.content?.text) {
|
|
533
|
+
titleGenerator.maybeGenerate(message.content.text);
|
|
534
|
+
boardAnalyzer.noteUserMessage(message.content.text);
|
|
535
|
+
}
|
|
536
|
+
const attachmentsForThisMessage = await session.drainAttachmentsForUserMessage();
|
|
537
|
+
let messagePermissionMode = currentPermissionMode;
|
|
538
|
+
const messageCreatedAt = message.createdAt;
|
|
539
|
+
const staleModeSnapshot = message.meta?.permissionMode !== void 0 && typeof messageCreatedAt === "number" && messageCreatedAt < lastExplicitModeSwitchAt;
|
|
540
|
+
if (staleModeSnapshot) {
|
|
541
|
+
logger.debug(`[loop] Ignoring stale meta.permissionMode=${message.meta?.permissionMode} (message ${messageCreatedAt} predates explicit switch ${lastExplicitModeSwitchAt}); keeping ${currentPermissionMode}`);
|
|
542
|
+
} else if (message.meta?.permissionMode) {
|
|
543
|
+
const previousPermissionMode = currentPermissionMode;
|
|
544
|
+
messagePermissionMode = resolveRemoteClaudePermissionMode(
|
|
545
|
+
currentPermissionMode,
|
|
546
|
+
message.meta.permissionMode,
|
|
547
|
+
sandboxEnabled
|
|
548
|
+
);
|
|
549
|
+
publishPermissionMode(messagePermissionMode);
|
|
550
|
+
const ignoredDefaultDowngrade = (previousPermissionMode === "bypassPermissions" || previousPermissionMode === "yolo") && message.meta.permissionMode === "default" && currentPermissionMode === previousPermissionMode;
|
|
551
|
+
if (ignoredDefaultDowngrade) {
|
|
552
|
+
logger.debug(`[loop] Ignoring permission mode downgrade from ${previousPermissionMode} to default`);
|
|
553
|
+
} else {
|
|
554
|
+
logger.debug(`[loop] Permission mode updated from user message to: ${currentPermissionMode}`);
|
|
555
|
+
}
|
|
556
|
+
} else {
|
|
557
|
+
logger.debug(`[loop] User message received with no permission mode override, using current: ${currentPermissionMode}`);
|
|
558
|
+
}
|
|
559
|
+
let messageModel = currentModel;
|
|
560
|
+
if (message.meta?.hasOwnProperty("model")) {
|
|
561
|
+
messageModel = message.meta.model || void 0;
|
|
562
|
+
currentModel = messageModel;
|
|
563
|
+
logger.debug(`[loop] Model updated from user message: ${messageModel || "reset to default"}`);
|
|
564
|
+
} else {
|
|
565
|
+
logger.debug(`[loop] User message received with no model override, using current: ${currentModel || "default"}`);
|
|
566
|
+
}
|
|
567
|
+
let messageCustomSystemPrompt = currentCustomSystemPrompt;
|
|
568
|
+
if (message.meta?.hasOwnProperty("customSystemPrompt")) {
|
|
569
|
+
messageCustomSystemPrompt = message.meta.customSystemPrompt || void 0;
|
|
570
|
+
currentCustomSystemPrompt = messageCustomSystemPrompt;
|
|
571
|
+
logger.debug(`[loop] Custom system prompt updated from user message: ${messageCustomSystemPrompt ? "set" : "reset to none"}`);
|
|
572
|
+
} else {
|
|
573
|
+
logger.debug(`[loop] User message received with no custom system prompt override, using current: ${currentCustomSystemPrompt ? "set" : "none"}`);
|
|
574
|
+
}
|
|
575
|
+
let messageFallbackModel = currentFallbackModel;
|
|
576
|
+
if (message.meta?.hasOwnProperty("fallbackModel")) {
|
|
577
|
+
messageFallbackModel = message.meta.fallbackModel || void 0;
|
|
578
|
+
currentFallbackModel = messageFallbackModel;
|
|
579
|
+
logger.debug(`[loop] Fallback model updated from user message: ${messageFallbackModel || "reset to none"}`);
|
|
580
|
+
} else {
|
|
581
|
+
logger.debug(`[loop] User message received with no fallback model override, using current: ${currentFallbackModel || "none"}`);
|
|
582
|
+
}
|
|
583
|
+
let messageAppendSystemPrompt = currentAppendSystemPrompt;
|
|
584
|
+
if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
|
|
585
|
+
messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
|
|
586
|
+
currentAppendSystemPrompt = messageAppendSystemPrompt;
|
|
587
|
+
logger.debug(`[loop] Append system prompt updated from user message: ${messageAppendSystemPrompt ? "set" : "reset to none"}`);
|
|
588
|
+
} else {
|
|
589
|
+
logger.debug(`[loop] User message received with no append system prompt override, using current: ${currentAppendSystemPrompt ? "set" : "none"}`);
|
|
590
|
+
}
|
|
591
|
+
let messageAllowedTools = currentAllowedTools;
|
|
592
|
+
if (message.meta?.hasOwnProperty("allowedTools")) {
|
|
593
|
+
messageAllowedTools = message.meta.allowedTools || void 0;
|
|
594
|
+
currentAllowedTools = messageAllowedTools;
|
|
595
|
+
logger.debug(`[loop] Allowed tools updated from user message: ${messageAllowedTools ? messageAllowedTools.join(", ") : "reset to none"}`);
|
|
596
|
+
} else {
|
|
597
|
+
logger.debug(`[loop] User message received with no allowed tools override, using current: ${currentAllowedTools ? currentAllowedTools.join(", ") : "none"}`);
|
|
598
|
+
}
|
|
599
|
+
let messageDisallowedTools = currentDisallowedTools;
|
|
600
|
+
if (message.meta?.hasOwnProperty("disallowedTools")) {
|
|
601
|
+
messageDisallowedTools = withAssistantDenylist(message.meta.disallowedTools || void 0, isAssistantVariant);
|
|
602
|
+
currentDisallowedTools = messageDisallowedTools;
|
|
603
|
+
logger.debug(`[loop] Disallowed tools updated from user message: ${messageDisallowedTools ? messageDisallowedTools.join(", ") : "reset to none"}`);
|
|
604
|
+
} else {
|
|
605
|
+
logger.debug(`[loop] User message received with no disallowed tools override, using current: ${currentDisallowedTools ? currentDisallowedTools.join(", ") : "none"}`);
|
|
606
|
+
}
|
|
607
|
+
let messageEffort = currentEffort;
|
|
608
|
+
const VALID_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
609
|
+
if (message.meta?.hasOwnProperty("effort")) {
|
|
610
|
+
const incoming = message.meta.effort;
|
|
611
|
+
if (incoming === null || incoming === void 0) {
|
|
612
|
+
messageEffort = void 0;
|
|
613
|
+
currentEffort = void 0;
|
|
614
|
+
logger.debug(`[loop] Effort reset to default`);
|
|
615
|
+
} else if (typeof incoming === "string" && VALID_EFFORTS.has(incoming)) {
|
|
616
|
+
messageEffort = incoming;
|
|
617
|
+
currentEffort = messageEffort;
|
|
618
|
+
logger.debug(`[loop] Effort updated from user message: ${messageEffort}`);
|
|
619
|
+
} else {
|
|
620
|
+
logger.debug(`[loop] Ignoring invalid effort from user message: ${String(incoming)}`);
|
|
621
|
+
}
|
|
622
|
+
} else {
|
|
623
|
+
logger.debug(`[loop] User message received with no effort override, using current: ${currentEffort ?? "default"}`);
|
|
624
|
+
}
|
|
625
|
+
const specialCommand = parseSpecialCommand(message.content.text);
|
|
626
|
+
if (specialCommand.type === "compact") {
|
|
627
|
+
logger.debug("[start] Detected /compact command");
|
|
628
|
+
const enhancedMode2 = {
|
|
629
|
+
permissionMode: messagePermissionMode || "default",
|
|
630
|
+
model: messageModel,
|
|
631
|
+
fallbackModel: messageFallbackModel,
|
|
632
|
+
customSystemPrompt: messageCustomSystemPrompt,
|
|
633
|
+
appendSystemPrompt: messageAppendSystemPrompt,
|
|
634
|
+
allowedTools: messageAllowedTools,
|
|
635
|
+
disallowedTools: messageDisallowedTools,
|
|
636
|
+
effort: messageEffort
|
|
637
|
+
};
|
|
638
|
+
messageQueue.pushIsolateAndClear(specialCommand.originalMessage || message.content.text, enhancedMode2, attachmentsForThisMessage, message.localKey);
|
|
639
|
+
logger.debug("[start] /compact command pushed to queue:", {
|
|
640
|
+
...contentLogMetadata(message.content.text),
|
|
641
|
+
attachmentCount: attachmentsForThisMessage?.length ?? 0,
|
|
642
|
+
hasMetadata: Boolean(message.meta)
|
|
643
|
+
});
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (specialCommand.type === "clear") {
|
|
647
|
+
logger.debug("[start] Detected /clear command");
|
|
648
|
+
const enhancedMode2 = {
|
|
649
|
+
permissionMode: messagePermissionMode || "default",
|
|
650
|
+
model: messageModel,
|
|
651
|
+
fallbackModel: messageFallbackModel,
|
|
652
|
+
customSystemPrompt: messageCustomSystemPrompt,
|
|
653
|
+
appendSystemPrompt: messageAppendSystemPrompt,
|
|
654
|
+
allowedTools: messageAllowedTools,
|
|
655
|
+
disallowedTools: messageDisallowedTools,
|
|
656
|
+
effort: messageEffort
|
|
657
|
+
};
|
|
658
|
+
messageQueue.pushIsolateAndClear(specialCommand.originalMessage || message.content.text, enhancedMode2, attachmentsForThisMessage, message.localKey);
|
|
659
|
+
logger.debug("[start] /clear command pushed to queue:", {
|
|
660
|
+
...contentLogMetadata(message.content.text),
|
|
661
|
+
attachmentCount: attachmentsForThisMessage?.length ?? 0,
|
|
662
|
+
hasMetadata: Boolean(message.meta)
|
|
663
|
+
});
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (specialCommand.type === "mcp" || specialCommand.type === "skills") {
|
|
667
|
+
if (currentRunMode === "local") {
|
|
668
|
+
logger.debug(`[start] /${specialCommand.type} in local mode \u2014 passing through to Claude Code`);
|
|
669
|
+
} else {
|
|
670
|
+
logger.debug(`[start] Detected /${specialCommand.type} command in remote mode`);
|
|
671
|
+
const metadata2 = session.getMetadata();
|
|
672
|
+
let responseText;
|
|
673
|
+
if (specialCommand.type === "mcp") {
|
|
674
|
+
const servers = metadata2?.mcpServers;
|
|
675
|
+
if (servers && servers.length > 0) {
|
|
676
|
+
responseText = "**MCP Servers**\n\n" + servers.map((s) => `- **${s.name}** \u2014 ${s.status}`).join("\n");
|
|
677
|
+
} else {
|
|
678
|
+
responseText = "No MCP servers configured. Session may still be initializing \u2014 try again after sending a message.";
|
|
679
|
+
}
|
|
680
|
+
} else {
|
|
681
|
+
const skills = metadata2?.skills ?? metadata2?.slashCommands;
|
|
682
|
+
if (skills && skills.length > 0) {
|
|
683
|
+
responseText = "**Available Skills**\n\n" + skills.map((s) => `- /${s}`).join("\n");
|
|
684
|
+
} else {
|
|
685
|
+
responseText = "No skills available. Session may still be initializing \u2014 try again after sending a message.";
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
session.sendClaudeSessionMessage({
|
|
689
|
+
type: "assistant",
|
|
690
|
+
uuid: randomUUID(),
|
|
691
|
+
parentUuid: null,
|
|
692
|
+
isSidechain: false,
|
|
693
|
+
sessionId: session.sessionId || "unknown",
|
|
694
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
695
|
+
message: {
|
|
696
|
+
role: "assistant",
|
|
697
|
+
model: "system",
|
|
698
|
+
content: [{ type: "text", text: responseText }]
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const enhancedMode = {
|
|
705
|
+
permissionMode: messagePermissionMode || "default",
|
|
706
|
+
model: messageModel,
|
|
707
|
+
fallbackModel: messageFallbackModel,
|
|
708
|
+
customSystemPrompt: messageCustomSystemPrompt,
|
|
709
|
+
appendSystemPrompt: messageAppendSystemPrompt,
|
|
710
|
+
allowedTools: messageAllowedTools,
|
|
711
|
+
disallowedTools: messageDisallowedTools,
|
|
712
|
+
effort: messageEffort
|
|
713
|
+
};
|
|
714
|
+
if (message.meta?.delivery === "steer" && currentRunMode === "remote") {
|
|
715
|
+
const accepted = await currentSession?.trySteer({
|
|
716
|
+
message: message.content.text,
|
|
717
|
+
mode: enhancedMode,
|
|
718
|
+
attachments: attachmentsForThisMessage
|
|
719
|
+
}) === true;
|
|
720
|
+
if (accepted) {
|
|
721
|
+
logger.debug("User message steered into current turn:", {
|
|
722
|
+
...contentLogMetadata(message.content.text),
|
|
723
|
+
attachmentCount: attachmentsForThisMessage?.length ?? 0
|
|
724
|
+
});
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
logger.debug("[start] Steer arrived without a compatible live turn; falling back to queue");
|
|
728
|
+
}
|
|
729
|
+
messageQueue.push(message.content.text, enhancedMode, attachmentsForThisMessage, message.localKey);
|
|
730
|
+
logger.debug("User message pushed to queue:", {
|
|
731
|
+
...contentLogMetadata(message.content.text),
|
|
732
|
+
attachmentCount: attachmentsForThisMessage?.length ?? 0,
|
|
733
|
+
hasMetadata: Boolean(message.meta)
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
const exitGate = new SessionExitGate();
|
|
737
|
+
const cleanup = async (opts = { archive: true }) => {
|
|
738
|
+
const intent = exitGate.claim(exitIntentFromArchiveFlag(opts.archive));
|
|
739
|
+
if (intent === null) {
|
|
740
|
+
logger.debug(`[START] Termination (archive=${opts.archive ?? true}) ignored: cleanup already running as ${exitGate.current}`);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
logger.debug(`[START] Received termination signal, cleaning up (archive=${intent === "archive"})...`);
|
|
744
|
+
promptQueueDrain.close();
|
|
745
|
+
try {
|
|
746
|
+
if (session) {
|
|
747
|
+
currentSession?.cleanup();
|
|
748
|
+
await settleSessionOnExit(session.sessionId, intent, {
|
|
749
|
+
updateMetadata: (handler) => session.updateMetadata(handler),
|
|
750
|
+
sendSessionDeath: () => session.sendSessionDeath(),
|
|
751
|
+
archiveSession: (id) => api.archiveSession(id),
|
|
752
|
+
deactivateSession: (id) => api.deactivateSession(id),
|
|
753
|
+
log: (message, ...args) => logger.debug(message, ...args)
|
|
754
|
+
});
|
|
755
|
+
await session.flush();
|
|
756
|
+
await session.close();
|
|
757
|
+
}
|
|
758
|
+
happyServer.stop();
|
|
759
|
+
hookServer.stop();
|
|
760
|
+
cleanupHookSettingsFile(hookSettingsPath);
|
|
761
|
+
await remoteScanner.cleanup();
|
|
762
|
+
logger.debug("[START] Cleanup complete, exiting");
|
|
763
|
+
process.exit(0);
|
|
764
|
+
} catch (error) {
|
|
765
|
+
logger.debug("[START] Error during cleanup:", error);
|
|
766
|
+
process.exit(1);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
process.on("SIGTERM", () => {
|
|
770
|
+
void cleanup({ archive: false });
|
|
771
|
+
});
|
|
772
|
+
process.on("SIGINT", () => {
|
|
773
|
+
void cleanup({ archive: false });
|
|
774
|
+
});
|
|
775
|
+
process.on("uncaughtException", (error) => {
|
|
776
|
+
logger.debug("[START] Uncaught exception:", error);
|
|
777
|
+
if (currentSession?.isAborting()) {
|
|
778
|
+
logger.debug("[START] Uncaught exception during abort teardown \u2014 swallowing, not archiving");
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
void cleanup({ archive: true });
|
|
782
|
+
});
|
|
783
|
+
process.on("unhandledRejection", (reason) => {
|
|
784
|
+
logger.debug("[START] Unhandled rejection:", reason);
|
|
785
|
+
if (currentSession?.isAborting()) {
|
|
786
|
+
logger.debug("[START] Unhandled rejection during abort teardown \u2014 swallowing, not archiving");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
void cleanup({ archive: true });
|
|
790
|
+
});
|
|
791
|
+
registerKillSessionHandler(session.rpcHandlerManager, () => cleanup({ archive: true }), session);
|
|
792
|
+
const exitCode = await loop({
|
|
793
|
+
path: workingDirectory,
|
|
794
|
+
model: options.model,
|
|
795
|
+
permissionMode: initialPermissionMode,
|
|
796
|
+
startingMode: options.startingMode,
|
|
797
|
+
messageQueue,
|
|
798
|
+
api,
|
|
799
|
+
allowedTools: happyServer.toolNames.map((toolName) => `mcp__happy__${toolName}`),
|
|
800
|
+
onModeChange: (newMode) => {
|
|
801
|
+
currentRunMode = newMode;
|
|
802
|
+
session.sendSessionEvent({ type: "switch", mode: newMode });
|
|
803
|
+
session.updateAgentState((currentState) => ({
|
|
804
|
+
...currentState,
|
|
805
|
+
controlledByUser: newMode === "local"
|
|
806
|
+
}));
|
|
807
|
+
},
|
|
808
|
+
onSessionReady: (sessionInstance) => {
|
|
809
|
+
currentSession = sessionInstance;
|
|
810
|
+
},
|
|
811
|
+
// B-355: remember exactly what the SDK is about to write to the Claude
|
|
812
|
+
// transcript, so the JSONL scanner below recognises it as ours.
|
|
813
|
+
onPromptFinalized: recordAppPrompt,
|
|
814
|
+
onAbort: resetCurrentModeDefaults,
|
|
815
|
+
onPermissionModeChange: (mode) => {
|
|
816
|
+
lastExplicitModeSwitchAt = Date.now();
|
|
817
|
+
publishPermissionMode(mode);
|
|
818
|
+
logger.debug(`[loop] Permission mode updated from session RPC / approval to: ${mode}`);
|
|
819
|
+
},
|
|
820
|
+
onEffectivePermissionMode: (mode) => publishEffectivePermissionMode(mode),
|
|
821
|
+
mcpServers: {
|
|
822
|
+
"happy": {
|
|
823
|
+
type: "http",
|
|
824
|
+
url: happyServer.url
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
session,
|
|
828
|
+
claudeEnvVars: options.claudeEnvVars,
|
|
829
|
+
claudeArgs: options.claudeArgs,
|
|
830
|
+
sandboxConfig,
|
|
831
|
+
hookSettingsPath,
|
|
832
|
+
jsRuntime: options.jsRuntime
|
|
833
|
+
});
|
|
834
|
+
currentSession?.cleanup();
|
|
835
|
+
session.sendSessionDeath();
|
|
836
|
+
logger.debug("Waiting for socket to flush...");
|
|
837
|
+
await session.flush();
|
|
838
|
+
logger.debug("Closing session...");
|
|
839
|
+
await session.close();
|
|
840
|
+
happyServer.stop();
|
|
841
|
+
logger.debug("Stopped Happy MCP server");
|
|
842
|
+
hookServer.stop();
|
|
843
|
+
cleanupHookSettingsFile(hookSettingsPath);
|
|
844
|
+
logger.debug("Stopped Hook server and cleaned up settings file");
|
|
845
|
+
process.exit(exitCode);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
export { runClaude };
|