@vellumai/assistant 0.12.0 → 0.12.1-dev.202609142113.d1590bc
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 +5 -1
- 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 +2 -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 +119 -0
- package/src/desktop/desktop-chrome-policy.test.ts +71 -0
- package/src/desktop/desktop-chrome-policy.ts +42 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +306 -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 +627 -0
- package/src/desktop/desktop-session-manager.ts +814 -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-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-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/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- 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/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +24 -3
- 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__/safe-env.test.ts +6 -6
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/safe-env.ts +23 -3
- 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
|
@@ -26,7 +26,7 @@ afterAll(() => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
describe("always-loaded tool count", () => {
|
|
29
|
-
test("should be exactly
|
|
29
|
+
test("should be exactly 19 with recall occupying the existing slot", async () => {
|
|
30
30
|
await initializeTools();
|
|
31
31
|
const allDefs = getAllToolDefinitions();
|
|
32
32
|
|
|
@@ -44,23 +44,31 @@ describe("always-loaded tool count", () => {
|
|
|
44
44
|
);
|
|
45
45
|
const activeNames = activeTools.map((t) => t.name).sort();
|
|
46
46
|
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
47
|
+
// `watch_retro_report` survives this baseline for the same reason the
|
|
48
|
+
// ui_* tools do: a watch retrospective runs clientless, so it can only
|
|
49
|
+
// report through a tool that survives this baseline. The ui_* tools are
|
|
50
|
+
// here because background UI surfaces persist and return instead of
|
|
51
|
+
// awaiting action, so they no longer need a connected client.
|
|
52
|
+
// Host tool definitions stay on the wire for background turns: schemas
|
|
53
|
+
// are cache-stable, and the shared execution gate rejects host tools on
|
|
54
|
+
// explicitly non-interactive turns before any proxy dispatch.
|
|
55
55
|
const expectedNames = [
|
|
56
56
|
"bash",
|
|
57
57
|
"file_edit",
|
|
58
58
|
"file_read",
|
|
59
59
|
"file_write",
|
|
60
|
+
"host_bash",
|
|
61
|
+
"host_file_edit",
|
|
62
|
+
"host_file_read",
|
|
63
|
+
"host_file_transfer",
|
|
64
|
+
"host_file_write",
|
|
60
65
|
"recall",
|
|
61
66
|
"remember",
|
|
62
67
|
"skill_execute",
|
|
63
68
|
"skill_load",
|
|
69
|
+
"ui_dismiss",
|
|
70
|
+
"ui_show",
|
|
71
|
+
"ui_update",
|
|
64
72
|
"watch_retro_report",
|
|
65
73
|
"web_fetch",
|
|
66
74
|
"web_search",
|
|
@@ -68,6 +76,6 @@ describe("always-loaded tool count", () => {
|
|
|
68
76
|
|
|
69
77
|
expect(activeNames).toEqual(expectedNames);
|
|
70
78
|
expect(activeNames.filter((name) => name === "recall")).toHaveLength(1);
|
|
71
|
-
expect(activeTools.length).toBe(
|
|
79
|
+
expect(activeTools.length).toBe(19);
|
|
72
80
|
});
|
|
73
81
|
});
|
|
@@ -3029,69 +3029,6 @@ describe("AnthropicProvider - internal attachment id", () => {
|
|
|
3029
3029
|
});
|
|
3030
3030
|
});
|
|
3031
3031
|
|
|
3032
|
-
// ---------------------------------------------------------------------------
|
|
3033
|
-
// Tests — Orphaned UTF-16 surrogate sanitization
|
|
3034
|
-
// ---------------------------------------------------------------------------
|
|
3035
|
-
|
|
3036
|
-
describe("AnthropicProvider — surrogate sanitization", () => {
|
|
3037
|
-
let provider: AnthropicProvider;
|
|
3038
|
-
|
|
3039
|
-
beforeEach(() => {
|
|
3040
|
-
lastStreamParams = null;
|
|
3041
|
-
provider = new AnthropicProvider("sk-ant-test", "claude-sonnet-4-6");
|
|
3042
|
-
});
|
|
3043
|
-
|
|
3044
|
-
test("strips orphaned high surrogate from a tool result before sending", async () => {
|
|
3045
|
-
// An orphaned high surrogate — the exact shape that triggers Anthropic's
|
|
3046
|
-
// "no low surrogate in string" 400. The mock's JSON.parse(JSON.stringify)
|
|
3047
|
-
// on line ~44 would throw if sanitization didn't happen.
|
|
3048
|
-
const LONE_HIGH = "\uD83C";
|
|
3049
|
-
const messages: Message[] = [
|
|
3050
|
-
toolUseMsg("tu1", "bash"),
|
|
3051
|
-
toolResultMsg("tu1", `shell output ${LONE_HIGH} more output`),
|
|
3052
|
-
userMsg("what happened?"),
|
|
3053
|
-
];
|
|
3054
|
-
|
|
3055
|
-
await provider.sendMessage(messages);
|
|
3056
|
-
|
|
3057
|
-
const sent = lastStreamParams!.messages as Array<{
|
|
3058
|
-
role: string;
|
|
3059
|
-
content: Array<{ type: string; content?: string; text?: string }>;
|
|
3060
|
-
}>;
|
|
3061
|
-
// Find the tool_result block in the captured payload and assert no orphans.
|
|
3062
|
-
const toolResult = sent
|
|
3063
|
-
.flatMap((m) => m.content)
|
|
3064
|
-
.find((b) => b.type === "tool_result");
|
|
3065
|
-
expect(toolResult).toBeDefined();
|
|
3066
|
-
expect(toolResult!.content).toBeDefined();
|
|
3067
|
-
const content = toolResult!.content as string;
|
|
3068
|
-
for (let i = 0; i < content.length; i++) {
|
|
3069
|
-
const code = content.charCodeAt(i);
|
|
3070
|
-
if (code >= 0xd800 && code <= 0xdbff) {
|
|
3071
|
-
const next = i + 1 < content.length ? content.charCodeAt(i + 1) : 0;
|
|
3072
|
-
expect(next >= 0xdc00 && next <= 0xdfff).toBe(true);
|
|
3073
|
-
i++;
|
|
3074
|
-
} else {
|
|
3075
|
-
expect(code < 0xdc00 || code > 0xdfff).toBe(true);
|
|
3076
|
-
}
|
|
3077
|
-
}
|
|
3078
|
-
});
|
|
3079
|
-
|
|
3080
|
-
test("clean payloads are not copied unnecessarily", async () => {
|
|
3081
|
-
// When there are no orphans, the sanitizer should be a no-op. We can't
|
|
3082
|
-
// easily assert reference equality through the mock boundary (the mock
|
|
3083
|
-
// JSON-round-trips params for capture), but we can at least confirm the
|
|
3084
|
-
// call succeeds without error on ordinary payloads containing valid
|
|
3085
|
-
// surrogate pairs (emoji).
|
|
3086
|
-
const EMOJI = "\uD83C\uDF89";
|
|
3087
|
-
await provider.sendMessage([userMsg(`hello ${EMOJI} world`)]);
|
|
3088
|
-
const sent = lastStreamParams!.messages as Array<{
|
|
3089
|
-
content: Array<{ text?: string }>;
|
|
3090
|
-
}>;
|
|
3091
|
-
expect(sent[0].content[0].text).toContain(EMOJI);
|
|
3092
|
-
});
|
|
3093
|
-
});
|
|
3094
|
-
|
|
3095
3032
|
// ---------------------------------------------------------------------------
|
|
3096
3033
|
// Haiku model gating
|
|
3097
3034
|
// ---------------------------------------------------------------------------
|
|
@@ -112,6 +112,12 @@ describe("compileApp", () => {
|
|
|
112
112
|
const html = await readFile(join(appDir, "dist", "index.html"), "utf-8");
|
|
113
113
|
expect(html).toContain('src="main.js"');
|
|
114
114
|
expect(html).toContain('type="module"');
|
|
115
|
+
|
|
116
|
+
const fingerprint = JSON.parse(
|
|
117
|
+
await readFile(join(appDir, "dist", ".source-fingerprint.json"), "utf-8"),
|
|
118
|
+
) as { algorithm: string; files: Record<string, string> };
|
|
119
|
+
expect(fingerprint.algorithm).toBe("sha256");
|
|
120
|
+
expect(fingerprint.files["src/main.tsx"]).toMatch(/^[0-9a-f]{64}$/);
|
|
115
121
|
});
|
|
116
122
|
|
|
117
123
|
test("compiles preact JSX correctly", async () => {
|
|
@@ -591,8 +591,11 @@ describe("forwardAppControlProxyTool", () => {
|
|
|
591
591
|
ctx,
|
|
592
592
|
);
|
|
593
593
|
expect(result.isError).toBe(true);
|
|
594
|
-
expect(result.content).toContain(
|
|
595
|
-
|
|
594
|
+
expect(result.content).toContain(
|
|
595
|
+
"The Vellum desktop app is needed to control apps on your computer",
|
|
596
|
+
);
|
|
597
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
598
|
+
expect(result.content).not.toContain("app_control_click");
|
|
596
599
|
});
|
|
597
600
|
|
|
598
601
|
test("delegates to proxy resolver when available", async () => {
|
|
@@ -9,15 +9,13 @@ import { describe, expect, test } from "bun:test";
|
|
|
9
9
|
* This prevents regressions where new code bypasses the dirName-based path
|
|
10
10
|
* resolution and constructs UUID-based paths directly.
|
|
11
11
|
*
|
|
12
|
-
* Allowlist: only app-store.ts itself
|
|
13
|
-
* for the fs.watch root, not for per-app paths), and workspace migrations
|
|
12
|
+
* Allowlist: only app-store.ts itself and workspace migrations
|
|
14
13
|
* (self-contained, don't import from app-store).
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
/** Files that are permitted to import getAppsDir. */
|
|
18
17
|
const ALLOWLIST = new Set([
|
|
19
18
|
"assistant/src/apps/app-store.ts", // defines getAppsDir
|
|
20
|
-
"assistant/src/daemon/app-source-watcher.ts", // uses getAppsDir for recursive fs.watch root, not per-app paths
|
|
21
19
|
"assistant/src/tools/filesystem/write.ts", // uses getAppsDir as an exemption root for the artifact-HTML guard, not per-app paths
|
|
22
20
|
]);
|
|
23
21
|
|
|
@@ -125,7 +125,10 @@ describe("AssistantEventHub — machineName", () => {
|
|
|
125
125
|
});
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
-
describe(
|
|
128
|
+
describe.each([
|
|
129
|
+
["x-vellum-cu-window-capture", "host_cu_window_capture"],
|
|
130
|
+
["x-vellum-cu-sequence", "host_cu_sequence"],
|
|
131
|
+
] as const)("%s negotiation", (header, capability) => {
|
|
129
132
|
test.each([
|
|
130
133
|
["macos", undefined, false],
|
|
131
134
|
["macos", "0", false],
|
|
@@ -145,18 +148,14 @@ describe("window capture negotiation", () => {
|
|
|
145
148
|
headers: {
|
|
146
149
|
"x-vellum-client-id": "client-1",
|
|
147
150
|
"x-vellum-interface-id": interfaceId,
|
|
148
|
-
...(advertised
|
|
149
|
-
? { "x-vellum-cu-window-capture": advertised }
|
|
150
|
-
: {}),
|
|
151
|
+
...(advertised ? { [header]: advertised } : {}),
|
|
151
152
|
},
|
|
152
153
|
abortSignal: ac.signal,
|
|
153
154
|
},
|
|
154
155
|
{ hub },
|
|
155
156
|
);
|
|
156
157
|
expect(
|
|
157
|
-
hub
|
|
158
|
-
.getClientById("client-1")
|
|
159
|
-
?.capabilities.includes("host_cu_window_capture"),
|
|
158
|
+
hub.getClientById("client-1")?.capabilities.includes(capability),
|
|
160
159
|
).toBe(supported);
|
|
161
160
|
} finally {
|
|
162
161
|
ac.abort();
|
|
@@ -690,3 +690,47 @@ test("window requests never reach an older overlapping connection with the same
|
|
|
690
690
|
supported.dispose();
|
|
691
691
|
}
|
|
692
692
|
});
|
|
693
|
+
|
|
694
|
+
test("batched action requests never reach an older overlapping connection with the same device ID", async () => {
|
|
695
|
+
const receivedLegacy: unknown[] = [];
|
|
696
|
+
const receivedSupported: unknown[] = [];
|
|
697
|
+
const legacy = assistantEventHub.subscribe({
|
|
698
|
+
type: "client",
|
|
699
|
+
clientId: "sequence-client",
|
|
700
|
+
interfaceId: "macos",
|
|
701
|
+
capabilities: ["host_cu"],
|
|
702
|
+
callback: (event) => {
|
|
703
|
+
receivedLegacy.push(event);
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
const supported = assistantEventHub.subscribe({
|
|
707
|
+
type: "client",
|
|
708
|
+
clientId: "sequence-client",
|
|
709
|
+
interfaceId: "macos",
|
|
710
|
+
capabilities: ["host_cu", "host_cu_sequence"],
|
|
711
|
+
callback: (event) => {
|
|
712
|
+
receivedSupported.push(event);
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
try {
|
|
716
|
+
broadcastMessage(
|
|
717
|
+
{
|
|
718
|
+
type: "host_cu_request",
|
|
719
|
+
requestId: "sequence-request",
|
|
720
|
+
conversationId: "sequence-session",
|
|
721
|
+
toolName: "computer_use_sequence",
|
|
722
|
+
input: { actions: [{ action: "key", key: "cmd+n" }] },
|
|
723
|
+
stepNumber: 1,
|
|
724
|
+
targetClientId: "sequence-client",
|
|
725
|
+
},
|
|
726
|
+
"sequence-session",
|
|
727
|
+
{ targetClientId: "sequence-client" },
|
|
728
|
+
);
|
|
729
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
730
|
+
expect(receivedLegacy).toHaveLength(0);
|
|
731
|
+
expect(receivedSupported).toHaveLength(1);
|
|
732
|
+
} finally {
|
|
733
|
+
legacy.dispose();
|
|
734
|
+
supported.dispose();
|
|
735
|
+
}
|
|
736
|
+
});
|
|
@@ -29,7 +29,7 @@ const manifestPath = resolve(
|
|
|
29
29
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
30
30
|
|
|
31
31
|
describe("computer-use skill manifest regression", () => {
|
|
32
|
-
test("manifest has exactly
|
|
32
|
+
test("manifest has exactly 12 tools", () => {
|
|
33
33
|
expect(manifest.tools).toHaveLength(COMPUTER_USE_TOOL_COUNT);
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -13,8 +13,11 @@ describe("forwardComputerUseProxyTool", () => {
|
|
|
13
13
|
);
|
|
14
14
|
|
|
15
15
|
expect(result.isError).toBe(true);
|
|
16
|
-
expect(result.content).toContain(
|
|
17
|
-
|
|
16
|
+
expect(result.content).toContain(
|
|
17
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
18
|
+
);
|
|
19
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
20
|
+
expect(result.content).not.toContain("computer_use_click");
|
|
18
21
|
});
|
|
19
22
|
|
|
20
23
|
test("forwards to proxyToolResolver when present", async () => {
|
|
@@ -37,8 +37,8 @@ const ctx: ToolContext = {
|
|
|
37
37
|
// ── Tool definitions ────────────────────────────────────────────────
|
|
38
38
|
|
|
39
39
|
describe("computer-use tool definitions", () => {
|
|
40
|
-
test("allComputerUseTools contains
|
|
41
|
-
expect(allComputerUseTools.length).toBe(
|
|
40
|
+
test("allComputerUseTools contains 12 tools", () => {
|
|
41
|
+
expect(allComputerUseTools.length).toBe(12);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
test("all tools belong to computer-use category", () => {
|
|
@@ -104,7 +104,10 @@ describe("computer_use_click (unified)", () => {
|
|
|
104
104
|
test("execute returns isError when no proxy resolver is configured", async () => {
|
|
105
105
|
const result = await computerUseClickTool.execute({}, ctx);
|
|
106
106
|
expect(result.isError).toBe(true);
|
|
107
|
-
expect(result.content).
|
|
107
|
+
expect(result.content).toContain(
|
|
108
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
109
|
+
);
|
|
110
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
108
111
|
});
|
|
109
112
|
});
|
|
110
113
|
|
|
@@ -119,7 +122,10 @@ describe("computer_use_type_text", () => {
|
|
|
119
122
|
test("execute returns isError when no proxy resolver is configured", async () => {
|
|
120
123
|
const result = await computerUseTypeTextTool.execute({}, ctx);
|
|
121
124
|
expect(result.isError).toBe(true);
|
|
122
|
-
expect(result.content).
|
|
125
|
+
expect(result.content).toContain(
|
|
126
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
127
|
+
);
|
|
128
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
123
129
|
});
|
|
124
130
|
});
|
|
125
131
|
|
|
@@ -134,7 +140,10 @@ describe("computer_use_key", () => {
|
|
|
134
140
|
test("execute returns isError when no proxy resolver is configured", async () => {
|
|
135
141
|
const result = await computerUseKeyTool.execute({}, ctx);
|
|
136
142
|
expect(result.isError).toBe(true);
|
|
137
|
-
expect(result.content).
|
|
143
|
+
expect(result.content).toContain(
|
|
144
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
145
|
+
);
|
|
146
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
138
147
|
});
|
|
139
148
|
});
|
|
140
149
|
|
|
@@ -241,8 +250,11 @@ describe("forwardComputerUseProxyTool", () => {
|
|
|
241
250
|
);
|
|
242
251
|
|
|
243
252
|
expect(result.isError).toBe(true);
|
|
244
|
-
expect(result.content).toContain(
|
|
245
|
-
|
|
253
|
+
expect(result.content).toContain(
|
|
254
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
255
|
+
);
|
|
256
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
257
|
+
expect(result.content).not.toContain("computer_use_click");
|
|
246
258
|
});
|
|
247
259
|
|
|
248
260
|
test("delegates to proxy resolver when available", async () => {
|
|
@@ -130,7 +130,7 @@ describe("getSchemaAtPath", () => {
|
|
|
130
130
|
expect(properties).toBeDefined();
|
|
131
131
|
expect(properties.enabled).toBeDefined();
|
|
132
132
|
expect(properties.voice).toBeDefined();
|
|
133
|
-
expect(properties.
|
|
133
|
+
expect(properties.disclosure).toBeDefined();
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
test("navigates through .transform() wrappers (ingress → object schema)", () => {
|
|
@@ -230,7 +230,7 @@ describe("z.toJSONSchema integration", () => {
|
|
|
230
230
|
expect(properties).toBeDefined();
|
|
231
231
|
expect(properties!.enabled).toBeDefined();
|
|
232
232
|
expect(properties!.voice).toBeDefined();
|
|
233
|
-
expect(properties!.
|
|
233
|
+
expect(properties!.disclosure).toBeDefined();
|
|
234
234
|
});
|
|
235
235
|
|
|
236
236
|
test("sub-schema at a leaf like llm.profileSession.defaultTtlSeconds produces integer schema", () => {
|
|
@@ -952,11 +952,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
952
952
|
enabled: true,
|
|
953
953
|
text: 'At the very beginning of the call, introduce yourself as an assistant calling on behalf of the person you represent. Do not say "AI assistant".',
|
|
954
954
|
},
|
|
955
|
-
safety: {
|
|
956
|
-
denyCategories: [],
|
|
957
|
-
},
|
|
958
955
|
voice: {
|
|
959
|
-
interruptSensitivity: "low",
|
|
960
956
|
telephonyStreaming: true,
|
|
961
957
|
utteranceEndMs: 1000,
|
|
962
958
|
},
|
|
@@ -978,7 +974,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
978
974
|
maxDurationSeconds: 1800,
|
|
979
975
|
userConsultTimeoutSeconds: 60,
|
|
980
976
|
disclosure: { enabled: false, text: "Custom disclosure" },
|
|
981
|
-
safety: { denyCategories: ["spam"] },
|
|
982
977
|
},
|
|
983
978
|
});
|
|
984
979
|
expect(result.calls.enabled).toBe(false);
|
|
@@ -986,7 +981,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
986
981
|
expect(result.calls.userConsultTimeoutSeconds).toBe(60);
|
|
987
982
|
expect(result.calls.disclosure.enabled).toBe(false);
|
|
988
983
|
expect(result.calls.disclosure.text).toBe("Custom disclosure");
|
|
989
|
-
expect(result.calls.safety.denyCategories).toEqual(["spam"]);
|
|
990
984
|
});
|
|
991
985
|
|
|
992
986
|
// ── Live voice config ───────────────────────────────────────────────
|
|
@@ -994,7 +988,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
994
988
|
test("applies liveVoice defaults", () => {
|
|
995
989
|
const result = AssistantConfigSchema.parse({});
|
|
996
990
|
expect(result.liveVoice).toEqual({
|
|
997
|
-
mode: "open-mic",
|
|
998
991
|
vad: {
|
|
999
992
|
speechEnergyThreshold: 800,
|
|
1000
993
|
noiseFloorMargin: 3,
|
|
@@ -1005,6 +998,18 @@ describe("AssistantConfigSchema", () => {
|
|
|
1005
998
|
echoEmaHalfLifeMs: 400,
|
|
1006
999
|
echoDrainSlackMs: 300,
|
|
1007
1000
|
},
|
|
1001
|
+
flux: {
|
|
1002
|
+
turnEnd: { enabled: true },
|
|
1003
|
+
eotThreshold: 0.7,
|
|
1004
|
+
eotTimeoutMs: 5000,
|
|
1005
|
+
},
|
|
1006
|
+
archiveAudio: false,
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
test("applies voice defaults", () => {
|
|
1011
|
+
const result = AssistantConfigSchema.parse({});
|
|
1012
|
+
expect(result.voice).toEqual({
|
|
1008
1013
|
frontModel: {
|
|
1009
1014
|
endpointDecisionTimeoutMs: 1200,
|
|
1010
1015
|
endpointExtensionMs: 1500,
|
|
@@ -1019,35 +1024,24 @@ describe("AssistantConfigSchema", () => {
|
|
|
1019
1024
|
generationTimeoutMs: 1500,
|
|
1020
1025
|
},
|
|
1021
1026
|
},
|
|
1022
|
-
flux: {
|
|
1023
|
-
turnEnd: { enabled: true },
|
|
1024
|
-
eotThreshold: 0.7,
|
|
1025
|
-
eotTimeoutMs: 5000,
|
|
1026
|
-
},
|
|
1027
|
-
maxSessionDurationSeconds: 1800,
|
|
1028
|
-
archiveAudio: false,
|
|
1029
1027
|
});
|
|
1030
1028
|
});
|
|
1031
1029
|
|
|
1032
1030
|
test("accepts valid liveVoice config overrides", () => {
|
|
1033
1031
|
const result = AssistantConfigSchema.parse({
|
|
1034
1032
|
liveVoice: {
|
|
1035
|
-
mode: "ptt",
|
|
1036
1033
|
vad: {
|
|
1037
1034
|
speechEnergyThreshold: 1500,
|
|
1038
1035
|
silenceThresholdMs: 1000,
|
|
1039
1036
|
bargeInMinSpeechMs: 120,
|
|
1040
1037
|
},
|
|
1041
|
-
maxSessionDurationSeconds: 900,
|
|
1042
1038
|
},
|
|
1043
1039
|
});
|
|
1044
|
-
expect(result.liveVoice.mode).toBe("ptt");
|
|
1045
1040
|
expect(result.liveVoice.vad.speechEnergyThreshold).toBe(1500);
|
|
1046
1041
|
expect(result.liveVoice.vad.silenceThresholdMs).toBe(1000);
|
|
1047
1042
|
expect(result.liveVoice.vad.bargeInMinSpeechMs).toBe(120);
|
|
1048
1043
|
// Unspecified vad fields still get defaults
|
|
1049
1044
|
expect(result.liveVoice.vad.maxTurnDurationMs).toBe(30000);
|
|
1050
|
-
expect(result.liveVoice.maxSessionDurationSeconds).toBe(900);
|
|
1051
1045
|
// A partial liveVoice override leaves Flux turn-end disabled by default.
|
|
1052
1046
|
expect(result.liveVoice.flux.turnEnd.enabled).toBe(true);
|
|
1053
1047
|
});
|
|
@@ -1136,18 +1130,10 @@ describe("AssistantConfigSchema", () => {
|
|
|
1136
1130
|
expect(result.success).toBe(false);
|
|
1137
1131
|
});
|
|
1138
1132
|
|
|
1139
|
-
test("rejects non-array calls.safety.denyCategories", () => {
|
|
1140
|
-
const result = AssistantConfigSchema.safeParse({
|
|
1141
|
-
calls: { safety: { denyCategories: "spam" } },
|
|
1142
|
-
});
|
|
1143
|
-
expect(result.success).toBe(false);
|
|
1144
|
-
});
|
|
1145
|
-
|
|
1146
1133
|
// ── Calls voice config ──────────────────────────────────────────────
|
|
1147
1134
|
|
|
1148
1135
|
test("config without calls.voice parses correctly and produces defaults", () => {
|
|
1149
1136
|
const result = AssistantConfigSchema.parse({});
|
|
1150
|
-
expect(result.calls.voice.interruptSensitivity).toBe("low");
|
|
1151
1137
|
expect(result.calls.voice.telephonyStreaming).toBe(true);
|
|
1152
1138
|
expect(result.calls.voice.utteranceEndMs).toBe(1000);
|
|
1153
1139
|
});
|
|
@@ -1165,9 +1151,23 @@ describe("AssistantConfigSchema", () => {
|
|
|
1165
1151
|
expect(result.calls.voice.utteranceEndMs).toBe(2500);
|
|
1166
1152
|
});
|
|
1167
1153
|
|
|
1154
|
+
test("persisted calls configs carrying retired keys keep parsing", () => {
|
|
1155
|
+
// Zod strips unrecognized keys, so a persisted config that still carries
|
|
1156
|
+
// `calls.safety.denyCategories` or `calls.voice.interruptSensitivity`
|
|
1157
|
+
// parses.
|
|
1158
|
+
const result = AssistantConfigSchema.parse({
|
|
1159
|
+
calls: {
|
|
1160
|
+
safety: { denyCategories: ["spam"] },
|
|
1161
|
+
voice: { interruptSensitivity: "high" },
|
|
1162
|
+
},
|
|
1163
|
+
});
|
|
1164
|
+
expect(result.calls).not.toHaveProperty("safety");
|
|
1165
|
+
expect(result.calls.voice).not.toHaveProperty("interruptSensitivity");
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
1168
|
test("language is no longer part of the voice config schema", () => {
|
|
1169
|
-
//
|
|
1170
|
-
//
|
|
1169
|
+
// Zod strips the unrecognized key, so persisted configs that still carry
|
|
1170
|
+
// it parse.
|
|
1171
1171
|
const result = AssistantConfigSchema.parse({
|
|
1172
1172
|
calls: { voice: { language: "es-ES" } },
|
|
1173
1173
|
});
|
|
@@ -2633,7 +2633,6 @@ describe("loadConfig with schema validation", () => {
|
|
|
2633
2633
|
expect(config.calls.maxDurationSeconds).toBe(3600);
|
|
2634
2634
|
expect(config.calls.userConsultTimeoutSeconds).toBe(120);
|
|
2635
2635
|
expect(config.calls.disclosure.enabled).toBe(true);
|
|
2636
|
-
expect(config.calls.safety.denyCategories).toEqual([]);
|
|
2637
2636
|
expect(
|
|
2638
2637
|
(config.calls.voice as Record<string, unknown>).language,
|
|
2639
2638
|
).toBeUndefined();
|
|
@@ -113,8 +113,11 @@ mock.module("../providers/registry.js", () => ({
|
|
|
113
113
|
resolveProviderFromConnection: async () => null,
|
|
114
114
|
}));
|
|
115
115
|
|
|
116
|
+
let mcpReloadCallCount = 0;
|
|
116
117
|
mock.module("../daemon/mcp-reload-service.js", () => ({
|
|
117
|
-
reloadMcpServers: async () => {
|
|
118
|
+
reloadMcpServers: async () => {
|
|
119
|
+
mcpReloadCallCount += 1;
|
|
120
|
+
},
|
|
118
121
|
}));
|
|
119
122
|
|
|
120
123
|
mock.module("../signals/conversation-undo.js", () => ({
|
|
@@ -154,10 +157,6 @@ mock.module("../runtime/sync/resource-sync-events.js", () => ({
|
|
|
154
157
|
},
|
|
155
158
|
}));
|
|
156
159
|
|
|
157
|
-
mock.module("../daemon/skill-memory-refresh.js", () => ({
|
|
158
|
-
refreshSkillCapabilityMemories: () => {},
|
|
159
|
-
}));
|
|
160
|
-
|
|
161
160
|
mock.module("../platform/sync-identity.js", () => ({
|
|
162
161
|
syncIdentityNameToPlatform: () => {},
|
|
163
162
|
}));
|
|
@@ -183,6 +182,7 @@ function findFileWatch(filePath: string): CapturedFileWatch | undefined {
|
|
|
183
182
|
|
|
184
183
|
const WORKSPACE_FILES = new Set([
|
|
185
184
|
"config.json",
|
|
185
|
+
"mcp.json",
|
|
186
186
|
"SOUL.md",
|
|
187
187
|
"IDENTITY.md",
|
|
188
188
|
"ui/theme.json",
|
|
@@ -239,6 +239,7 @@ beforeEach(() => {
|
|
|
239
239
|
evictCallCount = 0;
|
|
240
240
|
identityCallCount = 0;
|
|
241
241
|
themeCallCount = 0;
|
|
242
|
+
mcpReloadCallCount = 0;
|
|
242
243
|
watcher = new ConfigWatcher(undefined, TEST_DEBOUNCE_MS);
|
|
243
244
|
});
|
|
244
245
|
|
|
@@ -270,9 +271,9 @@ describe("ConfigWatcher workspace file handlers", () => {
|
|
|
270
271
|
|
|
271
272
|
test("unregistered workspace files are not subscribed (only the registered handler set is)", () => {
|
|
272
273
|
watcher.start();
|
|
273
|
-
// Per-file watching only registers config.json,
|
|
274
|
-
// and ui/theme.json. The whole workspace dir must not
|
|
275
|
-
// either
|
|
274
|
+
// Per-file watching only registers config.json, mcp.json, SOUL.md,
|
|
275
|
+
// IDENTITY.md, and ui/theme.json. The whole workspace dir must not
|
|
276
|
+
// be watched either: that was the ENXIO-on-Unix-sockets bug.
|
|
276
277
|
expect(findFileWatch(join(WORKSPACE_DIR, "OTHER.md"))).toBeUndefined();
|
|
277
278
|
expect(findWatcher(WORKSPACE_DIR)).toBeUndefined();
|
|
278
279
|
});
|
|
@@ -285,6 +286,14 @@ describe("ConfigWatcher workspace file handlers", () => {
|
|
|
285
286
|
expect(evictCallCount).toBe(0);
|
|
286
287
|
});
|
|
287
288
|
|
|
289
|
+
test("mcp.json change reloads MCP servers without evicting conversations", async () => {
|
|
290
|
+
watcher.start();
|
|
291
|
+
simulateFileChange(WORKSPACE_DIR, "mcp.json");
|
|
292
|
+
await new Promise((r) => setTimeout(r, WAIT_MS));
|
|
293
|
+
expect(mcpReloadCallCount).toBe(1);
|
|
294
|
+
expect(evictCallCount).toBe(0);
|
|
295
|
+
});
|
|
296
|
+
|
|
288
297
|
test("config.json change calls refreshConfigFromSources", async () => {
|
|
289
298
|
let refreshCalled = false;
|
|
290
299
|
watcher.refreshConfigFromSources = async () => {
|
|
@@ -332,6 +341,12 @@ describe("ConfigWatcher watcher lifecycle", () => {
|
|
|
332
341
|
expect(findFileWatch(join(WORKSPACE_DIR, "IDENTITY.md"))).toBeDefined();
|
|
333
342
|
});
|
|
334
343
|
|
|
344
|
+
test("start does not watch the skills directory", () => {
|
|
345
|
+
mkdirSync(join(WORKSPACE_DIR, "skills"), { recursive: true });
|
|
346
|
+
watcher.start();
|
|
347
|
+
expect(findWatcher(join(WORKSPACE_DIR, "skills"))).toBeUndefined();
|
|
348
|
+
});
|
|
349
|
+
|
|
335
350
|
test("stop cancels pending debounce work, no eviction fires after", async () => {
|
|
336
351
|
watcher.start();
|
|
337
352
|
simulateFileChange(WORKSPACE_DIR, "SOUL.md");
|
|
@@ -19,7 +19,7 @@ describe("isConnectionCompatibleWithModel", () => {
|
|
|
19
19
|
expect(
|
|
20
20
|
isConnectionCompatibleWithModel(
|
|
21
21
|
{ auth: { type: "oauth_subscription" } as never },
|
|
22
|
-
"gpt-5.
|
|
22
|
+
"gpt-5.5",
|
|
23
23
|
),
|
|
24
24
|
).toBe(true);
|
|
25
25
|
});
|
|
@@ -79,7 +79,7 @@ describe("describeSubscriptionModelIncompatibility", () => {
|
|
|
79
79
|
|
|
80
80
|
test("returns null when model is Codex-compatible", () => {
|
|
81
81
|
expect(
|
|
82
|
-
describeSubscriptionModelIncompatibility([subscriptionConn], "gpt-5.
|
|
82
|
+
describeSubscriptionModelIncompatibility([subscriptionConn], "gpt-5.5"),
|
|
83
83
|
).toBeNull();
|
|
84
84
|
});
|
|
85
85
|
});
|
|
@@ -59,7 +59,7 @@ describe("conversation-key-store disk view", () => {
|
|
|
59
59
|
|
|
60
60
|
const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
61
61
|
expect(meta.id).toBe(first.conversationId);
|
|
62
|
-
expect(meta.title).toBe("
|
|
62
|
+
expect(meta.title).toBe("conversation.title.generating");
|
|
63
63
|
expect(meta.type).toBe("standard");
|
|
64
64
|
expect(meta.channel).toBeNull();
|
|
65
65
|
expect(readdirSync(conversationsDir)).toEqual([expectedDirName]);
|
|
@@ -901,6 +901,32 @@ describe("pairDeliveryWithConversation", () => {
|
|
|
901
901
|
// body has to land there for that button to open something that contains
|
|
902
902
|
// the notification the user tapped.
|
|
903
903
|
|
|
904
|
+
test("assistant reply keeps the source conversation without appending its preview", async () => {
|
|
905
|
+
mockExistingConversations["conv-user-chat"] = {
|
|
906
|
+
id: "conv-user-chat",
|
|
907
|
+
source: "user",
|
|
908
|
+
title: "An ordinary chat",
|
|
909
|
+
};
|
|
910
|
+
const signal = makeSignal({
|
|
911
|
+
requiresConversation: undefined,
|
|
912
|
+
sourceContextId: "conv-user-chat",
|
|
913
|
+
sourceEventName: "chat.assistant_reply",
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
const result = await pairDeliveryWithConversation(
|
|
917
|
+
signal,
|
|
918
|
+
"vellum" as NotificationChannel,
|
|
919
|
+
makeCopy({ body: "A short push preview…" }),
|
|
920
|
+
);
|
|
921
|
+
|
|
922
|
+
expect(result.conversationId).toBe("conv-user-chat");
|
|
923
|
+
expect(result.messageId).toBeNull();
|
|
924
|
+
expect(result.createdNewConversation).toBe(false);
|
|
925
|
+
expect(createConversationMock).not.toHaveBeenCalled();
|
|
926
|
+
expect(addMessageMock).not.toHaveBeenCalled();
|
|
927
|
+
expect(messagesInvalidated).toEqual([]);
|
|
928
|
+
});
|
|
929
|
+
|
|
904
930
|
test("passive vellum signal appends the body to the producing conversation", async () => {
|
|
905
931
|
mockExistingConversations["conv-producer"] = {
|
|
906
932
|
id: "conv-producer",
|