very-happy-cli 0.2.159 → 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-DUYFlyfN.cjs → AcpBackend-Bm2rdke8.cjs} +78 -76
- package/dist/{AcpBackend-VsfkZiVF.mjs → AcpBackend-mzv8Dhba.mjs} +4 -2
- package/dist/{AcpSessionManager-Bdv8LWXh.cjs → AcpSessionManager-BAY1Pggx.cjs} +9 -9
- package/dist/{AcpSessionManager-rYtVb0kN.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-CHh4lmjM.cjs → agentHome-CboCeKWq.cjs} +14 -28
- package/dist/{agentHome-CvsJyeQB.mjs → agentHome-Dj1C43aJ.mjs} +8 -22
- package/dist/api-CLMUr-SX.mjs +3339 -0
- package/dist/api-CgundNNX.cjs +3346 -0
- package/dist/{types-Dh-s4Lqs.mjs → apiMachine-B9dLW_p5.mjs} +9116 -16205
- package/dist/{types-BbvK1Tr_.cjs → apiMachine-Bn8482VM.cjs} +9104 -16247
- 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-DKmEls8Z.cjs → auto-BOX_fgmR.cjs} +70 -79
- package/dist/{auto-nb8GhRRy.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-ClLtM6F3.mjs → config-CTx_jb-C.mjs} +7 -40
- package/dist/{config-DLx-vW3I.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-BEOMECtE.cjs → index-B6pAdvJx.cjs} +121 -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-2mCaeKyf.mjs → index-DMZqWwsP.mjs} +68 -38
- 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-BPhJppvd.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-BOHrUaFM.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-Cou3WUf3.cjs → installTerminalHooks-BQZbvxsZ.cjs} +5 -40
- package/dist/{installTerminalHooks-WeznjkuC.mjs → installTerminalHooks-By7gBMEF.mjs} +3 -38
- package/dist/lib.cjs +28 -19
- package/dist/lib.d.cts +12 -2
- package/dist/lib.d.mts +12 -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-Dy3f_K0h.mjs → mcp-B1A4I3TK.mjs} +27 -33
- package/dist/{mcp-CPYbtsYG.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-kLZNpAAl.mjs → piRuntime-CSGkwCKQ.mjs} +2 -2
- package/dist/{piRuntime-NtF0YqjH.cjs → piRuntime-C_H7MJCM.cjs} +4 -4
- package/dist/{piTerminal-B0fEOV_2.mjs → piTerminal-C8a1OrNQ.mjs} +32 -35
- package/dist/{piTerminal-9J0GAKzb.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-DV6Ouadt.mjs → runGemini-BGeTz57m.mjs} +58 -31
- package/dist/{runGemini-F-mqx3_K.cjs → runGemini-DSr9LIxL.cjs} +202 -175
- package/dist/{runOpenClaw-Qqo9j63M.cjs → runOpenClaw-B1mTxN35.cjs} +64 -65
- package/dist/{runOpenClaw-D7MlPS5n.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-4qJTCAYW.cjs → send-B7J_eaG3.cjs} +16 -9
- package/dist/{send-CLhYXNgy.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-CzIH2rGp.mjs → sessions-BEG_HTSi.mjs} +17 -8
- package/dist/{sessions-Bh1pyddi.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-CPIWlPOM.mjs → spawn-B7yro0b2.mjs} +26 -38
- package/dist/{spawn-CmFOSVdW.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-CEahj4cm.mjs → teams-C32F1hqd.mjs} +9 -40
- package/dist/{teams-CB1R9-Z9.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-DqyRI4wP.cjs → todo-BvUOTa8G.cjs} +15 -12
- package/dist/{todo-Cknp5D4U.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-BRDN1NoZ.cjs +0 -2217
- package/dist/agentGuidance-KgvYuF3R.mjs +0 -2145
- package/dist/index-BF6WfgV5.mjs +0 -19624
- package/dist/index-C-JazrMB.mjs +0 -52
- package/dist/index-blDSadUR.cjs +0 -58
- package/dist/index-rH3DrQ5B.cjs +0 -19688
- package/dist/limits-BH5CV5vR.cjs +0 -2683
- package/dist/limits-CjRj8IX5.mjs +0 -2558
|
@@ -0,0 +1,2088 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var auth = require('./auth-ba4WMYrN.cjs');
|
|
4
|
+
var index = require('./index-DA7yINuk.cjs');
|
|
5
|
+
var node_crypto = require('node:crypto');
|
|
6
|
+
var cuid2 = require('@paralleldrive/cuid2');
|
|
7
|
+
var agentAttachments = require('./agentAttachments-Dbw8iMCZ.cjs');
|
|
8
|
+
var attachmentContent = require('./attachmentContent-DpiTJZb4.cjs');
|
|
9
|
+
var ink = require('ink');
|
|
10
|
+
var sessionLock = require('./sessionLock-Bys1PNSo.cjs');
|
|
11
|
+
var React = require('react');
|
|
12
|
+
var api = require('./api-CgundNNX.cjs');
|
|
13
|
+
var codexAppServerClient = require('./codexAppServerClient-q2FjnRcK.cjs');
|
|
14
|
+
var logger = require('./logger-CS0mw3fa.cjs');
|
|
15
|
+
var BasePermissionHandler = require('./BasePermissionHandler-CFHAxumV.cjs');
|
|
16
|
+
var BaseReasoningProcessor = require('./BaseReasoningProcessor-BFa9_rZA.cjs');
|
|
17
|
+
var node_child_process = require('node:child_process');
|
|
18
|
+
var persistence = require('./persistence-yKTH3-5X.cjs');
|
|
19
|
+
var machineMetadata = require('./machineMetadata-DyDbODYd.cjs');
|
|
20
|
+
var configuration = require('./configuration-BSYpcPwE.cjs');
|
|
21
|
+
var registerKillSessionHandler = require('./registerKillSessionHandler-DZeLXWcB.cjs');
|
|
22
|
+
var projectPath = require('./projectPath-ChLhdOyt.cjs');
|
|
23
|
+
var path = require('node:path');
|
|
24
|
+
var createSessionMetadata = require('./createSessionMetadata-BJxEnM-o.cjs');
|
|
25
|
+
var deterministicJson = require('./deterministicJson-C5CYTVpm.cjs');
|
|
26
|
+
var messageBuffer = require('./messageBuffer-fJMH6uY8.cjs');
|
|
27
|
+
var controlClient = require('./controlClient-Bcwv3D21.cjs');
|
|
28
|
+
var authRelay = require('./authRelay-BdYkC1Po.cjs');
|
|
29
|
+
var setupOfflineReconnection = require('./setupOfflineReconnection-ByElY60m.cjs');
|
|
30
|
+
var trimIdent = require('./trimIdent-4gvk9Wk6.cjs');
|
|
31
|
+
var specialCommands = require('./specialCommands-DSsTeRej.cjs');
|
|
32
|
+
var constants = require('./constants-VJ0N6WuG.cjs');
|
|
33
|
+
var defaultPermissionMode = require('./defaultPermissionMode-CxmTXUQR.cjs');
|
|
34
|
+
var editPaths = require('./editPaths-Bqp4OFPw.cjs');
|
|
35
|
+
var ensureDaemonRunning = require('./ensureDaemonRunning-BCwNrMyv.cjs');
|
|
36
|
+
require('tweetnacl');
|
|
37
|
+
require('axios');
|
|
38
|
+
require('./time-DCuTyook.cjs');
|
|
39
|
+
require('./browser-Y6Bl8L_O.cjs');
|
|
40
|
+
require('open');
|
|
41
|
+
require('zod');
|
|
42
|
+
require('node:fs/promises');
|
|
43
|
+
require('./attachmentLimits-CzVrqgLR.cjs');
|
|
44
|
+
require('node:fs');
|
|
45
|
+
require('node:events');
|
|
46
|
+
require('socket.io-client');
|
|
47
|
+
require('./limits-C-a7gr04.cjs');
|
|
48
|
+
require('./lidState-BeDfxxEX.cjs');
|
|
49
|
+
require('os');
|
|
50
|
+
require('child_process');
|
|
51
|
+
require('./registerCommonHandlers-DskirN-_.cjs');
|
|
52
|
+
require('util');
|
|
53
|
+
require('fs/promises');
|
|
54
|
+
require('crypto');
|
|
55
|
+
require('path');
|
|
56
|
+
require('cross-spawn');
|
|
57
|
+
require('./sync-DccIKZ5_.cjs');
|
|
58
|
+
require('./backgroundTaskActivity-CBvUdj39.cjs');
|
|
59
|
+
require('./contentLogMetadata-ZSFk_NjQ.cjs');
|
|
60
|
+
require('chalk');
|
|
61
|
+
require('node:readline');
|
|
62
|
+
require('./manager-Cly8xVIR.cjs');
|
|
63
|
+
require('@anthropic-ai/sandbox-runtime');
|
|
64
|
+
require('node:os');
|
|
65
|
+
require('./package-BwMrPwqH.cjs');
|
|
66
|
+
require('node:util');
|
|
67
|
+
require('./detectCLI-DR9ZaN6B.cjs');
|
|
68
|
+
require('fs');
|
|
69
|
+
require('./localHappyAgentAuth-D6rFhaoc.cjs');
|
|
70
|
+
require('./types-CRS-srZK.cjs');
|
|
71
|
+
require('url');
|
|
72
|
+
require('./client-3Ac-O4pw.cjs');
|
|
73
|
+
require('./context-DsDr6E2t.cjs');
|
|
74
|
+
require('./sessionOps-Dn8z85h_.cjs');
|
|
75
|
+
require('./resources-JDPugcDq.cjs');
|
|
76
|
+
require('./tools-Be3AoAwu.cjs');
|
|
77
|
+
require('./tools-DQj0AeLk.cjs');
|
|
78
|
+
require('./client-BPNbmtvO.cjs');
|
|
79
|
+
require('./peerTools-BvKtHXj8.cjs');
|
|
80
|
+
require('./ids-B6ZC0HGt.cjs');
|
|
81
|
+
require('./remoteSessionClient-B0LraSB9.cjs');
|
|
82
|
+
require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
83
|
+
require('node:http');
|
|
84
|
+
require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
85
|
+
require('./assistantTools-BTqPnlFl.cjs');
|
|
86
|
+
require('./terminals-B_bfC9c2.cjs');
|
|
87
|
+
require('./terminalTitleSuggest-DhvRFX3H.cjs');
|
|
88
|
+
require('./tmuxSocket-D-Xt6Vn-.cjs');
|
|
89
|
+
require('./bootstrap-B5G1TijC.cjs');
|
|
90
|
+
require('./agentGuidance-B5NATuFa.cjs');
|
|
91
|
+
require('./previewPath-B0rBdhy1.cjs');
|
|
92
|
+
require('./spawnHappyCLI-DBhwZT7O.cjs');
|
|
93
|
+
require('./doctorReadiness-BCZGcN0a.cjs');
|
|
94
|
+
|
|
95
|
+
function getStartedSubagents(state) {
|
|
96
|
+
return state.startedSubagents ?? /* @__PURE__ */ new Set();
|
|
97
|
+
}
|
|
98
|
+
function getActiveSubagents(state) {
|
|
99
|
+
return state.activeSubagents ?? /* @__PURE__ */ new Set();
|
|
100
|
+
}
|
|
101
|
+
function getProviderSubagentToSessionSubagent(state) {
|
|
102
|
+
return state.providerSubagentToSessionSubagent ?? /* @__PURE__ */ new Map();
|
|
103
|
+
}
|
|
104
|
+
function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes) {
|
|
105
|
+
if (!subagent || startedSubagents.has(subagent)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
envelopes.push(index.createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
|
|
109
|
+
startedSubagents.add(subagent);
|
|
110
|
+
activeSubagents.add(subagent);
|
|
111
|
+
}
|
|
112
|
+
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
113
|
+
const envelopes = [];
|
|
114
|
+
for (const subagent of activeSubagents) {
|
|
115
|
+
envelopes.push(index.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
116
|
+
}
|
|
117
|
+
activeSubagents.clear();
|
|
118
|
+
startedSubagents.clear();
|
|
119
|
+
return envelopes;
|
|
120
|
+
}
|
|
121
|
+
function buildEnvelopeOptions(currentTurnId, subagent) {
|
|
122
|
+
return {
|
|
123
|
+
...currentTurnId ? { turn: currentTurnId } : {},
|
|
124
|
+
...subagent ? { subagent } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function pickProviderSubagent(message) {
|
|
128
|
+
const candidates = [message.subagent, message.parent_call_id, message.parentCallId];
|
|
129
|
+
for (const candidate of candidates) {
|
|
130
|
+
if (typeof candidate === "string" && candidate.length > 0) {
|
|
131
|
+
return candidate;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
function resolveSessionSubagent(message, providerSubagentToSessionSubagent) {
|
|
137
|
+
const providerSubagent = pickProviderSubagent(message);
|
|
138
|
+
if (!providerSubagent) {
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
const existing = providerSubagentToSessionSubagent.get(providerSubagent);
|
|
142
|
+
if (existing) {
|
|
143
|
+
return existing;
|
|
144
|
+
}
|
|
145
|
+
const created = cuid2.createId();
|
|
146
|
+
providerSubagentToSessionSubagent.set(providerSubagent, created);
|
|
147
|
+
return created;
|
|
148
|
+
}
|
|
149
|
+
function pickCallId(message) {
|
|
150
|
+
const callId = message.call_id ?? message.callId;
|
|
151
|
+
if (typeof callId === "string" && callId.length > 0) {
|
|
152
|
+
return callId;
|
|
153
|
+
}
|
|
154
|
+
return node_crypto.randomUUID();
|
|
155
|
+
}
|
|
156
|
+
function summarizeCommand(command) {
|
|
157
|
+
if (typeof command === "string" && command.trim().length > 0) {
|
|
158
|
+
return command;
|
|
159
|
+
}
|
|
160
|
+
if (Array.isArray(command)) {
|
|
161
|
+
const cmd = command.map((v) => String(v)).join(" ").trim();
|
|
162
|
+
return cmd.length > 0 ? cmd : null;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
function commandToTitle(command) {
|
|
167
|
+
if (!command) {
|
|
168
|
+
return "Run command";
|
|
169
|
+
}
|
|
170
|
+
const short = command.length > 80 ? `${command.slice(0, 77)}...` : command;
|
|
171
|
+
return `Run \`${short}\``;
|
|
172
|
+
}
|
|
173
|
+
function turnTimestampMs(turn) {
|
|
174
|
+
const seconds = turn.startedAt ?? turn.completedAt;
|
|
175
|
+
return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
|
|
176
|
+
}
|
|
177
|
+
function completedTimestampMs(turn) {
|
|
178
|
+
const seconds = turn.completedAt ?? turn.startedAt;
|
|
179
|
+
return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
|
|
180
|
+
}
|
|
181
|
+
function textFromInputItems(items) {
|
|
182
|
+
if (!Array.isArray(items)) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const text = items.filter((item) => Boolean(item) && typeof item === "object" && item.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n").trim();
|
|
186
|
+
return text.length > 0 ? text : null;
|
|
187
|
+
}
|
|
188
|
+
function reasoningText(item) {
|
|
189
|
+
const summary = item.summary;
|
|
190
|
+
const content = item.content;
|
|
191
|
+
const parts = [
|
|
192
|
+
...Array.isArray(summary) ? summary : [],
|
|
193
|
+
...Array.isArray(content) ? content : []
|
|
194
|
+
].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
195
|
+
const text = parts.join("\n").trim();
|
|
196
|
+
return text.length > 0 ? text : null;
|
|
197
|
+
}
|
|
198
|
+
function turnStatus(turn) {
|
|
199
|
+
const status = typeof turn.status === "string" ? turn.status : null;
|
|
200
|
+
if (status === "failed") {
|
|
201
|
+
return "failed";
|
|
202
|
+
}
|
|
203
|
+
if (status === "cancelled" || status === "canceled" || status === "aborted" || status === "interrupted") {
|
|
204
|
+
return "cancelled";
|
|
205
|
+
}
|
|
206
|
+
return "completed";
|
|
207
|
+
}
|
|
208
|
+
function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
|
|
209
|
+
const time = turnTimestampMs(turn);
|
|
210
|
+
const opts = { turn: turn.id, time, codexItemId: item.id };
|
|
211
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
212
|
+
t: "tool-call-start",
|
|
213
|
+
call: item.id,
|
|
214
|
+
name,
|
|
215
|
+
title,
|
|
216
|
+
description: title,
|
|
217
|
+
args
|
|
218
|
+
}, {
|
|
219
|
+
...opts,
|
|
220
|
+
id: `${item.id}:start`
|
|
221
|
+
}));
|
|
222
|
+
if (output && output.trim().length > 0) {
|
|
223
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
224
|
+
t: "text",
|
|
225
|
+
text: output,
|
|
226
|
+
thinking: true
|
|
227
|
+
}, {
|
|
228
|
+
...opts,
|
|
229
|
+
id: `${item.id}:output`
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
|
|
233
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
234
|
+
t: "tool-call-end",
|
|
235
|
+
call: item.id
|
|
236
|
+
}, {
|
|
237
|
+
...opts,
|
|
238
|
+
id: `${item.id}:end`,
|
|
239
|
+
time: completedTimestampMs(turn)
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
243
|
+
if (item.type === "subAgentActivity" && typeof item.agentThreadId === "string") {
|
|
244
|
+
item = { ...item, type: "collabAgentToolCall", tool: item.kind, status: "completed", receiverThreadIds: [item.agentThreadId] };
|
|
245
|
+
}
|
|
246
|
+
if (item.type !== "collabAgentToolCall" || typeof item.id !== "string" || typeof item.tool !== "string") return [];
|
|
247
|
+
const args = {
|
|
248
|
+
operation: item.tool,
|
|
249
|
+
...typeof item.prompt === "string" ? { prompt: item.prompt } : {},
|
|
250
|
+
...typeof item.model === "string" ? { model: item.model } : {},
|
|
251
|
+
receiverThreadIds: Array.isArray(item.receiverThreadIds) ? item.receiverThreadIds : [],
|
|
252
|
+
agentsStates: item.agentsStates ?? {}
|
|
253
|
+
};
|
|
254
|
+
const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
|
|
255
|
+
const envelopes = [index.createEnvelope("agent", {
|
|
256
|
+
t: "tool-call-start",
|
|
257
|
+
call: item.id,
|
|
258
|
+
name: "CodexCollaboration",
|
|
259
|
+
title: `Codex \xB7 ${item.tool}`,
|
|
260
|
+
description: `Codex \xB7 ${item.tool}`,
|
|
261
|
+
args
|
|
262
|
+
}, { ...opts, id: startId })];
|
|
263
|
+
if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
|
|
264
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
265
|
+
t: "tool-call-end",
|
|
266
|
+
call: item.id,
|
|
267
|
+
result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
|
|
268
|
+
}, { ...opts, id: `${item.id}:end` }));
|
|
269
|
+
}
|
|
270
|
+
return envelopes;
|
|
271
|
+
}
|
|
272
|
+
function mapCodexThreadToSessionEnvelopes(thread) {
|
|
273
|
+
const envelopes = [];
|
|
274
|
+
for (const turn of thread.turns ?? []) {
|
|
275
|
+
const startedAt = turnTimestampMs(turn);
|
|
276
|
+
const completedAt = completedTimestampMs(turn);
|
|
277
|
+
envelopes.push(index.createEnvelope("agent", { t: "turn-start" }, {
|
|
278
|
+
id: `${turn.id}:start`,
|
|
279
|
+
turn: turn.id,
|
|
280
|
+
time: startedAt
|
|
281
|
+
}));
|
|
282
|
+
for (const item of turn.items ?? []) {
|
|
283
|
+
switch (item.type) {
|
|
284
|
+
case "subAgentActivity":
|
|
285
|
+
case "collabAgentToolCall": {
|
|
286
|
+
envelopes.push(...mapCodexCollaborationItem(item, { turn: turn.id, time: startedAt, codexItemId: item.id }));
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
case "userMessage": {
|
|
290
|
+
const text = textFromInputItems(item.content);
|
|
291
|
+
if (text) {
|
|
292
|
+
envelopes.push(index.createEnvelope("user", { t: "text", text }, {
|
|
293
|
+
id: item.id,
|
|
294
|
+
time: startedAt,
|
|
295
|
+
codexItemId: item.id
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
case "agentMessage": {
|
|
301
|
+
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
302
|
+
if (text.length > 0) {
|
|
303
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text }, {
|
|
304
|
+
id: item.id,
|
|
305
|
+
turn: turn.id,
|
|
306
|
+
time: completedAt,
|
|
307
|
+
codexItemId: item.id
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case "reasoning": {
|
|
313
|
+
const text = reasoningText(item);
|
|
314
|
+
if (text) {
|
|
315
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
316
|
+
id: item.id,
|
|
317
|
+
turn: turn.id,
|
|
318
|
+
time: startedAt,
|
|
319
|
+
codexItemId: item.id
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case "commandExecution": {
|
|
325
|
+
const command = typeof item.command === "string" ? item.command : "";
|
|
326
|
+
emitHistoricalToolCall(
|
|
327
|
+
envelopes,
|
|
328
|
+
turn,
|
|
329
|
+
item,
|
|
330
|
+
"CodexBash",
|
|
331
|
+
commandToTitle(command),
|
|
332
|
+
{ command, cwd: item.cwd },
|
|
333
|
+
typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : null
|
|
334
|
+
);
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
case "fileChange": {
|
|
338
|
+
const title = "Apply patch";
|
|
339
|
+
emitHistoricalToolCall(
|
|
340
|
+
envelopes,
|
|
341
|
+
turn,
|
|
342
|
+
item,
|
|
343
|
+
"CodexPatch",
|
|
344
|
+
title,
|
|
345
|
+
{ changes: item.changes, status: item.status },
|
|
346
|
+
null
|
|
347
|
+
);
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
case "mcpToolCall": {
|
|
351
|
+
const title = `${item.server}.${item.tool}`;
|
|
352
|
+
const output = item.error !== void 0 && item.error !== null ? String(item.error) : item.result !== void 0 && item.result !== null ? String(item.result) : null;
|
|
353
|
+
emitHistoricalToolCall(
|
|
354
|
+
envelopes,
|
|
355
|
+
turn,
|
|
356
|
+
item,
|
|
357
|
+
"McpTool",
|
|
358
|
+
title,
|
|
359
|
+
{
|
|
360
|
+
server: item.server,
|
|
361
|
+
tool: item.tool,
|
|
362
|
+
arguments: item.arguments
|
|
363
|
+
},
|
|
364
|
+
output
|
|
365
|
+
);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
envelopes.push(index.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
371
|
+
id: `${turn.id}:end`,
|
|
372
|
+
turn: turn.id,
|
|
373
|
+
time: completedAt
|
|
374
|
+
}));
|
|
375
|
+
}
|
|
376
|
+
return envelopes;
|
|
377
|
+
}
|
|
378
|
+
function patchDescription(changes) {
|
|
379
|
+
if (!changes || typeof changes !== "object") {
|
|
380
|
+
return "Applying patch";
|
|
381
|
+
}
|
|
382
|
+
const fileCount = Object.keys(changes).length;
|
|
383
|
+
if (fileCount === 1) {
|
|
384
|
+
return "Applying patch to 1 file";
|
|
385
|
+
}
|
|
386
|
+
return `Applying patch to ${fileCount} files`;
|
|
387
|
+
}
|
|
388
|
+
function pickTurnEndStatus(message, type) {
|
|
389
|
+
const rawStatus = message.status;
|
|
390
|
+
if (rawStatus === "completed" || rawStatus === "failed" || rawStatus === "cancelled") {
|
|
391
|
+
return rawStatus;
|
|
392
|
+
}
|
|
393
|
+
if (rawStatus === "canceled") {
|
|
394
|
+
return "cancelled";
|
|
395
|
+
}
|
|
396
|
+
if (type === "turn_aborted") {
|
|
397
|
+
const reason = message.reason;
|
|
398
|
+
const error = message.error;
|
|
399
|
+
if (typeof reason === "string" && /(fail|error)/i.test(reason) || typeof error === "string" && error.length > 0 || error !== void 0 && error !== null && typeof error === "object") {
|
|
400
|
+
return "failed";
|
|
401
|
+
}
|
|
402
|
+
return "cancelled";
|
|
403
|
+
}
|
|
404
|
+
if (message.error !== void 0 && message.error !== null) {
|
|
405
|
+
return "failed";
|
|
406
|
+
}
|
|
407
|
+
return "completed";
|
|
408
|
+
}
|
|
409
|
+
function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
410
|
+
const type = message.type;
|
|
411
|
+
const startedSubagents = getStartedSubagents(state);
|
|
412
|
+
const activeSubagents = getActiveSubagents(state);
|
|
413
|
+
const providerSubagentToSessionSubagent = getProviderSubagentToSessionSubagent(state);
|
|
414
|
+
if (type === "collab_agent_item") {
|
|
415
|
+
return {
|
|
416
|
+
currentTurnId: state.currentTurnId,
|
|
417
|
+
startedSubagents,
|
|
418
|
+
activeSubagents,
|
|
419
|
+
providerSubagentToSessionSubagent,
|
|
420
|
+
envelopes: mapCodexCollaborationItem(message.item, buildEnvelopeOptions(state.currentTurnId), "live")
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
if (type === "task_started") {
|
|
424
|
+
const turnId = cuid2.createId();
|
|
425
|
+
const turnStart = index.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
426
|
+
startedSubagents.clear();
|
|
427
|
+
activeSubagents.clear();
|
|
428
|
+
providerSubagentToSessionSubagent.clear();
|
|
429
|
+
return {
|
|
430
|
+
currentTurnId: turnId,
|
|
431
|
+
startedSubagents,
|
|
432
|
+
activeSubagents,
|
|
433
|
+
providerSubagentToSessionSubagent,
|
|
434
|
+
envelopes: [turnStart]
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (type === "task_complete" || type === "turn_aborted") {
|
|
438
|
+
if (!state.currentTurnId) {
|
|
439
|
+
return {
|
|
440
|
+
currentTurnId: null,
|
|
441
|
+
startedSubagents,
|
|
442
|
+
activeSubagents,
|
|
443
|
+
providerSubagentToSessionSubagent,
|
|
444
|
+
envelopes: []
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
const lifecycleOpts = { turn: state.currentTurnId };
|
|
448
|
+
providerSubagentToSessionSubagent.clear();
|
|
449
|
+
return {
|
|
450
|
+
currentTurnId: null,
|
|
451
|
+
startedSubagents,
|
|
452
|
+
activeSubagents,
|
|
453
|
+
providerSubagentToSessionSubagent,
|
|
454
|
+
envelopes: [
|
|
455
|
+
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
456
|
+
index.createEnvelope("agent", {
|
|
457
|
+
t: "turn-end",
|
|
458
|
+
status: pickTurnEndStatus(message, type)
|
|
459
|
+
}, lifecycleOpts)
|
|
460
|
+
]
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (type === "token_count") {
|
|
464
|
+
return {
|
|
465
|
+
currentTurnId: state.currentTurnId,
|
|
466
|
+
startedSubagents,
|
|
467
|
+
activeSubagents,
|
|
468
|
+
providerSubagentToSessionSubagent,
|
|
469
|
+
envelopes: []
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
const subagent = resolveSessionSubagent(message, providerSubagentToSessionSubagent);
|
|
473
|
+
const opts = buildEnvelopeOptions(state.currentTurnId, subagent);
|
|
474
|
+
if (type === "agent_message") {
|
|
475
|
+
if (typeof message.message !== "string") {
|
|
476
|
+
return {
|
|
477
|
+
currentTurnId: state.currentTurnId,
|
|
478
|
+
startedSubagents,
|
|
479
|
+
activeSubagents,
|
|
480
|
+
providerSubagentToSessionSubagent,
|
|
481
|
+
envelopes: []
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
const envelopes = [];
|
|
485
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
486
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text: message.message }, opts));
|
|
487
|
+
return {
|
|
488
|
+
currentTurnId: state.currentTurnId,
|
|
489
|
+
startedSubagents,
|
|
490
|
+
activeSubagents,
|
|
491
|
+
providerSubagentToSessionSubagent,
|
|
492
|
+
envelopes
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
if (type === "agent_reasoning" || type === "agent_reasoning_delta") {
|
|
496
|
+
const text = typeof message.text === "string" ? message.text : typeof message.delta === "string" ? message.delta : null;
|
|
497
|
+
if (!text) {
|
|
498
|
+
return {
|
|
499
|
+
currentTurnId: state.currentTurnId,
|
|
500
|
+
startedSubagents,
|
|
501
|
+
activeSubagents,
|
|
502
|
+
providerSubagentToSessionSubagent,
|
|
503
|
+
envelopes: []
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const envelopes = [];
|
|
507
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
508
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
509
|
+
return {
|
|
510
|
+
currentTurnId: state.currentTurnId,
|
|
511
|
+
startedSubagents,
|
|
512
|
+
activeSubagents,
|
|
513
|
+
providerSubagentToSessionSubagent,
|
|
514
|
+
envelopes
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
if (type === "exec_command_begin") {
|
|
518
|
+
const call = pickCallId(message);
|
|
519
|
+
const { call_id: _callIdSnake, callId: _callIdCamel, type: _type, ...args } = message;
|
|
520
|
+
const command = summarizeCommand(args.command);
|
|
521
|
+
const description = typeof args.description === "string" ? args.description : command ?? "Execute command";
|
|
522
|
+
const envelopes = [];
|
|
523
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
524
|
+
envelopes.push(
|
|
525
|
+
index.createEnvelope("agent", {
|
|
526
|
+
t: "tool-call-start",
|
|
527
|
+
call,
|
|
528
|
+
name: "CodexBash",
|
|
529
|
+
title: commandToTitle(command),
|
|
530
|
+
description,
|
|
531
|
+
args
|
|
532
|
+
}, opts)
|
|
533
|
+
);
|
|
534
|
+
return {
|
|
535
|
+
currentTurnId: state.currentTurnId,
|
|
536
|
+
startedSubagents,
|
|
537
|
+
activeSubagents,
|
|
538
|
+
providerSubagentToSessionSubagent,
|
|
539
|
+
envelopes
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (type === "exec_command_end") {
|
|
543
|
+
const call = pickCallId(message);
|
|
544
|
+
const envelopes = [];
|
|
545
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
546
|
+
envelopes.push(index.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
547
|
+
return {
|
|
548
|
+
currentTurnId: state.currentTurnId,
|
|
549
|
+
startedSubagents,
|
|
550
|
+
activeSubagents,
|
|
551
|
+
providerSubagentToSessionSubagent,
|
|
552
|
+
envelopes
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
if (type === "patch_apply_begin") {
|
|
556
|
+
const call = pickCallId(message);
|
|
557
|
+
const autoApproved = message.auto_approved;
|
|
558
|
+
const changes = message.changes;
|
|
559
|
+
const envelopes = [];
|
|
560
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
561
|
+
envelopes.push(
|
|
562
|
+
index.createEnvelope("agent", {
|
|
563
|
+
t: "tool-call-start",
|
|
564
|
+
call,
|
|
565
|
+
name: "CodexPatch",
|
|
566
|
+
title: "Apply patch",
|
|
567
|
+
description: patchDescription(changes),
|
|
568
|
+
args: {
|
|
569
|
+
auto_approved: autoApproved,
|
|
570
|
+
changes
|
|
571
|
+
}
|
|
572
|
+
}, opts)
|
|
573
|
+
);
|
|
574
|
+
return {
|
|
575
|
+
currentTurnId: state.currentTurnId,
|
|
576
|
+
startedSubagents,
|
|
577
|
+
activeSubagents,
|
|
578
|
+
providerSubagentToSessionSubagent,
|
|
579
|
+
envelopes
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
if (type === "patch_apply_end") {
|
|
583
|
+
const call = pickCallId(message);
|
|
584
|
+
const envelopes = [];
|
|
585
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
586
|
+
envelopes.push(index.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
587
|
+
return {
|
|
588
|
+
currentTurnId: state.currentTurnId,
|
|
589
|
+
startedSubagents,
|
|
590
|
+
activeSubagents,
|
|
591
|
+
providerSubagentToSessionSubagent,
|
|
592
|
+
envelopes
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
currentTurnId: state.currentTurnId,
|
|
597
|
+
startedSubagents,
|
|
598
|
+
activeSubagents,
|
|
599
|
+
providerSubagentToSessionSubagent,
|
|
600
|
+
envelopes: []
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
604
|
+
const toolLikeMessage = message;
|
|
605
|
+
const opts = buildEnvelopeOptions(state.currentTurnId);
|
|
606
|
+
if (message.type === "reasoning") {
|
|
607
|
+
return [index.createEnvelope("agent", {
|
|
608
|
+
t: "text",
|
|
609
|
+
text: message.message,
|
|
610
|
+
thinking: true
|
|
611
|
+
}, opts)];
|
|
612
|
+
}
|
|
613
|
+
if (message.type === "tool-call") {
|
|
614
|
+
const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
|
|
615
|
+
return [index.createEnvelope("agent", {
|
|
616
|
+
t: "tool-call-start",
|
|
617
|
+
call: toolLikeMessage.callId,
|
|
618
|
+
name: toolLikeMessage.name || "unknown",
|
|
619
|
+
title,
|
|
620
|
+
description: title,
|
|
621
|
+
args: toolLikeMessage.input && typeof toolLikeMessage.input === "object" ? toolLikeMessage.input : {}
|
|
622
|
+
}, opts)];
|
|
623
|
+
}
|
|
624
|
+
if (message.type === "tool-call-result") {
|
|
625
|
+
const envelopes = [];
|
|
626
|
+
const content = toolLikeMessage.output?.content;
|
|
627
|
+
if (typeof content === "string" && content.trim().length > 0) {
|
|
628
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
629
|
+
t: "text",
|
|
630
|
+
text: content,
|
|
631
|
+
thinking: true
|
|
632
|
+
}, opts));
|
|
633
|
+
}
|
|
634
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
635
|
+
t: "tool-call-end",
|
|
636
|
+
call: toolLikeMessage.callId
|
|
637
|
+
}, opts));
|
|
638
|
+
return envelopes;
|
|
639
|
+
}
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async function readChildThread(client, childId) {
|
|
644
|
+
if (!client.threadId || typeof childId !== "string" || !childId) throw new Error("Invalid child thread");
|
|
645
|
+
const parentId = client.threadId;
|
|
646
|
+
const { thread: parent } = await client.readThread({ threadId: parentId, includeTurns: true });
|
|
647
|
+
const belongs = parent.id === parentId && parent.turns?.some((turn) => turn.items.some((item) => item.type === "collabAgentToolCall" && Array.isArray(item.receiverThreadIds) && item.receiverThreadIds.includes(childId) || item.type === "subAgentActivity" && item.agentThreadId === childId));
|
|
648
|
+
if (!belongs) throw new Error("Thread is not a child of this session");
|
|
649
|
+
const { thread } = await client.readThread({ threadId: childId, includeTurns: true });
|
|
650
|
+
if (thread.id !== childId) throw new Error("Child thread mismatch");
|
|
651
|
+
return { envelopes: mapCodexThreadToSessionEnvelopes(thread) };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
class CodexPermissionHandler extends BasePermissionHandler.BasePermissionHandler {
|
|
655
|
+
// Exact tool names that should always be auto-approved. Include the bare
|
|
656
|
+
// form (used by Codex elicitation messages like `tool "change_title"`)
|
|
657
|
+
// and the MCP-qualified form for defense in depth.
|
|
658
|
+
static ALWAYS_AUTO_APPROVE_NAMES = /* @__PURE__ */ new Set([
|
|
659
|
+
"change_title",
|
|
660
|
+
"mcp__happy__change_title"
|
|
661
|
+
]);
|
|
662
|
+
// Tool-call IDs that should auto-approve when they exactly match one of
|
|
663
|
+
// these values or start with `<name>-` (e.g. `change_title-1765385846663`).
|
|
664
|
+
// Substring matching was a bypass vector — any tool whose ID happened to
|
|
665
|
+
// contain `change_title` as a substring would be silently approved.
|
|
666
|
+
static ALWAYS_AUTO_APPROVE_ID_PREFIXES = [
|
|
667
|
+
"change_title"
|
|
668
|
+
];
|
|
669
|
+
constructor(session) {
|
|
670
|
+
super(session);
|
|
671
|
+
}
|
|
672
|
+
getLogPrefix() {
|
|
673
|
+
return "[Codex]";
|
|
674
|
+
}
|
|
675
|
+
shouldAutoApprove(toolName, toolCallId) {
|
|
676
|
+
if (CodexPermissionHandler.ALWAYS_AUTO_APPROVE_NAMES.has(toolName)) {
|
|
677
|
+
return true;
|
|
678
|
+
}
|
|
679
|
+
for (const prefix of CodexPermissionHandler.ALWAYS_AUTO_APPROVE_ID_PREFIXES) {
|
|
680
|
+
if (toolCallId === prefix || toolCallId.startsWith(`${prefix}-`)) {
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Handle a tool permission request
|
|
688
|
+
* @param toolCallId - The unique ID of the tool call
|
|
689
|
+
* @param toolName - The name of the tool being called
|
|
690
|
+
* @param input - The input parameters for the tool
|
|
691
|
+
* @returns Promise resolving to permission result
|
|
692
|
+
*/
|
|
693
|
+
async handleToolCall(toolCallId, toolName, input) {
|
|
694
|
+
if (this.shouldAutoApprove(toolName, toolCallId)) {
|
|
695
|
+
logger.logger.debug(`${this.getLogPrefix()} Auto-approving tool ${toolName} (${toolCallId})`);
|
|
696
|
+
this.session.updateAgentState((currentState) => ({
|
|
697
|
+
...currentState,
|
|
698
|
+
completedRequests: {
|
|
699
|
+
...currentState.completedRequests,
|
|
700
|
+
[toolCallId]: {
|
|
701
|
+
tool: toolName,
|
|
702
|
+
arguments: input,
|
|
703
|
+
createdAt: Date.now(),
|
|
704
|
+
completedAt: Date.now(),
|
|
705
|
+
status: "approved",
|
|
706
|
+
decision: "approved"
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}));
|
|
710
|
+
return { decision: "approved" };
|
|
711
|
+
}
|
|
712
|
+
return new Promise((resolve, reject) => {
|
|
713
|
+
this.pendingRequests.set(toolCallId, {
|
|
714
|
+
resolve,
|
|
715
|
+
reject,
|
|
716
|
+
toolName,
|
|
717
|
+
input
|
|
718
|
+
});
|
|
719
|
+
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
720
|
+
logger.logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})`);
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
class ReasoningProcessor extends BaseReasoningProcessor.BaseReasoningProcessor {
|
|
726
|
+
getToolName() {
|
|
727
|
+
return "CodexReasoning";
|
|
728
|
+
}
|
|
729
|
+
getLogPrefix() {
|
|
730
|
+
return "[ReasoningProcessor]";
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Process a reasoning delta and accumulate content.
|
|
734
|
+
*/
|
|
735
|
+
processDelta(delta) {
|
|
736
|
+
this.processInput(delta);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Complete the reasoning section with final text.
|
|
740
|
+
*/
|
|
741
|
+
complete(fullText) {
|
|
742
|
+
this.completeReasoning(fullText);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
class DiffProcessor {
|
|
747
|
+
previousDiff = null;
|
|
748
|
+
onMessage = null;
|
|
749
|
+
constructor(onMessage) {
|
|
750
|
+
this.onMessage = onMessage || null;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Process a turn_diff message and check if the unified_diff has changed
|
|
754
|
+
*/
|
|
755
|
+
processDiff(unifiedDiff) {
|
|
756
|
+
if (this.previousDiff !== unifiedDiff) {
|
|
757
|
+
logger.logger.debug("[DiffProcessor] Unified diff changed, sending CodexDiff tool call");
|
|
758
|
+
const callId = node_crypto.randomUUID();
|
|
759
|
+
const toolCall = {
|
|
760
|
+
type: "tool-call",
|
|
761
|
+
name: "CodexDiff",
|
|
762
|
+
callId,
|
|
763
|
+
input: {
|
|
764
|
+
unified_diff: unifiedDiff
|
|
765
|
+
},
|
|
766
|
+
id: node_crypto.randomUUID()
|
|
767
|
+
};
|
|
768
|
+
this.onMessage?.(toolCall);
|
|
769
|
+
const toolResult = {
|
|
770
|
+
type: "tool-call-result",
|
|
771
|
+
callId,
|
|
772
|
+
output: {
|
|
773
|
+
status: "completed"
|
|
774
|
+
},
|
|
775
|
+
id: node_crypto.randomUUID()
|
|
776
|
+
};
|
|
777
|
+
this.onMessage?.(toolResult);
|
|
778
|
+
}
|
|
779
|
+
this.previousDiff = unifiedDiff;
|
|
780
|
+
logger.logger.debug("[DiffProcessor] Updated stored diff");
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Reset the processor state (called on task_complete or turn_aborted)
|
|
784
|
+
*/
|
|
785
|
+
reset() {
|
|
786
|
+
logger.logger.debug("[DiffProcessor] Resetting diff state");
|
|
787
|
+
this.previousDiff = null;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Set the message callback for sending messages directly
|
|
791
|
+
*/
|
|
792
|
+
setMessageCallback(callback) {
|
|
793
|
+
this.onMessage = callback;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Get the current diff value
|
|
797
|
+
*/
|
|
798
|
+
getCurrentDiff() {
|
|
799
|
+
return this.previousDiff;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
|
|
804
|
+
const [messages, setMessages] = React.useState([]);
|
|
805
|
+
const [confirmationMode, setConfirmationMode] = React.useState(false);
|
|
806
|
+
const [actionInProgress, setActionInProgress] = React.useState(false);
|
|
807
|
+
const confirmationTimeoutRef = React.useRef(null);
|
|
808
|
+
const { stdout } = ink.useStdout();
|
|
809
|
+
const terminalWidth = stdout.columns || 80;
|
|
810
|
+
const terminalHeight = stdout.rows || 24;
|
|
811
|
+
React.useEffect(() => {
|
|
812
|
+
setMessages(messageBuffer.getMessages());
|
|
813
|
+
const unsubscribe = messageBuffer.onUpdate((newMessages) => {
|
|
814
|
+
setMessages(newMessages);
|
|
815
|
+
});
|
|
816
|
+
return () => {
|
|
817
|
+
unsubscribe();
|
|
818
|
+
if (confirmationTimeoutRef.current) {
|
|
819
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
}, [messageBuffer]);
|
|
823
|
+
const resetConfirmation = React.useCallback(() => {
|
|
824
|
+
setConfirmationMode(false);
|
|
825
|
+
if (confirmationTimeoutRef.current) {
|
|
826
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
827
|
+
confirmationTimeoutRef.current = null;
|
|
828
|
+
}
|
|
829
|
+
}, []);
|
|
830
|
+
const setConfirmationWithTimeout = React.useCallback(() => {
|
|
831
|
+
setConfirmationMode(true);
|
|
832
|
+
if (confirmationTimeoutRef.current) {
|
|
833
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
834
|
+
}
|
|
835
|
+
confirmationTimeoutRef.current = setTimeout(() => {
|
|
836
|
+
resetConfirmation();
|
|
837
|
+
}, 15e3);
|
|
838
|
+
}, [resetConfirmation]);
|
|
839
|
+
ink.useInput(React.useCallback(async (input, key) => {
|
|
840
|
+
if (actionInProgress) return;
|
|
841
|
+
if (key.ctrl && input === "c") {
|
|
842
|
+
if (confirmationMode) {
|
|
843
|
+
resetConfirmation();
|
|
844
|
+
setActionInProgress(true);
|
|
845
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
846
|
+
onExit?.();
|
|
847
|
+
} else {
|
|
848
|
+
setConfirmationWithTimeout();
|
|
849
|
+
}
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (confirmationMode) {
|
|
853
|
+
resetConfirmation();
|
|
854
|
+
}
|
|
855
|
+
}, [confirmationMode, actionInProgress, onExit, setConfirmationWithTimeout, resetConfirmation]));
|
|
856
|
+
const getMessageColor = (type) => {
|
|
857
|
+
switch (type) {
|
|
858
|
+
case "user":
|
|
859
|
+
return "magenta";
|
|
860
|
+
case "assistant":
|
|
861
|
+
return "cyan";
|
|
862
|
+
case "system":
|
|
863
|
+
return "blue";
|
|
864
|
+
case "tool":
|
|
865
|
+
return "yellow";
|
|
866
|
+
case "result":
|
|
867
|
+
return "green";
|
|
868
|
+
case "status":
|
|
869
|
+
return "gray";
|
|
870
|
+
default:
|
|
871
|
+
return "white";
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
const formatMessage = (msg) => {
|
|
875
|
+
const lines = msg.content.split("\n");
|
|
876
|
+
const maxLineLength = terminalWidth - 10;
|
|
877
|
+
return lines.map((line) => {
|
|
878
|
+
if (line.length <= maxLineLength) return line;
|
|
879
|
+
const chunks = [];
|
|
880
|
+
for (let i = 0; i < line.length; i += maxLineLength) {
|
|
881
|
+
chunks.push(line.slice(i, i + maxLineLength));
|
|
882
|
+
}
|
|
883
|
+
return chunks.join("\n");
|
|
884
|
+
}).join("\n");
|
|
885
|
+
};
|
|
886
|
+
return /* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", width: terminalWidth, height: terminalHeight }, /* @__PURE__ */ React.createElement(
|
|
887
|
+
ink.Box,
|
|
888
|
+
{
|
|
889
|
+
flexDirection: "column",
|
|
890
|
+
width: terminalWidth,
|
|
891
|
+
height: terminalHeight - 4,
|
|
892
|
+
borderStyle: "round",
|
|
893
|
+
borderColor: "gray",
|
|
894
|
+
paddingX: 1,
|
|
895
|
+
overflow: "hidden"
|
|
896
|
+
},
|
|
897
|
+
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "\u{1F916} Codex Agent Messages"), /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "\u2500".repeat(Math.min(terminalWidth - 4, 60)))),
|
|
898
|
+
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", height: terminalHeight - 10, overflow: "hidden" }, messages.length === 0 ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "Waiting for messages...") : (
|
|
899
|
+
// Show only the last messages that fit in the available space
|
|
900
|
+
messages.slice(-Math.max(1, terminalHeight - 10)).map((msg) => /* @__PURE__ */ React.createElement(ink.Box, { key: msg.id, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { color: getMessageColor(msg.type), dimColor: true }, formatMessage(msg))))
|
|
901
|
+
))
|
|
902
|
+
), /* @__PURE__ */ React.createElement(
|
|
903
|
+
ink.Box,
|
|
904
|
+
{
|
|
905
|
+
width: terminalWidth,
|
|
906
|
+
borderStyle: "round",
|
|
907
|
+
borderColor: actionInProgress ? "gray" : confirmationMode ? "red" : "green",
|
|
908
|
+
paddingX: 2,
|
|
909
|
+
justifyContent: "center",
|
|
910
|
+
alignItems: "center",
|
|
911
|
+
flexDirection: "column"
|
|
912
|
+
},
|
|
913
|
+
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", alignItems: "center" }, actionInProgress ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "Exiting agent...") : confirmationMode ? /* @__PURE__ */ React.createElement(ink.Text, { color: "red", bold: true }, "\u26A0\uFE0F Press Ctrl-C again to exit the agent") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ink.Text, { color: "green", bold: true }, "\u{1F916} Codex Agent Running \u2022 Ctrl-C to exit")), process.env.DEBUG && logPath && /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "Debug logs: ", logPath))
|
|
914
|
+
));
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
function resolveCodexExecutionPolicy(permissionMode, sandboxManagedByHappy) {
|
|
918
|
+
if (sandboxManagedByHappy) {
|
|
919
|
+
return {
|
|
920
|
+
approvalPolicy: "never",
|
|
921
|
+
sandbox: "danger-full-access"
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
const approvalPolicy = (() => {
|
|
925
|
+
switch (permissionMode) {
|
|
926
|
+
// Codex native modes
|
|
927
|
+
case "default":
|
|
928
|
+
return "untrusted";
|
|
929
|
+
// Ask for non-trusted commands
|
|
930
|
+
case "read-only":
|
|
931
|
+
return "never";
|
|
932
|
+
// Never ask, read-only enforced by sandbox
|
|
933
|
+
case "safe-yolo":
|
|
934
|
+
return "on-request";
|
|
935
|
+
// Workspace auto-run; request approval for escalation (0.153 removed on-failure)
|
|
936
|
+
case "yolo":
|
|
937
|
+
return "never";
|
|
938
|
+
// Full YOLO: never interrupt for approvals
|
|
939
|
+
// Defensive fallback for Claude-specific modes (backward compatibility)
|
|
940
|
+
case "bypassPermissions":
|
|
941
|
+
return "never";
|
|
942
|
+
// Full access: map to yolo behavior
|
|
943
|
+
case "acceptEdits":
|
|
944
|
+
return "on-request";
|
|
945
|
+
// Let model decide (closest to auto-approve edits)
|
|
946
|
+
case "plan":
|
|
947
|
+
return "untrusted";
|
|
948
|
+
// Conservative: ask for non-trusted
|
|
949
|
+
default:
|
|
950
|
+
return "untrusted";
|
|
951
|
+
}
|
|
952
|
+
})();
|
|
953
|
+
const sandbox = (() => {
|
|
954
|
+
switch (permissionMode) {
|
|
955
|
+
// Codex native modes
|
|
956
|
+
case "default":
|
|
957
|
+
return "workspace-write";
|
|
958
|
+
// Can write in workspace
|
|
959
|
+
case "read-only":
|
|
960
|
+
return "read-only";
|
|
961
|
+
// Read-only filesystem
|
|
962
|
+
case "safe-yolo":
|
|
963
|
+
return "workspace-write";
|
|
964
|
+
// Can write in workspace
|
|
965
|
+
case "yolo":
|
|
966
|
+
return "danger-full-access";
|
|
967
|
+
// Full system access
|
|
968
|
+
// Defensive fallback for Claude-specific modes
|
|
969
|
+
case "bypassPermissions":
|
|
970
|
+
return "danger-full-access";
|
|
971
|
+
// Full access: map to yolo
|
|
972
|
+
case "acceptEdits":
|
|
973
|
+
return "workspace-write";
|
|
974
|
+
// Can edit files in workspace
|
|
975
|
+
case "plan":
|
|
976
|
+
return "workspace-write";
|
|
977
|
+
// Can write for planning
|
|
978
|
+
default:
|
|
979
|
+
return "workspace-write";
|
|
980
|
+
}
|
|
981
|
+
})();
|
|
982
|
+
return { approvalPolicy, sandbox };
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function unsupportedEffortReason(catalog, model, effort) {
|
|
986
|
+
if (!effort) return null;
|
|
987
|
+
const selected = catalog.find((entry) => model ? entry.model === model : entry.isDefault);
|
|
988
|
+
if (!selected) return null;
|
|
989
|
+
if (selected.supportedReasoningEfforts.some((option) => option.reasoningEffort === effort)) return null;
|
|
990
|
+
return `Reasoning effort "${effort}" is not supported by ${model ?? "the default model"}. Choose a supported level.`;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function readableCodexErrorText(text) {
|
|
994
|
+
const trimmed = text.trim();
|
|
995
|
+
if (!trimmed.startsWith("{")) return trimmed;
|
|
996
|
+
try {
|
|
997
|
+
const body = JSON.parse(trimmed);
|
|
998
|
+
const inner = typeof body.error === "object" && body.error ? body.error.message : void 0;
|
|
999
|
+
const message = typeof inner === "string" && inner.trim() ? inner : typeof body.error === "string" && body.error.trim() ? body.error : typeof body.message === "string" && body.message.trim() ? body.message : null;
|
|
1000
|
+
return message ? message.trim() : trimmed;
|
|
1001
|
+
} catch {
|
|
1002
|
+
return trimmed;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function describeCodexFailure(msg) {
|
|
1006
|
+
const hasFailure = msg?.status === "failed" || msg?.error !== void 0 && msg?.error !== null;
|
|
1007
|
+
if (!hasFailure) return null;
|
|
1008
|
+
const err = msg.error;
|
|
1009
|
+
if (typeof err === "string" && err.length > 0) return readableCodexErrorText(err);
|
|
1010
|
+
if (err && typeof err === "object" && typeof err.message === "string" && err.message.length > 0) {
|
|
1011
|
+
return readableCodexErrorText(err.message);
|
|
1012
|
+
}
|
|
1013
|
+
return "Unknown error";
|
|
1014
|
+
}
|
|
1015
|
+
const TRANSPORT_LOSS = /^Codex process (exited|disconnected)\b|stdin not writable| timed out after \d+ms/;
|
|
1016
|
+
function describeThrownTurnError(error) {
|
|
1017
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
1018
|
+
if (!message.trim() || TRANSPORT_LOSS.test(message)) return null;
|
|
1019
|
+
return readableCodexErrorText(message);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
async function resumeExistingThread(opts) {
|
|
1023
|
+
try {
|
|
1024
|
+
const resumedThread = await opts.client.resumeThread({
|
|
1025
|
+
threadId: opts.threadId,
|
|
1026
|
+
cwd: opts.cwd,
|
|
1027
|
+
mcpServers: opts.mcpServers
|
|
1028
|
+
});
|
|
1029
|
+
opts.session.updateMetadata((currentMetadata) => ({
|
|
1030
|
+
...currentMetadata,
|
|
1031
|
+
codexThreadId: resumedThread.threadId
|
|
1032
|
+
}));
|
|
1033
|
+
opts.messageBuffer.addMessage(`Resumed thread ${trimIdent.trimIdent(resumedThread.threadId)}`, "status");
|
|
1034
|
+
opts.session.sendSessionEvent({
|
|
1035
|
+
type: "message",
|
|
1036
|
+
message: `Resumed Codex thread ${resumedThread.threadId}`
|
|
1037
|
+
});
|
|
1038
|
+
return resumedThread;
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1041
|
+
throw new Error(`Failed to resume Codex thread ${opts.threadId}: ${reason}`);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
const IMPORT_CODEX_THREAD_ENV = "HAPPY_IMPORT_CODEX_THREAD_ID";
|
|
1046
|
+
async function importCodexThread(opts) {
|
|
1047
|
+
let forked;
|
|
1048
|
+
try {
|
|
1049
|
+
forked = await opts.client.forkThread({
|
|
1050
|
+
threadId: opts.threadId,
|
|
1051
|
+
cwd: opts.cwd,
|
|
1052
|
+
mcpServers: opts.mcpServers
|
|
1053
|
+
});
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1056
|
+
throw new Error(`Failed to import Codex thread ${opts.threadId}: ${reason}`);
|
|
1057
|
+
}
|
|
1058
|
+
if (!forked.threadId || forked.threadId === opts.threadId) {
|
|
1059
|
+
throw new Error(`Failed to import Codex thread ${opts.threadId}: Codex did not create an independent copy`);
|
|
1060
|
+
}
|
|
1061
|
+
opts.session.updateMetadata((currentMetadata) => ({
|
|
1062
|
+
...currentMetadata,
|
|
1063
|
+
codexThreadId: forked.threadId,
|
|
1064
|
+
importedFromCodexThreadId: opts.threadId
|
|
1065
|
+
}));
|
|
1066
|
+
let thread = forked.thread;
|
|
1067
|
+
if (!Array.isArray(thread?.turns)) {
|
|
1068
|
+
thread = (await opts.client.readThread({ threadId: forked.threadId, includeTurns: true })).thread;
|
|
1069
|
+
}
|
|
1070
|
+
const envelopes = mapCodexThreadToSessionEnvelopes(thread);
|
|
1071
|
+
for (const envelope of envelopes) {
|
|
1072
|
+
opts.session.sendSessionProtocolMessage(envelope);
|
|
1073
|
+
}
|
|
1074
|
+
opts.messageBuffer.addMessage(`Imported thread ${trimIdent.trimIdent(opts.threadId)} as ${trimIdent.trimIdent(forked.threadId)}`, "status");
|
|
1075
|
+
opts.session.sendSessionEvent({
|
|
1076
|
+
type: "message",
|
|
1077
|
+
message: `Imported Codex thread ${opts.threadId} (continuing as ${forked.threadId})`
|
|
1078
|
+
});
|
|
1079
|
+
return { threadId: forked.threadId, model: forked.model, replayed: envelopes.length };
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function emitReadyIfIdle({ pending, queueSize, shouldExit, sendReady, notify }) {
|
|
1083
|
+
if (shouldExit) {
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
if (pending) {
|
|
1087
|
+
return false;
|
|
1088
|
+
}
|
|
1089
|
+
if (queueSize() > 0) {
|
|
1090
|
+
return false;
|
|
1091
|
+
}
|
|
1092
|
+
sendReady();
|
|
1093
|
+
notify?.();
|
|
1094
|
+
return true;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
const DEFAULT_CODEX_IDLE_RELEASE_MS = 5 * 60 * 1e3;
|
|
1098
|
+
const CODEX_IDLE_RELEASE_ENV = "HAPPY_CODEX_IDLE_RELEASE_MS";
|
|
1099
|
+
function resolveCodexIdleReleaseMs(raw) {
|
|
1100
|
+
if (raw === void 0 || raw.trim() === "") return DEFAULT_CODEX_IDLE_RELEASE_MS;
|
|
1101
|
+
const parsed = Number(raw);
|
|
1102
|
+
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_CODEX_IDLE_RELEASE_MS;
|
|
1103
|
+
return Math.floor(parsed);
|
|
1104
|
+
}
|
|
1105
|
+
function shouldReleaseIdleCodex(state) {
|
|
1106
|
+
if (state.shouldExit) return false;
|
|
1107
|
+
if (state.turnActive) return false;
|
|
1108
|
+
if (state.abortInProgress) return false;
|
|
1109
|
+
if (state.queueSize > 0) return false;
|
|
1110
|
+
return true;
|
|
1111
|
+
}
|
|
1112
|
+
class CodexIdleReleaseTimer {
|
|
1113
|
+
constructor(opts) {
|
|
1114
|
+
this.opts = opts;
|
|
1115
|
+
}
|
|
1116
|
+
timer = null;
|
|
1117
|
+
releasing = null;
|
|
1118
|
+
get enabled() {
|
|
1119
|
+
return this.opts.delayMs > 0;
|
|
1120
|
+
}
|
|
1121
|
+
get armed() {
|
|
1122
|
+
return this.timer !== null;
|
|
1123
|
+
}
|
|
1124
|
+
arm() {
|
|
1125
|
+
this.cancel();
|
|
1126
|
+
if (!this.enabled) return;
|
|
1127
|
+
const timer = setTimeout(() => {
|
|
1128
|
+
if (this.timer !== timer) return;
|
|
1129
|
+
this.timer = null;
|
|
1130
|
+
void this.fire();
|
|
1131
|
+
}, this.opts.delayMs);
|
|
1132
|
+
timer.unref?.();
|
|
1133
|
+
this.timer = timer;
|
|
1134
|
+
}
|
|
1135
|
+
cancel() {
|
|
1136
|
+
if (this.timer) {
|
|
1137
|
+
clearTimeout(this.timer);
|
|
1138
|
+
this.timer = null;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
/** Resolves once an in-flight release (if any) has finished. Test hook. */
|
|
1142
|
+
async settle() {
|
|
1143
|
+
await this.releasing;
|
|
1144
|
+
}
|
|
1145
|
+
async fire() {
|
|
1146
|
+
if (!this.opts.isIdle()) return;
|
|
1147
|
+
const run = (async () => {
|
|
1148
|
+
try {
|
|
1149
|
+
await this.opts.release();
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
this.opts.onError?.(error);
|
|
1152
|
+
}
|
|
1153
|
+
})();
|
|
1154
|
+
this.releasing = run;
|
|
1155
|
+
try {
|
|
1156
|
+
await run;
|
|
1157
|
+
} finally {
|
|
1158
|
+
if (this.releasing === run) this.releasing = null;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function isCodexClearText(text) {
|
|
1164
|
+
return specialCommands.parseSpecialCommand(text).type === "clear";
|
|
1165
|
+
}
|
|
1166
|
+
function enqueueCodexUserText(opts) {
|
|
1167
|
+
if (isCodexClearText(opts.text)) {
|
|
1168
|
+
opts.queue.pushIsolateAndClear(opts.text, opts.mode, opts.attachments, opts.sourceId);
|
|
1169
|
+
return "clear";
|
|
1170
|
+
}
|
|
1171
|
+
opts.queue.push(opts.text, opts.mode, opts.attachments, opts.sourceId);
|
|
1172
|
+
return "queued";
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function hashCodexEnhancedMode(mode) {
|
|
1176
|
+
return deterministicJson.hashObject({
|
|
1177
|
+
permissionMode: mode.permissionMode,
|
|
1178
|
+
model: mode.model,
|
|
1179
|
+
appendSystemPrompt: mode.appendSystemPrompt,
|
|
1180
|
+
effort: mode.effort
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
function buildCodexTurnPrompt(opts) {
|
|
1184
|
+
const parts = [];
|
|
1185
|
+
if (opts.includeAppendSystemPrompt && opts.mode.appendSystemPrompt) {
|
|
1186
|
+
parts.push(opts.mode.appendSystemPrompt);
|
|
1187
|
+
}
|
|
1188
|
+
parts.push(opts.message);
|
|
1189
|
+
if (opts.includeTitleInstruction) {
|
|
1190
|
+
parts.push(constants.CHANGE_TITLE_INSTRUCTION);
|
|
1191
|
+
parts.push(index.BUILTIN_TODO_DISCOVERY);
|
|
1192
|
+
}
|
|
1193
|
+
return parts.join("\n\n");
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
function byteLength(value) {
|
|
1197
|
+
return Buffer.byteLength(value, "utf8");
|
|
1198
|
+
}
|
|
1199
|
+
function safeTypeToken(value) {
|
|
1200
|
+
if (typeof value !== "string") return typeof value;
|
|
1201
|
+
return /^[a-z][a-z0-9_.-]{0,63}$/i.test(value) ? value : "unknown";
|
|
1202
|
+
}
|
|
1203
|
+
function logValueMetadata(value) {
|
|
1204
|
+
return typeof value === "string" ? { valueType: "string", valueBytes: byteLength(value) } : { valueType: value === null ? "null" : typeof value };
|
|
1205
|
+
}
|
|
1206
|
+
function codexEventLogMetadata(value) {
|
|
1207
|
+
const event = value && typeof value === "object" ? value : {};
|
|
1208
|
+
let payloadBytes;
|
|
1209
|
+
try {
|
|
1210
|
+
const encoded = JSON.stringify(value);
|
|
1211
|
+
if (encoded !== void 0) payloadBytes = byteLength(encoded);
|
|
1212
|
+
} catch {
|
|
1213
|
+
}
|
|
1214
|
+
return {
|
|
1215
|
+
eventType: safeTypeToken(event.type),
|
|
1216
|
+
...payloadBytes === void 0 ? {} : { payloadBytes },
|
|
1217
|
+
hasId: typeof event.id === "string" || typeof event.turn_id === "string" || typeof event.callId === "string"
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
function safeCodexErrorMetadata(value) {
|
|
1221
|
+
if (!value || typeof value !== "object") return { errorType: typeof value };
|
|
1222
|
+
const error = value;
|
|
1223
|
+
const status = error.status ?? error.response?.status;
|
|
1224
|
+
const safeCode = typeof error.code === "string" && /^[a-z0-9_.-]{1,64}$/i.test(error.code) ? { code: error.code } : typeof error.code === "number" ? { code: error.code } : typeof error.code === "string" ? { codeBytes: byteLength(error.code) } : {};
|
|
1225
|
+
return {
|
|
1226
|
+
errorType: safeTypeToken(error.name ?? value.constructor?.name),
|
|
1227
|
+
...safeCode,
|
|
1228
|
+
...typeof status === "number" ? { status } : {}
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const DEFAULT_CODEX_MODEL = "gpt-5.5";
|
|
1233
|
+
const DEFAULT_CODEX_EFFORT = "medium";
|
|
1234
|
+
async function runCodex(opts) {
|
|
1235
|
+
try {
|
|
1236
|
+
node_child_process.execSync("codex --version", { encoding: "utf8", stdio: "pipe", windowsHide: true });
|
|
1237
|
+
} catch {
|
|
1238
|
+
console.error("\n\x1B[1m\x1B[33mCodex CLI is not installed\x1B[0m\n");
|
|
1239
|
+
console.error("Please install Codex CLI using one of these methods:\n");
|
|
1240
|
+
console.error("\x1B[1mOption 1 - npm (recommended):\x1B[0m");
|
|
1241
|
+
console.error(" \x1B[36mnpm install -g @openai/codex\x1B[0m\n");
|
|
1242
|
+
console.error("\x1B[1mOption 2 - Homebrew (macOS):\x1B[0m");
|
|
1243
|
+
console.error(" \x1B[36mbrew install --cask codex\x1B[0m\n");
|
|
1244
|
+
console.error("Alternatively, use Claude Code:");
|
|
1245
|
+
console.error(" \x1B[36mvery-happy claude\x1B[0m\n");
|
|
1246
|
+
process.exit(1);
|
|
1247
|
+
}
|
|
1248
|
+
const sessionTag = node_crypto.randomUUID();
|
|
1249
|
+
api.connectionState.setBackend("Codex");
|
|
1250
|
+
const api$1 = await api.ApiClient.create(opts.credentials);
|
|
1251
|
+
logger.logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
|
|
1252
|
+
const settings = await persistence.readSettings();
|
|
1253
|
+
let machineId = settings?.machineId;
|
|
1254
|
+
const sandboxConfig = opts.noSandbox ? void 0 : settings?.sandboxConfig;
|
|
1255
|
+
if (!machineId) {
|
|
1256
|
+
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`);
|
|
1257
|
+
process.exit(1);
|
|
1258
|
+
}
|
|
1259
|
+
logger.logger.debug(`Using machineId: ${machineId}`);
|
|
1260
|
+
await api$1.getOrCreateMachine({
|
|
1261
|
+
machineId,
|
|
1262
|
+
metadata: machineMetadata.getInitialMachineMetadata()
|
|
1263
|
+
});
|
|
1264
|
+
const initialPermissionMode = opts.permissionMode ?? defaultPermissionMode.DEFAULT_CODEX_PERMISSION_MODE;
|
|
1265
|
+
const forkedFromSessionId = process.env.HAPPY_FORKED_FROM_SESSION_ID;
|
|
1266
|
+
const forkedFromMessageId = process.env.HAPPY_FORKED_FROM_MESSAGE_ID;
|
|
1267
|
+
const { state, metadata } = createSessionMetadata.createSessionMetadata({
|
|
1268
|
+
flavor: "codex",
|
|
1269
|
+
...process.env.VH_TEAM_OPERATION_ID ? { teamOperationId: process.env.VH_TEAM_OPERATION_ID } : {},
|
|
1270
|
+
machineId,
|
|
1271
|
+
startedBy: opts.startedBy,
|
|
1272
|
+
sandbox: sandboxConfig,
|
|
1273
|
+
dangerouslySkipPermissions: initialPermissionMode === "yolo" || initialPermissionMode === "bypassPermissions",
|
|
1274
|
+
...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
|
|
1275
|
+
...forkedFromMessageId ? { forkedFromMessageId } : {},
|
|
1276
|
+
capabilities: [index.PROMPT_QUEUE_CAPABILITY]
|
|
1277
|
+
});
|
|
1278
|
+
metadata.attachmentKinds = [...attachmentContent.CLAUDE_ATTACHMENT_KINDS];
|
|
1279
|
+
const importCodexThreadId = process.env[IMPORT_CODEX_THREAD_ENV];
|
|
1280
|
+
if (importCodexThreadId) {
|
|
1281
|
+
metadata.importedFromCodexThreadId = importCodexThreadId;
|
|
1282
|
+
const importTitle = process.env.HAPPY_IMPORT_TITLE;
|
|
1283
|
+
if (importTitle) metadata.summary = { text: importTitle, updatedAt: Date.now() };
|
|
1284
|
+
}
|
|
1285
|
+
const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
|
|
1286
|
+
const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
|
|
1287
|
+
const reconnectVariant = process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT;
|
|
1288
|
+
const reconnectSeq = process.env.HAPPY_RECONNECT_SEQ;
|
|
1289
|
+
const reconnectMetadataVersion = process.env.HAPPY_RECONNECT_METADATA_VERSION;
|
|
1290
|
+
const reconnectAgentStateVersion = process.env.HAPPY_RECONNECT_AGENT_STATE_VERSION;
|
|
1291
|
+
let response;
|
|
1292
|
+
if (reconnectSessionId && reconnectKeyBase64 && reconnectVariant) {
|
|
1293
|
+
logger.logger.debug(`[START] Reconnecting to existing session ${reconnectSessionId}`);
|
|
1294
|
+
response = {
|
|
1295
|
+
id: reconnectSessionId,
|
|
1296
|
+
seq: parseInt(reconnectSeq || "0", 10),
|
|
1297
|
+
encryptionKey: authRelay.decodeBase64(reconnectKeyBase64),
|
|
1298
|
+
encryptionVariant: reconnectVariant,
|
|
1299
|
+
metadata,
|
|
1300
|
+
metadataVersion: parseInt(reconnectMetadataVersion || "0", 10),
|
|
1301
|
+
agentState: state,
|
|
1302
|
+
agentStateVersion: parseInt(reconnectAgentStateVersion || "0", 10)
|
|
1303
|
+
};
|
|
1304
|
+
} else {
|
|
1305
|
+
response = await api$1.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
1306
|
+
}
|
|
1307
|
+
if (response) {
|
|
1308
|
+
await sessionLock.claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "codex" });
|
|
1309
|
+
}
|
|
1310
|
+
if (reconnectSessionId && response && !await api$1.reactivateSession(response.id)) {
|
|
1311
|
+
throw new Error(`Failed to reactivate archived session ${response.id}`);
|
|
1312
|
+
}
|
|
1313
|
+
let reconnectSeeded = false;
|
|
1314
|
+
if (reconnectSessionId && response) {
|
|
1315
|
+
const snapshot = await api$1.getSession(response.id, response.encryptionKey, response.encryptionVariant);
|
|
1316
|
+
if (snapshot.ok) {
|
|
1317
|
+
response = sessionLock.applyServerSnapshot(response, snapshot.session);
|
|
1318
|
+
reconnectSeeded = true;
|
|
1319
|
+
logger.logger.debug(`[START] Reconnect seeded from server: seq=${snapshot.session.seq} metadataVersion=${snapshot.session.metadataVersion}`);
|
|
1320
|
+
} else {
|
|
1321
|
+
response = sessionLock.withoutServerSnapshot(response);
|
|
1322
|
+
logger.logger.debug(`[START] Reconnect without server snapshot (${snapshot.reason}); skipping existing messages`);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
let session;
|
|
1326
|
+
let permissionHandler;
|
|
1327
|
+
let client;
|
|
1328
|
+
let reasoningProcessor;
|
|
1329
|
+
let abortInProgress = null;
|
|
1330
|
+
let turnActive = false;
|
|
1331
|
+
const { session: initialSession, reconnectionHandle } = setupOfflineReconnection.setupOfflineReconnection({
|
|
1332
|
+
api: api$1,
|
|
1333
|
+
sessionTag,
|
|
1334
|
+
metadata,
|
|
1335
|
+
state,
|
|
1336
|
+
response,
|
|
1337
|
+
sessionClientOptions: reconnectSeeded && response ? { initialSeq: response.seq } : void 0,
|
|
1338
|
+
onSessionSwap: (newSession) => {
|
|
1339
|
+
session = newSession;
|
|
1340
|
+
if (permissionHandler) {
|
|
1341
|
+
permissionHandler.updateSession(newSession);
|
|
1342
|
+
}
|
|
1343
|
+
attachPromptQueueDrain(newSession);
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
session = initialSession;
|
|
1347
|
+
if (reconnectSessionId) {
|
|
1348
|
+
session.suppressNextArchiveSignal();
|
|
1349
|
+
if (!reconnectSeeded) session.skipExistingMessages();
|
|
1350
|
+
void session.cancelUndeliveredQueuedInputs();
|
|
1351
|
+
session.updateMetadata((meta) => sessionLock.mergeReconnectMetadata(meta, metadata, Date.now()));
|
|
1352
|
+
}
|
|
1353
|
+
if (response) {
|
|
1354
|
+
try {
|
|
1355
|
+
logger.logger.debug(`[START] Reporting session ${response.id} to daemon`);
|
|
1356
|
+
const result = await controlClient.notifyDaemonSessionStarted(response.id, metadata, {
|
|
1357
|
+
encryptionKey: authRelay.encodeBase64(response.encryptionKey),
|
|
1358
|
+
encryptionVariant: response.encryptionVariant,
|
|
1359
|
+
seq: response.seq,
|
|
1360
|
+
metadataVersion: response.metadataVersion,
|
|
1361
|
+
agentStateVersion: response.agentStateVersion
|
|
1362
|
+
});
|
|
1363
|
+
if (result.error) {
|
|
1364
|
+
logger.logger.debug("[START] Failed to report to daemon (may not be running)", safeCodexErrorMetadata(result.error));
|
|
1365
|
+
} else {
|
|
1366
|
+
logger.logger.debug(`[START] Reported session ${response.id} to daemon`);
|
|
1367
|
+
}
|
|
1368
|
+
} catch (error) {
|
|
1369
|
+
logger.logger.debug("[START] Failed to report to daemon (may not be running)", safeCodexErrorMetadata(error));
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
const messageQueue = new registerKillSessionHandler.MessageQueue2(hashCodexEnhancedMode);
|
|
1373
|
+
function attachPromptQueueDrain(client2) {
|
|
1374
|
+
const drain = client2.attachPromptQueueDrain({
|
|
1375
|
+
isIdle: () => messageQueue.size() === 0 && messageQueue.isWaiting()
|
|
1376
|
+
});
|
|
1377
|
+
messageQueue.setOnWait(() => drain.onIdle());
|
|
1378
|
+
}
|
|
1379
|
+
attachPromptQueueDrain(session);
|
|
1380
|
+
messageQueue.setOnDiscard((entries) => {
|
|
1381
|
+
const keys = entries.map((entry) => entry.sourceId).filter((key) => typeof key === "string");
|
|
1382
|
+
if (keys.length > 0) session.sendQueueCancelReason(keys, entries[0].reason);
|
|
1383
|
+
});
|
|
1384
|
+
let currentPermissionMode = initialPermissionMode;
|
|
1385
|
+
let currentModel = DEFAULT_CODEX_MODEL;
|
|
1386
|
+
let currentEffort = DEFAULT_CODEX_EFFORT;
|
|
1387
|
+
let currentAppendSystemPrompt = void 0;
|
|
1388
|
+
const resetCurrentModeDefaults = () => {
|
|
1389
|
+
currentPermissionMode = defaultPermissionMode.DEFAULT_CODEX_PERMISSION_MODE;
|
|
1390
|
+
currentModel = DEFAULT_CODEX_MODEL;
|
|
1391
|
+
currentEffort = DEFAULT_CODEX_EFFORT;
|
|
1392
|
+
currentAppendSystemPrompt = void 0;
|
|
1393
|
+
logger.logger.debug("[Codex] Reset current mode defaults after abort");
|
|
1394
|
+
};
|
|
1395
|
+
const VALID_REMOTE_PERMISSION_MODES = [
|
|
1396
|
+
"default",
|
|
1397
|
+
"read-only",
|
|
1398
|
+
"safe-yolo",
|
|
1399
|
+
"yolo"
|
|
1400
|
+
];
|
|
1401
|
+
let discoveredModels = [];
|
|
1402
|
+
agentAttachments.registerAgentAttachmentDownloads(session);
|
|
1403
|
+
let userMessageDelivery = Promise.resolve();
|
|
1404
|
+
session.onUserMessage((message) => {
|
|
1405
|
+
const downloads = session.drainAttachmentsForUserMessage();
|
|
1406
|
+
userMessageDelivery = userMessageDelivery.then(async () => {
|
|
1407
|
+
const attachments = await downloads;
|
|
1408
|
+
let messagePermissionMode = currentPermissionMode;
|
|
1409
|
+
if (message.meta?.permissionMode) {
|
|
1410
|
+
const incoming = message.meta.permissionMode;
|
|
1411
|
+
if (VALID_REMOTE_PERMISSION_MODES.includes(incoming)) {
|
|
1412
|
+
messagePermissionMode = incoming;
|
|
1413
|
+
currentPermissionMode = messagePermissionMode;
|
|
1414
|
+
logger.logger.debug("[Codex] Valid permission mode override applied");
|
|
1415
|
+
} else {
|
|
1416
|
+
logger.logger.debug("[Codex] Ignoring invalid permission mode override", logValueMetadata(message.meta.permissionMode));
|
|
1417
|
+
}
|
|
1418
|
+
} else {
|
|
1419
|
+
logger.logger.debug("[Codex] User message received without a permission mode override");
|
|
1420
|
+
}
|
|
1421
|
+
let messageModel = currentModel;
|
|
1422
|
+
if (message.meta?.hasOwnProperty("model")) {
|
|
1423
|
+
messageModel = message.meta.model || void 0;
|
|
1424
|
+
currentModel = messageModel;
|
|
1425
|
+
logger.logger.debug("[Codex] Model override updated", logValueMetadata(message.meta.model));
|
|
1426
|
+
} else {
|
|
1427
|
+
logger.logger.debug("[Codex] User message received without a model override");
|
|
1428
|
+
}
|
|
1429
|
+
let messageEffort = currentEffort;
|
|
1430
|
+
if (message.meta?.hasOwnProperty("effort")) {
|
|
1431
|
+
const incoming = message.meta.effort;
|
|
1432
|
+
if (incoming === null || incoming === void 0) {
|
|
1433
|
+
messageEffort = void 0;
|
|
1434
|
+
currentEffort = void 0;
|
|
1435
|
+
logger.logger.debug(`[Codex] Effort reset to default`);
|
|
1436
|
+
} else if (typeof incoming === "string") {
|
|
1437
|
+
messageEffort = incoming;
|
|
1438
|
+
currentEffort = messageEffort;
|
|
1439
|
+
logger.logger.debug("[Codex] Valid effort override applied");
|
|
1440
|
+
} else {
|
|
1441
|
+
logger.logger.debug("[Codex] Ignoring invalid effort override", logValueMetadata(incoming));
|
|
1442
|
+
}
|
|
1443
|
+
} else {
|
|
1444
|
+
logger.logger.debug("[Codex] User message received without an effort override");
|
|
1445
|
+
}
|
|
1446
|
+
let messageAppendSystemPrompt = currentAppendSystemPrompt;
|
|
1447
|
+
if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
|
|
1448
|
+
messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
|
|
1449
|
+
currentAppendSystemPrompt = messageAppendSystemPrompt;
|
|
1450
|
+
logger.logger.debug("[Codex] Append system prompt override updated", logValueMetadata(message.meta.appendSystemPrompt));
|
|
1451
|
+
} else {
|
|
1452
|
+
logger.logger.debug("[Codex] User message received without an append system prompt override");
|
|
1453
|
+
}
|
|
1454
|
+
const enhancedMode = {
|
|
1455
|
+
permissionMode: messagePermissionMode || "default",
|
|
1456
|
+
model: messageModel,
|
|
1457
|
+
appendSystemPrompt: messageAppendSystemPrompt,
|
|
1458
|
+
effort: messageEffort
|
|
1459
|
+
};
|
|
1460
|
+
const enqueueResult = enqueueCodexUserText({
|
|
1461
|
+
text: message.content.text,
|
|
1462
|
+
mode: enhancedMode,
|
|
1463
|
+
queue: messageQueue,
|
|
1464
|
+
attachments,
|
|
1465
|
+
sourceId: message.localKey
|
|
1466
|
+
});
|
|
1467
|
+
if (enqueueResult === "clear") {
|
|
1468
|
+
logger.logger.debug("[Codex] /clear command pushed to isolated queue");
|
|
1469
|
+
}
|
|
1470
|
+
}).catch((error) => {
|
|
1471
|
+
logger.logger.warn("[Codex] Failed to prepare user attachments", safeCodexErrorMetadata(error));
|
|
1472
|
+
session.sendSessionEvent({ type: "message", message: "Could not prepare this message. Please send it again." });
|
|
1473
|
+
});
|
|
1474
|
+
});
|
|
1475
|
+
let thinking = false;
|
|
1476
|
+
let currentTurnId = null;
|
|
1477
|
+
let codexStartedSubagents = /* @__PURE__ */ new Set();
|
|
1478
|
+
let codexActiveSubagents = /* @__PURE__ */ new Set();
|
|
1479
|
+
let codexProviderSubagentToSessionSubagent = /* @__PURE__ */ new Map();
|
|
1480
|
+
session.keepAlive(thinking, "remote");
|
|
1481
|
+
const keepAliveInterval = setInterval(() => {
|
|
1482
|
+
session.keepAlive(thinking, "remote");
|
|
1483
|
+
}, 2e3);
|
|
1484
|
+
const sendReady = () => {
|
|
1485
|
+
session.sendSessionEvent({ type: "ready" });
|
|
1486
|
+
try {
|
|
1487
|
+
api$1.push().sendSessionNotification({
|
|
1488
|
+
kind: "done",
|
|
1489
|
+
metadata: session.getMetadata(),
|
|
1490
|
+
data: {
|
|
1491
|
+
sessionId: session.sessionId,
|
|
1492
|
+
type: "ready",
|
|
1493
|
+
provider: "codex"
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
} catch (pushError) {
|
|
1497
|
+
logger.logger.debug("[Codex] Failed to send ready push", safeCodexErrorMetadata(pushError));
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
function logActiveHandles(tag) {
|
|
1501
|
+
if (!process.env.DEBUG) return;
|
|
1502
|
+
const anyProc = process;
|
|
1503
|
+
const handles = typeof anyProc._getActiveHandles === "function" ? anyProc._getActiveHandles() : [];
|
|
1504
|
+
const requests = typeof anyProc._getActiveRequests === "function" ? anyProc._getActiveRequests() : [];
|
|
1505
|
+
logger.logger.debug(`[codex][handles] ${tag}: handles=${handles.length} requests=${requests.length}`);
|
|
1506
|
+
try {
|
|
1507
|
+
const kinds = handles.map((h) => h && h.constructor ? h.constructor.name : typeof h);
|
|
1508
|
+
logger.logger.debug(`[codex][handles] kinds=${JSON.stringify(kinds)}`);
|
|
1509
|
+
} catch {
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
let abortController = new AbortController();
|
|
1513
|
+
let shouldExit = false;
|
|
1514
|
+
async function handleAbort() {
|
|
1515
|
+
if (abortInProgress) {
|
|
1516
|
+
await abortInProgress;
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
logger.logger.debug("[Codex] Abort requested - stopping current task");
|
|
1520
|
+
abortInProgress = (async () => {
|
|
1521
|
+
try {
|
|
1522
|
+
if (permissionHandler) {
|
|
1523
|
+
permissionHandler.abortAll();
|
|
1524
|
+
}
|
|
1525
|
+
if (client) {
|
|
1526
|
+
const abortResult = await client.abortTurnWithFallback({
|
|
1527
|
+
gracePeriodMs: 3e3,
|
|
1528
|
+
forceRestartOnTimeout: true
|
|
1529
|
+
});
|
|
1530
|
+
if (abortResult.forcedRestart) {
|
|
1531
|
+
logger.logger.warn("[Codex] Forced app-server restart after interrupt timeout");
|
|
1532
|
+
session.sendSessionEvent({
|
|
1533
|
+
type: "message",
|
|
1534
|
+
message: abortResult.resumedThread ? "Force-stopped active task after interrupt timeout. Codex backend was restarted and the previous thread was resumed." : "Force-stopped active task after interrupt timeout. Codex backend was restarted, but the previous thread could not be resumed."
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
if (reasoningProcessor) {
|
|
1539
|
+
reasoningProcessor.abort();
|
|
1540
|
+
}
|
|
1541
|
+
logger.logger.debug("[Codex] Abort completed - session remains active");
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
logger.logger.debug("[Codex] Error during abort", safeCodexErrorMetadata(error));
|
|
1544
|
+
} finally {
|
|
1545
|
+
resetCurrentModeDefaults();
|
|
1546
|
+
abortController.abort();
|
|
1547
|
+
abortController = new AbortController();
|
|
1548
|
+
}
|
|
1549
|
+
})();
|
|
1550
|
+
await abortInProgress;
|
|
1551
|
+
abortInProgress = null;
|
|
1552
|
+
}
|
|
1553
|
+
const handleKillSession = async () => {
|
|
1554
|
+
logger.logger.debug("[Codex] Kill session requested - terminating process");
|
|
1555
|
+
await handleAbort();
|
|
1556
|
+
logger.logger.debug("[Codex] Abort completed, proceeding with termination");
|
|
1557
|
+
try {
|
|
1558
|
+
if (session) {
|
|
1559
|
+
session.updateMetadata((currentMetadata) => ({
|
|
1560
|
+
...currentMetadata,
|
|
1561
|
+
lifecycleState: "archived",
|
|
1562
|
+
lifecycleStateSince: Date.now(),
|
|
1563
|
+
archivedBy: "cli",
|
|
1564
|
+
archiveReason: "User terminated"
|
|
1565
|
+
}));
|
|
1566
|
+
session.sendSessionDeath();
|
|
1567
|
+
await session.flush();
|
|
1568
|
+
await session.close();
|
|
1569
|
+
}
|
|
1570
|
+
try {
|
|
1571
|
+
await client.disconnect();
|
|
1572
|
+
} catch (e) {
|
|
1573
|
+
logger.logger.debug("[Codex] Error disconnecting Codex during termination", safeCodexErrorMetadata(e));
|
|
1574
|
+
}
|
|
1575
|
+
happyServer.stop();
|
|
1576
|
+
logger.logger.debug("[Codex] Session termination complete, exiting");
|
|
1577
|
+
process.exit(0);
|
|
1578
|
+
} catch (error) {
|
|
1579
|
+
logger.logger.debug("[Codex] Error during session termination", safeCodexErrorMetadata(error));
|
|
1580
|
+
process.exit(1);
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
session.rpcHandlerManager.registerHandler("codex-child-read", async (args) => {
|
|
1584
|
+
try {
|
|
1585
|
+
return await readChildThread(client, args?.threadId);
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
return { error: error instanceof Error ? error.message : "Child read failed" };
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
session.rpcHandlerManager.registerHandler("abort", handleAbort);
|
|
1591
|
+
const disposeTermination = registerKillSessionHandler.registerKillSessionHandler(session.rpcHandlerManager, handleKillSession, session, { processSignals: true });
|
|
1592
|
+
const messageBuffer$1 = new messageBuffer.MessageBuffer();
|
|
1593
|
+
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
1594
|
+
let inkInstance = null;
|
|
1595
|
+
if (hasTTY) {
|
|
1596
|
+
console.clear();
|
|
1597
|
+
inkInstance = ink.render(React.createElement(CodexDisplay, {
|
|
1598
|
+
messageBuffer: messageBuffer$1,
|
|
1599
|
+
logPath: process.env.DEBUG ? logger.logger.getLogPath() : void 0,
|
|
1600
|
+
onExit: async () => {
|
|
1601
|
+
logger.logger.debug("[codex]: Exiting agent via Ctrl-C");
|
|
1602
|
+
shouldExit = true;
|
|
1603
|
+
await handleAbort();
|
|
1604
|
+
}
|
|
1605
|
+
}), {
|
|
1606
|
+
exitOnCtrlC: false,
|
|
1607
|
+
patchConsole: false
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
if (hasTTY) {
|
|
1611
|
+
process.stdin.resume();
|
|
1612
|
+
if (process.stdin.isTTY) {
|
|
1613
|
+
process.stdin.setRawMode(true);
|
|
1614
|
+
}
|
|
1615
|
+
process.stdin.setEncoding("utf8");
|
|
1616
|
+
}
|
|
1617
|
+
client = new codexAppServerClient.CodexAppServerClient(sandboxConfig);
|
|
1618
|
+
const idleRelease = new CodexIdleReleaseTimer({
|
|
1619
|
+
delayMs: resolveCodexIdleReleaseMs(process.env[CODEX_IDLE_RELEASE_ENV]),
|
|
1620
|
+
isIdle: () => shouldReleaseIdleCodex({
|
|
1621
|
+
turnActive,
|
|
1622
|
+
queueSize: messageQueue.size(),
|
|
1623
|
+
shouldExit,
|
|
1624
|
+
abortInProgress: abortInProgress !== null
|
|
1625
|
+
}),
|
|
1626
|
+
release: async () => {
|
|
1627
|
+
const released = await client.releaseIdleProcess();
|
|
1628
|
+
if (released) {
|
|
1629
|
+
logger.logger.debug("[Codex] Released idle app-server; thread kept for resume on next message");
|
|
1630
|
+
}
|
|
1631
|
+
},
|
|
1632
|
+
onError: (error) => logger.logger.debug("[Codex] Idle app-server release failed", safeCodexErrorMetadata(error))
|
|
1633
|
+
});
|
|
1634
|
+
permissionHandler = new CodexPermissionHandler(session);
|
|
1635
|
+
permissionHandler.reset("Previous CLI process exited before responding");
|
|
1636
|
+
reasoningProcessor = new ReasoningProcessor((message) => {
|
|
1637
|
+
const envelopes = mapCodexProcessorMessageToSessionEnvelopes(message, { currentTurnId });
|
|
1638
|
+
for (const envelope of envelopes) {
|
|
1639
|
+
session.sendSessionProtocolMessage(envelope);
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
const diffProcessor = new DiffProcessor((message) => {
|
|
1643
|
+
const envelopes = mapCodexProcessorMessageToSessionEnvelopes(message, { currentTurnId });
|
|
1644
|
+
for (const envelope of envelopes) {
|
|
1645
|
+
session.sendSessionProtocolMessage(envelope);
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
client.setApprovalHandler(async (params) => {
|
|
1649
|
+
const toolName = params.type === "exec" ? "CodexBash" : params.type === "patch" ? "CodexPatch" : params.toolName ?? "McpTool";
|
|
1650
|
+
const input = params.type === "exec" ? { command: params.command, cwd: params.cwd } : params.type === "patch" ? { changes: params.fileChanges } : params.input ?? {};
|
|
1651
|
+
try {
|
|
1652
|
+
const result = await permissionHandler.handleToolCall(params.callId, toolName, input);
|
|
1653
|
+
logger.logger.debug("[Codex] Permission request resolved");
|
|
1654
|
+
return result.decision;
|
|
1655
|
+
} catch (error) {
|
|
1656
|
+
logger.logger.debug("[Codex] Error handling permission", safeCodexErrorMetadata(error));
|
|
1657
|
+
return "denied";
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
const editThrottle = new editPaths.EditReportThrottle();
|
|
1661
|
+
client.setEventHandler((msg) => {
|
|
1662
|
+
if (msg.type === "token_count") {
|
|
1663
|
+
session.sendAgentUsageSnapshot("codex", msg);
|
|
1664
|
+
}
|
|
1665
|
+
logger.logger.debug("[Codex] Event metadata", codexEventLogMetadata(msg));
|
|
1666
|
+
if (msg.type === "agent_message") {
|
|
1667
|
+
messageBuffer$1.addMessage(msg.message, "assistant");
|
|
1668
|
+
} else if (msg.type === "agent_reasoning_delta") ; else if (msg.type === "agent_reasoning") {
|
|
1669
|
+
messageBuffer$1.addMessage(`[Thinking] ${msg.text.substring(0, 100)}...`, "system");
|
|
1670
|
+
} else if (msg.type === "exec_command_begin") {
|
|
1671
|
+
messageBuffer$1.addMessage(`Executing: ${msg.command}`, "tool");
|
|
1672
|
+
} else if (msg.type === "exec_command_end") {
|
|
1673
|
+
const output = msg.output || msg.error || "Command completed";
|
|
1674
|
+
const truncatedOutput = output.substring(0, 200);
|
|
1675
|
+
messageBuffer$1.addMessage(
|
|
1676
|
+
`Result: ${truncatedOutput}${output.length > 200 ? "..." : ""}`,
|
|
1677
|
+
"result"
|
|
1678
|
+
);
|
|
1679
|
+
} else if (msg.type === "task_started") {
|
|
1680
|
+
messageBuffer$1.addMessage("Starting task...", "status");
|
|
1681
|
+
} else if (msg.type === "task_complete") {
|
|
1682
|
+
const failure = describeCodexFailure(msg);
|
|
1683
|
+
if (failure) {
|
|
1684
|
+
messageBuffer$1.addMessage(`Task failed: ${failure}`, "status");
|
|
1685
|
+
session.sendSessionEvent({ type: "message", message: `Codex error: ${failure}` });
|
|
1686
|
+
} else {
|
|
1687
|
+
messageBuffer$1.addMessage("Task completed", "status");
|
|
1688
|
+
}
|
|
1689
|
+
} else if (msg.type === "turn_aborted") {
|
|
1690
|
+
const failure = describeCodexFailure(msg);
|
|
1691
|
+
if (failure) {
|
|
1692
|
+
messageBuffer$1.addMessage(`Turn aborted: ${failure}`, "status");
|
|
1693
|
+
session.sendSessionEvent({ type: "message", message: `Codex error: ${failure}` });
|
|
1694
|
+
} else {
|
|
1695
|
+
messageBuffer$1.addMessage("Turn aborted", "status");
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
if (msg.type === "task_started") {
|
|
1699
|
+
if (!thinking) {
|
|
1700
|
+
logger.logger.debug("thinking started");
|
|
1701
|
+
thinking = true;
|
|
1702
|
+
session.keepAlive(thinking, "remote");
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
if (msg.type === "task_complete" || msg.type === "turn_aborted") {
|
|
1706
|
+
if (thinking) {
|
|
1707
|
+
logger.logger.debug("thinking completed");
|
|
1708
|
+
thinking = false;
|
|
1709
|
+
session.keepAlive(thinking, "remote");
|
|
1710
|
+
}
|
|
1711
|
+
diffProcessor.reset();
|
|
1712
|
+
}
|
|
1713
|
+
if (msg.type === "agent_reasoning_section_break") {
|
|
1714
|
+
reasoningProcessor.handleSectionBreak();
|
|
1715
|
+
}
|
|
1716
|
+
if (msg.type === "agent_reasoning_delta") {
|
|
1717
|
+
reasoningProcessor.processDelta(msg.delta);
|
|
1718
|
+
}
|
|
1719
|
+
if (msg.type === "agent_reasoning") {
|
|
1720
|
+
reasoningProcessor.complete(msg.text);
|
|
1721
|
+
}
|
|
1722
|
+
if (msg.type === "patch_apply_begin") {
|
|
1723
|
+
const { changes } = msg;
|
|
1724
|
+
for (const edit of editThrottle.take(editPaths.extractCodexPatchPaths(changes))) {
|
|
1725
|
+
controlClient.reportSessionEditToDaemon({ sessionId: session.sessionId, path: edit.path, tool: edit.tool, cwd: process.cwd() });
|
|
1726
|
+
}
|
|
1727
|
+
const changeCount = Object.keys(changes).length;
|
|
1728
|
+
const filesMsg = changeCount === 1 ? "1 file" : `${changeCount} files`;
|
|
1729
|
+
messageBuffer$1.addMessage(`Modifying ${filesMsg}...`, "tool");
|
|
1730
|
+
}
|
|
1731
|
+
if (msg.type === "patch_apply_end") {
|
|
1732
|
+
const { stdout, stderr, success } = msg;
|
|
1733
|
+
if (success) {
|
|
1734
|
+
const message = stdout || "Files modified successfully";
|
|
1735
|
+
messageBuffer$1.addMessage(message.substring(0, 200), "result");
|
|
1736
|
+
} else {
|
|
1737
|
+
const errorMsg = stderr || "Failed to modify files";
|
|
1738
|
+
messageBuffer$1.addMessage(`Error: ${errorMsg.substring(0, 200)}`, "result");
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
if (msg.type === "turn_diff") {
|
|
1742
|
+
if (msg.unified_diff) {
|
|
1743
|
+
diffProcessor.processDiff(msg.unified_diff);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
if (msg.type !== "agent_reasoning_delta" && msg.type !== "agent_reasoning" && msg.type !== "agent_reasoning_section_break" && msg.type !== "turn_diff") {
|
|
1747
|
+
const mapped = mapCodexMcpMessageToSessionEnvelopes(msg, {
|
|
1748
|
+
currentTurnId,
|
|
1749
|
+
startedSubagents: codexStartedSubagents,
|
|
1750
|
+
activeSubagents: codexActiveSubagents,
|
|
1751
|
+
providerSubagentToSessionSubagent: codexProviderSubagentToSessionSubagent
|
|
1752
|
+
});
|
|
1753
|
+
currentTurnId = mapped.currentTurnId;
|
|
1754
|
+
codexStartedSubagents = mapped.startedSubagents;
|
|
1755
|
+
codexActiveSubagents = mapped.activeSubagents;
|
|
1756
|
+
codexProviderSubagentToSessionSubagent = mapped.providerSubagentToSessionSubagent;
|
|
1757
|
+
for (const envelope of mapped.envelopes) {
|
|
1758
|
+
session.sendSessionProtocolMessage(envelope);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
});
|
|
1762
|
+
const happyServer = await deterministicJson.startHappyServer(session);
|
|
1763
|
+
const bridgeEntrypoint = path.join(projectPath.projectPath(), "bin", "very-happy-mcp.mjs");
|
|
1764
|
+
const mcpServers = {
|
|
1765
|
+
happy: {
|
|
1766
|
+
command: process.execPath,
|
|
1767
|
+
args: ["--no-warnings", "--no-deprecation", bridgeEntrypoint, "--url", happyServer.url]
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
let first = true;
|
|
1771
|
+
let appendSystemPromptInjected = false;
|
|
1772
|
+
process.env.VH_PEER_SESSION_ID = session.sessionId;
|
|
1773
|
+
try {
|
|
1774
|
+
logger.logger.debug("[codex]: client.connect begin");
|
|
1775
|
+
await client.connect();
|
|
1776
|
+
logger.logger.debug("[codex]: client.connect done");
|
|
1777
|
+
try {
|
|
1778
|
+
discoveredModels = await client.listModels();
|
|
1779
|
+
if (discoveredModels.length > 0) {
|
|
1780
|
+
session.updateMetadata((metadata2) => ({
|
|
1781
|
+
...metadata2,
|
|
1782
|
+
models: discoveredModels.map((model) => ({
|
|
1783
|
+
code: model.model,
|
|
1784
|
+
value: model.displayName,
|
|
1785
|
+
description: model.description,
|
|
1786
|
+
reasoningEfforts: model.supportedReasoningEfforts.map((option) => option.reasoningEffort),
|
|
1787
|
+
defaultReasoningEffort: model.defaultReasoningEffort
|
|
1788
|
+
})),
|
|
1789
|
+
defaultModelCode: discoveredModels.find((model) => model.isDefault)?.model
|
|
1790
|
+
}));
|
|
1791
|
+
}
|
|
1792
|
+
} catch (error) {
|
|
1793
|
+
logger.logger.debug("[Codex] Model discovery unavailable; using compatibility fallback", safeCodexErrorMetadata(error));
|
|
1794
|
+
}
|
|
1795
|
+
if (opts.resumeThreadId) {
|
|
1796
|
+
await resumeExistingThread({
|
|
1797
|
+
client,
|
|
1798
|
+
session,
|
|
1799
|
+
messageBuffer: messageBuffer$1,
|
|
1800
|
+
threadId: opts.resumeThreadId,
|
|
1801
|
+
cwd: process.cwd(),
|
|
1802
|
+
mcpServers
|
|
1803
|
+
});
|
|
1804
|
+
first = false;
|
|
1805
|
+
appendSystemPromptInjected = true;
|
|
1806
|
+
}
|
|
1807
|
+
if (!reconnectSessionId && !opts.resumeThreadId && importCodexThreadId) {
|
|
1808
|
+
await importCodexThread({
|
|
1809
|
+
client,
|
|
1810
|
+
session,
|
|
1811
|
+
messageBuffer: messageBuffer$1,
|
|
1812
|
+
threadId: importCodexThreadId,
|
|
1813
|
+
cwd: process.cwd(),
|
|
1814
|
+
mcpServers
|
|
1815
|
+
});
|
|
1816
|
+
first = false;
|
|
1817
|
+
appendSystemPromptInjected = true;
|
|
1818
|
+
}
|
|
1819
|
+
const forkCodexThreadId = process.env.HAPPY_FORK_CODEX_THREAD_ID;
|
|
1820
|
+
if (!reconnectSessionId && forkCodexThreadId) {
|
|
1821
|
+
try {
|
|
1822
|
+
const { thread } = await client.readThread({
|
|
1823
|
+
threadId: forkCodexThreadId,
|
|
1824
|
+
includeTurns: true
|
|
1825
|
+
});
|
|
1826
|
+
const envelopes = mapCodexThreadToSessionEnvelopes(thread);
|
|
1827
|
+
for (const envelope of envelopes) {
|
|
1828
|
+
session.sendSessionProtocolMessage(envelope);
|
|
1829
|
+
}
|
|
1830
|
+
session.updateMetadata((currentMetadata) => ({
|
|
1831
|
+
...currentMetadata,
|
|
1832
|
+
codexThreadId: forkCodexThreadId
|
|
1833
|
+
}));
|
|
1834
|
+
logger.logger.debug(`[CODEX FORK BACKFILL] Replayed ${envelopes.length} historical envelopes from thread ${forkCodexThreadId}`);
|
|
1835
|
+
} catch (error) {
|
|
1836
|
+
logger.logger.debug(`[CODEX FORK BACKFILL] Failed to read thread ${forkCodexThreadId}`, safeCodexErrorMetadata(error));
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
let pending = null;
|
|
1840
|
+
idleRelease.arm();
|
|
1841
|
+
while (!shouldExit) {
|
|
1842
|
+
logActiveHandles("loop-top");
|
|
1843
|
+
let message = pending;
|
|
1844
|
+
pending = null;
|
|
1845
|
+
if (!message) {
|
|
1846
|
+
const waitSignal = abortController.signal;
|
|
1847
|
+
const batch = await messageQueue.waitForMessagesAndGetAsString(waitSignal);
|
|
1848
|
+
if (!batch) {
|
|
1849
|
+
if (waitSignal.aborted && !shouldExit) {
|
|
1850
|
+
logger.logger.debug("[codex]: Wait aborted while idle; ignoring and continuing");
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
logger.logger.debug(`[codex]: batch=${!!batch}, shouldExit=${shouldExit}`);
|
|
1854
|
+
break;
|
|
1855
|
+
}
|
|
1856
|
+
message = batch;
|
|
1857
|
+
}
|
|
1858
|
+
if (!message) {
|
|
1859
|
+
break;
|
|
1860
|
+
}
|
|
1861
|
+
idleRelease.cancel();
|
|
1862
|
+
turnActive = true;
|
|
1863
|
+
if (isCodexClearText(message.message)) {
|
|
1864
|
+
logger.logger.debug("[Codex] Handling /clear command - resetting Codex thread state");
|
|
1865
|
+
client.clearThreadState();
|
|
1866
|
+
currentTurnId = null;
|
|
1867
|
+
codexStartedSubagents = /* @__PURE__ */ new Set();
|
|
1868
|
+
codexActiveSubagents = /* @__PURE__ */ new Set();
|
|
1869
|
+
codexProviderSubagentToSessionSubagent = /* @__PURE__ */ new Map();
|
|
1870
|
+
permissionHandler.reset();
|
|
1871
|
+
reasoningProcessor.abort();
|
|
1872
|
+
diffProcessor.reset();
|
|
1873
|
+
appendSystemPromptInjected = false;
|
|
1874
|
+
thinking = false;
|
|
1875
|
+
session.keepAlive(thinking, "remote");
|
|
1876
|
+
messageBuffer$1.addMessage("Context was reset", "status");
|
|
1877
|
+
session.sendSessionEvent({ type: "message", message: "Context was reset" });
|
|
1878
|
+
session.updateMetadata((currentMetadata) => {
|
|
1879
|
+
const nextMetadata = { ...currentMetadata };
|
|
1880
|
+
delete nextMetadata.codexThreadId;
|
|
1881
|
+
return nextMetadata;
|
|
1882
|
+
});
|
|
1883
|
+
emitReadyIfIdle({
|
|
1884
|
+
pending,
|
|
1885
|
+
// B-509: server-queued prompts count as pending input (no ready/push per item).
|
|
1886
|
+
queueSize: () => messageQueue.size() + (session.promptQueueHasPending() ? 1 : 0),
|
|
1887
|
+
shouldExit,
|
|
1888
|
+
sendReady
|
|
1889
|
+
});
|
|
1890
|
+
turnActive = false;
|
|
1891
|
+
idleRelease.arm();
|
|
1892
|
+
continue;
|
|
1893
|
+
}
|
|
1894
|
+
messageBuffer$1.addMessage(message.message, "user");
|
|
1895
|
+
try {
|
|
1896
|
+
await client.ensureConnected();
|
|
1897
|
+
const sandboxManagedByHappy = client.sandboxEnabled;
|
|
1898
|
+
const executionPolicy = resolveCodexExecutionPolicy(
|
|
1899
|
+
message.mode.permissionMode,
|
|
1900
|
+
sandboxManagedByHappy
|
|
1901
|
+
);
|
|
1902
|
+
const effortRefusal = unsupportedEffortReason(discoveredModels, message.mode.model, message.mode.effort);
|
|
1903
|
+
if (effortRefusal) {
|
|
1904
|
+
throw new Error(effortRefusal);
|
|
1905
|
+
}
|
|
1906
|
+
if (!client.hasActiveThread()) {
|
|
1907
|
+
const startedThread = await client.startThread({
|
|
1908
|
+
model: message.mode.model,
|
|
1909
|
+
cwd: process.cwd(),
|
|
1910
|
+
approvalPolicy: executionPolicy.approvalPolicy,
|
|
1911
|
+
sandbox: executionPolicy.sandbox,
|
|
1912
|
+
mcpServers
|
|
1913
|
+
});
|
|
1914
|
+
session.updateMetadata((currentMetadata) => ({
|
|
1915
|
+
...currentMetadata,
|
|
1916
|
+
codexThreadId: startedThread.threadId
|
|
1917
|
+
}));
|
|
1918
|
+
}
|
|
1919
|
+
const includeAppendSystemPrompt = Boolean(
|
|
1920
|
+
message.mode.appendSystemPrompt && !appendSystemPromptInjected
|
|
1921
|
+
);
|
|
1922
|
+
const turnPrompt = buildCodexTurnPrompt({
|
|
1923
|
+
message: message.message,
|
|
1924
|
+
mode: message.mode,
|
|
1925
|
+
includeAppendSystemPrompt,
|
|
1926
|
+
includeTitleInstruction: first
|
|
1927
|
+
});
|
|
1928
|
+
const staged = message.attachments?.length ? await attachmentContent.stageClaudeAttachments(message.attachments, {
|
|
1929
|
+
happyHomeDir: configuration.configuration.happyHomeDir,
|
|
1930
|
+
sessionId: session.sessionId
|
|
1931
|
+
}) : [];
|
|
1932
|
+
const result = await client.sendTurnAndWait(attachmentContent.appendStagedAttachmentsToPrompt(turnPrompt, staged), {
|
|
1933
|
+
images: staged.filter((file) => ["image/jpeg", "image/png", "image/gif", "image/webp"].includes(file.mimeType)).map((file) => file.path),
|
|
1934
|
+
model: message.mode.model,
|
|
1935
|
+
approvalPolicy: executionPolicy.approvalPolicy,
|
|
1936
|
+
sandbox: executionPolicy.sandbox,
|
|
1937
|
+
effort: message.mode.effort
|
|
1938
|
+
});
|
|
1939
|
+
first = false;
|
|
1940
|
+
if (includeAppendSystemPrompt) {
|
|
1941
|
+
appendSystemPromptInjected = true;
|
|
1942
|
+
}
|
|
1943
|
+
if (result.aborted) {
|
|
1944
|
+
logger.logger.debug("[Codex] Turn aborted");
|
|
1945
|
+
}
|
|
1946
|
+
} catch (error) {
|
|
1947
|
+
logger.logger.warn("Error in codex session", safeCodexErrorMetadata(error));
|
|
1948
|
+
const reason = describeThrownTurnError(error);
|
|
1949
|
+
if (reason) {
|
|
1950
|
+
messageBuffer$1.addMessage(`Task failed: ${reason}`, "status");
|
|
1951
|
+
session.sendSessionEvent({ type: "message", message: `Codex error: ${reason}` });
|
|
1952
|
+
} else {
|
|
1953
|
+
messageBuffer$1.addMessage("Process exited unexpectedly", "status");
|
|
1954
|
+
session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
|
|
1955
|
+
}
|
|
1956
|
+
} finally {
|
|
1957
|
+
permissionHandler.reset();
|
|
1958
|
+
reasoningProcessor.abort();
|
|
1959
|
+
diffProcessor.reset();
|
|
1960
|
+
thinking = false;
|
|
1961
|
+
session.keepAlive(thinking, "remote");
|
|
1962
|
+
emitReadyIfIdle({
|
|
1963
|
+
pending,
|
|
1964
|
+
// B-509: server-queued prompts count as pending input (no ready/push per item).
|
|
1965
|
+
queueSize: () => messageQueue.size() + (session.promptQueueHasPending() ? 1 : 0),
|
|
1966
|
+
shouldExit,
|
|
1967
|
+
sendReady
|
|
1968
|
+
});
|
|
1969
|
+
turnActive = false;
|
|
1970
|
+
idleRelease.arm();
|
|
1971
|
+
logActiveHandles("after-turn");
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
} finally {
|
|
1975
|
+
disposeTermination?.();
|
|
1976
|
+
session.closePromptQueueDrain();
|
|
1977
|
+
logger.logger.debug("[codex]: Final cleanup start");
|
|
1978
|
+
logActiveHandles("cleanup-start");
|
|
1979
|
+
if (reconnectionHandle) {
|
|
1980
|
+
logger.logger.debug("[codex]: Cancelling offline reconnection");
|
|
1981
|
+
reconnectionHandle.cancel();
|
|
1982
|
+
}
|
|
1983
|
+
try {
|
|
1984
|
+
logger.logger.debug("[codex]: sendSessionDeath");
|
|
1985
|
+
session.sendSessionDeath();
|
|
1986
|
+
logger.logger.debug("[codex]: flush begin");
|
|
1987
|
+
await session.flush();
|
|
1988
|
+
logger.logger.debug("[codex]: flush done");
|
|
1989
|
+
logger.logger.debug("[codex]: session.close begin");
|
|
1990
|
+
await session.close();
|
|
1991
|
+
logger.logger.debug("[codex]: session.close done");
|
|
1992
|
+
} catch (e) {
|
|
1993
|
+
logger.logger.debug("[codex]: Error while closing session", safeCodexErrorMetadata(e));
|
|
1994
|
+
}
|
|
1995
|
+
idleRelease.cancel();
|
|
1996
|
+
logger.logger.debug("[codex]: client.disconnect begin");
|
|
1997
|
+
await client.disconnect();
|
|
1998
|
+
logger.logger.debug("[codex]: client.disconnect done");
|
|
1999
|
+
logger.logger.debug("[codex]: happyServer.stop");
|
|
2000
|
+
happyServer.stop();
|
|
2001
|
+
if (process.stdin.isTTY) {
|
|
2002
|
+
logger.logger.debug("[codex]: setRawMode(false)");
|
|
2003
|
+
try {
|
|
2004
|
+
process.stdin.setRawMode(false);
|
|
2005
|
+
} catch {
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
if (hasTTY) {
|
|
2009
|
+
logger.logger.debug("[codex]: stdin.pause()");
|
|
2010
|
+
try {
|
|
2011
|
+
process.stdin.pause();
|
|
2012
|
+
} catch {
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
logger.logger.debug("[codex]: clearInterval(keepAlive)");
|
|
2016
|
+
clearInterval(keepAliveInterval);
|
|
2017
|
+
if (inkInstance) {
|
|
2018
|
+
logger.logger.debug("[codex]: inkInstance.unmount()");
|
|
2019
|
+
inkInstance.unmount();
|
|
2020
|
+
}
|
|
2021
|
+
messageBuffer$1.clear();
|
|
2022
|
+
logActiveHandles("cleanup-end");
|
|
2023
|
+
logger.logger.debug("[codex]: Final cleanup completed");
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
function extractCodexResumeFlag(args) {
|
|
2028
|
+
const remainingArgs = [];
|
|
2029
|
+
let resumeThreadId = null;
|
|
2030
|
+
for (let i = 0; i < args.length; i++) {
|
|
2031
|
+
const arg = args[i];
|
|
2032
|
+
if (arg === "--resume" || arg === "-r") {
|
|
2033
|
+
if (resumeThreadId !== null) {
|
|
2034
|
+
throw new Error("Codex resume flag can only be provided once.");
|
|
2035
|
+
}
|
|
2036
|
+
const nextArg = args[i + 1];
|
|
2037
|
+
if (!nextArg || nextArg.startsWith("-")) {
|
|
2038
|
+
throw new Error("Codex resume requires a thread ID: very-happy codex --resume <thread-id>");
|
|
2039
|
+
}
|
|
2040
|
+
resumeThreadId = nextArg;
|
|
2041
|
+
i++;
|
|
2042
|
+
continue;
|
|
2043
|
+
}
|
|
2044
|
+
if (arg.startsWith("--resume=")) {
|
|
2045
|
+
if (resumeThreadId !== null) {
|
|
2046
|
+
throw new Error("Codex resume flag can only be provided once.");
|
|
2047
|
+
}
|
|
2048
|
+
const value = arg.slice("--resume=".length).trim();
|
|
2049
|
+
if (!value) {
|
|
2050
|
+
throw new Error("Codex resume requires a thread ID: very-happy codex --resume <thread-id>");
|
|
2051
|
+
}
|
|
2052
|
+
resumeThreadId = value;
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
remainingArgs.push(arg);
|
|
2056
|
+
}
|
|
2057
|
+
return {
|
|
2058
|
+
resumeThreadId,
|
|
2059
|
+
args: remainingArgs
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
async function handleCodexCommand(args) {
|
|
2064
|
+
let startedBy = void 0;
|
|
2065
|
+
let permissionMode = void 0;
|
|
2066
|
+
const sandboxArgs = ensureDaemonRunning.extractNoSandboxFlag(args);
|
|
2067
|
+
const codexArgs = extractCodexResumeFlag(sandboxArgs.args);
|
|
2068
|
+
for (let i = 0; i < codexArgs.args.length; i++) {
|
|
2069
|
+
if (codexArgs.args[i] === "--started-by") {
|
|
2070
|
+
startedBy = codexArgs.args[++i];
|
|
2071
|
+
} else if (codexArgs.args[i] === "--permission-mode") {
|
|
2072
|
+
permissionMode = codexArgs.args[++i];
|
|
2073
|
+
} else if (codexArgs.args[i] === "--yolo") {
|
|
2074
|
+
permissionMode = "yolo";
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
const { credentials } = await auth.authAndSetupMachineIfNeeded();
|
|
2078
|
+
await ensureDaemonRunning.ensureDaemonRunning();
|
|
2079
|
+
await runCodex({
|
|
2080
|
+
credentials,
|
|
2081
|
+
startedBy,
|
|
2082
|
+
noSandbox: sandboxArgs.noSandbox,
|
|
2083
|
+
resumeThreadId: codexArgs.resumeThreadId ?? void 0,
|
|
2084
|
+
permissionMode
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
exports.handleCodexCommand = handleCodexCommand;
|