@vellumai/assistant 0.12.1-staging.1 → 0.12.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 +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/openapi.yaml +1 -291
- package/package.json +1 -1
- package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
- package/src/__tests__/app-compiler.test.ts +0 -6
- package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
- package/src/__tests__/app-dir-path-guard.test.ts +3 -1
- package/src/__tests__/app-source-watcher.test.ts +218 -0
- package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +5 -17
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +29 -28
- package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
- package/src/__tests__/config-watcher.test.ts +8 -23
- 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 +0 -26
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
- package/src/__tests__/conversation-queue.test.ts +207 -48
- package/src/__tests__/conversation-retry-discard.test.ts +1 -54
- package/src/__tests__/conversation-skill-tools.test.ts +0 -27
- package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
- package/src/__tests__/conversation-title-service.test.ts +10 -14
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
- package/src/__tests__/credential-record-retire.test.ts +1 -171
- package/src/__tests__/credential-routes.test.ts +3 -212
- package/src/__tests__/credential-security-invariants.test.ts +1 -2
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/host-bash-proxy.test.ts +45 -26
- package/src/__tests__/host-browser-proxy.test.ts +17 -6
- package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
- package/src/__tests__/host-file-proxy.test.ts +23 -11
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
- package/src/__tests__/llm-catalog-parity.test.ts +0 -25
- package/src/__tests__/mcp-auth-routes.test.ts +9 -6
- package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
- package/src/__tests__/mcp-health-check.test.ts +11 -8
- package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
- package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
- package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
- package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
- package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +548 -355
- package/src/__tests__/model-intents.test.ts +0 -3
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
- package/src/__tests__/script-proxy-certs.test.ts +0 -35
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/speaker-identification.test.ts +56 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
- package/src/__tests__/subagent-tools.test.ts +2 -3
- package/src/__tests__/telephony-tts-guard.test.ts +3 -38
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
- package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
- package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
- package/src/__tests__/workspace-git-service.test.ts +0 -26
- package/src/acp/__tests__/agent-process.test.ts +4 -258
- package/src/acp/__tests__/auth-required.test.ts +0 -96
- package/src/acp/__tests__/client-handler.test.ts +2 -90
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
- package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
- package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
- package/src/acp/__tests__/session-manager.test.ts +4 -895
- package/src/acp/agent-process.test.ts +1 -4
- package/src/acp/agent-process.ts +9 -94
- package/src/acp/auth-required.ts +1 -50
- package/src/acp/auto-install.test.ts +7 -9
- package/src/acp/client-handler.ts +3 -16
- package/src/acp/resolve-agent.test.ts +6 -220
- package/src/acp/resolve-agent.ts +23 -88
- package/src/acp/session-manager.test.ts +3 -196
- package/src/acp/session-manager.ts +18 -441
- package/src/acp/types.ts +5 -50
- package/src/api/events/app-files-changed.ts +3 -4
- package/src/api/index.ts +0 -6
- package/src/apps/app-store.ts +1 -1
- package/src/bundler/app-compiler.ts +0 -15
- package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
- package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
- package/src/calls/call-constants.ts +4 -24
- package/src/calls/call-controller.ts +199 -499
- package/src/calls/call-transport.ts +0 -16
- package/src/calls/media-stream-output.ts +0 -32
- package/src/calls/media-stream-server.ts +30 -82
- package/src/calls/media-stream-stt-session.ts +0 -15
- package/src/calls/resolve-call-tts-provider.ts +2 -33
- package/src/calls/speaker-identification.ts +233 -0
- package/src/calls/tts-call-strategy.ts +5 -9
- package/src/calls/types.ts +0 -2
- package/src/calls/voice-control-protocol.ts +21 -53
- package/src/calls/voice-session-bridge.ts +46 -59
- package/src/calls/voice-triage-escalate.ts +56 -175
- package/src/cli/commands/apps.help.ts +13 -67
- package/src/cli/commands/apps.ts +0 -183
- package/src/cli/commands/channels/index.help.ts +2 -70
- package/src/cli/commands/channels/index.ts +3 -16
- package/src/cli/commands/credentials.help.ts +2 -6
- 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 +1 -5
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +90 -103
- package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
- package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
- package/src/config/__tests__/platform-rehydration.test.ts +0 -93
- package/src/config/acp-defaults.test.ts +3 -13
- package/src/config/acp-defaults.ts +6 -23
- package/src/config/acp-schema.ts +2 -30
- package/src/config/bundled-skills/acp/SKILL.md +14 -22
- package/src/config/bundled-skills/acp/TOOLS.json +1 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +12 -8
- package/src/config/memory-v3-gate.test.ts +0 -31
- package/src/config/memory-v3-gate.ts +16 -20
- package/src/config/platform-rehydration.ts +21 -75
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
- package/src/config/schemas/calls.ts +28 -1
- package/src/config/schemas/live-voice.ts +173 -1
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +0 -10
- package/src/config/schemas/skills.ts +14 -0
- package/src/credential-execution/process-manager.test.ts +19 -18
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
- package/src/daemon/app-source-watcher.ts +218 -0
- package/src/daemon/config-watcher.ts +194 -13
- package/src/daemon/conversation-process.ts +64 -35
- package/src/daemon/conversation-skill-tools.ts +31 -13
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +8 -10
- package/src/daemon/conversation-tool-setup.ts +43 -37
- package/src/daemon/conversation.ts +3 -0
- package/src/daemon/host-bash-proxy.ts +12 -8
- package/src/daemon/host-browser-proxy.ts +1 -15
- package/src/daemon/host-file-proxy.ts +10 -8
- package/src/daemon/host-transfer-proxy.ts +18 -14
- package/src/daemon/lifecycle.ts +5 -0
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +9 -1
- package/src/daemon/skill-memory-refresh.ts +0 -64
- package/src/daemon/tool-side-effects.ts +18 -7
- package/src/hooks/types.ts +0 -33
- package/src/inbound/platform-callback-registration.ts +5 -4
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
- package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
- package/src/live-voice/live-voice-session.ts +693 -213
- package/src/{calls → live-voice}/progress-narration.ts +2 -2
- package/src/live-voice/protocol.ts +2 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +45 -29
- package/src/mcp/startup.ts +4 -8
- package/src/messaging/provider.ts +3 -10
- package/src/messaging/providers/callback-routing.ts +0 -18
- package/src/messaging/providers/channel-transport.ts +0 -56
- package/src/messaging/providers/discord/transport.ts +0 -17
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
- package/src/messaging/providers/slack/adapter.ts +44 -7
- package/src/messaging/providers/slack/transport.ts +0 -16
- package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
- package/src/messaging/providers/telegram-bot/client.ts +26 -0
- package/src/messaging/providers/telegram-bot/transport.ts +0 -20
- package/src/messaging/providers/whatsapp/adapter.ts +48 -1
- package/src/messaging/providers/whatsapp/client.ts +29 -0
- package/src/messaging/providers/whatsapp/transport.ts +0 -18
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/assistant-reply-producer.ts +3 -8
- package/src/notifications/conversation-pairing.ts +16 -36
- package/src/notifications/delivered-post-record.ts +1 -11
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/outbound-proxy/certs.ts +5 -3
- package/src/permissions/checker.test.ts +6 -11
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +63 -71
- package/src/persistence/conversation-lineage.ts +4 -12
- package/src/persistence/conversation-title-service.ts +26 -9
- 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-reads.ts +0 -30
- package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
- package/src/persistence/schema/memory-core.ts +0 -5
- package/src/platform/client.test.ts +0 -2
- package/src/platform/client.ts +10 -2
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +0 -1
- package/src/plugin-api/types.ts +0 -2
- package/src/plugins/defaults/index.ts +0 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
- package/src/plugins/defaults/memory/hooks/init.ts +0 -10
- package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
- package/src/plugins/defaults/memory/startup.ts +1 -6
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
- package/src/plugins/defaults/memory/worker.ts +0 -6
- package/src/plugins/mcp-servers.ts +75 -15
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +23 -9
- package/src/providers/model-catalog.ts +0 -34
- package/src/providers/model-intents.ts +5 -10
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
- package/src/providers/openai/chat-completions-provider.ts +78 -192
- package/src/providers/openai/codex-models.ts +4 -0
- package/src/runtime/AGENTS.md +0 -2
- package/src/runtime/auth/same-actor.ts +3 -38
- package/src/runtime/http-server.ts +151 -160
- package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
- package/src/runtime/routes/acp-routes.test.ts +0 -15
- package/src/runtime/routes/acp-routes.ts +9 -72
- package/src/runtime/routes/app-management-routes.ts +0 -73
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +0 -40
- package/src/runtime/routes/credential-routes.ts +10 -19
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/log-export-routes.ts +0 -2
- package/src/runtime/routes/mcp-auth-routes.ts +47 -39
- package/src/runtime/routes/migration-routes.ts +21 -8
- package/src/runtime/routes/platform-routes.ts +15 -18
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
- package/src/runtime/services/conversation-serializer.ts +3 -16
- package/src/runtime/sync/resource-sync-events.ts +1 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/ces-rpc-record-backend.ts +0 -7
- package/src/subagent/consult-context.ts +25 -12
- package/src/tools/acp/list-agents.test.ts +1 -3
- package/src/tools/acp/spawn.test.ts +11 -146
- package/src/tools/acp/spawn.ts +3 -21
- package/src/tools/acp/status.ts +3 -40
- package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
- package/src/tools/capability-offer.test.ts +2 -22
- package/src/tools/capability-offer.ts +1 -16
- package/src/tools/computer-use/definitions.ts +2 -4
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +0 -100
- package/src/tools/host-filesystem/edit.test.ts +1 -2
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +1 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +1 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +1 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +0 -16
- package/src/tools/host-terminal/host-shell.ts +20 -4
- package/src/tools/policy-context.ts +7 -6
- package/src/tools/subagent/spawn.ts +8 -5
- package/src/tools/watch/watch-retro-report.ts +8 -0
- package/src/util/__tests__/language-subtag.test.ts +1 -23
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/language-subtag.ts +0 -16
- package/src/util/platform.ts +0 -8
- package/src/workspace/git-service.ts +54 -5
- package/src/workspace/migrations/registry.ts +0 -6
- package/scripts/smoke-desktop-window-theme.ts +0 -186
- package/src/__tests__/git-porcelain.test.ts +0 -47
- package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
- package/src/__tests__/resolve-app.test.ts +0 -135
- package/src/__tests__/source-fingerprint.test.ts +0 -184
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
- package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
- package/src/acp/model-config.test.ts +0 -166
- package/src/acp/model-config.ts +0 -138
- package/src/api/events/acp-session-model-update.test.ts +0 -88
- package/src/api/events/acp-session-model-update.ts +0 -46
- package/src/apps/resolve-app.ts +0 -81
- package/src/apps/source-fingerprint.ts +0 -266
- package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
- package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
- package/src/calls/barge-in-guard.ts +0 -118
- package/src/calls/voice-leg-coordinator.ts +0 -189
- package/src/calls/voice-progress-cadence.ts +0 -423
- package/src/cli/commands/__tests__/apps.test.ts +0 -245
- package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
- package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
- package/src/cli/commands/channels/send.ts +0 -128
- package/src/config/__tests__/platform-identity.test.ts +0 -117
- package/src/config/acp-agent-write.ts +0 -43
- package/src/config/acp-schema.test.ts +0 -81
- package/src/config/platform-identity.ts +0 -144
- package/src/config/schemas/__tests__/voice.test.ts +0 -148
- package/src/config/schemas/voice.ts +0 -150
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
- package/src/daemon/app-change-notify.ts +0 -45
- package/src/desktop/__tests__/fake-desktop.ts +0 -119
- package/src/desktop/desktop-chrome-policy.test.ts +0 -71
- package/src/desktop/desktop-chrome-policy.ts +0 -42
- package/src/desktop/desktop-dependencies.test.ts +0 -102
- package/src/desktop/desktop-dependencies.ts +0 -306
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
- package/src/desktop/desktop-panel-config.test.ts +0 -135
- package/src/desktop/desktop-panel-config.ts +0 -149
- package/src/desktop/desktop-session-manager.test.ts +0 -627
- package/src/desktop/desktop-session-manager.ts +0 -814
- package/src/desktop/desktop-stream-bridge.test.ts +0 -395
- package/src/desktop/desktop-stream-bridge.ts +0 -214
- package/src/desktop/desktop-wallpaper.test.ts +0 -191
- package/src/desktop/desktop-wallpaper.ts +0 -113
- package/src/desktop/desktop-window-theme.ts +0 -170
- package/src/i18n/AGENTS.md +0 -22
- package/src/i18n/__tests__/i18n.test.ts +0 -108
- package/src/i18n/index.ts +0 -16
- package/src/i18n/locales.ts +0 -93
- package/src/i18n/messages.ts +0 -61
- package/src/i18n/title.ts +0 -25
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
- package/src/mcp/spec-schema.ts +0 -111
- package/src/mcp/workspace-mcp-config.ts +0 -188
- package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
- package/src/persistence/conversation-title-placeholders.ts +0 -23
- package/src/persistence/message-cursor.ts +0 -78
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
- package/src/providers/gemini-thought-signature.test.ts +0 -63
- package/src/providers/gemini-thought-signature.ts +0 -44
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
- package/src/providers/openai/google-thought-signature.ts +0 -320
- package/src/runtime/__tests__/channel-send.test.ts +0 -443
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
- package/src/runtime/channel-send.ts +0 -316
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
- package/src/runtime/routes/channel-send-routes.ts +0 -85
- package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
- package/src/runtime/routes/desktop-setup-routes.ts +0 -35
- package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
- package/src/security/ces-http-record-backend.ts +0 -120
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
- package/src/skills/workspace-skill-md-signature.ts +0 -50
- package/src/tools/acp/status.test.ts +0 -94
- package/src/util/ca-bundle.ts +0 -13
- package/src/workspace/git-porcelain.ts +0 -66
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
- /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
|
@@ -32,12 +32,6 @@ 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";
|
|
41
35
|
import {
|
|
42
36
|
createLiveVoiceConnection,
|
|
43
37
|
type LiveVoiceConnection,
|
|
@@ -235,32 +229,6 @@ interface WatchStreamWebSocketData {
|
|
|
235
229
|
session?: WatchStreamSession;
|
|
236
230
|
}
|
|
237
231
|
|
|
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
|
-
|
|
264
232
|
export class RuntimeHttpServer {
|
|
265
233
|
private server: ReturnType<typeof Bun.serve> | null = null;
|
|
266
234
|
private port: number;
|
|
@@ -296,6 +264,11 @@ export class RuntimeHttpServer {
|
|
|
296
264
|
}
|
|
297
265
|
|
|
298
266
|
async start(): Promise<void> {
|
|
267
|
+
type AllWebSocketData =
|
|
268
|
+
| MediaStreamWebSocketData
|
|
269
|
+
| SttStreamWebSocketData
|
|
270
|
+
| LiveVoiceWebSocketData
|
|
271
|
+
| WatchStreamWebSocketData;
|
|
299
272
|
this.server = Bun.serve<AllWebSocketData>({
|
|
300
273
|
port: this.port,
|
|
301
274
|
hostname: this.hostname,
|
|
@@ -451,22 +424,6 @@ export class RuntimeHttpServer {
|
|
|
451
424
|
void session.start();
|
|
452
425
|
return;
|
|
453
426
|
}
|
|
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
|
-
}
|
|
470
427
|
log.warn("WebSocket opened with unknown data type — closing");
|
|
471
428
|
ws.close(1008, "Unknown WebSocket type");
|
|
472
429
|
},
|
|
@@ -517,10 +474,6 @@ export class RuntimeHttpServer {
|
|
|
517
474
|
}
|
|
518
475
|
return;
|
|
519
476
|
}
|
|
520
|
-
if (data.wsType === "desktop-stream") {
|
|
521
|
-
data.bridge?.handleClientFrame(message);
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
477
|
log.warn("WebSocket message on unknown data type — closing");
|
|
525
478
|
ws.close(1008, "Unknown WebSocket type");
|
|
526
479
|
},
|
|
@@ -614,14 +567,6 @@ export class RuntimeHttpServer {
|
|
|
614
567
|
}
|
|
615
568
|
return;
|
|
616
569
|
}
|
|
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
|
-
}
|
|
625
570
|
log.warn(
|
|
626
571
|
{ code, reason: reason?.toString() },
|
|
627
572
|
"WebSocket with unknown data type closed",
|
|
@@ -762,9 +707,6 @@ export class RuntimeHttpServer {
|
|
|
762
707
|
// that a socket closing just before shutdown had already under way.
|
|
763
708
|
await drainWatchRetros();
|
|
764
709
|
|
|
765
|
-
// Bounded by the manager's kill grace, so shutdown never hangs on X.
|
|
766
|
-
await destroyDesktopSessionManager();
|
|
767
|
-
|
|
768
710
|
const liveVoiceManager = getLiveVoiceSessionManager();
|
|
769
711
|
const liveVoiceSessionId = liveVoiceManager.activeSessionId;
|
|
770
712
|
if (liveVoiceSessionId) {
|
|
@@ -868,15 +810,6 @@ export class RuntimeHttpServer {
|
|
|
868
810
|
return this.handleWatchStreamUpgrade(req, server);
|
|
869
811
|
}
|
|
870
812
|
|
|
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
|
-
|
|
880
813
|
// Twilio webhook endpoints — before auth check because Twilio
|
|
881
814
|
// webhook POSTs don't include bearer tokens.
|
|
882
815
|
const twilioResponse = await this.handleTwilioWebhook(req, path);
|
|
@@ -1016,141 +949,199 @@ export class RuntimeHttpServer {
|
|
|
1016
949
|
return null;
|
|
1017
950
|
}
|
|
1018
951
|
|
|
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(
|
|
952
|
+
private handleMediaStreamUpgrade(
|
|
1028
953
|
req: Request,
|
|
1029
954
|
server: ReturnType<typeof Bun.serve>,
|
|
1030
|
-
label: string,
|
|
1031
|
-
parse: (query: URLSearchParams) => AllWebSocketData | Response,
|
|
1032
955
|
): Response {
|
|
1033
956
|
if (!isPrivateNetworkPeer(server, req) || !isPrivateNetworkOrigin(req)) {
|
|
1034
957
|
return httpError(
|
|
1035
958
|
"FORBIDDEN",
|
|
1036
|
-
|
|
959
|
+
"Direct media-stream access disabled — only private network peers allowed",
|
|
1037
960
|
403,
|
|
1038
961
|
);
|
|
1039
962
|
}
|
|
963
|
+
|
|
964
|
+
// Verify the gateway service token before accepting the upgrade.
|
|
1040
965
|
const tokenError = this.verifyGatewayServiceToken(req);
|
|
1041
966
|
if (tokenError) {
|
|
1042
967
|
return tokenError;
|
|
1043
968
|
}
|
|
1044
|
-
const data = parse(new URL(req.url).searchParams);
|
|
1045
|
-
if (data instanceof Response) {
|
|
1046
|
-
return data;
|
|
1047
|
-
}
|
|
1048
|
-
if (!server.upgrade(req, { data })) {
|
|
1049
|
-
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
1050
|
-
}
|
|
1051
|
-
return undefined!;
|
|
1052
|
-
}
|
|
1053
969
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
// A distinct wsType routes these to MediaStreamCallSession.
|
|
1064
|
-
return {
|
|
970
|
+
const wsUrl = new URL(req.url);
|
|
971
|
+
const callSessionId = wsUrl.searchParams.get("callSessionId");
|
|
972
|
+
if (!callSessionId) {
|
|
973
|
+
return new Response("Missing callSessionId", { status: 400 });
|
|
974
|
+
}
|
|
975
|
+
// Media-stream connections use a distinct wsType so the open/message/close
|
|
976
|
+
// handlers route them to MediaStreamCallSession.
|
|
977
|
+
const upgraded = server.upgrade(req, {
|
|
978
|
+
data: {
|
|
1065
979
|
wsType: "media-stream",
|
|
1066
980
|
callSessionId,
|
|
1067
|
-
} satisfies MediaStreamWebSocketData
|
|
981
|
+
} satisfies MediaStreamWebSocketData,
|
|
1068
982
|
});
|
|
983
|
+
if (!upgraded) {
|
|
984
|
+
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
985
|
+
}
|
|
986
|
+
// Bun's WebSocket upgrade consumes the request — no Response is sent.
|
|
987
|
+
return undefined!;
|
|
1069
988
|
}
|
|
1070
989
|
|
|
1071
|
-
/**
|
|
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
|
+
*/
|
|
1072
998
|
private handleSttStreamUpgrade(
|
|
1073
999
|
req: Request,
|
|
1074
1000
|
server: ReturnType<typeof Bun.serve>,
|
|
1075
1001
|
): Response {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
return
|
|
1002
|
+
if (!isPrivateNetworkPeer(server, req) || !isPrivateNetworkOrigin(req)) {
|
|
1003
|
+
return httpError(
|
|
1004
|
+
"FORBIDDEN",
|
|
1005
|
+
"Direct STT stream access disabled — only private network peers allowed",
|
|
1006
|
+
403,
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Verify the gateway service token before accepting the upgrade.
|
|
1011
|
+
const tokenError = this.verifyGatewayServiceToken(req);
|
|
1012
|
+
if (tokenError) {
|
|
1013
|
+
return tokenError;
|
|
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;
|
|
1029
|
+
|
|
1030
|
+
const sessionId = crypto.randomUUID();
|
|
1031
|
+
const upgraded = server.upgrade(req, {
|
|
1032
|
+
data: {
|
|
1088
1033
|
wsType: "stt-stream",
|
|
1089
1034
|
provider,
|
|
1090
1035
|
mimeType,
|
|
1091
|
-
sampleRate
|
|
1092
|
-
sessionId
|
|
1093
|
-
} satisfies SttStreamWebSocketData
|
|
1036
|
+
sampleRate,
|
|
1037
|
+
sessionId,
|
|
1038
|
+
} satisfies SttStreamWebSocketData,
|
|
1094
1039
|
});
|
|
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!;
|
|
1095
1045
|
}
|
|
1096
1046
|
|
|
1097
|
-
/**
|
|
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
|
+
*/
|
|
1098
1054
|
private handleLiveVoiceUpgrade(
|
|
1099
1055
|
req: Request,
|
|
1100
1056
|
server: ReturnType<typeof Bun.serve>,
|
|
1101
1057
|
): Response {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1058
|
+
if (!isPrivateNetworkPeer(server, req) || !isPrivateNetworkOrigin(req)) {
|
|
1059
|
+
return httpError(
|
|
1060
|
+
"FORBIDDEN",
|
|
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: {
|
|
1105
1077
|
wsType: "live-voice",
|
|
1106
1078
|
...(guardianPrincipalId ? { guardianPrincipalId } : {}),
|
|
1107
|
-
} satisfies LiveVoiceWebSocketData
|
|
1079
|
+
} satisfies LiveVoiceWebSocketData,
|
|
1108
1080
|
});
|
|
1081
|
+
if (!upgraded) {
|
|
1082
|
+
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
1083
|
+
}
|
|
1084
|
+
return undefined!;
|
|
1109
1085
|
}
|
|
1110
1086
|
|
|
1111
|
-
/**
|
|
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
|
+
*/
|
|
1112
1094
|
private handleWatchStreamUpgrade(
|
|
1113
1095
|
req: Request,
|
|
1114
1096
|
server: ReturnType<typeof Bun.serve>,
|
|
1115
1097
|
): Response {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1098
|
+
if (!isPrivateNetworkPeer(server, req) || !isPrivateNetworkOrigin(req)) {
|
|
1099
|
+
return httpError(
|
|
1100
|
+
"FORBIDDEN",
|
|
1101
|
+
"Direct watch stream access disabled: only private network peers allowed",
|
|
1102
|
+
403,
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
const tokenError = this.verifyGatewayServiceToken(req);
|
|
1107
|
+
if (tokenError) {
|
|
1108
|
+
return tokenError;
|
|
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: {
|
|
1129
1131
|
wsType: "watch-stream",
|
|
1130
1132
|
mimeType,
|
|
1131
|
-
sampleRate
|
|
1132
|
-
conversationId
|
|
1133
|
-
clientId
|
|
1133
|
+
sampleRate,
|
|
1134
|
+
conversationId,
|
|
1135
|
+
clientId,
|
|
1134
1136
|
captureTarget: parsedTarget.captureTarget,
|
|
1135
1137
|
sessionId: crypto.randomUUID(),
|
|
1136
|
-
} satisfies WatchStreamWebSocketData
|
|
1138
|
+
} satisfies WatchStreamWebSocketData,
|
|
1137
1139
|
});
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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
|
-
);
|
|
1140
|
+
if (!upgraded) {
|
|
1141
|
+
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
1142
|
+
}
|
|
1143
|
+
// Bun's WebSocket upgrade consumes the request, so no Response is sent.
|
|
1144
|
+
return undefined!;
|
|
1154
1145
|
}
|
|
1155
1146
|
|
|
1156
1147
|
private async handleTwilioWebhook(
|
|
@@ -11,8 +11,7 @@
|
|
|
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.
|
|
15
|
-
* the session manager and relays the warning a refused model comes back with.
|
|
14
|
+
* `bun add --global` outcomes.
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -44,10 +43,6 @@ interface FakeSessionState {
|
|
|
44
43
|
completedAt?: number;
|
|
45
44
|
error?: string;
|
|
46
45
|
stopReason?: string;
|
|
47
|
-
model?: string;
|
|
48
|
-
availableModels?: Array<{ value: string; label: string }>;
|
|
49
|
-
modelRevisionEpoch?: string;
|
|
50
|
-
modelRevision?: number;
|
|
51
46
|
latestUsage?: {
|
|
52
47
|
usedTokens: number;
|
|
53
48
|
contextSize: number;
|
|
@@ -60,27 +55,10 @@ interface FakeSessionState {
|
|
|
60
55
|
|
|
61
56
|
let fakeInMemorySessions: FakeSessionState[] = [];
|
|
62
57
|
|
|
63
|
-
|
|
64
|
-
acpSessionId: string;
|
|
65
|
-
protocolSessionId: string;
|
|
66
|
-
requestedModel?: string;
|
|
67
|
-
effectiveModel?: string;
|
|
68
|
-
modelWarning?: string;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const DEFAULT_SPAWN_RESULT: SpawnResult = {
|
|
58
|
+
const spawnMock = mock(async () => ({
|
|
72
59
|
acpSessionId: "acp-route-session",
|
|
73
60
|
protocolSessionId: "proto-route-session",
|
|
74
|
-
|
|
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
|
-
});
|
|
61
|
+
}));
|
|
84
62
|
|
|
85
63
|
const defaultSteerOrResumeImpl = async (
|
|
86
64
|
_id: string,
|
|
@@ -204,10 +182,6 @@ interface ResponseShape {
|
|
|
204
182
|
outputTokens?: number;
|
|
205
183
|
eventLog?: unknown[];
|
|
206
184
|
authErrorCode?: string;
|
|
207
|
-
model?: string;
|
|
208
|
-
availableModels?: Array<{ value: string; label: string }>;
|
|
209
|
-
modelRevisionEpoch?: string;
|
|
210
|
-
modelRevision?: number;
|
|
211
185
|
}>;
|
|
212
186
|
}
|
|
213
187
|
|
|
@@ -216,7 +190,6 @@ beforeEach(() => {
|
|
|
216
190
|
clearHistory();
|
|
217
191
|
resetExecFileStub();
|
|
218
192
|
spawnMock.mockClear();
|
|
219
|
-
spawnResult = DEFAULT_SPAWN_RESULT;
|
|
220
193
|
steerOrResumeMock.mockClear();
|
|
221
194
|
steerOrResumeImpl = defaultSteerOrResumeImpl;
|
|
222
195
|
_resetAdapterInstallCacheForTests();
|
|
@@ -355,60 +328,6 @@ describe("GET /v1/acp/sessions — merged in-memory + history", () => {
|
|
|
355
328
|
expect(s!.outputTokens).toBeUndefined();
|
|
356
329
|
});
|
|
357
330
|
|
|
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
|
-
|
|
412
331
|
test("dedupes by id with in-memory winning on collision", async () => {
|
|
413
332
|
// Same id in both layers — in-memory entry should win and its eventLog
|
|
414
333
|
// comes from the live ring buffer (empty here), not the stale history row.
|
|
@@ -660,8 +579,6 @@ describe("POST /v1/acp/spawn: sandboxed bun auto-install on missing binary", ()
|
|
|
660
579
|
acpSessionId: "acp-route-session",
|
|
661
580
|
protocolSessionId: "proto-route-session",
|
|
662
581
|
agent: "claude",
|
|
663
|
-
requestedModel: null,
|
|
664
|
-
effectiveModel: "opus",
|
|
665
582
|
});
|
|
666
583
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
667
584
|
// The real adapter binary is spawned, not a `bun x` wrapper.
|
|
@@ -676,7 +593,7 @@ describe("POST /v1/acp/spawn: sandboxed bun auto-install on missing binary", ()
|
|
|
676
593
|
expect(args).toEqual([
|
|
677
594
|
"add",
|
|
678
595
|
"--global",
|
|
679
|
-
"@agentclientprotocol/claude-agent-acp
|
|
596
|
+
"@agentclientprotocol/claude-agent-acp",
|
|
680
597
|
]);
|
|
681
598
|
});
|
|
682
599
|
|
|
@@ -763,78 +680,6 @@ describe("POST /v1/acp/spawn: sandboxed bun auto-install on missing binary", ()
|
|
|
763
680
|
});
|
|
764
681
|
});
|
|
765
682
|
|
|
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
|
-
|
|
838
683
|
// ---------------------------------------------------------------------------
|
|
839
684
|
// POST /v1/acp/:id/steer: transparent resume of sessions not in memory
|
|
840
685
|
// ---------------------------------------------------------------------------
|