very-happy-cli 0.2.182 → 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-DLk3gMYH.mjs → AcpBackend-9c-Jeaxd.mjs} +3 -3
- package/dist/{AcpBackend-JA5eu8Y0.cjs → AcpBackend-B-aMuEzO.cjs} +3 -3
- package/dist/{AcpSessionManager-DjYidq4C.cjs → AcpSessionManager-BxXe24AV.cjs} +10 -3
- package/dist/{AcpSessionManager-OrCww-Qv.mjs → AcpSessionManager-CS0FOSQk.mjs} +10 -3
- package/dist/{BasePermissionHandler-u-neo-iN.mjs → BasePermissionHandler-BJ1ID_Br.mjs} +1 -1
- package/dist/{BasePermissionHandler-D3PMKLBS.cjs → BasePermissionHandler-BREjppjP.cjs} +1 -1
- package/dist/{acpSessionResume-u2TwX5Jd.mjs → acpSessionResume-BKtI9Jq_.mjs} +1 -1
- package/dist/{acpSessionResume-Dxo8222T.cjs → acpSessionResume-mVoJ75vI.cjs} +1 -1
- package/dist/{agentHome-D3hNrnoA.mjs → agentHome-B8Ctr_Nu.mjs} +4 -4
- package/dist/{agentHome-CP1tCBXz.cjs → agentHome-rzjm56o4.cjs} +4 -4
- package/dist/{api-C_YSQnsi.mjs → api-C29WLYkW.mjs} +79 -12
- package/dist/{api-CZWcG7uh.cjs → api-xZiqVynF.cjs} +81 -11
- package/dist/{apiMachine-DPGrED5C.cjs → apiMachine-B-FNBIYG.cjs} +14 -14
- package/dist/{apiMachine-C_GA5Bm-.mjs → apiMachine-Dz6Yfk0m.mjs} +14 -14
- package/dist/{attachmentContent-I_dJIevE.cjs → attachmentContent-COV4LF8k.cjs} +2 -2
- package/dist/{attachmentContent-Ckzyb1Uy.mjs → attachmentContent-DwY0uIg6.mjs} +2 -2
- package/dist/{auth-Lw_utSMV.cjs → auth-CYtk9_ga.cjs} +5 -5
- package/dist/{auth-BKnE7uv7.cjs → auth-D_qsnVr3.cjs} +8 -8
- package/dist/{auth-DOXMI-xp.mjs → auth-DvbSDalm.mjs} +8 -8
- package/dist/{auth-CGgpiJ5-.mjs → auth-xT70IYMx.mjs} +5 -5
- package/dist/{auto-CIgFHIyQ.cjs → auto-DT_PfpCx.cjs} +32 -25
- package/dist/{auto-Z56mMZnH.mjs → auto-IiPqvIsN.mjs} +32 -26
- package/dist/{bootstrap-DsXp74wv.cjs → bootstrap-C50NkfPx.cjs} +2 -2
- package/dist/{bootstrap-D3vlOROj.mjs → bootstrap-DS8Acdhm.mjs} +2 -2
- package/dist/{browser-CUxGoATV.mjs → browser-1j9gdZoD.mjs} +1 -1
- package/dist/{browser-CZlA9AkO.cjs → browser-CxKZLd2T.cjs} +1 -1
- package/dist/{claudeLocal-oELvwiZL.cjs → claudeLocal-CgNWpepB.cjs} +5 -5
- package/dist/{claudeLocal-FnTUgwCi.mjs → claudeLocal-U1Fa9sJo.mjs} +5 -5
- package/dist/{claudeLocalLauncher-CO5YHCzi.mjs → claudeLocalLauncher-BjCsfZUE.mjs} +10 -10
- package/dist/{claudeLocalLauncher-QsVXEVdE.cjs → claudeLocalLauncher-IyFm0D6a.cjs} +10 -10
- package/dist/{claudeSessionFork-Cspr6QW0.mjs → claudeSessionFork-6-1vKISn.mjs} +1 -1
- package/dist/{claudeSessionFork-BA4GE5AL.cjs → claudeSessionFork-BUO1XdSm.cjs} +1 -1
- package/dist/{client-xnWiHqdb.cjs → client--vZueZYF.cjs} +4 -4
- package/dist/{client-o-meK7-a.mjs → client-5_zDNdOh.mjs} +2 -2
- package/dist/{client-Ctbw05Xj.mjs → client-BgHvK7lo.mjs} +15 -7
- package/dist/{client-CDuW4MS9.cjs → client-DYGDBD0M.cjs} +15 -7
- package/dist/{client-DK8CzUry.cjs → client-DcKJ0ul_.cjs} +2 -2
- package/dist/{client-BGfmSbWc.mjs → client-DfDyE322.mjs} +4 -4
- package/dist/codex/happyMcpStdioBridge.cjs +16 -16
- package/dist/codex/happyMcpStdioBridge.mjs +16 -16
- package/dist/{codexAppServerClient-DhNDTU_i.cjs → codexAppServerClient-C3nrTfwY.cjs} +3 -3
- package/dist/{codexAppServerClient-BlsOZLul.mjs → codexAppServerClient-DvBg4hGc.mjs} +3 -3
- package/dist/{codexCommand-BGx5NMQR.mjs → codexCommand-Bqp2--g_.mjs} +94 -60
- package/dist/{codexCommand-DJDPfpIs.cjs → codexCommand-DSiU6MNt.cjs} +94 -60
- package/dist/{codexTermination-CqPMuokw.cjs → codexTermination-D9TRTglF.cjs} +1 -1
- package/dist/{codexTermination-CVhhwHZU.mjs → codexTermination-DsliWl_s.mjs} +1 -1
- package/dist/{config-CHpxzXBL.cjs → config-CRhWiOwi.cjs} +3 -3
- package/dist/{config-CWAe08-Q.mjs → config-DeaqRn3o.mjs} +3 -3
- package/dist/{configuration-B-CugKJ3.cjs → configuration-K3I6yOjZ.cjs} +1 -1
- package/dist/{configuration-YXw_Dcsb.mjs → configuration-elzht62L.mjs} +1 -1
- package/dist/{connect-BmHvMXH2.cjs → connect-B5UbfmO3.cjs} +13 -13
- package/dist/{connect-CPeJXHha.mjs → connect-CT5cOlnC.mjs} +13 -13
- package/dist/{context-Drzs_Z38.mjs → context-CLj1_RYF.mjs} +1 -1
- package/dist/{context-BtIVqHI5.cjs → context-DNKNF8Yh.cjs} +1 -1
- package/dist/{controlClient-D0kGwKYx.cjs → controlClient-ChqmxgbB.cjs} +3 -3
- package/dist/{controlClient-BL4hb4l3.mjs → controlClient-CuYQs2aZ.mjs} +3 -3
- package/dist/{createSessionMetadata-DvGDsnsn.mjs → createSessionMetadata-C1MbLQnh.mjs} +2 -2
- package/dist/{createSessionMetadata-L499emJn.cjs → createSessionMetadata-QfiRx4Tf.cjs} +2 -2
- package/dist/daemonSupervisor-57JYYzMf.cjs +268 -0
- package/dist/daemonSupervisor-DfzrtNhM.mjs +245 -0
- package/dist/{defaultPermissionMode-MXpZlXud.cjs → defaultPermissionMode-DQmdZZIg.cjs} +2 -2
- package/dist/{defaultPermissionMode-BLfxu-Yz.mjs → defaultPermissionMode-U9LuUeQh.mjs} +2 -2
- package/dist/{deterministicJson-CfLkBRvU.cjs → deterministicJson-BBRUcB8i.cjs} +569 -17
- package/dist/{deterministicJson-B_UAbmQ4.mjs → deterministicJson-BixlyPWV.mjs} +572 -21
- package/dist/{doctor-DMvlzmnE.cjs → doctor-CKgHUV4W.cjs} +7 -7
- package/dist/{doctor-bKScPtZu.mjs → doctor-Dban2oE2.mjs} +7 -7
- package/dist/{downloadTarget-D33BFvew.cjs → downloadTarget-CmC11Y7E.cjs} +6 -6
- package/dist/{downloadTarget-J9j37eFG.mjs → downloadTarget-VUTd9lEO.mjs} +6 -6
- package/dist/{ensureDaemonRunning-2IqWROJp.cjs → ensureDaemonRunning-L0fl0cLE.cjs} +5 -5
- package/dist/{ensureDaemonRunning-DLvwhpNg.mjs → ensureDaemonRunning-khb8Nr1n.mjs} +5 -5
- package/dist/{handleResumeCommand-CjtjQ_I7.mjs → handleResumeCommand-CLDGWpTJ.mjs} +5 -5
- package/dist/{handleResumeCommand-Dp2Y-p6V.cjs → handleResumeCommand-D9myBN2N.cjs} +5 -5
- package/dist/{index-BOT0Tg5E.mjs → index-BMZx-699.mjs} +4 -4
- package/dist/{index-CZC7egJA.cjs → index-BdJs0465.cjs} +3 -3
- package/dist/{index-BI0h2Zks.mjs → index-BptfFoXM.mjs} +268 -77
- package/dist/{index-DymOv3kK.cjs → index-COSjJPIv.cjs} +4 -4
- package/dist/{index-D_LvOWUN.mjs → index-CtwqBeH6.mjs} +3 -3
- package/dist/{index-DYVIbhAJ.cjs → index-DgA79dVw.cjs} +40 -40
- package/dist/{index-oh8XEozJ.cjs → index-Fq-jxjsB.cjs} +277 -76
- package/dist/{index-Cllzqoes.mjs → index-GkkbUZX-.mjs} +4 -4
- package/dist/{index-kA46KMxN.mjs → index-IRI8s8kz.mjs} +41 -41
- package/dist/{index-B_aNaW9w.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-C46mkr9a.mjs → install-JvCk2PJV.mjs} +3 -3
- package/dist/{install-BAa_Xmof.cjs → install-cn98bTCs.cjs} +3 -3
- package/dist/lib.cjs +12 -12
- package/dist/lib.d.cts +1 -1
- package/dist/lib.d.mts +1 -1
- package/dist/lib.mjs +12 -12
- package/dist/{lidState-DcPvwniz.cjs → lidState-Bcs5kRHi.cjs} +1 -1
- package/dist/{lidState-D8Iuvjgl.mjs → lidState-DXeYTsrm.mjs} +1 -1
- package/dist/{localHappyAgentAuth-D3rQDRd9.mjs → localHappyAgentAuth-C1A4ChCR.mjs} +1 -1
- package/dist/{localHappyAgentAuth-SD6GM97y.cjs → localHappyAgentAuth-CypTSe42.cjs} +1 -1
- package/dist/{logger-DK1ytCz6.cjs → logger-Cgwz30rK.cjs} +2 -2
- package/dist/{logger-DpqihN1f.mjs → logger-RMPh5aqe.mjs} +2 -2
- package/dist/{machineMetadata-IJc6_iyx.cjs → machineMetadata-BSOGMNRi.cjs} +3 -3
- package/dist/{machineMetadata-D4E3qoIG.mjs → machineMetadata-BTUtPRth.mjs} +3 -3
- package/dist/{main-DUXh2rgW.cjs → main-4iiwYpCR.cjs} +61 -56
- package/dist/{main-CpUmw7hm.mjs → main-DNDL6t8z.mjs} +61 -56
- package/dist/{mcp-D5f3Vd-z.cjs → mcp-C7eEoGhz.cjs} +15 -15
- package/dist/{mcp-66g1TJ8P.mjs → mcp-DXYDPOpJ.mjs} +15 -15
- package/dist/{mirrorProtocol-bsCUcaCY.mjs → mirrorProtocol-DQRy5PaT.mjs} +3 -3
- package/dist/{mirrorProtocol-DkWOcZh9.cjs → mirrorProtocol-c5g5MgNc.cjs} +3 -3
- package/dist/{package-BfeqAzyJ.cjs → package-3aPiSpZj.cjs} +7 -7
- package/dist/{package-CKj8zndE.mjs → package-CTrz6U3C.mjs} +7 -7
- package/dist/{panels-r8LmaBs6.mjs → panels-B5sZ84vA.mjs} +5 -5
- package/dist/{panels-2PXsw9-d.cjs → panels-BmsmKHbw.cjs} +5 -5
- package/dist/{peerTools-D_id4d41.mjs → peerTools-DIPOX033.mjs} +5 -5
- package/dist/{peerTools-BVp1z0T_.cjs → peerTools-Dxe7iCi_.cjs} +5 -5
- package/dist/{persistence-BdpwWXKh.cjs → persistence-DZRMUZZS.cjs} +2 -2
- package/dist/{persistence-jOEuAFTS.mjs → persistence-zJo4r7YI.mjs} +2 -2
- package/dist/{piRunnerArgs-vxCHKnSA.cjs → piRunnerArgs-BSPiS6tZ.cjs} +1 -1
- package/dist/{piRunnerArgs-68I5F_M1.mjs → piRunnerArgs-IVdoHUB6.mjs} +1 -1
- package/dist/{piRuntime-C3Dg5uni.cjs → piRuntime-BJzjlMcN.cjs} +2 -2
- package/dist/{piRuntime-DgLIard1.mjs → piRuntime-BvHO3vgO.mjs} +2 -2
- package/dist/{piTerminal-C135BcLP.cjs → piTerminal-Cz4pW55d.cjs} +18 -18
- package/dist/{piTerminal-DJL0w-uA.mjs → piTerminal-D7vBOpkE.mjs} +18 -18
- package/dist/{pushNotifications-CMmRsbKv.mjs → pushNotifications-C4R9-1hU.mjs} +3 -3
- package/dist/{pushNotifications-CyQVDThC.cjs → pushNotifications-CgIrYml3.cjs} +3 -3
- package/dist/{query-CK--1fbL.cjs → query-CZQG1PdU.cjs} +1 -1
- package/dist/{query-CPXnDURZ.mjs → query-DKPmDsHS.mjs} +1 -1
- package/dist/{rateLimitWindows-DJxnNw-X.cjs → rateLimitWindows-CPV0nLeq.cjs} +1 -1
- package/dist/{rateLimitWindows-Cqa4AGaB.mjs → rateLimitWindows-HlHATOQ0.mjs} +1 -1
- package/dist/{registerCommonHandlers-DbNoM39q.cjs → registerCommonHandlers-D1pC83Op.cjs} +2 -2
- package/dist/{registerCommonHandlers-K8dICQO2.mjs → registerCommonHandlers-a9lHojSV.mjs} +2 -2
- package/dist/{registerKillSessionHandler-O1iriOMO.mjs → registerKillSessionHandler-Bp-fuH-w.mjs} +6 -6
- package/dist/{registerKillSessionHandler-DiS_go8d.cjs → registerKillSessionHandler-nTi2vVJy.cjs} +6 -6
- package/dist/{remoteSessionClient-BlURPulk.mjs → remoteSessionClient-Cwrddscy.mjs} +5 -5
- package/dist/{remoteSessionClient-CtV9mDko.cjs → remoteSessionClient-HQAKAvVd.cjs} +5 -5
- package/dist/{reportProgressTool-DlD_xjzU.cjs → reportProgressTool-Be-8ADI4.cjs} +4 -4
- package/dist/{reportProgressTool-nCExrHZH.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-drtwHYkO.mjs → run-BBU8GyxE.mjs} +59 -243
- package/dist/{run-BBNrcCfI.cjs → run-CxRJk9qq.cjs} +69 -254
- package/dist/{runClaude-CFp27Tyi.mjs → runClaude-DR6pEQwd.mjs} +22 -22
- package/dist/{runClaude-B3KSeby4.cjs → runClaude-Dd5zJaUZ.cjs} +22 -22
- package/dist/{runClaudeDeps-DMcv93lA.mjs → runClaudeDeps-COgZaUZm.mjs} +50 -45
- package/dist/{runClaudeDeps-C_C9TCI0.cjs → runClaudeDeps-CTtZ_1mC.cjs} +45 -40
- package/dist/{runGemini-Bc6SY-hd.mjs → runGemini-DVoVUfPp.mjs} +37 -35
- package/dist/{runGemini-DzdSBAr9.cjs → runGemini-DX72qB92.cjs} +37 -35
- package/dist/{runOpenClaw-DI0qMlgF.mjs → runOpenClaw-BHjzmzeC.mjs} +18 -18
- package/dist/{runOpenClaw-DN6rEVqC.cjs → runOpenClaw-BSg4hFVt.cjs} +18 -18
- package/dist/{sandbox-v-ffe8LR.mjs → sandbox-CWR9EqoN.mjs} +4 -4
- package/dist/{sandbox-DtttPBMA.cjs → sandbox-D1T_hmgZ.cjs} +4 -4
- package/dist/{selfCheck-B-yy6bW3.cjs → selfCheck-CE7vlnNS.cjs} +36 -36
- package/dist/selfCheck-ZGgBtxFQ.mjs +85 -0
- package/dist/{send-DpBwSZuj.mjs → send-BDF0Zvxo.mjs} +8 -8
- package/dist/{send-ShzXYPRu.cjs → send-BxZqRCcG.cjs} +8 -8
- package/dist/{server-DScgJXxa.mjs → server-B4JtnDLF.mjs} +4 -4
- package/dist/{server-CEzKrfKj.cjs → server-DB7_70Mi.cjs} +6 -6
- package/dist/{sessionLock-Dc0fadGG.cjs → sessionLock-BXvFN6uw.cjs} +3 -3
- package/dist/{sessionLock-B6K4SBZD.mjs → sessionLock-CXOOX-VD.mjs} +3 -3
- package/dist/{sessionOps-Dx7wARjI.cjs → sessionOps-BWlDlFYP.cjs} +4 -4
- package/dist/{sessionOps-D0BPWPoh.mjs → sessionOps-ByVggB_q.mjs} +4 -4
- package/dist/{sessionScanner-BJ35TVG6.cjs → sessionScanner-DkxPB5VA.cjs} +3 -3
- package/dist/{sessionScanner-9Fpgaxtf.mjs → sessionScanner-r66RtF95.mjs} +3 -3
- package/dist/{sessions-TZGGAry1.cjs → sessions-BiJTxFy9.cjs} +10 -10
- package/dist/{sessions-CFtDlyZd.mjs → sessions-C1lLK_a9.mjs} +10 -10
- package/dist/{setupOfflineReconnection-Biaus-0T.mjs → setupOfflineReconnection-Bl3DuakT.mjs} +2 -2
- package/dist/{setupOfflineReconnection-B1GsVA8P.cjs → setupOfflineReconnection-CecTiFZ-.cjs} +2 -2
- package/dist/{spawn-CsoQ-Ecz.mjs → spawn-D-8MlEM6.mjs} +14 -14
- package/dist/{spawn-BRTuypQ2.cjs → spawn-D79mVa3h.cjs} +14 -14
- package/dist/{spawnAgents-Co-SLcx-.mjs → spawnAgents-DJHjq01u.mjs} +1 -1
- package/dist/{spawnAgents-CG211IW7.cjs → spawnAgents-Sfnf-4pN.cjs} +1 -1
- package/dist/{spawnHappyCLI-D47kX6jg.cjs → spawnHappyCLI-CQwQs9G5.cjs} +1 -1
- package/dist/{spawnHappyCLI-D1doCjkZ.mjs → spawnHappyCLI-v_bJwZ4Z.mjs} +1 -1
- package/dist/{specialCommands-Ch-Gvy-i.cjs → specialCommands-CWMVfT5s.cjs} +3 -3
- package/dist/{specialCommands-xSmqT_Fu.mjs → specialCommands-yMnFvS-W.mjs} +3 -3
- package/dist/{startFileWatcher-CCl2fFxT.mjs → startFileWatcher-BCDkT_tr.mjs} +1 -1
- package/dist/{startFileWatcher-gKQTstR6.cjs → startFileWatcher-GW4_E7dV.cjs} +1 -1
- package/dist/{startupSessionCreate-DlZc8yPl.mjs → startupSessionCreate-BcAGYo51.mjs} +1 -1
- package/dist/{startupSessionCreate-BXqoUX30.cjs → startupSessionCreate-tBRa_ze8.cjs} +1 -1
- package/dist/{streamRelay-DnJXsi36.mjs → streamRelay-BEDiy6_b.mjs} +1 -1
- package/dist/{streamRelay-B-J-4St1.cjs → streamRelay-BZ8drtof.cjs} +1 -1
- package/dist/{sync-BbjMC3sR.mjs → sync--QUIOuf8.mjs} +1 -1
- package/dist/{sync-BxMdnZzz.cjs → sync-BMDkyo75.cjs} +1 -1
- package/dist/{systemPrompt-C2KojRmK.cjs → systemPrompt-DoHSXrH0.cjs} +2 -2
- package/dist/{systemPrompt-CmSDAoIx.mjs → systemPrompt-O0Kd6vbA.mjs} +2 -2
- package/dist/{teams-DESdalox.cjs → teams-BkZop-sP.cjs} +9 -9
- package/dist/{teams-lRy1MS0Z.mjs → teams-CTF1gT48.mjs} +9 -9
- package/dist/{template-BaHAmH2K.cjs → template-CeC1dYe-.cjs} +32 -2
- package/dist/{template-CiOPa2rd.mjs → template-D1fm22em.mjs} +31 -3
- package/dist/{time-Cfknl7xG.mjs → time-B2KKdAym.mjs} +1 -1
- package/dist/{time-DNLeIRgR.cjs → time-CdCotzKU.cjs} +1 -1
- package/dist/{titleGenerator-ClnMSzwC.mjs → titleGenerator-BFDczskR.mjs} +1 -1
- package/dist/{titleGenerator-YNutaU88.cjs → titleGenerator-DZH1XqAf.cjs} +2 -2
- package/dist/{todo-DZbK5POc.mjs → todo-C2gyOUom.mjs} +6 -6
- package/dist/{todo-DS75Mboz.cjs → todo-DnTFTHmV.cjs} +6 -6
- package/dist/{tools-ClYcb1YQ.cjs → tools-C8rpwyIl.cjs} +1 -1
- package/dist/{tools-BgdU0dWp.mjs → tools-DUjkqSQL.mjs} +1 -1
- package/dist/{uninstall-bXenFevR.mjs → uninstall-B5Ew50mZ.mjs} +3 -3
- package/dist/{uninstall-Di1vPBLV.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-BDfTwxqB.mjs +0 -85
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
import { l as logger } from './logger-
|
|
1
|
+
import { l as logger } from './logger-RMPh5aqe.mjs';
|
|
2
2
|
import { p as prependToMessage, b as buildDigestLine, a as buildFirstTurnPrefix } from './prompt-QHuFx9j6.mjs';
|
|
3
|
-
import { P as ProjectsUnsupportedError } from './client-
|
|
4
|
-
import { c as createSelfReportState, A as AUTOMATION_TOOL_NAMES, P as PROJECT_TOOL_NAMES, r as registerAutomationTools, a as registerProjectTools, d as createProjectToolExecutor, b as registerReportProgressTool, n as normalizeAttention, e as normalizePhase, f as normalizeProgress, s as shouldAcceptSelfReport } from './reportProgressTool-
|
|
3
|
+
import { P as ProjectsUnsupportedError } from './client-5_zDNdOh.mjs';
|
|
4
|
+
import { c as createSelfReportState, A as AUTOMATION_TOOL_NAMES, P as PROJECT_TOOL_NAMES, r as registerAutomationTools, a as registerProjectTools, d as createProjectToolExecutor, b as registerReportProgressTool, n as normalizeAttention, e as normalizePhase, f as normalizeProgress, s as shouldAcceptSelfReport } from './reportProgressTool-CcZ32nrd.mjs';
|
|
5
5
|
import { readFileSync, constants } from 'node:fs';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { join, dirname } from 'node:path';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { p as piAgentDir } from './pi-CqyoZMjI.mjs';
|
|
10
10
|
import { unlink, mkdir, open, rename } from 'node:fs/promises';
|
|
11
|
-
import { randomUUID } from 'node:crypto';
|
|
12
|
-
import { T as TeamConnectionPreflightError, S as SessionInOtherTeamError, c as createTeamsClient } from './client-
|
|
13
|
-
import { t as teamScopePath } from './context-
|
|
14
|
-
import { g as waitForSessionKey, e as sendUserMessage } from './sessionOps-
|
|
15
|
-
import { T as TEAM_SKILL, a as TEAM_LEAD_PLAYBOOK } from './resources-
|
|
16
|
-
import { T as TEAM_TOOL_NAMES, r as registerTeamsTools } from './tools-
|
|
17
|
-
import { S as SESSION_PEER_TOOL_NAMES, r as registerSessionPeerTools, a as createSessionPeerToolExecutor } from './peerTools-
|
|
11
|
+
import { randomUUID, createHash } from 'node:crypto';
|
|
12
|
+
import { T as TeamConnectionPreflightError, S as SessionInOtherTeamError, c as createTeamsClient, t as teamRequest } from './client-DfDyE322.mjs';
|
|
13
|
+
import { t as teamScopePath, r as readTeamScope } from './context-CLj1_RYF.mjs';
|
|
14
|
+
import { g as waitForSessionKey, e as sendUserMessage } from './sessionOps-ByVggB_q.mjs';
|
|
15
|
+
import { T as TEAM_SKILL, a as TEAM_LEAD_PLAYBOOK } from './resources-CLdQfe3b.mjs';
|
|
16
|
+
import { T as TEAM_TOOL_NAMES, r as registerTeamsTools } from './tools-DUjkqSQL.mjs';
|
|
17
|
+
import { S as SESSION_PEER_TOOL_NAMES, r as registerSessionPeerTools, a as createSessionPeerToolExecutor } from './peerTools-DIPOX033.mjs';
|
|
18
18
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
19
19
|
import { createServer } from 'node:http';
|
|
20
20
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
21
21
|
import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, c as CLIPBOARD_MAX_BYTES } from './limits-DIo7rEBR.mjs';
|
|
22
|
-
import { A as ASSISTANT_TOOL_NAMES, a as registerAssistantTools, c as checkDownloadTarget } from './downloadTarget-
|
|
22
|
+
import { A as ASSISTANT_TOOL_NAMES, a as registerAssistantTools, c as checkDownloadTarget } from './downloadTarget-VUTd9lEO.mjs';
|
|
23
23
|
import { P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, c as PREVIEW_DOWNLOAD_PARAM_DESCRIPTION } from './agentGuidance-BDYt2Qy2.mjs';
|
|
24
24
|
import { n as normalizeWebPanels, w as webPanelPath } from './webPanels-B2_C2-3g.mjs';
|
|
25
|
-
import { r as readSettings } from './persistence-
|
|
26
|
-
import { g as getSessionNotificationBody } from './pushNotifications-
|
|
25
|
+
import { b as readCredentialsForConfiguredRelay, r as readSettings } from './persistence-zJo4r7YI.mjs';
|
|
26
|
+
import { g as getSessionNotificationBody } from './pushNotifications-C4R9-1hU.mjs';
|
|
27
27
|
import { c as checkPreviewPath } from './previewPath-CXN4Vag0.mjs';
|
|
28
|
-
import {
|
|
28
|
+
import { x as ATTENTION_DISABLED_ERROR, y as ATTENTION_LIMITS, z as ownerBoardPostToRest, C as isAttentionBoardLocalId, O as OwnerBoardPostInputSchema, D as OwnerBoardUpdateInputSchema, E as OwnerBoardResolveInputSchema, F as OwnerBoardListInputSchema, G as ATTENTION_SESSION_CAPABILITY } from './index-BptfFoXM.mjs';
|
|
29
|
+
import { c as configuration } from './configuration-elzht62L.mjs';
|
|
30
|
+
import { createHash as createHash$1 } from 'crypto';
|
|
29
31
|
|
|
30
32
|
function createProjectInjector(deps) {
|
|
31
33
|
const timeoutMs = deps.timeoutMs ?? 1500;
|
|
@@ -301,10 +303,6 @@ function createRequestHuman(deps) {
|
|
|
301
303
|
const reason = rawReason.replace(/\s+/g, " ").trim().slice(0, REQUEST_HUMAN_REASON_MAX);
|
|
302
304
|
if (!reason) return { notified: false, error: "reason must be a non-empty string" };
|
|
303
305
|
await deps.reportBlocked(reason);
|
|
304
|
-
const at = now();
|
|
305
|
-
if (last && last.reason === reason && at - last.at < REQUEST_HUMAN_DEDUPE_MS) {
|
|
306
|
-
return { notified: true, deduped: true };
|
|
307
|
-
}
|
|
308
306
|
let url = `/session/${encodeURIComponent(deps.sessionId)}`;
|
|
309
307
|
let unknownPanel;
|
|
310
308
|
if (panel) {
|
|
@@ -313,18 +311,534 @@ function createRequestHuman(deps) {
|
|
|
313
311
|
if (machineId && panels.some((p) => p.id === panel)) url = webPanelPath(machineId, panel);
|
|
314
312
|
else unknownPanel = panel;
|
|
315
313
|
}
|
|
314
|
+
if (deps.postBlocked && await deps.postBlocked(reason, unknownPanel ? void 0 : panel).catch(() => false)) {
|
|
315
|
+
return { notified: true, board: true, ...unknownPanel ? { unknownPanel } : {} };
|
|
316
|
+
}
|
|
317
|
+
const at = now();
|
|
318
|
+
if (last && last.reason === reason && at - last.at < REQUEST_HUMAN_DEDUPE_MS) {
|
|
319
|
+
return { notified: true, deduped: true };
|
|
320
|
+
}
|
|
316
321
|
const sent = await deps.sendAttention({ sessionId: deps.sessionId, reason, url });
|
|
317
322
|
if (sent.sent) last = { reason, at };
|
|
318
323
|
return { notified: sent.sent, url, ...unknownPanel ? { unknownPanel } : {}, ...sent.error ? { error: sent.error } : {} };
|
|
319
324
|
};
|
|
320
325
|
}
|
|
321
326
|
function requestHumanResultText(r) {
|
|
327
|
+
if (r.board) {
|
|
328
|
+
const panelNote2 = r.unknownPanel ? ` (panel "${r.unknownPanel}" is not declared on this machine, so it is not linked)` : "";
|
|
329
|
+
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.`;
|
|
330
|
+
}
|
|
322
331
|
if (r.deduped) return "Already asked for this in the last minute; the board still shows it. Keep polling your completion condition.";
|
|
323
332
|
if (!r.notified) return `Could not notify the user: ${r.error ?? "unknown error"}. The task board is marked blocked.`;
|
|
324
333
|
const panelNote = r.unknownPanel ? ` (panel "${r.unknownPanel}" is not declared on this machine, so the notification opens the session instead)` : "";
|
|
325
334
|
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.`;
|
|
326
335
|
}
|
|
327
336
|
|
|
337
|
+
const UNSUPPORTED_BACKOFF_MS = 10 * 6e4;
|
|
338
|
+
class AttentionUnsupportedError extends Error {
|
|
339
|
+
constructor() {
|
|
340
|
+
super("This Very Happy server has no briefing board");
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
class AttentionRequestError extends Error {
|
|
344
|
+
constructor(status, code) {
|
|
345
|
+
super(code ? `Briefing board request failed: ${code}` : `Briefing board request failed (HTTP ${status})`);
|
|
346
|
+
this.status = status;
|
|
347
|
+
this.code = code;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function isItemErrorCode(code) {
|
|
351
|
+
return typeof code === "string" && code.startsWith("attention_") || code === "session_not_found";
|
|
352
|
+
}
|
|
353
|
+
function createAttentionClient(deps = {}) {
|
|
354
|
+
const now = deps.now ?? Date.now;
|
|
355
|
+
const doFetch = deps.fetch ?? fetch;
|
|
356
|
+
const token = deps.token ?? (async () => (await readCredentialsForConfiguredRelay())?.token ?? null);
|
|
357
|
+
let disabledUntil = 0;
|
|
358
|
+
async function request(path, method, body, timeoutMs = 1e4) {
|
|
359
|
+
if (now() < disabledUntil) throw new AttentionUnsupportedError();
|
|
360
|
+
const bearer = await token();
|
|
361
|
+
if (!bearer) throw new AttentionRequestError(401, "not_signed_in");
|
|
362
|
+
const response = await doFetch(`${deps.serverUrl ?? configuration.serverUrl}${path}`, {
|
|
363
|
+
method,
|
|
364
|
+
redirect: "error",
|
|
365
|
+
headers: { authorization: `Bearer ${bearer}`, ...body === void 0 ? {} : { "content-type": "application/json" } },
|
|
366
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
367
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
368
|
+
});
|
|
369
|
+
let json = null;
|
|
370
|
+
try {
|
|
371
|
+
json = await response.json();
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
if (response.status === 404 && (json?.error === ATTENTION_DISABLED_ERROR || !isItemErrorCode(json?.error))) {
|
|
375
|
+
disabledUntil = now() + UNSUPPORTED_BACKOFF_MS;
|
|
376
|
+
throw new AttentionUnsupportedError();
|
|
377
|
+
}
|
|
378
|
+
if (!response.ok) throw new AttentionRequestError(response.status, typeof json?.error === "string" ? json.error : null);
|
|
379
|
+
return json;
|
|
380
|
+
}
|
|
381
|
+
const enc = encodeURIComponent;
|
|
382
|
+
return {
|
|
383
|
+
isDisabled: () => now() < disabledUntil,
|
|
384
|
+
/** True when this server serves the board for this account (feature flag on). Network errors throw. */
|
|
385
|
+
async probe(sessionId, timeoutMs = 5e3) {
|
|
386
|
+
try {
|
|
387
|
+
await request(`/v1/attention-items?sessionId=${enc(sessionId)}&limit=1`, "GET", void 0, timeoutMs);
|
|
388
|
+
return true;
|
|
389
|
+
} catch (error) {
|
|
390
|
+
if (error instanceof AttentionUnsupportedError) return false;
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
post: (input) => request("/v1/attention-items", "POST", input),
|
|
395
|
+
patch: (id, patch) => request(`/v1/attention-items/${enc(id)}`, "PATCH", patch),
|
|
396
|
+
resolve: (id, input) => request(`/v1/attention-items/${enc(id)}/resolve`, "POST", input),
|
|
397
|
+
list: (query, timeoutMs) => {
|
|
398
|
+
const params = new URLSearchParams();
|
|
399
|
+
for (const [key, value] of Object.entries(query)) if (value !== void 0) params.set(key, String(value));
|
|
400
|
+
return request(`/v1/attention-items?${params.toString()}`, "GET", void 0, timeoutMs);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
let shared = null;
|
|
405
|
+
function sharedAttentionClient() {
|
|
406
|
+
return shared ??= createAttentionClient();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const TRIGGERS = [
|
|
410
|
+
/需要你(?:来)?(?:决定|拍板|确认|选择|定夺|批准|授权|选)/,
|
|
411
|
+
/请你?(?:来)?(?:拍板|决定|定夺|确认一下|选择|选一个|选一下)/,
|
|
412
|
+
/(?:等你|由你|你来)(?:拍板|决定|定夺|确认|选)/,
|
|
413
|
+
/需要\s*owner\s*(?:决定|拍板|确认)/i,
|
|
414
|
+
/\b(?:need|needs|waiting for|awaiting)\s+(?:your|the owner'?s?)\s+(?:decision|call|approval|sign-?off|go-?ahead)\b/i,
|
|
415
|
+
/\b(?:i|we)\s+need\s+you\s+to\s+(?:decide|choose|pick|confirm|approve)\b/i,
|
|
416
|
+
/\bplease\s+(?:decide|choose|pick one|confirm which|approve)\b/i,
|
|
417
|
+
/<options>/i
|
|
418
|
+
];
|
|
419
|
+
const OPTION_LINE = /^\s*(?:[-*+]\s+)?(?:\*\*)?(?:选项|option\s*)?([A-Da-d]|[1-4])(?:\*\*)?\s*[.)、::)]\s*(?:\*\*)?\s*(\S.*?)\s*$/i;
|
|
420
|
+
const TAGGED_OPTION = /<option(?:\s+id="([^"]{1,16})")?\s*>([\s\S]*?)<\/option>/gi;
|
|
421
|
+
const RECOMMENDED = /推荐|建议选|\brecommend(?:ed)?\b|\(rec\)/i;
|
|
422
|
+
function stripCode(text) {
|
|
423
|
+
return text.replace(/```[\s\S]*?(?:```|$)/g, "\n").replace(/`[^`\n]*`/g, " ");
|
|
424
|
+
}
|
|
425
|
+
function clean(text, max) {
|
|
426
|
+
const flat = text.replace(/\*\*|__|^#+\s*/g, "").replace(/\s+/g, " ").trim();
|
|
427
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
428
|
+
}
|
|
429
|
+
function triggerSentence(text, match) {
|
|
430
|
+
const start = Math.max(text.lastIndexOf("\n", match.index), ...["\u3002", "\uFF01", "\uFF1F", ". ", "! ", "? "].map((p) => {
|
|
431
|
+
const at = text.lastIndexOf(p, match.index);
|
|
432
|
+
return at < 0 ? -1 : at + p.length - 1;
|
|
433
|
+
})) + 1;
|
|
434
|
+
const rest = text.slice(match.index);
|
|
435
|
+
const endRel = rest.search(/[。!?\n]|[.!?](?:\s|$)/);
|
|
436
|
+
const end = endRel < 0 ? text.length : match.index + endRel + 1;
|
|
437
|
+
return text.slice(start, end);
|
|
438
|
+
}
|
|
439
|
+
function optionLabel(raw) {
|
|
440
|
+
return clean(raw.replace(/<[^>]+>/g, ""), ATTENTION_LIMITS.optionLabelChars);
|
|
441
|
+
}
|
|
442
|
+
function parseOptions(text) {
|
|
443
|
+
const tagged = [...text.matchAll(TAGGED_OPTION)].slice(0, ATTENTION_LIMITS.options.max);
|
|
444
|
+
if (tagged.length >= ATTENTION_LIMITS.options.min) {
|
|
445
|
+
return tagged.map((m, i) => ({
|
|
446
|
+
id: (m[1] ?? String.fromCharCode(65 + i)).slice(0, ATTENTION_LIMITS.optionIdChars),
|
|
447
|
+
label: optionLabel(m[2]) || `Option ${i + 1}`,
|
|
448
|
+
...RECOMMENDED.test(m[2]) ? { recommended: true } : {}
|
|
449
|
+
}));
|
|
450
|
+
}
|
|
451
|
+
const groups = [];
|
|
452
|
+
let current = null;
|
|
453
|
+
for (const line of text.split("\n")) {
|
|
454
|
+
if (!line.trim()) continue;
|
|
455
|
+
const m = OPTION_LINE.exec(line);
|
|
456
|
+
if (!m) {
|
|
457
|
+
current = null;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const id = m[1].toUpperCase();
|
|
461
|
+
const letter = /[A-D]/.test(id);
|
|
462
|
+
if (!current || current.letter !== letter || current.lines.some((l) => l.id === id)) {
|
|
463
|
+
current = { letter, lines: [] };
|
|
464
|
+
groups.push(current);
|
|
465
|
+
}
|
|
466
|
+
current.lines.push({ id, label: optionLabel(m[2]), recommended: RECOMMENDED.test(m[2]) });
|
|
467
|
+
}
|
|
468
|
+
const usable = groups.filter((g) => g.lines.length >= ATTENTION_LIMITS.options.min && g.lines.every((l) => l.label));
|
|
469
|
+
const pick = usable.find((g) => g.letter) ?? usable[0];
|
|
470
|
+
if (!pick) return [];
|
|
471
|
+
return pick.lines.slice(0, ATTENTION_LIMITS.options.max).map((l) => ({ id: l.id, label: l.label, ...l.recommended ? { recommended: true } : {} }));
|
|
472
|
+
}
|
|
473
|
+
function defaultOptions(cjk) {
|
|
474
|
+
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)" }];
|
|
475
|
+
}
|
|
476
|
+
function inferOwnerDecision(text) {
|
|
477
|
+
const prose = stripCode(text);
|
|
478
|
+
let match = null;
|
|
479
|
+
for (const trigger of TRIGGERS) {
|
|
480
|
+
match = trigger.exec(prose);
|
|
481
|
+
if (match) break;
|
|
482
|
+
}
|
|
483
|
+
if (!match) return null;
|
|
484
|
+
const title = clean(triggerSentence(prose, match).replace(/<\/?options>/gi, ""), ATTENTION_LIMITS.titleChars);
|
|
485
|
+
if (!title) return null;
|
|
486
|
+
const parsed = parseOptions(text);
|
|
487
|
+
const options = parsed.length ? parsed : defaultOptions(/[㐀-鿿]/.test(title));
|
|
488
|
+
const digest = createHash("sha256").update(`${title}
|
|
489
|
+
${options.map((o) => `${o.id}:${o.label}`).join("\n")}`).digest("hex").slice(0, 16);
|
|
490
|
+
const encoder = new TextEncoder();
|
|
491
|
+
let bodyMd = text.trim();
|
|
492
|
+
while (encoder.encode(bodyMd).length > ATTENTION_LIMITS.bodyBytes / 2) bodyMd = `${bodyMd.slice(0, Math.floor(bodyMd.length * 0.8))}\u2026`;
|
|
493
|
+
return { title, options, key: `inferred-${digest}`, bodyMd };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const OWNER_BOARD_DIGEST_TIMEOUT_MS = 1500;
|
|
497
|
+
const QUESTION_TITLE_CHARS = 60;
|
|
498
|
+
const DIGEST_TITLE_CHARS = 40;
|
|
499
|
+
const DIGEST_MAX_CHARS = 480;
|
|
500
|
+
const LEAD_RECHECK_MS = 6e4;
|
|
501
|
+
const OWNER_SENT_FROM = ["web", "mobile", "ios", "android"];
|
|
502
|
+
const AUTOMATED_LOCAL_ID_PREFIXES = ["automation-", "teams-", "remote-send-", "session-message-", "edit-conflict-"];
|
|
503
|
+
function isOwnerMessage(input) {
|
|
504
|
+
const localId = input.localId ?? "";
|
|
505
|
+
if (isAttentionBoardLocalId(localId)) return false;
|
|
506
|
+
if (AUTOMATED_LOCAL_ID_PREFIXES.some((prefix) => localId.startsWith(prefix))) return false;
|
|
507
|
+
return typeof input.sentFrom === "string" && OWNER_SENT_FROM.includes(input.sentFrom);
|
|
508
|
+
}
|
|
509
|
+
function clip(text, max) {
|
|
510
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
511
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
512
|
+
}
|
|
513
|
+
function ownerQuestionTitle(text) {
|
|
514
|
+
const trimmed = text.trim();
|
|
515
|
+
if (!trimmed || trimmed.startsWith("/")) return null;
|
|
516
|
+
const first = trimmed.split("\n").find((line) => line.trim()) ?? "";
|
|
517
|
+
return clip(first, QUESTION_TITLE_CHARS) || null;
|
|
518
|
+
}
|
|
519
|
+
function minutesAgo(ms, now) {
|
|
520
|
+
const m = Math.max(0, Math.round((now - ms) / 6e4));
|
|
521
|
+
return m < 60 ? `${m}m` : `${Math.round(m / 60)}h`;
|
|
522
|
+
}
|
|
523
|
+
function formatBoardDigest(items, now, opts) {
|
|
524
|
+
if (items.length === 0) return null;
|
|
525
|
+
const parts = [];
|
|
526
|
+
let used = 0;
|
|
527
|
+
let shown = 0;
|
|
528
|
+
for (const item of items) {
|
|
529
|
+
const handle = item.key && !item.key.startsWith("req:") ? `key=${item.key}` : `id=${item.id}`;
|
|
530
|
+
const part = `${item.source === "owner" ? "owner " : ""}${item.kind} \u300C${clip(item.title, DIGEST_TITLE_CHARS)}\u300D ${handle} ${minutesAgo(item.createdAt, now)}`;
|
|
531
|
+
if (shown > 0 && used + part.length > DIGEST_MAX_CHARS) break;
|
|
532
|
+
parts.push(part);
|
|
533
|
+
used += part.length + 2;
|
|
534
|
+
shown++;
|
|
535
|
+
}
|
|
536
|
+
const more = items.length - shown;
|
|
537
|
+
const line1 = `[Briefing board] ${items.length} open: ${parts.join("; ")}${more > 0 ? `; +${more} more` : ""}`;
|
|
538
|
+
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;
|
|
539
|
+
return line2 ? `${line1}
|
|
540
|
+
${line2}` : line1;
|
|
541
|
+
}
|
|
542
|
+
function parseSince(since) {
|
|
543
|
+
if (since === void 0) return void 0;
|
|
544
|
+
if (/^\d+$/.test(since)) return Number(since);
|
|
545
|
+
const parsed = Date.parse(since);
|
|
546
|
+
if (Number.isNaN(parsed)) throw new Error("since must be ISO-8601 or epoch milliseconds");
|
|
547
|
+
return parsed;
|
|
548
|
+
}
|
|
549
|
+
async function resolveTeamLead(sessionId) {
|
|
550
|
+
try {
|
|
551
|
+
const scope = await readTeamScope(teamScopePath(sessionId));
|
|
552
|
+
if (!scope) return false;
|
|
553
|
+
const inspected = await teamRequest(`/v1/teams/${encodeURIComponent(scope.teamId)}/agent`, "GET", scope.scopeToken, true);
|
|
554
|
+
const bots = Array.isArray(inspected?.team?.bots) ? inspected.team.bots : [];
|
|
555
|
+
return bots.some((bot) => bot.id === scope.botId && bot.root === true);
|
|
556
|
+
} catch {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function createLeadBoard(deps) {
|
|
561
|
+
const now = deps.now ?? Date.now;
|
|
562
|
+
const log = deps.log ?? (() => {
|
|
563
|
+
});
|
|
564
|
+
let supported = false;
|
|
565
|
+
let lead = false;
|
|
566
|
+
let leadCheckedAt = 0;
|
|
567
|
+
let toolsFrozen = null;
|
|
568
|
+
let initPromise = null;
|
|
569
|
+
let initDone = false;
|
|
570
|
+
let lastLocalId = null;
|
|
571
|
+
let pending = [];
|
|
572
|
+
let turn = null;
|
|
573
|
+
let work = Promise.resolve();
|
|
574
|
+
const queue = (task) => {
|
|
575
|
+
work = work.then(task).catch((error) => log(`[ownerBoard] ${error instanceof Error ? error.message : String(error)}`));
|
|
576
|
+
return work;
|
|
577
|
+
};
|
|
578
|
+
async function refreshLead() {
|
|
579
|
+
if (lead || now() - leadCheckedAt < LEAD_RECHECK_MS) return lead;
|
|
580
|
+
leadCheckedAt = now();
|
|
581
|
+
lead = await deps.isLead().catch(() => false);
|
|
582
|
+
return lead;
|
|
583
|
+
}
|
|
584
|
+
function init() {
|
|
585
|
+
return initPromise ??= (async () => {
|
|
586
|
+
try {
|
|
587
|
+
supported = await deps.client.probe(deps.sessionId());
|
|
588
|
+
} catch (error) {
|
|
589
|
+
log(`[ownerBoard] probe failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
590
|
+
supported = false;
|
|
591
|
+
}
|
|
592
|
+
if (supported) {
|
|
593
|
+
deps.onSupported?.();
|
|
594
|
+
await refreshLead();
|
|
595
|
+
}
|
|
596
|
+
initDone = true;
|
|
597
|
+
})();
|
|
598
|
+
}
|
|
599
|
+
async function active() {
|
|
600
|
+
await init();
|
|
601
|
+
return supported && !deps.client.isDisabled() && await refreshLead();
|
|
602
|
+
}
|
|
603
|
+
function thisTurnAnchor() {
|
|
604
|
+
const localId = turn?.promptLocalId ?? lastLocalId;
|
|
605
|
+
if (!localId) return void 0;
|
|
606
|
+
const text = turn?.texts.at(-1);
|
|
607
|
+
return { localId, ...text ? { snippet: clip(text, ATTENTION_LIMITS.snippetChars) } : {} };
|
|
608
|
+
}
|
|
609
|
+
const noteBoardTool = (name) => {
|
|
610
|
+
turn?.boardTools.add(name);
|
|
611
|
+
};
|
|
612
|
+
async function settleQuestions(questions, reply) {
|
|
613
|
+
for (const question of questions) {
|
|
614
|
+
const id = await question.itemId;
|
|
615
|
+
if (!id) continue;
|
|
616
|
+
const anchor = { localId: question.localId, snippet: clip(reply, ATTENTION_LIMITS.snippetChars) };
|
|
617
|
+
const { item } = await deps.client.patch(id, { firstReply: anchor, requestId: `first-reply-${question.localId}`.slice(0, 128) });
|
|
618
|
+
if (item.state === "open") {
|
|
619
|
+
await deps.client.resolve(id, { outcome: "answered", by: "auto-first-reply", requestId: `auto-answered-${question.localId}`.slice(0, 128) });
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async function draftDecision(text, anchor) {
|
|
624
|
+
if (!await deps.autoDetect().catch(() => true)) return;
|
|
625
|
+
const draft = inferOwnerDecision(text);
|
|
626
|
+
if (!draft) return;
|
|
627
|
+
await deps.client.post({
|
|
628
|
+
sessionId: deps.sessionId(),
|
|
629
|
+
source: "lead",
|
|
630
|
+
kind: "decision",
|
|
631
|
+
inferred: true,
|
|
632
|
+
title: draft.title,
|
|
633
|
+
bodyMd: draft.bodyMd,
|
|
634
|
+
options: draft.options,
|
|
635
|
+
key: draft.key,
|
|
636
|
+
...anchor ? { anchor } : {},
|
|
637
|
+
requestId: randomUUID()
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
init,
|
|
642
|
+
/** Waits (bounded) for the first probe so the first `tools/list` sees the final tool set. */
|
|
643
|
+
async ready(timeoutMs) {
|
|
644
|
+
await Promise.race([init(), new Promise((resolve) => setTimeout(resolve, timeoutMs).unref?.())]);
|
|
645
|
+
},
|
|
646
|
+
/** Server serves the board (any session: request_human items, `owner-board-v1`). */
|
|
647
|
+
supported: () => supported && !deps.client.isDisabled(),
|
|
648
|
+
/**
|
|
649
|
+
* `owner_board_*` exposure, decided once per process: the tool list must
|
|
650
|
+
* not change under a running agent. A session adopted into a team later
|
|
651
|
+
* gets the tools on its next relaunch; the fallbacks start at once.
|
|
652
|
+
*/
|
|
653
|
+
toolsExposed() {
|
|
654
|
+
if (toolsFrozen === null && initDone) toolsFrozen = supported && lead;
|
|
655
|
+
return toolsFrozen ?? false;
|
|
656
|
+
},
|
|
657
|
+
// ─── tools ──────────────────────────────────────────────────────────
|
|
658
|
+
async post(input) {
|
|
659
|
+
noteBoardTool("owner_board_post");
|
|
660
|
+
const anchor = input.anchor === void 0 || input.anchor === "this_turn" ? thisTurnAnchor() : { localId: input.anchor.localId };
|
|
661
|
+
const { item, created } = await deps.client.post(ownerBoardPostToRest(deps.sessionId(), input, anchor));
|
|
662
|
+
return { id: item.id, created };
|
|
663
|
+
},
|
|
664
|
+
async update(input) {
|
|
665
|
+
noteBoardTool("owner_board_update");
|
|
666
|
+
return deps.client.patch(input.id, {
|
|
667
|
+
...input.title !== void 0 ? { title: input.title } : {},
|
|
668
|
+
...input.body_md !== void 0 ? { bodyMd: input.body_md } : {},
|
|
669
|
+
...input.append_md !== void 0 ? { appendMd: input.append_md } : {},
|
|
670
|
+
...input.options !== void 0 ? { options: input.options } : {},
|
|
671
|
+
...input.links !== void 0 ? { links: input.links } : {},
|
|
672
|
+
...input.state !== void 0 ? { state: input.state } : {},
|
|
673
|
+
requestId: input.request_id
|
|
674
|
+
});
|
|
675
|
+
},
|
|
676
|
+
async resolve(input) {
|
|
677
|
+
noteBoardTool("owner_board_resolve");
|
|
678
|
+
return deps.client.resolve(input.id, { outcome: input.outcome, ...input.note !== void 0 ? { note: input.note } : {}, by: "lead", requestId: input.request_id });
|
|
679
|
+
},
|
|
680
|
+
async list(input) {
|
|
681
|
+
const since = parseSince(input.since);
|
|
682
|
+
return deps.client.list({ sessionId: deps.sessionId(), state: input.state === "all" ? "all" : "open", limit: 50, ...since !== void 0 ? { since } : {} });
|
|
683
|
+
},
|
|
684
|
+
/**
|
|
685
|
+
* `request_human` as a `blocked` item (server dedupes by key and pushes
|
|
686
|
+
* once). False when this server has no board: the caller keeps the old
|
|
687
|
+
* metadata + push path.
|
|
688
|
+
*/
|
|
689
|
+
async postBlocked(reason, panel) {
|
|
690
|
+
noteBoardTool("request_human");
|
|
691
|
+
await init();
|
|
692
|
+
if (!supported || deps.client.isDisabled()) return false;
|
|
693
|
+
try {
|
|
694
|
+
await deps.client.post({
|
|
695
|
+
sessionId: deps.sessionId(),
|
|
696
|
+
source: "agent",
|
|
697
|
+
kind: "blocked",
|
|
698
|
+
title: clip(reason, ATTENTION_LIMITS.titleChars),
|
|
699
|
+
...reason.length > ATTENTION_LIMITS.titleChars ? { bodyMd: reason } : {},
|
|
700
|
+
key: `request-human-${createHash("sha256").update(reason).digest("hex").slice(0, 16)}`,
|
|
701
|
+
...panel ? { links: [{ type: "panel", ref: panel }] } : {},
|
|
702
|
+
...thisTurnAnchor() ? { anchor: thisTurnAnchor() } : {},
|
|
703
|
+
requestId: randomUUID()
|
|
704
|
+
});
|
|
705
|
+
return true;
|
|
706
|
+
} catch (error) {
|
|
707
|
+
if (!(error instanceof AttentionUnsupportedError)) log(`[ownerBoard] request_human item failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
},
|
|
711
|
+
// ─── message taps ───────────────────────────────────────────────────
|
|
712
|
+
/** Every routed user message (any runner). Owner messages in a lead session become `question` items. */
|
|
713
|
+
onUserMessage(message) {
|
|
714
|
+
if (message.localId) lastLocalId = message.localId;
|
|
715
|
+
if (!message.localId || !isOwnerMessage(message)) return;
|
|
716
|
+
const title = ownerQuestionTitle(message.text ?? "");
|
|
717
|
+
if (!title) return;
|
|
718
|
+
const localId = message.localId;
|
|
719
|
+
const itemId = (async () => {
|
|
720
|
+
if (!await active()) return null;
|
|
721
|
+
const { item } = await deps.client.post({
|
|
722
|
+
sessionId: deps.sessionId(),
|
|
723
|
+
source: "owner",
|
|
724
|
+
kind: "question",
|
|
725
|
+
title,
|
|
726
|
+
anchor: { localId, snippet: clip(message.text ?? "", ATTENTION_LIMITS.snippetChars) },
|
|
727
|
+
// No key: the server keys it by request id, so a replay after a restart is a no-op.
|
|
728
|
+
requestId: `owner-q-${localId}`.slice(0, 128)
|
|
729
|
+
});
|
|
730
|
+
return item.id;
|
|
731
|
+
})().catch((error) => {
|
|
732
|
+
log(`[ownerBoard] owner question not posted: ${error instanceof Error ? error.message : String(error)}`);
|
|
733
|
+
return null;
|
|
734
|
+
});
|
|
735
|
+
const question = { localId, itemId };
|
|
736
|
+
if (message.delivery === "steer" && turn) turn.questions.push(question);
|
|
737
|
+
else pending.push(question);
|
|
738
|
+
},
|
|
739
|
+
/**
|
|
740
|
+
* The agent takes queued input (Claude remote launcher, right before the
|
|
741
|
+
* prompt goes to the SDK). `sourceIds` = the batch's message localIds when
|
|
742
|
+
* known: only those questions belong to this turn. When none match (an
|
|
743
|
+
* id scheme we do not recognise) every waiting question does.
|
|
744
|
+
*/
|
|
745
|
+
onTurnStart(sourceIds) {
|
|
746
|
+
const ids = new Set(sourceIds ?? []);
|
|
747
|
+
const mine = pending.filter((q) => ids.has(q.localId));
|
|
748
|
+
const questions = ids.size && mine.length ? mine : pending;
|
|
749
|
+
pending = pending.filter((q) => !questions.includes(q));
|
|
750
|
+
turn = { promptLocalId: sourceIds?.at(-1) ?? lastLocalId, questions, texts: [], boardTools: /* @__PURE__ */ new Set() };
|
|
751
|
+
},
|
|
752
|
+
onAssistantText(text) {
|
|
753
|
+
if (!turn || !text.trim() || turn.texts.at(-1) === text) return;
|
|
754
|
+
turn.texts.push(text);
|
|
755
|
+
},
|
|
756
|
+
onTurnEnd() {
|
|
757
|
+
const ended = turn;
|
|
758
|
+
turn = null;
|
|
759
|
+
if (!ended) return work;
|
|
760
|
+
const anchor = ended.promptLocalId ? { localId: ended.promptLocalId } : void 0;
|
|
761
|
+
return queue(async () => {
|
|
762
|
+
const reply = ended.texts.join("\n\n").trim();
|
|
763
|
+
if (!reply || !await active()) return;
|
|
764
|
+
if (ended.questions.length) await settleQuestions(ended.questions, ended.texts[0]);
|
|
765
|
+
if (!ended.boardTools.has("owner_board_post") && !ended.boardTools.has("request_human")) {
|
|
766
|
+
await draftDecision(reply, anchor ? { ...anchor, snippet: clip(ended.texts.at(-1) ?? reply, ATTENTION_LIMITS.snippetChars) } : void 0);
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
},
|
|
770
|
+
/** Prefix the turn's prompt with the open-items digest (lead sessions only; unchanged on any failure). */
|
|
771
|
+
async decorate(message) {
|
|
772
|
+
if (typeof message !== "string") return message;
|
|
773
|
+
try {
|
|
774
|
+
const result = await Promise.race([
|
|
775
|
+
(async () => {
|
|
776
|
+
if (!await active()) return null;
|
|
777
|
+
const fresh = new Set(turn?.questions.map((q) => q.localId) ?? []);
|
|
778
|
+
const { items } = await deps.client.list({ sessionId: deps.sessionId(), state: "open", limit: 20 }, OWNER_BOARD_DIGEST_TIMEOUT_MS);
|
|
779
|
+
return formatBoardDigest(items.filter((item) => !(item.anchor?.localId && fresh.has(item.anchor.localId) && item.source === "owner")), now(), { tools: toolsFrozen === true });
|
|
780
|
+
})(),
|
|
781
|
+
new Promise((resolve) => setTimeout(() => resolve(null), OWNER_BOARD_DIGEST_TIMEOUT_MS).unref?.())
|
|
782
|
+
]);
|
|
783
|
+
return result ? `${result}
|
|
784
|
+
${message}` : message;
|
|
785
|
+
} catch (error) {
|
|
786
|
+
log(`[ownerBoard] digest skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
787
|
+
return message;
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
/** Settles once queued turn-end follow-ups are done (tests, shutdown). */
|
|
791
|
+
idle: () => work
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
const boards = /* @__PURE__ */ new WeakMap();
|
|
795
|
+
function attachLeadBoard(client, board) {
|
|
796
|
+
boards.set(client, board);
|
|
797
|
+
}
|
|
798
|
+
function leadBoardFor(client) {
|
|
799
|
+
return boards.get(client);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const OWNER_BOARD_TOOL_NAMES = ["owner_board_post", "owner_board_update", "owner_board_resolve", "owner_board_list"];
|
|
803
|
+
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.";
|
|
804
|
+
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.`;
|
|
805
|
+
const RESOLVE_DESCRIPTION = "Close one of this session's briefing board items: done, answered, dropped or superseded (say why in note).";
|
|
806
|
+
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.";
|
|
807
|
+
function errorText(error) {
|
|
808
|
+
if (error instanceof AttentionUnsupportedError) return "The briefing board is not available on this server right now.";
|
|
809
|
+
if (error instanceof AttentionRequestError) return error.message;
|
|
810
|
+
return error instanceof Error ? error.message : "Briefing board request failed";
|
|
811
|
+
}
|
|
812
|
+
function registerOwnerBoardTools(server, board) {
|
|
813
|
+
const register = (name, description, schema, execute, readOnly = false) => {
|
|
814
|
+
server.registerTool(name, {
|
|
815
|
+
description,
|
|
816
|
+
inputSchema: schema.shape,
|
|
817
|
+
annotations: { readOnlyHint: readOnly, idempotentHint: true, openWorldHint: false, destructiveHint: false }
|
|
818
|
+
}, async (args) => {
|
|
819
|
+
const parsed = schema.safeParse(args);
|
|
820
|
+
if (!parsed.success) {
|
|
821
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues.map((i) => `${i.path.join(".") || "input"}: ${i.message}`).join("; ")}` }], isError: true };
|
|
822
|
+
}
|
|
823
|
+
try {
|
|
824
|
+
return { content: [{ type: "text", text: JSON.stringify(await execute(parsed.data)) }], isError: false };
|
|
825
|
+
} catch (error) {
|
|
826
|
+
return { content: [{ type: "text", text: errorText(error) }], isError: true };
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
};
|
|
830
|
+
register("owner_board_post", POST_DESCRIPTION, OwnerBoardPostInputSchema, (args) => board.post(args));
|
|
831
|
+
register("owner_board_update", UPDATE_DESCRIPTION, OwnerBoardUpdateInputSchema, (args) => board.update(args));
|
|
832
|
+
register("owner_board_resolve", RESOLVE_DESCRIPTION, OwnerBoardResolveInputSchema, (args) => board.resolve(args));
|
|
833
|
+
register("owner_board_list", LIST_DESCRIPTION, OwnerBoardListInputSchema, (args) => board.list(args), true);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const OWNER_BOARD_READY_MS = 3e3;
|
|
837
|
+
function assistantTexts(body) {
|
|
838
|
+
const line = body;
|
|
839
|
+
if (line?.type !== "assistant" || line.isSidechain || !Array.isArray(line.message?.content)) return [];
|
|
840
|
+
return line.message.content.filter((block) => block?.type === "text" && typeof block.text === "string" && block.text.trim()).map((block) => block.text);
|
|
841
|
+
}
|
|
328
842
|
function createMcpServer(handlers, options, sessionId, client) {
|
|
329
843
|
const mcp = new McpServer({
|
|
330
844
|
name: "Very Happy Tools",
|
|
@@ -456,6 +970,32 @@ function withoutPhase(board) {
|
|
|
456
970
|
async function startHappyServer(client, options) {
|
|
457
971
|
logger.debug(`[happyMCP] server:start sessionId=${client.sessionId} assistant=${!!options?.assistant}`);
|
|
458
972
|
const selfReportState = options?.selfReportState ?? createSelfReportState();
|
|
973
|
+
const leadBoard = createLeadBoard({
|
|
974
|
+
sessionId: () => client.sessionId,
|
|
975
|
+
client: sharedAttentionClient(),
|
|
976
|
+
isLead: () => resolveTeamLead(client.sessionId),
|
|
977
|
+
autoDetect: async () => (await readSettings()).ownerBoardAutoDetect !== false,
|
|
978
|
+
onSupported: () => client.updateMetadata((metadata) => ({ ...metadata, capabilities: [.../* @__PURE__ */ new Set([...metadata.capabilities ?? [], ATTENTION_SESSION_CAPABILITY])] })),
|
|
979
|
+
log: (message) => logger.debug(message)
|
|
980
|
+
});
|
|
981
|
+
attachLeadBoard(client, leadBoard);
|
|
982
|
+
void leadBoard.init();
|
|
983
|
+
const onRoutedUserMessage = (event) => {
|
|
984
|
+
const text = event.message?.content?.text;
|
|
985
|
+
leadBoard.onUserMessage({ localId: event.localId, text: typeof text === "string" ? text : null, sentFrom: event.message?.meta?.sentFrom, delivery: event.message?.meta?.delivery });
|
|
986
|
+
};
|
|
987
|
+
const onClaudeLine = (body) => {
|
|
988
|
+
for (const text of assistantTexts(body)) leadBoard.onAssistantText(text);
|
|
989
|
+
};
|
|
990
|
+
const onTurnEnded = () => {
|
|
991
|
+
void leadBoard.onTurnEnd();
|
|
992
|
+
};
|
|
993
|
+
const tapped = typeof client.on === "function";
|
|
994
|
+
if (tapped) {
|
|
995
|
+
client.on("user-message-routed", onRoutedUserMessage);
|
|
996
|
+
client.on("claude-session-message", onClaudeLine);
|
|
997
|
+
client.on("turn-ended", onTurnEnded);
|
|
998
|
+
}
|
|
459
999
|
const handlers = {
|
|
460
1000
|
openPreview: async (path, mode, download = false) => {
|
|
461
1001
|
const verdict = checkPreviewPath(path);
|
|
@@ -536,7 +1076,8 @@ async function startHappyServer(client, options) {
|
|
|
536
1076
|
readPanels: async () => (await readSettings()).webPanels,
|
|
537
1077
|
// Same board write (and attention-change throttle bypass) as report_progress.
|
|
538
1078
|
reportBlocked: (reason) => handlers.reportProgress(reason, "blocked"),
|
|
539
|
-
sendAttention: (input) => options?.push ? options.push.sendAttention({ ...input, sessionTitle: getSessionNotificationBody(client.getMetadata()) }) : Promise.resolve({ sent: false, error: "push is not available in this runner" })
|
|
1079
|
+
sendAttention: (input) => options?.push ? options.push.sendAttention({ ...input, sessionTitle: getSessionNotificationBody(client.getMetadata()) }) : Promise.resolve({ sent: false, error: "push is not available in this runner" }),
|
|
1080
|
+
postBlocked: (reason, panel) => leadBoard.postBlocked(reason, panel)
|
|
540
1081
|
})
|
|
541
1082
|
};
|
|
542
1083
|
const contextPath = "/runtime-context/" + randomUUID();
|
|
@@ -610,7 +1151,9 @@ async function startHappyServer(client, options) {
|
|
|
610
1151
|
}
|
|
611
1152
|
return;
|
|
612
1153
|
}
|
|
1154
|
+
await leadBoard.ready(OWNER_BOARD_READY_MS);
|
|
613
1155
|
const mcp = createMcpServer(handlers, options, client.sessionId, client);
|
|
1156
|
+
if (leadBoard.toolsExposed()) registerOwnerBoardTools(mcp, leadBoard);
|
|
614
1157
|
try {
|
|
615
1158
|
const transport = new StreamableHTTPServerTransport({
|
|
616
1159
|
sessionIdGenerator: void 0
|
|
@@ -655,11 +1198,19 @@ async function startHappyServer(client, options) {
|
|
|
655
1198
|
...AUTOMATION_TOOL_NAMES,
|
|
656
1199
|
...SESSION_PEER_TOOL_NAMES,
|
|
657
1200
|
...PROJECT_TOOL_NAMES,
|
|
1201
|
+
// Auto-allow only; the tools themselves appear for a lead on a board server.
|
|
1202
|
+
...OWNER_BOARD_TOOL_NAMES,
|
|
658
1203
|
...options?.assistant && !process.env.VH_TEAM_SCOPE_FILE ? ASSISTANT_TOOL_NAMES : []
|
|
659
1204
|
],
|
|
1205
|
+
leadBoard,
|
|
660
1206
|
stop: () => {
|
|
661
1207
|
logger.debug(`[happyMCP] server:stop sessionId=${client.sessionId}`);
|
|
662
1208
|
if (adoptionEnabled) client.rpcHandlerManager.unregisterHandler("teams-adopt");
|
|
1209
|
+
if (tapped) {
|
|
1210
|
+
client.off("user-message-routed", onRoutedUserMessage);
|
|
1211
|
+
client.off("claude-session-message", onClaudeLine);
|
|
1212
|
+
client.off("turn-ended", onTurnEnded);
|
|
1213
|
+
}
|
|
663
1214
|
server.close();
|
|
664
1215
|
}
|
|
665
1216
|
};
|
|
@@ -748,7 +1299,7 @@ function deterministicStringify(obj, options = {}) {
|
|
|
748
1299
|
}
|
|
749
1300
|
function hashObject(obj, options, encoding = "hex") {
|
|
750
1301
|
const jsonString = deterministicStringify(obj, options);
|
|
751
|
-
return createHash("sha256").update(jsonString).digest(encoding);
|
|
1302
|
+
return createHash$1("sha256").update(jsonString).digest(encoding);
|
|
752
1303
|
}
|
|
753
1304
|
|
|
754
|
-
export { resolvePiContextUsage as a, createProjectInjector as c, hashObject as h, readPiModelsJson as r, startHappyServer as s };
|
|
1305
|
+
export { resolvePiContextUsage as a, createProjectInjector as c, hashObject as h, leadBoardFor as l, readPiModelsJson as r, startHappyServer as s };
|