very-happy-cli 0.2.181 → 0.2.183
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-DZGCSuXF.mjs → AcpBackend-9c-Jeaxd.mjs} +3 -3
- package/dist/{AcpBackend-CgHb_ya5.cjs → AcpBackend-B-aMuEzO.cjs} +3 -3
- package/dist/{AcpSessionManager-CacC2EVn.cjs → AcpSessionManager-BxXe24AV.cjs} +10 -3
- package/dist/{AcpSessionManager-DwuPzWBh.mjs → AcpSessionManager-CS0FOSQk.mjs} +10 -3
- package/dist/{BasePermissionHandler-Uo-GUjbb.mjs → BasePermissionHandler-BJ1ID_Br.mjs} +1 -1
- package/dist/{BasePermissionHandler-DCP8ZEB4.cjs → BasePermissionHandler-BREjppjP.cjs} +1 -1
- package/dist/{acpSessionResume-9OLTD6kT.mjs → acpSessionResume-BKtI9Jq_.mjs} +1 -1
- package/dist/{acpSessionResume-BaVMD80f.cjs → acpSessionResume-mVoJ75vI.cjs} +1 -1
- package/dist/{agentHome-B1BsKeqD.mjs → agentHome-B8Ctr_Nu.mjs} +4 -4
- package/dist/{agentHome-BtUmGcPX.cjs → agentHome-rzjm56o4.cjs} +4 -4
- package/dist/{api-Dr3wXL_7.mjs → api-C29WLYkW.mjs} +79 -12
- package/dist/{api-COHFAkb_.cjs → api-xZiqVynF.cjs} +81 -11
- package/dist/{apiMachine-D8H7CXaz.cjs → apiMachine-B-FNBIYG.cjs} +19 -14
- package/dist/{apiMachine-CHEzPQdS.mjs → apiMachine-Dz6Yfk0m.mjs} +19 -14
- package/dist/{attachmentContent-mIhbtRKl.cjs → attachmentContent-COV4LF8k.cjs} +2 -2
- package/dist/{attachmentContent-1YwtKgWl.mjs → attachmentContent-DwY0uIg6.mjs} +2 -2
- package/dist/{auth-CgQP7d0q.cjs → auth-CYtk9_ga.cjs} +5 -5
- package/dist/{auth-Fg8fLadK.cjs → auth-D_qsnVr3.cjs} +8 -8
- package/dist/{auth-CbZkcMAb.mjs → auth-DvbSDalm.mjs} +8 -8
- package/dist/{auth-DZsthtSk.mjs → auth-xT70IYMx.mjs} +5 -5
- package/dist/{auto-BB7kqSh5.cjs → auto-DT_PfpCx.cjs} +32 -25
- package/dist/{auto-_T-Ud-z5.mjs → auto-IiPqvIsN.mjs} +32 -26
- package/dist/{bootstrap-CYtOtbi7.cjs → bootstrap-C50NkfPx.cjs} +2 -2
- package/dist/{bootstrap-B6EkZh4u.mjs → bootstrap-DS8Acdhm.mjs} +2 -2
- package/dist/{browser-C2qhUHSE.mjs → browser-1j9gdZoD.mjs} +1 -1
- package/dist/{browser-DjNNQQk2.cjs → browser-CxKZLd2T.cjs} +1 -1
- package/dist/{claudeLocal-O_w7-fBh.cjs → claudeLocal-CgNWpepB.cjs} +5 -5
- package/dist/{claudeLocal-BAFjWhZ8.mjs → claudeLocal-U1Fa9sJo.mjs} +5 -5
- package/dist/{claudeLocalLauncher-CWn30LZR.mjs → claudeLocalLauncher-BjCsfZUE.mjs} +10 -10
- package/dist/{claudeLocalLauncher-Df3zg1q0.cjs → claudeLocalLauncher-IyFm0D6a.cjs} +10 -10
- package/dist/{claudeSessionFork-2ZkSTOGn.mjs → claudeSessionFork-6-1vKISn.mjs} +1 -1
- package/dist/{claudeSessionFork-pwcKqB-e.cjs → claudeSessionFork-BUO1XdSm.cjs} +1 -1
- package/dist/{client-CUO3ItoB.cjs → client--vZueZYF.cjs} +14 -4
- package/dist/{client-BKyU66lg.mjs → client-5_zDNdOh.mjs} +2 -2
- package/dist/{client-CXOnzuOy.mjs → client-BgHvK7lo.mjs} +15 -7
- package/dist/{client-D_U2VjO3.cjs → client-DYGDBD0M.cjs} +15 -7
- package/dist/{client-DWs6k041.cjs → client-DcKJ0ul_.cjs} +2 -2
- package/dist/{client-DsQcxVUV.mjs → client-DfDyE322.mjs} +14 -4
- package/dist/codex/happyMcpStdioBridge.cjs +20 -19
- package/dist/codex/happyMcpStdioBridge.mjs +20 -19
- package/dist/{codexAppServerClient-BRX3JZ_5.cjs → codexAppServerClient-C3nrTfwY.cjs} +3 -3
- package/dist/{codexAppServerClient-C3JO3pwM.mjs → codexAppServerClient-DvBg4hGc.mjs} +3 -3
- package/dist/{codexCommand-BZIAAqKS.mjs → codexCommand-Bqp2--g_.mjs} +96 -61
- package/dist/{codexCommand-dKOc5iQl.cjs → codexCommand-DSiU6MNt.cjs} +96 -61
- package/dist/{codexTermination-BVR9HeVI.cjs → codexTermination-D9TRTglF.cjs} +1 -1
- package/dist/{codexTermination-ztFkwHrG.mjs → codexTermination-DsliWl_s.mjs} +1 -1
- package/dist/{config-cftYxLC7.cjs → config-CRhWiOwi.cjs} +3 -3
- package/dist/{config-NbHjx8ge.mjs → config-DeaqRn3o.mjs} +3 -3
- package/dist/{configuration-C8JgseHy.cjs → configuration-K3I6yOjZ.cjs} +1 -1
- package/dist/{configuration-BPrfb17M.mjs → configuration-elzht62L.mjs} +1 -1
- package/dist/{connect-Br8VMrK1.cjs → connect-B5UbfmO3.cjs} +13 -13
- package/dist/{connect-CLD1ai0k.mjs → connect-CT5cOlnC.mjs} +13 -13
- package/dist/{context-_YGq8xdH.mjs → context-CLj1_RYF.mjs} +1 -1
- package/dist/{context-D1lgQTJC.cjs → context-DNKNF8Yh.cjs} +1 -1
- package/dist/{controlClient-D68Zj7Q-.cjs → controlClient-ChqmxgbB.cjs} +3 -3
- package/dist/{controlClient-CkNpT5zS.mjs → controlClient-CuYQs2aZ.mjs} +3 -3
- package/dist/{createSessionMetadata-3WAj3_xl.mjs → createSessionMetadata-C1MbLQnh.mjs} +2 -2
- package/dist/{createSessionMetadata-DNIbKLHg.cjs → createSessionMetadata-QfiRx4Tf.cjs} +2 -2
- package/dist/daemonSupervisor-57JYYzMf.cjs +268 -0
- package/dist/daemonSupervisor-DfzrtNhM.mjs +245 -0
- package/dist/{defaultPermissionMode-BmvjP32v.cjs → defaultPermissionMode-DQmdZZIg.cjs} +2 -2
- package/dist/{defaultPermissionMode-DXMznl8v.mjs → defaultPermissionMode-U9LuUeQh.mjs} +2 -2
- package/dist/{deterministicJson-BS7D7URE.cjs → deterministicJson-BBRUcB8i.cjs} +569 -17
- package/dist/{deterministicJson-BcBIKKf1.mjs → deterministicJson-BixlyPWV.mjs} +572 -21
- package/dist/{doctor-Be7zC0AF.cjs → doctor-CKgHUV4W.cjs} +7 -7
- package/dist/{doctor-CoOaPq6w.mjs → doctor-Dban2oE2.mjs} +7 -7
- package/dist/{downloadTarget-LyOiucn_.cjs → downloadTarget-CmC11Y7E.cjs} +6 -6
- package/dist/{downloadTarget-CeaAXdJj.mjs → downloadTarget-VUTd9lEO.mjs} +6 -6
- package/dist/{ensureDaemonRunning-B8z37VKD.cjs → ensureDaemonRunning-L0fl0cLE.cjs} +5 -5
- package/dist/{ensureDaemonRunning--98j7-17.mjs → ensureDaemonRunning-khb8Nr1n.mjs} +5 -5
- package/dist/fsMkdir-0K3yENml.cjs +56 -0
- package/dist/fsMkdir-DumgLTzR.mjs +53 -0
- package/dist/{handleResumeCommand-gO8I4keP.mjs → handleResumeCommand-CLDGWpTJ.mjs} +5 -5
- package/dist/{handleResumeCommand-BFZlrFVH.cjs → handleResumeCommand-D9myBN2N.cjs} +5 -5
- package/dist/{index-WUj3954m.mjs → index-BMZx-699.mjs} +4 -4
- package/dist/{index-BRMq9gOe.cjs → index-BdJs0465.cjs} +3 -3
- package/dist/{index-CeRXrGnH.mjs → index-BptfFoXM.mjs} +283 -77
- package/dist/{index-jF2t3ACW.cjs → index-COSjJPIv.cjs} +4 -4
- package/dist/{index-hQ8LP1C2.mjs → index-CtwqBeH6.mjs} +3 -3
- package/dist/{index-B5PXbZjW.cjs → index-DgA79dVw.cjs} +41 -40
- package/dist/{index-Df2eHm3O.cjs → index-Fq-jxjsB.cjs} +294 -76
- package/dist/{index-CO0n9ue6.mjs → index-GkkbUZX-.mjs} +4 -4
- package/dist/{index-DneHoQXf.mjs → index-IRI8s8kz.mjs} +42 -41
- package/dist/{index-BkluinoS.cjs → index-y4JNyz9H.cjs} +4 -4
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{install-D2NCbeJ-.cjs → install-BATFlr_l.cjs} +2 -2
- package/dist/{install-D0inLCv-.mjs → install-F3rP8tBU.mjs} +2 -2
- package/dist/{install-DkYFiVK4.mjs → install-JvCk2PJV.mjs} +3 -3
- package/dist/{install-FqkSUjPC.cjs → install-cn98bTCs.cjs} +3 -3
- package/dist/lib.cjs +12 -12
- package/dist/lib.d.cts +2 -1
- package/dist/lib.d.mts +2 -1
- package/dist/lib.mjs +12 -12
- package/dist/{lidState-x8cJ8J_9.cjs → lidState-Bcs5kRHi.cjs} +1 -1
- package/dist/{lidState-DghbfRbr.mjs → lidState-DXeYTsrm.mjs} +1 -1
- package/dist/{localHappyAgentAuth-IKmeRpuQ.mjs → localHappyAgentAuth-C1A4ChCR.mjs} +1 -1
- package/dist/{localHappyAgentAuth-DZ2kb9uq.cjs → localHappyAgentAuth-CypTSe42.cjs} +1 -1
- package/dist/{logger-DgBwUSgS.cjs → logger-Cgwz30rK.cjs} +2 -2
- package/dist/{logger-LFr8iPgl.mjs → logger-RMPh5aqe.mjs} +2 -2
- package/dist/{machineMetadata-DB03ez4K.cjs → machineMetadata-BSOGMNRi.cjs} +5 -3
- package/dist/{machineMetadata-BglCsfvf.mjs → machineMetadata-BTUtPRth.mjs} +5 -3
- package/dist/{main-YQ-Vkakw.cjs → main-4iiwYpCR.cjs} +61 -56
- package/dist/{main-_1LSzlLv.mjs → main-DNDL6t8z.mjs} +61 -56
- package/dist/{mcp-BIY80jSc.cjs → mcp-C7eEoGhz.cjs} +17 -15
- package/dist/{mcp-Csj-mT5Y.mjs → mcp-DXYDPOpJ.mjs} +17 -15
- package/dist/{mirrorProtocol-CAxOMBkb.mjs → mirrorProtocol-DQRy5PaT.mjs} +3 -3
- package/dist/{mirrorProtocol-BphKIAK9.cjs → mirrorProtocol-c5g5MgNc.cjs} +3 -3
- package/dist/{package-NoD8QOOM.cjs → package-3aPiSpZj.cjs} +7 -7
- package/dist/{package-DYGwlmXl.mjs → package-CTrz6U3C.mjs} +7 -7
- package/dist/{panels-B8zRm7_d.mjs → panels-B5sZ84vA.mjs} +5 -5
- package/dist/{panels-DYEc1NyQ.cjs → panels-BmsmKHbw.cjs} +5 -5
- package/dist/{peerTools-CK3JYu1k.mjs → peerTools-DIPOX033.mjs} +5 -5
- package/dist/{peerTools-CP-x-Peu.cjs → peerTools-Dxe7iCi_.cjs} +5 -5
- package/dist/{persistence-BY-D00wT.cjs → persistence-DZRMUZZS.cjs} +2 -2
- package/dist/{persistence-D2Djrf7t.mjs → persistence-zJo4r7YI.mjs} +2 -2
- package/dist/{piRunnerArgs-gxLYIZH4.cjs → piRunnerArgs-BSPiS6tZ.cjs} +1 -1
- package/dist/{piRunnerArgs-IZLoL7TG.mjs → piRunnerArgs-IVdoHUB6.mjs} +1 -1
- package/dist/{piRuntime-o5L8uX-W.cjs → piRuntime-BJzjlMcN.cjs} +2 -2
- package/dist/{piRuntime-D7Y4oHVL.mjs → piRuntime-BvHO3vgO.mjs} +2 -2
- package/dist/{piTerminal-C_-JDkLN.cjs → piTerminal-Cz4pW55d.cjs} +20 -18
- package/dist/{piTerminal-Kphx8Uhw.mjs → piTerminal-D7vBOpkE.mjs} +20 -18
- package/dist/{pushNotifications-DXGIhE-A.mjs → pushNotifications-C4R9-1hU.mjs} +5 -3
- package/dist/{pushNotifications-BXoOSq_G.cjs → pushNotifications-CgIrYml3.cjs} +5 -3
- package/dist/{query-CdCxzuaB.cjs → query-CZQG1PdU.cjs} +1 -1
- package/dist/{query-CcA6DVZK.mjs → query-DKPmDsHS.mjs} +1 -1
- package/dist/{rateLimitWindows-OpAmdOtC.cjs → rateLimitWindows-CPV0nLeq.cjs} +1 -1
- package/dist/{rateLimitWindows-ZtHZHe-l.mjs → rateLimitWindows-HlHATOQ0.mjs} +1 -1
- package/dist/{registerCommonHandlers-CRjBoJLK.cjs → registerCommonHandlers-D1pC83Op.cjs} +2 -2
- package/dist/{registerCommonHandlers-DF6bdDtd.mjs → registerCommonHandlers-a9lHojSV.mjs} +2 -2
- package/dist/{registerKillSessionHandler-DDCKL3wf.mjs → registerKillSessionHandler-Bp-fuH-w.mjs} +6 -6
- package/dist/{registerKillSessionHandler-7fOm6ooh.cjs → registerKillSessionHandler-nTi2vVJy.cjs} +6 -6
- package/dist/{remoteSessionClient-B2POSKFT.mjs → remoteSessionClient-Cwrddscy.mjs} +5 -5
- package/dist/{remoteSessionClient-CFCn42bs.cjs → remoteSessionClient-HQAKAvVd.cjs} +5 -5
- package/dist/{reportProgressTool-th4trcET.cjs → reportProgressTool-Be-8ADI4.cjs} +4 -4
- package/dist/{reportProgressTool-tPxEvh6x.mjs → reportProgressTool-CcZ32nrd.mjs} +4 -4
- package/dist/{resources-CH1iP6Cv.mjs → resources-CLdQfe3b.mjs} +1 -1
- package/dist/{resources-BPyN59Iv.cjs → resources-CqwHPhdK.cjs} +1 -1
- package/dist/{run-cKQBbVi4.mjs → run-BBU8GyxE.mjs} +63 -244
- package/dist/{run-C_FumCQk.cjs → run-CxRJk9qq.cjs} +73 -255
- package/dist/{runClaude-CYnM6krL.mjs → runClaude-DR6pEQwd.mjs} +23 -22
- package/dist/{runClaude-z_2YC_aa.cjs → runClaude-Dd5zJaUZ.cjs} +23 -22
- package/dist/{runClaudeDeps-duS06Pb4.mjs → runClaudeDeps-COgZaUZm.mjs} +50 -45
- package/dist/{runClaudeDeps-CLubzWnA.cjs → runClaudeDeps-CTtZ_1mC.cjs} +45 -40
- package/dist/{runGemini-C_Pc2EhH.mjs → runGemini-DVoVUfPp.mjs} +40 -37
- package/dist/{runGemini-vEoXV6qT.cjs → runGemini-DX72qB92.cjs} +40 -37
- package/dist/{runOpenClaw-Cxun1qLC.mjs → runOpenClaw-BHjzmzeC.mjs} +19 -18
- package/dist/{runOpenClaw-CXJCtMgB.cjs → runOpenClaw-BSg4hFVt.cjs} +19 -18
- package/dist/{sandbox-CsvPnTmz.mjs → sandbox-CWR9EqoN.mjs} +4 -4
- package/dist/{sandbox-CO46H2tK.cjs → sandbox-D1T_hmgZ.cjs} +4 -4
- package/dist/{selfCheck-DIr7Gc0i.cjs → selfCheck-CE7vlnNS.cjs} +36 -36
- package/dist/selfCheck-ZGgBtxFQ.mjs +85 -0
- package/dist/{send-DMoFg6F1.mjs → send-BDF0Zvxo.mjs} +8 -8
- package/dist/{send-X2mfVEyr.cjs → send-BxZqRCcG.cjs} +8 -8
- package/dist/{server-CkqQ0sRH.mjs → server-B4JtnDLF.mjs} +4 -4
- package/dist/{server-BwTEdbkr.cjs → server-DB7_70Mi.cjs} +6 -6
- package/dist/{sessionLock-CdIRJlHH.cjs → sessionLock-BXvFN6uw.cjs} +3 -3
- package/dist/{sessionLock-JIfwCqMk.mjs → sessionLock-CXOOX-VD.mjs} +3 -3
- package/dist/{sessionOps-DVA-00nR.cjs → sessionOps-BWlDlFYP.cjs} +4 -4
- package/dist/{sessionOps-DqlftXxe.mjs → sessionOps-ByVggB_q.mjs} +4 -4
- package/dist/{sessionScanner-BMwgGAhY.cjs → sessionScanner-DkxPB5VA.cjs} +3 -3
- package/dist/{sessionScanner-BI0Vo1xT.mjs → sessionScanner-r66RtF95.mjs} +3 -3
- package/dist/{sessions-DjgEcHaT.cjs → sessions-BiJTxFy9.cjs} +10 -10
- package/dist/{sessions-D6IimX7b.mjs → sessions-C1lLK_a9.mjs} +10 -10
- package/dist/{setupOfflineReconnection-DgJK_4m1.mjs → setupOfflineReconnection-Bl3DuakT.mjs} +2 -2
- package/dist/{setupOfflineReconnection-CjuCiKY3.cjs → setupOfflineReconnection-CecTiFZ-.cjs} +2 -2
- package/dist/{spawn-Dvzy-tzc.mjs → spawn-D-8MlEM6.mjs} +14 -14
- package/dist/{spawn-rhF28shY.cjs → spawn-D79mVa3h.cjs} +14 -14
- package/dist/{spawnAgents-CCnlq-FI.mjs → spawnAgents-DJHjq01u.mjs} +1 -1
- package/dist/{spawnAgents-iwuqrgR9.cjs → spawnAgents-Sfnf-4pN.cjs} +1 -1
- package/dist/{spawnHappyCLI-DnKmIsEt.cjs → spawnHappyCLI-CQwQs9G5.cjs} +1 -1
- package/dist/{spawnHappyCLI-Dq3LLXLb.mjs → spawnHappyCLI-v_bJwZ4Z.mjs} +1 -1
- package/dist/{specialCommands-Zwr2C6A2.cjs → specialCommands-CWMVfT5s.cjs} +3 -3
- package/dist/{specialCommands-CcyZ1hGA.mjs → specialCommands-yMnFvS-W.mjs} +3 -3
- package/dist/{startFileWatcher-4thXDK9d.mjs → startFileWatcher-BCDkT_tr.mjs} +1 -1
- package/dist/{startFileWatcher-K6hJSyiR.cjs → startFileWatcher-GW4_E7dV.cjs} +1 -1
- package/dist/{startupSessionCreate-BfByJ16A.mjs → startupSessionCreate-BcAGYo51.mjs} +1 -1
- package/dist/{startupSessionCreate-CKEbq0tL.cjs → startupSessionCreate-tBRa_ze8.cjs} +1 -1
- package/dist/{streamRelay-LcDvyEjK.mjs → streamRelay-BEDiy6_b.mjs} +1 -1
- package/dist/{streamRelay-Gy8Xfnon.cjs → streamRelay-BZ8drtof.cjs} +1 -1
- package/dist/{sync-DO-2PU45.mjs → sync--QUIOuf8.mjs} +1 -1
- package/dist/{sync-1hQ5KHpL.cjs → sync-BMDkyo75.cjs} +1 -1
- package/dist/{systemPrompt-Dj7pgkMK.cjs → systemPrompt-DoHSXrH0.cjs} +2 -2
- package/dist/{systemPrompt-BctLfMZs.mjs → systemPrompt-O0Kd6vbA.mjs} +2 -2
- package/dist/{teams-uQxUlyyz.cjs → teams-BkZop-sP.cjs} +13 -10
- package/dist/{teams-Bu8YeOoF.mjs → teams-CTF1gT48.mjs} +13 -10
- package/dist/{template-BaHAmH2K.cjs → template-CeC1dYe-.cjs} +32 -2
- package/dist/{template-CiOPa2rd.mjs → template-D1fm22em.mjs} +31 -3
- package/dist/{time-BcoJaG8E.mjs → time-B2KKdAym.mjs} +1 -1
- package/dist/{time-nqa9shu8.cjs → time-CdCotzKU.cjs} +1 -1
- package/dist/{titleGenerator-D5r06bhf.mjs → titleGenerator-BFDczskR.mjs} +1 -1
- package/dist/{titleGenerator-D09XyYYG.cjs → titleGenerator-DZH1XqAf.cjs} +2 -2
- package/dist/{todo-D1q8146q.mjs → todo-C2gyOUom.mjs} +6 -6
- package/dist/{todo-CCOnL7pT.cjs → todo-DnTFTHmV.cjs} +6 -6
- package/dist/{tools-DiCXnAC5.cjs → tools-C8rpwyIl.cjs} +1 -1
- package/dist/{tools-D2E--n1x.mjs → tools-DUjkqSQL.mjs} +1 -1
- package/dist/{uninstall-CtwXi1Rt.mjs → uninstall-B5Ew50mZ.mjs} +3 -3
- package/dist/{uninstall-BrNwwwSD.cjs → uninstall-CKMPWTMv.cjs} +3 -3
- package/package.json +7 -7
- package/dist/daemonSupervisor-B-SPSqhs.cjs +0 -13
- package/dist/daemonSupervisor-C6urv3_8.mjs +0 -10
- package/dist/selfCheck-NamtBHVB.mjs +0 -85
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var logger = require('./logger-
|
|
3
|
+
var logger = require('./logger-Cgwz30rK.cjs');
|
|
4
4
|
var prompt = require('./prompt-Bof2aQkI.cjs');
|
|
5
|
-
var client = require('./client-
|
|
6
|
-
var reportProgressTool = require('./reportProgressTool-
|
|
5
|
+
var client = require('./client-DcKJ0ul_.cjs');
|
|
6
|
+
var reportProgressTool = require('./reportProgressTool-Be-8ADI4.cjs');
|
|
7
7
|
var node_fs = require('node:fs');
|
|
8
8
|
var os = require('node:os');
|
|
9
9
|
var path = require('node:path');
|
|
@@ -11,22 +11,24 @@ var z = require('zod');
|
|
|
11
11
|
var pi = require('./pi-DlsrPCTa.cjs');
|
|
12
12
|
var promises = require('node:fs/promises');
|
|
13
13
|
var node_crypto = require('node:crypto');
|
|
14
|
-
var client$1 = require('./client
|
|
15
|
-
var context = require('./context-
|
|
16
|
-
var sessionOps = require('./sessionOps-
|
|
17
|
-
var resources = require('./resources-
|
|
18
|
-
var tools = require('./tools-
|
|
19
|
-
var peerTools = require('./peerTools-
|
|
14
|
+
var client$1 = require('./client--vZueZYF.cjs');
|
|
15
|
+
var context = require('./context-DNKNF8Yh.cjs');
|
|
16
|
+
var sessionOps = require('./sessionOps-BWlDlFYP.cjs');
|
|
17
|
+
var resources = require('./resources-CqwHPhdK.cjs');
|
|
18
|
+
var tools = require('./tools-C8rpwyIl.cjs');
|
|
19
|
+
var peerTools = require('./peerTools-Dxe7iCi_.cjs');
|
|
20
20
|
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
21
21
|
var node_http = require('node:http');
|
|
22
22
|
var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
23
23
|
var limits = require('./limits-C-a7gr04.cjs');
|
|
24
|
-
var downloadTarget = require('./downloadTarget-
|
|
24
|
+
var downloadTarget = require('./downloadTarget-CmC11Y7E.cjs');
|
|
25
25
|
var agentGuidance = require('./agentGuidance-BtBx2xxE.cjs');
|
|
26
26
|
var webPanels = require('./webPanels-FCgFKvVe.cjs');
|
|
27
|
-
var persistence = require('./persistence-
|
|
28
|
-
var pushNotifications = require('./pushNotifications-
|
|
27
|
+
var persistence = require('./persistence-DZRMUZZS.cjs');
|
|
28
|
+
var pushNotifications = require('./pushNotifications-CgIrYml3.cjs');
|
|
29
29
|
var previewPath = require('./previewPath-B0rBdhy1.cjs');
|
|
30
|
+
var index = require('./index-Fq-jxjsB.cjs');
|
|
31
|
+
var configuration = require('./configuration-K3I6yOjZ.cjs');
|
|
30
32
|
var require$$3 = require('crypto');
|
|
31
33
|
|
|
32
34
|
function createProjectInjector(deps) {
|
|
@@ -303,10 +305,6 @@ function createRequestHuman(deps) {
|
|
|
303
305
|
const reason = rawReason.replace(/\s+/g, " ").trim().slice(0, REQUEST_HUMAN_REASON_MAX);
|
|
304
306
|
if (!reason) return { notified: false, error: "reason must be a non-empty string" };
|
|
305
307
|
await deps.reportBlocked(reason);
|
|
306
|
-
const at = now();
|
|
307
|
-
if (last && last.reason === reason && at - last.at < REQUEST_HUMAN_DEDUPE_MS) {
|
|
308
|
-
return { notified: true, deduped: true };
|
|
309
|
-
}
|
|
310
308
|
let url = `/session/${encodeURIComponent(deps.sessionId)}`;
|
|
311
309
|
let unknownPanel;
|
|
312
310
|
if (panel) {
|
|
@@ -315,18 +313,534 @@ function createRequestHuman(deps) {
|
|
|
315
313
|
if (machineId && panels.some((p) => p.id === panel)) url = webPanels.webPanelPath(machineId, panel);
|
|
316
314
|
else unknownPanel = panel;
|
|
317
315
|
}
|
|
316
|
+
if (deps.postBlocked && await deps.postBlocked(reason, unknownPanel ? void 0 : panel).catch(() => false)) {
|
|
317
|
+
return { notified: true, board: true, ...unknownPanel ? { unknownPanel } : {} };
|
|
318
|
+
}
|
|
319
|
+
const at = now();
|
|
320
|
+
if (last && last.reason === reason && at - last.at < REQUEST_HUMAN_DEDUPE_MS) {
|
|
321
|
+
return { notified: true, deduped: true };
|
|
322
|
+
}
|
|
318
323
|
const sent = await deps.sendAttention({ sessionId: deps.sessionId, reason, url });
|
|
319
324
|
if (sent.sent) last = { reason, at };
|
|
320
325
|
return { notified: sent.sent, url, ...unknownPanel ? { unknownPanel } : {}, ...sent.error ? { error: sent.error } : {} };
|
|
321
326
|
};
|
|
322
327
|
}
|
|
323
328
|
function requestHumanResultText(r) {
|
|
329
|
+
if (r.board) {
|
|
330
|
+
const panelNote2 = r.unknownPanel ? ` (panel "${r.unknownPanel}" is not declared on this machine, so it is not linked)` : "";
|
|
331
|
+
return `Posted to the user's briefing board as blocked; the server notifies them once (asking again with the same reason only refreshes it)${panelNote2}. This does not mean they have seen it. Poll your own completion condition (URL changed, element gone) instead of waiting for a reply.`;
|
|
332
|
+
}
|
|
324
333
|
if (r.deduped) return "Already asked for this in the last minute; the board still shows it. Keep polling your completion condition.";
|
|
325
334
|
if (!r.notified) return `Could not notify the user: ${r.error ?? "unknown error"}. The task board is marked blocked.`;
|
|
326
335
|
const panelNote = r.unknownPanel ? ` (panel "${r.unknownPanel}" is not declared on this machine, so the notification opens the session instead)` : "";
|
|
327
336
|
return `Notified the user${panelNote}. This does not mean they have seen it. Poll your own completion condition (URL changed, element gone) instead of waiting for a reply.`;
|
|
328
337
|
}
|
|
329
338
|
|
|
339
|
+
const UNSUPPORTED_BACKOFF_MS = 10 * 6e4;
|
|
340
|
+
class AttentionUnsupportedError extends Error {
|
|
341
|
+
constructor() {
|
|
342
|
+
super("This Very Happy server has no briefing board");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
class AttentionRequestError extends Error {
|
|
346
|
+
constructor(status, code) {
|
|
347
|
+
super(code ? `Briefing board request failed: ${code}` : `Briefing board request failed (HTTP ${status})`);
|
|
348
|
+
this.status = status;
|
|
349
|
+
this.code = code;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function isItemErrorCode(code) {
|
|
353
|
+
return typeof code === "string" && code.startsWith("attention_") || code === "session_not_found";
|
|
354
|
+
}
|
|
355
|
+
function createAttentionClient(deps = {}) {
|
|
356
|
+
const now = deps.now ?? Date.now;
|
|
357
|
+
const doFetch = deps.fetch ?? fetch;
|
|
358
|
+
const token = deps.token ?? (async () => (await persistence.readCredentialsForConfiguredRelay())?.token ?? null);
|
|
359
|
+
let disabledUntil = 0;
|
|
360
|
+
async function request(path, method, body, timeoutMs = 1e4) {
|
|
361
|
+
if (now() < disabledUntil) throw new AttentionUnsupportedError();
|
|
362
|
+
const bearer = await token();
|
|
363
|
+
if (!bearer) throw new AttentionRequestError(401, "not_signed_in");
|
|
364
|
+
const response = await doFetch(`${deps.serverUrl ?? configuration.configuration.serverUrl}${path}`, {
|
|
365
|
+
method,
|
|
366
|
+
redirect: "error",
|
|
367
|
+
headers: { authorization: `Bearer ${bearer}`, ...body === void 0 ? {} : { "content-type": "application/json" } },
|
|
368
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
369
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
370
|
+
});
|
|
371
|
+
let json = null;
|
|
372
|
+
try {
|
|
373
|
+
json = await response.json();
|
|
374
|
+
} catch {
|
|
375
|
+
}
|
|
376
|
+
if (response.status === 404 && (json?.error === index.ATTENTION_DISABLED_ERROR || !isItemErrorCode(json?.error))) {
|
|
377
|
+
disabledUntil = now() + UNSUPPORTED_BACKOFF_MS;
|
|
378
|
+
throw new AttentionUnsupportedError();
|
|
379
|
+
}
|
|
380
|
+
if (!response.ok) throw new AttentionRequestError(response.status, typeof json?.error === "string" ? json.error : null);
|
|
381
|
+
return json;
|
|
382
|
+
}
|
|
383
|
+
const enc = encodeURIComponent;
|
|
384
|
+
return {
|
|
385
|
+
isDisabled: () => now() < disabledUntil,
|
|
386
|
+
/** True when this server serves the board for this account (feature flag on). Network errors throw. */
|
|
387
|
+
async probe(sessionId, timeoutMs = 5e3) {
|
|
388
|
+
try {
|
|
389
|
+
await request(`/v1/attention-items?sessionId=${enc(sessionId)}&limit=1`, "GET", void 0, timeoutMs);
|
|
390
|
+
return true;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (error instanceof AttentionUnsupportedError) return false;
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
post: (input) => request("/v1/attention-items", "POST", input),
|
|
397
|
+
patch: (id, patch) => request(`/v1/attention-items/${enc(id)}`, "PATCH", patch),
|
|
398
|
+
resolve: (id, input) => request(`/v1/attention-items/${enc(id)}/resolve`, "POST", input),
|
|
399
|
+
list: (query, timeoutMs) => {
|
|
400
|
+
const params = new URLSearchParams();
|
|
401
|
+
for (const [key, value] of Object.entries(query)) if (value !== void 0) params.set(key, String(value));
|
|
402
|
+
return request(`/v1/attention-items?${params.toString()}`, "GET", void 0, timeoutMs);
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
let shared = null;
|
|
407
|
+
function sharedAttentionClient() {
|
|
408
|
+
return shared ??= createAttentionClient();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const TRIGGERS = [
|
|
412
|
+
/需要你(?:来)?(?:决定|拍板|确认|选择|定夺|批准|授权|选)/,
|
|
413
|
+
/请你?(?:来)?(?:拍板|决定|定夺|确认一下|选择|选一个|选一下)/,
|
|
414
|
+
/(?:等你|由你|你来)(?:拍板|决定|定夺|确认|选)/,
|
|
415
|
+
/需要\s*owner\s*(?:决定|拍板|确认)/i,
|
|
416
|
+
/\b(?:need|needs|waiting for|awaiting)\s+(?:your|the owner'?s?)\s+(?:decision|call|approval|sign-?off|go-?ahead)\b/i,
|
|
417
|
+
/\b(?:i|we)\s+need\s+you\s+to\s+(?:decide|choose|pick|confirm|approve)\b/i,
|
|
418
|
+
/\bplease\s+(?:decide|choose|pick one|confirm which|approve)\b/i,
|
|
419
|
+
/<options>/i
|
|
420
|
+
];
|
|
421
|
+
const OPTION_LINE = /^\s*(?:[-*+]\s+)?(?:\*\*)?(?:选项|option\s*)?([A-Da-d]|[1-4])(?:\*\*)?\s*[.)、::)]\s*(?:\*\*)?\s*(\S.*?)\s*$/i;
|
|
422
|
+
const TAGGED_OPTION = /<option(?:\s+id="([^"]{1,16})")?\s*>([\s\S]*?)<\/option>/gi;
|
|
423
|
+
const RECOMMENDED = /推荐|建议选|\brecommend(?:ed)?\b|\(rec\)/i;
|
|
424
|
+
function stripCode(text) {
|
|
425
|
+
return text.replace(/```[\s\S]*?(?:```|$)/g, "\n").replace(/`[^`\n]*`/g, " ");
|
|
426
|
+
}
|
|
427
|
+
function clean(text, max) {
|
|
428
|
+
const flat = text.replace(/\*\*|__|^#+\s*/g, "").replace(/\s+/g, " ").trim();
|
|
429
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
430
|
+
}
|
|
431
|
+
function triggerSentence(text, match) {
|
|
432
|
+
const start = Math.max(text.lastIndexOf("\n", match.index), ...["\u3002", "\uFF01", "\uFF1F", ". ", "! ", "? "].map((p) => {
|
|
433
|
+
const at = text.lastIndexOf(p, match.index);
|
|
434
|
+
return at < 0 ? -1 : at + p.length - 1;
|
|
435
|
+
})) + 1;
|
|
436
|
+
const rest = text.slice(match.index);
|
|
437
|
+
const endRel = rest.search(/[。!?\n]|[.!?](?:\s|$)/);
|
|
438
|
+
const end = endRel < 0 ? text.length : match.index + endRel + 1;
|
|
439
|
+
return text.slice(start, end);
|
|
440
|
+
}
|
|
441
|
+
function optionLabel(raw) {
|
|
442
|
+
return clean(raw.replace(/<[^>]+>/g, ""), index.ATTENTION_LIMITS.optionLabelChars);
|
|
443
|
+
}
|
|
444
|
+
function parseOptions(text) {
|
|
445
|
+
const tagged = [...text.matchAll(TAGGED_OPTION)].slice(0, index.ATTENTION_LIMITS.options.max);
|
|
446
|
+
if (tagged.length >= index.ATTENTION_LIMITS.options.min) {
|
|
447
|
+
return tagged.map((m, i) => ({
|
|
448
|
+
id: (m[1] ?? String.fromCharCode(65 + i)).slice(0, index.ATTENTION_LIMITS.optionIdChars),
|
|
449
|
+
label: optionLabel(m[2]) || `Option ${i + 1}`,
|
|
450
|
+
...RECOMMENDED.test(m[2]) ? { recommended: true } : {}
|
|
451
|
+
}));
|
|
452
|
+
}
|
|
453
|
+
const groups = [];
|
|
454
|
+
let current = null;
|
|
455
|
+
for (const line of text.split("\n")) {
|
|
456
|
+
if (!line.trim()) continue;
|
|
457
|
+
const m = OPTION_LINE.exec(line);
|
|
458
|
+
if (!m) {
|
|
459
|
+
current = null;
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
const id = m[1].toUpperCase();
|
|
463
|
+
const letter = /[A-D]/.test(id);
|
|
464
|
+
if (!current || current.letter !== letter || current.lines.some((l) => l.id === id)) {
|
|
465
|
+
current = { letter, lines: [] };
|
|
466
|
+
groups.push(current);
|
|
467
|
+
}
|
|
468
|
+
current.lines.push({ id, label: optionLabel(m[2]), recommended: RECOMMENDED.test(m[2]) });
|
|
469
|
+
}
|
|
470
|
+
const usable = groups.filter((g) => g.lines.length >= index.ATTENTION_LIMITS.options.min && g.lines.every((l) => l.label));
|
|
471
|
+
const pick = usable.find((g) => g.letter) ?? usable[0];
|
|
472
|
+
if (!pick) return [];
|
|
473
|
+
return pick.lines.slice(0, index.ATTENTION_LIMITS.options.max).map((l) => ({ id: l.id, label: l.label, ...l.recommended ? { recommended: true } : {} }));
|
|
474
|
+
}
|
|
475
|
+
function defaultOptions(cjk) {
|
|
476
|
+
return cjk ? [{ id: "yes", label: "\u540C\u610F" }, { id: "other", label: "\u53E6\u6709\u5B89\u6392\uFF08\u56DE\u590D\u8BF4\u660E\uFF09" }] : [{ id: "yes", label: "Approve" }, { id: "other", label: "Something else (reply)" }];
|
|
477
|
+
}
|
|
478
|
+
function inferOwnerDecision(text) {
|
|
479
|
+
const prose = stripCode(text);
|
|
480
|
+
let match = null;
|
|
481
|
+
for (const trigger of TRIGGERS) {
|
|
482
|
+
match = trigger.exec(prose);
|
|
483
|
+
if (match) break;
|
|
484
|
+
}
|
|
485
|
+
if (!match) return null;
|
|
486
|
+
const title = clean(triggerSentence(prose, match).replace(/<\/?options>/gi, ""), index.ATTENTION_LIMITS.titleChars);
|
|
487
|
+
if (!title) return null;
|
|
488
|
+
const parsed = parseOptions(text);
|
|
489
|
+
const options = parsed.length ? parsed : defaultOptions(/[㐀-鿿]/.test(title));
|
|
490
|
+
const digest = node_crypto.createHash("sha256").update(`${title}
|
|
491
|
+
${options.map((o) => `${o.id}:${o.label}`).join("\n")}`).digest("hex").slice(0, 16);
|
|
492
|
+
const encoder = new TextEncoder();
|
|
493
|
+
let bodyMd = text.trim();
|
|
494
|
+
while (encoder.encode(bodyMd).length > index.ATTENTION_LIMITS.bodyBytes / 2) bodyMd = `${bodyMd.slice(0, Math.floor(bodyMd.length * 0.8))}\u2026`;
|
|
495
|
+
return { title, options, key: `inferred-${digest}`, bodyMd };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const OWNER_BOARD_DIGEST_TIMEOUT_MS = 1500;
|
|
499
|
+
const QUESTION_TITLE_CHARS = 60;
|
|
500
|
+
const DIGEST_TITLE_CHARS = 40;
|
|
501
|
+
const DIGEST_MAX_CHARS = 480;
|
|
502
|
+
const LEAD_RECHECK_MS = 6e4;
|
|
503
|
+
const OWNER_SENT_FROM = ["web", "mobile", "ios", "android"];
|
|
504
|
+
const AUTOMATED_LOCAL_ID_PREFIXES = ["automation-", "teams-", "remote-send-", "session-message-", "edit-conflict-"];
|
|
505
|
+
function isOwnerMessage(input) {
|
|
506
|
+
const localId = input.localId ?? "";
|
|
507
|
+
if (index.isAttentionBoardLocalId(localId)) return false;
|
|
508
|
+
if (AUTOMATED_LOCAL_ID_PREFIXES.some((prefix) => localId.startsWith(prefix))) return false;
|
|
509
|
+
return typeof input.sentFrom === "string" && OWNER_SENT_FROM.includes(input.sentFrom);
|
|
510
|
+
}
|
|
511
|
+
function clip(text, max) {
|
|
512
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
513
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
514
|
+
}
|
|
515
|
+
function ownerQuestionTitle(text) {
|
|
516
|
+
const trimmed = text.trim();
|
|
517
|
+
if (!trimmed || trimmed.startsWith("/")) return null;
|
|
518
|
+
const first = trimmed.split("\n").find((line) => line.trim()) ?? "";
|
|
519
|
+
return clip(first, QUESTION_TITLE_CHARS) || null;
|
|
520
|
+
}
|
|
521
|
+
function minutesAgo(ms, now) {
|
|
522
|
+
const m = Math.max(0, Math.round((now - ms) / 6e4));
|
|
523
|
+
return m < 60 ? `${m}m` : `${Math.round(m / 60)}h`;
|
|
524
|
+
}
|
|
525
|
+
function formatBoardDigest(items, now, opts) {
|
|
526
|
+
if (items.length === 0) return null;
|
|
527
|
+
const parts = [];
|
|
528
|
+
let used = 0;
|
|
529
|
+
let shown = 0;
|
|
530
|
+
for (const item of items) {
|
|
531
|
+
const handle = item.key && !item.key.startsWith("req:") ? `key=${item.key}` : `id=${item.id}`;
|
|
532
|
+
const part = `${item.source === "owner" ? "owner " : ""}${item.kind} \u300C${clip(item.title, DIGEST_TITLE_CHARS)}\u300D ${handle} ${minutesAgo(item.createdAt, now)}`;
|
|
533
|
+
if (shown > 0 && used + part.length > DIGEST_MAX_CHARS) break;
|
|
534
|
+
parts.push(part);
|
|
535
|
+
used += part.length + 2;
|
|
536
|
+
shown++;
|
|
537
|
+
}
|
|
538
|
+
const more = items.length - shown;
|
|
539
|
+
const line1 = `[Briefing board] ${items.length} open: ${parts.join("; ")}${more > 0 ? `; +${more} more` : ""}`;
|
|
540
|
+
const line2 = opts.tools ? "[Briefing board] Answer owner items in this turn or settle them with owner_board_update / owner_board_resolve (owner_board_list shows ids); do not repeat open decisions in chat." : null;
|
|
541
|
+
return line2 ? `${line1}
|
|
542
|
+
${line2}` : line1;
|
|
543
|
+
}
|
|
544
|
+
function parseSince(since) {
|
|
545
|
+
if (since === void 0) return void 0;
|
|
546
|
+
if (/^\d+$/.test(since)) return Number(since);
|
|
547
|
+
const parsed = Date.parse(since);
|
|
548
|
+
if (Number.isNaN(parsed)) throw new Error("since must be ISO-8601 or epoch milliseconds");
|
|
549
|
+
return parsed;
|
|
550
|
+
}
|
|
551
|
+
async function resolveTeamLead(sessionId) {
|
|
552
|
+
try {
|
|
553
|
+
const scope = await context.readTeamScope(context.teamScopePath(sessionId));
|
|
554
|
+
if (!scope) return false;
|
|
555
|
+
const inspected = await client$1.teamRequest(`/v1/teams/${encodeURIComponent(scope.teamId)}/agent`, "GET", scope.scopeToken, true);
|
|
556
|
+
const bots = Array.isArray(inspected?.team?.bots) ? inspected.team.bots : [];
|
|
557
|
+
return bots.some((bot) => bot.id === scope.botId && bot.root === true);
|
|
558
|
+
} catch {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function createLeadBoard(deps) {
|
|
563
|
+
const now = deps.now ?? Date.now;
|
|
564
|
+
const log = deps.log ?? (() => {
|
|
565
|
+
});
|
|
566
|
+
let supported = false;
|
|
567
|
+
let lead = false;
|
|
568
|
+
let leadCheckedAt = 0;
|
|
569
|
+
let toolsFrozen = null;
|
|
570
|
+
let initPromise = null;
|
|
571
|
+
let initDone = false;
|
|
572
|
+
let lastLocalId = null;
|
|
573
|
+
let pending = [];
|
|
574
|
+
let turn = null;
|
|
575
|
+
let work = Promise.resolve();
|
|
576
|
+
const queue = (task) => {
|
|
577
|
+
work = work.then(task).catch((error) => log(`[ownerBoard] ${error instanceof Error ? error.message : String(error)}`));
|
|
578
|
+
return work;
|
|
579
|
+
};
|
|
580
|
+
async function refreshLead() {
|
|
581
|
+
if (lead || now() - leadCheckedAt < LEAD_RECHECK_MS) return lead;
|
|
582
|
+
leadCheckedAt = now();
|
|
583
|
+
lead = await deps.isLead().catch(() => false);
|
|
584
|
+
return lead;
|
|
585
|
+
}
|
|
586
|
+
function init() {
|
|
587
|
+
return initPromise ??= (async () => {
|
|
588
|
+
try {
|
|
589
|
+
supported = await deps.client.probe(deps.sessionId());
|
|
590
|
+
} catch (error) {
|
|
591
|
+
log(`[ownerBoard] probe failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
592
|
+
supported = false;
|
|
593
|
+
}
|
|
594
|
+
if (supported) {
|
|
595
|
+
deps.onSupported?.();
|
|
596
|
+
await refreshLead();
|
|
597
|
+
}
|
|
598
|
+
initDone = true;
|
|
599
|
+
})();
|
|
600
|
+
}
|
|
601
|
+
async function active() {
|
|
602
|
+
await init();
|
|
603
|
+
return supported && !deps.client.isDisabled() && await refreshLead();
|
|
604
|
+
}
|
|
605
|
+
function thisTurnAnchor() {
|
|
606
|
+
const localId = turn?.promptLocalId ?? lastLocalId;
|
|
607
|
+
if (!localId) return void 0;
|
|
608
|
+
const text = turn?.texts.at(-1);
|
|
609
|
+
return { localId, ...text ? { snippet: clip(text, index.ATTENTION_LIMITS.snippetChars) } : {} };
|
|
610
|
+
}
|
|
611
|
+
const noteBoardTool = (name) => {
|
|
612
|
+
turn?.boardTools.add(name);
|
|
613
|
+
};
|
|
614
|
+
async function settleQuestions(questions, reply) {
|
|
615
|
+
for (const question of questions) {
|
|
616
|
+
const id = await question.itemId;
|
|
617
|
+
if (!id) continue;
|
|
618
|
+
const anchor = { localId: question.localId, snippet: clip(reply, index.ATTENTION_LIMITS.snippetChars) };
|
|
619
|
+
const { item } = await deps.client.patch(id, { firstReply: anchor, requestId: `first-reply-${question.localId}`.slice(0, 128) });
|
|
620
|
+
if (item.state === "open") {
|
|
621
|
+
await deps.client.resolve(id, { outcome: "answered", by: "auto-first-reply", requestId: `auto-answered-${question.localId}`.slice(0, 128) });
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
async function draftDecision(text, anchor) {
|
|
626
|
+
if (!await deps.autoDetect().catch(() => true)) return;
|
|
627
|
+
const draft = inferOwnerDecision(text);
|
|
628
|
+
if (!draft) return;
|
|
629
|
+
await deps.client.post({
|
|
630
|
+
sessionId: deps.sessionId(),
|
|
631
|
+
source: "lead",
|
|
632
|
+
kind: "decision",
|
|
633
|
+
inferred: true,
|
|
634
|
+
title: draft.title,
|
|
635
|
+
bodyMd: draft.bodyMd,
|
|
636
|
+
options: draft.options,
|
|
637
|
+
key: draft.key,
|
|
638
|
+
...anchor ? { anchor } : {},
|
|
639
|
+
requestId: node_crypto.randomUUID()
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
init,
|
|
644
|
+
/** Waits (bounded) for the first probe so the first `tools/list` sees the final tool set. */
|
|
645
|
+
async ready(timeoutMs) {
|
|
646
|
+
await Promise.race([init(), new Promise((resolve) => setTimeout(resolve, timeoutMs).unref?.())]);
|
|
647
|
+
},
|
|
648
|
+
/** Server serves the board (any session: request_human items, `owner-board-v1`). */
|
|
649
|
+
supported: () => supported && !deps.client.isDisabled(),
|
|
650
|
+
/**
|
|
651
|
+
* `owner_board_*` exposure, decided once per process: the tool list must
|
|
652
|
+
* not change under a running agent. A session adopted into a team later
|
|
653
|
+
* gets the tools on its next relaunch; the fallbacks start at once.
|
|
654
|
+
*/
|
|
655
|
+
toolsExposed() {
|
|
656
|
+
if (toolsFrozen === null && initDone) toolsFrozen = supported && lead;
|
|
657
|
+
return toolsFrozen ?? false;
|
|
658
|
+
},
|
|
659
|
+
// ─── tools ──────────────────────────────────────────────────────────
|
|
660
|
+
async post(input) {
|
|
661
|
+
noteBoardTool("owner_board_post");
|
|
662
|
+
const anchor = input.anchor === void 0 || input.anchor === "this_turn" ? thisTurnAnchor() : { localId: input.anchor.localId };
|
|
663
|
+
const { item, created } = await deps.client.post(index.ownerBoardPostToRest(deps.sessionId(), input, anchor));
|
|
664
|
+
return { id: item.id, created };
|
|
665
|
+
},
|
|
666
|
+
async update(input) {
|
|
667
|
+
noteBoardTool("owner_board_update");
|
|
668
|
+
return deps.client.patch(input.id, {
|
|
669
|
+
...input.title !== void 0 ? { title: input.title } : {},
|
|
670
|
+
...input.body_md !== void 0 ? { bodyMd: input.body_md } : {},
|
|
671
|
+
...input.append_md !== void 0 ? { appendMd: input.append_md } : {},
|
|
672
|
+
...input.options !== void 0 ? { options: input.options } : {},
|
|
673
|
+
...input.links !== void 0 ? { links: input.links } : {},
|
|
674
|
+
...input.state !== void 0 ? { state: input.state } : {},
|
|
675
|
+
requestId: input.request_id
|
|
676
|
+
});
|
|
677
|
+
},
|
|
678
|
+
async resolve(input) {
|
|
679
|
+
noteBoardTool("owner_board_resolve");
|
|
680
|
+
return deps.client.resolve(input.id, { outcome: input.outcome, ...input.note !== void 0 ? { note: input.note } : {}, by: "lead", requestId: input.request_id });
|
|
681
|
+
},
|
|
682
|
+
async list(input) {
|
|
683
|
+
const since = parseSince(input.since);
|
|
684
|
+
return deps.client.list({ sessionId: deps.sessionId(), state: input.state === "all" ? "all" : "open", limit: 50, ...since !== void 0 ? { since } : {} });
|
|
685
|
+
},
|
|
686
|
+
/**
|
|
687
|
+
* `request_human` as a `blocked` item (server dedupes by key and pushes
|
|
688
|
+
* once). False when this server has no board: the caller keeps the old
|
|
689
|
+
* metadata + push path.
|
|
690
|
+
*/
|
|
691
|
+
async postBlocked(reason, panel) {
|
|
692
|
+
noteBoardTool("request_human");
|
|
693
|
+
await init();
|
|
694
|
+
if (!supported || deps.client.isDisabled()) return false;
|
|
695
|
+
try {
|
|
696
|
+
await deps.client.post({
|
|
697
|
+
sessionId: deps.sessionId(),
|
|
698
|
+
source: "agent",
|
|
699
|
+
kind: "blocked",
|
|
700
|
+
title: clip(reason, index.ATTENTION_LIMITS.titleChars),
|
|
701
|
+
...reason.length > index.ATTENTION_LIMITS.titleChars ? { bodyMd: reason } : {},
|
|
702
|
+
key: `request-human-${node_crypto.createHash("sha256").update(reason).digest("hex").slice(0, 16)}`,
|
|
703
|
+
...panel ? { links: [{ type: "panel", ref: panel }] } : {},
|
|
704
|
+
...thisTurnAnchor() ? { anchor: thisTurnAnchor() } : {},
|
|
705
|
+
requestId: node_crypto.randomUUID()
|
|
706
|
+
});
|
|
707
|
+
return true;
|
|
708
|
+
} catch (error) {
|
|
709
|
+
if (!(error instanceof AttentionUnsupportedError)) log(`[ownerBoard] request_human item failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
// ─── message taps ───────────────────────────────────────────────────
|
|
714
|
+
/** Every routed user message (any runner). Owner messages in a lead session become `question` items. */
|
|
715
|
+
onUserMessage(message) {
|
|
716
|
+
if (message.localId) lastLocalId = message.localId;
|
|
717
|
+
if (!message.localId || !isOwnerMessage(message)) return;
|
|
718
|
+
const title = ownerQuestionTitle(message.text ?? "");
|
|
719
|
+
if (!title) return;
|
|
720
|
+
const localId = message.localId;
|
|
721
|
+
const itemId = (async () => {
|
|
722
|
+
if (!await active()) return null;
|
|
723
|
+
const { item } = await deps.client.post({
|
|
724
|
+
sessionId: deps.sessionId(),
|
|
725
|
+
source: "owner",
|
|
726
|
+
kind: "question",
|
|
727
|
+
title,
|
|
728
|
+
anchor: { localId, snippet: clip(message.text ?? "", index.ATTENTION_LIMITS.snippetChars) },
|
|
729
|
+
// No key: the server keys it by request id, so a replay after a restart is a no-op.
|
|
730
|
+
requestId: `owner-q-${localId}`.slice(0, 128)
|
|
731
|
+
});
|
|
732
|
+
return item.id;
|
|
733
|
+
})().catch((error) => {
|
|
734
|
+
log(`[ownerBoard] owner question not posted: ${error instanceof Error ? error.message : String(error)}`);
|
|
735
|
+
return null;
|
|
736
|
+
});
|
|
737
|
+
const question = { localId, itemId };
|
|
738
|
+
if (message.delivery === "steer" && turn) turn.questions.push(question);
|
|
739
|
+
else pending.push(question);
|
|
740
|
+
},
|
|
741
|
+
/**
|
|
742
|
+
* The agent takes queued input (Claude remote launcher, right before the
|
|
743
|
+
* prompt goes to the SDK). `sourceIds` = the batch's message localIds when
|
|
744
|
+
* known: only those questions belong to this turn. When none match (an
|
|
745
|
+
* id scheme we do not recognise) every waiting question does.
|
|
746
|
+
*/
|
|
747
|
+
onTurnStart(sourceIds) {
|
|
748
|
+
const ids = new Set(sourceIds ?? []);
|
|
749
|
+
const mine = pending.filter((q) => ids.has(q.localId));
|
|
750
|
+
const questions = ids.size && mine.length ? mine : pending;
|
|
751
|
+
pending = pending.filter((q) => !questions.includes(q));
|
|
752
|
+
turn = { promptLocalId: sourceIds?.at(-1) ?? lastLocalId, questions, texts: [], boardTools: /* @__PURE__ */ new Set() };
|
|
753
|
+
},
|
|
754
|
+
onAssistantText(text) {
|
|
755
|
+
if (!turn || !text.trim() || turn.texts.at(-1) === text) return;
|
|
756
|
+
turn.texts.push(text);
|
|
757
|
+
},
|
|
758
|
+
onTurnEnd() {
|
|
759
|
+
const ended = turn;
|
|
760
|
+
turn = null;
|
|
761
|
+
if (!ended) return work;
|
|
762
|
+
const anchor = ended.promptLocalId ? { localId: ended.promptLocalId } : void 0;
|
|
763
|
+
return queue(async () => {
|
|
764
|
+
const reply = ended.texts.join("\n\n").trim();
|
|
765
|
+
if (!reply || !await active()) return;
|
|
766
|
+
if (ended.questions.length) await settleQuestions(ended.questions, ended.texts[0]);
|
|
767
|
+
if (!ended.boardTools.has("owner_board_post") && !ended.boardTools.has("request_human")) {
|
|
768
|
+
await draftDecision(reply, anchor ? { ...anchor, snippet: clip(ended.texts.at(-1) ?? reply, index.ATTENTION_LIMITS.snippetChars) } : void 0);
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
},
|
|
772
|
+
/** Prefix the turn's prompt with the open-items digest (lead sessions only; unchanged on any failure). */
|
|
773
|
+
async decorate(message) {
|
|
774
|
+
if (typeof message !== "string") return message;
|
|
775
|
+
try {
|
|
776
|
+
const result = await Promise.race([
|
|
777
|
+
(async () => {
|
|
778
|
+
if (!await active()) return null;
|
|
779
|
+
const fresh = new Set(turn?.questions.map((q) => q.localId) ?? []);
|
|
780
|
+
const { items } = await deps.client.list({ sessionId: deps.sessionId(), state: "open", limit: 20 }, OWNER_BOARD_DIGEST_TIMEOUT_MS);
|
|
781
|
+
return formatBoardDigest(items.filter((item) => !(item.anchor?.localId && fresh.has(item.anchor.localId) && item.source === "owner")), now(), { tools: toolsFrozen === true });
|
|
782
|
+
})(),
|
|
783
|
+
new Promise((resolve) => setTimeout(() => resolve(null), OWNER_BOARD_DIGEST_TIMEOUT_MS).unref?.())
|
|
784
|
+
]);
|
|
785
|
+
return result ? `${result}
|
|
786
|
+
${message}` : message;
|
|
787
|
+
} catch (error) {
|
|
788
|
+
log(`[ownerBoard] digest skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
789
|
+
return message;
|
|
790
|
+
}
|
|
791
|
+
},
|
|
792
|
+
/** Settles once queued turn-end follow-ups are done (tests, shutdown). */
|
|
793
|
+
idle: () => work
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
const boards = /* @__PURE__ */ new WeakMap();
|
|
797
|
+
function attachLeadBoard(client, board) {
|
|
798
|
+
boards.set(client, board);
|
|
799
|
+
}
|
|
800
|
+
function leadBoardFor(client) {
|
|
801
|
+
return boards.get(client);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const OWNER_BOARD_TOOL_NAMES = ["owner_board_post", "owner_board_update", "owner_board_resolve", "owner_board_list"];
|
|
805
|
+
const POST_DESCRIPTION = "Post to the owner's briefing board (\u6C47\u62A5\u677F / Briefing), the one place the user checks for what needs them. kind: decision (they must choose; 2-4 options, mark the recommended one), todo (something only they can do offline: log in, authorize, look at a device), report (a milestone: accepted, merged, released, incident conclusion), answer (reply_to = the owner question item id). Use a stable key per topic: posting the same key again updates that item instead of adding one. anchor defaults to this turn's reply. Do not also paste open decisions into chat or member replies.";
|
|
806
|
+
const UPDATE_DESCRIPTION = `Edit one of this session's briefing board items, or set state in_progress ("on it, the result will come to the board") / open. Use it to refresh a reminder instead of repeating it in chat; an owner question can be reclassified only by the board itself.`;
|
|
807
|
+
const RESOLVE_DESCRIPTION = "Close one of this session's briefing board items: done, answered, dropped or superseded (say why in note).";
|
|
808
|
+
const LIST_DESCRIPTION = "List this session's briefing board items with the owner's choice and note. state: open (default) or all; since: ISO-8601 or epoch ms.";
|
|
809
|
+
function errorText(error) {
|
|
810
|
+
if (error instanceof AttentionUnsupportedError) return "The briefing board is not available on this server right now.";
|
|
811
|
+
if (error instanceof AttentionRequestError) return error.message;
|
|
812
|
+
return error instanceof Error ? error.message : "Briefing board request failed";
|
|
813
|
+
}
|
|
814
|
+
function registerOwnerBoardTools(server, board) {
|
|
815
|
+
const register = (name, description, schema, execute, readOnly = false) => {
|
|
816
|
+
server.registerTool(name, {
|
|
817
|
+
description,
|
|
818
|
+
inputSchema: schema.shape,
|
|
819
|
+
annotations: { readOnlyHint: readOnly, idempotentHint: true, openWorldHint: false, destructiveHint: false }
|
|
820
|
+
}, async (args) => {
|
|
821
|
+
const parsed = schema.safeParse(args);
|
|
822
|
+
if (!parsed.success) {
|
|
823
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues.map((i) => `${i.path.join(".") || "input"}: ${i.message}`).join("; ")}` }], isError: true };
|
|
824
|
+
}
|
|
825
|
+
try {
|
|
826
|
+
return { content: [{ type: "text", text: JSON.stringify(await execute(parsed.data)) }], isError: false };
|
|
827
|
+
} catch (error) {
|
|
828
|
+
return { content: [{ type: "text", text: errorText(error) }], isError: true };
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
};
|
|
832
|
+
register("owner_board_post", POST_DESCRIPTION, index.OwnerBoardPostInputSchema, (args) => board.post(args));
|
|
833
|
+
register("owner_board_update", UPDATE_DESCRIPTION, index.OwnerBoardUpdateInputSchema, (args) => board.update(args));
|
|
834
|
+
register("owner_board_resolve", RESOLVE_DESCRIPTION, index.OwnerBoardResolveInputSchema, (args) => board.resolve(args));
|
|
835
|
+
register("owner_board_list", LIST_DESCRIPTION, index.OwnerBoardListInputSchema, (args) => board.list(args), true);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
const OWNER_BOARD_READY_MS = 3e3;
|
|
839
|
+
function assistantTexts(body) {
|
|
840
|
+
const line = body;
|
|
841
|
+
if (line?.type !== "assistant" || line.isSidechain || !Array.isArray(line.message?.content)) return [];
|
|
842
|
+
return line.message.content.filter((block) => block?.type === "text" && typeof block.text === "string" && block.text.trim()).map((block) => block.text);
|
|
843
|
+
}
|
|
330
844
|
function createMcpServer(handlers, options, sessionId, client) {
|
|
331
845
|
const mcp = new mcp_js.McpServer({
|
|
332
846
|
name: "Very Happy Tools",
|
|
@@ -458,6 +972,32 @@ function withoutPhase(board) {
|
|
|
458
972
|
async function startHappyServer(client, options) {
|
|
459
973
|
logger.logger.debug(`[happyMCP] server:start sessionId=${client.sessionId} assistant=${!!options?.assistant}`);
|
|
460
974
|
const selfReportState = options?.selfReportState ?? reportProgressTool.createSelfReportState();
|
|
975
|
+
const leadBoard = createLeadBoard({
|
|
976
|
+
sessionId: () => client.sessionId,
|
|
977
|
+
client: sharedAttentionClient(),
|
|
978
|
+
isLead: () => resolveTeamLead(client.sessionId),
|
|
979
|
+
autoDetect: async () => (await persistence.readSettings()).ownerBoardAutoDetect !== false,
|
|
980
|
+
onSupported: () => client.updateMetadata((metadata) => ({ ...metadata, capabilities: [.../* @__PURE__ */ new Set([...metadata.capabilities ?? [], index.ATTENTION_SESSION_CAPABILITY])] })),
|
|
981
|
+
log: (message) => logger.logger.debug(message)
|
|
982
|
+
});
|
|
983
|
+
attachLeadBoard(client, leadBoard);
|
|
984
|
+
void leadBoard.init();
|
|
985
|
+
const onRoutedUserMessage = (event) => {
|
|
986
|
+
const text = event.message?.content?.text;
|
|
987
|
+
leadBoard.onUserMessage({ localId: event.localId, text: typeof text === "string" ? text : null, sentFrom: event.message?.meta?.sentFrom, delivery: event.message?.meta?.delivery });
|
|
988
|
+
};
|
|
989
|
+
const onClaudeLine = (body) => {
|
|
990
|
+
for (const text of assistantTexts(body)) leadBoard.onAssistantText(text);
|
|
991
|
+
};
|
|
992
|
+
const onTurnEnded = () => {
|
|
993
|
+
void leadBoard.onTurnEnd();
|
|
994
|
+
};
|
|
995
|
+
const tapped = typeof client.on === "function";
|
|
996
|
+
if (tapped) {
|
|
997
|
+
client.on("user-message-routed", onRoutedUserMessage);
|
|
998
|
+
client.on("claude-session-message", onClaudeLine);
|
|
999
|
+
client.on("turn-ended", onTurnEnded);
|
|
1000
|
+
}
|
|
461
1001
|
const handlers = {
|
|
462
1002
|
openPreview: async (path, mode, download = false) => {
|
|
463
1003
|
const verdict = previewPath.checkPreviewPath(path);
|
|
@@ -538,7 +1078,8 @@ async function startHappyServer(client, options) {
|
|
|
538
1078
|
readPanels: async () => (await persistence.readSettings()).webPanels,
|
|
539
1079
|
// Same board write (and attention-change throttle bypass) as report_progress.
|
|
540
1080
|
reportBlocked: (reason) => handlers.reportProgress(reason, "blocked"),
|
|
541
|
-
sendAttention: (input) => options?.push ? options.push.sendAttention({ ...input, sessionTitle: pushNotifications.getSessionNotificationBody(client.getMetadata()) }) : Promise.resolve({ sent: false, error: "push is not available in this runner" })
|
|
1081
|
+
sendAttention: (input) => options?.push ? options.push.sendAttention({ ...input, sessionTitle: pushNotifications.getSessionNotificationBody(client.getMetadata()) }) : Promise.resolve({ sent: false, error: "push is not available in this runner" }),
|
|
1082
|
+
postBlocked: (reason, panel) => leadBoard.postBlocked(reason, panel)
|
|
542
1083
|
})
|
|
543
1084
|
};
|
|
544
1085
|
const contextPath = "/runtime-context/" + node_crypto.randomUUID();
|
|
@@ -612,7 +1153,9 @@ async function startHappyServer(client, options) {
|
|
|
612
1153
|
}
|
|
613
1154
|
return;
|
|
614
1155
|
}
|
|
1156
|
+
await leadBoard.ready(OWNER_BOARD_READY_MS);
|
|
615
1157
|
const mcp = createMcpServer(handlers, options, client.sessionId, client);
|
|
1158
|
+
if (leadBoard.toolsExposed()) registerOwnerBoardTools(mcp, leadBoard);
|
|
616
1159
|
try {
|
|
617
1160
|
const transport = new streamableHttp_js.StreamableHTTPServerTransport({
|
|
618
1161
|
sessionIdGenerator: void 0
|
|
@@ -657,11 +1200,19 @@ async function startHappyServer(client, options) {
|
|
|
657
1200
|
...reportProgressTool.AUTOMATION_TOOL_NAMES,
|
|
658
1201
|
...peerTools.SESSION_PEER_TOOL_NAMES,
|
|
659
1202
|
...reportProgressTool.PROJECT_TOOL_NAMES,
|
|
1203
|
+
// Auto-allow only; the tools themselves appear for a lead on a board server.
|
|
1204
|
+
...OWNER_BOARD_TOOL_NAMES,
|
|
660
1205
|
...options?.assistant && !process.env.VH_TEAM_SCOPE_FILE ? downloadTarget.ASSISTANT_TOOL_NAMES : []
|
|
661
1206
|
],
|
|
1207
|
+
leadBoard,
|
|
662
1208
|
stop: () => {
|
|
663
1209
|
logger.logger.debug(`[happyMCP] server:stop sessionId=${client.sessionId}`);
|
|
664
1210
|
if (adoptionEnabled) client.rpcHandlerManager.unregisterHandler("teams-adopt");
|
|
1211
|
+
if (tapped) {
|
|
1212
|
+
client.off("user-message-routed", onRoutedUserMessage);
|
|
1213
|
+
client.off("claude-session-message", onClaudeLine);
|
|
1214
|
+
client.off("turn-ended", onTurnEnded);
|
|
1215
|
+
}
|
|
665
1216
|
server.close();
|
|
666
1217
|
}
|
|
667
1218
|
};
|
|
@@ -755,6 +1306,7 @@ function hashObject(obj, options, encoding = "hex") {
|
|
|
755
1306
|
|
|
756
1307
|
exports.createProjectInjector = createProjectInjector;
|
|
757
1308
|
exports.hashObject = hashObject;
|
|
1309
|
+
exports.leadBoardFor = leadBoardFor;
|
|
758
1310
|
exports.readPiModelsJson = readPiModelsJson;
|
|
759
1311
|
exports.resolvePiContextUsage = resolvePiContextUsage;
|
|
760
1312
|
exports.startHappyServer = startHappyServer;
|