@xopcai/xopc 0.0.373 → 0.0.375
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear--kDkSTqC.js → agent-avatar-dicebear-B2Popl59.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-z4_2-Kn2.js → agent-defaults-settings-panel-BnF3HrHY.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-plugin-dialog-BYndK83K.js → agent-plugin-dialog-CwP6OrUs.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-CVo96cx1.js → agents-n3IremKl.js} +3 -3
- package/dist/gateway/static/root/assets/{app-management-settings-panel-D9oOJKiH.js → app-management-settings-panel-DNkXIPxK.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-B0-8Cdyv.js → appearance-settings-p1_NpH8a.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-DasVc6i4.js → apps-page-CP2u5iEh.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BlCNqy2i.js → automations-page-CjYiGOov.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-C8ipDP05.js → automations-workspace-BAHiUQbe.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Cktlez6g.js → block-editor-DiOou1HO.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-KRfDl8ej.js → browser-automations-page-nArl2uwX.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-BraQ4wnh.js → browser-settings-page-Bd8tF-gg.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-page-B3m8xGzd.js → capabilities-page-Cx4cdgse.js} +2 -2
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-VQ4fHuBV.js → capabilities-settings-panel-DpWceNd0.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-header-actions-f5Ido4Eg.js → capability-header-actions-AeBubNQK.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-N4Vc2nEG.js → channels-Cd1Rf0Vk.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BwvTKwsn.js → chat-terminal-dock-DrVlbq_P.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-CAwUsKpw.js → computer-settings-page-TUzZnurj.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-C0HuAMuH.js → connector-service-page-BlpwjGW7.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-BcBmW8JT.js → connectors-page-j8VFMD4o.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-DiQxRpe-.js → date-picker-CjKRpA8X.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-Ds0_BJbN.js → dependency-picker-J9_0obij.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-ZMEOYwrR.js → desktop-pet-DySwgreF.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DkyTqD07.js → desktop-pet-settings-B1-oyKBG.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-CvaI5ZJZ.js → device-pairing-wizard-C5rjPAyc.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Bfv93eK1.js → directory-picker-path-field-BY7MwiZd.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-JXKGtIdM.js → extension-page-JoSFhfb6.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-lsQ2xvbr.js → extension-settings-page-B7LtTNx7.js} +1 -1
- package/dist/gateway/static/root/assets/{fallback-models-7bG0AND1.js → fallback-models-DKSWcBt_.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BpbcoPx9.js → gateway-settings-CGm-lbsX.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-BxctC0aJ.js → home-page-CX3uWkyH.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-Bq8CazCb.js → imports-page-DS_RFtWf.js} +1 -1
- package/dist/gateway/static/root/assets/{index-B40-KDN8.js → index-CgvOIDy_.js} +6 -6
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-kkAevkl2.js → keyboard-shortcuts-settings-BxwXR2qA.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-abLaf1hW.js → local-app-workbench-page-BWd4RMBE.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-nwsnwYjK.js → local-apps-page-BsgcA6-N.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-BEgYS-Xg.js → logs-page-CLP5pu2j.js} +1 -1
- package/dist/gateway/static/root/assets/management-settings-CZAZwCpE.js +1 -0
- package/dist/gateway/static/root/assets/{note-detail-panel-CCnfurBR.js → note-detail-panel-BeAHgdtw.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-D9u_znxM.js → notes-page-MP21Zf_R.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-BO3FoRej.js → page-context-capture-button-BZADmKcV.js} +1 -1
- package/dist/gateway/static/root/assets/personal-page-BbaTGmE-.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-Dso2_hyl.js → product-open-page-CBnKsW4p.js} +1 -1
- package/dist/gateway/static/root/assets/{product-preview-content-CkvVb3EA.js → product-preview-content-B0gGexdG.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-UjrrzE3H.js → project-detail-page-ZjQSv_NG.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-C1h63P2M.js → projects-page-Bf7Sd_Ck.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-BJifH8AB.js → remote-access-hub-Bl8aPuXU.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-vG1wqcEb.js → runtime-tools-settings-panel-CQZ1c-re.js} +1 -1
- package/dist/gateway/static/root/assets/{scenes-page-CwoAoYMX.js → scenes-page-1xGRBOJO.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BIsHdyz7.js → schema-form-BYqugMox.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-BcCLp3yU.js → sessions-page-DjMSVXU3.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BJuMTtyI.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-z9Jlk0BL.js → settings-page-CSqi5fmN.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-CrpXECqe.js → setup-status-panel-D5bM0TvA.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-D9XpTJXP.js → shares-settings-C-V9WllF.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-DtPOf1aW.js → skill-api-CFtEHy5e.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-Bq9-2b9U.js → skills-page-B-olXUVH.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-P7T729uD.js → system-settings-panel-DBMR9ks_.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-CsB9AFx6.js → task-detail-page-CGe34vxR.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-CuowqIZ7.js → tunnel-api-CzNIlgdJ.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-avX3msoZ.js → usage-page-O3abwewp.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-Bx2JlCpj.js +2 -0
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BgJNz2tH.js → work-discovery-overlay-D967-fVj.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-1I2q2J_U.js → workflow-run-setup-panel-BJ945m7G.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-DhJl9pnO.js → workflows-page-DUpVeIuw.js} +1 -1
- package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CnPBP1V_.js → xopc-cloud-account-card-Bq9ubffP.js} +1 -1
- package/dist/gateway/static/root/index.html +1 -1
- package/dist/package.js +1 -1
- package/dist/packages/realtime-protocol/src/voice.js +11 -0
- package/dist/src/agent/agent-manager.d.ts +2 -7
- package/dist/src/agent/agent-manager.js +21 -50
- package/dist/src/agent/background-review/coordinator.js +1 -1
- package/dist/src/agent/background-review/prompts.d.ts +6 -0
- package/dist/src/agent/background-review/prompts.js +7 -1
- package/dist/src/agent/background-review/run-background-review.d.ts +3 -0
- package/dist/src/agent/background-review/run-background-review.js +185 -17
- package/dist/src/agent/context/coordinator.d.ts +1 -0
- package/dist/src/agent/context/coordinator.js +11 -2
- package/dist/src/agent/context/execution-context.js +40 -4
- package/dist/src/agent/context/project-context.js +11 -3
- package/dist/src/agent/embedded/run-for-session.js +2 -2
- package/dist/src/agent/embedded/run-turn.js +4 -4
- package/dist/src/agent/external-tools/endpoint-provider.js +2 -2
- package/dist/src/agent/image/tool-model-config.js +1 -1
- package/dist/src/agent/inbound/attachment-pipeline.js +1 -1
- package/dist/src/agent/memory/compaction-promotion.d.ts +2 -0
- package/dist/src/agent/memory/compaction-promotion.js +33 -2
- package/dist/src/agent/service/process-direct-streaming.js +1 -1
- package/dist/src/agent/service.js +5 -5
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/tasks/task-judge-service.js +2 -2
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/knowledge-memory-tool.js +14 -6
- package/dist/src/agent/tools/xopc-use-tool.js +2 -2
- package/dist/src/agent/workspace-runtime/registry.js +1 -1
- package/dist/src/agent-catalog/capabilities.js +2 -2
- package/dist/src/agent-config/schema.d.ts +9 -0
- package/dist/src/agent-config/schema.js +1 -0
- package/dist/src/ai-assist/text-assist.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/automations/capabilities/drafts.js +1 -1
- package/dist/src/automations/draft/automation-draft-service.js +1 -1
- package/dist/src/automations/service/action-executor.js +1 -1
- package/dist/src/bootstrap/application-state.js +2 -2
- package/dist/src/capabilities/runtime/dispatcher.js +1 -1
- package/dist/src/capabilities/runtime/product.js +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/agents.js +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 -1
- package/dist/src/cli/commands/gateway/shared.js +2 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard/model.js +1 -1
- package/dist/src/cli/extension-cli-register.js +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +1 -1
- package/dist/src/computer/runtime.js +1 -1
- package/dist/src/config/agent-model-intents.js +1 -1
- package/dist/src/config/agent-profile.js +2 -2
- package/dist/src/config/gateway-bind.js +2 -1
- package/dist/src/config/index.js +4 -4
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.d.ts +8 -8
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/schema.d.ts +18 -12
- package/dist/src/config/schema.js +12 -8
- package/dist/src/connectors/connected-source-ingestion.js +45 -7
- package/dist/src/connectors/connected-source-understanding.d.ts +1 -0
- package/dist/src/connectors/connected-source-understanding.js +59 -67
- package/dist/src/connectors/content-enrichment.js +1 -1
- package/dist/src/connectors/learning-coordinator.js +2 -2
- package/dist/src/daemon/constants.js +14 -9
- package/dist/src/daemon/index.js +4 -1
- package/dist/src/daemon/install-plan.js +2 -1
- package/dist/src/daemon/launchd.js +2 -1
- package/dist/src/daemon/schtasks.js +2 -1
- package/dist/src/daemon/service-audit.js +2 -1
- package/dist/src/daemon/service.js +2 -1
- package/dist/src/daemon/systemd.js +2 -1
- package/dist/src/extensions/agent-plugins/validation.js +1 -1
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/client-ip.js +2 -1
- package/dist/src/gateway/file-path-classifier.js +1 -1
- package/dist/src/gateway/hono/app.js +2 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/middleware/auth.d.ts +3 -5
- package/dist/src/gateway/hono/middleware/auth.js +56 -14
- package/dist/src/gateway/hono/middleware/logger.js +2 -1
- package/dist/src/gateway/hono/middleware/public-device-rate-limit.d.ts +16 -0
- package/dist/src/gateway/hono/middleware/public-device-rate-limit.js +43 -0
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js +3 -2
- package/dist/src/gateway/hono/oauth-async.js +2 -1
- package/dist/src/gateway/hono/routes/browser.js +2 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +5 -5
- package/dist/src/gateway/hono/routes/config.js +2 -1
- package/dist/src/gateway/hono/routes/connectors.js +1 -1
- package/dist/src/gateway/hono/routes/context-sources.js +2 -2
- package/dist/src/gateway/hono/routes/device-pairing-approval.d.ts +3 -8
- package/dist/src/gateway/hono/routes/device-pairing-approval.js +5 -20
- package/dist/src/gateway/hono/routes/devices.js +25 -9
- package/dist/src/gateway/hono/routes/endpoint-tools.js +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
- package/dist/src/gateway/hono/routes/mcp.js +1 -1
- package/dist/src/gateway/hono/routes/memory-learning.d.ts +3 -0
- package/dist/src/gateway/hono/routes/memory-learning.js +76 -0
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/notes.js +2 -2
- package/dist/src/gateway/hono/routes/personal-agent.js +3 -2
- package/dist/src/gateway/hono/routes/projects.js +3 -3
- package/dist/src/gateway/hono/routes/public-gateway.js +6 -6
- package/dist/src/gateway/hono/routes/session-input-handler.js +1 -1
- package/dist/src/gateway/hono/routes/shares.js +2 -1
- package/dist/src/gateway/hono/routes/tasks.js +1 -1
- package/dist/src/gateway/hono/routes/user-model-refresh.js +1 -1
- package/dist/src/gateway/hono/routes/user-model.js +5 -3
- package/dist/src/gateway/hono/routes/voice.js +1 -0
- package/dist/src/gateway/host.js +9 -4
- package/dist/src/gateway/image-generation-setup.js +2 -2
- package/dist/src/gateway/index.js +8 -4
- package/dist/src/gateway/listen.js +2 -1
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/rate-limit/auth-policy.js +7 -11
- package/dist/src/gateway/rate-limit/buckets.d.ts +6 -0
- package/dist/src/gateway/rate-limit/buckets.js +211 -189
- package/dist/src/gateway/rate-limit/env-flags.js +4 -1
- package/dist/src/gateway/rate-limit/index.js +13 -4
- package/dist/src/gateway/runtime-config.js +5 -3
- package/dist/src/gateway/security/audit.js +3 -2
- package/dist/src/gateway/security/browser-session.js +2 -1
- package/dist/src/gateway/security/device-request-proof.d.ts +28 -0
- package/dist/src/gateway/security/device-request-proof.js +122 -0
- package/dist/src/gateway/security/loopback.js +7 -6
- package/dist/src/gateway/service/session-command-service.js +1 -1
- package/dist/src/gateway/service.js +18 -16
- package/dist/src/gateway/tailscale-auth.js +2 -1
- package/dist/src/gateway/ws-security.js +2 -1
- package/dist/src/gateway/xopc-cloud-capability-setup.js +1 -1
- package/dist/src/home-intelligence/generator.js +2 -2
- package/dist/src/home-intelligence/host.js +2 -2
- package/dist/src/imports/inventory.js +1 -1
- package/dist/src/infra/rate-limit/failure-limiter.js +78 -90
- package/dist/src/infra/rate-limit/index.js +11 -3
- package/dist/src/infra/rate-limit/keyed-store.js +52 -47
- package/dist/src/infra/rate-limit/rate-limiter.js +55 -58
- package/dist/src/infra/update-service-refresh.js +2 -1
- package/dist/src/knowledge/chunks.d.ts +10 -0
- package/dist/src/knowledge/chunks.js +28 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js +67 -33
- package/dist/src/knowledge/index.js +1 -1
- package/dist/src/knowledge/ingestion-service.js +5 -1
- package/dist/src/knowledge/local-folder-adapter.d.ts +2 -1
- package/dist/src/knowledge/local-folder-adapter.js +22 -13
- package/dist/src/knowledge-memory/domain.d.ts +4 -0
- package/dist/src/knowledge-memory/index.d.ts +1 -0
- package/dist/src/knowledge-memory/index.js +2 -1
- package/dist/src/knowledge-memory/repair-evidence.d.ts +2 -0
- package/dist/src/knowledge-memory/repair-evidence.js +57 -0
- package/dist/src/knowledge-memory/repository.d.ts +8 -0
- package/dist/src/knowledge-memory/repository.js +48 -27
- package/dist/src/knowledge-memory/search-text.d.ts +3 -0
- package/dist/src/knowledge-memory/search-text.js +32 -0
- package/dist/src/knowledge-memory/usage-policy.d.ts +6 -0
- package/dist/src/knowledge-memory/usage-policy.js +61 -0
- package/dist/src/local-apps/service.js +1 -1
- package/dist/src/memory-learning/repository.d.ts +24 -0
- package/dist/src/memory-learning/repository.js +96 -0
- package/dist/src/memory-learning/service.d.ts +17 -0
- package/dist/src/memory-learning/service.js +105 -0
- package/dist/src/memory-learning/source-learning.d.ts +7 -0
- package/dist/src/memory-learning/source-learning.js +235 -0
- package/dist/src/memory-learning/task-receipts.d.ts +3 -0
- package/dist/src/memory-learning/task-receipts.js +95 -0
- package/dist/src/memory-learning/work-capture.d.ts +51 -0
- package/dist/src/memory-learning/work-capture.js +140 -0
- package/dist/src/memory-maintenance/service.js +5 -5
- package/dist/src/personal-agent/proactivity/service.js +2 -2
- package/dist/src/personal-agent/prompt-context.js +4 -4
- package/dist/src/personal-agent/reply-composer.js +1 -1
- package/dist/src/personal-agent/repository.d.ts +3 -0
- package/dist/src/personal-agent/repository.js +1 -0
- package/dist/src/personal-agent/service.d.ts +6 -2
- package/dist/src/personal-agent/service.js +6 -5
- package/dist/src/platform/client.d.ts +1 -1
- package/dist/src/platform/contracts.d.ts +1 -1
- package/dist/src/providers/local-model.d.ts +2 -0
- package/dist/src/providers/local-model.js +16 -0
- package/dist/src/providers/model-catalog-persistence.js +1 -1
- package/dist/src/realtime/runtime.js +6 -0
- package/dist/src/realtime/tickets.d.ts +1 -0
- package/dist/src/realtime/tickets.js +4 -0
- package/dist/src/remote-access/exposure-guards.js +2 -1
- package/dist/src/routing/resolve-route.js +1 -1
- package/dist/src/routing/resolve-tui-session-key.js +1 -1
- package/dist/src/scenes/taskFollowUp/service.js +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.js +2 -1
- package/dist/src/share/share-rate-limit.js +2 -1
- package/dist/src/share/share-url.js +2 -1
- package/dist/src/share/site-share-router.js +2 -1
- package/dist/src/storage/sqlite/device-access-repository.d.ts +7 -0
- package/dist/src/storage/sqlite/device-access-repository.js +22 -4
- package/dist/src/storage/sqlite/knowledge-repository.js +6 -0
- package/dist/src/storage/sqlite/migrations/243_voice_opening_greetings.sql +12 -0
- package/dist/src/storage/sqlite/migrations/244_device_request_proofs.sql +8 -0
- package/dist/src/storage/sqlite/migrations/245_memory_learning.sql +125 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +6 -3
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tasks/capabilities/read.js +2 -2
- package/dist/src/tasks/task-handoff-service.js +1 -1
- package/dist/src/tasks/task-main-update-decision-service.js +1 -1
- package/dist/src/tui/backends/embedded-backend.js +4 -4
- package/dist/src/tui/extension-host/load-extensions.js +1 -1
- package/dist/src/tui/tui-commands.js +1 -1
- package/dist/src/tui/tui-skills-autocomplete.js +1 -1
- package/dist/src/tui/tui-startup-resources.js +2 -2
- package/dist/src/tui/tui.js +2 -2
- package/dist/src/tunnel/broker-client.js +2 -0
- package/dist/src/tunnel/tunnel-config.js +2 -2
- package/dist/src/tunnel/tunnel-rate-limit.js +2 -1
- package/dist/src/user-context/access-policy.js +2 -1
- package/dist/src/user-context/config.d.ts +10 -0
- package/dist/src/user-context/config.js +5 -0
- package/dist/src/user-context/extraction/registry.d.ts +1 -1
- package/dist/src/user-context/extraction/registry.js +10 -0
- package/dist/src/user-context/identity-bindings.d.ts +10 -0
- package/dist/src/user-context/identity-bindings.js +24 -0
- package/dist/src/user-context/sources/consent-fields.d.ts +2 -0
- package/dist/src/user-context/sources/consent-fields.js +66 -0
- package/dist/src/user-model/bootstrap.js +1 -1
- package/dist/src/user-model/capture/executor.js +1 -1
- package/dist/src/user-model/capture/semantic.d.ts +3 -1
- package/dist/src/user-model/capture/semantic.js +14 -6
- package/dist/src/user-model/index.js +1 -1
- package/dist/src/user-model/usage-policy.js +34 -0
- package/dist/src/utils/logger/redact.js +4 -1
- package/dist/src/voice/platform-streams.d.ts +6 -0
- package/dist/src/voice/platform-streams.js +44 -4
- package/dist/src/voice/realtime/agentEngine.js +138 -83
- package/dist/src/voice/realtime/engine.d.ts +6 -0
- package/dist/src/voice/realtime/memory-context.js +19 -5
- package/dist/src/voice/realtime/opening-greeting.d.ts +12 -0
- package/dist/src/voice/realtime/opening-greeting.js +22 -0
- package/dist/src/voice/realtime/persona-context.js +1 -1
- package/dist/src/voice/realtime/runtime.js +90 -1
- package/dist/src/voice/realtime/runtime.types.d.ts +4 -0
- package/dist/src/work-discovery/incremental-repository.js +1 -1
- package/dist/src/work-discovery/service.js +6 -6
- package/dist/src/workflows/service/workflow-run-service.js +2 -2
- package/dist/src/workflows/service/workflow-session-bridge.js +38 -10
- package/package.json +3 -3
- package/dist/gateway/static/root/assets/management-settings-D_JQ6_X0.js +0 -1
- package/dist/gateway/static/root/assets/personal-page-BJn6UHAF.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-D114UdRI.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-DGDBWw7P.js +0 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { PlatformVoiceError } from "../platform-streams.js";
|
|
3
4
|
import { prepareSpeechStreamSession, speakStream } from "../tts/speak-core.js";
|
|
4
5
|
import { AudioPlaybackWindow } from "./audio-playback-window.js";
|
|
5
6
|
import { TurnCoordinator } from "./turnPolicy.js";
|
|
@@ -43,6 +44,7 @@ function createAgentVoiceEngine(options) {
|
|
|
43
44
|
let turnReceivedAtMs;
|
|
44
45
|
let taskUpdateQueued = false;
|
|
45
46
|
let lastUserSpeechAt = 0;
|
|
47
|
+
let openingInputSeen = false;
|
|
46
48
|
const turn = new TurnCoordinator(claim.silenceDurationMs, (text, decision, turnId) => {
|
|
47
49
|
if (closed || muted) return;
|
|
48
50
|
const receivedAtMs = turnReceivedAtMs ?? Date.now();
|
|
@@ -103,13 +105,15 @@ function createAgentVoiceEngine(options) {
|
|
|
103
105
|
};
|
|
104
106
|
activeResponse = void 0;
|
|
105
107
|
clearTimeout(response.firstPhraseTimer);
|
|
108
|
+
clearTimeout(response.openingTimer);
|
|
109
|
+
if (response.opening && response.opening.result !== "expired") response.opening.result = "interrupted";
|
|
106
110
|
response.abortController.abort(reason);
|
|
107
111
|
send("response.cancelled", {
|
|
108
112
|
responseId: response.id,
|
|
109
113
|
reason
|
|
110
114
|
});
|
|
111
115
|
const conversationId = claim.request.conversationId;
|
|
112
|
-
if (conversationId && reason !== "session_closed") interruptionWrites = interruptionWrites.then(() => options.runtime.recordInterruption({
|
|
116
|
+
if (conversationId && !response.opening && reason !== "session_closed") interruptionWrites = interruptionWrites.then(() => options.runtime.recordInterruption({
|
|
113
117
|
conversationId,
|
|
114
118
|
responseId: response.id,
|
|
115
119
|
reason,
|
|
@@ -221,6 +225,12 @@ function createAgentVoiceEngine(options) {
|
|
|
221
225
|
for (const frame of response.pcm.push(item.value)) {
|
|
222
226
|
await response.playback.reserve(frame.byteLength, response.abortController.signal);
|
|
223
227
|
if (activeResponse !== response || closed) return;
|
|
228
|
+
if (response.opening && options.runtime.sessionBusy(claim.request.conversationId)) {
|
|
229
|
+
cancelActiveResponse("barge_in");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
response.firstFrameSent = true;
|
|
233
|
+
clearTimeout(response.openingTimer);
|
|
224
234
|
options.sendAudio(response.id, frame);
|
|
225
235
|
}
|
|
226
236
|
}
|
|
@@ -304,17 +314,19 @@ function createAgentVoiceEngine(options) {
|
|
|
304
314
|
if (response.speechWorker) prefetchNextSpeech(response);
|
|
305
315
|
else startSpeechWorker(response);
|
|
306
316
|
}
|
|
307
|
-
async function runAssistantTurn(text, turnId, updateClientMessageId, receivedAtMs = Date.now(), committedAtMs = receivedAtMs) {
|
|
317
|
+
async function runAssistantTurn(text, turnId, updateClientMessageId, receivedAtMs = Date.now(), committedAtMs = receivedAtMs, opening) {
|
|
308
318
|
if (!claim.tts || !claim.request.conversationId || !text.trim() || closed) return false;
|
|
309
319
|
cancelActiveResponse("barge_in");
|
|
310
320
|
const response = {
|
|
311
|
-
id: `resp_${crypto.randomUUID()}`,
|
|
321
|
+
id: opening?.responseId ?? `resp_${crypto.randomUUID()}`,
|
|
322
|
+
...opening ? { opening } : {},
|
|
312
323
|
playback: new AudioPlaybackWindow(),
|
|
313
324
|
abortController: new AbortController(),
|
|
314
325
|
segmenter: new SpeakableSegmenter(AGENT_TTS_MAX_SEGMENT_CHARACTERS, AGENT_TTS_MIN_SEGMENT_CHARACTERS, 0),
|
|
315
326
|
text: "",
|
|
316
327
|
audibleText: "",
|
|
317
328
|
audioStarted: false,
|
|
329
|
+
firstFrameSent: false,
|
|
318
330
|
speechQueue: [],
|
|
319
331
|
queuedSpeechCharacters: 0,
|
|
320
332
|
startedAt: Date.now(),
|
|
@@ -328,13 +340,19 @@ function createAgentVoiceEngine(options) {
|
|
|
328
340
|
const taskUpdateExposed = () => Boolean(updateClientMessageId && response.text.trim());
|
|
329
341
|
activeResponse = response;
|
|
330
342
|
send("response.created", { responseId: response.id });
|
|
343
|
+
if (opening) response.openingTimer = setTimeout(() => {
|
|
344
|
+
if (activeResponse === response) {
|
|
345
|
+
opening.result = "expired";
|
|
346
|
+
cancelActiveResponse("client_cancelled");
|
|
347
|
+
}
|
|
348
|
+
}, Math.max(0, opening.deadlineAt - Date.now()));
|
|
331
349
|
try {
|
|
332
|
-
const
|
|
350
|
+
const speechOpening = prepareSpeechStreamSession(claim.tts.config, {
|
|
333
351
|
appConfig: claim.config,
|
|
334
352
|
signal: response.abortController.signal,
|
|
335
353
|
allowFallback: false
|
|
336
354
|
});
|
|
337
|
-
if (
|
|
355
|
+
if (speechOpening) response.preparedSpeech = speechOpening.then((session) => {
|
|
338
356
|
log.info({
|
|
339
357
|
sessionId: claim.sessionId,
|
|
340
358
|
responseId: response.id,
|
|
@@ -346,86 +364,96 @@ function createAgentVoiceEngine(options) {
|
|
|
346
364
|
}, (error) => ({ error }));
|
|
347
365
|
await interruptionWrites;
|
|
348
366
|
if (response.abortController.signal.aborted || closed) return taskUpdateExposed();
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
367
|
+
if (opening) {
|
|
368
|
+
response.text = text;
|
|
369
|
+
response.firstTextAtMs = Date.now();
|
|
370
|
+
send("response.text.delta", {
|
|
371
|
+
responseId: response.id,
|
|
372
|
+
delta: text
|
|
373
|
+
});
|
|
374
|
+
queuePhrases(response, [text]);
|
|
375
|
+
} else {
|
|
376
|
+
if (!claim.conversationSessionId) throw new Error("Conversation identity is unavailable");
|
|
377
|
+
const task = await options.runtime.agentBroker.delegate({
|
|
378
|
+
text,
|
|
379
|
+
turnId,
|
|
380
|
+
conversationId: claim.request.conversationId,
|
|
381
|
+
expectedTranscriptId: claim.conversationSessionId,
|
|
382
|
+
signal: response.abortController.signal,
|
|
383
|
+
clientMessageId: updateClientMessageId ?? `voice:${claim.conversationSessionId}:${claim.sessionId}:${inputGeneration}:${turnId}`,
|
|
384
|
+
...updateClientMessageId ? { origin: {
|
|
385
|
+
type: "system",
|
|
386
|
+
source: "task_update"
|
|
387
|
+
} } : {}
|
|
388
|
+
});
|
|
389
|
+
response.taskId = task.taskId;
|
|
390
|
+
send("task.created", {
|
|
391
|
+
responseId: response.id,
|
|
392
|
+
taskId: task.taskId
|
|
393
|
+
});
|
|
394
|
+
for await (const event of task.events) {
|
|
395
|
+
if (activeResponse !== response || response.abortController.signal.aborted) return taskUpdateExposed();
|
|
396
|
+
if (event.type === "assistant_delta" || event.type === "tool_end" && event.payload?.toolName !== "clarify") response.awaitingClarification = false;
|
|
397
|
+
if (event.type === "assistant_delta" && typeof event.payload?.delta === "string") {
|
|
398
|
+
if (response.text.length + event.payload.delta.length > 32e3) throw new Error("Voice response text limit reached");
|
|
399
|
+
if (!response.firstTextSeen && event.payload.delta.trim()) {
|
|
400
|
+
response.firstTextSeen = true;
|
|
401
|
+
response.firstTextAtMs = Date.now();
|
|
402
|
+
log.info({
|
|
403
|
+
sessionId: claim.sessionId,
|
|
404
|
+
responseId: response.id,
|
|
405
|
+
conversationId: claim.request.conversationId,
|
|
406
|
+
phase: "first_response_text",
|
|
407
|
+
latencyMs: Date.now() - response.startedAt,
|
|
408
|
+
inputToTextMs: Date.now() - response.inputReceivedAtMs,
|
|
409
|
+
turnDecisionMs: Math.max(0, response.turnCommittedAtMs - response.inputReceivedAtMs),
|
|
410
|
+
queueMs: Math.max(0, response.startedAt - response.turnCommittedAtMs)
|
|
411
|
+
}, "Realtime voice first response text ready");
|
|
412
|
+
}
|
|
413
|
+
response.text += event.payload.delta;
|
|
414
|
+
send("response.text.delta", {
|
|
377
415
|
responseId: response.id,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
},
|
|
416
|
+
delta: event.payload.delta
|
|
417
|
+
});
|
|
418
|
+
queuePhrases(response, response.segmenter.push(event.payload.delta));
|
|
419
|
+
if (response.firstPhraseAtMs === void 0 && response.firstPhraseTimer === void 0) response.firstPhraseTimer = setTimeout(() => {
|
|
420
|
+
response.firstPhraseTimer = void 0;
|
|
421
|
+
if (activeResponse === response && !response.abortController.signal.aborted) queuePhrases(response, response.segmenter.flushFirstPhrase());
|
|
422
|
+
}, FIRST_PHRASE_WAIT_MS);
|
|
385
423
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
424
|
+
if (event.type === "tool_start") queuePhrases(response, response.segmenter.flush());
|
|
425
|
+
if ((event.type === "tool_start" || event.type === "tool_end") && typeof event.payload?.toolCallId === "string" && typeof event.payload?.toolName === "string") send("task.activity", {
|
|
426
|
+
taskId: task.taskId,
|
|
427
|
+
toolCallId: event.payload.toolCallId.slice(0, 160),
|
|
428
|
+
toolName: event.payload.toolName.slice(0, 256),
|
|
429
|
+
status: event.type === "tool_start" ? "running" : event.payload.status === "error" ? "failed" : "completed"
|
|
390
430
|
});
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
response.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
431
|
+
if (event.type === "run_end" || event.type === "stream_end") {
|
|
432
|
+
const status = event.payload?.status;
|
|
433
|
+
if (!response.taskDone) {
|
|
434
|
+
response.taskDone = true;
|
|
435
|
+
send("task.done", {
|
|
436
|
+
taskId: task.taskId,
|
|
437
|
+
status: status === "cancelled" ? "cancelled" : status === "suspended" ? "suspended" : status === "error" ? "failed" : "completed"
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (event.type === "clarify_request" && typeof event.payload?.requestId === "string" && typeof event.payload?.question === "string") {
|
|
442
|
+
response.awaitingClarification = true;
|
|
443
|
+
send("response.clarification", {
|
|
444
|
+
responseId: response.id,
|
|
445
|
+
requestId: event.payload.requestId.slice(0, 160),
|
|
446
|
+
kind: event.payload.kind === "approval" ? "approval" : "input",
|
|
447
|
+
question: event.payload.question.slice(0, 8e3),
|
|
448
|
+
...Array.isArray(event.payload.choices) ? { choices: event.payload.choices.filter((choice) => typeof choice === "string").slice(0, 20).map((choice) => choice.slice(0, 1e3)) } : {},
|
|
449
|
+
...typeof event.payload.suggestedAnswer === "string" ? { suggestedAnswer: event.payload.suggestedAnswer.slice(0, 1e3) } : {},
|
|
450
|
+
version: typeof event.payload.version === "number" ? event.payload.version : 1,
|
|
451
|
+
createdAt: typeof event.payload.createdAt === "number" ? event.payload.createdAt : Date.now(),
|
|
452
|
+
...typeof event.payload.expiresAt === "number" ? { expiresAt: event.payload.expiresAt } : {}
|
|
411
453
|
});
|
|
412
454
|
}
|
|
455
|
+
if (event.type === "error") throw new Error(typeof event.payload?.message === "string" ? event.payload.message : "Agent response failed");
|
|
413
456
|
}
|
|
414
|
-
if (event.type === "clarify_request" && typeof event.payload?.requestId === "string" && typeof event.payload?.question === "string") {
|
|
415
|
-
response.awaitingClarification = true;
|
|
416
|
-
send("response.clarification", {
|
|
417
|
-
responseId: response.id,
|
|
418
|
-
requestId: event.payload.requestId.slice(0, 160),
|
|
419
|
-
kind: event.payload.kind === "approval" ? "approval" : "input",
|
|
420
|
-
question: event.payload.question.slice(0, 8e3),
|
|
421
|
-
...Array.isArray(event.payload.choices) ? { choices: event.payload.choices.filter((choice) => typeof choice === "string").slice(0, 20).map((choice) => choice.slice(0, 1e3)) } : {},
|
|
422
|
-
...typeof event.payload.suggestedAnswer === "string" ? { suggestedAnswer: event.payload.suggestedAnswer.slice(0, 1e3) } : {},
|
|
423
|
-
version: typeof event.payload.version === "number" ? event.payload.version : 1,
|
|
424
|
-
createdAt: typeof event.payload.createdAt === "number" ? event.payload.createdAt : Date.now(),
|
|
425
|
-
...typeof event.payload.expiresAt === "number" ? { expiresAt: event.payload.expiresAt } : {}
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
if (event.type === "error") throw new Error(typeof event.payload?.message === "string" ? event.payload.message : "Agent response failed");
|
|
429
457
|
}
|
|
430
458
|
if (activeResponse !== response || response.abortController.signal.aborted || closed) return taskUpdateExposed();
|
|
431
459
|
queuePhrases(response, response.segmenter.flush());
|
|
@@ -436,6 +464,12 @@ function createAgentVoiceEngine(options) {
|
|
|
436
464
|
if (finalFrame) {
|
|
437
465
|
await response.playback.reserve(finalFrame.byteLength, response.abortController.signal);
|
|
438
466
|
if (activeResponse !== response || closed) return taskUpdateExposed();
|
|
467
|
+
if (response.opening && options.runtime.sessionBusy(claim.request.conversationId)) {
|
|
468
|
+
cancelActiveResponse("barge_in");
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
response.firstFrameSent = true;
|
|
472
|
+
clearTimeout(response.openingTimer);
|
|
439
473
|
options.sendAudio(response.id, finalFrame);
|
|
440
474
|
}
|
|
441
475
|
await response.playback.drain(response.abortController.signal);
|
|
@@ -451,6 +485,7 @@ function createAgentVoiceEngine(options) {
|
|
|
451
485
|
finishReason: response.audioStarted ? "completed" : "text_only",
|
|
452
486
|
audio: response.audioStarted
|
|
453
487
|
});
|
|
488
|
+
if (opening) opening.result = response.audioStarted ? "completed" : "failed";
|
|
454
489
|
activeResponse = void 0;
|
|
455
490
|
return !updateClientMessageId || taskUpdateExposed();
|
|
456
491
|
} catch (error) {
|
|
@@ -461,7 +496,7 @@ function createAgentVoiceEngine(options) {
|
|
|
461
496
|
responseId: response.id,
|
|
462
497
|
...updateClientMessageId ? { clientMessageId: updateClientMessageId } : {}
|
|
463
498
|
}, updateClientMessageId ? "Proactive voice task update deferred" : "Realtime voice response failed");
|
|
464
|
-
if (!updateClientMessageId) send("session.error", {
|
|
499
|
+
if (!updateClientMessageId && !opening) send("session.error", {
|
|
465
500
|
code: "RESPONSE_FAILED",
|
|
466
501
|
message: "Voice response failed",
|
|
467
502
|
recoverable: true
|
|
@@ -483,6 +518,7 @@ function createAgentVoiceEngine(options) {
|
|
|
483
518
|
if (activeResponse === response) activeResponse = void 0;
|
|
484
519
|
return taskUpdateExposed();
|
|
485
520
|
} finally {
|
|
521
|
+
clearTimeout(response.openingTimer);
|
|
486
522
|
clearTimeout(response.firstPhraseTimer);
|
|
487
523
|
await waitForSpeech(response);
|
|
488
524
|
response.abortController.abort("response_finished");
|
|
@@ -538,14 +574,15 @@ function createAgentVoiceEngine(options) {
|
|
|
538
574
|
if (event.type === "error") {
|
|
539
575
|
turn.reset();
|
|
540
576
|
echoCandidates.clear();
|
|
577
|
+
const message = event.error instanceof PlatformVoiceError ? event.error.clientMessage : "Streaming transcription failed";
|
|
541
578
|
log.warn({
|
|
542
579
|
err: event.error,
|
|
543
580
|
sessionId: claim.sessionId,
|
|
544
581
|
provider: stt.route.provider
|
|
545
|
-
},
|
|
582
|
+
}, `Realtime STT failed: ${message}`);
|
|
546
583
|
send("session.error", {
|
|
547
584
|
code: "PROVIDER_ERROR",
|
|
548
|
-
message
|
|
585
|
+
message,
|
|
549
586
|
recoverable: false
|
|
550
587
|
});
|
|
551
588
|
options.onClose("provider_error", true);
|
|
@@ -553,6 +590,8 @@ function createAgentVoiceEngine(options) {
|
|
|
553
590
|
}
|
|
554
591
|
if (muted || finalizedUtterances.has(event.utteranceId)) return;
|
|
555
592
|
if (event.type === "speech_started") {
|
|
593
|
+
openingInputSeen = true;
|
|
594
|
+
if (activeResponse?.opening && !activeResponse.firstFrameSent) cancelActiveResponse("barge_in");
|
|
556
595
|
lastUserSpeechAt = Date.now();
|
|
557
596
|
echoCandidates.remember(event.utteranceId, activeResponse?.audioStarted ? activeResponse.audibleText : recentPlayback && recentPlayback.expiresAt >= Date.now() ? recentPlayback.text : void 0);
|
|
558
597
|
if (claim.request.purpose === "conversation") {
|
|
@@ -578,6 +617,7 @@ function createAgentVoiceEngine(options) {
|
|
|
578
617
|
if (event.type === "transcript_final") {
|
|
579
618
|
lastUserSpeechAt = Date.now();
|
|
580
619
|
const text = event.text.trim();
|
|
620
|
+
if (text) openingInputSeen = true;
|
|
581
621
|
if (!text) {
|
|
582
622
|
if (claim.request.purpose === "conversation") bufferFinal(event.utteranceId, "");
|
|
583
623
|
return;
|
|
@@ -613,7 +653,7 @@ function createAgentVoiceEngine(options) {
|
|
|
613
653
|
turn.reset();
|
|
614
654
|
return;
|
|
615
655
|
}
|
|
616
|
-
if (claim.config.voice?.realtime?.bargeIn) cancelActiveResponse("barge_in");
|
|
656
|
+
if (activeResponse?.opening || claim.config.voice?.realtime?.bargeIn) cancelActiveResponse("barge_in");
|
|
617
657
|
bufferFinal(event.utteranceId, text);
|
|
618
658
|
}
|
|
619
659
|
}
|
|
@@ -654,6 +694,21 @@ function createAgentVoiceEngine(options) {
|
|
|
654
694
|
}
|
|
655
695
|
return {
|
|
656
696
|
isConversationIdle,
|
|
697
|
+
async offerOpeningGreeting(input) {
|
|
698
|
+
if (openingInputSeen || !canOfferTaskUpdateInput() || options.runtime.sessionBusy(claim.request.conversationId)) return "skipped";
|
|
699
|
+
const opening = {
|
|
700
|
+
...input,
|
|
701
|
+
result: "failed"
|
|
702
|
+
};
|
|
703
|
+
const attempt = conversationTail.then(async () => {
|
|
704
|
+
if (Date.now() >= input.deadlineAt) return "expired";
|
|
705
|
+
if (openingInputSeen || !canOfferTaskUpdateInput() || options.runtime.sessionBusy(claim.request.conversationId)) return "skipped";
|
|
706
|
+
await runAssistantTurn(input.text, input.responseId, void 0, input.deadlineAt - 2e3, Date.now(), opening);
|
|
707
|
+
return opening.result;
|
|
708
|
+
});
|
|
709
|
+
conversationTail = attempt.then(() => {}, () => {});
|
|
710
|
+
return attempt;
|
|
711
|
+
},
|
|
657
712
|
canOfferTaskUpdate() {
|
|
658
713
|
return !taskUpdateQueued && canOfferTaskUpdateInput();
|
|
659
714
|
},
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import type { OpeningGreetingResult } from './opening-greeting.js';
|
|
1
2
|
import type { VoiceServerEvent } from '@xopcai/realtime-protocol/voice';
|
|
2
3
|
export type VoiceEventSink = <T extends VoiceServerEvent['type']>(type: T, payload: Extract<VoiceServerEvent, {
|
|
3
4
|
type: T;
|
|
4
5
|
}>['payload']) => void;
|
|
5
6
|
export interface VoiceEngine {
|
|
6
7
|
start(): Promise<void>;
|
|
8
|
+
offerOpeningGreeting?(input: {
|
|
9
|
+
responseId: string;
|
|
10
|
+
text: string;
|
|
11
|
+
deadlineAt: number;
|
|
12
|
+
}): Promise<OpeningGreetingResult>;
|
|
7
13
|
appendAudio(bytes: Uint8Array): void;
|
|
8
14
|
setInputMuted(muted: boolean): void | Promise<void>;
|
|
9
15
|
commit(): Promise<void>;
|
|
@@ -3,16 +3,20 @@ import { init_logger } from "../../utils/logger.js";
|
|
|
3
3
|
import { getSessionMetadata } from "../../storage/sqlite/session-read-repository.js";
|
|
4
4
|
import { init_session_repository } from "../../storage/sqlite/session-repository.js";
|
|
5
5
|
import { getSessionConfig, init_config_repository } from "../../storage/sqlite/config-repository.js";
|
|
6
|
+
import { canUseKnowledge } from "../../knowledge-memory/usage-policy.js";
|
|
7
|
+
import { getKnowledgeItem } from "../../knowledge-memory/repository.js";
|
|
8
|
+
import { init_config, resolveKnowledgeReadPolicy } from "../../user-context/config.js";
|
|
6
9
|
import { getConversationRouting } from "../../storage/sqlite/conversation-repository.js";
|
|
7
10
|
import { getConversation } from "../../routing/agent-session-key.js";
|
|
8
11
|
import { init_session_key } from "../../routing/session-key.js";
|
|
9
|
-
import {
|
|
12
|
+
import { resolveUserContextSessionAccess } from "../../user-context/access-policy.js";
|
|
10
13
|
import "../../knowledge-memory/index.js";
|
|
14
|
+
import { stripRuntimeContextFromUserMessage } from "../../session/user-message-display.js";
|
|
11
15
|
import { getUserAssertion } from "../../user-model/repository.js";
|
|
12
16
|
import "../../user-model/index.js";
|
|
13
|
-
import { stripRuntimeContextFromUserMessage } from "../../session/user-message-display.js";
|
|
14
17
|
import { buildExecutionContext } from "../../agent/context/execution-context.js";
|
|
15
18
|
//#region src/voice/realtime/memory-context.ts
|
|
19
|
+
init_config();
|
|
16
20
|
init_session_key();
|
|
17
21
|
init_config_repository();
|
|
18
22
|
init_session_repository();
|
|
@@ -35,7 +39,7 @@ function buildVoiceMemoryContext(input) {
|
|
|
35
39
|
const started = performance.now();
|
|
36
40
|
try {
|
|
37
41
|
const config = input.getConfig();
|
|
38
|
-
if (!voiceMemoryEnabled(config, input.conversationId) || input.maxChars < 128) return;
|
|
42
|
+
if (!resolveUserContextSessionAccess(config, input.conversationId).userModel || !voiceMemoryEnabled(config, input.conversationId) || input.maxChars < 128) return;
|
|
39
43
|
const session = getSessionMetadata(input.conversationId);
|
|
40
44
|
const context = buildExecutionContext({
|
|
41
45
|
query: voiceMemoryQuery(input.history),
|
|
@@ -44,7 +48,9 @@ function buildVoiceMemoryContext(input) {
|
|
|
44
48
|
projectId: input.projectId,
|
|
45
49
|
conversationId: input.conversationId,
|
|
46
50
|
maxAssertions: 6,
|
|
47
|
-
maxKnowledge: 3
|
|
51
|
+
maxKnowledge: 3,
|
|
52
|
+
includeKnowledge: resolveUserContextSessionAccess(config, input.conversationId).knowledge,
|
|
53
|
+
knowledgePolicy: resolveKnowledgeReadPolicy(config.userContext.knowledgeMemory)
|
|
48
54
|
});
|
|
49
55
|
const block = JSON.stringify({ backgroundMemory: {
|
|
50
56
|
userFacts: context.assertions.map((item) => item.assertion.statement),
|
|
@@ -64,7 +70,15 @@ function buildVoiceMemoryContext(input) {
|
|
|
64
70
|
const configVersion = JSON.stringify(config.userContext);
|
|
65
71
|
const isCurrent = () => {
|
|
66
72
|
if (!voiceMemoryEnabled(input.getConfig(), input.conversationId) || JSON.stringify(input.getConfig().userContext) !== configVersion) return false;
|
|
67
|
-
return references.every((reference) => reference.kind === "assertion" ? getUserAssertion(reference.id)?.recordedAt === reference.updatedAt :
|
|
73
|
+
return references.every((reference) => reference.kind === "assertion" ? getUserAssertion(reference.id)?.recordedAt === reference.updatedAt : (() => {
|
|
74
|
+
const item = getKnowledgeItem(reference.id);
|
|
75
|
+
return Boolean(item && item.updatedAt === reference.updatedAt && canUseKnowledge(item, {
|
|
76
|
+
agentId: session?.routing?.agentId ?? "main",
|
|
77
|
+
workspaceId: input.workspaceId,
|
|
78
|
+
projectId: input.projectId,
|
|
79
|
+
conversationId: input.conversationId
|
|
80
|
+
}, resolveKnowledgeReadPolicy(input.getConfig().userContext.knowledgeMemory)));
|
|
81
|
+
})());
|
|
68
82
|
};
|
|
69
83
|
log.debug({
|
|
70
84
|
conversationId: input.conversationId,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type OpeningGreetingResult = 'completed' | 'skipped' | 'interrupted' | 'failed' | 'expired';
|
|
2
|
+
export interface OpeningGreetingIdentity {
|
|
3
|
+
principalId: string;
|
|
4
|
+
conversationId: string;
|
|
5
|
+
logicalCallId: string;
|
|
6
|
+
responseId: string;
|
|
7
|
+
}
|
|
8
|
+
/** Only an explicitly provided name is used; never infer identity from an account ID. */
|
|
9
|
+
export declare function openingGreetingText(callName: string | null | undefined, language: 'zh' | 'en'): string;
|
|
10
|
+
/** Claim before output: crashes and interrupted calls must never replay the greeting. */
|
|
11
|
+
export declare function claimOpeningGreeting(identity: OpeningGreetingIdentity, now?: number): boolean;
|
|
12
|
+
export declare function finishOpeningGreeting(identity: OpeningGreetingIdentity, state: OpeningGreetingResult, reason?: string): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getSqliteDatabase, init_transaction } from "../../storage/sqlite/transaction.js";
|
|
2
|
+
//#region src/voice/realtime/opening-greeting.ts
|
|
3
|
+
init_transaction();
|
|
4
|
+
/** Only an explicitly provided name is used; never infer identity from an account ID. */
|
|
5
|
+
function openingGreetingText(callName, language) {
|
|
6
|
+
const name = callName?.replace(/[\p{Cc}\p{Cf}]/gu, "").replace(/\s+/g, " ").trim().slice(0, 40);
|
|
7
|
+
return language === "en" ? name ? `Hi, ${name}. I'm here.` : "Hi, I'm here." : name ? `嗨,${name},我在。` : "嗨,我在。";
|
|
8
|
+
}
|
|
9
|
+
/** Claim before output: crashes and interrupted calls must never replay the greeting. */
|
|
10
|
+
function claimOpeningGreeting(identity, now = Date.now()) {
|
|
11
|
+
const db = getSqliteDatabase();
|
|
12
|
+
db.prepare("DELETE FROM voice_opening_greetings WHERE created_at < ?").run(now - 864e5);
|
|
13
|
+
return db.prepare(`INSERT OR IGNORE INTO voice_opening_greetings
|
|
14
|
+
(principal_id, conversation_id, logical_call_id, response_id, state, created_at, updated_at)
|
|
15
|
+
VALUES (?, ?, ?, ?, 'claimed', ?, ?)`).run(identity.principalId, identity.conversationId, identity.logicalCallId, identity.responseId, now, now).changes === 1;
|
|
16
|
+
}
|
|
17
|
+
function finishOpeningGreeting(identity, state, reason) {
|
|
18
|
+
getSqliteDatabase().prepare(`UPDATE voice_opening_greetings SET state = ?, reason = ?, updated_at = ?
|
|
19
|
+
WHERE principal_id = ? AND conversation_id = ? AND logical_call_id = ? AND state = 'claimed'`).run(state, reason ?? null, Date.now(), identity.principalId, identity.conversationId, identity.logicalCallId);
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { claimOpeningGreeting, finishOpeningGreeting, openingGreetingText };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { init_agent_scope, resolveAgentProfileDir } from "../../agent/agent-scope.js";
|
|
2
|
-
import { DEFAULT_SOUL_FILENAME } from "../../agent/context/workspace.js";
|
|
3
2
|
import { resolveEffectiveAgentProfileForSession } from "../../config/agent-profile.js";
|
|
3
|
+
import { DEFAULT_SOUL_FILENAME } from "../../agent/context/workspace.js";
|
|
4
4
|
import { loadProfileBootstrapFiles } from "../../agent/bootstrap/load-bootstrap-files.js";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { statSync } from "node:fs";
|
|
@@ -12,7 +12,9 @@ import { ALIBABA_REALTIME_TTS_MODEL } from "../tts/providers/alibaba-speech.js";
|
|
|
12
12
|
import { resolveSpeechProviderChain } from "../tts/factory.js";
|
|
13
13
|
import { mergeTtsConfigFromAppConfig } from "../tts/merge-config.js";
|
|
14
14
|
import { voiceConversationInstructions } from "./conversation-context.js";
|
|
15
|
+
import { verifyDeviceConnectionProof } from "../../gateway/security/device-request-proof.js";
|
|
15
16
|
import { createPreauthConnectionBudget } from "../../gateway/security/preauth-connection-budget.js";
|
|
17
|
+
import { claimOpeningGreeting, finishOpeningGreeting, openingGreetingText } from "./opening-greeting.js";
|
|
16
18
|
import { createAgentVoiceEngine } from "./agentEngine.js";
|
|
17
19
|
import { resolveOmniRoute } from "./omniRoute.js";
|
|
18
20
|
import { createOmniVoiceEngine } from "./omniEngine.js";
|
|
@@ -225,12 +227,18 @@ var VoiceRealtimeRuntime = class {
|
|
|
225
227
|
async createSession(request, principalId, now = Date.now()) {
|
|
226
228
|
const { request: resolvedRequest, config, omni, conversationSessionId, stt, tts, inputMode } = await this.prepareSession(request, now);
|
|
227
229
|
request = resolvedRequest;
|
|
230
|
+
const personal = request.conversationId ? getPersonalAgentByConversation(request.conversationId) : null;
|
|
231
|
+
const openingGreeting = request.openingGreeting && request.openingGreeting.initiation === "user" && request.purpose === "conversation" && request.mode === "assistant" && personal?.state === "ready" && personal.voiceCallPreferences.openingGreetingEnabled ? {
|
|
232
|
+
responseId: `greeting_${crypto.randomUUID()}`,
|
|
233
|
+
text: openingGreetingText(personal.userCallName, request.language ?? "zh")
|
|
234
|
+
} : void 0;
|
|
228
235
|
const sessionId = crypto.randomUUID();
|
|
229
236
|
const ticket = crypto.randomBytes(32).toString("base64url");
|
|
230
237
|
const connectionEpoch = crypto.randomInt(1, 4294967296);
|
|
231
238
|
const maxSessionMs = request.purpose === "conversation" ? Math.min(config.voice.realtime.maxConversationMs, omni?.maxSessionSeconds ? omni.maxSessionSeconds * 1e3 : Number.MAX_SAFE_INTEGER) : config.voice.realtime.maxDictationMs;
|
|
232
239
|
const claim = {
|
|
233
240
|
conversationSessionId,
|
|
241
|
+
openingGreeting,
|
|
234
242
|
sessionId,
|
|
235
243
|
connectionEpoch,
|
|
236
244
|
principalId,
|
|
@@ -250,6 +258,10 @@ var VoiceRealtimeRuntime = class {
|
|
|
250
258
|
this.tickets.set(ticketKey(ticket), claim);
|
|
251
259
|
if (request.purpose === "conversation" && request.conversationId) this.conversationReservations.set(request.conversationId, sessionId);
|
|
252
260
|
return {
|
|
261
|
+
...request.openingGreeting ? { openingGreeting: {
|
|
262
|
+
accepted: Boolean(openingGreeting),
|
|
263
|
+
...openingGreeting ? { greetingResponseId: openingGreeting.responseId } : {}
|
|
264
|
+
} } : {},
|
|
253
265
|
sessionId,
|
|
254
266
|
ticket,
|
|
255
267
|
ticketExpiresAt: new Date(claim.expiresAt).toISOString(),
|
|
@@ -394,6 +406,20 @@ var VoiceRealtimeRuntime = class {
|
|
|
394
406
|
let context;
|
|
395
407
|
let unsubscribeMemory;
|
|
396
408
|
let unsubscribeSession;
|
|
409
|
+
let openingTimer;
|
|
410
|
+
let outputReady = false;
|
|
411
|
+
let pendingGreeting;
|
|
412
|
+
const finishGreeting = (identity, result, reason) => {
|
|
413
|
+
try {
|
|
414
|
+
finishOpeningGreeting(identity, result, reason);
|
|
415
|
+
} catch (err) {
|
|
416
|
+
log.warn({
|
|
417
|
+
err,
|
|
418
|
+
...identity,
|
|
419
|
+
phase: "opening_greeting_audit"
|
|
420
|
+
}, "Opening greeting audit failed");
|
|
421
|
+
}
|
|
422
|
+
};
|
|
397
423
|
let inputMutationTail = Promise.resolve();
|
|
398
424
|
const send = (type, payload) => {
|
|
399
425
|
if (!claim || socket.readyState !== WebSocketState.OPEN) return;
|
|
@@ -423,6 +449,11 @@ var VoiceRealtimeRuntime = class {
|
|
|
423
449
|
if (shutdownCompletion) return shutdownCompletion;
|
|
424
450
|
if (closed) return Promise.resolve();
|
|
425
451
|
closed = true;
|
|
452
|
+
clearTimeout(openingTimer);
|
|
453
|
+
if (pendingGreeting) {
|
|
454
|
+
finishGreeting(pendingGreeting, "skipped", "session_closed");
|
|
455
|
+
pendingGreeting = void 0;
|
|
456
|
+
}
|
|
426
457
|
shutdownCompletion = (async () => {
|
|
427
458
|
unsubscribeMemory?.();
|
|
428
459
|
unsubscribeSession?.();
|
|
@@ -476,6 +507,11 @@ var VoiceRealtimeRuntime = class {
|
|
|
476
507
|
shutdown("context_changed", true);
|
|
477
508
|
};
|
|
478
509
|
const authenticate = async (message) => {
|
|
510
|
+
const pendingClaim = this.tickets.get(ticketKey(message.payload.ticket));
|
|
511
|
+
if (pendingClaim && !verifyDeviceConnectionProof(pendingClaim.principalId, message.payload.ticket, new URL(request.url, "http://localhost").pathname + new URL(request.url, "http://localhost").search, typeof request.headers["x-xopc-proof"] === "string" ? request.headers["x-xopc-proof"] : void 0)) {
|
|
512
|
+
socket.close(4401, "Invalid voice session proof");
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
479
515
|
const consumed = this.consumeTicket(message.payload.ticket, message.payload.sessionId);
|
|
480
516
|
if (!consumed || this.options.isPrincipalActive?.(consumed.principalId) === false) {
|
|
481
517
|
if (consumed) this.releaseConversationReservation(consumed);
|
|
@@ -697,7 +733,60 @@ var VoiceRealtimeRuntime = class {
|
|
|
697
733
|
return;
|
|
698
734
|
}
|
|
699
735
|
if (message.type === "session.ping") send("session.pong", {});
|
|
700
|
-
else if (message.type === "session.
|
|
736
|
+
else if (message.type === "session.output_ready") {
|
|
737
|
+
if (!ready || !claim.request.openingGreeting || message.payload.connectionEpoch !== claim.connectionEpoch) {
|
|
738
|
+
socket.close(4400, "Invalid output readiness");
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (outputReady) return;
|
|
742
|
+
outputReady = true;
|
|
743
|
+
const greeting = claim.openingGreeting;
|
|
744
|
+
if (!greeting || !engine?.offerOpeningGreeting) return;
|
|
745
|
+
const identity = {
|
|
746
|
+
principalId: claim.principalId,
|
|
747
|
+
conversationId: claim.request.conversationId,
|
|
748
|
+
logicalCallId: claim.request.openingGreeting.logicalCallId,
|
|
749
|
+
responseId: greeting.responseId
|
|
750
|
+
};
|
|
751
|
+
try {
|
|
752
|
+
if (!claimOpeningGreeting(identity)) return;
|
|
753
|
+
} catch (err) {
|
|
754
|
+
log.warn({
|
|
755
|
+
err,
|
|
756
|
+
sessionId: claim.sessionId
|
|
757
|
+
}, "Opening greeting claim failed");
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const deadlineAt = Date.now() + 2e3;
|
|
761
|
+
pendingGreeting = identity;
|
|
762
|
+
openingTimer = setTimeout(() => {
|
|
763
|
+
openingTimer = void 0;
|
|
764
|
+
pendingGreeting = void 0;
|
|
765
|
+
if (closed) {
|
|
766
|
+
finishGreeting(identity, "skipped", "session_closed");
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
engine.offerOpeningGreeting({
|
|
770
|
+
...greeting,
|
|
771
|
+
deadlineAt
|
|
772
|
+
}).then((result) => {
|
|
773
|
+
finishGreeting(identity, result);
|
|
774
|
+
log.info({
|
|
775
|
+
...identity,
|
|
776
|
+
sessionId: claim?.sessionId,
|
|
777
|
+
phase: "opening_greeting",
|
|
778
|
+
result
|
|
779
|
+
}, "Opening greeting finished");
|
|
780
|
+
}).catch((err) => {
|
|
781
|
+
finishGreeting(identity, "failed");
|
|
782
|
+
log.warn({
|
|
783
|
+
err,
|
|
784
|
+
...identity,
|
|
785
|
+
phase: "opening_greeting"
|
|
786
|
+
}, "Opening greeting failed");
|
|
787
|
+
});
|
|
788
|
+
}, 300);
|
|
789
|
+
} else if (message.type === "session.stop") shutdown(message.payload.reason, true);
|
|
701
790
|
else if (message.type === "session.metric") {
|
|
702
791
|
const { responseId, metric, durationMs } = message.payload;
|
|
703
792
|
const key = `${responseId}:${metric}`;
|
|
@@ -18,6 +18,10 @@ export interface ResolvedStreamingStt {
|
|
|
18
18
|
route: VoiceProviderRoute;
|
|
19
19
|
}
|
|
20
20
|
export interface VoiceTicketClaim {
|
|
21
|
+
openingGreeting?: {
|
|
22
|
+
responseId: string;
|
|
23
|
+
text: string;
|
|
24
|
+
};
|
|
21
25
|
sessionId: string;
|
|
22
26
|
connectionEpoch: number;
|
|
23
27
|
principalId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getWorkDiscoveryRun } from "./repository.js";
|
|
2
1
|
import { createUnderstandingSourceRun, listUnderstandingSourceGrants, listUnderstandingSourceRuns, updateUnderstandingSourceRun } from "../user-context/sources/repository.js";
|
|
2
|
+
import { getWorkDiscoveryRun } from "./repository.js";
|
|
3
3
|
import { workDiscoveryFingerprintsEqual } from "./incremental.js";
|
|
4
4
|
//#region src/work-discovery/incremental-repository.ts
|
|
5
5
|
function refreshFromRun(run) {
|