@vellumai/assistant 0.12.1 → 0.12.2-staging.1
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -32,6 +32,12 @@ import {
|
|
|
32
32
|
} from "../daemon/daemon-readiness.js";
|
|
33
33
|
import { processMessage } from "../daemon/process-message.js";
|
|
34
34
|
import { makeAddrInUseError } from "../daemon/startup-error.js";
|
|
35
|
+
import { isAssistantDesktopEnabled } from "../desktop/desktop-feature.js";
|
|
36
|
+
import {
|
|
37
|
+
DESKTOP_CLOSE,
|
|
38
|
+
destroyDesktopSessionManager,
|
|
39
|
+
} from "../desktop/desktop-session-manager.js";
|
|
40
|
+
import { DesktopStreamBridge } from "../desktop/desktop-stream-bridge.js";
|
|
35
41
|
import {
|
|
36
42
|
createLiveVoiceConnection,
|
|
37
43
|
type LiveVoiceConnection,
|
|
@@ -229,6 +235,32 @@ interface WatchStreamWebSocketData {
|
|
|
229
235
|
session?: WatchStreamSession;
|
|
230
236
|
}
|
|
231
237
|
|
|
238
|
+
/**
|
|
239
|
+
* WebSocket data for `/v1/desktop/stream`: raw RFB bytes to the assistant
|
|
240
|
+
* desktop.
|
|
241
|
+
*/
|
|
242
|
+
interface DesktopStreamWebSocketData {
|
|
243
|
+
wsType: "desktop-stream";
|
|
244
|
+
/** Bound at open time so message/close handlers reach this socket's pump. */
|
|
245
|
+
bridge?: DesktopStreamBridge;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
type AllWebSocketData =
|
|
249
|
+
| MediaStreamWebSocketData
|
|
250
|
+
| SttStreamWebSocketData
|
|
251
|
+
| LiveVoiceWebSocketData
|
|
252
|
+
| WatchStreamWebSocketData
|
|
253
|
+
| DesktopStreamWebSocketData;
|
|
254
|
+
|
|
255
|
+
function assistantDesktopEnabled(): boolean {
|
|
256
|
+
try {
|
|
257
|
+
return isAssistantDesktopEnabled(getConfig());
|
|
258
|
+
} catch (err) {
|
|
259
|
+
log.warn({ err }, "Failed to read config for desktop stream gate");
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
232
264
|
export class RuntimeHttpServer {
|
|
233
265
|
private server: ReturnType<typeof Bun.serve> | null = null;
|
|
234
266
|
private port: number;
|
|
@@ -264,11 +296,6 @@ export class RuntimeHttpServer {
|
|
|
264
296
|
}
|
|
265
297
|
|
|
266
298
|
async start(): Promise<void> {
|
|
267
|
-
type AllWebSocketData =
|
|
268
|
-
| MediaStreamWebSocketData
|
|
269
|
-
| SttStreamWebSocketData
|
|
270
|
-
| LiveVoiceWebSocketData
|
|
271
|
-
| WatchStreamWebSocketData;
|
|
272
299
|
this.server = Bun.serve<AllWebSocketData>({
|
|
273
300
|
port: this.port,
|
|
274
301
|
hostname: this.hostname,
|
|
@@ -424,6 +451,22 @@ export class RuntimeHttpServer {
|
|
|
424
451
|
void session.start();
|
|
425
452
|
return;
|
|
426
453
|
}
|
|
454
|
+
if (data.wsType === "desktop-stream") {
|
|
455
|
+
log.info("Desktop stream WebSocket opened");
|
|
456
|
+
if (!assistantDesktopEnabled()) {
|
|
457
|
+
ws.close(
|
|
458
|
+
DESKTOP_CLOSE.unavailable,
|
|
459
|
+
"Desktop is not available on this assistant",
|
|
460
|
+
);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const bridge = new DesktopStreamBridge(ws, {
|
|
464
|
+
isEnabled: assistantDesktopEnabled,
|
|
465
|
+
});
|
|
466
|
+
data.bridge = bridge;
|
|
467
|
+
void bridge.start();
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
427
470
|
log.warn("WebSocket opened with unknown data type — closing");
|
|
428
471
|
ws.close(1008, "Unknown WebSocket type");
|
|
429
472
|
},
|
|
@@ -474,6 +517,10 @@ export class RuntimeHttpServer {
|
|
|
474
517
|
}
|
|
475
518
|
return;
|
|
476
519
|
}
|
|
520
|
+
if (data.wsType === "desktop-stream") {
|
|
521
|
+
data.bridge?.handleClientFrame(message);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
477
524
|
log.warn("WebSocket message on unknown data type — closing");
|
|
478
525
|
ws.close(1008, "Unknown WebSocket type");
|
|
479
526
|
},
|
|
@@ -567,6 +614,14 @@ export class RuntimeHttpServer {
|
|
|
567
614
|
}
|
|
568
615
|
return;
|
|
569
616
|
}
|
|
617
|
+
if (data.wsType === "desktop-stream") {
|
|
618
|
+
log.info(
|
|
619
|
+
{ code, reason: reason?.toString() },
|
|
620
|
+
"Desktop stream WebSocket closed",
|
|
621
|
+
);
|
|
622
|
+
data.bridge?.handleClose();
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
570
625
|
log.warn(
|
|
571
626
|
{ code, reason: reason?.toString() },
|
|
572
627
|
"WebSocket with unknown data type closed",
|
|
@@ -707,6 +762,9 @@ export class RuntimeHttpServer {
|
|
|
707
762
|
// that a socket closing just before shutdown had already under way.
|
|
708
763
|
await drainWatchRetros();
|
|
709
764
|
|
|
765
|
+
// Bounded by the manager's kill grace, so shutdown never hangs on X.
|
|
766
|
+
await destroyDesktopSessionManager();
|
|
767
|
+
|
|
710
768
|
const liveVoiceManager = getLiveVoiceSessionManager();
|
|
711
769
|
const liveVoiceSessionId = liveVoiceManager.activeSessionId;
|
|
712
770
|
if (liveVoiceSessionId) {
|
|
@@ -810,6 +868,15 @@ export class RuntimeHttpServer {
|
|
|
810
868
|
return this.handleWatchStreamUpgrade(req, server);
|
|
811
869
|
}
|
|
812
870
|
|
|
871
|
+
// WebSocket upgrade for the assistant desktop RFB stream, under the same
|
|
872
|
+
// private-network restrictions and gateway-service token verification.
|
|
873
|
+
if (
|
|
874
|
+
path === "/v1/desktop/stream" &&
|
|
875
|
+
req.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
876
|
+
) {
|
|
877
|
+
return this.handleDesktopStreamUpgrade(req, server);
|
|
878
|
+
}
|
|
879
|
+
|
|
813
880
|
// Twilio webhook endpoints — before auth check because Twilio
|
|
814
881
|
// webhook POSTs don't include bearer tokens.
|
|
815
882
|
const twilioResponse = await this.handleTwilioWebhook(req, path);
|
|
@@ -949,199 +1016,141 @@ export class RuntimeHttpServer {
|
|
|
949
1016
|
return null;
|
|
950
1017
|
}
|
|
951
1018
|
|
|
952
|
-
|
|
1019
|
+
/**
|
|
1020
|
+
* Shared path for gateway-proxied WebSocket upgrades: private network peers
|
|
1021
|
+
* and origins only, then a gateway service token, then `parse` builds the
|
|
1022
|
+
* socket data (or a 4xx Response) and the upgrade happens. The gateway owns
|
|
1023
|
+
* downstream client auth and dials these upstreams on the client's behalf.
|
|
1024
|
+
* Returns a Response on failure. On success Bun has consumed the request and
|
|
1025
|
+
* nothing is returned; the type is `Response` only to fit the router.
|
|
1026
|
+
*/
|
|
1027
|
+
private upgradeRuntimeStream(
|
|
953
1028
|
req: Request,
|
|
954
1029
|
server: ReturnType<typeof Bun.serve>,
|
|
1030
|
+
label: string,
|
|
1031
|
+
parse: (query: URLSearchParams) => AllWebSocketData | Response,
|
|
955
1032
|
): Response {
|
|
956
1033
|
if (!isPrivateNetworkPeer(server, req) || !isPrivateNetworkOrigin(req)) {
|
|
957
1034
|
return httpError(
|
|
958
1035
|
"FORBIDDEN",
|
|
959
|
-
|
|
1036
|
+
`Direct ${label} access disabled: only private network peers allowed`,
|
|
960
1037
|
403,
|
|
961
1038
|
);
|
|
962
1039
|
}
|
|
963
|
-
|
|
964
|
-
// Verify the gateway service token before accepting the upgrade.
|
|
965
1040
|
const tokenError = this.verifyGatewayServiceToken(req);
|
|
966
1041
|
if (tokenError) {
|
|
967
1042
|
return tokenError;
|
|
968
1043
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
if (!callSessionId) {
|
|
973
|
-
return new Response("Missing callSessionId", { status: 400 });
|
|
1044
|
+
const data = parse(new URL(req.url).searchParams);
|
|
1045
|
+
if (data instanceof Response) {
|
|
1046
|
+
return data;
|
|
974
1047
|
}
|
|
975
|
-
|
|
976
|
-
// handlers route them to MediaStreamCallSession.
|
|
977
|
-
const upgraded = server.upgrade(req, {
|
|
978
|
-
data: {
|
|
979
|
-
wsType: "media-stream",
|
|
980
|
-
callSessionId,
|
|
981
|
-
} satisfies MediaStreamWebSocketData,
|
|
982
|
-
});
|
|
983
|
-
if (!upgraded) {
|
|
1048
|
+
if (!server.upgrade(req, { data })) {
|
|
984
1049
|
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
985
1050
|
}
|
|
986
|
-
// Bun's WebSocket upgrade consumes the request — no Response is sent.
|
|
987
1051
|
return undefined!;
|
|
988
1052
|
}
|
|
989
1053
|
|
|
990
|
-
|
|
991
|
-
* Handle WebSocket upgrade for `/v1/stt/stream`.
|
|
992
|
-
*
|
|
993
|
-
* Private-network restrictions apply (same as media-stream) so the
|
|
994
|
-
* runtime remains unreachable from the public internet. The gateway
|
|
995
|
-
* authenticates the downstream client and proxies the upgrade with a
|
|
996
|
-
* short-lived gateway service token.
|
|
997
|
-
*/
|
|
998
|
-
private handleSttStreamUpgrade(
|
|
1054
|
+
private handleMediaStreamUpgrade(
|
|
999
1055
|
req: Request,
|
|
1000
1056
|
server: ReturnType<typeof Bun.serve>,
|
|
1001
1057
|
): Response {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
const wsUrl = new URL(req.url);
|
|
1017
|
-
// provider is optional compatibility metadata — the runtime resolves
|
|
1018
|
-
// the streaming transcriber from config (`services.stt.provider`).
|
|
1019
|
-
const provider = wsUrl.searchParams.get("provider") ?? undefined;
|
|
1020
|
-
const mimeType = wsUrl.searchParams.get("mimeType");
|
|
1021
|
-
if (!mimeType) {
|
|
1022
|
-
return new Response("Missing required query parameter: mimeType", {
|
|
1023
|
-
status: 400,
|
|
1024
|
-
});
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
const sampleRateRaw = wsUrl.searchParams.get("sampleRate");
|
|
1028
|
-
const sampleRate = sampleRateRaw ? parseInt(sampleRateRaw, 10) : undefined;
|
|
1058
|
+
return this.upgradeRuntimeStream(req, server, "media-stream", (query) => {
|
|
1059
|
+
const callSessionId = query.get("callSessionId");
|
|
1060
|
+
if (!callSessionId) {
|
|
1061
|
+
return new Response("Missing callSessionId", { status: 400 });
|
|
1062
|
+
}
|
|
1063
|
+
// A distinct wsType routes these to MediaStreamCallSession.
|
|
1064
|
+
return {
|
|
1065
|
+
wsType: "media-stream",
|
|
1066
|
+
callSessionId,
|
|
1067
|
+
} satisfies MediaStreamWebSocketData;
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1029
1070
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1071
|
+
/** Handle WebSocket upgrade for `/v1/stt/stream`. */
|
|
1072
|
+
private handleSttStreamUpgrade(
|
|
1073
|
+
req: Request,
|
|
1074
|
+
server: ReturnType<typeof Bun.serve>,
|
|
1075
|
+
): Response {
|
|
1076
|
+
return this.upgradeRuntimeStream(req, server, "STT stream", (query) => {
|
|
1077
|
+
// provider is optional compatibility metadata; the runtime resolves
|
|
1078
|
+
// the streaming transcriber from config (`services.stt.provider`).
|
|
1079
|
+
const provider = query.get("provider") ?? undefined;
|
|
1080
|
+
const mimeType = query.get("mimeType");
|
|
1081
|
+
if (!mimeType) {
|
|
1082
|
+
return new Response("Missing required query parameter: mimeType", {
|
|
1083
|
+
status: 400,
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
const sampleRateRaw = query.get("sampleRate");
|
|
1087
|
+
return {
|
|
1033
1088
|
wsType: "stt-stream",
|
|
1034
1089
|
provider,
|
|
1035
1090
|
mimeType,
|
|
1036
|
-
sampleRate,
|
|
1037
|
-
sessionId,
|
|
1038
|
-
} satisfies SttStreamWebSocketData
|
|
1091
|
+
sampleRate: sampleRateRaw ? parseInt(sampleRateRaw, 10) : undefined,
|
|
1092
|
+
sessionId: crypto.randomUUID(),
|
|
1093
|
+
} satisfies SttStreamWebSocketData;
|
|
1039
1094
|
});
|
|
1040
|
-
if (!upgraded) {
|
|
1041
|
-
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
1042
|
-
}
|
|
1043
|
-
// Bun's WebSocket upgrade consumes the request — no Response is sent.
|
|
1044
|
-
return undefined!;
|
|
1045
1095
|
}
|
|
1046
1096
|
|
|
1047
|
-
/**
|
|
1048
|
-
* Handle WebSocket upgrade for `/v1/live-voice`.
|
|
1049
|
-
*
|
|
1050
|
-
* The gateway owns downstream client auth and forwards this upstream with
|
|
1051
|
-
* a short-lived gateway service token. The runtime accepts only private
|
|
1052
|
-
* network peers/origins so the shell is not publicly reachable.
|
|
1053
|
-
*/
|
|
1097
|
+
/** Handle WebSocket upgrade for `/v1/live-voice`. */
|
|
1054
1098
|
private handleLiveVoiceUpgrade(
|
|
1055
1099
|
req: Request,
|
|
1056
1100
|
server: ReturnType<typeof Bun.serve>,
|
|
1057
1101
|
): Response {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
"Direct live voice access disabled — only private network peers allowed",
|
|
1062
|
-
403,
|
|
1063
|
-
);
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
const tokenError = this.verifyGatewayServiceToken(req);
|
|
1067
|
-
if (tokenError) {
|
|
1068
|
-
return tokenError;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
const guardianPrincipalId =
|
|
1072
|
-
new URL(req.url).searchParams.get("guardianPrincipalId")?.trim() ||
|
|
1073
|
-
undefined;
|
|
1074
|
-
|
|
1075
|
-
const upgraded = server.upgrade(req, {
|
|
1076
|
-
data: {
|
|
1102
|
+
return this.upgradeRuntimeStream(req, server, "live voice", (query) => {
|
|
1103
|
+
const guardianPrincipalId = query.get("guardianPrincipalId")?.trim();
|
|
1104
|
+
return {
|
|
1077
1105
|
wsType: "live-voice",
|
|
1078
1106
|
...(guardianPrincipalId ? { guardianPrincipalId } : {}),
|
|
1079
|
-
} satisfies LiveVoiceWebSocketData
|
|
1107
|
+
} satisfies LiveVoiceWebSocketData;
|
|
1080
1108
|
});
|
|
1081
|
-
if (!upgraded) {
|
|
1082
|
-
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
1083
|
-
}
|
|
1084
|
-
return undefined!;
|
|
1085
1109
|
}
|
|
1086
1110
|
|
|
1087
|
-
/**
|
|
1088
|
-
* Handle WebSocket upgrade for `/v1/watch/stream`.
|
|
1089
|
-
*
|
|
1090
|
-
* Gated exactly as `/v1/stt/stream` is: private network peers and origins
|
|
1091
|
-
* only, then a gateway service token. The gateway owns downstream client
|
|
1092
|
-
* auth and dials this upstream on the client's behalf.
|
|
1093
|
-
*/
|
|
1111
|
+
/** Handle WebSocket upgrade for `/v1/watch/stream`. */
|
|
1094
1112
|
private handleWatchStreamUpgrade(
|
|
1095
1113
|
req: Request,
|
|
1096
1114
|
server: ReturnType<typeof Bun.serve>,
|
|
1097
1115
|
): Response {
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
"
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
const wsUrl = new URL(req.url);
|
|
1112
|
-
const mimeType = wsUrl.searchParams.get("mimeType");
|
|
1113
|
-
if (!mimeType) {
|
|
1114
|
-
return new Response("Missing required query parameter: mimeType", {
|
|
1115
|
-
status: 400,
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
const sampleRateRaw = wsUrl.searchParams.get("sampleRate");
|
|
1120
|
-
const sampleRate = sampleRateRaw ? parseInt(sampleRateRaw, 10) : undefined;
|
|
1121
|
-
const conversationId =
|
|
1122
|
-
wsUrl.searchParams.get("conversationId")?.trim() || undefined;
|
|
1123
|
-
const clientId = wsUrl.searchParams.get("clientId")?.trim() || undefined;
|
|
1124
|
-
const parsedTarget = parseWatchCaptureTarget(wsUrl.searchParams);
|
|
1125
|
-
if ("error" in parsedTarget) {
|
|
1126
|
-
return new Response(parsedTarget.error, { status: 400 });
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
const upgraded = server.upgrade(req, {
|
|
1130
|
-
data: {
|
|
1116
|
+
return this.upgradeRuntimeStream(req, server, "watch stream", (query) => {
|
|
1117
|
+
const mimeType = query.get("mimeType");
|
|
1118
|
+
if (!mimeType) {
|
|
1119
|
+
return new Response("Missing required query parameter: mimeType", {
|
|
1120
|
+
status: 400,
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
const sampleRateRaw = query.get("sampleRate");
|
|
1124
|
+
const parsedTarget = parseWatchCaptureTarget(query);
|
|
1125
|
+
if ("error" in parsedTarget) {
|
|
1126
|
+
return new Response(parsedTarget.error, { status: 400 });
|
|
1127
|
+
}
|
|
1128
|
+
return {
|
|
1131
1129
|
wsType: "watch-stream",
|
|
1132
1130
|
mimeType,
|
|
1133
|
-
sampleRate,
|
|
1134
|
-
conversationId,
|
|
1135
|
-
clientId,
|
|
1131
|
+
sampleRate: sampleRateRaw ? parseInt(sampleRateRaw, 10) : undefined,
|
|
1132
|
+
conversationId: query.get("conversationId")?.trim() || undefined,
|
|
1133
|
+
clientId: query.get("clientId")?.trim() || undefined,
|
|
1136
1134
|
captureTarget: parsedTarget.captureTarget,
|
|
1137
1135
|
sessionId: crypto.randomUUID(),
|
|
1138
|
-
} satisfies WatchStreamWebSocketData
|
|
1136
|
+
} satisfies WatchStreamWebSocketData;
|
|
1139
1137
|
});
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Handle WebSocket upgrade for `/v1/desktop/stream`. The feature gate runs
|
|
1142
|
+
* in the open handler, as close code 4008.
|
|
1143
|
+
*/
|
|
1144
|
+
private handleDesktopStreamUpgrade(
|
|
1145
|
+
req: Request,
|
|
1146
|
+
server: ReturnType<typeof Bun.serve>,
|
|
1147
|
+
): Response {
|
|
1148
|
+
return this.upgradeRuntimeStream(
|
|
1149
|
+
req,
|
|
1150
|
+
server,
|
|
1151
|
+
"desktop stream",
|
|
1152
|
+
() => ({ wsType: "desktop-stream" }) satisfies DesktopStreamWebSocketData,
|
|
1153
|
+
);
|
|
1145
1154
|
}
|
|
1146
1155
|
|
|
1147
1156
|
private async handleTwilioWebhook(
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* silently auto-installs allowlisted adapter packages via a sandboxed `bun`
|
|
12
12
|
* global install before failing with the install hint. `execFile` is stubbed
|
|
13
13
|
* via the shared `installExecFileStub` helper so tests can script
|
|
14
|
-
* `bun add --global` outcomes.
|
|
14
|
+
* `bun add --global` outcomes. It also threads the optional `model` through to
|
|
15
|
+
* the session manager and relays the warning a refused model comes back with.
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -43,6 +44,10 @@ interface FakeSessionState {
|
|
|
43
44
|
completedAt?: number;
|
|
44
45
|
error?: string;
|
|
45
46
|
stopReason?: string;
|
|
47
|
+
model?: string;
|
|
48
|
+
availableModels?: Array<{ value: string; label: string }>;
|
|
49
|
+
modelRevisionEpoch?: string;
|
|
50
|
+
modelRevision?: number;
|
|
46
51
|
latestUsage?: {
|
|
47
52
|
usedTokens: number;
|
|
48
53
|
contextSize: number;
|
|
@@ -55,10 +60,27 @@ interface FakeSessionState {
|
|
|
55
60
|
|
|
56
61
|
let fakeInMemorySessions: FakeSessionState[] = [];
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
interface SpawnResult {
|
|
64
|
+
acpSessionId: string;
|
|
65
|
+
protocolSessionId: string;
|
|
66
|
+
requestedModel?: string;
|
|
67
|
+
effectiveModel?: string;
|
|
68
|
+
modelWarning?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const DEFAULT_SPAWN_RESULT: SpawnResult = {
|
|
59
72
|
acpSessionId: "acp-route-session",
|
|
60
73
|
protocolSessionId: "proto-route-session",
|
|
61
|
-
|
|
74
|
+
effectiveModel: "opus",
|
|
75
|
+
};
|
|
76
|
+
let spawnResult: SpawnResult = DEFAULT_SPAWN_RESULT;
|
|
77
|
+
const spawnMock = mock(async (...args: unknown[]) => {
|
|
78
|
+
const options = args[6] as { model?: string } | undefined;
|
|
79
|
+
return {
|
|
80
|
+
...spawnResult,
|
|
81
|
+
requestedModel: options?.model?.trim() || undefined,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
62
84
|
|
|
63
85
|
const defaultSteerOrResumeImpl = async (
|
|
64
86
|
_id: string,
|
|
@@ -182,6 +204,10 @@ interface ResponseShape {
|
|
|
182
204
|
outputTokens?: number;
|
|
183
205
|
eventLog?: unknown[];
|
|
184
206
|
authErrorCode?: string;
|
|
207
|
+
model?: string;
|
|
208
|
+
availableModels?: Array<{ value: string; label: string }>;
|
|
209
|
+
modelRevisionEpoch?: string;
|
|
210
|
+
modelRevision?: number;
|
|
185
211
|
}>;
|
|
186
212
|
}
|
|
187
213
|
|
|
@@ -190,6 +216,7 @@ beforeEach(() => {
|
|
|
190
216
|
clearHistory();
|
|
191
217
|
resetExecFileStub();
|
|
192
218
|
spawnMock.mockClear();
|
|
219
|
+
spawnResult = DEFAULT_SPAWN_RESULT;
|
|
193
220
|
steerOrResumeMock.mockClear();
|
|
194
221
|
steerOrResumeImpl = defaultSteerOrResumeImpl;
|
|
195
222
|
_resetAdapterInstallCacheForTests();
|
|
@@ -328,6 +355,60 @@ describe("GET /v1/acp/sessions — merged in-memory + history", () => {
|
|
|
328
355
|
expect(s!.outputTokens).toBeUndefined();
|
|
329
356
|
});
|
|
330
357
|
|
|
358
|
+
test("carries the model for both layers and the picker for live ones only", async () => {
|
|
359
|
+
fakeInMemorySessions = [
|
|
360
|
+
{
|
|
361
|
+
id: "live-model",
|
|
362
|
+
agentId: "agent-live",
|
|
363
|
+
acpSessionId: "proto-live",
|
|
364
|
+
parentConversationId: "conv-model",
|
|
365
|
+
status: "running",
|
|
366
|
+
startedAt: 9000,
|
|
367
|
+
model: "opus",
|
|
368
|
+
modelRevisionEpoch: "01900000-0000-7000-8000-000000000001",
|
|
369
|
+
modelRevision: 17,
|
|
370
|
+
availableModels: [
|
|
371
|
+
{ value: "sonnet", label: "Sonnet" },
|
|
372
|
+
{ value: "opus", label: "Opus" },
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
];
|
|
376
|
+
insertHistoryRow({
|
|
377
|
+
id: "hist-model",
|
|
378
|
+
agentId: "agent-hist",
|
|
379
|
+
acpSessionId: "proto-hist",
|
|
380
|
+
parentConversationId: "conv-model",
|
|
381
|
+
startedAt: 1000,
|
|
382
|
+
status: "completed",
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const handler = getSessionsHandler();
|
|
386
|
+
const body = (await handler({})) as ResponseShape;
|
|
387
|
+
const live = body.sessions.find((s) => s.id === "live-model");
|
|
388
|
+
const hist = body.sessions.find((s) => s.id === "hist-model");
|
|
389
|
+
expect(live).toMatchObject({
|
|
390
|
+
model: "opus",
|
|
391
|
+
modelRevisionEpoch: "01900000-0000-7000-8000-000000000001",
|
|
392
|
+
modelRevision: 17,
|
|
393
|
+
availableModels: [
|
|
394
|
+
{ value: "sonnet", label: "Sonnet" },
|
|
395
|
+
{ value: "opus", label: "Opus" },
|
|
396
|
+
],
|
|
397
|
+
});
|
|
398
|
+
// A finished run has no live process to ask, so it reports neither.
|
|
399
|
+
expect(hist?.model).toBeUndefined();
|
|
400
|
+
expect(hist?.availableModels).toBeUndefined();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("omits the model for history rows written without one", async () => {
|
|
404
|
+
insertHistoryRow({ id: "hist-no-model", status: "completed" });
|
|
405
|
+
|
|
406
|
+
const handler = getSessionsHandler();
|
|
407
|
+
const body = (await handler({})) as ResponseShape;
|
|
408
|
+
const s = body.sessions.find((row) => row.id === "hist-no-model");
|
|
409
|
+
expect(s?.model).toBeUndefined();
|
|
410
|
+
});
|
|
411
|
+
|
|
331
412
|
test("dedupes by id with in-memory winning on collision", async () => {
|
|
332
413
|
// Same id in both layers — in-memory entry should win and its eventLog
|
|
333
414
|
// comes from the live ring buffer (empty here), not the stale history row.
|
|
@@ -579,6 +660,8 @@ describe("POST /v1/acp/spawn: sandboxed bun auto-install on missing binary", ()
|
|
|
579
660
|
acpSessionId: "acp-route-session",
|
|
580
661
|
protocolSessionId: "proto-route-session",
|
|
581
662
|
agent: "claude",
|
|
663
|
+
requestedModel: null,
|
|
664
|
+
effectiveModel: "opus",
|
|
582
665
|
});
|
|
583
666
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
584
667
|
// The real adapter binary is spawned, not a `bun x` wrapper.
|
|
@@ -593,7 +676,7 @@ describe("POST /v1/acp/spawn: sandboxed bun auto-install on missing binary", ()
|
|
|
593
676
|
expect(args).toEqual([
|
|
594
677
|
"add",
|
|
595
678
|
"--global",
|
|
596
|
-
"@agentclientprotocol/claude-agent-acp",
|
|
679
|
+
"@agentclientprotocol/claude-agent-acp@0.75.1",
|
|
597
680
|
]);
|
|
598
681
|
});
|
|
599
682
|
|
|
@@ -680,6 +763,78 @@ describe("POST /v1/acp/spawn: sandboxed bun auto-install on missing binary", ()
|
|
|
680
763
|
});
|
|
681
764
|
});
|
|
682
765
|
|
|
766
|
+
describe("POST /v1/acp/spawn: model selection", () => {
|
|
767
|
+
test("threads the requested model to the session manager", async () => {
|
|
768
|
+
const handler = getSpawnHandler();
|
|
769
|
+
const body = (await handler({
|
|
770
|
+
body: { ...SPAWN_BODY, model: "opus" },
|
|
771
|
+
})) as Record<string, unknown>;
|
|
772
|
+
|
|
773
|
+
expect(confirmationRequests).toHaveLength(1);
|
|
774
|
+
expect(confirmationRequests[0]?.input).toEqual({
|
|
775
|
+
agent: "claude",
|
|
776
|
+
task: "do something",
|
|
777
|
+
cwd: process.cwd(),
|
|
778
|
+
model: "opus",
|
|
779
|
+
});
|
|
780
|
+
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
781
|
+
expect((spawnMock.mock.calls[0] as unknown[])[6]).toEqual({
|
|
782
|
+
model: "opus",
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
expect(body.requestedModel).toBe("opus");
|
|
786
|
+
expect(body.effectiveModel).toBe("opus");
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
test("reports the manager-normalized requested model", async () => {
|
|
790
|
+
const handler = getSpawnHandler();
|
|
791
|
+
const body = (await handler({
|
|
792
|
+
body: { ...SPAWN_BODY, model: " opus " },
|
|
793
|
+
})) as Record<string, unknown>;
|
|
794
|
+
|
|
795
|
+
expect((spawnMock.mock.calls[0] as unknown[])[6]).toEqual({
|
|
796
|
+
model: " opus ",
|
|
797
|
+
});
|
|
798
|
+
expect(body.requestedModel).toBe("opus");
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
test("a spawn that names no model asks the manager for none", async () => {
|
|
802
|
+
const handler = getSpawnHandler();
|
|
803
|
+
await handler({ body: SPAWN_BODY });
|
|
804
|
+
|
|
805
|
+
const options = (spawnMock.mock.calls[0] as unknown[])[6] as {
|
|
806
|
+
model?: string;
|
|
807
|
+
};
|
|
808
|
+
const approvalInput = confirmationRequests[0]?.input as Record<
|
|
809
|
+
string,
|
|
810
|
+
unknown
|
|
811
|
+
>;
|
|
812
|
+
expect(approvalInput).not.toHaveProperty("model");
|
|
813
|
+
expect(options.model).toBeUndefined();
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
test("a refused model is a warning on an otherwise successful spawn", async () => {
|
|
817
|
+
spawnResult = {
|
|
818
|
+
...DEFAULT_SPAWN_RESULT,
|
|
819
|
+
modelWarning: "Invalid value for config option model: nope",
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
const handler = getSpawnHandler();
|
|
823
|
+
const body = (await handler({
|
|
824
|
+
body: { ...SPAWN_BODY, model: "nope" },
|
|
825
|
+
})) as Record<string, unknown>;
|
|
826
|
+
|
|
827
|
+
expect(body).toEqual({
|
|
828
|
+
acpSessionId: "acp-route-session",
|
|
829
|
+
protocolSessionId: "proto-route-session",
|
|
830
|
+
agent: "claude",
|
|
831
|
+
requestedModel: "nope",
|
|
832
|
+
effectiveModel: "opus",
|
|
833
|
+
modelWarning: "Invalid value for config option model: nope",
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
|
|
683
838
|
// ---------------------------------------------------------------------------
|
|
684
839
|
// POST /v1/acp/:id/steer: transparent resume of sessions not in memory
|
|
685
840
|
// ---------------------------------------------------------------------------
|