@vellumai/assistant 0.12.1 → 0.12.2-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
- 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-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- 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 +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- 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 +596 -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 +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -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__/secret-routes-acp-guard.test.ts +59 -1
- 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 +70 -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__/ui-channel-variants.test.ts +1 -56
- 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__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -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__/claude-token-refresh.test.ts +257 -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__/prepare-agent-env.test.ts +60 -1
- 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/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- 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/claude-token-refresh.ts +150 -0
- 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/prepare-agent-env.ts +21 -6
- 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 +120 -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 +56 -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 +155 -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 +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- 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/oauth/status.ts +5 -0
- 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 +29 -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 +33 -13
- package/src/config/loader.ts +2 -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 +18 -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 +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- 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 +224 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -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__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- 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 +149 -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 +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -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-claude-auth-routes.ts +11 -3
- 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 +90 -2
- 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 +20 -14
- 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 +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- 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-retro.ts +6 -8
- 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
|
@@ -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",
|
|
@@ -161,12 +161,6 @@ function makeFakeContext(opts: {
|
|
|
161
161
|
trustClass: "guardian" as const,
|
|
162
162
|
guardianPrincipalId: "user-1",
|
|
163
163
|
},
|
|
164
|
-
setTrustContext(
|
|
165
|
-
this: { trustContext?: TrustContext },
|
|
166
|
-
trustContext: TrustContext | null,
|
|
167
|
-
) {
|
|
168
|
-
this.trustContext = trustContext ?? undefined;
|
|
169
|
-
},
|
|
170
164
|
setTransportHints() {},
|
|
171
165
|
applyHostEnvFromTransport() {},
|
|
172
166
|
ensureHostProxiesForTurn() {},
|
|
@@ -415,10 +409,10 @@ describe("drainQueue preactivation re-add for host-proxy interfaces", () => {
|
|
|
415
409
|
"U-contact",
|
|
416
410
|
);
|
|
417
411
|
expect(ctx.currentTurnTrustContext?.sourceChannel).toBe("slack");
|
|
418
|
-
// The
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
expect(ctx.trustContext).toBe(
|
|
412
|
+
// The slot itself is left alone; only the turn's view is corrected. A
|
|
413
|
+
// drain stamping it would attribute the conversation to a sender whose
|
|
414
|
+
// turn can still lose the processing lock at persist time.
|
|
415
|
+
expect(ctx.trustContext?.trustClass).toBe("guardian");
|
|
422
416
|
});
|
|
423
417
|
|
|
424
418
|
test("buildPassthroughBatch refuses to coalesce two channel senders", async () => {
|
|
@@ -450,17 +450,6 @@ async function waitForCondition(
|
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
-
/** Count the history reloads a conversation performs from this point on. */
|
|
454
|
-
function countHistoryReloads(conversation: Conversation): () => number {
|
|
455
|
-
const originalLoad = conversation.loadFromDb.bind(conversation);
|
|
456
|
-
let reloads = 0;
|
|
457
|
-
conversation.loadFromDb = async () => {
|
|
458
|
-
reloads++;
|
|
459
|
-
return originalLoad();
|
|
460
|
-
};
|
|
461
|
-
return () => reloads;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
453
|
/**
|
|
465
454
|
* Resolve the Nth pending AgentLoop.run() call. Fires the minimal events
|
|
466
455
|
* that `runAgentLoop` expects (usage + message_complete) so the conversation
|
|
@@ -726,22 +715,21 @@ describe("Conversation message queue", () => {
|
|
|
726
715
|
await new Promise((r) => setTimeout(r, 10));
|
|
727
716
|
});
|
|
728
717
|
|
|
729
|
-
test("
|
|
730
|
-
//
|
|
731
|
-
//
|
|
732
|
-
//
|
|
733
|
-
//
|
|
734
|
-
//
|
|
735
|
-
// so a drain that stamped only the snapshot rendered the guardian's own
|
|
736
|
-
// web turn as a trusted contact's and hid the guardian's rows from it.
|
|
718
|
+
test("the turn-context actor section describes the turn's actor, not the conversation's resting actor", async () => {
|
|
719
|
+
// The drain carries its sender on the per-turn field and leaves the
|
|
720
|
+
// resting slot naming the previous actor. The actor section is frozen at
|
|
721
|
+
// turn start, so it has to read the turn's actor: reading the slot is what
|
|
722
|
+
// told the guardian's own drained turn it was talking to a trusted
|
|
723
|
+
// contact.
|
|
737
724
|
const conversation = makeConversation();
|
|
738
725
|
await conversation.loadFromDb();
|
|
739
726
|
|
|
740
|
-
|
|
741
|
-
trustClass: "trusted_contact",
|
|
742
|
-
sourceChannel: "slack",
|
|
727
|
+
const contact = {
|
|
728
|
+
trustClass: "trusted_contact" as const,
|
|
729
|
+
sourceChannel: "slack" as const,
|
|
743
730
|
requesterExternalUserId: "U-contact",
|
|
744
|
-
}
|
|
731
|
+
};
|
|
732
|
+
conversation.setTrustContext(contact);
|
|
745
733
|
|
|
746
734
|
const p1 = conversation.processMessage({
|
|
747
735
|
content: "msg-1",
|
|
@@ -756,7 +744,6 @@ describe("Conversation message queue", () => {
|
|
|
756
744
|
sourceChannel: "vellum" as const,
|
|
757
745
|
requesterExternalUserId: "guardian-principal",
|
|
758
746
|
};
|
|
759
|
-
const reloads = countHistoryReloads(conversation);
|
|
760
747
|
conversation.enqueueMessage({
|
|
761
748
|
content: "msg-2",
|
|
762
749
|
requestId: "req-2",
|
|
@@ -767,83 +754,30 @@ describe("Conversation message queue", () => {
|
|
|
767
754
|
await p1;
|
|
768
755
|
await waitForPendingRun(2);
|
|
769
756
|
|
|
770
|
-
//
|
|
771
|
-
//
|
|
772
|
-
|
|
773
|
-
expect(conversation.getTrustContext()).toBe(guardian);
|
|
757
|
+
// Guard the test itself: the slot must disagree with the turn, or the
|
|
758
|
+
// assertion below passes for the wrong reason.
|
|
759
|
+
expect(conversation.getTrustContext()).toBe(contact);
|
|
774
760
|
expect(conversation.currentTurnTrustContext).toBe(guardian);
|
|
761
|
+
// A guardian turn renders no actor section, whatever the slot says.
|
|
775
762
|
expect(conversation.currentTurnInboundActorContext).toBeNull();
|
|
776
|
-
expect(reloads()).toBe(1);
|
|
777
763
|
|
|
778
764
|
await resolveRun(1);
|
|
779
765
|
await new Promise((r) => setTimeout(r, 10));
|
|
780
766
|
});
|
|
781
767
|
|
|
782
|
-
test("
|
|
783
|
-
//
|
|
784
|
-
//
|
|
768
|
+
test("the actor section names the drained contact while the slot holds the guardian", async () => {
|
|
769
|
+
// The inverse direction, so the assertion above cannot pass merely because
|
|
770
|
+
// the section is always absent: a contact's message drained behind the
|
|
771
|
+
// guardian's turn must be described as that contact.
|
|
785
772
|
const conversation = makeConversation();
|
|
786
773
|
await conversation.loadFromDb();
|
|
787
774
|
|
|
788
|
-
conversation.setTrustContext({
|
|
789
|
-
trustClass: "trusted_contact",
|
|
790
|
-
sourceChannel: "slack",
|
|
791
|
-
requesterExternalUserId: "U-contact",
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
const p1 = conversation.processMessage({
|
|
795
|
-
content: "msg-1",
|
|
796
|
-
attachments: [],
|
|
797
|
-
onEvent: () => {},
|
|
798
|
-
requestId: "req-1",
|
|
799
|
-
});
|
|
800
|
-
await waitForPendingRun(1);
|
|
801
|
-
|
|
802
775
|
const guardian = {
|
|
803
776
|
trustClass: "guardian" as const,
|
|
804
777
|
sourceChannel: "vellum" as const,
|
|
805
778
|
requesterExternalUserId: "guardian-principal",
|
|
806
779
|
};
|
|
807
|
-
|
|
808
|
-
conversation.enqueueMessage({
|
|
809
|
-
content: "msg-2",
|
|
810
|
-
requestId: "req-2",
|
|
811
|
-
trustContext: guardian,
|
|
812
|
-
});
|
|
813
|
-
conversation.enqueueMessage({
|
|
814
|
-
content: "msg-3",
|
|
815
|
-
requestId: "req-3",
|
|
816
|
-
trustContext: guardian,
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
await resolveRun(0);
|
|
820
|
-
await p1;
|
|
821
|
-
await waitForPendingRun(2);
|
|
822
|
-
|
|
823
|
-
// One batched run for both siblings, committed as the guardian.
|
|
824
|
-
expect(pendingRuns.length).toBe(2);
|
|
825
|
-
expect(conversation.getTrustContext()).toBe(guardian);
|
|
826
|
-
expect(conversation.currentTurnTrustContext).toBe(guardian);
|
|
827
|
-
expect(conversation.currentTurnInboundActorContext).toBeNull();
|
|
828
|
-
expect(reloads()).toBe(1);
|
|
829
|
-
|
|
830
|
-
await resolveRun(1);
|
|
831
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
test("a drained turn keeps its sender when the slot moves during the history reload", async () => {
|
|
835
|
-
// The commit captures the sender before the reload awaits. A writer that
|
|
836
|
-
// moves the slot inside that await (a wake's stamp, a pointer elevation)
|
|
837
|
-
// must not become the turn's actor: the history was reloaded for the
|
|
838
|
-
// sender, and running someone else's trust over it is the escalation.
|
|
839
|
-
const conversation = makeConversation();
|
|
840
|
-
await conversation.loadFromDb();
|
|
841
|
-
|
|
842
|
-
conversation.setTrustContext({
|
|
843
|
-
trustClass: "trusted_contact",
|
|
844
|
-
sourceChannel: "slack",
|
|
845
|
-
requesterExternalUserId: "U-contact",
|
|
846
|
-
});
|
|
780
|
+
conversation.setTrustContext(guardian);
|
|
847
781
|
|
|
848
782
|
const p1 = conversation.processMessage({
|
|
849
783
|
content: "msg-1",
|
|
@@ -853,49 +787,39 @@ describe("Conversation message queue", () => {
|
|
|
853
787
|
});
|
|
854
788
|
await waitForPendingRun(1);
|
|
855
789
|
|
|
856
|
-
const
|
|
857
|
-
trustClass: "
|
|
858
|
-
sourceChannel: "
|
|
859
|
-
requesterExternalUserId: "
|
|
860
|
-
};
|
|
861
|
-
const intruder = {
|
|
862
|
-
trustClass: "unknown" as const,
|
|
863
|
-
sourceChannel: "telegram" as const,
|
|
864
|
-
requesterExternalUserId: "T-stranger",
|
|
790
|
+
const contact = {
|
|
791
|
+
trustClass: "trusted_contact" as const,
|
|
792
|
+
sourceChannel: "slack" as const,
|
|
793
|
+
requesterExternalUserId: "U-contact",
|
|
865
794
|
};
|
|
866
795
|
conversation.enqueueMessage({
|
|
867
796
|
content: "msg-2",
|
|
868
797
|
requestId: "req-2",
|
|
869
|
-
trustContext:
|
|
798
|
+
trustContext: contact,
|
|
870
799
|
});
|
|
871
800
|
|
|
872
|
-
// The drain's reload is the await the writer lands inside.
|
|
873
|
-
const originalLoad = conversation.loadFromDb.bind(conversation);
|
|
874
|
-
let movedDuringReload = false;
|
|
875
|
-
conversation.loadFromDb = async () => {
|
|
876
|
-
const result = await originalLoad();
|
|
877
|
-
conversation.setTrustContext(intruder);
|
|
878
|
-
movedDuringReload = true;
|
|
879
|
-
return result;
|
|
880
|
-
};
|
|
881
|
-
|
|
882
801
|
await resolveRun(0);
|
|
883
802
|
await p1;
|
|
884
803
|
await waitForPendingRun(2);
|
|
885
804
|
|
|
886
|
-
expect(
|
|
887
|
-
expect(conversation.
|
|
888
|
-
expect(conversation.
|
|
889
|
-
|
|
805
|
+
expect(conversation.getTrustContext()).toBe(guardian);
|
|
806
|
+
expect(conversation.currentTurnTrustContext).toBe(contact);
|
|
807
|
+
expect(conversation.currentTurnInboundActorContext).toMatchObject({
|
|
808
|
+
trustClass: "trusted_contact",
|
|
809
|
+
sourceChannel: "slack",
|
|
810
|
+
canonicalActorIdentity: "U-contact",
|
|
811
|
+
});
|
|
890
812
|
|
|
891
813
|
await resolveRun(1);
|
|
892
814
|
await new Promise((r) => setTimeout(r, 10));
|
|
893
815
|
});
|
|
894
816
|
|
|
895
|
-
test("a
|
|
896
|
-
//
|
|
897
|
-
//
|
|
898
|
-
//
|
|
817
|
+
test("a processMessage turn whose history reload fails puts the resting actor back", async () => {
|
|
818
|
+
// `processMessage` is the commitment point, so it stamps the slot before
|
|
819
|
+
// scoping history. A reload that throws starts no turn, and leaving the
|
|
820
|
+
// stamp would attribute the conversation to a sender that never ran: an
|
|
821
|
+
// actorless dispatch afterwards (a deferred wake) resolves the resting
|
|
822
|
+
// slot and would inherit it.
|
|
899
823
|
const conversation = makeConversation();
|
|
900
824
|
await conversation.loadFromDb();
|
|
901
825
|
|
|
@@ -906,110 +830,27 @@ describe("Conversation message queue", () => {
|
|
|
906
830
|
};
|
|
907
831
|
conversation.setTrustContext(contact);
|
|
908
832
|
|
|
909
|
-
const p1 = conversation.processMessage({
|
|
910
|
-
content: "msg-1",
|
|
911
|
-
attachments: [],
|
|
912
|
-
onEvent: () => {},
|
|
913
|
-
requestId: "req-1",
|
|
914
|
-
});
|
|
915
|
-
await waitForPendingRun(1);
|
|
916
|
-
|
|
917
|
-
const guardian = {
|
|
918
|
-
trustClass: "guardian" as const,
|
|
919
|
-
sourceChannel: "vellum" as const,
|
|
920
|
-
requesterExternalUserId: "guardian-principal",
|
|
921
|
-
};
|
|
922
|
-
conversation.enqueueMessage({
|
|
923
|
-
content: "msg-2",
|
|
924
|
-
requestId: "req-2",
|
|
925
|
-
trustContext: guardian,
|
|
926
|
-
});
|
|
927
|
-
|
|
928
|
-
let reloadAttempts = 0;
|
|
929
833
|
conversation.loadFromDb = async () => {
|
|
930
|
-
reloadAttempts++;
|
|
931
834
|
throw new Error("history store exploded");
|
|
932
835
|
};
|
|
933
836
|
|
|
934
|
-
// Finish the first turn; the drain (and its one retry) fail on the reload.
|
|
935
|
-
await resolveRun(0);
|
|
936
|
-
await p1;
|
|
937
|
-
await waitForCondition(() => reloadAttempts >= 2);
|
|
938
|
-
await new Promise((r) => setTimeout(r, 20));
|
|
939
|
-
|
|
940
|
-
expect(pendingRuns.length).toBe(1);
|
|
941
|
-
expect(conversation.getQueueDepth()).toBe(1);
|
|
942
|
-
expect(conversation.getTrustContext()).toBe(contact);
|
|
943
|
-
});
|
|
944
|
-
|
|
945
|
-
test("the turn-context actor section follows the turn's actor when the slot moves before the loop opens", async () => {
|
|
946
|
-
// The actor section is frozen at turn start from the turn's own actor,
|
|
947
|
-
// not from the resting slot. The drain stamps the slot and then awaits
|
|
948
|
-
// (persist) before the loop opens; an out-of-band writer landing in that
|
|
949
|
-
// window (pointer elevation restore, a wake's restore) moves the slot
|
|
950
|
-
// without owning the turn, and a read of the slot there would describe
|
|
951
|
-
// that writer's actor to the model.
|
|
952
|
-
const conversation = makeConversation();
|
|
953
|
-
await conversation.loadFromDb();
|
|
954
|
-
|
|
955
|
-
const contact = {
|
|
956
|
-
trustClass: "trusted_contact" as const,
|
|
957
|
-
sourceChannel: "slack" as const,
|
|
958
|
-
requesterExternalUserId: "U-contact",
|
|
959
|
-
};
|
|
960
|
-
conversation.setTrustContext(contact);
|
|
961
|
-
|
|
962
|
-
const p1 = conversation.processMessage({
|
|
963
|
-
content: "msg-1",
|
|
964
|
-
attachments: [],
|
|
965
|
-
onEvent: () => {},
|
|
966
|
-
requestId: "req-1",
|
|
967
|
-
});
|
|
968
|
-
await waitForPendingRun(1);
|
|
969
|
-
|
|
970
837
|
const guardian = {
|
|
971
838
|
trustClass: "guardian" as const,
|
|
972
839
|
sourceChannel: "vellum" as const,
|
|
973
840
|
requesterExternalUserId: "guardian-principal",
|
|
974
841
|
};
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
// drain's to carry into the run.
|
|
985
|
-
const originalPersist = conversation.persistUserMessage.bind(conversation);
|
|
986
|
-
let slotMoved = false;
|
|
987
|
-
(
|
|
988
|
-
conversation as unknown as {
|
|
989
|
-
persistUserMessage: typeof conversation.persistUserMessage;
|
|
990
|
-
}
|
|
991
|
-
).persistUserMessage = async (opts) => {
|
|
992
|
-
const result = await originalPersist(opts);
|
|
993
|
-
conversation.setTrustContext(contact);
|
|
994
|
-
slotMoved = true;
|
|
995
|
-
return result;
|
|
996
|
-
};
|
|
997
|
-
|
|
998
|
-
await resolveRun(0);
|
|
999
|
-
await p1;
|
|
1000
|
-
await waitForPendingRun(2);
|
|
842
|
+
await expect(
|
|
843
|
+
conversation.processMessage({
|
|
844
|
+
content: "msg-1",
|
|
845
|
+
attachments: [],
|
|
846
|
+
onEvent: () => {},
|
|
847
|
+
requestId: "req-1",
|
|
848
|
+
trustContext: guardian,
|
|
849
|
+
}),
|
|
850
|
+
).rejects.toThrow("history store exploded");
|
|
1001
851
|
|
|
1002
|
-
// Guard the test itself: the injection must have run, and the slot must
|
|
1003
|
-
// disagree with the turn, or the assertion below passes for the wrong
|
|
1004
|
-
// reason.
|
|
1005
|
-
expect(slotMoved).toBe(true);
|
|
1006
852
|
expect(conversation.getTrustContext()).toBe(contact);
|
|
1007
|
-
expect(
|
|
1008
|
-
// The guardian's turn renders no actor section, whatever the slot says.
|
|
1009
|
-
expect(conversation.currentTurnInboundActorContext).toBeNull();
|
|
1010
|
-
|
|
1011
|
-
await resolveRun(1);
|
|
1012
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
853
|
+
expect(pendingRuns.length).toBe(0);
|
|
1013
854
|
});
|
|
1014
855
|
|
|
1015
856
|
test("a processMessage turn keeps its turn-start trust when the slot moves before the loop opens", async () => {
|