very-happy-cli 0.2.158 → 0.2.160

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/dist/{AcpBackend-DIDRig5G.cjs → AcpBackend-Bm2rdke8.cjs} +78 -76
  2. package/dist/{AcpBackend-vDVLHuYJ.mjs → AcpBackend-mzv8Dhba.mjs} +4 -2
  3. package/dist/{AcpSessionManager-C44U99CC.cjs → AcpSessionManager-BAY1Pggx.cjs} +9 -9
  4. package/dist/{AcpSessionManager-CmRjfhn3.mjs → AcpSessionManager-CoMWFVw3.mjs} +1 -1
  5. package/dist/BasePermissionHandler-CFHAxumV.cjs +157 -0
  6. package/dist/BasePermissionHandler-m3WVholL.mjs +155 -0
  7. package/dist/BaseReasoningProcessor-BFa9_rZA.cjs +200 -0
  8. package/dist/BaseReasoningProcessor-CYrKBXGm.mjs +198 -0
  9. package/dist/RemoteModeDisplay-D-HewyXg.mjs +150 -0
  10. package/dist/RemoteModeDisplay-NC1gPHtj.cjs +153 -0
  11. package/dist/agentAttachments-Dbw8iMCZ.cjs +20 -0
  12. package/dist/agentAttachments-M_B5qEF2.mjs +18 -0
  13. package/dist/agentGuidance-B5NATuFa.cjs +28 -0
  14. package/dist/agentGuidance-CQuNlNx8.mjs +20 -0
  15. package/dist/{agentHome-Dw8Jx_CJ.cjs → agentHome-CboCeKWq.cjs} +14 -28
  16. package/dist/{agentHome-CyLmGH5L.mjs → agentHome-Dj1C43aJ.mjs} +8 -22
  17. package/dist/api-CLMUr-SX.mjs +3339 -0
  18. package/dist/api-CgundNNX.cjs +3346 -0
  19. package/dist/{types-DIBF4U0Y.mjs → apiMachine-B9dLW_p5.mjs} +9116 -16015
  20. package/dist/{types-DnrPeN80.cjs → apiMachine-Bn8482VM.cjs} +9104 -16057
  21. package/dist/assistantTools-BTqPnlFl.cjs +380 -0
  22. package/dist/assistantTools-D6tuc6o4.mjs +376 -0
  23. package/dist/attachmentContent-DpiTJZb4.cjs +88 -0
  24. package/dist/attachmentContent-_kxRznNf.mjs +82 -0
  25. package/dist/attachmentLimits-CzVrqgLR.cjs +354 -0
  26. package/dist/attachmentLimits-yba1uHFO.mjs +348 -0
  27. package/dist/auth-BDOVh5h5.cjs +220 -0
  28. package/dist/auth-Cwutmdsa.mjs +225 -0
  29. package/dist/auth-PYHZQNPp.mjs +218 -0
  30. package/dist/auth-ba4WMYrN.cjs +227 -0
  31. package/dist/authRelay-BdYkC1Po.cjs +156 -0
  32. package/dist/authRelay-DSAvg1uv.mjs +143 -0
  33. package/dist/{auto-DZbNcTXx.cjs → auto-BOX_fgmR.cjs} +70 -79
  34. package/dist/{auto-BlMZPwOk.mjs → auto-BXu14yP_.mjs} +27 -36
  35. package/dist/backgroundTaskActivity-8PdPxrvy.mjs +149 -0
  36. package/dist/backgroundTaskActivity-CBvUdj39.cjs +157 -0
  37. package/dist/bootstrap-B5G1TijC.cjs +116 -0
  38. package/dist/bootstrap-hxXPCFMx.mjs +112 -0
  39. package/dist/browser-Y6Bl8L_O.cjs +22 -0
  40. package/dist/browser-jh2P0UVZ.mjs +20 -0
  41. package/dist/claudeCliPath-BqkaH2oR.mjs +8 -0
  42. package/dist/claudeCliPath-U4HHmV4r.cjs +10 -0
  43. package/dist/claudeLocal-BaY4fZKi.mjs +339 -0
  44. package/dist/claudeLocal-CRGG4Pea.cjs +343 -0
  45. package/dist/claudeLocalLauncher-D4T7MTqd.mjs +152 -0
  46. package/dist/claudeLocalLauncher-DyEbVzUN.cjs +154 -0
  47. package/dist/cliUpdate-DsKXPFYp.cjs +120 -0
  48. package/dist/cliUpdate-Y34VGe9Y.mjs +112 -0
  49. package/dist/client-3Ac-O4pw.cjs +104 -0
  50. package/dist/client-BPNbmtvO.cjs +119 -0
  51. package/dist/client-CeGKqqcJ.mjs +100 -0
  52. package/dist/client-ClR0K8zX.mjs +112 -0
  53. package/dist/codex/happyMcpStdioBridge.cjs +33 -14
  54. package/dist/codex/happyMcpStdioBridge.mjs +26 -7
  55. package/dist/codexAppServerClient-I3teiV-N.mjs +1088 -0
  56. package/dist/codexAppServerClient-q2FjnRcK.cjs +1090 -0
  57. package/dist/codexCommand-4zcccIoq.cjs +2088 -0
  58. package/dist/codexCommand-Cc9gcbdX.mjs +2086 -0
  59. package/dist/codexThreadFork-BH9IZPqD.cjs +350 -0
  60. package/dist/codexThreadFork-slzAseLx.mjs +338 -0
  61. package/dist/{config-BtPArqRH.mjs → config-CTx_jb-C.mjs} +7 -40
  62. package/dist/{config-DG84bjwF.cjs → config-Ci9Kpgwa.cjs} +26 -59
  63. package/dist/configuration-BSYpcPwE.cjs +181 -0
  64. package/dist/configuration-RfeNjFmd.mjs +170 -0
  65. package/dist/connect-BpOdzNbE.mjs +682 -0
  66. package/dist/connect-C2CiOUFs.cjs +684 -0
  67. package/dist/constants-BC4St0xd.mjs +11 -0
  68. package/dist/constants-VJ0N6WuG.cjs +17 -0
  69. package/dist/contentLogMetadata-BlWI0egu.mjs +51 -0
  70. package/dist/contentLogMetadata-ZSFk_NjQ.cjs +54 -0
  71. package/dist/context-DsDr6E2t.cjs +69 -0
  72. package/dist/context-H0Bcb9S_.mjs +65 -0
  73. package/dist/controlClient-BGbDcgWS.mjs +260 -0
  74. package/dist/controlClient-Bcwv3D21.cjs +279 -0
  75. package/dist/createSessionMetadata-BJxEnM-o.cjs +58 -0
  76. package/dist/createSessionMetadata-CVVkGjxV.mjs +54 -0
  77. package/dist/defaultPermissionMode-BnxJPVHT.mjs +4 -0
  78. package/dist/defaultPermissionMode-CxmTXUQR.cjs +7 -0
  79. package/dist/detectCLI-CSGMlV51.mjs +58 -0
  80. package/dist/detectCLI-DR9ZaN6B.cjs +61 -0
  81. package/dist/deterministicJson-C5CYTVpm.cjs +507 -0
  82. package/dist/deterministicJson-CzkFJWw1.mjs +502 -0
  83. package/dist/doctor-BPkYWSuq.mjs +75 -0
  84. package/dist/doctor-D2YJ-Yn2.cjs +395 -0
  85. package/dist/doctor-DOvByjbk.mjs +391 -0
  86. package/dist/doctor-u46qYYKX.cjs +79 -0
  87. package/dist/doctorReadiness-BCZGcN0a.cjs +151 -0
  88. package/dist/doctorReadiness-C208SMN9.mjs +142 -0
  89. package/dist/editPaths-BVfS_FFL.mjs +68 -0
  90. package/dist/editPaths-Bqp4OFPw.cjs +73 -0
  91. package/dist/ensureDaemonRunning-BCwNrMyv.cjs +64 -0
  92. package/dist/ensureDaemonRunning-Dqa2pOjY.mjs +61 -0
  93. package/dist/handleResumeCommand-BvXYf5El.mjs +240 -0
  94. package/dist/handleResumeCommand-C2cKchpX.cjs +245 -0
  95. package/dist/ids-B6ZC0HGt.cjs +13 -0
  96. package/dist/ids-DU6sLx4C.mjs +10 -0
  97. package/dist/index-B6UGRQ7h.mjs +102 -0
  98. package/dist/{index-DPCsWlWK.cjs → index-B6pAdvJx.cjs} +131 -91
  99. package/dist/index-Bu5Zi_a9.cjs +182 -0
  100. package/dist/index-D3kI0hDR.cjs +107 -0
  101. package/dist/index-DA7yINuk.cjs +994 -0
  102. package/dist/index-DDeHXrIX.cjs +71 -0
  103. package/dist/index-DKLU_fD0.mjs +158 -0
  104. package/dist/{index-ZChGsuB7.mjs → index-DMZqWwsP.mjs} +79 -39
  105. package/dist/index-DVkMGPyS.mjs +68 -0
  106. package/dist/index-qdS1friN.mjs +949 -0
  107. package/dist/index.cjs +5 -46
  108. package/dist/index.mjs +5 -46
  109. package/dist/{install-DarS_Wco.mjs → install-BO_W7VhL.mjs} +37 -37
  110. package/dist/install-BgoC2e_2.cjs +88 -0
  111. package/dist/install-CFY3tYtJ.mjs +86 -0
  112. package/dist/{install-wKQGgXvN.cjs → install-Cs5qAPrB.cjs} +38 -38
  113. package/dist/{installPiTools-DuZ6jZvX.cjs → installPiTools-BKnXjrVu.cjs} +2 -3
  114. package/dist/{installPiTools-Cj7yU9Fc.mjs → installPiTools-B_CdM5_2.mjs} +1 -2
  115. package/dist/{installTerminalHooks-DcQfee_O.cjs → installTerminalHooks-BQZbvxsZ.cjs} +5 -40
  116. package/dist/{installTerminalHooks-CpNbRFPz.mjs → installTerminalHooks-By7gBMEF.mjs} +3 -38
  117. package/dist/lib.cjs +28 -19
  118. package/dist/lib.d.cts +126 -2
  119. package/dist/lib.d.mts +126 -2
  120. package/dist/lib.mjs +23 -14
  121. package/dist/lidState-BeDfxxEX.cjs +305 -0
  122. package/dist/lidState-CeEt442o.mjs +297 -0
  123. package/dist/limits-C-a7gr04.cjs +43 -0
  124. package/dist/limits-DIo7rEBR.mjs +36 -0
  125. package/dist/localHappyAgentAuth-C-HQyiv6.mjs +82 -0
  126. package/dist/localHappyAgentAuth-D6rFhaoc.cjs +86 -0
  127. package/dist/logger-6HYUEXNk.mjs +271 -0
  128. package/dist/logger-CS0mw3fa.cjs +275 -0
  129. package/dist/loginShellEnv-BkuYtExZ.cjs +182 -0
  130. package/dist/loginShellEnv-Dw8M7tkI.mjs +167 -0
  131. package/dist/machineMetadata-DyDbODYd.cjs +41 -0
  132. package/dist/machineMetadata-oOzBfDfs.mjs +38 -0
  133. package/dist/main-BkxJP6JQ.cjs +941 -0
  134. package/dist/main-BwNIAI1A.mjs +939 -0
  135. package/dist/manager-BQ4flvQZ.mjs +101 -0
  136. package/dist/manager-Cly8xVIR.cjs +105 -0
  137. package/dist/{mcp-BN6bKIGb.mjs → mcp-B1A4I3TK.mjs} +27 -33
  138. package/dist/{mcp-DGdeUUDJ.cjs → mcp-Hszd7qpS.cjs} +42 -48
  139. package/dist/messageBuffer-BWlGgR3A.mjs +71 -0
  140. package/dist/messageBuffer-fJMH6uY8.cjs +73 -0
  141. package/dist/mirrorProtocol-BNLtoWjO.mjs +170 -0
  142. package/dist/mirrorProtocol-BUtmg1ws.cjs +186 -0
  143. package/dist/oneShotPrompts-CL0dip9C.cjs +16 -0
  144. package/dist/oneShotPrompts-DT4p6RNd.mjs +12 -0
  145. package/dist/package-BhaKlnxz.mjs +171 -0
  146. package/dist/package-BwMrPwqH.cjs +173 -0
  147. package/dist/path-B7zW-WwE.cjs +18 -0
  148. package/dist/path-D2wdaNSk.mjs +15 -0
  149. package/dist/peerTools-BTqw97Gb.mjs +272 -0
  150. package/dist/peerTools-BvKtHXj8.cjs +283 -0
  151. package/dist/permissionMode-Cr5JCbB7.mjs +80 -0
  152. package/dist/permissionMode-CrV-XuMs.cjs +87 -0
  153. package/dist/persistence-HGW_ZVs9.mjs +372 -0
  154. package/dist/persistence-yKTH3-5X.cjs +414 -0
  155. package/dist/piExtension.mjs +2 -1
  156. package/dist/piRunnerArgs-BNloJCCg.cjs +66 -0
  157. package/dist/piRunnerArgs-BuX1UFWy.mjs +59 -0
  158. package/dist/{piRuntime-bkw2NAZU.mjs → piRuntime-CSGkwCKQ.mjs} +2 -2
  159. package/dist/{piRuntime-3qtbKujX.cjs → piRuntime-C_H7MJCM.cjs} +4 -4
  160. package/dist/{piTerminal-DilKwM7B.mjs → piTerminal-C8a1OrNQ.mjs} +32 -35
  161. package/dist/{piTerminal-BYxp3LMr.cjs → piTerminal-DNCZ6yTM.cjs} +32 -35
  162. package/dist/previewPath-B0rBdhy1.cjs +83 -0
  163. package/dist/previewPath-CXN4Vag0.mjs +81 -0
  164. package/dist/projectPath-BTBIMB57.mjs +10 -0
  165. package/dist/projectPath-ChLhdOyt.cjs +13 -0
  166. package/dist/registerCommonHandlers-Ccvsz7Ou.mjs +536 -0
  167. package/dist/registerCommonHandlers-DskirN-_.cjs +538 -0
  168. package/dist/registerKillSessionHandler-DZeLXWcB.cjs +361 -0
  169. package/dist/registerKillSessionHandler-GYvHdipX.mjs +358 -0
  170. package/dist/remoteSessionClient-B0LraSB9.cjs +634 -0
  171. package/dist/remoteSessionClient-CYnXRynE.mjs +612 -0
  172. package/dist/resources-BdFBQi8r.mjs +117 -0
  173. package/dist/resources-JDPugcDq.cjs +120 -0
  174. package/dist/run-CJzxoHVh.cjs +5762 -0
  175. package/dist/run-D3ifupU3.mjs +5739 -0
  176. package/dist/runClaude-Ba01qT-u.mjs +848 -0
  177. package/dist/runClaude-Da3RirzE.cjs +850 -0
  178. package/dist/runClaudeDeps-DOt79DjU.mjs +3624 -0
  179. package/dist/runClaudeDeps-DOzEBMls.cjs +3649 -0
  180. package/dist/{runGemini-Cjii069p.mjs → runGemini-BGeTz57m.mjs} +58 -31
  181. package/dist/{runGemini-28O2sqyI.cjs → runGemini-DSr9LIxL.cjs} +202 -175
  182. package/dist/{runOpenClaw-CiMZad-H.cjs → runOpenClaw-B1mTxN35.cjs} +64 -65
  183. package/dist/{runOpenClaw-jaLr7KF1.mjs → runOpenClaw-CogSedIc.mjs} +31 -32
  184. package/dist/sandbox-CG7IyDTU.cjs +187 -0
  185. package/dist/sandbox-DTe6d54R.mjs +180 -0
  186. package/dist/selfCheck-C9mVrQwf.mjs +84 -0
  187. package/dist/selfCheck-Dc-mNSLb.cjs +87 -0
  188. package/dist/{send-D5ejHPXG.cjs → send-B7J_eaG3.cjs} +16 -9
  189. package/dist/{send-C9mOtChi.mjs → send-CBXJKc6O.mjs} +12 -5
  190. package/dist/server-CZTP_iOn.cjs +407 -0
  191. package/dist/server-DcVBv04l.mjs +404 -0
  192. package/dist/sessionLock-Bys1PNSo.cjs +205 -0
  193. package/dist/sessionLock-D-EG2CfG.mjs +197 -0
  194. package/dist/sessionModeFile-DMl3dw3r.mjs +32 -0
  195. package/dist/sessionModeFile-RSm0NQkH.cjs +36 -0
  196. package/dist/sessionOps-Dn8z85h_.cjs +783 -0
  197. package/dist/sessionOps-SuHW8Dfe.mjs +760 -0
  198. package/dist/sessionScanner-BOrGjMXL.cjs +209 -0
  199. package/dist/sessionScanner-CX3onqXh.mjs +206 -0
  200. package/dist/{sessions-W6771g3X.mjs → sessions-BEG_HTSi.mjs} +17 -8
  201. package/dist/{sessions-BNMPQoLE.cjs → sessions-D74aJbhL.cjs} +37 -28
  202. package/dist/setupOfflineReconnection-ByElY60m.cjs +74 -0
  203. package/dist/setupOfflineReconnection-DpvWTt69.mjs +72 -0
  204. package/dist/smallFastModel-BLNgMrwR.mjs +18 -0
  205. package/dist/smallFastModel-D_uxGMo3.cjs +21 -0
  206. package/dist/{spawn-D0QVhzRb.mjs → spawn-B7yro0b2.mjs} +26 -38
  207. package/dist/{spawn-Ddtom1cb.cjs → spawn-DgR0RKRs.cjs} +52 -64
  208. package/dist/spawnAgents-BGQiEF-w.cjs +52 -0
  209. package/dist/spawnAgents-DZzZ9jMI.mjs +45 -0
  210. package/dist/spawnHappyCLI-B2H5s4B_.mjs +64 -0
  211. package/dist/spawnHappyCLI-DBhwZT7O.cjs +66 -0
  212. package/dist/specialCommands-BSqhYg5X.mjs +52 -0
  213. package/dist/specialCommands-DSsTeRej.cjs +54 -0
  214. package/dist/startFileWatcher-B4VsLgjD.cjs +71 -0
  215. package/dist/startFileWatcher-DErVWZE4.mjs +69 -0
  216. package/dist/startupPlan-BLMbfQIH.cjs +12 -0
  217. package/dist/startupPlan-DFMyNgAs.mjs +10 -0
  218. package/dist/streamRelay-BiKw59Cc.cjs +233 -0
  219. package/dist/streamRelay-FPVbHpbM.mjs +231 -0
  220. package/dist/sync-C8gyQFMc.mjs +69 -0
  221. package/dist/sync-DccIKZ5_.cjs +71 -0
  222. package/dist/systemPrompt-BwTPWfMu.cjs +97 -0
  223. package/dist/systemPrompt-DX521JJz.mjs +94 -0
  224. package/dist/{teams-CqSwtw2O.mjs → teams-C32F1hqd.mjs} +9 -40
  225. package/dist/{teams-BZ1tayg_.cjs → teams-Dl8g8kTI.cjs} +14 -45
  226. package/dist/template-BaHAmH2K.cjs +129 -0
  227. package/dist/template-CiOPa2rd.mjs +121 -0
  228. package/dist/{terminalTitleSuggest-DKgX3osW.mjs → terminalTitleSuggest-CeKsX_V6.mjs} +1 -9
  229. package/dist/{terminalTitleSuggest-Dji8I0Jy.cjs → terminalTitleSuggest-DhvRFX3H.cjs} +0 -10
  230. package/dist/terminals-B_bfC9c2.cjs +250 -0
  231. package/dist/terminals-Dlg0x3nY.mjs +235 -0
  232. package/dist/time-D0XyHOid.mjs +38 -0
  233. package/dist/time-DCuTyook.cjs +42 -0
  234. package/dist/titleGenerator-CQ8CoGhg.mjs +166 -0
  235. package/dist/titleGenerator-ChzMJsyg.cjs +172 -0
  236. package/dist/tmuxSocket-B8Yx20sT.mjs +19 -0
  237. package/dist/tmuxSocket-D-Xt6Vn-.cjs +24 -0
  238. package/dist/{todo-BYMg0fR0.cjs → todo-BvUOTa8G.cjs} +15 -12
  239. package/dist/{todo-BbusB1zM.mjs → todo-CZ6cFrrn.mjs} +11 -8
  240. package/dist/tools-BZtHF_qQ.mjs +177 -0
  241. package/dist/tools-Be3AoAwu.cjs +63 -0
  242. package/dist/tools-DQj0AeLk.cjs +180 -0
  243. package/dist/tools-DkluDcG4.mjs +60 -0
  244. package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
  245. package/dist/trimIdent-BAjmv_TG.mjs +20 -0
  246. package/dist/types-CRS-srZK.cjs +66 -0
  247. package/dist/types-CkqAACXx.mjs +64 -0
  248. package/dist/uninstall-BkLzaJ6I.mjs +47 -0
  249. package/dist/uninstall-x-TUtf5H.cjs +49 -0
  250. package/package.json +7 -7
  251. package/dist/agentGuidance-CrR9l62U.mjs +0 -2145
  252. package/dist/agentGuidance-DwOWbX55.cjs +0 -2217
  253. package/dist/index-BG932eCh.cjs +0 -19655
  254. package/dist/index-CVCt3kSt.mjs +0 -19591
  255. package/dist/index-D5ScEErM.cjs +0 -58
  256. package/dist/index-DgO0FHOx.mjs +0 -52
  257. package/dist/limits-D2uN090G.mjs +0 -2528
  258. package/dist/limits-kqFqhfsh.cjs +0 -2652
@@ -0,0 +1,760 @@
1
+ import axios from 'axios';
2
+ import { c as configuration } from './configuration-RfeNjFmd.mjs';
3
+ import { e as encodeBase64, g as encrypt, d as decodeBase64, h as decrypt } from './authRelay-DSAvg1uv.mjs';
4
+ import { l as listDaemonSessions, s as stopDaemonSession } from './controlClient-BGbDcgWS.mjs';
5
+ import { readCredentialsForConfiguredRelay, readPersistedSessions } from './persistence-HGW_ZVs9.mjs';
6
+ import { randomUUID } from 'node:crypto';
7
+ import { d as delay } from './time-D0XyHOid.mjs';
8
+ import { io } from 'socket.io-client';
9
+ import { b as backgroundTasksReport } from './backgroundTaskActivity-8PdPxrvy.mjs';
10
+
11
+ function sessionWebUrl(sessionId) {
12
+ return `${configuration.webappUrl.replace(/\/+$/, "")}/session/${sessionId}`;
13
+ }
14
+ async function waitForSessionKey(sessionId, timeoutMs) {
15
+ const deadline = Date.now() + timeoutMs;
16
+ while (true) {
17
+ const entry = readPersistedSessions()[sessionId];
18
+ if (entry) return entry;
19
+ if (Date.now() >= deadline) {
20
+ throw new Error(
21
+ `Session ${sessionId} has no encryption key in ${configuration.sessionsFile}. Either it was not spawned by this machine's daemon, or the daemon is too old to persist session keys.`
22
+ );
23
+ }
24
+ await delay(200);
25
+ }
26
+ }
27
+ async function sendUserMessage(sessionId, persisted, text, client, options = {}) {
28
+ const credentials = await readCredentialsForConfiguredRelay();
29
+ if (!credentials) {
30
+ throw new Error("Not authenticated. Run `very-happy auth login` first.");
31
+ }
32
+ const envelope = {
33
+ role: "user",
34
+ content: {
35
+ type: "text",
36
+ text
37
+ },
38
+ meta: {
39
+ sentFrom: options.sentFrom ?? "cli",
40
+ ...options.model !== void 0 ? { model: options.model } : {},
41
+ ...options.permissionMode !== void 0 ? { permissionMode: options.permissionMode } : {},
42
+ ...options.effort !== void 0 ? { effort: options.effort } : {},
43
+ ...options.delivery !== void 0 ? { delivery: options.delivery } : {}
44
+ }
45
+ };
46
+ const encrypted = encodeBase64(encrypt(
47
+ decodeBase64(persisted.encryptionKey),
48
+ persisted.encryptionVariant,
49
+ envelope
50
+ ));
51
+ await axios.post(
52
+ `${configuration.serverUrl}/v3/sessions/${encodeURIComponent(sessionId)}/messages`,
53
+ { messages: [{ content: encrypted, localId: options.localId ?? randomUUID() }] },
54
+ {
55
+ headers: {
56
+ "Authorization": `Bearer ${credentials.token}`,
57
+ "Content-Type": "application/json",
58
+ "X-Happy-Client": `${client}/${configuration.currentCliVersion}`
59
+ },
60
+ timeout: 3e4
61
+ }
62
+ );
63
+ }
64
+
65
+ const MAX_LINE_CHARS = 500;
66
+ const MAX_ARGS_CHARS = 160;
67
+ const DEFAULT_LIMITS = { line: MAX_LINE_CHARS, args: MAX_ARGS_CHARS };
68
+ const NO_LIMITS = { line: Number.POSITIVE_INFINITY, args: Number.POSITIVE_INFINITY };
69
+ function truncateText(text, max = MAX_LINE_CHARS) {
70
+ const chars = Array.from(text);
71
+ if (chars.length <= max) return text;
72
+ return chars.slice(0, max).join("") + ` \u2026[+${chars.length - max} chars]`;
73
+ }
74
+ function squash(text) {
75
+ return text.replace(/\s+/g, " ").trim();
76
+ }
77
+ function safeJson(value, max) {
78
+ try {
79
+ return truncateText(JSON.stringify(value) ?? "", max);
80
+ } catch {
81
+ return "[unserializable]";
82
+ }
83
+ }
84
+ function formatMessageBody(body, limits = DEFAULT_LIMITS) {
85
+ if (!body || typeof body !== "object") return null;
86
+ const b = body;
87
+ if (b.role === "user" && b.content?.type === "text" && typeof b.content.text === "string") {
88
+ const text = squash(b.content.text);
89
+ return text.length > 0 ? `user: ${truncateText(text, limits.line)}` : null;
90
+ }
91
+ if (b.role === "session" && b.content && typeof b.content === "object") {
92
+ const env = b.content;
93
+ const ev = env.ev;
94
+ if (!ev || typeof ev.t !== "string") return null;
95
+ switch (ev.t) {
96
+ case "text": {
97
+ if (ev.thinking) return null;
98
+ const text = squash(String(ev.text ?? ""));
99
+ if (text.length === 0) return null;
100
+ const role = env.role === "user" ? "user" : "assistant";
101
+ return `${role}: ${truncateText(text, limits.line)}`;
102
+ }
103
+ case "tool-call-start": {
104
+ const name = String(ev.name ?? "tool");
105
+ const title = squash(String(ev.title ?? ""));
106
+ const args = ev.args ? safeJson(ev.args, limits.args) : "";
107
+ const detail = title || args;
108
+ return `[tool] ${name}${detail ? `: ${truncateText(detail, limits.args)}` : ""}`;
109
+ }
110
+ case "service": {
111
+ const text = squash(String(ev.text ?? ""));
112
+ return text.length > 0 ? `[service] ${truncateText(text, limits.args)}` : null;
113
+ }
114
+ case "turn-end": {
115
+ if (ev.status && ev.status !== "completed") return `[turn ${ev.status}]`;
116
+ return null;
117
+ }
118
+ default:
119
+ return null;
120
+ }
121
+ }
122
+ if (b.role === "agent" && b.content?.type === "acp" && b.content.data && typeof b.content.data === "object") {
123
+ const d = b.content.data;
124
+ switch (d.type) {
125
+ case "message": {
126
+ const text = squash(String(d.message ?? ""));
127
+ return text.length > 0 ? `assistant: ${truncateText(text, limits.line)}` : null;
128
+ }
129
+ case "tool-call":
130
+ return `[tool] ${String(d.name ?? "tool")}: ${safeJson(d.input, limits.args)}`;
131
+ case "permission-request":
132
+ return `[permission] ${String(d.toolName ?? "")}: ${truncateText(squash(String(d.description ?? "")), limits.args)}`;
133
+ default:
134
+ return null;
135
+ }
136
+ }
137
+ if (b.role === "agent" && b.content?.type === "output") {
138
+ const msg = b.content.data?.message;
139
+ if (msg && Array.isArray(msg.content)) {
140
+ const texts = msg.content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => squash(c.text)).filter((t) => t.length > 0);
141
+ if (texts.length > 0) {
142
+ const role = msg.role === "user" ? "user" : "assistant";
143
+ return `${role}: ${truncateText(texts.join(" "), limits.line)}`;
144
+ }
145
+ }
146
+ return null;
147
+ }
148
+ return null;
149
+ }
150
+ function formatTranscript(bodies, options = {}) {
151
+ const limits = options.full ? NO_LIMITS : DEFAULT_LIMITS;
152
+ const lines = [];
153
+ for (const body of bodies) {
154
+ const line = formatMessageBody(body, limits);
155
+ if (line !== null) lines.push(line);
156
+ }
157
+ return lines.join("\n");
158
+ }
159
+
160
+ function asRecord(value) {
161
+ return value && typeof value === "object" ? value : null;
162
+ }
163
+ function sessionEnvelope(body) {
164
+ const b = asRecord(body);
165
+ if (!b || b.role !== "session") return null;
166
+ const env = asRecord(b.content);
167
+ if (!env) return null;
168
+ const ev = asRecord(env.ev);
169
+ return ev && typeof ev.t === "string" ? { role: env.role, ev } : null;
170
+ }
171
+ function isUserPrompt(body) {
172
+ const b = asRecord(body);
173
+ return !!b && b.role === "user" && b.content?.type === "text" && typeof b.content.text === "string";
174
+ }
175
+ function agentPiece(body) {
176
+ const env = sessionEnvelope(body);
177
+ if (env && env.role !== "user") {
178
+ if (env.ev.t === "text" && !env.ev.thinking) {
179
+ const text = String(env.ev.text ?? "");
180
+ return text.trim().length > 0 ? { kind: "text", text } : null;
181
+ }
182
+ if (env.ev.t === "tool-call-start") return { kind: "tool" };
183
+ return null;
184
+ }
185
+ const b = asRecord(body);
186
+ if (b?.role === "agent" && b.content?.type === "acp") {
187
+ const d = asRecord(b.content.data);
188
+ if (d?.type === "message") {
189
+ const text = String(d.message ?? "");
190
+ return text.trim().length > 0 ? { kind: "text", text } : null;
191
+ }
192
+ if (d?.type === "tool-call") return { kind: "tool" };
193
+ }
194
+ return null;
195
+ }
196
+ function userPromptText(body) {
197
+ return isUserPrompt(body) ? body.content.text : null;
198
+ }
199
+ function analyzeLatestTurn(entries, options = {}) {
200
+ const lastSeq = entries.length > 0 ? entries[entries.length - 1].seq : 0;
201
+ let userIndex = -1;
202
+ for (let i = entries.length - 1; i >= 0; i--) {
203
+ if (isUserPrompt(entries[i].body) && (!options.anchor || options.anchor(entries[i].body))) {
204
+ userIndex = i;
205
+ break;
206
+ }
207
+ }
208
+ let ended = false;
209
+ let status = null;
210
+ let error = null;
211
+ let texts = [];
212
+ let lastMarker = null;
213
+ let startedAfterPrompt = false;
214
+ const emitsStarts = entries.some((entry) => sessionEnvelope(entry.body)?.ev?.t === "turn-start");
215
+ for (let i = userIndex + 1; i < entries.length; i++) {
216
+ const body = entries[i].body;
217
+ if (options.anchor && isUserPrompt(body)) {
218
+ if (ended) break;
219
+ continue;
220
+ }
221
+ const env = sessionEnvelope(body);
222
+ if (env?.ev?.t === "turn-start") {
223
+ lastMarker = "start";
224
+ startedAfterPrompt = true;
225
+ ended = false;
226
+ status = null;
227
+ error = null;
228
+ texts = [];
229
+ continue;
230
+ }
231
+ if (env?.ev?.t === "turn-end") {
232
+ lastMarker = "end";
233
+ if (userIndex >= 0 && emitsStarts && !startedAfterPrompt) continue;
234
+ ended = true;
235
+ status = typeof env.ev.status === "string" ? env.ev.status : null;
236
+ error = typeof env.ev.error === "string" ? env.ev.error : null;
237
+ continue;
238
+ }
239
+ const piece = agentPiece(body);
240
+ if (!piece) continue;
241
+ if (piece.kind === "tool") texts = [];
242
+ else texts.push(piece.text);
243
+ }
244
+ if (userIndex < 0) ended = lastMarker === "end";
245
+ return {
246
+ userSeq: userIndex >= 0 ? entries[userIndex].seq : null,
247
+ ended,
248
+ status: ended ? status : null,
249
+ error: ended ? error : null,
250
+ answer: texts.join("\n\n").trim(),
251
+ lastSeq
252
+ };
253
+ }
254
+
255
+ const USER_SOCKET_CONNECT_TIMEOUT_MS = 15e3;
256
+ async function openUserScopedSocket(token, options = {}) {
257
+ const socket = io(configuration.serverUrl, {
258
+ auth: {
259
+ token,
260
+ clientType: "user-scoped",
261
+ happyClient: `cli-session-ops/${configuration.currentCliVersion}`
262
+ },
263
+ path: "/v1/updates",
264
+ transports: ["websocket"],
265
+ reconnection: false,
266
+ autoConnect: false
267
+ });
268
+ await new Promise((resolve, reject) => {
269
+ const timer = setTimeout(() => {
270
+ socket.close();
271
+ reject(new Error(`Timed out connecting to ${configuration.serverUrl}`));
272
+ }, options.connectTimeoutMs ?? USER_SOCKET_CONNECT_TIMEOUT_MS);
273
+ socket.once("connect", () => {
274
+ clearTimeout(timer);
275
+ resolve();
276
+ });
277
+ socket.once("connect_error", (error) => {
278
+ clearTimeout(timer);
279
+ socket.close();
280
+ reject(new Error(`Could not connect to ${configuration.serverUrl}: ${error.message}`));
281
+ });
282
+ socket.connect();
283
+ });
284
+ return {
285
+ async rpcCall(payload, timeoutMs) {
286
+ const ack = await socket.timeout(timeoutMs).emitWithAck("rpc-call", payload);
287
+ return ack;
288
+ },
289
+ close() {
290
+ socket.close();
291
+ }
292
+ };
293
+ }
294
+
295
+ const RPC_RATE_LIMIT_ERROR = "RPC rate limit reached";
296
+ const RPC_ACCOUNT_RATE_LIMIT_ERROR = "RPC account rate limit reached";
297
+ const RPC_RATE_RETRY_MIN_DELAY_MS = 2e3;
298
+ const RPC_RATE_RETRY_MAX_DELAY_MS = 6e4;
299
+ const RPC_RATE_MAX_RETRIES = 2;
300
+ function isRpcRateLimitAck(ack) {
301
+ if (!ack || typeof ack !== "object") return false;
302
+ const a = ack;
303
+ if (a.ok !== false) return false;
304
+ if (a.code === "rpc_rate_limited" || a.code === "rpc_account_rate_limited") return true;
305
+ return a.error === RPC_RATE_LIMIT_ERROR || a.error === RPC_ACCOUNT_RATE_LIMIT_ERROR;
306
+ }
307
+ function rpcRateRetryDelayMs(attempt, retryAfterMs, random = Math.random()) {
308
+ if (typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs > 0) {
309
+ return Math.min(RPC_RATE_RETRY_MAX_DELAY_MS, Math.round(retryAfterMs * (1 + random * 0.5)));
310
+ }
311
+ const exponent = Math.max(0, Math.min(attempt, 32) - 1);
312
+ const ceiling = Math.min(RPC_RATE_RETRY_MIN_DELAY_MS * 2 ** exponent, RPC_RATE_RETRY_MAX_DELAY_MS);
313
+ return Math.round(RPC_RATE_RETRY_MIN_DELAY_MS + random * (ceiling - RPC_RATE_RETRY_MIN_DELAY_MS));
314
+ }
315
+
316
+ const PERMISSION_OPS_CLIENT = "session-ops";
317
+ const PERMISSION_RPC_TIMEOUT_MS = 3e4;
318
+ const SETTLE_TIMEOUT_MS = 5e3;
319
+ const SETTLE_POLL_MS = 500;
320
+ function buildPermissionRpcPayload(requestId, verdict) {
321
+ if (verdict.kind === "approve") {
322
+ return {
323
+ id: requestId,
324
+ approved: true,
325
+ decision: verdict.forSession ? "approved_for_session" : "approved"
326
+ };
327
+ }
328
+ return {
329
+ id: requestId,
330
+ approved: false,
331
+ decision: "denied",
332
+ ...verdict.reason ? { reason: verdict.reason } : {}
333
+ };
334
+ }
335
+ function interpretPermissionAck(ack, decryptResult) {
336
+ if (!ack.ok) {
337
+ const message = ack.error ?? "RPC call failed";
338
+ if (message === "RPC method not available") {
339
+ return { status: "offline", message: "no running wrapper has registered the permission RPC for this session" };
340
+ }
341
+ if (message === "RPC target disconnected") {
342
+ return { status: "offline", message: "the wrapper disconnected while the permission RPC was in flight" };
343
+ }
344
+ if (/timed? ?out/i.test(message)) {
345
+ return { status: "timeout", message };
346
+ }
347
+ if (isRpcRateLimitAck(ack)) {
348
+ return { status: "rate-limited", message, retryAfterMs: rpcRateRetryDelayMs(1, ack.retryAfterMs, 0) };
349
+ }
350
+ return { status: "rejected", message };
351
+ }
352
+ let body = null;
353
+ if (typeof ack.result === "string" && ack.result.length > 0) {
354
+ try {
355
+ body = decryptResult(ack.result);
356
+ } catch {
357
+ body = null;
358
+ }
359
+ }
360
+ if (body && typeof body === "object" && typeof body.error === "string") {
361
+ return { status: "handler-error", message: body.error };
362
+ }
363
+ return { status: "acknowledged" };
364
+ }
365
+ function pendingRequestsOf(agentState, now) {
366
+ const requests = agentState?.requests;
367
+ if (!requests || typeof requests !== "object") return [];
368
+ return Object.entries(requests).map(([id, request]) => ({
369
+ id,
370
+ tool: typeof request?.tool === "string" ? request.tool : "unknown",
371
+ ...request?.kind ? { kind: request.kind } : {},
372
+ ...typeof request?.createdAt === "number" ? { createdAt: request.createdAt, waitingMs: Math.max(0, now - request.createdAt) } : {}
373
+ })).sort((a, b) => (a.createdAt ?? Number.MAX_SAFE_INTEGER) - (b.createdAt ?? Number.MAX_SAFE_INTEGER));
374
+ }
375
+ const RPC_DEADLINE_GRACE_MS = 2e3;
376
+ function withDeadline(promise, ms, message) {
377
+ return new Promise((resolve, reject) => {
378
+ const timer = setTimeout(() => reject(new Error(`${message} (timed out)`)), ms);
379
+ promise.then(
380
+ (value) => {
381
+ clearTimeout(timer);
382
+ resolve(value);
383
+ },
384
+ (error) => {
385
+ clearTimeout(timer);
386
+ reject(error);
387
+ }
388
+ );
389
+ });
390
+ }
391
+ async function resolvePermissionRequest(sessionId, requestId, verdict, deps = {}) {
392
+ const now = deps.now ?? Date.now;
393
+ const persisted = (deps.readPersisted ?? readPersistedSessions)()[sessionId];
394
+ if (!persisted) {
395
+ throw new Error(
396
+ `No local key for session ${sessionId} \u2014 approve/deny needs the session key from ~/.happy/sessions.json (the RPC payload is encrypted with it), so only sessions this machine's daemon spawned within 14 days can be answered from here.`
397
+ );
398
+ }
399
+ const token = await (deps.bearerToken ?? bearerToken$1)();
400
+ const fetchState = deps.fetchAgentState ?? fetchDecryptedAgentState;
401
+ const before = pendingRequestsOf(await fetchState(sessionId, persisted, token), now());
402
+ if (!before.some((request) => request.id === requestId)) {
403
+ const hint = before.length > 0 ? `Pending request ids: ${before.map((request) => `${request.id} (${request.tool})`).join(", ")}` : "The session has no pending requests.";
404
+ throw new Error(`Request ${requestId} is not pending on session ${sessionId}. ${hint}`);
405
+ }
406
+ const payload = buildPermissionRpcPayload(requestId, verdict);
407
+ const key = decodeBase64(persisted.encryptionKey);
408
+ const params = encodeBase64(encrypt(key, persisted.encryptionVariant, payload));
409
+ const transport = await (deps.openTransport ?? openUserScopedSocket)(token);
410
+ const rpcTimeoutMs = deps.rpcTimeoutMs ?? PERMISSION_RPC_TIMEOUT_MS;
411
+ const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
412
+ let ack;
413
+ try {
414
+ for (let attempt = 1; ; attempt++) {
415
+ try {
416
+ ack = await withDeadline(
417
+ transport.rpcCall({ method: `${sessionId}:permission`, params }, rpcTimeoutMs),
418
+ rpcTimeoutMs + RPC_DEADLINE_GRACE_MS,
419
+ `permission RPC did not settle within ${rpcTimeoutMs}ms`
420
+ );
421
+ } catch (error) {
422
+ ack = { ok: false, error: error instanceof Error ? error.message : "RPC call failed" };
423
+ }
424
+ if (!isRpcRateLimitAck(ack) || attempt > RPC_RATE_MAX_RETRIES) break;
425
+ const waitMs = rpcRateRetryDelayMs(attempt, ack.retryAfterMs, (deps.random ?? Math.random)());
426
+ deps.onRateLimited?.({ attempt, waitMs, serverError: ack.error ?? "RPC rate limit reached" });
427
+ await sleep(waitMs);
428
+ }
429
+ } finally {
430
+ transport.close();
431
+ }
432
+ const outcome = interpretPermissionAck(ack, (encrypted) => decrypt(key, persisted.encryptionVariant, decodeBase64(encrypted)));
433
+ if (outcome.status !== "acknowledged") {
434
+ return { sessionId, requestId, payload, outcome };
435
+ }
436
+ const deadline = now() + (deps.settleTimeoutMs ?? SETTLE_TIMEOUT_MS);
437
+ let settled = false;
438
+ for (; ; ) {
439
+ const pending = pendingRequestsOf(await fetchState(sessionId, persisted, token), now());
440
+ if (!pending.some((request) => request.id === requestId)) {
441
+ settled = true;
442
+ break;
443
+ }
444
+ if (now() >= deadline) break;
445
+ await sleep(SETTLE_POLL_MS);
446
+ }
447
+ return { sessionId, requestId, payload, outcome, settled };
448
+ }
449
+ async function bearerToken$1() {
450
+ const credentials = await readCredentialsForConfiguredRelay();
451
+ if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
452
+ return credentials.token;
453
+ }
454
+ async function fetchDecryptedAgentState(sessionId, persisted, token) {
455
+ const response = await axios.get(
456
+ `${configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,
457
+ {
458
+ headers: {
459
+ "Authorization": `Bearer ${token}`,
460
+ "X-Happy-Client": `${PERMISSION_OPS_CLIENT}/${configuration.currentCliVersion}`
461
+ },
462
+ timeout: 15e3,
463
+ // 404 is a real answer ("not yours / gone"), not a transport fault.
464
+ validateStatus: (status) => status >= 200 && status < 300 || status === 404
465
+ }
466
+ );
467
+ if (response.status === 404) {
468
+ throw new Error(`Session ${sessionId} was not found on this account (it may have been deleted, or the local key belongs to a session on another relay).`);
469
+ }
470
+ const raw = response.data?.session?.agentState;
471
+ if (typeof raw !== "string" || raw.length === 0) return null;
472
+ return decrypt(decodeBase64(persisted.encryptionKey), persisted.encryptionVariant, decodeBase64(raw));
473
+ }
474
+
475
+ const SESSION_OPS_CLIENT = "session-ops";
476
+ const DEFAULT_RECENT_LIMIT = 15;
477
+ const MAX_READ_LIMIT = 100;
478
+ function backgroundTasksOf(child) {
479
+ const raw = child.backgroundTasks;
480
+ if (!raw || typeof raw !== "object") return void 0;
481
+ const r = raw;
482
+ if (typeof r.count !== "number" || !Array.isArray(r.tasks)) return void 0;
483
+ return {
484
+ count: r.count,
485
+ tasks: r.tasks.filter((task) => !!task && typeof task === "object" && typeof task.id === "string"),
486
+ ...typeof r.reportedAt === "number" ? { reportedAt: r.reportedAt } : {},
487
+ ...r.stale === true ? { stale: true } : {}
488
+ };
489
+ }
490
+ function mergeSessionSummaries(live, persisted, options = {}) {
491
+ const recentLimit = Math.max(0, options.recentLimit ?? DEFAULT_RECENT_LIMIT);
492
+ const seen = /* @__PURE__ */ new Set();
493
+ const summaries = [];
494
+ for (const child of live) {
495
+ const id = typeof child.happySessionId === "string" ? child.happySessionId : void 0;
496
+ if (!id || seen.has(id)) continue;
497
+ seen.add(id);
498
+ summaries.push(toSummary(id, persisted[id], livenessOf(child)));
499
+ }
500
+ const rest = Object.entries(persisted).filter(([id]) => !seen.has(id)).filter(([, entry]) => entry.metadata?.flavor !== "terminal-mirror").sort((a, b) => b[1].savedAt - a[1].savedAt).slice(0, recentLimit);
501
+ for (const [id, entry] of rest) {
502
+ summaries.push(toSummary(id, entry, { live: false }));
503
+ }
504
+ if (!options.tag) return summaries;
505
+ const wanted = options.tag;
506
+ return summaries.filter((summary) => summary.tags?.includes(wanted) === true);
507
+ }
508
+ function livenessOf(child) {
509
+ const backgroundTasks = backgroundTasksOf(child);
510
+ return { live: true, pid: typeof child.pid === "number" ? child.pid : void 0, ...backgroundTasks ? { backgroundTasks } : {} };
511
+ }
512
+ function sessionLiveness(live, sessionId) {
513
+ const child = live.find((entry) => entry.happySessionId === sessionId);
514
+ return child ? livenessOf(child) : { live: false };
515
+ }
516
+ function toSummary(id, persisted, extra) {
517
+ const meta = persisted?.metadata;
518
+ return {
519
+ id,
520
+ live: extra.live,
521
+ ...extra.pid !== void 0 ? { pid: extra.pid } : {},
522
+ ...extra.backgroundTasks ? { backgroundTasks: extra.backgroundTasks } : {},
523
+ ...meta?.summary?.text ? { title: meta.summary.text } : {},
524
+ ...meta?.path ? { cwd: meta.path } : {},
525
+ ...meta?.flavor ? { flavor: meta.flavor } : {},
526
+ ...meta?.tags?.length ? { tags: [...meta.tags] } : {},
527
+ ...meta?.variant ? { variant: meta.variant } : {},
528
+ url: sessionWebUrl(id),
529
+ ...persisted?.savedAt !== void 0 ? { savedAt: persisted.savedAt } : {}
530
+ };
531
+ }
532
+ async function listSessions(options = {}) {
533
+ const live = await listDaemonSessions();
534
+ return mergeSessionSummaries(live, readPersistedSessions(), options);
535
+ }
536
+ function summarizeAccountSession(row, persisted, liveIds, now) {
537
+ const base = {
538
+ id: row.id,
539
+ live: liveIds.has(row.id),
540
+ url: sessionWebUrl(row.id),
541
+ decryptable: false,
542
+ readable: false,
543
+ active: row.active === true,
544
+ archived: typeof row.archivedAt === "number",
545
+ ...typeof row.activeAt === "number" ? { activeAt: row.activeAt } : {},
546
+ ...typeof row.updatedAt === "number" ? { updatedAt: row.updatedAt } : {},
547
+ attention: false
548
+ };
549
+ if (!persisted) return base;
550
+ const key = decodeBase64(persisted.encryptionKey);
551
+ let metadata = null;
552
+ let agentState = null;
553
+ try {
554
+ metadata = row.metadata ? decrypt(key, persisted.encryptionVariant, decodeBase64(row.metadata)) : null;
555
+ agentState = row.agentState ? decrypt(key, persisted.encryptionVariant, decodeBase64(row.agentState)) : null;
556
+ } catch {
557
+ return base;
558
+ }
559
+ if (!metadata) return base;
560
+ const meta = metadata;
561
+ const pending = pendingRequestsOf(agentState, now);
562
+ const backgroundTasks = backgroundTasksReport(agentState?.backgroundTasks, now, { active: row.active === true });
563
+ return {
564
+ ...base,
565
+ decryptable: true,
566
+ readable: true,
567
+ backgroundTasks,
568
+ ...meta.summary?.text ? { title: meta.summary.text } : {},
569
+ ...meta.path ? { cwd: meta.path } : {},
570
+ ...meta.flavor ? { flavor: meta.flavor } : {},
571
+ ...meta.tags?.length ? { tags: [...meta.tags] } : {},
572
+ ...meta.variant ? { variant: meta.variant } : {},
573
+ ...meta.machineId ? { machineId: meta.machineId } : {},
574
+ ...persisted.savedAt !== void 0 ? { savedAt: persisted.savedAt } : {},
575
+ pending,
576
+ attention: pending.length > 0
577
+ };
578
+ }
579
+ function orderAccountSessions(summaries, options = {}) {
580
+ const oldestWait = (summary) => Math.max(0, ...(summary.pending ?? []).map((request) => request.waitingMs ?? 0));
581
+ const rank = (summary) => summary.attention ? 0 : summary.live ? 1 : 2;
582
+ let rows = summaries.filter((summary) => summary.flavor !== "terminal-mirror").slice().sort((a, b) => {
583
+ const byRank = rank(a) - rank(b);
584
+ if (byRank !== 0) return byRank;
585
+ if (a.attention && b.attention) return oldestWait(b) - oldestWait(a);
586
+ return (b.updatedAt ?? 0) - (a.updatedAt ?? 0);
587
+ });
588
+ if (options.tag) {
589
+ const wanted = options.tag;
590
+ rows = rows.filter((summary) => summary.tags?.includes(wanted) === true);
591
+ }
592
+ const recentLimit = Math.max(0, options.recentLimit ?? DEFAULT_RECENT_LIMIT);
593
+ let idleKept = 0;
594
+ return rows.filter((summary) => rank(summary) < 2 || idleKept++ < recentLimit);
595
+ }
596
+ async function listAccountSessions(options = {}) {
597
+ const token = await bearerToken();
598
+ const response = await axios.get(`${configuration.serverUrl}/v1/sessions`, {
599
+ headers: {
600
+ "Authorization": `Bearer ${token}`,
601
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
602
+ },
603
+ timeout: 15e3
604
+ });
605
+ const rows = Array.isArray(response.data?.sessions) ? response.data.sessions : [];
606
+ const persisted = readPersistedSessions();
607
+ const live = await listDaemonSessions();
608
+ const liveIds = new Set(live.map((child) => child.happySessionId).filter((id) => typeof id === "string"));
609
+ const now = Date.now();
610
+ const summaries = rows.filter((row) => typeof row?.id === "string").map((row) => summarizeAccountSession(row, persisted[row.id], liveIds, now)).filter((summary) => options.includeArchived || !summary.archived);
611
+ return orderAccountSessions(summaries, options);
612
+ }
613
+ async function bearerToken() {
614
+ const credentials = await readCredentialsForConfiguredRelay();
615
+ if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
616
+ return credentials.token;
617
+ }
618
+ function requirePersisted(sessionId) {
619
+ const persisted = readPersistedSessions()[sessionId];
620
+ if (!persisted) {
621
+ throw new Error(
622
+ `No local key for session ${sessionId} \u2014 it was not spawned by this machine's daemon (or is older than 14 days).`
623
+ );
624
+ }
625
+ return persisted;
626
+ }
627
+ async function readSessionLog(sessionId, persisted, limit) {
628
+ const token = await bearerToken();
629
+ const response = await axios.get(
630
+ `${configuration.serverUrl}/v3/sessions/${encodeURIComponent(sessionId)}/messages`,
631
+ {
632
+ params: { before_seq: 2147483647, limit },
633
+ headers: {
634
+ "Authorization": `Bearer ${token}`,
635
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
636
+ },
637
+ timeout: 15e3
638
+ }
639
+ );
640
+ const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
641
+ messages.reverse();
642
+ const key = decodeBase64(persisted.encryptionKey);
643
+ return messages.map((message) => {
644
+ if (message.content?.t !== "encrypted") return { seq: message.seq, body: null };
645
+ try {
646
+ return { seq: message.seq, body: decrypt(key, persisted.encryptionVariant, decodeBase64(message.content.c)) };
647
+ } catch {
648
+ return { seq: message.seq, body: null };
649
+ }
650
+ });
651
+ }
652
+ const TURN_WINDOW = 500;
653
+ async function readSessionTranscript(sessionId, limit, options = {}) {
654
+ const persisted = requirePersisted(sessionId);
655
+ const bounded = Math.max(1, Math.min(MAX_READ_LIMIT, Math.floor(limit)));
656
+ const entries = await readSessionLog(sessionId, persisted, Math.max(bounded, TURN_WINDOW));
657
+ const shown = entries.slice(-bounded);
658
+ const live = await listDaemonSessions();
659
+ return {
660
+ summary: toSummary(sessionId, persisted, sessionLiveness(live, sessionId)),
661
+ messageCount: shown.length,
662
+ transcript: formatTranscript(shown.map((entry) => entry.body), { full: options.full }),
663
+ turn: analyzeLatestTurn(entries)
664
+ };
665
+ }
666
+ async function readSessionMetadata(sessionId, persisted) {
667
+ const token = await bearerToken();
668
+ const response = await axios.get(`${configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}`, {
669
+ headers: {
670
+ "Authorization": `Bearer ${token}`,
671
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
672
+ },
673
+ timeout: 15e3
674
+ });
675
+ const ciphertext = response.data?.session?.metadata;
676
+ if (typeof ciphertext !== "string" || ciphertext.length === 0) return null;
677
+ return decrypt(decodeBase64(persisted.encryptionKey), persisted.encryptionVariant, decodeBase64(ciphertext));
678
+ }
679
+ async function readSessionState(sessionId, persisted) {
680
+ const token = await bearerToken();
681
+ const response = await axios.get(`${configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}`, {
682
+ headers: {
683
+ "Authorization": `Bearer ${token}`,
684
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
685
+ },
686
+ timeout: 15e3,
687
+ validateStatus: (status) => status >= 200 && status < 300 || status === 404
688
+ });
689
+ if (response.status === 404) return { found: false, active: false };
690
+ const row = response.data?.session;
691
+ if (!row || typeof row.id !== "string") throw new Error(`Server returned no session row for ${sessionId}`);
692
+ const state = {
693
+ found: true,
694
+ active: row.active === true,
695
+ ...typeof row.activeAt === "number" ? { activeAt: row.activeAt } : {},
696
+ archivedAt: typeof row.archivedAt === "number" ? row.archivedAt : null,
697
+ ...typeof row.updatedAt === "number" ? { updatedAt: row.updatedAt } : {}
698
+ };
699
+ if (persisted && typeof row.metadata === "string" && row.metadata.length > 0) {
700
+ try {
701
+ const metadata = decrypt(decodeBase64(persisted.encryptionKey), persisted.encryptionVariant, decodeBase64(row.metadata));
702
+ if (metadata?.machineId) state.machineId = metadata.machineId;
703
+ } catch {
704
+ }
705
+ }
706
+ return state;
707
+ }
708
+ class TurnWaitTimeoutError extends Error {
709
+ constructor(sessionId, timeoutMs, turn) {
710
+ super(`Session ${sessionId}: the latest turn did not end within ${Math.round(timeoutMs / 1e3)}s`);
711
+ this.sessionId = sessionId;
712
+ this.timeoutMs = timeoutMs;
713
+ this.turn = turn;
714
+ }
715
+ }
716
+ async function waitForTurnEnd(sessionId, options) {
717
+ const persisted = requirePersisted(sessionId);
718
+ const pollMs = options.pollMs ?? 3e3;
719
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
720
+ const deadline = Date.now() + options.timeoutMs;
721
+ while (true) {
722
+ const turn = analyzeLatestTurn(await readSessionLog(sessionId, persisted, TURN_WINDOW));
723
+ if (turn.ended) return turn;
724
+ if (Date.now() + pollMs > deadline) throw new TurnWaitTimeoutError(sessionId, options.timeoutMs, turn);
725
+ await sleep(pollMs);
726
+ }
727
+ }
728
+ async function stopSession(sessionId) {
729
+ return await stopDaemonSession(sessionId);
730
+ }
731
+ async function archiveSession(sessionId) {
732
+ const token = await bearerToken();
733
+ await axios.post(
734
+ `${configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}/archive`,
735
+ {},
736
+ {
737
+ headers: {
738
+ "Authorization": `Bearer ${token}`,
739
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
740
+ },
741
+ timeout: 1e4
742
+ }
743
+ );
744
+ }
745
+ async function unarchiveSession(sessionId) {
746
+ const token = await bearerToken();
747
+ await axios.post(
748
+ `${configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}/unarchive`,
749
+ {},
750
+ {
751
+ headers: {
752
+ "Authorization": `Bearer ${token}`,
753
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
754
+ },
755
+ timeout: 1e4
756
+ }
757
+ );
758
+ }
759
+
760
+ export { DEFAULT_RECENT_LIMIT as D, MAX_READ_LIMIT as M, TurnWaitTimeoutError as T, listSessions as a, stopSession as b, resolvePermissionRequest as c, archiveSession as d, readSessionMetadata as e, waitForSessionKey as f, sendUserMessage as g, sessionLiveness as h, readSessionState as i, analyzeLatestTurn as j, readSessionLog as k, listAccountSessions as l, TURN_WINDOW as m, userPromptText as n, openUserScopedSocket as o, readSessionTranscript as r, sessionWebUrl as s, unarchiveSession as u, waitForTurnEnd as w };