@xopcai/xopc 0.0.115 → 0.0.117
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/README.md +26 -10
- package/README.zh-CN.md +26 -10
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/demo-memory/src/index.d.ts +6 -0
- package/dist/extensions/demo-memory/src/index.js +132 -0
- package/dist/extensions/demo-memory/src/index.js.map +1 -0
- package/dist/extensions/demo-memory/xopc.extension.json +15 -0
- package/dist/extensions/feishu/src/schema/config-schema.d.ts +3 -3
- package/dist/extensions/telegram/src/command-handler.js +1 -2
- package/dist/extensions/telegram/src/command-handler.js.map +1 -1
- package/dist/extensions/telegram/src/config-schema.d.ts +8 -8
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/config-schema.d.ts +3 -3
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DVnBfANm.js → agent-avatar-dicebear-BBLhJ2C6.js} +1 -1
- package/dist/gateway/static/root/assets/agents-C6xgagZd.js +71 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-CBpLONvG.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-BQpPSTHD.js → appearance-settings-CGcc54yj.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-DdDx0V4z.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CXu-Csep.js → archive-plugin-DZsVqMhU.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-BAjgOoGY.js → attachment-load-CIol6JWX.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-BSBgB2hO.js → binary-plugins-B3UauMfp.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BpXw9TTR.js → block-editor-2HJoFchH.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-CUOO-PQk.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-XQoMTI0A.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CfqF-kGr.js +1 -0
- package/dist/gateway/static/root/assets/{channels-settings-BbPUpzWB.js → channels-settings-BYyRzsex.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-DIvEz4NX.js +2 -0
- package/dist/gateway/static/root/assets/cron-api-R41aQCX9.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-D97nFqQD.js +3 -0
- package/dist/gateway/static/root/assets/dreaming-settings-Bceaw1PG.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-D9kgUHCc.js → extension-debug-page-DX5WRE_t.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CGyhXqzR.js → extension-page-Buah7Bw-.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DL8Xquqd.js → extension-settings-page-DuroLVnf.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-D0VXA932.js → fetch-B5zdl27C.js} +1 -1
- package/dist/gateway/static/root/assets/{format-interval-label-Be8SmgUw.js → format-interval-label-Dn_QKIqb.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-config-api-gT4ZhE7h.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-iISWAZRm.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DERUH61L.js → gateway-startup-retry-B3kL8KGY.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-DCUROLMQ.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-BPmqWk90.js → goals-config-api-AR-fVh0P.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-Cpb21ezg.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-CPAuFwoD.js +1 -0
- package/dist/gateway/static/root/assets/{heartbeat-config-api-Bfdzr8oV.js → heartbeat-config-api-D7bKFEeO.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CC69vNx0.js +1 -0
- package/dist/gateway/static/root/assets/index-CwhK8qCh.js +122 -0
- package/dist/gateway/static/root/assets/index-CyfhARL-.css +1 -0
- package/dist/gateway/static/root/assets/{interval-presets-utKbuCRY.js → interval-presets-0DtsnxE0.js} +1 -1
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CjqS6c6V.js → keyboard-shortcuts-settings-BPREtN8B.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DefCgF4o.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-DW8C47iB.js → markdown-split-ChbR9LGO.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-D6HwDhDT.js → media-plugins-B82oP80n.js} +1 -1
- package/dist/gateway/static/root/assets/messages-Cf1O9JB0.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DmKiIihT.js +2 -0
- package/dist/gateway/static/root/assets/note-detail-page-CxxfLRJZ.js +2 -0
- package/dist/gateway/static/root/assets/{note-image-lightbox-pJM35Pz5.js → note-image-lightbox-CDknITM3.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-X2slSx7V.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CVwHuyN6.js → preview-open-alternatives-DV3xQsWB.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BvmUWqgu.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-Ba-UEk7x.js → save-bar-controls-BODCXnvY.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CNN9595U.js → schedule-field-CgJSCD3B.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DnNMmtD5.js → schema-form-qtfEI6QM.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-B0yESuuL.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-q2M8RKOo.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-CmOz80i5.js → settings-collapsible-section-BHZKjVcS.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-BmK7JJOV.js → settings-form-section-5i35D-J1.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-CGozabLD.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-FoOSe6nU.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-Dp2qGrHC.js → share-preview-page-B0H63tDv.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-Cy5zWyYQ.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-DaZNFro9.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-CnwtUzvw.js +2 -0
- package/dist/gateway/static/root/assets/system-settings-panel-DXs2VmBt.js +1 -0
- package/dist/gateway/static/root/assets/typed-models-lib-4gPuydz_.js +1 -0
- package/dist/gateway/static/root/assets/{use-channel-catalog-CHlO8J9G.js → use-channel-catalog-BCjP_VHb.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DvKiOglO.js +1 -0
- package/dist/gateway/static/root/assets/{voice-player-store-DDDWYHvg.js → voice-player-store-BlF9V05n.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-CAVZcONQ.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -1
- package/dist/src/agent/agent-manager.js +7 -8
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/agent-scope.js +6 -8
- package/dist/src/agent/agent-scope.js.map +1 -1
- package/dist/src/agent/background-review/coordinator.js +0 -1
- package/dist/src/agent/background-review/coordinator.js.map +1 -1
- package/dist/src/agent/background-review/run-background-review.d.ts +0 -2
- package/dist/src/agent/background-review/run-background-review.js +2 -4
- package/dist/src/agent/background-review/run-background-review.js.map +1 -1
- package/dist/src/agent/background-review/settings.js +8 -7
- package/dist/src/agent/background-review/settings.js.map +1 -1
- package/dist/src/agent/bootstrap/bootstrap-context.d.ts +2 -2
- package/dist/src/agent/bootstrap/bootstrap-context.js +4 -4
- package/dist/src/agent/bootstrap/bootstrap-context.js.map +1 -1
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js.map +1 -1
- package/dist/src/agent/fallback/candidates.js +6 -3
- package/dist/src/agent/fallback/candidates.js.map +1 -1
- package/dist/src/agent/goals/checklist-judge.d.ts +1 -0
- package/dist/src/agent/goals/checklist-judge.js +1 -1
- package/dist/src/agent/goals/checklist-judge.js.map +1 -1
- package/dist/src/agent/goals/evaluate-turn.d.ts +1 -0
- package/dist/src/agent/goals/evaluate-turn.js +1 -0
- package/dist/src/agent/goals/evaluate-turn.js.map +1 -1
- package/dist/src/agent/goals/post-turn.js +8 -1
- package/dist/src/agent/goals/post-turn.js.map +1 -1
- package/dist/src/agent/image/generation/runtime-types.d.ts +2 -0
- package/dist/src/agent/image/generation/runtime.js +1 -1
- package/dist/src/agent/image/generation/runtime.js.map +1 -1
- package/dist/src/agent/image/image-helpers.d.ts +1 -1
- package/dist/src/agent/image/image-helpers.js +4 -17
- package/dist/src/agent/image/image-helpers.js.map +1 -1
- package/dist/src/agent/image/image-model-fallback.js +1 -1
- package/dist/src/agent/image/image-model-fallback.js.map +1 -1
- package/dist/src/agent/image/inbound-image-handling.js +1 -1
- package/dist/src/agent/image/inbound-image-handling.js.map +1 -1
- package/dist/src/agent/image/tool-model-config.d.ts +1 -1
- package/dist/src/agent/image/tool-model-config.js +2 -2
- package/dist/src/agent/image/tool-model-config.js.map +1 -1
- package/dist/src/agent/image/understanding/pi-ai-provider.js +5 -2
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/image/understanding/provider-registry.d.ts +1 -0
- package/dist/src/agent/image/understanding/provider-registry.js +16 -2
- package/dist/src/agent/image/understanding/provider-registry.js.map +1 -1
- package/dist/src/agent/mcp/resolve-embedded-mcp-tools.js +1 -1
- package/dist/src/agent/mcp/resolve-embedded-mcp-tools.js.map +1 -1
- package/dist/src/agent/media-generation/runtime-shared.d.ts +5 -2
- package/dist/src/agent/media-generation/runtime-shared.js +1 -1
- package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
- package/dist/src/agent/memory/builtin-memory-store.d.ts +2 -0
- package/dist/src/agent/memory/builtin-memory-store.js +4 -0
- package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
- package/dist/src/agent/memory/builtin-provider.d.ts +31 -9
- package/dist/src/agent/memory/builtin-provider.js +261 -11
- package/dist/src/agent/memory/builtin-provider.js.map +1 -1
- package/dist/src/agent/memory/context-assembler.d.ts +10 -0
- package/dist/src/agent/memory/context-assembler.js +26 -0
- package/dist/src/agent/memory/context-assembler.js.map +1 -0
- package/dist/src/agent/memory/create-memory-manager.js +25 -7
- package/dist/src/agent/memory/create-memory-manager.js.map +1 -1
- package/dist/src/agent/memory/dreaming/config.d.ts +1 -6
- package/dist/src/agent/memory/dreaming/config.js +8 -8
- package/dist/src/agent/memory/dreaming/config.js.map +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +2 -0
- package/dist/src/agent/memory/dreaming/deep-promotion.js +24 -0
- package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -1
- package/dist/src/agent/memory/index.d.ts +1 -0
- package/dist/src/agent/memory/index.js +4 -3
- package/dist/src/agent/memory/inject-prefetch.js +6 -2
- package/dist/src/agent/memory/inject-prefetch.js.map +1 -1
- package/dist/src/agent/memory/manager.d.ts +37 -1
- package/dist/src/agent/memory/manager.js +332 -40
- package/dist/src/agent/memory/manager.js.map +1 -1
- package/dist/src/agent/memory/memory-config.js +12 -15
- package/dist/src/agent/memory/memory-config.js.map +1 -1
- package/dist/src/agent/memory/plugin-discovery.d.ts +19 -5
- package/dist/src/agent/memory/plugin-discovery.js +97 -31
- package/dist/src/agent/memory/plugin-discovery.js.map +1 -1
- package/dist/src/agent/memory/provider.d.ts +20 -15
- package/dist/src/agent/memory/stub-memory-provider.d.ts +17 -2
- package/dist/src/agent/memory/stub-memory-provider.js +20 -3
- package/dist/src/agent/memory/stub-memory-provider.js.map +1 -1
- package/dist/src/agent/memory/types.d.ts +136 -0
- package/dist/src/agent/models/manager.d.ts +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/models/manager.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.d.ts +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +2 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.d.ts +2 -2
- package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js +1 -1
- package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js.map +1 -1
- package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +2 -6
- package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +2 -8
- package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js.map +1 -1
- package/dist/src/agent/reply/post-compaction-context.js +11 -6
- package/dist/src/agent/reply/post-compaction-context.js.map +1 -1
- package/dist/src/agent/reply/startup-context.js +8 -9
- package/dist/src/agent/reply/startup-context.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +1 -4
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service/session-context-report.js +1 -1
- package/dist/src/agent/service/session-context-report.js.map +1 -1
- package/dist/src/agent/service.js +21 -25
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +1 -2
- package/dist/src/agent/session/session-hydrator.js +1 -2
- package/dist/src/agent/session/session-hydrator.js.map +1 -1
- package/dist/src/agent/session/session-inspector.d.ts +1 -1
- package/dist/src/agent/session/session-inspector.js +9 -14
- package/dist/src/agent/session/session-inspector.js.map +1 -1
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/starter-agents.d.ts +1 -3
- package/dist/src/agent/starter-agents.js +13 -21
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js +1 -2
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
- package/dist/src/agent/tools/composio-tool.d.ts +8 -0
- package/dist/src/agent/tools/composio-tool.js +46 -0
- package/dist/src/agent/tools/composio-tool.js.map +1 -0
- package/dist/src/agent/tools/curated-memory-tool.d.ts +2 -5
- package/dist/src/agent/tools/curated-memory-tool.js +20 -16
- package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +0 -4
- package/dist/src/agent/tools/factory.js +32 -66
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/image-generate-tool.d.ts +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +19 -14
- package/dist/src/agent/tools/image-generate-tool.js.map +1 -1
- package/dist/src/agent/tools/image-tool.js +1 -3
- package/dist/src/agent/tools/image-tool.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -0
- package/dist/src/agent/tools/index.js +2 -1
- package/dist/src/agent/tools/memory-tool.d.ts +2 -2
- package/dist/src/agent/tools/memory-tool.js +39 -14
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/agent/tools/session-search-tool.js +1 -3
- package/dist/src/agent/tools/session-search-tool.js.map +1 -1
- package/dist/src/agent/tools/web-extract.js +6 -5
- package/dist/src/agent/tools/web-extract.js.map +1 -1
- package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/audit-repo.js +15 -2
- package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
- package/dist/src/agent/workflow/builtins/client-proposal.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/client-proposal.js +10 -1
- package/dist/src/agent/workflow/builtins/client-proposal.js.map +1 -1
- package/dist/src/agent/workflow/builtins/competitor-scan.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/competitor-scan.js +16 -2
- package/dist/src/agent/workflow/builtins/competitor-scan.js.map +1 -1
- package/dist/src/agent/workflow/builtins/content-draft.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/content-draft.js +15 -2
- package/dist/src/agent/workflow/builtins/content-draft.js.map +1 -1
- package/dist/src/agent/workflow/builtins/content-repurpose.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/content-repurpose.js +10 -1
- package/dist/src/agent/workflow/builtins/content-repurpose.js.map +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js +9 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
- package/dist/src/agent/workflow/builtins/decision-compare.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/decision-compare.js +16 -2
- package/dist/src/agent/workflow/builtins/decision-compare.js.map +1 -1
- package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/implementation-plan.js +11 -1
- package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -1
- package/dist/src/agent/workflow/builtins/inbox-triage.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/inbox-triage.js +11 -1
- package/dist/src/agent/workflow/builtins/inbox-triage.js.map +1 -1
- package/dist/src/agent/workflow/builtins/meeting-prep.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/meeting-prep.js +9 -1
- package/dist/src/agent/workflow/builtins/meeting-prep.js.map +1 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js +9 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/offer-design.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/offer-design.js +10 -1
- package/dist/src/agent/workflow/builtins/offer-design.js.map +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js +9 -1
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/release-check.js +10 -1
- package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
- package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/research.js +16 -2
- package/dist/src/agent/workflow/builtins/research.js.map +1 -1
- package/dist/src/agent/workflow/builtins/weekly-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/weekly-review.js +9 -1
- package/dist/src/agent/workflow/builtins/weekly-review.js.map +1 -1
- package/dist/src/agent/workflow/subagent-runner.js +1 -3
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/agent-manifest/index.d.ts +4 -0
- package/dist/src/agent-manifest/index.js +10 -0
- package/dist/src/agent-manifest/index.js.map +1 -0
- package/dist/src/agent-manifest/prompt.d.ts +2 -0
- package/dist/src/agent-manifest/prompt.js +57 -0
- package/dist/src/agent-manifest/prompt.js.map +1 -0
- package/dist/src/agent-manifest/resolver.d.ts +11 -0
- package/dist/src/agent-manifest/resolver.js +93 -0
- package/dist/src/agent-manifest/resolver.js.map +1 -0
- package/dist/src/agent-manifest/schema.d.ts +778 -0
- package/dist/src/agent-manifest/schema.js +267 -0
- package/dist/src/agent-manifest/schema.js.map +1 -0
- package/dist/src/agent-manifest/validator.d.ts +24 -0
- package/dist/src/agent-manifest/validator.js +87 -0
- package/dist/src/agent-manifest/validator.js.map +1 -0
- package/dist/src/agent-runtime/boundary-guard.d.ts +12 -0
- package/dist/src/agent-runtime/boundary-guard.js +23 -0
- package/dist/src/agent-runtime/boundary-guard.js.map +1 -0
- package/dist/src/agent-runtime/index.d.ts +7 -0
- package/dist/src/agent-runtime/index.js +8 -0
- package/dist/src/agent-runtime/memory-runtime.d.ts +21 -0
- package/dist/src/agent-runtime/memory-runtime.js +50 -0
- package/dist/src/agent-runtime/memory-runtime.js.map +1 -0
- package/dist/src/agent-runtime/model-router.d.ts +11 -0
- package/dist/src/agent-runtime/model-router.js +16 -0
- package/dist/src/agent-runtime/model-router.js.map +1 -0
- package/dist/src/agent-runtime/run-trace.d.ts +27 -0
- package/dist/src/agent-runtime/run-trace.js +53 -0
- package/dist/src/agent-runtime/run-trace.js.map +1 -0
- package/dist/src/agent-runtime/runtime-profile.d.ts +24 -0
- package/dist/src/agent-runtime/runtime-profile.js +44 -0
- package/dist/src/agent-runtime/runtime-profile.js.map +1 -0
- package/dist/src/agent-runtime/tool-registry.d.ts +28 -0
- package/dist/src/agent-runtime/tool-registry.js +52 -0
- package/dist/src/agent-runtime/tool-registry.js.map +1 -0
- package/dist/src/agent-runtime/workflow-runtime.d.ts +31 -0
- package/dist/src/agent-runtime/workflow-runtime.js +34 -0
- package/dist/src/agent-runtime/workflow-runtime.js.map +1 -0
- package/dist/src/browser/actions/extension-registry-bridge.js +1 -1
- package/dist/src/browser/actions/extension-registry-bridge.js.map +1 -1
- package/dist/src/browser/actions/registry.js +5 -8
- package/dist/src/browser/actions/registry.js.map +1 -1
- package/dist/src/browser/backend-from-config.d.ts +1 -1
- package/dist/src/browser/backend-from-config.js +50 -71
- package/dist/src/browser/backend-from-config.js.map +1 -1
- package/dist/src/browser/browser-command-timeout.d.ts +0 -1
- package/dist/src/browser/browser-command-timeout.js +3 -7
- package/dist/src/browser/browser-command-timeout.js.map +1 -1
- package/dist/src/browser/providers/browser-ext-install.d.ts +2 -12
- package/dist/src/browser/providers/browser-ext-install.js +3 -8
- package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
- package/dist/src/browser/readiness.js +16 -16
- package/dist/src/browser/readiness.js.map +1 -1
- package/dist/src/browser/schemas.js +1 -1
- package/dist/src/browser/schemas.js.map +1 -1
- package/dist/src/chat-commands/builtins/config.js +4 -4
- package/dist/src/chat-commands/builtins/config.js.map +1 -1
- package/dist/src/chat-commands/builtins/thinking.js +16 -42
- package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
- package/dist/src/chat-commands/config-paths.js +1 -1
- package/dist/src/chat-commands/config-paths.js.map +1 -1
- package/dist/src/chat-commands/context.js +3 -4
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/cli/command-manifest.js +1 -1
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/agent.js +3 -1
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/agents.js +3 -6
- package/dist/src/cli/commands/agents.js.map +1 -1
- package/dist/src/cli/commands/browser-cli-helpers.js +9 -11
- package/dist/src/cli/commands/browser-cli-helpers.js.map +1 -1
- package/dist/src/cli/commands/config.js +5 -5
- package/dist/src/cli/commands/config.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -17
- package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -1
- package/dist/src/cli/commands/goal.js +1 -1
- package/dist/src/cli/commands/image.js +13 -372
- package/dist/src/cli/commands/image.js.map +1 -1
- package/dist/src/cli/commands/models.js +26 -10
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/cli/commands/onboard/channels/registry.js.map +1 -1
- package/dist/src/cli/commands/onboard/model.js +20 -22
- package/dist/src/cli/commands/onboard/model.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/utils/init-workspace-core.d.ts +1 -4
- package/dist/src/cli/utils/init-workspace-core.js +7 -6
- package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
- package/dist/src/commands/agents.config.d.ts +0 -1
- package/dist/src/commands/agents.config.js +66 -12
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.d.ts +5 -17
- package/dist/src/config/agent-profile.js +31 -64
- package/dist/src/config/agent-profile.js.map +1 -1
- package/dist/src/config/agent-typed-models.d.ts +1 -1
- package/dist/src/config/agent-typed-models.js +0 -1
- package/dist/src/config/agent-typed-models.js.map +1 -1
- package/dist/src/config/index.js +3 -3
- package/dist/src/config/integration.d.ts +1 -1
- package/dist/src/config/reload.js +1 -1
- package/dist/src/config/reload.js.map +1 -1
- package/dist/src/config/rules.js +6 -56
- package/dist/src/config/rules.js.map +1 -1
- package/dist/src/config/schema.d.ts +925 -781
- package/dist/src/config/schema.js +157 -424
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/composio-triggers.d.ts +10 -0
- package/dist/src/connectors/composio-triggers.js +32 -0
- package/dist/src/connectors/composio-triggers.js.map +1 -0
- package/dist/src/connectors/composio.d.ts +54 -0
- package/dist/src/connectors/composio.js +334 -0
- package/dist/src/connectors/composio.js.map +1 -0
- package/dist/src/connectors/health.js +16 -1
- package/dist/src/connectors/health.js.map +1 -1
- package/dist/src/connectors/install.d.ts +2 -1
- package/dist/src/connectors/install.js +50 -13
- package/dist/src/connectors/install.js.map +1 -1
- package/dist/src/connectors/instances.js +57 -6
- package/dist/src/connectors/instances.js.map +1 -1
- package/dist/src/connectors/lifecycle.d.ts +3 -0
- package/dist/src/connectors/lifecycle.js +23 -0
- package/dist/src/connectors/lifecycle.js.map +1 -0
- package/dist/src/connectors/materialize.js +2 -1
- package/dist/src/connectors/materialize.js.map +1 -1
- package/dist/src/connectors/providers.d.ts +1 -0
- package/dist/src/connectors/providers.js +10 -2
- package/dist/src/connectors/providers.js.map +1 -1
- package/dist/src/connectors/registries/modelscope.d.ts +2 -0
- package/dist/src/connectors/registries/modelscope.js +205 -0
- package/dist/src/connectors/registries/modelscope.js.map +1 -0
- package/dist/src/connectors/registries/official-mcp.d.ts +2 -0
- package/dist/src/connectors/registries/official-mcp.js +142 -0
- package/dist/src/connectors/registries/official-mcp.js.map +1 -0
- package/dist/src/connectors/registries/search.d.ts +12 -0
- package/dist/src/connectors/registries/search.js +89 -0
- package/dist/src/connectors/registries/search.js.map +1 -0
- package/dist/src/connectors/registries/smithery.d.ts +2 -0
- package/dist/src/connectors/registries/smithery.js +98 -0
- package/dist/src/connectors/registries/smithery.js.map +1 -0
- package/dist/src/connectors/registries/types.d.ts +20 -0
- package/dist/src/connectors/registries/types.js +1 -0
- package/dist/src/connectors/secret-store.js +4 -2
- package/dist/src/connectors/secret-store.js.map +1 -1
- package/dist/src/connectors/setup-secrets.d.ts +15 -0
- package/dist/src/connectors/setup-secrets.js +64 -0
- package/dist/src/connectors/setup-secrets.js.map +1 -0
- package/dist/src/connectors/supervisor.d.ts +15 -0
- package/dist/src/connectors/supervisor.js +73 -0
- package/dist/src/connectors/supervisor.js.map +1 -0
- package/dist/src/connectors/types.d.ts +27 -3
- package/dist/src/connectors/usage.js +6 -1
- package/dist/src/connectors/usage.js.map +1 -1
- package/dist/src/cron/index.js +1 -1
- package/dist/src/cron/service.js +1 -1
- package/dist/src/cron/validation.d.ts +3 -3
- package/dist/src/extensions/activation-context.js +13 -7
- package/dist/src/extensions/activation-context.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +17 -18
- package/dist/src/gateway/agents-admin.js +123 -88
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/capability-presets-admin.d.ts +56 -0
- package/dist/src/gateway/capability-presets-admin.js +214 -0
- package/dist/src/gateway/capability-presets-admin.js.map +1 -0
- package/dist/src/gateway/hono/lib/agent-model.d.ts +1 -1
- package/dist/src/gateway/hono/lib/agent-model.js +1 -1
- package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +234 -127
- package/dist/src/gateway/hono/lib/config-payload.js +13 -37
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +56 -33
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/browser.js +8 -15
- package/dist/src/gateway/hono/routes/browser.js.map +1 -1
- package/dist/src/gateway/hono/routes/capability-presets.d.ts +3 -0
- package/dist/src/gateway/hono/routes/capability-presets.js +145 -0
- package/dist/src/gateway/hono/routes/capability-presets.js.map +1 -0
- package/dist/src/gateway/hono/routes/config-patch/agents.d.ts +1 -17
- package/dist/src/gateway/hono/routes/config-patch/agents.js +2 -443
- package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +7 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +1 -1
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/connectors.js +230 -3
- package/dist/src/gateway/hono/routes/connectors.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +7 -0
- package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
- package/dist/src/gateway/hono/routes/goals.js +52 -5
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.d.ts +3 -0
- package/dist/src/gateway/hono/routes/memory.js +290 -0
- package/dist/src/gateway/hono/routes/memory.js.map +1 -0
- package/dist/src/gateway/hono/routes/voice.js +14 -1
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/service/agent-runner.d.ts +5 -12
- package/dist/src/gateway/service/agent-runner.js +8 -8
- package/dist/src/gateway/service/agent-runner.js.map +1 -1
- package/dist/src/gateway/service/run-gateway-agent.d.ts +2 -10
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service.d.ts +2 -2
- package/dist/src/gateway/service.js +20 -16
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/user-turn-input.d.ts +18 -0
- package/dist/src/gateway/user-turn-input.js +16 -0
- package/dist/src/gateway/user-turn-input.js.map +1 -0
- package/dist/src/goals/goal-queue-store.d.ts +2 -1
- package/dist/src/goals/goal-queue-store.js +2 -2
- package/dist/src/goals/goal-queue-store.js.map +1 -1
- package/dist/src/goals/goal-queue-types.d.ts +4 -3
- package/dist/src/goals/goal-runner.js +25 -3
- package/dist/src/goals/goal-runner.js.map +1 -1
- package/dist/src/goals/goal-service.d.ts +6 -1
- package/dist/src/goals/goal-service.js +9 -2
- package/dist/src/goals/goal-service.js.map +1 -1
- package/dist/src/goals/goal-store.d.ts +7 -1
- package/dist/src/goals/goal-store.js +42 -0
- package/dist/src/goals/goal-store.js.map +1 -1
- package/dist/src/goals/types.d.ts +10 -0
- package/dist/src/notes/agent-context.js +2 -2
- package/dist/src/providers/index.d.ts +2 -2
- package/dist/src/providers/index.js +10 -14
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/session/index.js +1 -1
- package/dist/src/session/session-workspace.js +7 -2
- package/dist/src/session/session-workspace.js.map +1 -1
- package/dist/src/session/store.js +5 -0
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/thinking-resolve.d.ts +1 -1
- package/dist/src/session/thinking-resolve.js +1 -1
- package/dist/src/session/thinking-resolve.js.map +1 -1
- package/dist/src/storage/sqlite/connection.js +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +101 -0
- package/dist/src/storage/sqlite/memory-records-repository.js +309 -0
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -0
- package/dist/src/storage/sqlite/migrations/011_goal_context_messages.sql +8 -0
- package/dist/src/storage/sqlite/migrations/012_memory_records.sql +63 -0
- package/dist/src/storage/sqlite/migrations/013_memory_trace_events.sql +21 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +2 -2
- package/dist/src/storage/sqlite/migrations/runner.js +4 -4
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +2 -2
- package/dist/src/storage/sqlite/schema.js +3 -3
- package/dist/src/storage/sqlite/schema.sql +95 -0
- package/dist/src/tui/backends/embedded-backend.js +6 -4
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/extension-host/load-extensions.js.map +1 -1
- package/dist/src/voice/tts/openai-compatible-speech.d.ts +1 -1
- package/dist/src/voice/tts/openai-compatible-speech.js.map +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +14 -2
- package/dist/src/voice/tts/providers/edge-speech.js.map +1 -1
- package/dist/src/voice/tts/speak-core.js +3 -3
- package/dist/src/voice/tts/speak-core.js.map +1 -1
- package/dist/src/voice/tts/speech-provider-types.d.ts +2 -2
- package/dist/src/voice/tts/types.js +1 -1
- package/dist/src/voice/tts/types.js.map +1 -1
- package/dist/src/workflows/domain/result.d.ts +0 -1
- package/dist/src/workflows/engine/workflow-engine.js +7 -23
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.js +3 -4
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
- package/package.json +4 -3
- package/dist/gateway/static/root/assets/agent-browser-settings-page-DPXc4hz8.js +0 -6
- package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-CTJ0YnRQ.js +0 -1
- package/dist/gateway/static/root/assets/agents-zJXQfJ_W.js +0 -71
- package/dist/gateway/static/root/assets/app-management-settings-panel-DzGbdr04.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-CPjfyzSD.js +0 -1
- package/dist/gateway/static/root/assets/config-api-NOoPiIYz.js +0 -3
- package/dist/gateway/static/root/assets/connectors-settings-CAXYMOdp.js +0 -2
- package/dist/gateway/static/root/assets/cron-api-C9BOqAXd.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-DuUbBeKI.js +0 -3
- package/dist/gateway/static/root/assets/dreaming-settings-DQ4DtiK1.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-C-xjn__h.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-BMPwIjKI.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BJsyjJnF.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-BerDgIhw.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-ByTjeAto.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-TbHgcjTn.js +0 -1
- package/dist/gateway/static/root/assets/index-AkTrf6Pb.js +0 -122
- package/dist/gateway/static/root/assets/index-fh5sXTj9.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-DGXuPf5B.js +0 -2
- package/dist/gateway/static/root/assets/messages-upgxw6Ub.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-Bot16nn7.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-Brlyou8M.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-DvZ4pwc-.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BZEDmxbZ.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DdGGlsfn.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DlkEkPf9.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-DFPTrsPF.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CYCCfgh4.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-Bff1pGd0.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CJGKpBur.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-C6ocduTD.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-5jrIEr5b.js +0 -1
- package/dist/gateway/static/root/assets/use-agent-defaults-form-BuiXLIs5.js +0 -1
- package/dist/gateway/static/root/assets/use-settings-tab-guard-CW5JORnl.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DDoQyghO.js +0 -1
- package/dist/gateway/static/root/assets/workflow-page.utils-CKjeEGqx.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-B604ehHG.js +0 -18
|
@@ -278,6 +278,92 @@ CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
|
278
278
|
tokenize='unicode61'
|
|
279
279
|
);
|
|
280
280
|
|
|
281
|
+
CREATE TABLE IF NOT EXISTS memory_records (
|
|
282
|
+
record_id TEXT PRIMARY KEY,
|
|
283
|
+
provider_id TEXT NOT NULL,
|
|
284
|
+
kind TEXT NOT NULL,
|
|
285
|
+
agent_id TEXT NOT NULL,
|
|
286
|
+
workspace_id TEXT,
|
|
287
|
+
session_key TEXT,
|
|
288
|
+
content TEXT NOT NULL,
|
|
289
|
+
source_json TEXT NOT NULL,
|
|
290
|
+
confidence REAL,
|
|
291
|
+
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
292
|
+
created_at INTEGER NOT NULL,
|
|
293
|
+
updated_at INTEGER NOT NULL,
|
|
294
|
+
last_recalled_at INTEGER,
|
|
295
|
+
recall_count INTEGER NOT NULL DEFAULT 0
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
CREATE INDEX IF NOT EXISTS idx_memory_records_scope_updated
|
|
299
|
+
ON memory_records(agent_id, workspace_id, updated_at DESC);
|
|
300
|
+
CREATE INDEX IF NOT EXISTS idx_memory_records_kind_updated
|
|
301
|
+
ON memory_records(kind, updated_at DESC);
|
|
302
|
+
CREATE INDEX IF NOT EXISTS idx_memory_records_provider
|
|
303
|
+
ON memory_records(provider_id, updated_at DESC);
|
|
304
|
+
|
|
305
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_records_fts USING fts5(
|
|
306
|
+
content,
|
|
307
|
+
record_id UNINDEXED,
|
|
308
|
+
provider_id UNINDEXED,
|
|
309
|
+
kind UNINDEXED,
|
|
310
|
+
agent_id UNINDEXED,
|
|
311
|
+
workspace_id UNINDEXED,
|
|
312
|
+
tokenize='unicode61'
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
CREATE TABLE IF NOT EXISTS memory_signals (
|
|
316
|
+
signal_id TEXT PRIMARY KEY,
|
|
317
|
+
source TEXT NOT NULL,
|
|
318
|
+
record_id TEXT,
|
|
319
|
+
provider_id TEXT,
|
|
320
|
+
agent_id TEXT,
|
|
321
|
+
workspace_id TEXT,
|
|
322
|
+
session_key TEXT,
|
|
323
|
+
score REAL,
|
|
324
|
+
content TEXT,
|
|
325
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
326
|
+
created_at INTEGER NOT NULL,
|
|
327
|
+
FOREIGN KEY (record_id) REFERENCES memory_records(record_id) ON DELETE SET NULL
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
CREATE INDEX IF NOT EXISTS idx_memory_signals_created
|
|
331
|
+
ON memory_signals(created_at DESC);
|
|
332
|
+
CREATE INDEX IF NOT EXISTS idx_memory_signals_record_created
|
|
333
|
+
ON memory_signals(record_id, created_at DESC);
|
|
334
|
+
CREATE INDEX IF NOT EXISTS idx_memory_signals_scope_created
|
|
335
|
+
ON memory_signals(agent_id, workspace_id, created_at DESC);
|
|
336
|
+
|
|
337
|
+
CREATE TABLE IF NOT EXISTS memory_provider_state (
|
|
338
|
+
provider_id TEXT NOT NULL,
|
|
339
|
+
scope_key TEXT NOT NULL,
|
|
340
|
+
state_json TEXT NOT NULL,
|
|
341
|
+
updated_at INTEGER NOT NULL,
|
|
342
|
+
PRIMARY KEY (provider_id, scope_key)
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
CREATE TABLE IF NOT EXISTS memory_trace_events (
|
|
346
|
+
trace_id TEXT PRIMARY KEY,
|
|
347
|
+
session_key TEXT,
|
|
348
|
+
turn_id TEXT,
|
|
349
|
+
phase TEXT NOT NULL,
|
|
350
|
+
provider_id TEXT NOT NULL,
|
|
351
|
+
request_json TEXT NOT NULL DEFAULT '{}',
|
|
352
|
+
result_count INTEGER,
|
|
353
|
+
selected_record_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
354
|
+
skipped_reason TEXT,
|
|
355
|
+
error TEXT,
|
|
356
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
357
|
+
created_at INTEGER NOT NULL
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
CREATE INDEX IF NOT EXISTS idx_memory_trace_created
|
|
361
|
+
ON memory_trace_events(created_at DESC);
|
|
362
|
+
CREATE INDEX IF NOT EXISTS idx_memory_trace_provider_created
|
|
363
|
+
ON memory_trace_events(provider_id, created_at DESC);
|
|
364
|
+
CREATE INDEX IF NOT EXISTS idx_memory_trace_session_created
|
|
365
|
+
ON memory_trace_events(session_key, created_at DESC);
|
|
366
|
+
|
|
281
367
|
CREATE TABLE IF NOT EXISTS goals (
|
|
282
368
|
goal_id TEXT PRIMARY KEY,
|
|
283
369
|
title TEXT NOT NULL,
|
|
@@ -308,6 +394,15 @@ CREATE INDEX IF NOT EXISTS idx_goals_agent_updated
|
|
|
308
394
|
CREATE INDEX IF NOT EXISTS idx_goals_active_session
|
|
309
395
|
ON goals(active_session_key);
|
|
310
396
|
|
|
397
|
+
CREATE TABLE IF NOT EXISTS goal_context_messages (
|
|
398
|
+
goal_id TEXT PRIMARY KEY,
|
|
399
|
+
text TEXT NOT NULL DEFAULT '',
|
|
400
|
+
attachments_json TEXT NOT NULL DEFAULT '[]',
|
|
401
|
+
created_at INTEGER NOT NULL,
|
|
402
|
+
updated_at INTEGER NOT NULL,
|
|
403
|
+
FOREIGN KEY (goal_id) REFERENCES goals(goal_id) ON DELETE CASCADE
|
|
404
|
+
);
|
|
405
|
+
|
|
311
406
|
CREATE TABLE IF NOT EXISTS goal_queue (
|
|
312
407
|
queue_id TEXT PRIMARY KEY,
|
|
313
408
|
goal_id TEXT NOT NULL,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { getAgentDefaultModelRef, init_schema } from "../../config/schema.js";
|
|
1
2
|
import { init_agent_scope, listAgentEntries, normalizeAgentId } from "../../agent/agent-scope.js";
|
|
2
3
|
import { init_agent_session_key, resolveAgentIdFromSessionKey } from "../../routing/agent-session-key.js";
|
|
3
4
|
import { createLogger } from "../../utils/logger/index.js";
|
|
4
5
|
import { init_logger } from "../../utils/logger.js";
|
|
5
6
|
import { loadConfig } from "../../config/loader.js";
|
|
6
7
|
import { getAvailableModels, init_providers } from "../../providers/index.js";
|
|
8
|
+
import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
|
|
7
9
|
import { openXopcDatabase } from "../../storage/sqlite/connection.js";
|
|
8
10
|
import "../../storage/sqlite/index.js";
|
|
9
|
-
import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
|
|
10
11
|
import { evictEmbeddedSessionRunner } from "../../agent/embedded/session-runner.js";
|
|
11
12
|
import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
|
|
12
13
|
import { SessionIndex } from "../../session/manager.js";
|
|
@@ -32,6 +33,7 @@ import { isAbsolute, relative, resolve } from "node:path";
|
|
|
32
33
|
//#region src/tui/backends/embedded-backend.ts
|
|
33
34
|
init_agent_scope();
|
|
34
35
|
init_agent_session_key();
|
|
36
|
+
init_schema();
|
|
35
37
|
init_providers();
|
|
36
38
|
init_logger();
|
|
37
39
|
const log = createLogger("TUI:Embedded");
|
|
@@ -72,7 +74,7 @@ var EmbeddedBackend = class {
|
|
|
72
74
|
this.config = config;
|
|
73
75
|
const workspace = getWorkspacePath(config);
|
|
74
76
|
this.workspace = workspace;
|
|
75
|
-
const modelId = config
|
|
77
|
+
const modelId = getAgentDefaultModelRef(config);
|
|
76
78
|
openXopcDatabase();
|
|
77
79
|
this.sessionIndex = new SessionIndex({ config });
|
|
78
80
|
this.sessionIndexReady = this.sessionIndex.initialize().catch((err) => {
|
|
@@ -349,7 +351,7 @@ var EmbeddedBackend = class {
|
|
|
349
351
|
}
|
|
350
352
|
}
|
|
351
353
|
async getSessionInfo(sessionKey) {
|
|
352
|
-
if (!this.agent) return { model: loadConfig()
|
|
354
|
+
if (!this.agent) return { model: getAgentDefaultModelRef(loadConfig()) ?? void 0 };
|
|
353
355
|
try {
|
|
354
356
|
const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);
|
|
355
357
|
const parsed = parseModelRef(cfg.model);
|
|
@@ -373,7 +375,7 @@ var EmbeddedBackend = class {
|
|
|
373
375
|
sessionKey,
|
|
374
376
|
errorMessage
|
|
375
377
|
}, `getSessionInfo failed: ${errorMessage}`);
|
|
376
|
-
return { model: loadConfig()
|
|
378
|
+
return { model: getAgentDefaultModelRef(loadConfig()) ?? void 0 };
|
|
377
379
|
}
|
|
378
380
|
}
|
|
379
381
|
async listModels() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedded-backend.js","names":[],"sources":["../../../../src/tui/backends/embedded-backend.ts"],"sourcesContent":["import type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { AgentService } from '../../agent/index.js';\nimport { listAgentEntries, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { resolveAgentIdFromSessionKey } from '../../routing/agent-session-key.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { createCreateShareTool, isShareToolAvailable } from '../../agent/tools/create-share-tool.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { getAvailableModels } from '../../providers/index.js';\nimport { evictEmbeddedSessionRunner } from '../../agent/embedded/session-runner.js';\nimport type { ExportFormat } from '../../session/types.js';\nimport { SessionIndex } from '../../session/index.js';\nimport { openXopcDatabase } from '../../storage/sqlite/index.js';\nimport { buildWorkflowChildTools } from '../../agent/workflow/workflow-child-tools.js';\nimport type { GatewayWorkflowHost } from '../../gateway/gateway-workflow-host.types.js';\nimport { WorkflowRunService } from '../../workflows/service/workflow-run-service.js';\nimport { WorkflowSessionBridge } from '../../workflows/service/workflow-session-bridge.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiCompactionResult,\n TuiEvent,\n TuiModelChoice,\n TuiShareRequest,\n TuiShareResult,\n TuiSessionStats,\n TuiSessionItem,\n TuiTranscriptTreeEntry,\n TuiAgentInfo,\n TuiWorkspaceFileSearchEntry,\n TuiWorkflowRunStartRequest,\n TuiWorkflowRunStartResult,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { sessionMetadataToTuiItem } from '../tui-session-format.js';\nimport { computeTuiSessionStats } from '../tui-session-stats.js';\nimport { buildTuiTranscriptTree, transcriptTreeEntryIdToRowNumber } from '../tui-transcript-tree.js';\nimport { ChatStreamMapper } from '../../gateway/chat-stream/mapper.js';\nimport { collectTuiStartupResources } from '../tui-startup-resources.js';\nimport { fuzzySearchWorkspaceFiles } from '../../gateway/workspace-file-search.js';\n\nconst log = createLogger('TUI:Embedded');\n\ninterface EmbeddedBackendOptions {\n extensionRegistry?: ExtensionRegistryImpl;\n implicitTrustedWorkspace?: string;\n isWorkspaceTrusted?: (workspaceDir: string) => boolean | null | undefined;\n}\n\nfunction isPathSameOrInside(parentDir: string, childDir: string): boolean {\n const rel = relative(resolve(parentDir), resolve(childDir));\n return rel === '' || (!!rel && !rel.startsWith('..') && !isAbsolute(rel));\n}\n\n/**\n * TUI backend that runs the agent in-process (no gateway required).\n *\n * Wraps `AgentService` directly and emits TuiEvents by observing the\n * `MessageBus` output stream.\n */\nexport class EmbeddedBackend implements TuiBackend {\n private bus: MessageBus;\n private agent: AgentService | null = null;\n private config: ReturnType<typeof loadConfig> | null = null;\n private workspace = '';\n private sessionIndex: SessionIndex | null = null;\n private sessionIndexReady: Promise<void> | null = null;\n private workflowRunService: WorkflowRunService | null = null;\n private running = false;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n\n constructor(private readonly opts?: EmbeddedBackendOptions) {\n this.bus = new MessageBus();\n }\n\n get connectionLabel(): string {\n return 'local embedded';\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const config = loadConfig();\n this.config = config;\n const workspace = getWorkspacePath(config);\n this.workspace = workspace;\n const modelId = config.agents?.defaults?.models?.chat?.primary;\n openXopcDatabase();\n this.sessionIndex = new SessionIndex({ config });\n this.sessionIndexReady = this.sessionIndex.initialize().catch((err: unknown) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage }, `Embedded session index initialization failed: ${errorMessage}`);\n throw err;\n });\n\n this.agent = new AgentService(this.bus, {\n workspace,\n model: modelId,\n config,\n sessionStore: this.sessionIndex.getStore(),\n getWorkflowRunService: () => this.getWorkflowRunService(),\n extensionRegistry: this.opts?.extensionRegistry,\n isWorkspaceTrusted: (workspaceDir) => {\n const explicit = this.opts?.isWorkspaceTrusted?.(workspaceDir);\n if (explicit !== undefined && explicit !== null) {\n return explicit;\n }\n const implicit = this.opts?.implicitTrustedWorkspace;\n if (implicit && isPathSameOrInside(implicit, workspaceDir)) {\n return true;\n }\n return undefined;\n },\n });\n\n this.agent.start().then(() => {\n this.onConnected?.();\n }).catch((err) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage }, `Embedded agent failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n });\n\n // Process outbound messages in background\n this.processOutbound();\n }\n\n stop(): void {\n this.running = false;\n this.chatAbort?.abort();\n this.chatAbort = null;\n this.bus.shutdown();\n void this.agent?.stop();\n this.agent = null;\n }\n\n getActiveSignal(): AbortSignal | undefined {\n const signal = this.chatAbort?.signal;\n return signal && !signal.aborted ? signal : undefined;\n }\n\n private getWorkflowRunService(): WorkflowRunService {\n if (this.workflowRunService) return this.workflowRunService;\n if (!this.config || !this.sessionIndex || !this.agent) {\n throw new Error('Embedded workflow runtime is not ready.');\n }\n\n const host: GatewayWorkflowHost = {\n currentConfig: this.config,\n currentWorkspacePath: this.workspace,\n messageBusInstance: this.bus,\n agentService: {\n getModelForSession: (sessionKey) => this.agent!.getModelForSession(sessionKey),\n },\n sessionIndexInstance: this.sessionIndex,\n emit: (event, payload) => {\n this.onEvent?.({\n event,\n data: payload,\n source: 'broadcast',\n });\n },\n };\n\n this.workflowRunService = new WorkflowRunService({\n service: host,\n sessionBridge: new WorkflowSessionBridge(host),\n buildChildTools: buildWorkflowChildTools,\n });\n return this.workflowRunService;\n }\n\n async getStartupResources(sessionKey: string) {\n return collectTuiStartupResources(loadConfig(), sessionKey);\n }\n\n async startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult> {\n await this.sessionIndexReady;\n const agentId = opts.agentId?.trim() || resolveAgentIdFromSessionKey(opts.sessionKey);\n const result = await this.getWorkflowRunService().startWorkflowRun({\n agentId,\n definitionId: opts.definitionId,\n parentSessionKey: opts.sessionKey,\n source: { kind: 'chat', sessionKey: opts.sessionKey },\n goal: opts.goal,\n input: opts.input,\n });\n if (result.ok === false) {\n throw new Error(result.message);\n }\n return {\n runId: result.runId,\n sessionKey: result.sessionKey,\n definitionId: opts.definitionId,\n };\n }\n\n async searchWorkspaceFiles(\n sessionKey: string,\n query: string,\n options?: { limit?: number },\n ): Promise<TuiWorkspaceFileSearchEntry[]> {\n if (!this.agent) return [];\n try {\n const workspaceRoot = await this.agent.getEffectiveWorkspacePathForSession(sessionKey);\n return await fuzzySearchWorkspaceFiles(workspaceRoot, query, options?.limit ?? 15);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded workspace file search failed: ${errorMessage}`);\n return [];\n }\n }\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n if (!this.agent) throw new Error('Agent not started');\n\n const runId = crypto.randomUUID();\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n\n const mapper = new ChatStreamMapper({ runId, sessionKey: opts.sessionKey, channel: 'tui' });\n for (const event of mapper.start()) {\n this.onEvent?.({ event: event.type, data: event, source: 'embedded' });\n }\n\n // Run the stream in background so the TUI event loop stays responsive.\n void (async () => {\n try {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines (Telegram, Weixin, etc.).\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const messageForAgent = opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message);\n\n const stream = this.agent!.turnDispatcher.processDirectStreaming(\n messageForAgent,\n opts.sessionKey,\n opts.attachments,\n opts.thinking,\n { signal, runId },\n );\n\n for await (const event of stream) {\n if (signal.aborted) break;\n for (const mapped of mapper.map(event)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n\n if (!signal.aborted) {\n for (const mapped of mapper.end('success')) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n for (const mapped of mapper.error(errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n for (const mapped of mapper.end('error', errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n })();\n\n return { runId };\n }\n\n async abortChat(_opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n if (this.chatAbort) {\n this.chatAbort.abort();\n this.chatAbort = null;\n return { ok: true };\n }\n return { ok: false };\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n const ok = await this.agent.turnDispatcher.steerWebchatSession(opts.sessionKey, opts.message);\n return { ok };\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n if (!this.agent) {\n return { messages: [] };\n }\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(opts.sessionKey);\n return {\n messages: transcriptRowsToClientHistory(rows, { limit: opts.limit }),\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded loadHistory failed: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async loadTranscriptTree(sessionKey: string): Promise<TuiTranscriptTreeEntry[]> {\n if (!this.agent) return [];\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return buildTuiTranscriptTree(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded loadTranscriptTree failed: ${errorMessage}`);\n return [];\n }\n }\n\n async getSessionStats(sessionKey: string): Promise<TuiSessionStats> {\n if (!this.agent) return computeTuiSessionStats([]);\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return computeTuiSessionStats(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded getSessionStats failed: ${errorMessage}`);\n return computeTuiSessionStats([]);\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n if (!this.agent) return [];\n try {\n const result = await this.agent.sessionStore.list({\n limit: 200,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n return result.items.map(sessionMetadataToTuiItem);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded listSessions failed: ${errorMessage}`);\n return [];\n }\n }\n\n async listAgents(): Promise<TuiAgentInfo[]> {\n const config = loadConfig();\n const agents = new Map<string, TuiAgentInfo>();\n for (const entry of listAgentEntries(config)) {\n if (entry.enabled === false) continue;\n const id = normalizeAgentId(entry.id);\n agents.set(id, {\n id,\n enabled: true,\n });\n }\n return [...agents.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n await this.agent.sessionStore.updateMetadata(sessionKey, { name: name.trim() });\n return { ok: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded renameSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n const ok = await this.agent.sessionStore.deleteSession(sessionKey);\n return { ok };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded deleteSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n if (!this.agent) {\n const config = loadConfig();\n const model = config.agents?.defaults?.models?.chat?.primary;\n return { model: model ?? undefined };\n }\n try {\n const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);\n const parsed = parseModelRef(cfg.model);\n const usage = await this.agent.sessionInspector.contextUsage(sessionKey);\n return {\n model: parsed?.model ?? cfg.model,\n modelProvider: parsed?.provider,\n thinkingLevel: cfg.thinkingLevel,\n reasoningLevel: cfg.reasoningLevel,\n verboseLevel: cfg.verboseLevel,\n totalTokens: usage.estimatedTokens,\n contextWindow: usage.contextWindow,\n contextUsagePercent: usage.usagePercent,\n effectiveWorkspacePath: cfg.effectiveWorkspacePath,\n workingDirectoryLocked: cfg.workingDirectoryLocked,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `getSessionInfo failed: ${errorMessage}`);\n const config = loadConfig();\n const model = config.agents?.defaults?.models?.chat?.primary;\n return { model: model ?? undefined };\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n const models = await getAvailableModels();\n return models.map((model) => ({\n id: model.id,\n name: model.name ?? model.id,\n provider: model.provider,\n contextWindow: model.contextWindow,\n }));\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n if (!this.agent) return;\n await this.agent.resetSession(sessionKey);\n }\n\n async patchSession(\n sessionKey: string,\n patch: Record<string, unknown>,\n ): Promise<void> {\n if (!this.agent) return;\n const result = await this.agent.sessionConfig.patch(sessionKey, {\n model: typeof patch.model === 'string' ? patch.model : undefined,\n thinkingLevel: typeof patch.thinkingLevel === 'string' ? patch.thinkingLevel : undefined,\n reasoningLevel: typeof patch.reasoningLevel === 'string' ? patch.reasoningLevel : undefined,\n verboseLevel: typeof patch.verboseLevel === 'string' ? patch.verboseLevel : undefined,\n workingDirectory: typeof patch.workingDirectory === 'string' ? patch.workingDirectory : undefined,\n });\n if (!result.ok) {\n throw new Error(result.error);\n }\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean; instructions?: string },\n ): Promise<TuiCompactionResult> {\n if (!this.agent) return { compacted: false, summary: 'Agent not started' };\n try {\n const result = await this.agent.sessionInspector.compact(sessionKey, {\n force: options?.force ?? true,\n instructions: options?.instructions,\n });\n if (!result.compacted) return { compacted: false, summary: 'Nothing to compact' };\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n transcriptSummary: result.summary,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async exportSession(sessionKey: string, format: ExportFormat): Promise<string> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.exportSession(sessionKey, format);\n }\n\n async importSession(\n targetSessionKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.importSessionExport(targetSessionKey, jsonContent);\n }\n\n async createShare(\n _sessionKey: string,\n request: TuiShareRequest,\n _options?: { agentId?: string },\n ): Promise<TuiShareResult> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const config = loadConfig();\n if (!isShareToolAvailable(config)) {\n throw new Error('Sharing is disabled in gateway config');\n }\n const workspace = getWorkspacePath(config);\n const tool = createCreateShareTool({\n workspace,\n getConfig: () => config,\n getAgentId: () => 'tui',\n });\n const result = await tool.execute(\n 'tui-share',\n {\n filePath: request.path,\n audience: request.audience,\n mode: request.mode,\n title: request.title,\n description: request.description,\n },\n );\n const details = (result.details ?? {}) as Record<string, unknown>;\n const error = typeof details.error === 'string' ? details.error : undefined;\n if (error) {\n throw new Error(error);\n }\n const shareUrl = typeof details.shareUrl === 'string' ? details.shareUrl : undefined;\n if (!shareUrl) {\n const text = result.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .join('\\n')\n .trim();\n throw new Error(text || 'Share failed');\n }\n return {\n kind: typeof details.kind === 'string' ? details.kind : 'share',\n shareUrl,\n title: typeof details.title === 'string' ? details.title : undefined,\n description: typeof details.description === 'string' ? details.description : undefined,\n thumbnailUrl: typeof details.thumbnailUrl === 'string' ? details.thumbnailUrl : undefined,\n reachability: typeof details.reachability === 'string' ? details.reachability : undefined,\n reachabilityHint:\n typeof details.reachabilityHint === 'string' ? details.reachabilityHint : undefined,\n expiresAt: typeof details.expiresAt === 'string' ? details.expiresAt : undefined,\n maxViews: typeof details.maxViews === 'number' ? details.maxViews : null,\n routingReason:\n typeof (details.routing as { reason?: unknown } | undefined)?.reason === 'string'\n ? ((details.routing as { reason: string }).reason)\n : undefined,\n routingHint:\n typeof (details.routing as { hint?: unknown } | undefined)?.hint === 'string'\n ? ((details.routing as { hint: string }).hint)\n : undefined,\n };\n }\n\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n if (!this.agent) {\n return { text: '', error: 'Agent not started' };\n }\n return this.agent.sessionInspector.btwQuery(sessionKey, question);\n }\n\n async forkSession(\n sourceSessionKey: string,\n targetSessionKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.forkSession(sourceSessionKey, targetSessionKey);\n }\n\n async forkSessionAt(\n sourceSessionKey: string,\n targetSessionKey: string,\n entryId: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const throughRow = transcriptTreeEntryIdToRowNumber(entryId);\n if (throughRow == null) {\n throw new Error(`Invalid transcript entry: ${entryId}`);\n }\n return this.agent.sessionStore.forkSessionRows(sourceSessionKey, targetSessionKey, { throughRow });\n }\n\n async setTranscriptLabel(\n sessionKey: string,\n entryId: string,\n label: string | undefined,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptLabelEntry(sessionKey, { targetId: entryId, label });\n return { ok: true };\n }\n\n async appendCustomEntry(\n sessionKey: string,\n customType: string,\n data?: unknown,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomEntry(sessionKey, { customType, data });\n return { ok: true };\n }\n\n async appendCustomMessage(\n sessionKey: string,\n message: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomMessageEntry(sessionKey, message);\n evictEmbeddedSessionRunner(sessionKey, 'tui_custom_message_appended');\n return { ok: true };\n }\n\n async appendBashExecution(\n sessionKey: string,\n entry: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptBashExecutionEntry(sessionKey, entry);\n evictEmbeddedSessionRunner(sessionKey, 'tui_bash_execution_appended');\n return { ok: true };\n }\n\n private processOutbound(): void {\n void (async () => {\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n log.debug({ channel: msg.channel, chatId: msg.chat_id }, 'Outbound message');\n } catch (error) {\n if (error instanceof MessageBusShutdownError) break;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Outbound processor failed: ${errorMessage}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"embedded-backend.js","names":[],"sources":["../../../../src/tui/backends/embedded-backend.ts"],"sourcesContent":["import type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { AgentService } from '../../agent/index.js';\nimport { listAgentEntries, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { resolveAgentIdFromSessionKey } from '../../routing/agent-session-key.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { createCreateShareTool, isShareToolAvailable } from '../../agent/tools/create-share-tool.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { getAgentDefaultModelRef } from '../../config/schema.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { getAvailableModels } from '../../providers/index.js';\nimport { evictEmbeddedSessionRunner } from '../../agent/embedded/session-runner.js';\nimport type { ExportFormat } from '../../session/types.js';\nimport { SessionIndex } from '../../session/index.js';\nimport { openXopcDatabase } from '../../storage/sqlite/index.js';\nimport { buildWorkflowChildTools } from '../../agent/workflow/workflow-child-tools.js';\nimport type { GatewayWorkflowHost } from '../../gateway/gateway-workflow-host.types.js';\nimport { WorkflowRunService } from '../../workflows/service/workflow-run-service.js';\nimport { WorkflowSessionBridge } from '../../workflows/service/workflow-session-bridge.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiCompactionResult,\n TuiEvent,\n TuiModelChoice,\n TuiShareRequest,\n TuiShareResult,\n TuiSessionStats,\n TuiSessionItem,\n TuiTranscriptTreeEntry,\n TuiAgentInfo,\n TuiWorkspaceFileSearchEntry,\n TuiWorkflowRunStartRequest,\n TuiWorkflowRunStartResult,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { sessionMetadataToTuiItem } from '../tui-session-format.js';\nimport { computeTuiSessionStats } from '../tui-session-stats.js';\nimport { buildTuiTranscriptTree, transcriptTreeEntryIdToRowNumber } from '../tui-transcript-tree.js';\nimport { ChatStreamMapper } from '../../gateway/chat-stream/mapper.js';\nimport { collectTuiStartupResources } from '../tui-startup-resources.js';\nimport { fuzzySearchWorkspaceFiles } from '../../gateway/workspace-file-search.js';\n\nconst log = createLogger('TUI:Embedded');\n\ninterface EmbeddedBackendOptions {\n extensionRegistry?: ExtensionRegistryImpl;\n implicitTrustedWorkspace?: string;\n isWorkspaceTrusted?: (workspaceDir: string) => boolean | null | undefined;\n}\n\nfunction isPathSameOrInside(parentDir: string, childDir: string): boolean {\n const rel = relative(resolve(parentDir), resolve(childDir));\n return rel === '' || (!!rel && !rel.startsWith('..') && !isAbsolute(rel));\n}\n\n/**\n * TUI backend that runs the agent in-process (no gateway required).\n *\n * Wraps `AgentService` directly and emits TuiEvents by observing the\n * `MessageBus` output stream.\n */\nexport class EmbeddedBackend implements TuiBackend {\n private bus: MessageBus;\n private agent: AgentService | null = null;\n private config: ReturnType<typeof loadConfig> | null = null;\n private workspace = '';\n private sessionIndex: SessionIndex | null = null;\n private sessionIndexReady: Promise<void> | null = null;\n private workflowRunService: WorkflowRunService | null = null;\n private running = false;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n\n constructor(private readonly opts?: EmbeddedBackendOptions) {\n this.bus = new MessageBus();\n }\n\n get connectionLabel(): string {\n return 'local embedded';\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const config = loadConfig();\n this.config = config;\n const workspace = getWorkspacePath(config);\n this.workspace = workspace;\n const modelId = getAgentDefaultModelRef(config);\n openXopcDatabase();\n this.sessionIndex = new SessionIndex({ config });\n this.sessionIndexReady = this.sessionIndex.initialize().catch((err: unknown) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage }, `Embedded session index initialization failed: ${errorMessage}`);\n throw err;\n });\n\n this.agent = new AgentService(this.bus, {\n workspace,\n model: modelId,\n config,\n sessionStore: this.sessionIndex.getStore(),\n getWorkflowRunService: () => this.getWorkflowRunService(),\n extensionRegistry: this.opts?.extensionRegistry,\n isWorkspaceTrusted: (workspaceDir) => {\n const explicit = this.opts?.isWorkspaceTrusted?.(workspaceDir);\n if (explicit !== undefined && explicit !== null) {\n return explicit;\n }\n const implicit = this.opts?.implicitTrustedWorkspace;\n if (implicit && isPathSameOrInside(implicit, workspaceDir)) {\n return true;\n }\n return undefined;\n },\n });\n\n this.agent.start().then(() => {\n this.onConnected?.();\n }).catch((err) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage }, `Embedded agent failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n });\n\n // Process outbound messages in background\n this.processOutbound();\n }\n\n stop(): void {\n this.running = false;\n this.chatAbort?.abort();\n this.chatAbort = null;\n this.bus.shutdown();\n void this.agent?.stop();\n this.agent = null;\n }\n\n getActiveSignal(): AbortSignal | undefined {\n const signal = this.chatAbort?.signal;\n return signal && !signal.aborted ? signal : undefined;\n }\n\n private getWorkflowRunService(): WorkflowRunService {\n if (this.workflowRunService) return this.workflowRunService;\n if (!this.config || !this.sessionIndex || !this.agent) {\n throw new Error('Embedded workflow runtime is not ready.');\n }\n\n const host: GatewayWorkflowHost = {\n currentConfig: this.config,\n currentWorkspacePath: this.workspace,\n messageBusInstance: this.bus,\n agentService: {\n getModelForSession: (sessionKey) => this.agent!.getModelForSession(sessionKey),\n },\n sessionIndexInstance: this.sessionIndex,\n emit: (event, payload) => {\n this.onEvent?.({\n event,\n data: payload,\n source: 'broadcast',\n });\n },\n };\n\n this.workflowRunService = new WorkflowRunService({\n service: host,\n sessionBridge: new WorkflowSessionBridge(host),\n buildChildTools: buildWorkflowChildTools,\n });\n return this.workflowRunService;\n }\n\n async getStartupResources(sessionKey: string) {\n return collectTuiStartupResources(loadConfig(), sessionKey);\n }\n\n async startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult> {\n await this.sessionIndexReady;\n const agentId = opts.agentId?.trim() || resolveAgentIdFromSessionKey(opts.sessionKey);\n const result = await this.getWorkflowRunService().startWorkflowRun({\n agentId,\n definitionId: opts.definitionId,\n parentSessionKey: opts.sessionKey,\n source: { kind: 'chat', sessionKey: opts.sessionKey },\n goal: opts.goal,\n input: opts.input,\n });\n if (result.ok === false) {\n throw new Error(result.message);\n }\n return {\n runId: result.runId,\n sessionKey: result.sessionKey,\n definitionId: opts.definitionId,\n };\n }\n\n async searchWorkspaceFiles(\n sessionKey: string,\n query: string,\n options?: { limit?: number },\n ): Promise<TuiWorkspaceFileSearchEntry[]> {\n if (!this.agent) return [];\n try {\n const workspaceRoot = await this.agent.getEffectiveWorkspacePathForSession(sessionKey);\n return await fuzzySearchWorkspaceFiles(workspaceRoot, query, options?.limit ?? 15);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded workspace file search failed: ${errorMessage}`);\n return [];\n }\n }\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n if (!this.agent) throw new Error('Agent not started');\n\n const runId = crypto.randomUUID();\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n\n const mapper = new ChatStreamMapper({ runId, sessionKey: opts.sessionKey, channel: 'tui' });\n for (const event of mapper.start()) {\n this.onEvent?.({ event: event.type, data: event, source: 'embedded' });\n }\n\n // Run the stream in background so the TUI event loop stays responsive.\n void (async () => {\n try {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines (Telegram, Weixin, etc.).\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const messageForAgent = opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message);\n\n const stream = this.agent!.turnDispatcher.processDirectStreaming(\n messageForAgent,\n opts.sessionKey,\n opts.attachments,\n opts.thinking,\n { signal, runId },\n );\n\n for await (const event of stream) {\n if (signal.aborted) break;\n for (const mapped of mapper.map(event)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n\n if (!signal.aborted) {\n for (const mapped of mapper.end('success')) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n for (const mapped of mapper.error(errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n for (const mapped of mapper.end('error', errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n })();\n\n return { runId };\n }\n\n async abortChat(_opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n if (this.chatAbort) {\n this.chatAbort.abort();\n this.chatAbort = null;\n return { ok: true };\n }\n return { ok: false };\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n const ok = await this.agent.turnDispatcher.steerWebchatSession(opts.sessionKey, opts.message);\n return { ok };\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n if (!this.agent) {\n return { messages: [] };\n }\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(opts.sessionKey);\n return {\n messages: transcriptRowsToClientHistory(rows, { limit: opts.limit }),\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded loadHistory failed: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async loadTranscriptTree(sessionKey: string): Promise<TuiTranscriptTreeEntry[]> {\n if (!this.agent) return [];\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return buildTuiTranscriptTree(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded loadTranscriptTree failed: ${errorMessage}`);\n return [];\n }\n }\n\n async getSessionStats(sessionKey: string): Promise<TuiSessionStats> {\n if (!this.agent) return computeTuiSessionStats([]);\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return computeTuiSessionStats(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded getSessionStats failed: ${errorMessage}`);\n return computeTuiSessionStats([]);\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n if (!this.agent) return [];\n try {\n const result = await this.agent.sessionStore.list({\n limit: 200,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n return result.items.map(sessionMetadataToTuiItem);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded listSessions failed: ${errorMessage}`);\n return [];\n }\n }\n\n async listAgents(): Promise<TuiAgentInfo[]> {\n const config = loadConfig();\n const agents = new Map<string, TuiAgentInfo>();\n for (const entry of listAgentEntries(config)) {\n if (entry.enabled === false) continue;\n const id = normalizeAgentId(entry.id);\n agents.set(id, {\n id,\n enabled: true,\n });\n }\n return [...agents.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n await this.agent.sessionStore.updateMetadata(sessionKey, { name: name.trim() });\n return { ok: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded renameSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n const ok = await this.agent.sessionStore.deleteSession(sessionKey);\n return { ok };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded deleteSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n if (!this.agent) {\n const config = loadConfig();\n const model = getAgentDefaultModelRef(config);\n return { model: model ?? undefined };\n }\n try {\n const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);\n const parsed = parseModelRef(cfg.model);\n const usage = await this.agent.sessionInspector.contextUsage(sessionKey);\n return {\n model: parsed?.model ?? cfg.model,\n modelProvider: parsed?.provider,\n thinkingLevel: cfg.thinkingLevel,\n reasoningLevel: cfg.reasoningLevel,\n verboseLevel: cfg.verboseLevel,\n totalTokens: usage.estimatedTokens,\n contextWindow: usage.contextWindow,\n contextUsagePercent: usage.usagePercent,\n effectiveWorkspacePath: cfg.effectiveWorkspacePath,\n workingDirectoryLocked: cfg.workingDirectoryLocked,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `getSessionInfo failed: ${errorMessage}`);\n const config = loadConfig();\n const model = getAgentDefaultModelRef(config);\n return { model: model ?? undefined };\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n const models = await getAvailableModels();\n return models.map((model) => ({\n id: model.id,\n name: model.name ?? model.id,\n provider: model.provider,\n contextWindow: model.contextWindow,\n }));\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n if (!this.agent) return;\n await this.agent.resetSession(sessionKey);\n }\n\n async patchSession(\n sessionKey: string,\n patch: Record<string, unknown>,\n ): Promise<void> {\n if (!this.agent) return;\n const result = await this.agent.sessionConfig.patch(sessionKey, {\n model: typeof patch.model === 'string' ? patch.model : undefined,\n thinkingLevel: typeof patch.thinkingLevel === 'string' ? patch.thinkingLevel : undefined,\n reasoningLevel: typeof patch.reasoningLevel === 'string' ? patch.reasoningLevel : undefined,\n verboseLevel: typeof patch.verboseLevel === 'string' ? patch.verboseLevel : undefined,\n workingDirectory: typeof patch.workingDirectory === 'string' ? patch.workingDirectory : undefined,\n });\n if (!result.ok) {\n throw new Error(result.error);\n }\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean; instructions?: string },\n ): Promise<TuiCompactionResult> {\n if (!this.agent) return { compacted: false, summary: 'Agent not started' };\n try {\n const result = await this.agent.sessionInspector.compact(sessionKey, {\n force: options?.force ?? true,\n instructions: options?.instructions,\n });\n if (!result.compacted) return { compacted: false, summary: 'Nothing to compact' };\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n transcriptSummary: result.summary,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async exportSession(sessionKey: string, format: ExportFormat): Promise<string> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.exportSession(sessionKey, format);\n }\n\n async importSession(\n targetSessionKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.importSessionExport(targetSessionKey, jsonContent);\n }\n\n async createShare(\n _sessionKey: string,\n request: TuiShareRequest,\n _options?: { agentId?: string },\n ): Promise<TuiShareResult> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const config = loadConfig();\n if (!isShareToolAvailable(config)) {\n throw new Error('Sharing is disabled in gateway config');\n }\n const workspace = getWorkspacePath(config);\n const tool = createCreateShareTool({\n workspace,\n getConfig: () => config,\n getAgentId: () => 'tui',\n });\n const result = await tool.execute(\n 'tui-share',\n {\n filePath: request.path,\n audience: request.audience,\n mode: request.mode,\n title: request.title,\n description: request.description,\n },\n );\n const details = (result.details ?? {}) as Record<string, unknown>;\n const error = typeof details.error === 'string' ? details.error : undefined;\n if (error) {\n throw new Error(error);\n }\n const shareUrl = typeof details.shareUrl === 'string' ? details.shareUrl : undefined;\n if (!shareUrl) {\n const text = result.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .join('\\n')\n .trim();\n throw new Error(text || 'Share failed');\n }\n return {\n kind: typeof details.kind === 'string' ? details.kind : 'share',\n shareUrl,\n title: typeof details.title === 'string' ? details.title : undefined,\n description: typeof details.description === 'string' ? details.description : undefined,\n thumbnailUrl: typeof details.thumbnailUrl === 'string' ? details.thumbnailUrl : undefined,\n reachability: typeof details.reachability === 'string' ? details.reachability : undefined,\n reachabilityHint:\n typeof details.reachabilityHint === 'string' ? details.reachabilityHint : undefined,\n expiresAt: typeof details.expiresAt === 'string' ? details.expiresAt : undefined,\n maxViews: typeof details.maxViews === 'number' ? details.maxViews : null,\n routingReason:\n typeof (details.routing as { reason?: unknown } | undefined)?.reason === 'string'\n ? ((details.routing as { reason: string }).reason)\n : undefined,\n routingHint:\n typeof (details.routing as { hint?: unknown } | undefined)?.hint === 'string'\n ? ((details.routing as { hint: string }).hint)\n : undefined,\n };\n }\n\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n if (!this.agent) {\n return { text: '', error: 'Agent not started' };\n }\n return this.agent.sessionInspector.btwQuery(sessionKey, question);\n }\n\n async forkSession(\n sourceSessionKey: string,\n targetSessionKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.forkSession(sourceSessionKey, targetSessionKey);\n }\n\n async forkSessionAt(\n sourceSessionKey: string,\n targetSessionKey: string,\n entryId: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const throughRow = transcriptTreeEntryIdToRowNumber(entryId);\n if (throughRow == null) {\n throw new Error(`Invalid transcript entry: ${entryId}`);\n }\n return this.agent.sessionStore.forkSessionRows(sourceSessionKey, targetSessionKey, { throughRow });\n }\n\n async setTranscriptLabel(\n sessionKey: string,\n entryId: string,\n label: string | undefined,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptLabelEntry(sessionKey, { targetId: entryId, label });\n return { ok: true };\n }\n\n async appendCustomEntry(\n sessionKey: string,\n customType: string,\n data?: unknown,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomEntry(sessionKey, { customType, data });\n return { ok: true };\n }\n\n async appendCustomMessage(\n sessionKey: string,\n message: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomMessageEntry(sessionKey, message);\n evictEmbeddedSessionRunner(sessionKey, 'tui_custom_message_appended');\n return { ok: true };\n }\n\n async appendBashExecution(\n sessionKey: string,\n entry: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptBashExecutionEntry(sessionKey, entry);\n evictEmbeddedSessionRunner(sessionKey, 'tui_bash_execution_appended');\n return { ok: true };\n }\n\n private processOutbound(): void {\n void (async () => {\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n log.debug({ channel: msg.channel, chatId: msg.chat_id }, 'Outbound message');\n } catch (error) {\n if (error instanceof MessageBusShutdownError) break;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Outbound processor failed: ${errorMessage}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-extensions.js","names":[],"sources":["../../../../src/tui/extension-host/load-extensions.ts"],"sourcesContent":["import { ExtensionLoader } from '../../extensions/loader.js';\nimport type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { resolveExtensionsDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('TUI:Extensions');\n\n/** Load activated extensions for embedded TUI (shared registry with AgentService). */\nexport async function loadExtensionsForTuiLocalMode(runtime?: {\n setLabel?: (entryId: string, label: string | undefined) => void;\n sendUserMessage?: import('../../extensions/types/core.js').ExtensionRuntime['sendUserMessage'];\n appendEntry?: import('../../extensions/types/core.js').ExtensionRuntime['appendEntry'];\n sendMessage?: import('../../extensions/types/core.js').ExtensionRuntime['sendMessage'];\n}): Promise<ExtensionRegistryImpl> {\n const config = loadConfig();\n const workspaceDir = getWorkspacePath(config);\n const loader = new ExtensionLoader({\n workspaceDir,\n extensionsDir: resolveExtensionsDir(),\n });\n loader.setConfig(config);\n if (runtime) {\n loader.setRuntimeContext(runtime);\n }\n try {\n await loader.loadByActivationPlan();\n const registry = loader.getRegistry();\n log.info(\n {\n extensions: registry.extensions.size,\n tuiRegistrations: registry.getTuiRegistrations().length,\n },\n 'Extensions loaded for local TUI',\n );\n return registry;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage }, `Extension load for TUI failed: ${errorMessage}`);\n return loader.getRegistry();\n }\n}\n"],"mappings":";;;;;;;;YAG6D;aACR;AAErD,MAAM,MAAM,aAAa,iBAAiB;;AAG1C,eAAsB,8BAA8B,SAKjB;CACjC,MAAM,SAAS,YAAY;CAE3B,MAAM,SAAS,IAAI,gBAAgB;EACjC,cAFmB,iBAAiB,OAExB;EACZ,eAAe,sBAAsB;EACtC,CAAC;AACF,QAAO,UAAU,
|
|
1
|
+
{"version":3,"file":"load-extensions.js","names":[],"sources":["../../../../src/tui/extension-host/load-extensions.ts"],"sourcesContent":["import { ExtensionLoader } from '../../extensions/loader.js';\nimport type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { resolveExtensionsDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('TUI:Extensions');\n\n/** Load activated extensions for embedded TUI (shared registry with AgentService). */\nexport async function loadExtensionsForTuiLocalMode(runtime?: {\n setLabel?: (entryId: string, label: string | undefined) => void;\n sendUserMessage?: import('../../extensions/types/core.js').ExtensionRuntime['sendUserMessage'];\n appendEntry?: import('../../extensions/types/core.js').ExtensionRuntime['appendEntry'];\n sendMessage?: import('../../extensions/types/core.js').ExtensionRuntime['sendMessage'];\n}): Promise<ExtensionRegistryImpl> {\n const config = loadConfig();\n const workspaceDir = getWorkspacePath(config);\n const loader = new ExtensionLoader({\n workspaceDir,\n extensionsDir: resolveExtensionsDir(),\n });\n loader.setConfig(config as Parameters<ExtensionLoader['setConfig']>[0]);\n if (runtime) {\n loader.setRuntimeContext(runtime);\n }\n try {\n await loader.loadByActivationPlan();\n const registry = loader.getRegistry();\n log.info(\n {\n extensions: registry.extensions.size,\n tuiRegistrations: registry.getTuiRegistrations().length,\n },\n 'Extensions loaded for local TUI',\n );\n return registry;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage }, `Extension load for TUI failed: ${errorMessage}`);\n return loader.getRegistry();\n }\n}\n"],"mappings":";;;;;;;;YAG6D;aACR;AAErD,MAAM,MAAM,aAAa,iBAAiB;;AAG1C,eAAsB,8BAA8B,SAKjB;CACjC,MAAM,SAAS,YAAY;CAE3B,MAAM,SAAS,IAAI,gBAAgB;EACjC,cAFmB,iBAAiB,OAExB;EACZ,eAAe,sBAAsB;EACtC,CAAC;AACF,QAAO,UAAU,OAAsD;AACvE,KAAI,QACF,QAAO,kBAAkB,QAAQ;AAEnC,KAAI;AACF,QAAM,OAAO,sBAAsB;EACnC,MAAM,WAAW,OAAO,aAAa;AACrC,MAAI,KACF;GACE,YAAY,SAAS,WAAW;GAChC,kBAAkB,SAAS,qBAAqB,CAAC;GAClD,EACD,kCACD;AACD,SAAO;UACA,KAAK;EACZ,MAAM,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AACrE,MAAI,KAAK;GAAE;GAAK;GAAc,EAAE,kCAAkC,eAAe;AACjF,SAAO,OAAO,aAAa"}
|
|
@@ -42,7 +42,7 @@ export interface OpenAiCompatibleSpeechProviderExtraJsonBodyField {
|
|
|
42
42
|
export interface OpenAiCompatibleSpeechProviderOptions {
|
|
43
43
|
/** Canonical provider id (e.g. "openai", "minimax"). */
|
|
44
44
|
id: string;
|
|
45
|
-
/** Optional aliases for
|
|
45
|
+
/** Optional aliases for provider lookup. */
|
|
46
46
|
aliases?: readonly string[];
|
|
47
47
|
/** Human-readable label for error messages and logs (e.g. "OpenAI", "MiniMax"). */
|
|
48
48
|
label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-compatible-speech.js","names":["normalizeBaseUrlGeneric"],"sources":["../../../../src/voice/tts/openai-compatible-speech.ts"],"sourcesContent":["/**\n * Factory: createOpenAiCompatibleSpeechProvider\n *\n * Most TTS providers (OpenAI, MiniMax, ElevenLabs, OpenRouter, ...) speak the\n * OpenAI `/audio/speech` POST shape. This factory eliminates the boilerplate so\n * a new compatible vendor is ~30 lines of declaration instead of a full provider\n * implementation.\n *\n * Ported from openclaw/src/tts/openai-compatible-speech-provider.ts (commit\n * baseline 2026-05-08), with the following INTENTIONAL OMISSIONS per\n * docs/voice-rearchitecture.md §15.3:\n * - resolveTalkConfig / resolveTalkOverrides (talk-mode is excluded)\n * - Persona resolution context (persona system is excluded)\n * - Custom dispatcherPolicy (xopc uses Node 22 native fetch — see ssrf-guard.ts)\n *\n * DECISION: We use `Record<string, unknown>` for the extra-config slot rather\n * than openclaw's generic `<ExtraConfig extends Record<string, unknown>>`. The\n * generic adds significant TypeScript complexity but no runtime safety; we\n * declare per-provider config types in each provider file's own normalizer.\n *\n * DECISION: `voiceCompatibleResponseFormats` defaults to `['opus']` if omitted,\n * because Telegram voice notes require opus/ogg. Providers that natively support\n * opus output (OpenAI, ElevenLabs) can rely on this; others (MiniMax mp3-only)\n * pass an empty array and downstream ffmpeg-compresses to opus.\n */\n\nimport {\n ProviderHttpError,\n fetchWithTimeoutGuarded,\n normalizeBaseUrl as normalizeBaseUrlGeneric,\n postJsonRequest,\n} from '../../media-shared/http/index.js';\nimport { createLogger } from '../../utils/logger.js';\n\nimport type {\n SpeechDirectiveTokenParseContext,\n SpeechDirectiveTokenParseResult,\n SpeechProviderConfig,\n SpeechProviderConfiguredContext,\n SpeechProviderOverrides,\n SpeechProviderPlugin,\n SpeechProviderResolveConfigContext,\n SpeechSynthesisRequest,\n SpeechSynthesisResult,\n SpeechVoiceOption,\n} from './speech-provider-types.js';\n\nconst log = createLogger('SpeechProvider:OpenAiCompat');\n\ninterface OpenAiCompatibleBaseConfig {\n apiKey?: string;\n baseUrl?: string;\n model: string;\n voice: string;\n speed?: number;\n responseFormat?: string;\n}\n\nexport type OpenAiCompatibleSpeechProviderConfig = OpenAiCompatibleBaseConfig &\n Record<string, unknown>;\n\nexport interface OpenAiCompatibleSpeechProviderExtraJsonBodyField {\n /** Key to read from normalized config. */\n configKey: string;\n /** Key to send in the HTTP body. Defaults to `configKey`. */\n requestKey?: string;\n}\n\nexport interface OpenAiCompatibleSpeechProviderOptions {\n /** Canonical provider id (e.g. \"openai\", \"minimax\"). */\n id: string;\n /** Optional aliases for back-compat config strings. */\n aliases?: readonly string[];\n /** Human-readable label for error messages and logs (e.g. \"OpenAI\", \"MiniMax\"). */\n label: string;\n /** Sort key for UI auto-fallback ordering. Lower = higher priority. */\n autoSelectOrder: number;\n /** Allowed model ids. Used by listVoices and validation. */\n models: readonly string[];\n /** Allowed voice ids. */\n voices: readonly string[];\n /** Default model when config does not specify. */\n defaultModel: string;\n /** Default voice. */\n defaultVoice: string;\n /** Default base URL (e.g. \"https://api.openai.com/v1\"). */\n defaultBaseUrl: string;\n /** Env var name for the api key (e.g. \"OPENAI_API_KEY\"). */\n envKey: string;\n /** Allowed response formats. */\n responseFormats: readonly string[];\n /** Default response format (e.g. \"opus\" for OpenAI, \"mp3\" for MiniMax). */\n defaultResponseFormat: string;\n /** Response formats that produce a voice-note-compatible buffer (no ffmpeg needed). */\n voiceCompatibleResponseFormats?: readonly string[];\n /** Extra static headers (e.g. `{ \"OpenAI-Beta\": \"...\" }`). Auth header is added automatically. */\n extraHeaders?: Record<string, string>;\n /** Read provider-specific extra config keys (e.g. MiniMax's `groupId`). */\n readExtraConfig?: (raw: Record<string, unknown> | undefined) => Record<string, unknown>;\n /** Provider-specific extra body fields to copy from config into request JSON. */\n extraJsonBodyFields?: readonly OpenAiCompatibleSpeechProviderExtraJsonBodyField[];\n /** Override the API error label (defaults to `${label} TTS API error`). */\n apiErrorLabel?: string;\n /** Override the missing-key error message. */\n missingApiKeyError?: string;\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction trimToUndefined(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === 'string') {\n const parsed = Number.parseFloat(value);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction compactKey(value: string): string {\n return value.replace(/[^a-z0-9]+/giu, '').toLowerCase();\n}\n\nfunction normalizeResponseFormat(label: string, formats: readonly string[], value: unknown): string | undefined {\n const next = trimToUndefined(value)?.toLowerCase();\n if (!next) {\n return undefined;\n }\n if (formats.includes(next)) {\n return next;\n }\n throw new Error(`Invalid ${label} speech responseFormat: ${next}`);\n}\n\nfunction buildExtraJsonBodyFields(\n config: OpenAiCompatibleSpeechProviderConfig,\n fields: readonly OpenAiCompatibleSpeechProviderExtraJsonBodyField[] | undefined,\n): Record<string, unknown> {\n const body: Record<string, unknown> = {};\n for (const field of fields ?? []) {\n const value = config[field.configKey];\n if (value !== undefined && value !== null && value !== '') {\n body[field.requestKey ?? field.configKey] = value;\n }\n }\n return body;\n}\n\nfunction readSpeechOverrides(overrides: SpeechProviderOverrides | undefined): {\n model?: string;\n voice?: string;\n speed?: number;\n} {\n if (!overrides) {\n return {};\n }\n return {\n model: trimToUndefined(overrides.model ?? overrides.modelId),\n voice: trimToUndefined(overrides.voice ?? overrides.voiceId),\n speed: asFiniteNumber(overrides.speed),\n };\n}\n\nfunction parseDirectiveTokenInternal(\n ctx: SpeechDirectiveTokenParseContext,\n providerConfigKey: string,\n): SpeechDirectiveTokenParseResult {\n const compactProviderKey = compactKey(providerConfigKey);\n switch (ctx.key) {\n case 'voice':\n case 'voice_id':\n case 'voiceid':\n case `${providerConfigKey}_voice`:\n case `${compactProviderKey}voice`:\n if (!ctx.policy.allowVoice) {\n return { handled: true };\n }\n return { handled: true, overrides: { voice: ctx.value } };\n case 'model':\n case 'model_id':\n case 'modelid':\n case `${providerConfigKey}_model`:\n case `${compactProviderKey}model`:\n if (!ctx.policy.allowModelId) {\n return { handled: true };\n }\n return { handled: true, overrides: { model: ctx.value } };\n case 'speed': {\n const speed = asFiniteNumber(ctx.value);\n if (speed === undefined) {\n return { handled: true, warnings: [`Invalid speed value: ${ctx.value}`] };\n }\n return { handled: true, overrides: { speed } };\n }\n default:\n return { handled: false };\n }\n}\n\nexport function createOpenAiCompatibleSpeechProvider(\n options: OpenAiCompatibleSpeechProviderOptions,\n): SpeechProviderPlugin {\n const providerConfigKey = options.id;\n const voiceCompatibleResponseFormats = options.voiceCompatibleResponseFormats ?? ['opus'];\n\n function readExtraConfig(raw: Record<string, unknown> | undefined): Record<string, unknown> {\n return options.readExtraConfig?.(raw) ?? {};\n }\n\n function normalizeConfig(rawConfig: Record<string, unknown>): OpenAiCompatibleSpeechProviderConfig {\n const raw = asObject(rawConfig[providerConfigKey]) ?? rawConfig;\n return {\n apiKey: trimToUndefined(raw.apiKey),\n baseUrl: trimToUndefined(raw.baseUrl)\n ? normalizeBaseUrlGeneric(String(raw.baseUrl))\n : undefined,\n model: trimToUndefined(raw.model ?? raw.modelId) ?? options.defaultModel,\n voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? options.defaultVoice,\n speed: asFiniteNumber(raw.speed),\n responseFormat: normalizeResponseFormat(options.label, options.responseFormats, raw.responseFormat),\n ...readExtraConfig(raw),\n };\n }\n\n function readProviderConfig(config: SpeechProviderConfig): OpenAiCompatibleSpeechProviderConfig {\n return {\n apiKey: trimToUndefined(config.apiKey),\n baseUrl: trimToUndefined(config.baseUrl)\n ? normalizeBaseUrlGeneric(String(config.baseUrl))\n : undefined,\n model: trimToUndefined(config.model ?? config.modelId) ?? options.defaultModel,\n voice: trimToUndefined(config.voice ?? config.voiceId) ?? options.defaultVoice,\n speed: asFiniteNumber(config.speed),\n responseFormat:\n normalizeResponseFormat(options.label, options.responseFormats, config.responseFormat) ??\n options.defaultResponseFormat,\n ...readExtraConfig(config),\n };\n }\n\n function resolveApiKey(providerConfig: OpenAiCompatibleSpeechProviderConfig): string | undefined {\n return providerConfig.apiKey ?? trimToUndefined(process.env[options.envKey]);\n }\n\n function resolveBaseUrl(providerConfig: OpenAiCompatibleSpeechProviderConfig): string {\n return providerConfig.baseUrl ?? options.defaultBaseUrl.replace(/\\/+$/u, '');\n }\n\n return {\n id: options.id,\n aliases: options.aliases,\n autoSelectOrder: options.autoSelectOrder,\n\n resolveConfig: (ctx: SpeechProviderResolveConfigContext) => normalizeConfig(ctx.rawConfig),\n\n isConfigured: (ctx: SpeechProviderConfiguredContext) =>\n Boolean(resolveApiKey(readProviderConfig(ctx.providerConfig))),\n\n parseDirectiveToken: (ctx) => parseDirectiveTokenInternal(ctx, providerConfigKey),\n\n listVoices: async (): Promise<SpeechVoiceOption[]> =>\n options.voices.map((voice) => ({ id: voice, name: voice })),\n\n synthesize: async (req: SpeechSynthesisRequest): Promise<SpeechSynthesisResult> => {\n const config = readProviderConfig(req.providerConfig);\n const overrides = readSpeechOverrides(req.providerOverrides);\n const apiKey = resolveApiKey(config);\n if (!apiKey) {\n throw new Error(options.missingApiKeyError ?? `${options.label} API key missing`);\n }\n const baseUrl = resolveBaseUrl(config);\n const responseFormat = config.responseFormat ?? options.defaultResponseFormat;\n const speed = overrides.speed ?? config.speed;\n const model = overrides.model ?? config.model;\n const voice = overrides.voice ?? config.voice;\n\n const url = `${baseUrl}/audio/speech`;\n log.debug(\n { provider: options.id, model, voice, responseFormat, baseUrl, target: req.target },\n `Calling ${options.label} TTS`,\n );\n\n // postJsonRequest already throws ProviderHttpError on non-2xx.\n const response = await postJsonRequest(url, {\n timeoutMs: req.timeoutMs,\n label: options.apiErrorLabel ?? `${options.label} TTS API error`,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n ...options.extraHeaders,\n },\n body: {\n model,\n input: req.text,\n voice,\n response_format: responseFormat,\n ...(speed === undefined ? {} : { speed }),\n ...buildExtraJsonBodyFields(config, options.extraJsonBodyFields),\n },\n });\n\n const audioBuffer = Buffer.from(await response.arrayBuffer());\n return {\n audioBuffer,\n outputFormat: responseFormat,\n fileExtension: responseFormat,\n voiceCompatible: voiceCompatibleResponseFormats.includes(responseFormat),\n };\n },\n\n /**\n * Streaming via OpenAI's POST /audio/speech (the response body itself is\n * the audio stream — no SSE / WebSocket needed). We do NOT call assertOk\n * via postJsonRequest because we want to surface the body as-is for\n * streaming consumers; instead we manually check status and convert to\n * ProviderHttpError on failure (matches openclaw behavior).\n */\n synthesizeStream: async (req) => {\n const config = readProviderConfig(req.providerConfig);\n const overrides = readSpeechOverrides(req.providerOverrides);\n const apiKey = resolveApiKey(config);\n if (!apiKey) {\n throw new Error(options.missingApiKeyError ?? `${options.label} API key missing`);\n }\n const baseUrl = resolveBaseUrl(config);\n const responseFormat = config.responseFormat ?? options.defaultResponseFormat;\n const speed = overrides.speed ?? config.speed;\n const model = overrides.model ?? config.model;\n const voice = overrides.voice ?? config.voice;\n\n const response = await fetchWithTimeoutGuarded(`${baseUrl}/audio/speech`, {\n timeoutMs: req.timeoutMs,\n label: options.apiErrorLabel ?? `${options.label} TTS streaming`,\n init: {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'content-type': 'application/json',\n accept: 'application/octet-stream',\n ...options.extraHeaders,\n },\n body: JSON.stringify({\n model,\n input: req.text,\n voice,\n response_format: responseFormat,\n stream: true,\n ...(speed === undefined ? {} : { speed }),\n ...buildExtraJsonBodyFields(config, options.extraJsonBodyFields),\n }),\n },\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => undefined);\n throw new ProviderHttpError({\n label: options.apiErrorLabel ?? `${options.label} TTS streaming`,\n status: response.status,\n detail: detail?.slice(0, 220),\n });\n }\n if (!response.body) {\n throw new Error(`${options.label} TTS streaming: empty response body`);\n }\n\n return {\n audioStream: response.body,\n outputFormat: responseFormat,\n fileExtension: responseFormat,\n voiceCompatible: voiceCompatibleResponseFormats.includes(responseFormat),\n };\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAgCqD;AAerD,MAAM,MAAM,aAAa,8BAA8B;AA4DvD,SAAS,SAAS,OAAqD;AACrE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM,GACtE,QACD,KAAA;;AAGN,SAAS,gBAAgB,OAAoC;AAC3D,KAAI,OAAO,UAAU,SACnB;CAEF,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;;AAGxC,SAAS,eAAe,OAAoC;AAC1D,KAAI,OAAO,UAAU,YAAY,OAAO,SAAS,MAAM,CACrD,QAAO;AAET,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,SAAS,OAAO,WAAW,MAAM;AACvC,MAAI,OAAO,SAAS,OAAO,CACzB,QAAO;;;AAMb,SAAS,WAAW,OAAuB;AACzC,QAAO,MAAM,QAAQ,iBAAiB,GAAG,CAAC,aAAa;;AAGzD,SAAS,wBAAwB,OAAe,SAA4B,OAAoC;CAC9G,MAAM,OAAO,gBAAgB,MAAM,EAAE,aAAa;AAClD,KAAI,CAAC,KACH;AAEF,KAAI,QAAQ,SAAS,KAAK,CACxB,QAAO;AAET,OAAM,IAAI,MAAM,WAAW,MAAM,0BAA0B,OAAO;;AAGpE,SAAS,yBACP,QACA,QACyB;CACzB,MAAM,OAAgC,EAAE;AACxC,MAAK,MAAM,SAAS,UAAU,EAAE,EAAE;EAChC,MAAM,QAAQ,OAAO,MAAM;AAC3B,MAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,GACrD,MAAK,MAAM,cAAc,MAAM,aAAa;;AAGhD,QAAO;;AAGT,SAAS,oBAAoB,WAI3B;AACA,KAAI,CAAC,UACH,QAAO,EAAE;AAEX,QAAO;EACL,OAAO,gBAAgB,UAAU,SAAS,UAAU,QAAQ;EAC5D,OAAO,gBAAgB,UAAU,SAAS,UAAU,QAAQ;EAC5D,OAAO,eAAe,UAAU,MAAM;EACvC;;AAGH,SAAS,4BACP,KACA,mBACiC;CACjC,MAAM,qBAAqB,WAAW,kBAAkB;AACxD,SAAQ,IAAI,KAAZ;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,GAAG,kBAAkB;EAC1B,KAAK,GAAG,mBAAmB;AACzB,OAAI,CAAC,IAAI,OAAO,WACd,QAAO,EAAE,SAAS,MAAM;AAE1B,UAAO;IAAE,SAAS;IAAM,WAAW,EAAE,OAAO,IAAI,OAAO;IAAE;EAC3D,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,GAAG,kBAAkB;EAC1B,KAAK,GAAG,mBAAmB;AACzB,OAAI,CAAC,IAAI,OAAO,aACd,QAAO,EAAE,SAAS,MAAM;AAE1B,UAAO;IAAE,SAAS;IAAM,WAAW,EAAE,OAAO,IAAI,OAAO;IAAE;EAC3D,KAAK,SAAS;GACZ,MAAM,QAAQ,eAAe,IAAI,MAAM;AACvC,OAAI,UAAU,KAAA,EACZ,QAAO;IAAE,SAAS;IAAM,UAAU,CAAC,wBAAwB,IAAI,QAAQ;IAAE;AAE3E,UAAO;IAAE,SAAS;IAAM,WAAW,EAAE,OAAO;IAAE;;EAEhD,QACE,QAAO,EAAE,SAAS,OAAO;;;AAI/B,SAAgB,qCACd,SACsB;CACtB,MAAM,oBAAoB,QAAQ;CAClC,MAAM,iCAAiC,QAAQ,kCAAkC,CAAC,OAAO;CAEzF,SAAS,gBAAgB,KAAmE;AAC1F,SAAO,QAAQ,kBAAkB,IAAI,IAAI,EAAE;;CAG7C,SAAS,gBAAgB,WAA0E;EACjG,MAAM,MAAM,SAAS,UAAU,mBAAmB,IAAI;AACtD,SAAO;GACL,QAAQ,gBAAgB,IAAI,OAAO;GACnC,SAAS,gBAAgB,IAAI,QAAQ,GACjCA,iBAAwB,OAAO,IAAI,QAAQ,CAAC,GAC5C,KAAA;GACJ,OAAO,gBAAgB,IAAI,SAAS,IAAI,QAAQ,IAAI,QAAQ;GAC5D,OAAO,gBAAgB,IAAI,SAAS,IAAI,QAAQ,IAAI,QAAQ;GAC5D,OAAO,eAAe,IAAI,MAAM;GAChC,gBAAgB,wBAAwB,QAAQ,OAAO,QAAQ,iBAAiB,IAAI,eAAe;GACnG,GAAG,gBAAgB,IAAI;GACxB;;CAGH,SAAS,mBAAmB,QAAoE;AAC9F,SAAO;GACL,QAAQ,gBAAgB,OAAO,OAAO;GACtC,SAAS,gBAAgB,OAAO,QAAQ,GACpCA,iBAAwB,OAAO,OAAO,QAAQ,CAAC,GAC/C,KAAA;GACJ,OAAO,gBAAgB,OAAO,SAAS,OAAO,QAAQ,IAAI,QAAQ;GAClE,OAAO,gBAAgB,OAAO,SAAS,OAAO,QAAQ,IAAI,QAAQ;GAClE,OAAO,eAAe,OAAO,MAAM;GACnC,gBACE,wBAAwB,QAAQ,OAAO,QAAQ,iBAAiB,OAAO,eAAe,IACtF,QAAQ;GACV,GAAG,gBAAgB,OAAO;GAC3B;;CAGH,SAAS,cAAc,gBAA0E;AAC/F,SAAO,eAAe,UAAU,gBAAgB,QAAQ,IAAI,QAAQ,QAAQ;;CAG9E,SAAS,eAAe,gBAA8D;AACpF,SAAO,eAAe,WAAW,QAAQ,eAAe,QAAQ,SAAS,GAAG;;AAG9E,QAAO;EACL,IAAI,QAAQ;EACZ,SAAS,QAAQ;EACjB,iBAAiB,QAAQ;EAEzB,gBAAgB,QAA4C,gBAAgB,IAAI,UAAU;EAE1F,eAAe,QACb,QAAQ,cAAc,mBAAmB,IAAI,eAAe,CAAC,CAAC;EAEhE,sBAAsB,QAAQ,4BAA4B,KAAK,kBAAkB;EAEjF,YAAY,YACV,QAAQ,OAAO,KAAK,WAAW;GAAE,IAAI;GAAO,MAAM;GAAO,EAAE;EAE7D,YAAY,OAAO,QAAgE;GACjF,MAAM,SAAS,mBAAmB,IAAI,eAAe;GACrD,MAAM,YAAY,oBAAoB,IAAI,kBAAkB;GAC5D,MAAM,SAAS,cAAc,OAAO;AACpC,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,QAAQ,sBAAsB,GAAG,QAAQ,MAAM,kBAAkB;GAEnF,MAAM,UAAU,eAAe,OAAO;GACtC,MAAM,iBAAiB,OAAO,kBAAkB,QAAQ;GACxD,MAAM,QAAQ,UAAU,SAAS,OAAO;GACxC,MAAM,QAAQ,UAAU,SAAS,OAAO;GACxC,MAAM,QAAQ,UAAU,SAAS,OAAO;GAExC,MAAM,MAAM,GAAG,QAAQ;AACvB,OAAI,MACF;IAAE,UAAU,QAAQ;IAAI;IAAO;IAAO;IAAgB;IAAS,QAAQ,IAAI;IAAQ,EACnF,WAAW,QAAQ,MAAM,MAC1B;GAGD,MAAM,WAAW,MAAM,gBAAgB,KAAK;IAC1C,WAAW,IAAI;IACf,OAAO,QAAQ,iBAAiB,GAAG,QAAQ,MAAM;IACjD,SAAS;KACP,eAAe,UAAU;KACzB,GAAG,QAAQ;KACZ;IACD,MAAM;KACJ;KACA,OAAO,IAAI;KACX;KACA,iBAAiB;KACjB,GAAI,UAAU,KAAA,IAAY,EAAE,GAAG,EAAE,OAAO;KACxC,GAAG,yBAAyB,QAAQ,QAAQ,oBAAoB;KACjE;IACF,CAAC;AAGF,UAAO;IACL,aAFkB,OAAO,KAAK,MAAM,SAAS,aAAa,CAE/C;IACX,cAAc;IACd,eAAe;IACf,iBAAiB,+BAA+B,SAAS,eAAe;IACzE;;;;;;;;;EAUH,kBAAkB,OAAO,QAAQ;GAC/B,MAAM,SAAS,mBAAmB,IAAI,eAAe;GACrD,MAAM,YAAY,oBAAoB,IAAI,kBAAkB;GAC5D,MAAM,SAAS,cAAc,OAAO;AACpC,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,QAAQ,sBAAsB,GAAG,QAAQ,MAAM,kBAAkB;GAEnF,MAAM,UAAU,eAAe,OAAO;GACtC,MAAM,iBAAiB,OAAO,kBAAkB,QAAQ;GACxD,MAAM,QAAQ,UAAU,SAAS,OAAO;GACxC,MAAM,QAAQ,UAAU,SAAS,OAAO;GACxC,MAAM,QAAQ,UAAU,SAAS,OAAO;GAExC,MAAM,WAAW,MAAM,wBAAwB,GAAG,QAAQ,gBAAgB;IACxE,WAAW,IAAI;IACf,OAAO,QAAQ,iBAAiB,GAAG,QAAQ,MAAM;IACjD,MAAM;KACJ,QAAQ;KACR,SAAS;MACP,eAAe,UAAU;MACzB,gBAAgB;MAChB,QAAQ;MACR,GAAG,QAAQ;MACZ;KACD,MAAM,KAAK,UAAU;MACnB;MACA,OAAO,IAAI;MACX;MACA,iBAAiB;MACjB,QAAQ;MACR,GAAI,UAAU,KAAA,IAAY,EAAE,GAAG,EAAE,OAAO;MACxC,GAAG,yBAAyB,QAAQ,QAAQ,oBAAoB;MACjE,CAAC;KACH;IACF,CAAC;AAEF,OAAI,CAAC,SAAS,IAAI;IAChB,MAAM,SAAS,MAAM,SAAS,MAAM,CAAC,YAAY,KAAA,EAAU;AAC3D,UAAM,IAAI,kBAAkB;KAC1B,OAAO,QAAQ,iBAAiB,GAAG,QAAQ,MAAM;KACjD,QAAQ,SAAS;KACjB,QAAQ,QAAQ,MAAM,GAAG,IAAI;KAC9B,CAAC;;AAEJ,OAAI,CAAC,SAAS,KACZ,OAAM,IAAI,MAAM,GAAG,QAAQ,MAAM,qCAAqC;AAGxE,UAAO;IACL,aAAa,SAAS;IACtB,cAAc;IACd,eAAe;IACf,iBAAiB,+BAA+B,SAAS,eAAe;IACzE;;EAEJ"}
|
|
1
|
+
{"version":3,"file":"openai-compatible-speech.js","names":["normalizeBaseUrlGeneric"],"sources":["../../../../src/voice/tts/openai-compatible-speech.ts"],"sourcesContent":["/**\n * Factory: createOpenAiCompatibleSpeechProvider\n *\n * Most TTS providers (OpenAI, MiniMax, ElevenLabs, OpenRouter, ...) speak the\n * OpenAI `/audio/speech` POST shape. This factory eliminates the boilerplate so\n * a new compatible vendor is ~30 lines of declaration instead of a full provider\n * implementation.\n *\n * Ported from openclaw/src/tts/openai-compatible-speech-provider.ts (commit\n * baseline 2026-05-08), with the following INTENTIONAL OMISSIONS per\n * docs/voice-rearchitecture.md §15.3:\n * - resolveTalkConfig / resolveTalkOverrides (talk-mode is excluded)\n * - Persona resolution context (persona system is excluded)\n * - Custom dispatcherPolicy (xopc uses Node 22 native fetch — see ssrf-guard.ts)\n *\n * DECISION: We use `Record<string, unknown>` for the extra-config slot rather\n * than openclaw's generic `<ExtraConfig extends Record<string, unknown>>`. The\n * generic adds significant TypeScript complexity but no runtime safety; we\n * declare per-provider config types in each provider file's own normalizer.\n *\n * DECISION: `voiceCompatibleResponseFormats` defaults to `['opus']` if omitted,\n * because Telegram voice notes require opus/ogg. Providers that natively support\n * opus output (OpenAI, ElevenLabs) can rely on this; others (MiniMax mp3-only)\n * pass an empty array and downstream ffmpeg-compresses to opus.\n */\n\nimport {\n ProviderHttpError,\n fetchWithTimeoutGuarded,\n normalizeBaseUrl as normalizeBaseUrlGeneric,\n postJsonRequest,\n} from '../../media-shared/http/index.js';\nimport { createLogger } from '../../utils/logger.js';\n\nimport type {\n SpeechDirectiveTokenParseContext,\n SpeechDirectiveTokenParseResult,\n SpeechProviderConfig,\n SpeechProviderConfiguredContext,\n SpeechProviderOverrides,\n SpeechProviderPlugin,\n SpeechProviderResolveConfigContext,\n SpeechSynthesisRequest,\n SpeechSynthesisResult,\n SpeechVoiceOption,\n} from './speech-provider-types.js';\n\nconst log = createLogger('SpeechProvider:OpenAiCompat');\n\ninterface OpenAiCompatibleBaseConfig {\n apiKey?: string;\n baseUrl?: string;\n model: string;\n voice: string;\n speed?: number;\n responseFormat?: string;\n}\n\nexport type OpenAiCompatibleSpeechProviderConfig = OpenAiCompatibleBaseConfig &\n Record<string, unknown>;\n\nexport interface OpenAiCompatibleSpeechProviderExtraJsonBodyField {\n /** Key to read from normalized config. */\n configKey: string;\n /** Key to send in the HTTP body. Defaults to `configKey`. */\n requestKey?: string;\n}\n\nexport interface OpenAiCompatibleSpeechProviderOptions {\n /** Canonical provider id (e.g. \"openai\", \"minimax\"). */\n id: string;\n /** Optional aliases for provider lookup. */\n aliases?: readonly string[];\n /** Human-readable label for error messages and logs (e.g. \"OpenAI\", \"MiniMax\"). */\n label: string;\n /** Sort key for UI auto-fallback ordering. Lower = higher priority. */\n autoSelectOrder: number;\n /** Allowed model ids. Used by listVoices and validation. */\n models: readonly string[];\n /** Allowed voice ids. */\n voices: readonly string[];\n /** Default model when config does not specify. */\n defaultModel: string;\n /** Default voice. */\n defaultVoice: string;\n /** Default base URL (e.g. \"https://api.openai.com/v1\"). */\n defaultBaseUrl: string;\n /** Env var name for the api key (e.g. \"OPENAI_API_KEY\"). */\n envKey: string;\n /** Allowed response formats. */\n responseFormats: readonly string[];\n /** Default response format (e.g. \"opus\" for OpenAI, \"mp3\" for MiniMax). */\n defaultResponseFormat: string;\n /** Response formats that produce a voice-note-compatible buffer (no ffmpeg needed). */\n voiceCompatibleResponseFormats?: readonly string[];\n /** Extra static headers (e.g. `{ \"OpenAI-Beta\": \"...\" }`). Auth header is added automatically. */\n extraHeaders?: Record<string, string>;\n /** Read provider-specific extra config keys (e.g. MiniMax's `groupId`). */\n readExtraConfig?: (raw: Record<string, unknown> | undefined) => Record<string, unknown>;\n /** Provider-specific extra body fields to copy from config into request JSON. */\n extraJsonBodyFields?: readonly OpenAiCompatibleSpeechProviderExtraJsonBodyField[];\n /** Override the API error label (defaults to `${label} TTS API error`). */\n apiErrorLabel?: string;\n /** Override the missing-key error message. */\n missingApiKeyError?: string;\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction trimToUndefined(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === 'string') {\n const parsed = Number.parseFloat(value);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction compactKey(value: string): string {\n return value.replace(/[^a-z0-9]+/giu, '').toLowerCase();\n}\n\nfunction normalizeResponseFormat(label: string, formats: readonly string[], value: unknown): string | undefined {\n const next = trimToUndefined(value)?.toLowerCase();\n if (!next) {\n return undefined;\n }\n if (formats.includes(next)) {\n return next;\n }\n throw new Error(`Invalid ${label} speech responseFormat: ${next}`);\n}\n\nfunction buildExtraJsonBodyFields(\n config: OpenAiCompatibleSpeechProviderConfig,\n fields: readonly OpenAiCompatibleSpeechProviderExtraJsonBodyField[] | undefined,\n): Record<string, unknown> {\n const body: Record<string, unknown> = {};\n for (const field of fields ?? []) {\n const value = config[field.configKey];\n if (value !== undefined && value !== null && value !== '') {\n body[field.requestKey ?? field.configKey] = value;\n }\n }\n return body;\n}\n\nfunction readSpeechOverrides(overrides: SpeechProviderOverrides | undefined): {\n model?: string;\n voice?: string;\n speed?: number;\n} {\n if (!overrides) {\n return {};\n }\n return {\n model: trimToUndefined(overrides.model ?? overrides.modelId),\n voice: trimToUndefined(overrides.voice ?? overrides.voiceId),\n speed: asFiniteNumber(overrides.speed),\n };\n}\n\nfunction parseDirectiveTokenInternal(\n ctx: SpeechDirectiveTokenParseContext,\n providerConfigKey: string,\n): SpeechDirectiveTokenParseResult {\n const compactProviderKey = compactKey(providerConfigKey);\n switch (ctx.key) {\n case 'voice':\n case 'voice_id':\n case 'voiceid':\n case `${providerConfigKey}_voice`:\n case `${compactProviderKey}voice`:\n if (!ctx.policy.allowVoice) {\n return { handled: true };\n }\n return { handled: true, overrides: { voice: ctx.value } };\n case 'model':\n case 'model_id':\n case 'modelid':\n case `${providerConfigKey}_model`:\n case `${compactProviderKey}model`:\n if (!ctx.policy.allowModelId) {\n return { handled: true };\n }\n return { handled: true, overrides: { model: ctx.value } };\n case 'speed': {\n const speed = asFiniteNumber(ctx.value);\n if (speed === undefined) {\n return { handled: true, warnings: [`Invalid speed value: ${ctx.value}`] };\n }\n return { handled: true, overrides: { speed } };\n }\n default:\n return { handled: false };\n }\n}\n\nexport function createOpenAiCompatibleSpeechProvider(\n options: OpenAiCompatibleSpeechProviderOptions,\n): SpeechProviderPlugin {\n const providerConfigKey = options.id;\n const voiceCompatibleResponseFormats = options.voiceCompatibleResponseFormats ?? ['opus'];\n\n function readExtraConfig(raw: Record<string, unknown> | undefined): Record<string, unknown> {\n return options.readExtraConfig?.(raw) ?? {};\n }\n\n function normalizeConfig(rawConfig: Record<string, unknown>): OpenAiCompatibleSpeechProviderConfig {\n const raw = asObject(rawConfig[providerConfigKey]) ?? rawConfig;\n return {\n apiKey: trimToUndefined(raw.apiKey),\n baseUrl: trimToUndefined(raw.baseUrl)\n ? normalizeBaseUrlGeneric(String(raw.baseUrl))\n : undefined,\n model: trimToUndefined(raw.model ?? raw.modelId) ?? options.defaultModel,\n voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? options.defaultVoice,\n speed: asFiniteNumber(raw.speed),\n responseFormat: normalizeResponseFormat(options.label, options.responseFormats, raw.responseFormat),\n ...readExtraConfig(raw),\n };\n }\n\n function readProviderConfig(config: SpeechProviderConfig): OpenAiCompatibleSpeechProviderConfig {\n return {\n apiKey: trimToUndefined(config.apiKey),\n baseUrl: trimToUndefined(config.baseUrl)\n ? normalizeBaseUrlGeneric(String(config.baseUrl))\n : undefined,\n model: trimToUndefined(config.model ?? config.modelId) ?? options.defaultModel,\n voice: trimToUndefined(config.voice ?? config.voiceId) ?? options.defaultVoice,\n speed: asFiniteNumber(config.speed),\n responseFormat:\n normalizeResponseFormat(options.label, options.responseFormats, config.responseFormat) ??\n options.defaultResponseFormat,\n ...readExtraConfig(config),\n };\n }\n\n function resolveApiKey(providerConfig: OpenAiCompatibleSpeechProviderConfig): string | undefined {\n return providerConfig.apiKey ?? trimToUndefined(process.env[options.envKey]);\n }\n\n function resolveBaseUrl(providerConfig: OpenAiCompatibleSpeechProviderConfig): string {\n return providerConfig.baseUrl ?? options.defaultBaseUrl.replace(/\\/+$/u, '');\n }\n\n return {\n id: options.id,\n aliases: options.aliases,\n autoSelectOrder: options.autoSelectOrder,\n\n resolveConfig: (ctx: SpeechProviderResolveConfigContext) => normalizeConfig(ctx.rawConfig),\n\n isConfigured: (ctx: SpeechProviderConfiguredContext) =>\n Boolean(resolveApiKey(readProviderConfig(ctx.providerConfig))),\n\n parseDirectiveToken: (ctx) => parseDirectiveTokenInternal(ctx, providerConfigKey),\n\n listVoices: async (): Promise<SpeechVoiceOption[]> =>\n options.voices.map((voice) => ({ id: voice, name: voice })),\n\n synthesize: async (req: SpeechSynthesisRequest): Promise<SpeechSynthesisResult> => {\n const config = readProviderConfig(req.providerConfig);\n const overrides = readSpeechOverrides(req.providerOverrides);\n const apiKey = resolveApiKey(config);\n if (!apiKey) {\n throw new Error(options.missingApiKeyError ?? `${options.label} API key missing`);\n }\n const baseUrl = resolveBaseUrl(config);\n const responseFormat = config.responseFormat ?? options.defaultResponseFormat;\n const speed = overrides.speed ?? config.speed;\n const model = overrides.model ?? config.model;\n const voice = overrides.voice ?? config.voice;\n\n const url = `${baseUrl}/audio/speech`;\n log.debug(\n { provider: options.id, model, voice, responseFormat, baseUrl, target: req.target },\n `Calling ${options.label} TTS`,\n );\n\n // postJsonRequest already throws ProviderHttpError on non-2xx.\n const response = await postJsonRequest(url, {\n timeoutMs: req.timeoutMs,\n label: options.apiErrorLabel ?? `${options.label} TTS API error`,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n ...options.extraHeaders,\n },\n body: {\n model,\n input: req.text,\n voice,\n response_format: responseFormat,\n ...(speed === undefined ? {} : { speed }),\n ...buildExtraJsonBodyFields(config, options.extraJsonBodyFields),\n },\n });\n\n const audioBuffer = Buffer.from(await response.arrayBuffer());\n return {\n audioBuffer,\n outputFormat: responseFormat,\n fileExtension: responseFormat,\n voiceCompatible: voiceCompatibleResponseFormats.includes(responseFormat),\n };\n },\n\n /**\n * Streaming via OpenAI's POST /audio/speech (the response body itself is\n * the audio stream — no SSE / WebSocket needed). We do NOT call assertOk\n * via postJsonRequest because we want to surface the body as-is for\n * streaming consumers; instead we manually check status and convert to\n * ProviderHttpError on failure (matches openclaw behavior).\n */\n synthesizeStream: async (req) => {\n const config = readProviderConfig(req.providerConfig);\n const overrides = readSpeechOverrides(req.providerOverrides);\n const apiKey = resolveApiKey(config);\n if (!apiKey) {\n throw new Error(options.missingApiKeyError ?? `${options.label} API key missing`);\n }\n const baseUrl = resolveBaseUrl(config);\n const responseFormat = config.responseFormat ?? options.defaultResponseFormat;\n const speed = overrides.speed ?? config.speed;\n const model = overrides.model ?? config.model;\n const voice = overrides.voice ?? config.voice;\n\n const response = await fetchWithTimeoutGuarded(`${baseUrl}/audio/speech`, {\n timeoutMs: req.timeoutMs,\n label: options.apiErrorLabel ?? `${options.label} TTS streaming`,\n init: {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'content-type': 'application/json',\n accept: 'application/octet-stream',\n ...options.extraHeaders,\n },\n body: JSON.stringify({\n model,\n input: req.text,\n voice,\n response_format: responseFormat,\n stream: true,\n ...(speed === undefined ? {} : { speed }),\n ...buildExtraJsonBodyFields(config, options.extraJsonBodyFields),\n }),\n },\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => undefined);\n throw new ProviderHttpError({\n label: options.apiErrorLabel ?? `${options.label} TTS streaming`,\n status: response.status,\n detail: detail?.slice(0, 220),\n });\n }\n if (!response.body) {\n throw new Error(`${options.label} TTS streaming: empty response body`);\n }\n\n return {\n audioStream: response.body,\n outputFormat: responseFormat,\n fileExtension: responseFormat,\n voiceCompatible: voiceCompatibleResponseFormats.includes(responseFormat),\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { init_logger } from "../../../utils/logger.js";
|
|
|
3
3
|
import { registerSpeechProvider } from "../speech-registry.js";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
6
|
+
import { mkdtempSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
7
7
|
//#region src/voice/tts/providers/edge-speech.ts
|
|
8
8
|
/**
|
|
9
9
|
* Edge TTS provider — wraps the `node-edge-tts` package. Requires no API key
|
|
@@ -94,6 +94,15 @@ function parseDirectiveTokenInternal(ctx) {
|
|
|
94
94
|
default: return { handled: false };
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
+
async function waitForNonEmptyFile(filePath, timeoutMs) {
|
|
98
|
+
const deadline = Date.now() + Math.min(Math.max(timeoutMs, 1e3), 5e3);
|
|
99
|
+
while (Date.now() < deadline) {
|
|
100
|
+
try {
|
|
101
|
+
if (statSync(filePath).size > 0) return;
|
|
102
|
+
} catch {}
|
|
103
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
97
106
|
async function synthesizeToBuffer(text, config, voiceOverride, timeoutMs) {
|
|
98
107
|
const { EdgeTTS } = await import("node-edge-tts");
|
|
99
108
|
const voice = voiceOverride ?? config.voice;
|
|
@@ -116,8 +125,11 @@ async function synthesizeToBuffer(text, config, voiceOverride, timeoutMs) {
|
|
|
116
125
|
volume: config.volume,
|
|
117
126
|
timeout: timeoutMs
|
|
118
127
|
}).ttsPromise(text, outputPath);
|
|
128
|
+
await waitForNonEmptyFile(outputPath, timeoutMs);
|
|
129
|
+
const buffer = readFileSync(outputPath);
|
|
130
|
+
if (buffer.length === 0) throw new Error(`Edge TTS produced an empty ${format} file for voice ${voice}`);
|
|
119
131
|
return {
|
|
120
|
-
buffer
|
|
132
|
+
buffer,
|
|
121
133
|
outputFormat: format,
|
|
122
134
|
ext
|
|
123
135
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-speech.js","names":[],"sources":["../../../../../src/voice/tts/providers/edge-speech.ts"],"sourcesContent":["/**\n * Edge TTS provider — wraps the `node-edge-tts` package. Requires no API key\n * (Microsoft's Edge browser endpoint is unauthenticated).\n *\n * Implementation notes:\n * - Temp-file dance (mkdtemp → ttsPromise → readFileSync → rm) is required\n * because `node-edge-tts` does not expose a stream-to-buffer API.\n * - `synthesizeStream` is implemented as \"synthesize + wrap as single-chunk\n * stream\" so callers see uniform behavior. This is not a true network\n * stream; it's buffered-then-wrapped.\n * - `inferEdgeExtension` derives the file extension from Edge's outputFormat\n * string.\n */\n\nimport { mkdtempSync, readFileSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport path from 'node:path';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { registerSpeechProvider } from '../speech-registry.js';\nimport type {\n SpeechDirectiveTokenParseContext,\n SpeechDirectiveTokenParseResult,\n SpeechProviderConfig,\n SpeechProviderConfiguredContext,\n SpeechProviderPlugin,\n SpeechProviderResolveConfigContext,\n SpeechSynthesisRequest,\n SpeechSynthesisResult,\n SpeechSynthesisStreamRequest,\n SpeechSynthesisStreamResult,\n} from '../speech-provider-types.js';\n\nconst log = createLogger('SpeechProvider:Edge');\n\nconst DEFAULT_VOICE = 'en-US-MichelleNeural';\nconst DEFAULT_LANG = 'en-US';\nconst DEFAULT_OUTPUT_FORMAT = 'audio-24khz-48kbitrate-mono-mp3';\n\ninterface EdgeTtsConfig extends Record<string, unknown> {\n voice: string;\n lang: string;\n outputFormat: string;\n pitch?: string;\n rate?: string;\n volume?: string;\n proxy?: string;\n enabled: boolean;\n}\n\nfunction trimToUndefined(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction inferEdgeExtension(outputFormat: string): { ext: string; format: string } {\n const normalized = outputFormat.toLowerCase();\n if (normalized.includes('webm')) return { ext: 'webm', format: 'webm' };\n if (normalized.includes('opus')) return { ext: 'opus', format: 'opus' };\n if (normalized.includes('ogg')) return { ext: 'ogg', format: 'ogg' };\n if (normalized.includes('wav') || normalized.includes('riff') || normalized.includes('pcm')) {\n return { ext: 'wav', format: 'wav' };\n }\n return { ext: 'mp3', format: 'mp3' };\n}\n\nfunction normalizeConfig(rawConfig: Record<string, unknown>): EdgeTtsConfig {\n const raw = asObject(rawConfig.edge) ?? rawConfig;\n return {\n voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? DEFAULT_VOICE,\n lang: trimToUndefined(raw.lang) ?? DEFAULT_LANG,\n outputFormat: trimToUndefined(raw.outputFormat) ?? DEFAULT_OUTPUT_FORMAT,\n pitch: trimToUndefined(raw.pitch),\n rate: trimToUndefined(raw.rate),\n volume: trimToUndefined(raw.volume),\n proxy: trimToUndefined(raw.proxy),\n enabled: raw.enabled !== false, // default-on\n };\n}\n\nfunction readProviderConfig(config: SpeechProviderConfig): EdgeTtsConfig {\n return {\n voice: trimToUndefined(config.voice ?? config.voiceId) ?? DEFAULT_VOICE,\n lang: trimToUndefined(config.lang) ?? DEFAULT_LANG,\n outputFormat: trimToUndefined(config.outputFormat) ?? DEFAULT_OUTPUT_FORMAT,\n pitch: trimToUndefined(config.pitch),\n rate: trimToUndefined(config.rate),\n volume: trimToUndefined(config.volume),\n proxy: trimToUndefined(config.proxy),\n enabled: config.enabled !== false,\n };\n}\n\nfunction parseDirectiveTokenInternal(\n ctx: SpeechDirectiveTokenParseContext,\n): SpeechDirectiveTokenParseResult {\n switch (ctx.key) {\n case 'voice':\n case 'voice_id':\n case 'voiceid':\n case 'edge_voice':\n case 'edgevoice':\n if (!ctx.policy.allowVoice) {\n return { handled: true };\n }\n return { handled: true, overrides: { voice: ctx.value } };\n default:\n return { handled: false };\n }\n}\n\nasync function synthesizeToBuffer(\n text: string,\n config: EdgeTtsConfig,\n voiceOverride: string | undefined,\n timeoutMs: number,\n): Promise<{ buffer: Buffer; outputFormat: string; ext: string }> {\n // Lazy import — node-edge-tts loads ws transitively; skip cost for users that\n // never enable the edge provider.\n const { EdgeTTS } = await import('node-edge-tts');\n\n const voice = voiceOverride ?? config.voice;\n const tempDir = mkdtempSync(path.join(tmpdir(), 'tts-edge-'));\n const { ext, format } = inferEdgeExtension(config.outputFormat);\n const outputPath = path.join(tempDir, `speech-${Date.now()}.${ext}`);\n\n try {\n log.debug(\n { voice, outputFormat: config.outputFormat, textLength: text.length },\n 'Calling Edge TTS',\n );\n const tts = new EdgeTTS({\n voice,\n lang: config.lang,\n outputFormat: config.outputFormat,\n proxy: config.proxy,\n rate: config.rate,\n pitch: config.pitch,\n volume: config.volume,\n timeout: timeoutMs,\n });\n await tts.ttsPromise(text, outputPath);\n const buffer = readFileSync(outputPath);\n return { buffer, outputFormat: format, ext };\n } finally {\n try {\n rmSync(tempDir, { recursive: true, force: true });\n } catch (cleanupError) {\n log.warn({ err: cleanupError, tempDir }, 'Failed to cleanup Edge TTS temp directory');\n }\n }\n}\n\nexport const edgeSpeechProvider: SpeechProviderPlugin = {\n id: 'edge',\n autoSelectOrder: 100,\n\n resolveConfig: (ctx: SpeechProviderResolveConfigContext) => normalizeConfig(ctx.rawConfig),\n\n isConfigured: (ctx: SpeechProviderConfiguredContext) => readProviderConfig(ctx.providerConfig).enabled,\n\n parseDirectiveToken: parseDirectiveTokenInternal,\n\n // Edge has hundreds of voices; we don't enumerate them statically. UI should\n // call upstream Microsoft endpoint for the full catalog.\n listVoices: async () => [{ id: DEFAULT_VOICE, name: DEFAULT_VOICE, locale: DEFAULT_LANG }],\n\n synthesize: async (req: SpeechSynthesisRequest): Promise<SpeechSynthesisResult> => {\n const config = readProviderConfig(req.providerConfig);\n if (!config.enabled) {\n throw new Error('Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)');\n }\n const overrides = req.providerOverrides ?? {};\n const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);\n const { buffer, outputFormat, ext } = await synthesizeToBuffer(\n req.text,\n config,\n voiceOverride,\n req.timeoutMs,\n );\n return {\n audioBuffer: buffer,\n outputFormat,\n fileExtension: ext,\n voiceCompatible: outputFormat === 'opus' || outputFormat === 'ogg',\n };\n },\n\n // True streaming would require a node-edge-tts upstream change. We expose a\n // \"fake stream\" (single chunk) so callers don't fall through to speak-core's\n // wrapBufferAsStream branch and can treat all providers uniformly.\n synthesizeStream: async (req: SpeechSynthesisStreamRequest): Promise<SpeechSynthesisStreamResult> => {\n const config = readProviderConfig(req.providerConfig);\n if (!config.enabled) {\n throw new Error('Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)');\n }\n const overrides = req.providerOverrides ?? {};\n const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);\n const { buffer, outputFormat, ext } = await synthesizeToBuffer(\n req.text,\n config,\n voiceOverride,\n req.timeoutMs,\n );\n const audioStream = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(new Uint8Array(buffer));\n controller.close();\n },\n });\n return {\n audioStream,\n outputFormat,\n fileExtension: ext,\n voiceCompatible: outputFormat === 'opus' || outputFormat === 'ogg',\n };\n },\n};\n\nregisterSpeechProvider(edgeSpeechProvider);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"edge-speech.js","names":[],"sources":["../../../../../src/voice/tts/providers/edge-speech.ts"],"sourcesContent":["/**\n * Edge TTS provider — wraps the `node-edge-tts` package. Requires no API key\n * (Microsoft's Edge browser endpoint is unauthenticated).\n *\n * Implementation notes:\n * - Temp-file dance (mkdtemp → ttsPromise → readFileSync → rm) is required\n * because `node-edge-tts` does not expose a stream-to-buffer API.\n * - `synthesizeStream` is implemented as \"synthesize + wrap as single-chunk\n * stream\" so callers see uniform behavior. This is not a true network\n * stream; it's buffered-then-wrapped.\n * - `inferEdgeExtension` derives the file extension from Edge's outputFormat\n * string.\n */\n\nimport { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport path from 'node:path';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { registerSpeechProvider } from '../speech-registry.js';\nimport type {\n SpeechDirectiveTokenParseContext,\n SpeechDirectiveTokenParseResult,\n SpeechProviderConfig,\n SpeechProviderConfiguredContext,\n SpeechProviderPlugin,\n SpeechProviderResolveConfigContext,\n SpeechSynthesisRequest,\n SpeechSynthesisResult,\n SpeechSynthesisStreamRequest,\n SpeechSynthesisStreamResult,\n} from '../speech-provider-types.js';\n\nconst log = createLogger('SpeechProvider:Edge');\n\nconst DEFAULT_VOICE = 'en-US-MichelleNeural';\nconst DEFAULT_LANG = 'en-US';\nconst DEFAULT_OUTPUT_FORMAT = 'audio-24khz-48kbitrate-mono-mp3';\n\ninterface EdgeTtsConfig extends Record<string, unknown> {\n voice: string;\n lang: string;\n outputFormat: string;\n pitch?: string;\n rate?: string;\n volume?: string;\n proxy?: string;\n enabled: boolean;\n}\n\nfunction trimToUndefined(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction inferEdgeExtension(outputFormat: string): { ext: string; format: string } {\n const normalized = outputFormat.toLowerCase();\n if (normalized.includes('webm')) return { ext: 'webm', format: 'webm' };\n if (normalized.includes('opus')) return { ext: 'opus', format: 'opus' };\n if (normalized.includes('ogg')) return { ext: 'ogg', format: 'ogg' };\n if (normalized.includes('wav') || normalized.includes('riff') || normalized.includes('pcm')) {\n return { ext: 'wav', format: 'wav' };\n }\n return { ext: 'mp3', format: 'mp3' };\n}\n\nfunction normalizeConfig(rawConfig: Record<string, unknown>): EdgeTtsConfig {\n const raw = asObject(rawConfig.edge) ?? rawConfig;\n return {\n voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? DEFAULT_VOICE,\n lang: trimToUndefined(raw.lang) ?? DEFAULT_LANG,\n outputFormat: trimToUndefined(raw.outputFormat) ?? DEFAULT_OUTPUT_FORMAT,\n pitch: trimToUndefined(raw.pitch),\n rate: trimToUndefined(raw.rate),\n volume: trimToUndefined(raw.volume),\n proxy: trimToUndefined(raw.proxy),\n enabled: raw.enabled !== false, // default-on\n };\n}\n\nfunction readProviderConfig(config: SpeechProviderConfig): EdgeTtsConfig {\n return {\n voice: trimToUndefined(config.voice ?? config.voiceId) ?? DEFAULT_VOICE,\n lang: trimToUndefined(config.lang) ?? DEFAULT_LANG,\n outputFormat: trimToUndefined(config.outputFormat) ?? DEFAULT_OUTPUT_FORMAT,\n pitch: trimToUndefined(config.pitch),\n rate: trimToUndefined(config.rate),\n volume: trimToUndefined(config.volume),\n proxy: trimToUndefined(config.proxy),\n enabled: config.enabled !== false,\n };\n}\n\nfunction parseDirectiveTokenInternal(\n ctx: SpeechDirectiveTokenParseContext,\n): SpeechDirectiveTokenParseResult {\n switch (ctx.key) {\n case 'voice':\n case 'voice_id':\n case 'voiceid':\n case 'edge_voice':\n case 'edgevoice':\n if (!ctx.policy.allowVoice) {\n return { handled: true };\n }\n return { handled: true, overrides: { voice: ctx.value } };\n default:\n return { handled: false };\n }\n}\n\nasync function waitForNonEmptyFile(filePath: string, timeoutMs: number): Promise<void> {\n const deadline = Date.now() + Math.min(Math.max(timeoutMs, 1000), 5000);\n while (Date.now() < deadline) {\n try {\n if (statSync(filePath).size > 0) return;\n } catch {\n // The Edge package may resolve before the file is visible on slower filesystems.\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n}\n\nasync function synthesizeToBuffer(\n text: string,\n config: EdgeTtsConfig,\n voiceOverride: string | undefined,\n timeoutMs: number,\n): Promise<{ buffer: Buffer; outputFormat: string; ext: string }> {\n // Lazy import — node-edge-tts loads ws transitively; skip cost for users that\n // never enable the edge provider.\n const { EdgeTTS } = await import('node-edge-tts');\n\n const voice = voiceOverride ?? config.voice;\n const tempDir = mkdtempSync(path.join(tmpdir(), 'tts-edge-'));\n const { ext, format } = inferEdgeExtension(config.outputFormat);\n const outputPath = path.join(tempDir, `speech-${Date.now()}.${ext}`);\n\n try {\n log.debug(\n { voice, outputFormat: config.outputFormat, textLength: text.length },\n 'Calling Edge TTS',\n );\n const tts = new EdgeTTS({\n voice,\n lang: config.lang,\n outputFormat: config.outputFormat,\n proxy: config.proxy,\n rate: config.rate,\n pitch: config.pitch,\n volume: config.volume,\n timeout: timeoutMs,\n });\n await tts.ttsPromise(text, outputPath);\n await waitForNonEmptyFile(outputPath, timeoutMs);\n const buffer = readFileSync(outputPath);\n if (buffer.length === 0) {\n throw new Error(`Edge TTS produced an empty ${format} file for voice ${voice}`);\n }\n return { buffer, outputFormat: format, ext };\n } finally {\n try {\n rmSync(tempDir, { recursive: true, force: true });\n } catch (cleanupError) {\n log.warn({ err: cleanupError, tempDir }, 'Failed to cleanup Edge TTS temp directory');\n }\n }\n}\n\nexport const edgeSpeechProvider: SpeechProviderPlugin = {\n id: 'edge',\n autoSelectOrder: 100,\n\n resolveConfig: (ctx: SpeechProviderResolveConfigContext) => normalizeConfig(ctx.rawConfig),\n\n isConfigured: (ctx: SpeechProviderConfiguredContext) => readProviderConfig(ctx.providerConfig).enabled,\n\n parseDirectiveToken: parseDirectiveTokenInternal,\n\n // Edge has hundreds of voices; we don't enumerate them statically. UI should\n // call upstream Microsoft endpoint for the full catalog.\n listVoices: async () => [{ id: DEFAULT_VOICE, name: DEFAULT_VOICE, locale: DEFAULT_LANG }],\n\n synthesize: async (req: SpeechSynthesisRequest): Promise<SpeechSynthesisResult> => {\n const config = readProviderConfig(req.providerConfig);\n if (!config.enabled) {\n throw new Error('Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)');\n }\n const overrides = req.providerOverrides ?? {};\n const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);\n const { buffer, outputFormat, ext } = await synthesizeToBuffer(\n req.text,\n config,\n voiceOverride,\n req.timeoutMs,\n );\n return {\n audioBuffer: buffer,\n outputFormat,\n fileExtension: ext,\n voiceCompatible: outputFormat === 'opus' || outputFormat === 'ogg',\n };\n },\n\n // True streaming would require a node-edge-tts upstream change. We expose a\n // \"fake stream\" (single chunk) so callers don't fall through to speak-core's\n // wrapBufferAsStream branch and can treat all providers uniformly.\n synthesizeStream: async (req: SpeechSynthesisStreamRequest): Promise<SpeechSynthesisStreamResult> => {\n const config = readProviderConfig(req.providerConfig);\n if (!config.enabled) {\n throw new Error('Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)');\n }\n const overrides = req.providerOverrides ?? {};\n const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);\n const { buffer, outputFormat, ext } = await synthesizeToBuffer(\n req.text,\n config,\n voiceOverride,\n req.timeoutMs,\n );\n const audioStream = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(new Uint8Array(buffer));\n controller.close();\n },\n });\n return {\n audioStream,\n outputFormat,\n fileExtension: ext,\n voiceCompatible: outputFormat === 'opus' || outputFormat === 'ogg',\n };\n },\n};\n\nregisterSpeechProvider(edgeSpeechProvider);\n"],"mappings":"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"}
|
|
@@ -100,7 +100,7 @@ function buildSynthesisRequest(resolved, preparedText, ttsOptions, directiveOver
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
/** Convert SpeechSynthesisResult → TTSResult shape (audio/format/provider). */
|
|
103
|
-
function
|
|
103
|
+
function toTtsResult(resolved, result, durationSeconds) {
|
|
104
104
|
return {
|
|
105
105
|
audio: result.audioBuffer,
|
|
106
106
|
format: result.outputFormat,
|
|
@@ -167,7 +167,7 @@ async function speak(text, config, options) {
|
|
|
167
167
|
size: result.audioBuffer.length
|
|
168
168
|
}, "TTS succeeded");
|
|
169
169
|
return {
|
|
170
|
-
...
|
|
170
|
+
...toTtsResult(resolved, result, durationSeconds),
|
|
171
171
|
attempts,
|
|
172
172
|
...fallbackFrom ? { fallbackFrom } : {},
|
|
173
173
|
attemptedProviders,
|
|
@@ -211,7 +211,7 @@ async function speakWithProvider(text, config, providerName, options) {
|
|
|
211
211
|
const startTime = Date.now();
|
|
212
212
|
const request = buildSynthesisRequest(resolved, prepared.text, prepared.ttsOptions, prepared.directiveOverrides, options?.appConfig, "audio-file");
|
|
213
213
|
return {
|
|
214
|
-
...
|
|
214
|
+
...toTtsResult(resolved, await resolved.plugin.synthesize(request), (Date.now() - startTime) / 1e3),
|
|
215
215
|
attempts: [{
|
|
216
216
|
provider: providerName,
|
|
217
217
|
outcome: "success",
|