@xopcai/xopc 0.0.241 → 0.0.243
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-CLnbVm9G.js → activity-store-CAah-_8h.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DuBewmbW.js → agent-avatar-dicebear-DDH4HgP7.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BDCfJZhh.js +1 -0
- package/dist/gateway/static/root/assets/agents-B0ZWgf-U.js +1 -0
- package/dist/gateway/static/root/assets/{api-DjBZe303.js → api-B2tKxbcz.js} +1 -1
- package/dist/gateway/static/root/assets/app-management-settings-panel-Blfwszzn.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-B-iul0RG.js → appearance-settings-C3V7Na75.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-BiEks0-F.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-BOx0EJZs.js → archive-plugin-BmSx7wzY.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Cfk1lwBo.js → attachment-load-BS-S4LJ9.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BTSGvaeS.js → automations-page-C7Y3L30D.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-A3zPkPuo.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-ChZyZK45.js → binary-plugins-BM9B5kcm.js} +1 -1
- package/dist/gateway/static/root/assets/block-editor-BbnpkYP9.js +185 -0
- package/dist/gateway/static/root/assets/browser-settings-page-Dk4FQ7fY.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-jvfcgFG-.js → browser-workflow-inputs-CRnkgZHI.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-DrHW7XNi.js → browser-workflows-page-B9qGL-it.js} +2 -2
- package/dist/gateway/static/root/assets/{button-CSEbl4zT.js → button-CtTvuElV.js} +1 -1
- package/dist/gateway/static/root/assets/capabilities-settings-panel--tR_El9e.js +7 -0
- package/dist/gateway/static/root/assets/channels-settings-D347vl_J.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-CC9MFway.js → channels-status-swr-DuY4MGxe.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-DeeZrVbV.js → chat-terminal-dock-DABPvwBT.js} +2 -2
- package/dist/gateway/static/root/assets/connectors-page-B-tvJkRJ.js +2 -0
- package/dist/gateway/static/root/assets/{cron-expression-CiqQOeQ3.js → cron-expression-D3bsKbNd.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-CGAMGPfU.js → date-picker-BsGM6Yty.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-CxWeUTsc.js → dependency-picker-Dj3g6YXZ.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-C3QmYO75.js → desktop-pet-C1QT0Sur.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-CAADkWYy.js → desktop-pet-settings-r6kAOnfY.js} +1 -1
- package/dist/gateway/static/root/assets/dist-CpzYSoz7.js +1 -0
- package/dist/gateway/static/root/assets/{dist-DaxPZj9e.js → dist-DBS3OB8K.js} +1 -1
- package/dist/gateway/static/root/assets/dist-DLmwHyAh.js +1 -0
- package/dist/gateway/static/root/assets/es2015-BvLh9GaE.js +41 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-FQXjDJ8P.js → extension-debug-page-DOkf7nh9.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BJE1D5Ji.js → extension-page-Cq2IA4Ho.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-5Ik2tNry.js → extension-settings-page-DQn4qjDZ.js} +1 -1
- package/dist/gateway/static/root/assets/{files-DQjhZjr2.js → files-D6vf3S30.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-cTj71t2a.js → gateway-config-api-DM3poL22.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bxe_R-JH.js → gateway-config-swr-BC3pgb_P.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-KXBUjNcx.js → gateway-settings-DPgH1hnh.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DJG-I0wW.js → gateway-startup-retry-uMcSfLru.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-C8cVzIcs.js → heartbeat-settings-Bk80-E3b.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-DKo5JiMR.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-Dc_ojQuj.js → image-generation-api-D1qlQA-A.js} +1 -1
- package/dist/gateway/static/root/assets/index-B0WJL9N1.js +71 -0
- package/dist/gateway/static/root/assets/index-Dbh5SXyV.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-pOKl7blR.js → keyboard-shortcuts-settings-BEwvd8ZU.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page--eJ5rmNj.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-Bi6wwKgm.js → local-apps-page-CgkY9E-r.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-C5wr3_pc.js → locale-store-CMZljTJx.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-Z49Td0J9.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-DGlVizWi.js → management-settings-Dv8NSJKt.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-CXP5zBx_.js → markdown-split-Cm0RBB-t.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-D6LxlDxr.js → markdown-view-PPCLt7fu.js} +3 -3
- package/dist/gateway/static/root/assets/{media-plugins-BsENGLr2.js → media-plugins-Bcr5sU1f.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CTSgFlE4.js +3 -0
- package/dist/gateway/static/root/assets/mobile-pairing-wizard-kKDiXofY.js +1 -0
- package/dist/gateway/static/root/assets/{models-hub-cache-Y7GUjxHx.js → models-hub-cache-ZrbGRiGY.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-D2EqpDyo.js → notes-page-CgsaRaGi.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-C1DEE09k.js +4 -0
- package/dist/gateway/static/root/assets/{oauth-api-C5NVcsKE.js → oauth-api-4VeMbkTJ.js} +1 -1
- package/dist/gateway/static/root/assets/popover-select-C-Hj-nXR.js +1 -0
- package/dist/gateway/static/root/assets/{product-delivery-dUJgmBQZ.js → product-delivery-CV4pDOtG.js} +1 -1
- package/dist/gateway/static/root/assets/product-open-page-BU0HiZtv.js +1 -0
- package/dist/gateway/static/root/assets/project-detail-page-isRapnp5.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-zvsjg0mi.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-FXdFaoWM.js → registry-api-CWOtPFrE.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-C9TGZ9Hc.js +1 -0
- package/dist/gateway/static/root/assets/runtime-tools-settings-panel-2zm9edMA.js +5 -0
- package/dist/gateway/static/root/assets/{schema-form-BUzdHWx6.js → schema-form-54A5gzkN.js} +1 -1
- package/dist/gateway/static/root/assets/{schemas-zIBW3wiV.js → schemas-BHS7bo06.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-BP8RyiUn.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4EMxWAZ.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-C0OBLqpu.js → settings-page-DzDhoyxv.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-NpLQkFCh.js → setup-status-panel-uDicHU1w.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-BtbPYaYC.js → share-preview-page-lOmLCWFS.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-T4Yvouvv.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-DVb7nrXz.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-CYk8pN97.js → skills-page-CnyNH3To.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page.utils-CgDtf2qs.js +1 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DmCRSvtd.js → system-settings-panel-C8yWfwtR.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-Tzfjlbf3.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-DUko1ZnE.js → theme-store-uxUwr10D.js} +1 -1
- package/dist/gateway/static/root/assets/tunnel-api-Bvg679no.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-W_P8Kiv4.js → use-autosave-D88VGoyG.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-RijPtyfO.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-BZD0j1jc.js → voice-api-key-field-CBHLsMcc.js} +1 -1
- package/dist/gateway/static/root/assets/voice-config-api-MqyI6cgq.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-overlay-GGe2TQ98.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-C0layUE9.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-DQXIzBAc.js +5 -0
- package/dist/gateway/static/root/index.html +21 -22
- package/dist/package.js +1 -1
- package/dist/packages/realtime-protocol/src/voice-audio.js +20 -0
- package/dist/packages/realtime-protocol/src/voice.js +208 -0
- package/dist/src/agent/agent-manager.js +1 -0
- package/dist/src/agent/embedded/run-for-session.d.ts +1 -0
- package/dist/src/agent/embedded/run-for-session.js +2 -1
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +1 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +2 -1
- package/dist/src/agent/prompt/voice-presentation.d.ts +2 -0
- package/dist/src/agent/prompt/voice-presentation.js +12 -0
- package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -0
- package/dist/src/agent/service/direct-turn-helpers.js +1 -0
- package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
- package/dist/src/agent/service/process-direct-streaming.js +2 -1
- package/dist/src/agent/session/session-config-service.js +4 -2
- package/dist/src/agent/skills/installer.d.ts +1 -0
- package/dist/src/agent/skills/installer.js +9 -6
- package/dist/src/agent/tools/factory.d.ts +2 -0
- package/dist/src/agent/tools/factory.js +5 -1
- package/dist/src/automations/draft/automation-draft-service.js +1 -1
- package/dist/src/config/models-json.d.ts +8 -8
- package/dist/src/config/schema.d.ts +60 -0
- package/dist/src/config/schema.js +36 -4
- package/dist/src/extensions/install.js +1 -1
- package/dist/src/files/file-service.d.ts +1 -0
- package/dist/src/files/file-service.js +4 -0
- package/dist/src/gateway/chat-stream/thinking-delta-coalescer.js +8 -3
- package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -11
- package/dist/src/gateway/hono/lib/config-payload.js +2 -2
- package/dist/src/gateway/hono/lib/safe-voice-config.d.ts +14 -0
- package/dist/src/gateway/hono/lib/safe-voice-config.js +29 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -2
- package/dist/src/gateway/hono/routes/files.js +7 -0
- package/dist/src/gateway/hono/routes/lazy-fallback.js +18 -4
- package/dist/src/gateway/hono/routes/runtime-tools.js +7 -1
- package/dist/src/gateway/hono/routes/session-input-handler.js +14 -0
- package/dist/src/gateway/hono/routes/sessions.js +4 -1
- package/dist/src/gateway/hono/routes/voice.js +140 -3
- package/dist/src/gateway/server.js +1 -0
- package/dist/src/gateway/service/agent-runner.d.ts +1 -0
- package/dist/src/gateway/service/agent-runner.js +1 -6
- package/dist/src/gateway/service/run-gateway-agent.d.ts +1 -0
- package/dist/src/gateway/service/run-gateway-agent.js +19 -1
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +73 -0
- package/dist/src/media-understanding/types.d.ts +61 -0
- package/dist/src/runtime-tools/archive.js +17 -3
- package/dist/src/runtime-tools/command.d.ts +1 -0
- package/dist/src/runtime-tools/command.js +29 -8
- package/dist/src/runtime-tools/environment.d.ts +1 -0
- package/dist/src/runtime-tools/environment.js +11 -16
- package/dist/src/runtime-tools/install-lock.d.ts +1 -1
- package/dist/src/runtime-tools/install-lock.js +34 -21
- package/dist/src/runtime-tools/manager.d.ts +1 -1
- package/dist/src/runtime-tools/manager.js +99 -64
- package/dist/src/runtime-tools/manifest-store.d.ts +2 -0
- package/dist/src/runtime-tools/manifest-store.js +29 -8
- package/dist/src/runtime-tools/paths.d.ts +2 -1
- package/dist/src/runtime-tools/paths.js +19 -8
- package/dist/src/runtime-tools/probe.d.ts +2 -0
- package/dist/src/runtime-tools/probe.js +13 -1
- package/dist/src/runtime-tools/prune.js +21 -7
- package/dist/src/session/client-history.js +12 -0
- package/dist/src/session/manager.d.ts +1 -0
- package/dist/src/session/session-context-for-llm.js +8 -0
- package/dist/src/session/store.d.ts +1 -0
- package/dist/src/session/store.js +1 -0
- package/dist/src/session/stored-rows-to-file-entries.js +5 -1
- package/dist/src/session/voice-transcript.d.ts +9 -0
- package/dist/src/session/voice-transcript.js +45 -0
- package/dist/src/storage/sqlite/config-repository.js +9 -0
- package/dist/src/storage/sqlite/remote-context-repository.d.ts +3 -0
- package/dist/src/storage/sqlite/remote-context-repository.js +40 -0
- package/dist/src/storage/sqlite/session-repository.js +13 -0
- package/dist/src/storage/sqlite/user-context-repository.d.ts +3 -1
- package/dist/src/storage/sqlite/user-context-repository.js +50 -4
- package/dist/src/tui/tui.js +2 -1
- package/dist/src/user-context/automatic-context.d.ts +17 -0
- package/dist/src/user-context/automatic-context.js +85 -0
- package/dist/src/user-context/changes.d.ts +7 -0
- package/dist/src/user-context/changes.js +28 -0
- package/dist/src/user-context/planner.js +1 -24
- package/dist/src/user-context/retriever.js +10 -11
- package/dist/src/user-context/selection-policy.d.ts +20 -0
- package/dist/src/user-context/selection-policy.js +33 -0
- package/dist/src/user-context/sources/index.js +2 -2
- package/dist/src/user-context/sources/repository.d.ts +2 -1
- package/dist/src/user-context/sources/repository.js +26 -5
- package/dist/src/voice/dashscope/streaming-stt-session.d.ts +2 -0
- package/dist/src/voice/dashscope/streaming-stt-session.js +249 -0
- package/dist/src/voice/dashscope/streaming-tts-stream.d.ts +12 -0
- package/dist/src/voice/dashscope/streaming-tts-stream.js +153 -0
- package/dist/src/voice/realtime/agentEngine.d.ts +10 -0
- package/dist/src/voice/realtime/agentEngine.js +403 -0
- package/dist/src/voice/realtime/audio-playback-window.d.ts +9 -0
- package/dist/src/voice/realtime/audio-playback-window.js +49 -0
- package/dist/src/voice/realtime/conversation-context.d.ts +11 -0
- package/dist/src/voice/realtime/conversation-context.js +76 -0
- package/dist/src/voice/realtime/engine.d.ts +13 -0
- package/dist/src/voice/realtime/engine.js +1 -0
- package/dist/src/voice/realtime/memory-context.d.ts +17 -0
- package/dist/src/voice/realtime/memory-context.js +100 -0
- package/dist/src/voice/realtime/omniEngine.d.ts +18 -0
- package/dist/src/voice/realtime/omniEngine.js +443 -0
- package/dist/src/voice/realtime/omniRoute.d.ts +10 -0
- package/dist/src/voice/realtime/omniRoute.js +31 -0
- package/dist/src/voice/realtime/runtime.d.ts +99 -0
- package/dist/src/voice/realtime/runtime.js +622 -0
- package/dist/src/voice/realtime/speakable-segmenter.d.ts +10 -0
- package/dist/src/voice/realtime/speakable-segmenter.js +87 -0
- package/dist/src/voice/stt/list-providers.js +2 -1
- package/dist/src/voice/stt/providers/alibaba-transcription.js +13 -1
- package/dist/src/voice/stt/providers/xopc-cloud-transcription.js +23 -0
- package/dist/src/voice/tts/factory.js +1 -0
- package/dist/src/voice/tts/openai-compatible-speech.js +3 -1
- package/dist/src/voice/tts/providers/alibaba-speech.d.ts +1 -5
- package/dist/src/voice/tts/providers/alibaba-speech.js +25 -8
- package/dist/src/voice/tts/providers/edge-speech.js +21 -22
- package/dist/src/voice/tts/providers/minimax-speech.js +6 -3
- package/dist/src/voice/tts/providers/xopc-cloud-speech.js +26 -1
- package/dist/src/voice/tts/speak-core.d.ts +6 -2
- package/dist/src/voice/tts/speak-core.js +46 -34
- package/dist/src/voice/tts/speech-provider-types.d.ts +1 -0
- package/package.json +6 -6
- package/dist/gateway/static/root/assets/Combination-14nmIgxH.js +0 -41
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DbdgX6AA.js +0 -1
- package/dist/gateway/static/root/assets/agents-BX3uRVm4.js +0 -1
- package/dist/gateway/static/root/assets/app-chrome-BeB4KgOf.js +0 -1
- package/dist/gateway/static/root/assets/app-management-settings-panel-D8evZyFQ.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-CoZd_cZu.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-C8RRKT-a.js +0 -6
- package/dist/gateway/static/root/assets/block-editor-DCH9b2Ht.js +0 -184
- package/dist/gateway/static/root/assets/browser-settings-page-D1GPwIfx.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-CrXB1Q-l.js +0 -7
- package/dist/gateway/static/root/assets/channels-settings-DWMSYaNZ.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DXTl1fNB.js +0 -2
- package/dist/gateway/static/root/assets/dist-BPoKqzeH.js +0 -1
- package/dist/gateway/static/root/assets/dist-DqPZM2MH.js +0 -1
- package/dist/gateway/static/root/assets/home-page-NdWtBda5.js +0 -1
- package/dist/gateway/static/root/assets/index-7i2P3312.js +0 -70
- package/dist/gateway/static/root/assets/index-DTo3IcbW.css +0 -2
- package/dist/gateway/static/root/assets/local-app-workbench-page-C_j_od5f.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-D6PDZEVP.js +0 -2
- package/dist/gateway/static/root/assets/messages-_tYhRAHD.js +0 -3
- package/dist/gateway/static/root/assets/mobile-pairing-wizard-DcEz7qcX.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-Bu0a83Oj.js +0 -4
- package/dist/gateway/static/root/assets/popover-select-DrILF6mR.js +0 -1
- package/dist/gateway/static/root/assets/product-open-page-DMhf1rdz.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-DDHi0Y4Q.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-CVMFKpmu.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-B74aErBY.js +0 -1
- package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Bv-akndY.js +0 -5
- package/dist/gateway/static/root/assets/sessions-page-D2wmGNZE.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CYCCUMEt.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-rf8f1OGq.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BkvclZ_o.js +0 -1
- package/dist/gateway/static/root/assets/skills-page.utils-DGjnz-NG.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-6a2RdDl2.js +0 -1
- package/dist/gateway/static/root/assets/tunnel-api-Chfiba_x.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-SeOHP6XO.js +0 -1
- package/dist/gateway/static/root/assets/voice-config-api-BO6A8V3Z.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-overlay-BfwEoOGL.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-X7Z5_lRX.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-BklMzkg8.js +0 -5
|
@@ -131,7 +131,8 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
131
131
|
const eventStream = agentService.turnDispatcher.processDirectStreaming(stampedMessage, sessionKey, origin, prepared, thinking, {
|
|
132
132
|
signal: mergedSignal,
|
|
133
133
|
runId,
|
|
134
|
-
sourceContexts: runOptions?.sourceContexts
|
|
134
|
+
sourceContexts: runOptions?.sourceContexts,
|
|
135
|
+
presentation: runOptions?.presentation
|
|
135
136
|
});
|
|
136
137
|
const mappedEvents = (async function* () {
|
|
137
138
|
for await (const event of eventStream) yield* mapper.map(event);
|
|
@@ -150,6 +151,18 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
150
151
|
summary: mergedSignal.aborted ? "Interrupted" : "Message processed successfully"
|
|
151
152
|
};
|
|
152
153
|
} catch (error) {
|
|
154
|
+
if (mergedSignal.aborted) {
|
|
155
|
+
terminalStatus = "cancelled";
|
|
156
|
+
taskRunStatus = "cancelled";
|
|
157
|
+
taskRunSummary = "Interrupted";
|
|
158
|
+
yield* emitAndYield(mapper.end("cancelled", "Interrupted"));
|
|
159
|
+
completeRealtimeTopic(`run:${runId}`);
|
|
160
|
+
runTopicCompleted = true;
|
|
161
|
+
return {
|
|
162
|
+
status: "aborted",
|
|
163
|
+
summary: "Interrupted"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
153
166
|
const em = error instanceof Error ? error.message : String(error);
|
|
154
167
|
log.error({
|
|
155
168
|
err: error,
|
|
@@ -173,6 +186,11 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
173
186
|
summary: streamError
|
|
174
187
|
};
|
|
175
188
|
} finally {
|
|
189
|
+
if (mergedSignal.aborted) {
|
|
190
|
+
terminalStatus = "cancelled";
|
|
191
|
+
taskRunStatus = "cancelled";
|
|
192
|
+
taskRunSummary = "Interrupted";
|
|
193
|
+
}
|
|
176
194
|
if (registeredActiveWebchatRun && activeWebchatRunBySession.get(sessionKey) === runId) {
|
|
177
195
|
activeWebchatRunBySession.delete(sessionKey);
|
|
178
196
|
publishRealtime("sessions", "run.completed", {
|
|
@@ -19,6 +19,7 @@ import { GatewaySessionsApi } from './service/sessions-api.js';
|
|
|
19
19
|
import { GatewayMarketplaceService } from './service/marketplace-service.js';
|
|
20
20
|
import { GatewayAgentRunner } from './service/agent-runner.js';
|
|
21
21
|
import { RealtimeRuntime } from '../realtime/runtime.js';
|
|
22
|
+
import { VoiceRealtimeRuntime } from '../voice/realtime/runtime.js';
|
|
22
23
|
import type { GatewayServiceConfig } from './service/types.js';
|
|
23
24
|
import { type GatewayReadinessSnapshot } from './startup-readiness.js';
|
|
24
25
|
import { type ConnectorLearningCoordinator } from '../connectors/learning-coordinator.js';
|
|
@@ -51,6 +52,7 @@ export declare class GatewayService {
|
|
|
51
52
|
private auth;
|
|
52
53
|
readonly endpointTools: EndpointToolRuntime;
|
|
53
54
|
readonly realtime: RealtimeRuntime;
|
|
55
|
+
readonly voiceRealtime: VoiceRealtimeRuntime;
|
|
54
56
|
getConfig(): Config;
|
|
55
57
|
getModelCatalogSync(): ModelCatalogSyncService;
|
|
56
58
|
get browserRecipes(): BrowserRecipeService;
|
|
@@ -6,6 +6,8 @@ import { getRuntimeLogStats } from "../utils/logger/stats.js";
|
|
|
6
6
|
import { createLogger } from "../utils/logger/index.js";
|
|
7
7
|
import { init_logger } from "../utils/logger.js";
|
|
8
8
|
import { closeXopcDatabase, openXopcDatabase } from "../storage/sqlite/connection.js";
|
|
9
|
+
import { notifyUserContextChange } from "../user-context/changes.js";
|
|
10
|
+
import { getSessionConfig } from "../storage/sqlite/config-repository.js";
|
|
9
11
|
import "../storage/sqlite/index.js";
|
|
10
12
|
import { getAgentDefaultModelRef, init_schema } from "../config/schema.js";
|
|
11
13
|
import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
@@ -18,6 +20,7 @@ import { loadConfig, saveConfig } from "../config/loader.js";
|
|
|
18
20
|
import { buildSessionKey, init_session_key, sanitizeSegment } from "../routing/session-key.js";
|
|
19
21
|
import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
|
|
20
22
|
import { init_providers, providers_exports } from "../providers/index.js";
|
|
23
|
+
import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
|
|
21
24
|
import { TaskRepository } from "../tasks/task-repository.js";
|
|
22
25
|
import { TaskConversationRepository } from "../tasks/task-conversation-repository.js";
|
|
23
26
|
import { disposeAllSessionMcpRuntimes } from "../agent/mcp/bundle-mcp-runtime.js";
|
|
@@ -68,6 +71,8 @@ import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validate
|
|
|
68
71
|
import { prepareAutomationAgentSession } from "./automation-agent-session.js";
|
|
69
72
|
import { stopTailscaleExposure } from "./tailscale-lifecycle.js";
|
|
70
73
|
import { reconcileDreamingAutomations } from "./dreaming-automation-reconciler.js";
|
|
74
|
+
import { buildVoiceMemoryContext } from "../voice/realtime/memory-context.js";
|
|
75
|
+
import { voiceMemoryBudget } from "../voice/realtime/conversation-context.js";
|
|
71
76
|
import { NotesStore } from "../notes/store.js";
|
|
72
77
|
import { buildNoteAgentContext } from "../notes/agent-context.js";
|
|
73
78
|
import { NotesService } from "../notes/service.js";
|
|
@@ -114,6 +119,7 @@ import { GatewayMarketplaceService } from "./service/marketplace-service.js";
|
|
|
114
119
|
import { GatewayConfigCoordinator } from "./service/config-coordinator.js";
|
|
115
120
|
import { GatewayAgentRunner } from "./service/agent-runner.js";
|
|
116
121
|
import { RealtimeRuntime } from "../realtime/runtime.js";
|
|
122
|
+
import { VoiceRealtimeRuntime } from "../voice/realtime/runtime.js";
|
|
117
123
|
import { GatewayReadiness } from "./startup-readiness.js";
|
|
118
124
|
import { createGatewayStartupTrace } from "./startup-trace.js";
|
|
119
125
|
import { applyAutomaticVoiceLanguage, inferProductLanguageFromEnvironment, initializeVoiceDefaults } from "../voice/language-profile.js";
|
|
@@ -158,6 +164,71 @@ var GatewayService = class {
|
|
|
158
164
|
auth;
|
|
159
165
|
endpointTools = new EndpointToolRuntime();
|
|
160
166
|
realtime = new RealtimeRuntime(this.endpointTools);
|
|
167
|
+
voiceRealtime = new VoiceRealtimeRuntime({
|
|
168
|
+
getConversationContext: async (sessionKey, expectedSessionId) => {
|
|
169
|
+
if ((await this.sessionIndex.getSessionMetadata(sessionKey))?.sessionId !== expectedSessionId) throw new Error("Conversation changed before voice connection");
|
|
170
|
+
const messages = await this.sessionIndex.loadMessages(sessionKey);
|
|
171
|
+
const after = await this.sessionIndex.getSessionMetadata(sessionKey);
|
|
172
|
+
if (after?.sessionId !== expectedSessionId) throw new Error("Conversation changed while loading voice context");
|
|
173
|
+
const profile = resolveEffectiveAgentProfileForSession(this.config, sessionKey);
|
|
174
|
+
const context = {
|
|
175
|
+
identity: [`Your name is ${profile.config.profile?.name ?? profile.agentId}.`, profile.customInstructions ?? ""].join("\n"),
|
|
176
|
+
history: messages
|
|
177
|
+
};
|
|
178
|
+
const memory = buildVoiceMemoryContext({
|
|
179
|
+
getConfig: () => this.config,
|
|
180
|
+
sessionKey,
|
|
181
|
+
workspaceId: getSessionConfig(sessionKey)?.workingDirectoryOverride ?? profile.resolvedWorkspacePath,
|
|
182
|
+
projectId: after?.projectId,
|
|
183
|
+
history: messages,
|
|
184
|
+
maxChars: voiceMemoryBudget(this.config.voice.realtime.omni.instructions, context)
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
...context,
|
|
188
|
+
...memory ? { memory } : {}
|
|
189
|
+
};
|
|
190
|
+
},
|
|
191
|
+
getSessionIdentity: async (sessionKey) => (await this.sessionIndex.getSessionMetadata(sessionKey))?.sessionId,
|
|
192
|
+
recordOmniTranscript: async (sessionKey, callId, entry, expectedSessionId) => {
|
|
193
|
+
await this.sessionIndex.appendTranscriptCustomMessageEntry(sessionKey, {
|
|
194
|
+
expectedSessionId,
|
|
195
|
+
customType: "voice_omni_transcript",
|
|
196
|
+
content: entry.text,
|
|
197
|
+
display: true,
|
|
198
|
+
details: {
|
|
199
|
+
callId,
|
|
200
|
+
itemId: entry.itemId,
|
|
201
|
+
role: entry.role,
|
|
202
|
+
interrupted: entry.interrupted,
|
|
203
|
+
engine: "omni"
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
this.emit("session.transcript_updated", { key: sessionKey });
|
|
207
|
+
},
|
|
208
|
+
getConfig: () => this.config,
|
|
209
|
+
sessionExists: async (sessionKey) => Boolean(await this.sessionIndex.getSessionMetadata(sessionKey)),
|
|
210
|
+
sessionBusy: (sessionKey) => Boolean(this.agentRunner.getActiveRunId(sessionKey)) || this.agentRunner.inputs.snapshot(sessionKey).inputs.some((input) => input.status === "queued"),
|
|
211
|
+
recordInterruption: (entry) => this.sessionIndex.appendTranscriptContextEntry(entry.sessionKey, {
|
|
212
|
+
text: "Voice response was interrupted before playback completed.",
|
|
213
|
+
data: {
|
|
214
|
+
type: "voice_response_interrupted",
|
|
215
|
+
responseId: entry.responseId,
|
|
216
|
+
reason: entry.reason,
|
|
217
|
+
generatedCharacters: entry.generatedCharacters,
|
|
218
|
+
interruptedDuring: entry.interruptedDuring
|
|
219
|
+
}
|
|
220
|
+
}),
|
|
221
|
+
runAgent: (text, sessionKey, signal) => {
|
|
222
|
+
if (this.agentRunner.hasActiveRun(sessionKey)) throw new Error("Conversation session already has an active response");
|
|
223
|
+
return this.agentRunner.runAgent(text, "webchat", sessionKey, {
|
|
224
|
+
type: "channel",
|
|
225
|
+
channel: "webchat"
|
|
226
|
+
}, void 0, void 0, {
|
|
227
|
+
signal,
|
|
228
|
+
presentation: "voice"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
});
|
|
161
232
|
getConfig() {
|
|
162
233
|
return this.config;
|
|
163
234
|
}
|
|
@@ -1097,6 +1168,7 @@ var GatewayService = class {
|
|
|
1097
1168
|
this.endpointTools.close();
|
|
1098
1169
|
await this.sideChats.disposeAll();
|
|
1099
1170
|
this.realtime.close();
|
|
1171
|
+
this.voiceRealtime.close();
|
|
1100
1172
|
await this.proactiveWorker.stop();
|
|
1101
1173
|
await this.discussionWorker.stop();
|
|
1102
1174
|
await this.discussionSealer.stop();
|
|
@@ -1543,6 +1615,7 @@ var GatewayService = class {
|
|
|
1543
1615
|
});
|
|
1544
1616
|
}
|
|
1545
1617
|
emit(type, payload) {
|
|
1618
|
+
if (type === "config.reload") notifyUserContextChange({ kind: "policy" });
|
|
1546
1619
|
this.realtime.broker.publish("gateway", type, payload);
|
|
1547
1620
|
this.createNotificationService().handleGatewayEvent(type, payload);
|
|
1548
1621
|
}
|
|
@@ -112,6 +112,65 @@ export interface AudioTranscriptionResult {
|
|
|
112
112
|
/** Audio duration in seconds, when reported. */
|
|
113
113
|
durationSeconds?: number;
|
|
114
114
|
}
|
|
115
|
+
export interface PcmAudioFormat {
|
|
116
|
+
encoding: 'pcm_s16le';
|
|
117
|
+
sampleRate: 16_000 | 24_000;
|
|
118
|
+
channels: 1;
|
|
119
|
+
}
|
|
120
|
+
export interface StreamingSttCapabilities {
|
|
121
|
+
inputSampleRates: readonly number[];
|
|
122
|
+
turnDetection: readonly 'server_vad'[];
|
|
123
|
+
defaultModel: string;
|
|
124
|
+
models: readonly string[];
|
|
125
|
+
}
|
|
126
|
+
export type StreamingSttEvent = {
|
|
127
|
+
type: 'ready';
|
|
128
|
+
} | {
|
|
129
|
+
type: 'speech_started';
|
|
130
|
+
utteranceId: string;
|
|
131
|
+
} | {
|
|
132
|
+
type: 'speech_stopped';
|
|
133
|
+
utteranceId: string;
|
|
134
|
+
} | {
|
|
135
|
+
type: 'transcript_delta';
|
|
136
|
+
utteranceId: string;
|
|
137
|
+
revision: number;
|
|
138
|
+
text: string;
|
|
139
|
+
} | {
|
|
140
|
+
type: 'transcript_final';
|
|
141
|
+
utteranceId: string;
|
|
142
|
+
revision: number;
|
|
143
|
+
text: string;
|
|
144
|
+
language?: string;
|
|
145
|
+
} | {
|
|
146
|
+
type: 'usage';
|
|
147
|
+
inputAudioMs: number;
|
|
148
|
+
} | {
|
|
149
|
+
type: 'error';
|
|
150
|
+
error: Error;
|
|
151
|
+
};
|
|
152
|
+
export interface StreamingSttOpenRequest {
|
|
153
|
+
model: string;
|
|
154
|
+
inputFormat: PcmAudioFormat;
|
|
155
|
+
apiKey?: string;
|
|
156
|
+
baseUrl?: string;
|
|
157
|
+
headers?: Record<string, string>;
|
|
158
|
+
language?: string;
|
|
159
|
+
prompt?: string;
|
|
160
|
+
turnDetection: {
|
|
161
|
+
mode: 'server_vad';
|
|
162
|
+
silenceDurationMs: number;
|
|
163
|
+
};
|
|
164
|
+
timeoutMs: number;
|
|
165
|
+
signal: AbortSignal;
|
|
166
|
+
onEvent: (event: StreamingSttEvent) => void;
|
|
167
|
+
}
|
|
168
|
+
export interface StreamingSttSession {
|
|
169
|
+
appendAudio(chunk: Uint8Array): void;
|
|
170
|
+
commit(): Promise<void>;
|
|
171
|
+
close(): Promise<void>;
|
|
172
|
+
abort(reason: string): void;
|
|
173
|
+
}
|
|
115
174
|
export interface ImageDescriptionRequest {
|
|
116
175
|
buffer: Buffer;
|
|
117
176
|
fileName: string;
|
|
@@ -170,6 +229,8 @@ export interface MediaUnderstandingProvider {
|
|
|
170
229
|
/** True when the provider can natively ingest PDF (no OCR pre-step needed). */
|
|
171
230
|
nativeDocumentInputs?: ReadonlyArray<'pdf'>;
|
|
172
231
|
transcribeAudio?: (req: AudioTranscriptionRequest) => Promise<AudioTranscriptionResult>;
|
|
232
|
+
streamingAudio?: StreamingSttCapabilities;
|
|
233
|
+
openAudioStream?: (req: StreamingSttOpenRequest) => Promise<StreamingSttSession>;
|
|
173
234
|
describeImage?: (req: ImageDescriptionRequest) => Promise<ImageDescriptionResult>;
|
|
174
235
|
describeVideo?: (req: VideoDescriptionRequest) => Promise<VideoDescriptionResult>;
|
|
175
236
|
}
|
|
@@ -5,12 +5,15 @@ import { lstat, mkdir, readdir, readlink, rename, rm, stat } from "node:fs/promi
|
|
|
5
5
|
import AdmZip from "adm-zip";
|
|
6
6
|
//#region src/runtime-tools/archive.ts
|
|
7
7
|
const MAX_ARCHIVE_ENTRIES = 5e4;
|
|
8
|
+
const MAX_TAR_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
9
|
+
const MAX_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024;
|
|
8
10
|
function validateMemberPath(memberPath) {
|
|
9
11
|
const portablePath = memberPath.replaceAll("\\", "/");
|
|
10
12
|
const normalized = posix.normalize(portablePath);
|
|
11
13
|
if (!memberPath || isAbsolute(memberPath) || portablePath.startsWith("/") || normalized === ".." || normalized.startsWith("../")) throw new Error(`Unsafe archive member: ${memberPath}`);
|
|
12
14
|
}
|
|
13
15
|
async function validateExtractedTree(root, current = root) {
|
|
16
|
+
let totalBytes = 0;
|
|
14
17
|
for (const entry of await readdir(current)) {
|
|
15
18
|
const entryPath = join(current, entry);
|
|
16
19
|
const details = await lstat(entryPath);
|
|
@@ -19,8 +22,10 @@ async function validateExtractedTree(root, current = root) {
|
|
|
19
22
|
if (relativeTarget.split(/[\\/]/)[0] === ".." || isAbsolute(relativeTarget)) throw new Error(`Archive symlink escapes extraction root: ${entryPath}`);
|
|
20
23
|
continue;
|
|
21
24
|
}
|
|
22
|
-
|
|
25
|
+
totalBytes += details.isDirectory() ? await validateExtractedTree(root, entryPath) : details.size;
|
|
26
|
+
if (totalBytes > MAX_EXTRACTED_BYTES) throw new Error("Extracted archive exceeds size limit");
|
|
23
27
|
}
|
|
28
|
+
return totalBytes;
|
|
24
29
|
}
|
|
25
30
|
async function runTar(args) {
|
|
26
31
|
return await new Promise((resolvePromise, reject) => {
|
|
@@ -30,11 +35,19 @@ async function runTar(args) {
|
|
|
30
35
|
shell: false,
|
|
31
36
|
windowsHide: true
|
|
32
37
|
});
|
|
38
|
+
const append = (current, chunk) => {
|
|
39
|
+
if (Buffer.byteLength(current) + chunk.byteLength > MAX_TAR_OUTPUT_BYTES) {
|
|
40
|
+
child.kill("SIGKILL");
|
|
41
|
+
reject(/* @__PURE__ */ new Error("tar output exceeds size limit"));
|
|
42
|
+
return current;
|
|
43
|
+
}
|
|
44
|
+
return current + chunk.toString();
|
|
45
|
+
};
|
|
33
46
|
child.stdout?.on("data", (chunk) => {
|
|
34
|
-
stdout
|
|
47
|
+
stdout = append(stdout, chunk);
|
|
35
48
|
});
|
|
36
49
|
child.stderr?.on("data", (chunk) => {
|
|
37
|
-
stderr
|
|
50
|
+
stderr = append(stderr, chunk);
|
|
38
51
|
});
|
|
39
52
|
child.once("error", reject);
|
|
40
53
|
child.once("close", (code) => {
|
|
@@ -47,6 +60,7 @@ async function extractZip(archivePath, destination) {
|
|
|
47
60
|
const zip = new AdmZip(archivePath);
|
|
48
61
|
const entries = zip.getEntries();
|
|
49
62
|
if (entries.length > MAX_ARCHIVE_ENTRIES) throw new Error("Archive contains too many entries");
|
|
63
|
+
if (entries.reduce((total, entry) => total + entry.header.size, 0) > MAX_EXTRACTED_BYTES) throw new Error("Extracted archive exceeds size limit");
|
|
50
64
|
for (const entry of entries) validateMemberPath(entry.entryName);
|
|
51
65
|
zip.extractAllTo(destination, true, false);
|
|
52
66
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
//#region src/runtime-tools/command.ts
|
|
3
3
|
const DEFAULT_COMMAND_TIMEOUT_MS = 5e3;
|
|
4
|
+
const MAX_COMMAND_OUTPUT_BYTES = 1024 * 1024;
|
|
4
5
|
async function runRuntimeCommand(params) {
|
|
5
6
|
return await new Promise((resolve) => {
|
|
6
7
|
let stdout = "";
|
|
@@ -8,7 +9,10 @@ async function runRuntimeCommand(params) {
|
|
|
8
9
|
let settled = false;
|
|
9
10
|
let timedOut = false;
|
|
10
11
|
let aborted = false;
|
|
12
|
+
let outputTruncated = false;
|
|
13
|
+
let timeout;
|
|
11
14
|
const child = spawn(params.command, params.args, {
|
|
15
|
+
detached: process.platform !== "win32",
|
|
12
16
|
env: params.env,
|
|
13
17
|
shell: false,
|
|
14
18
|
windowsHide: process.platform === "win32",
|
|
@@ -18,36 +22,53 @@ async function runRuntimeCommand(params) {
|
|
|
18
22
|
"pipe"
|
|
19
23
|
]
|
|
20
24
|
});
|
|
25
|
+
const killChild = () => {
|
|
26
|
+
if (process.platform !== "win32" && child.pid) try {
|
|
27
|
+
process.kill(-child.pid, "SIGKILL");
|
|
28
|
+
return;
|
|
29
|
+
} catch {}
|
|
30
|
+
child.kill("SIGKILL");
|
|
31
|
+
};
|
|
21
32
|
const finish = (exitCode) => {
|
|
22
33
|
if (settled) return;
|
|
23
34
|
settled = true;
|
|
24
|
-
clearTimeout(timeout);
|
|
35
|
+
if (timeout) clearTimeout(timeout);
|
|
25
36
|
params.signal?.removeEventListener("abort", abort);
|
|
26
37
|
resolve({
|
|
27
|
-
ok: exitCode === 0 && !timedOut && !aborted,
|
|
38
|
+
ok: exitCode === 0 && !timedOut && !aborted && !outputTruncated,
|
|
28
39
|
stdout: stdout.trim(),
|
|
29
40
|
stderr: stderr.trim(),
|
|
30
41
|
exitCode,
|
|
31
42
|
timedOut,
|
|
32
|
-
aborted
|
|
43
|
+
aborted,
|
|
44
|
+
outputTruncated
|
|
33
45
|
});
|
|
34
46
|
};
|
|
35
47
|
const abort = () => {
|
|
36
48
|
aborted = true;
|
|
37
|
-
|
|
49
|
+
killChild();
|
|
38
50
|
finish(null);
|
|
39
51
|
};
|
|
52
|
+
const appendOutput = (current, chunk) => {
|
|
53
|
+
const remaining = MAX_COMMAND_OUTPUT_BYTES - Buffer.byteLength(current);
|
|
54
|
+
if (remaining <= 0 || chunk.byteLength > remaining) {
|
|
55
|
+
outputTruncated = true;
|
|
56
|
+
killChild();
|
|
57
|
+
return remaining > 0 ? current + chunk.subarray(0, remaining).toString() : current;
|
|
58
|
+
}
|
|
59
|
+
return current + chunk.toString();
|
|
60
|
+
};
|
|
40
61
|
child.stdout?.on("data", (chunk) => {
|
|
41
|
-
stdout
|
|
62
|
+
stdout = appendOutput(stdout, chunk);
|
|
42
63
|
});
|
|
43
64
|
child.stderr?.on("data", (chunk) => {
|
|
44
|
-
stderr
|
|
65
|
+
stderr = appendOutput(stderr, chunk);
|
|
45
66
|
});
|
|
46
67
|
child.once("error", () => finish(null));
|
|
47
68
|
child.once("close", finish);
|
|
48
|
-
|
|
69
|
+
timeout = setTimeout(() => {
|
|
49
70
|
timedOut = true;
|
|
50
|
-
|
|
71
|
+
killChild();
|
|
51
72
|
finish(null);
|
|
52
73
|
}, params.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS);
|
|
53
74
|
timeout.unref();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { applyPathPrepend } from "../infra/path-prepend.js";
|
|
1
|
+
import { applyPathPrepend, findPathKey, normalizePathPrepend } from "../infra/path-prepend.js";
|
|
2
2
|
import { ManagedRuntimeManager } from "./manager.js";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { access
|
|
3
|
+
import { delimiter, dirname, join } from "node:path";
|
|
4
|
+
import { access } from "node:fs/promises";
|
|
5
5
|
//#region src/runtime-tools/environment.ts
|
|
6
6
|
const POLLUTING_ENV_KEYS = [
|
|
7
7
|
"NODE_OPTIONS",
|
|
@@ -19,14 +19,8 @@ const POLLUTING_ENV_KEYS = [
|
|
|
19
19
|
"UV_CACHE_DIR",
|
|
20
20
|
"UV_PYTHON_INSTALL_MIRROR"
|
|
21
21
|
];
|
|
22
|
-
async function skillEnvironmentBinDirs(stateDir) {
|
|
22
|
+
async function skillEnvironmentBinDirs(stateDir, environmentIds) {
|
|
23
23
|
const root = join(stateDir, "tools", "environments", "skills");
|
|
24
|
-
let environmentIds;
|
|
25
|
-
try {
|
|
26
|
-
environmentIds = await readdir(root);
|
|
27
|
-
} catch {
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
24
|
const candidates = environmentIds.flatMap((id) => [join(root, id, "bin"), join(root, id, "node_modules", ".bin")]);
|
|
31
25
|
return (await Promise.all(candidates.map(async (candidate) => {
|
|
32
26
|
try {
|
|
@@ -37,6 +31,10 @@ async function skillEnvironmentBinDirs(stateDir) {
|
|
|
37
31
|
}
|
|
38
32
|
}))).filter((candidate) => candidate !== null);
|
|
39
33
|
}
|
|
34
|
+
function appendPathEntries(env, entries) {
|
|
35
|
+
const pathKey = findPathKey(env);
|
|
36
|
+
env[pathKey] = normalizePathPrepend([...(env[pathKey] ?? "").split(delimiter).filter(Boolean), ...entries]).join(delimiter);
|
|
37
|
+
}
|
|
40
38
|
async function buildRuntimeEnvironment(options) {
|
|
41
39
|
const env = { ...options.baseEnv };
|
|
42
40
|
for (const key of POLLUTING_ENV_KEYS) delete env[key];
|
|
@@ -60,12 +58,9 @@ async function buildRuntimeEnvironment(options) {
|
|
|
60
58
|
}
|
|
61
59
|
}))).filter((item) => item !== null);
|
|
62
60
|
const managedBinDirs = resolved.filter((item) => item.source === "managed").map((item) => dirname(item.executable));
|
|
63
|
-
const skillBinDirs = await skillEnvironmentBinDirs(options.stateDir);
|
|
64
|
-
applyPathPrepend(env, [
|
|
65
|
-
|
|
66
|
-
...skillBinDirs,
|
|
67
|
-
...options.extraBinDirs ?? []
|
|
68
|
-
]);
|
|
61
|
+
const skillBinDirs = await skillEnvironmentBinDirs(options.stateDir, options.skillEnvironmentIds ?? []);
|
|
62
|
+
applyPathPrepend(env, [...managedBinDirs, ...options.extraBinDirs ?? []]);
|
|
63
|
+
appendPathEntries(env, skillBinDirs);
|
|
69
64
|
if (resolved.some((item) => item.runtime === "python")) {
|
|
70
65
|
env.PYTHONNOUSERSITE = "1";
|
|
71
66
|
env.PIP_REQUIRE_VIRTUALENV = "1";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function withInstallLock<T>(path: string, metadata: Record<string, unknown>, fn: () => Promise<T
|
|
1
|
+
export declare function withInstallLock<T>(path: string, metadata: Record<string, unknown>, fn: () => Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
@@ -7,38 +7,51 @@ function isProcessAlive(pid) {
|
|
|
7
7
|
try {
|
|
8
8
|
process.kill(pid, 0);
|
|
9
9
|
return true;
|
|
10
|
-
} catch {
|
|
11
|
-
return
|
|
10
|
+
} catch (error) {
|
|
11
|
+
return error.code === "EPERM";
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
async function staleLock(path) {
|
|
15
15
|
try {
|
|
16
16
|
const value = JSON.parse(await readFile(path, "utf8"));
|
|
17
|
-
|
|
18
|
-
const createdAt = typeof value.createdAt === "number" ? value.createdAt : 0;
|
|
19
|
-
return !isProcessAlive(pid) && Date.now() - createdAt > LOCK_STALE_MS;
|
|
17
|
+
return !isProcessAlive(typeof value.pid === "number" ? value.pid : 0);
|
|
20
18
|
} catch {
|
|
21
19
|
return true;
|
|
22
20
|
}
|
|
23
21
|
}
|
|
24
|
-
async function withInstallLock(path, metadata, fn) {
|
|
22
|
+
async function withInstallLock(path, metadata, fn, signal) {
|
|
25
23
|
await mkdir(dirname(path), { recursive: true });
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
const waitStartedAt = Date.now();
|
|
25
|
+
while (true) {
|
|
26
|
+
if (signal?.aborted) throw signal.reason ?? /* @__PURE__ */ new Error("Runtime installation was aborted");
|
|
27
|
+
try {
|
|
28
|
+
const handle = await open(path, "wx", 384);
|
|
29
|
+
await handle.writeFile(JSON.stringify({
|
|
30
|
+
pid: process.pid,
|
|
31
|
+
createdAt: Date.now(),
|
|
32
|
+
...metadata
|
|
33
|
+
}));
|
|
34
|
+
await handle.close();
|
|
35
|
+
break;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error.code !== "EEXIST") throw error;
|
|
38
|
+
if (await staleLock(path)) {
|
|
39
|
+
await unlink(path).catch(() => void 0);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (Date.now() - waitStartedAt > LOCK_STALE_MS) throw new Error(`Timed out waiting for runtime installation lock: ${path}`);
|
|
43
|
+
await new Promise((resolve, reject) => {
|
|
44
|
+
const timeout = setTimeout(() => {
|
|
45
|
+
signal?.removeEventListener("abort", abort);
|
|
46
|
+
resolve();
|
|
47
|
+
}, LOCK_WAIT_MS);
|
|
48
|
+
const abort = () => {
|
|
49
|
+
clearTimeout(timeout);
|
|
50
|
+
reject(signal?.reason ?? /* @__PURE__ */ new Error("Runtime installation was aborted"));
|
|
51
|
+
};
|
|
52
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
53
|
+
});
|
|
40
54
|
}
|
|
41
|
-
await new Promise((resolve) => setTimeout(resolve, LOCK_WAIT_MS));
|
|
42
55
|
}
|
|
43
56
|
try {
|
|
44
57
|
return await fn();
|
|
@@ -15,6 +15,6 @@ export declare class ManagedRuntimeManager extends EventEmitter {
|
|
|
15
15
|
private uvEnvironment;
|
|
16
16
|
status(runtime: RuntimeKind): Promise<RuntimeStatus>;
|
|
17
17
|
statusAll(): Promise<RuntimeStatus[]>;
|
|
18
|
-
repair(runtime: RuntimeKind): Promise<ResolvedRuntime>;
|
|
18
|
+
repair(runtime: RuntimeKind, requestedVersion?: string): Promise<ResolvedRuntime>;
|
|
19
19
|
private progress;
|
|
20
20
|
}
|