@vellumai/assistant 0.12.1-staging.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/openapi.yaml +1 -291
- package/package.json +1 -1
- package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
- package/src/__tests__/app-compiler.test.ts +0 -6
- package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
- package/src/__tests__/app-dir-path-guard.test.ts +3 -1
- package/src/__tests__/app-source-watcher.test.ts +218 -0
- package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +5 -17
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +29 -28
- package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
- package/src/__tests__/config-watcher.test.ts +8 -23
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +0 -26
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
- package/src/__tests__/conversation-queue.test.ts +207 -48
- package/src/__tests__/conversation-retry-discard.test.ts +1 -54
- package/src/__tests__/conversation-skill-tools.test.ts +0 -27
- package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
- package/src/__tests__/conversation-title-service.test.ts +10 -14
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
- package/src/__tests__/credential-record-retire.test.ts +1 -171
- package/src/__tests__/credential-routes.test.ts +3 -212
- package/src/__tests__/credential-security-invariants.test.ts +1 -2
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/host-bash-proxy.test.ts +45 -26
- package/src/__tests__/host-browser-proxy.test.ts +17 -6
- package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
- package/src/__tests__/host-file-proxy.test.ts +23 -11
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
- package/src/__tests__/llm-catalog-parity.test.ts +0 -25
- package/src/__tests__/mcp-auth-routes.test.ts +9 -6
- package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
- package/src/__tests__/mcp-health-check.test.ts +11 -8
- package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
- package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
- package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
- package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
- package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +548 -355
- package/src/__tests__/model-intents.test.ts +0 -3
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
- package/src/__tests__/script-proxy-certs.test.ts +0 -35
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/speaker-identification.test.ts +56 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
- package/src/__tests__/subagent-tools.test.ts +2 -3
- package/src/__tests__/telephony-tts-guard.test.ts +3 -38
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
- package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
- package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
- package/src/__tests__/workspace-git-service.test.ts +0 -26
- package/src/acp/__tests__/agent-process.test.ts +4 -258
- package/src/acp/__tests__/auth-required.test.ts +0 -96
- package/src/acp/__tests__/client-handler.test.ts +2 -90
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
- package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
- package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
- package/src/acp/__tests__/session-manager.test.ts +4 -895
- package/src/acp/agent-process.test.ts +1 -4
- package/src/acp/agent-process.ts +9 -94
- package/src/acp/auth-required.ts +1 -50
- package/src/acp/auto-install.test.ts +7 -9
- package/src/acp/client-handler.ts +3 -16
- package/src/acp/resolve-agent.test.ts +6 -220
- package/src/acp/resolve-agent.ts +23 -88
- package/src/acp/session-manager.test.ts +3 -196
- package/src/acp/session-manager.ts +18 -441
- package/src/acp/types.ts +5 -50
- package/src/api/events/app-files-changed.ts +3 -4
- package/src/api/index.ts +0 -6
- package/src/apps/app-store.ts +1 -1
- package/src/bundler/app-compiler.ts +0 -15
- package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
- package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
- package/src/calls/call-constants.ts +4 -24
- package/src/calls/call-controller.ts +199 -499
- package/src/calls/call-transport.ts +0 -16
- package/src/calls/media-stream-output.ts +0 -32
- package/src/calls/media-stream-server.ts +30 -82
- package/src/calls/media-stream-stt-session.ts +0 -15
- package/src/calls/resolve-call-tts-provider.ts +2 -33
- package/src/calls/speaker-identification.ts +233 -0
- package/src/calls/tts-call-strategy.ts +5 -9
- package/src/calls/types.ts +0 -2
- package/src/calls/voice-control-protocol.ts +21 -53
- package/src/calls/voice-session-bridge.ts +46 -59
- package/src/calls/voice-triage-escalate.ts +56 -175
- package/src/cli/commands/apps.help.ts +13 -67
- package/src/cli/commands/apps.ts +0 -183
- package/src/cli/commands/channels/index.help.ts +2 -70
- package/src/cli/commands/channels/index.ts +3 -16
- package/src/cli/commands/credentials.help.ts +2 -6
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +1 -5
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +90 -103
- package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
- package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
- package/src/config/__tests__/platform-rehydration.test.ts +0 -93
- package/src/config/acp-defaults.test.ts +3 -13
- package/src/config/acp-defaults.ts +6 -23
- package/src/config/acp-schema.ts +2 -30
- package/src/config/bundled-skills/acp/SKILL.md +14 -22
- package/src/config/bundled-skills/acp/TOOLS.json +1 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +12 -8
- package/src/config/memory-v3-gate.test.ts +0 -31
- package/src/config/memory-v3-gate.ts +16 -20
- package/src/config/platform-rehydration.ts +21 -75
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
- package/src/config/schemas/calls.ts +28 -1
- package/src/config/schemas/live-voice.ts +173 -1
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +0 -10
- package/src/config/schemas/skills.ts +14 -0
- package/src/credential-execution/process-manager.test.ts +19 -18
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
- package/src/daemon/app-source-watcher.ts +218 -0
- package/src/daemon/config-watcher.ts +194 -13
- package/src/daemon/conversation-process.ts +64 -35
- package/src/daemon/conversation-skill-tools.ts +31 -13
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +8 -10
- package/src/daemon/conversation-tool-setup.ts +43 -37
- package/src/daemon/conversation.ts +3 -0
- package/src/daemon/host-bash-proxy.ts +12 -8
- package/src/daemon/host-browser-proxy.ts +1 -15
- package/src/daemon/host-file-proxy.ts +10 -8
- package/src/daemon/host-transfer-proxy.ts +18 -14
- package/src/daemon/lifecycle.ts +5 -0
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +9 -1
- package/src/daemon/skill-memory-refresh.ts +0 -64
- package/src/daemon/tool-side-effects.ts +18 -7
- package/src/hooks/types.ts +0 -33
- package/src/inbound/platform-callback-registration.ts +5 -4
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
- package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
- package/src/live-voice/live-voice-session.ts +693 -213
- package/src/{calls → live-voice}/progress-narration.ts +2 -2
- package/src/live-voice/protocol.ts +2 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +45 -29
- package/src/mcp/startup.ts +4 -8
- package/src/messaging/provider.ts +3 -10
- package/src/messaging/providers/callback-routing.ts +0 -18
- package/src/messaging/providers/channel-transport.ts +0 -56
- package/src/messaging/providers/discord/transport.ts +0 -17
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
- package/src/messaging/providers/slack/adapter.ts +44 -7
- package/src/messaging/providers/slack/transport.ts +0 -16
- package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
- package/src/messaging/providers/telegram-bot/client.ts +26 -0
- package/src/messaging/providers/telegram-bot/transport.ts +0 -20
- package/src/messaging/providers/whatsapp/adapter.ts +48 -1
- package/src/messaging/providers/whatsapp/client.ts +29 -0
- package/src/messaging/providers/whatsapp/transport.ts +0 -18
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/assistant-reply-producer.ts +3 -8
- package/src/notifications/conversation-pairing.ts +16 -36
- package/src/notifications/delivered-post-record.ts +1 -11
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/outbound-proxy/certs.ts +5 -3
- package/src/permissions/checker.test.ts +6 -11
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +63 -71
- package/src/persistence/conversation-lineage.ts +4 -12
- package/src/persistence/conversation-title-service.ts +26 -9
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-reads.ts +0 -30
- package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
- package/src/persistence/schema/memory-core.ts +0 -5
- package/src/platform/client.test.ts +0 -2
- package/src/platform/client.ts +10 -2
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +0 -1
- package/src/plugin-api/types.ts +0 -2
- package/src/plugins/defaults/index.ts +0 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
- package/src/plugins/defaults/memory/hooks/init.ts +0 -10
- package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
- package/src/plugins/defaults/memory/startup.ts +1 -6
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
- package/src/plugins/defaults/memory/worker.ts +0 -6
- package/src/plugins/mcp-servers.ts +75 -15
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +23 -9
- package/src/providers/model-catalog.ts +0 -34
- package/src/providers/model-intents.ts +5 -10
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
- package/src/providers/openai/chat-completions-provider.ts +78 -192
- package/src/providers/openai/codex-models.ts +4 -0
- package/src/runtime/AGENTS.md +0 -2
- package/src/runtime/auth/same-actor.ts +3 -38
- package/src/runtime/http-server.ts +151 -160
- package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
- package/src/runtime/routes/acp-routes.test.ts +0 -15
- package/src/runtime/routes/acp-routes.ts +9 -72
- package/src/runtime/routes/app-management-routes.ts +0 -73
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +0 -40
- package/src/runtime/routes/credential-routes.ts +10 -19
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/log-export-routes.ts +0 -2
- package/src/runtime/routes/mcp-auth-routes.ts +47 -39
- package/src/runtime/routes/migration-routes.ts +21 -8
- package/src/runtime/routes/platform-routes.ts +15 -18
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
- package/src/runtime/services/conversation-serializer.ts +3 -16
- package/src/runtime/sync/resource-sync-events.ts +1 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/ces-rpc-record-backend.ts +0 -7
- package/src/subagent/consult-context.ts +25 -12
- package/src/tools/acp/list-agents.test.ts +1 -3
- package/src/tools/acp/spawn.test.ts +11 -146
- package/src/tools/acp/spawn.ts +3 -21
- package/src/tools/acp/status.ts +3 -40
- package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
- package/src/tools/capability-offer.test.ts +2 -22
- package/src/tools/capability-offer.ts +1 -16
- package/src/tools/computer-use/definitions.ts +2 -4
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +0 -100
- package/src/tools/host-filesystem/edit.test.ts +1 -2
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +1 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +1 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +1 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +0 -16
- package/src/tools/host-terminal/host-shell.ts +20 -4
- package/src/tools/policy-context.ts +7 -6
- package/src/tools/subagent/spawn.ts +8 -5
- package/src/tools/watch/watch-retro-report.ts +8 -0
- package/src/util/__tests__/language-subtag.test.ts +1 -23
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/language-subtag.ts +0 -16
- package/src/util/platform.ts +0 -8
- package/src/workspace/git-service.ts +54 -5
- package/src/workspace/migrations/registry.ts +0 -6
- package/scripts/smoke-desktop-window-theme.ts +0 -186
- package/src/__tests__/git-porcelain.test.ts +0 -47
- package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
- package/src/__tests__/resolve-app.test.ts +0 -135
- package/src/__tests__/source-fingerprint.test.ts +0 -184
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
- package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
- package/src/acp/model-config.test.ts +0 -166
- package/src/acp/model-config.ts +0 -138
- package/src/api/events/acp-session-model-update.test.ts +0 -88
- package/src/api/events/acp-session-model-update.ts +0 -46
- package/src/apps/resolve-app.ts +0 -81
- package/src/apps/source-fingerprint.ts +0 -266
- package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
- package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
- package/src/calls/barge-in-guard.ts +0 -118
- package/src/calls/voice-leg-coordinator.ts +0 -189
- package/src/calls/voice-progress-cadence.ts +0 -423
- package/src/cli/commands/__tests__/apps.test.ts +0 -245
- package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
- package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
- package/src/cli/commands/channels/send.ts +0 -128
- package/src/config/__tests__/platform-identity.test.ts +0 -117
- package/src/config/acp-agent-write.ts +0 -43
- package/src/config/acp-schema.test.ts +0 -81
- package/src/config/platform-identity.ts +0 -144
- package/src/config/schemas/__tests__/voice.test.ts +0 -148
- package/src/config/schemas/voice.ts +0 -150
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
- package/src/daemon/app-change-notify.ts +0 -45
- package/src/desktop/__tests__/fake-desktop.ts +0 -119
- package/src/desktop/desktop-chrome-policy.test.ts +0 -71
- package/src/desktop/desktop-chrome-policy.ts +0 -42
- package/src/desktop/desktop-dependencies.test.ts +0 -102
- package/src/desktop/desktop-dependencies.ts +0 -306
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
- package/src/desktop/desktop-panel-config.test.ts +0 -135
- package/src/desktop/desktop-panel-config.ts +0 -149
- package/src/desktop/desktop-session-manager.test.ts +0 -627
- package/src/desktop/desktop-session-manager.ts +0 -814
- package/src/desktop/desktop-stream-bridge.test.ts +0 -395
- package/src/desktop/desktop-stream-bridge.ts +0 -214
- package/src/desktop/desktop-wallpaper.test.ts +0 -191
- package/src/desktop/desktop-wallpaper.ts +0 -113
- package/src/desktop/desktop-window-theme.ts +0 -170
- package/src/i18n/AGENTS.md +0 -22
- package/src/i18n/__tests__/i18n.test.ts +0 -108
- package/src/i18n/index.ts +0 -16
- package/src/i18n/locales.ts +0 -93
- package/src/i18n/messages.ts +0 -61
- package/src/i18n/title.ts +0 -25
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
- package/src/mcp/spec-schema.ts +0 -111
- package/src/mcp/workspace-mcp-config.ts +0 -188
- package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
- package/src/persistence/conversation-title-placeholders.ts +0 -23
- package/src/persistence/message-cursor.ts +0 -78
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
- package/src/providers/gemini-thought-signature.test.ts +0 -63
- package/src/providers/gemini-thought-signature.ts +0 -44
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
- package/src/providers/openai/google-thought-signature.ts +0 -320
- package/src/runtime/__tests__/channel-send.test.ts +0 -443
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
- package/src/runtime/channel-send.ts +0 -316
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
- package/src/runtime/routes/channel-send-routes.ts +0 -85
- package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
- package/src/runtime/routes/desktop-setup-routes.ts +0 -35
- package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
- package/src/security/ces-http-record-backend.ts +0 -120
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
- package/src/skills/workspace-skill-md-signature.ts +0 -50
- package/src/tools/acp/status.test.ts +0 -94
- package/src/util/ca-bundle.ts +0 -13
- package/src/workspace/git-porcelain.ts +0 -66
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
- /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
|
@@ -901,32 +901,6 @@ 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
|
-
|
|
930
904
|
test("passive vellum signal appends the body to the producing conversation", async () => {
|
|
931
905
|
mockExistingConversations["conv-producer"] = {
|
|
932
906
|
id: "conv-producer",
|
|
@@ -161,6 +161,12 @@ 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
|
+
},
|
|
164
170
|
setTransportHints() {},
|
|
165
171
|
applyHostEnvFromTransport() {},
|
|
166
172
|
ensureHostProxiesForTurn() {},
|
|
@@ -409,10 +415,10 @@ describe("drainQueue preactivation re-add for host-proxy interfaces", () => {
|
|
|
409
415
|
"U-contact",
|
|
410
416
|
);
|
|
411
417
|
expect(ctx.currentTurnTrustContext?.sourceChannel).toBe("slack");
|
|
412
|
-
// The
|
|
413
|
-
//
|
|
414
|
-
//
|
|
415
|
-
expect(ctx.trustContext
|
|
418
|
+
// The drain is where a queued message commits to a run, so the resting
|
|
419
|
+
// slot names the sender too: history is scoped from it, and a slot still
|
|
420
|
+
// naming the guardian would hand the contact's turn the guardian's rows.
|
|
421
|
+
expect(ctx.trustContext).toBe(contactTrust);
|
|
416
422
|
});
|
|
417
423
|
|
|
418
424
|
test("buildPassthroughBatch refuses to coalesce two channel senders", async () => {
|
|
@@ -450,6 +450,17 @@ 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
|
+
|
|
453
464
|
/**
|
|
454
465
|
* Resolve the Nth pending AgentLoop.run() call. Fires the minimal events
|
|
455
466
|
* that `runAgentLoop` expects (usage + message_complete) so the conversation
|
|
@@ -715,21 +726,22 @@ describe("Conversation message queue", () => {
|
|
|
715
726
|
await new Promise((r) => setTimeout(r, 10));
|
|
716
727
|
});
|
|
717
728
|
|
|
718
|
-
test("
|
|
719
|
-
//
|
|
720
|
-
//
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
//
|
|
729
|
+
test("a drained turn commits its sender as the resting actor and re-scopes history", async () => {
|
|
730
|
+
// A contact's channel turn leaves the resting slot, and the resident
|
|
731
|
+
// history scoped from it, naming the contact. The guardian's message
|
|
732
|
+
// queued behind that turn must run as the guardian everywhere the slot is
|
|
733
|
+
// read, not only in the per-turn snapshot: the `<turn_context>` actor
|
|
734
|
+
// section is derived at turn start and history is scoped from the slot,
|
|
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.
|
|
724
737
|
const conversation = makeConversation();
|
|
725
738
|
await conversation.loadFromDb();
|
|
726
739
|
|
|
727
|
-
|
|
728
|
-
trustClass: "trusted_contact"
|
|
729
|
-
sourceChannel: "slack"
|
|
740
|
+
conversation.setTrustContext({
|
|
741
|
+
trustClass: "trusted_contact",
|
|
742
|
+
sourceChannel: "slack",
|
|
730
743
|
requesterExternalUserId: "U-contact",
|
|
731
|
-
};
|
|
732
|
-
conversation.setTrustContext(contact);
|
|
744
|
+
});
|
|
733
745
|
|
|
734
746
|
const p1 = conversation.processMessage({
|
|
735
747
|
content: "msg-1",
|
|
@@ -744,6 +756,7 @@ describe("Conversation message queue", () => {
|
|
|
744
756
|
sourceChannel: "vellum" as const,
|
|
745
757
|
requesterExternalUserId: "guardian-principal",
|
|
746
758
|
};
|
|
759
|
+
const reloads = countHistoryReloads(conversation);
|
|
747
760
|
conversation.enqueueMessage({
|
|
748
761
|
content: "msg-2",
|
|
749
762
|
requestId: "req-2",
|
|
@@ -754,30 +767,83 @@ describe("Conversation message queue", () => {
|
|
|
754
767
|
await p1;
|
|
755
768
|
await waitForPendingRun(2);
|
|
756
769
|
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
|
|
770
|
+
// Read while run 2 is in flight: the slot, the snapshot, and the frozen
|
|
771
|
+
// actor section all describe the guardian, and the history was reloaded
|
|
772
|
+
// for that scope exactly once.
|
|
773
|
+
expect(conversation.getTrustContext()).toBe(guardian);
|
|
760
774
|
expect(conversation.currentTurnTrustContext).toBe(guardian);
|
|
761
|
-
// A guardian turn renders no actor section, whatever the slot says.
|
|
762
775
|
expect(conversation.currentTurnInboundActorContext).toBeNull();
|
|
776
|
+
expect(reloads()).toBe(1);
|
|
763
777
|
|
|
764
778
|
await resolveRun(1);
|
|
765
779
|
await new Promise((r) => setTimeout(r, 10));
|
|
766
780
|
});
|
|
767
781
|
|
|
768
|
-
test("
|
|
769
|
-
//
|
|
770
|
-
//
|
|
771
|
-
// guardian's turn must be described as that contact.
|
|
782
|
+
test("a batched drain commits the head's sender as the resting actor", async () => {
|
|
783
|
+
// Same commitment on the batched path, which coalesces only messages that
|
|
784
|
+
// share a trust identity, so the head's sender is the batch's actor.
|
|
772
785
|
const conversation = makeConversation();
|
|
773
786
|
await conversation.loadFromDb();
|
|
774
787
|
|
|
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
|
+
|
|
775
802
|
const guardian = {
|
|
776
803
|
trustClass: "guardian" as const,
|
|
777
804
|
sourceChannel: "vellum" as const,
|
|
778
805
|
requesterExternalUserId: "guardian-principal",
|
|
779
806
|
};
|
|
780
|
-
conversation
|
|
807
|
+
const reloads = countHistoryReloads(conversation);
|
|
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
|
+
});
|
|
781
847
|
|
|
782
848
|
const p1 = conversation.processMessage({
|
|
783
849
|
content: "msg-1",
|
|
@@ -787,39 +853,49 @@ describe("Conversation message queue", () => {
|
|
|
787
853
|
});
|
|
788
854
|
await waitForPendingRun(1);
|
|
789
855
|
|
|
790
|
-
const
|
|
791
|
-
trustClass: "
|
|
792
|
-
sourceChannel: "
|
|
793
|
-
requesterExternalUserId: "
|
|
856
|
+
const guardian = {
|
|
857
|
+
trustClass: "guardian" as const,
|
|
858
|
+
sourceChannel: "vellum" as const,
|
|
859
|
+
requesterExternalUserId: "guardian-principal",
|
|
860
|
+
};
|
|
861
|
+
const intruder = {
|
|
862
|
+
trustClass: "unknown" as const,
|
|
863
|
+
sourceChannel: "telegram" as const,
|
|
864
|
+
requesterExternalUserId: "T-stranger",
|
|
794
865
|
};
|
|
795
866
|
conversation.enqueueMessage({
|
|
796
867
|
content: "msg-2",
|
|
797
868
|
requestId: "req-2",
|
|
798
|
-
trustContext:
|
|
869
|
+
trustContext: guardian,
|
|
799
870
|
});
|
|
800
871
|
|
|
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
|
+
|
|
801
882
|
await resolveRun(0);
|
|
802
883
|
await p1;
|
|
803
884
|
await waitForPendingRun(2);
|
|
804
885
|
|
|
805
|
-
expect(
|
|
806
|
-
expect(conversation.
|
|
807
|
-
expect(conversation.
|
|
808
|
-
|
|
809
|
-
sourceChannel: "slack",
|
|
810
|
-
canonicalActorIdentity: "U-contact",
|
|
811
|
-
});
|
|
886
|
+
expect(movedDuringReload).toBe(true);
|
|
887
|
+
expect(conversation.getTrustContext()).toBe(intruder);
|
|
888
|
+
expect(conversation.currentTurnTrustContext).toBe(guardian);
|
|
889
|
+
expect(conversation.currentTurnInboundActorContext).toBeNull();
|
|
812
890
|
|
|
813
891
|
await resolveRun(1);
|
|
814
892
|
await new Promise((r) => setTimeout(r, 10));
|
|
815
893
|
});
|
|
816
894
|
|
|
817
|
-
test("a
|
|
818
|
-
//
|
|
819
|
-
//
|
|
820
|
-
//
|
|
821
|
-
// actorless dispatch afterwards (a deferred wake) resolves the resting
|
|
822
|
-
// slot and would inherit it.
|
|
895
|
+
test("a drain whose history reload fails puts the resting actor back", async () => {
|
|
896
|
+
// A reload that fails starts no turn: the message is requeued for the
|
|
897
|
+
// next drain, so the slot must not keep naming a sender whose turn never
|
|
898
|
+
// began, or conversation-level readers report an owner that is not there.
|
|
823
899
|
const conversation = makeConversation();
|
|
824
900
|
await conversation.loadFromDb();
|
|
825
901
|
|
|
@@ -830,27 +906,110 @@ describe("Conversation message queue", () => {
|
|
|
830
906
|
};
|
|
831
907
|
conversation.setTrustContext(contact);
|
|
832
908
|
|
|
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;
|
|
833
929
|
conversation.loadFromDb = async () => {
|
|
930
|
+
reloadAttempts++;
|
|
834
931
|
throw new Error("history store exploded");
|
|
835
932
|
};
|
|
836
933
|
|
|
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
|
+
|
|
837
970
|
const guardian = {
|
|
838
971
|
trustClass: "guardian" as const,
|
|
839
972
|
sourceChannel: "vellum" as const,
|
|
840
973
|
requesterExternalUserId: "guardian-principal",
|
|
841
974
|
};
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
requestId: "req-1",
|
|
848
|
-
trustContext: guardian,
|
|
849
|
-
}),
|
|
850
|
-
).rejects.toThrow("history store exploded");
|
|
975
|
+
conversation.enqueueMessage({
|
|
976
|
+
content: "msg-2",
|
|
977
|
+
requestId: "req-2",
|
|
978
|
+
trustContext: guardian,
|
|
979
|
+
});
|
|
851
980
|
|
|
981
|
+
// Land the out-of-band slot write inside the drain's window, after its
|
|
982
|
+
// stamp and before the loop opens: the drain awaits persistUserMessage
|
|
983
|
+
// there. The resting slot alone is moved; the per-turn snapshot is the
|
|
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);
|
|
1001
|
+
|
|
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);
|
|
852
1006
|
expect(conversation.getTrustContext()).toBe(contact);
|
|
853
|
-
expect(
|
|
1007
|
+
expect(conversation.currentTurnTrustContext).toBe(guardian);
|
|
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));
|
|
854
1013
|
});
|
|
855
1014
|
|
|
856
1015
|
test("a processMessage turn keeps its turn-start trust when the slot moves before the loop opens", async () => {
|
|
@@ -10,26 +10,13 @@ import {
|
|
|
10
10
|
discardLastAssistantDisplayTurn,
|
|
11
11
|
extractUserPromptText,
|
|
12
12
|
} from "../daemon/conversation-history.js";
|
|
13
|
-
import { registerPluginHooks } from "../hooks/registry.js";
|
|
14
13
|
import { addMessage, getMessages } from "../persistence/conversation-crud.js";
|
|
15
|
-
import { getDb
|
|
14
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
16
15
|
import { initializeDb } from "../persistence/db-init.js";
|
|
17
16
|
import { conversations, messages } from "../persistence/schema/index.js";
|
|
18
|
-
import memoryMessageDeleted from "../plugins/defaults/memory/hooks/message-deleted.js";
|
|
19
|
-
import {
|
|
20
|
-
ensureRetrospectiveCursorColumn,
|
|
21
|
-
getRetrospectiveState,
|
|
22
|
-
} from "../plugins/defaults/memory/memory-retrospective-state.js";
|
|
23
|
-
import type { PluginHooks } from "../plugins/types.js";
|
|
24
17
|
|
|
25
18
|
await initializeDb();
|
|
26
19
|
|
|
27
|
-
// Register the memory plugin's `message-deleted` hook the way boot does, so
|
|
28
|
-
// the delete primitive's dispatch reaches the cursor bookkeeping.
|
|
29
|
-
registerPluginHooks("default-memory", {
|
|
30
|
-
"message-deleted": memoryMessageDeleted,
|
|
31
|
-
} as PluginHooks);
|
|
32
|
-
|
|
33
20
|
const CONV_ID = "conv-retry-discard-test";
|
|
34
21
|
|
|
35
22
|
function seedConversation(): void {
|
|
@@ -224,43 +211,3 @@ describe("extractUserPromptText", () => {
|
|
|
224
211
|
expect(extractUserPromptText([])).toBe("");
|
|
225
212
|
});
|
|
226
213
|
});
|
|
227
|
-
|
|
228
|
-
describe("discardLastAssistantDisplayTurn keeps the memory-retrospective cursor placeable", () => {
|
|
229
|
-
beforeEach(() => {
|
|
230
|
-
ensureRetrospectiveCursorColumn("test");
|
|
231
|
-
getMemorySqlite()!.exec(`DELETE FROM memory_retrospective_state`);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test("a cursor stored without its timestamp gets it from the reply being discarded", async () => {
|
|
235
|
-
const [, assistantId] = await seed([
|
|
236
|
-
{ role: "user", content: text("hello") },
|
|
237
|
-
{ role: "assistant", content: text("first attempt") },
|
|
238
|
-
]);
|
|
239
|
-
const replyCreatedAt = getMessages(CONV_ID).find(
|
|
240
|
-
(m) => m.id === assistantId,
|
|
241
|
-
)!.createdAt;
|
|
242
|
-
// A state row as a build without the timestamp column wrote it: the
|
|
243
|
-
// retrospective reviewed through the reply, so the cursor sits on it.
|
|
244
|
-
getMemorySqlite()!
|
|
245
|
-
.query(
|
|
246
|
-
`INSERT INTO memory_retrospective_state
|
|
247
|
-
(conversation_id, last_processed_message_id, last_run_at, remembered_log, last_processed_created_at)
|
|
248
|
-
VALUES (?, ?, 1, NULL, NULL)`,
|
|
249
|
-
)
|
|
250
|
-
.run(CONV_ID, assistantId);
|
|
251
|
-
|
|
252
|
-
discardLastAssistantDisplayTurn(CONV_ID);
|
|
253
|
-
|
|
254
|
-
expect(getMessages(CONV_ID).some((m) => m.id === assistantId)).toBe(false);
|
|
255
|
-
// The hook chain is fire-and-forget from the delete primitive, so the
|
|
256
|
-
// stamp lands shortly after the discard returns.
|
|
257
|
-
const deadline = Date.now() + 2_000;
|
|
258
|
-
let stamped =
|
|
259
|
-
getRetrospectiveState(CONV_ID)?.lastProcessedCreatedAt ?? null;
|
|
260
|
-
while (stamped === null && Date.now() < deadline) {
|
|
261
|
-
await Bun.sleep(10);
|
|
262
|
-
stamped = getRetrospectiveState(CONV_ID)?.lastProcessedCreatedAt ?? null;
|
|
263
|
-
}
|
|
264
|
-
expect(stamped).toBe(replyCreatedAt);
|
|
265
|
-
});
|
|
266
|
-
});
|
|
@@ -545,33 +545,6 @@ describe("projectSkillTools", () => {
|
|
|
545
545
|
expect(mockRegisteredTools.has("deploy")).toBe(true);
|
|
546
546
|
});
|
|
547
547
|
|
|
548
|
-
test("catalog membership changes are visible on the next projection with a persistent cache", () => {
|
|
549
|
-
const cache = {};
|
|
550
|
-
mockCatalog = [];
|
|
551
|
-
mockManifests = { deploy: makeManifest(["deploy_run"]) };
|
|
552
|
-
|
|
553
|
-
const history: Message[] = [
|
|
554
|
-
...skillLoadMessages('<loaded_skill id="deploy" />'),
|
|
555
|
-
];
|
|
556
|
-
|
|
557
|
-
const result1 = projectSkillTools(history, {
|
|
558
|
-
previouslyActiveSkillIds: sessionState,
|
|
559
|
-
cache,
|
|
560
|
-
});
|
|
561
|
-
expect(result1.allowedToolNames.size).toBe(0);
|
|
562
|
-
expect(sessionState.has("deploy")).toBe(false);
|
|
563
|
-
|
|
564
|
-
mockCatalog = [makeSkill("deploy")];
|
|
565
|
-
|
|
566
|
-
const result2 = projectSkillTools(history, {
|
|
567
|
-
previouslyActiveSkillIds: sessionState,
|
|
568
|
-
cache,
|
|
569
|
-
});
|
|
570
|
-
expect(result2.allowedToolNames).toEqual(new Set(["deploy_run"]));
|
|
571
|
-
expect(sessionState.has("deploy")).toBe(true);
|
|
572
|
-
expect(mockRegisteredTools.has("deploy")).toBe(true);
|
|
573
|
-
});
|
|
574
|
-
|
|
575
548
|
test("skill with manifest failure on turn 1 is registered when manifest is available on turn 2", () => {
|
|
576
549
|
mockCatalog = [makeSkill("deploy")];
|
|
577
550
|
// No manifest — will fail to load
|
|
@@ -146,10 +146,8 @@ describe("surfaceProxyResolver — app-control tool routing", () => {
|
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
expect(result.isError).toBe(true);
|
|
149
|
-
expect(result.content).toContain(
|
|
150
|
-
|
|
151
|
-
);
|
|
152
|
-
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
149
|
+
expect(result.content).toContain("not available");
|
|
150
|
+
expect(result.content).toContain("app-control");
|
|
153
151
|
// No envelope dispatched.
|
|
154
152
|
expect(sentMessages).toHaveLength(0);
|
|
155
153
|
});
|
|
@@ -165,10 +163,7 @@ describe("surfaceProxyResolver — app-control tool routing", () => {
|
|
|
165
163
|
});
|
|
166
164
|
|
|
167
165
|
expect(result.isError).toBe(true);
|
|
168
|
-
expect(result.content).toContain(
|
|
169
|
-
"The Vellum desktop app is needed to control apps on your computer",
|
|
170
|
-
);
|
|
171
|
-
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
166
|
+
expect(result.content).toContain("not available");
|
|
172
167
|
expect(sentMessages).toHaveLength(0);
|
|
173
168
|
|
|
174
169
|
proxy.dispose();
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
function makeContext(
|
|
22
22
|
sent: AssistantEvent[] = [],
|
|
23
23
|
channelCapabilities?: { channel: string; supportsDynamicUi: boolean },
|
|
24
|
-
opts?: { hasNoClient?: boolean },
|
|
25
24
|
): Conversation {
|
|
26
25
|
return asConversation({
|
|
27
26
|
conversationId: "session-1",
|
|
@@ -38,7 +37,6 @@ function makeContext(
|
|
|
38
37
|
accumulatedSurfaceState: new Map<string, Record<string, unknown>>(),
|
|
39
38
|
surfaceActionRequestIds: new Set<string>(),
|
|
40
39
|
currentTurnSurfaces: [],
|
|
41
|
-
hasNoClient: opts?.hasNoClient ?? false,
|
|
42
40
|
isProcessing: () => false,
|
|
43
41
|
enqueueMessage: () => ({ queued: false, requestId: "req-1" }),
|
|
44
42
|
getQueueDepth: () => 0,
|
|
@@ -67,71 +65,6 @@ describe("task_progress surface compatibility", () => {
|
|
|
67
65
|
expect(sent).toHaveLength(0);
|
|
68
66
|
});
|
|
69
67
|
|
|
70
|
-
test("persists a clientless choice from a non-rendering channel without waiting", async () => {
|
|
71
|
-
const sent: AssistantEvent[] = [];
|
|
72
|
-
const ctx = makeContext(
|
|
73
|
-
sent,
|
|
74
|
-
{
|
|
75
|
-
channel: "phone",
|
|
76
|
-
supportsDynamicUi: false,
|
|
77
|
-
},
|
|
78
|
-
{ hasNoClient: true },
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
82
|
-
surface_type: "choice",
|
|
83
|
-
title: "Choose a focus",
|
|
84
|
-
data: {
|
|
85
|
-
options: [
|
|
86
|
-
{ id: "inbox", title: "Inbox" },
|
|
87
|
-
{ id: "calendar", title: "Calendar" },
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
expect(result.isError).toBe(false);
|
|
93
|
-
expect(result.yieldToUser).toBeUndefined();
|
|
94
|
-
const { surfaceId } = JSON.parse(result.content) as { surfaceId: string };
|
|
95
|
-
expect(ctx.currentTurnSurfaces.some((s) => s.surfaceId === surfaceId)).toBe(
|
|
96
|
-
true,
|
|
97
|
-
);
|
|
98
|
-
expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(false);
|
|
99
|
-
expect(sent.some((msg) => msg.type === "ui_surface_show")).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("persists a clientless update in the current-turn surface snapshot", async () => {
|
|
103
|
-
const sent: AssistantEvent[] = [];
|
|
104
|
-
const ctx = makeContext(
|
|
105
|
-
sent,
|
|
106
|
-
{
|
|
107
|
-
channel: "phone",
|
|
108
|
-
supportsDynamicUi: false,
|
|
109
|
-
},
|
|
110
|
-
{ hasNoClient: true },
|
|
111
|
-
);
|
|
112
|
-
const shown = await surfaceProxyResolver(ctx, "ui_show", {
|
|
113
|
-
surface_type: "card",
|
|
114
|
-
title: "Background work",
|
|
115
|
-
data: {
|
|
116
|
-
template: "task_progress",
|
|
117
|
-
templateData: { status: "in_progress", steps: [] },
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
const { surfaceId } = JSON.parse(shown.content) as { surfaceId: string };
|
|
121
|
-
|
|
122
|
-
const result = await surfaceProxyResolver(ctx, "ui_update", {
|
|
123
|
-
surface_id: surfaceId,
|
|
124
|
-
data: { templateData: { status: "completed" } },
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
expect(result.isError).toBe(false);
|
|
128
|
-
const data = ctx.currentTurnSurfaces.find((s) => s.surfaceId === surfaceId)
|
|
129
|
-
?.data as CardSurfaceData;
|
|
130
|
-
expect((data.templateData as Record<string, unknown>).status).toBe(
|
|
131
|
-
"completed",
|
|
132
|
-
);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
68
|
test("blocks ui_update when channel lacks dynamic UI support", async () => {
|
|
136
69
|
const sent: AssistantEvent[] = [];
|
|
137
70
|
const ctx = makeContext(sent, {
|