@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
|
@@ -13,11 +13,8 @@ describe("forwardComputerUseProxyTool", () => {
|
|
|
13
13
|
);
|
|
14
14
|
|
|
15
15
|
expect(result.isError).toBe(true);
|
|
16
|
-
expect(result.content).toContain(
|
|
17
|
-
|
|
18
|
-
);
|
|
19
|
-
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
20
|
-
expect(result.content).not.toContain("computer_use_click");
|
|
16
|
+
expect(result.content).toContain("no proxy resolver available");
|
|
17
|
+
expect(result.content).toContain("computer_use_click");
|
|
21
18
|
});
|
|
22
19
|
|
|
23
20
|
test("forwards to proxyToolResolver when present", async () => {
|
|
@@ -104,10 +104,7 @@ describe("computer_use_click (unified)", () => {
|
|
|
104
104
|
test("execute returns isError when no proxy resolver is configured", async () => {
|
|
105
105
|
const result = await computerUseClickTool.execute({}, ctx);
|
|
106
106
|
expect(result.isError).toBe(true);
|
|
107
|
-
expect(result.content).
|
|
108
|
-
"The Vellum desktop app is needed to view or control your screen",
|
|
109
|
-
);
|
|
110
|
-
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
107
|
+
expect(result.content).toMatch(/No proxy resolver/);
|
|
111
108
|
});
|
|
112
109
|
});
|
|
113
110
|
|
|
@@ -122,10 +119,7 @@ describe("computer_use_type_text", () => {
|
|
|
122
119
|
test("execute returns isError when no proxy resolver is configured", async () => {
|
|
123
120
|
const result = await computerUseTypeTextTool.execute({}, ctx);
|
|
124
121
|
expect(result.isError).toBe(true);
|
|
125
|
-
expect(result.content).
|
|
126
|
-
"The Vellum desktop app is needed to view or control your screen",
|
|
127
|
-
);
|
|
128
|
-
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
122
|
+
expect(result.content).toMatch(/No proxy resolver/);
|
|
129
123
|
});
|
|
130
124
|
});
|
|
131
125
|
|
|
@@ -140,10 +134,7 @@ describe("computer_use_key", () => {
|
|
|
140
134
|
test("execute returns isError when no proxy resolver is configured", async () => {
|
|
141
135
|
const result = await computerUseKeyTool.execute({}, ctx);
|
|
142
136
|
expect(result.isError).toBe(true);
|
|
143
|
-
expect(result.content).
|
|
144
|
-
"The Vellum desktop app is needed to view or control your screen",
|
|
145
|
-
);
|
|
146
|
-
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
137
|
+
expect(result.content).toMatch(/No proxy resolver/);
|
|
147
138
|
});
|
|
148
139
|
});
|
|
149
140
|
|
|
@@ -250,11 +241,8 @@ describe("forwardComputerUseProxyTool", () => {
|
|
|
250
241
|
);
|
|
251
242
|
|
|
252
243
|
expect(result.isError).toBe(true);
|
|
253
|
-
expect(result.content).toContain(
|
|
254
|
-
|
|
255
|
-
);
|
|
256
|
-
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
257
|
-
expect(result.content).not.toContain("computer_use_click");
|
|
244
|
+
expect(result.content).toContain("no proxy resolver available");
|
|
245
|
+
expect(result.content).toContain("computer_use_click");
|
|
258
246
|
});
|
|
259
247
|
|
|
260
248
|
test("delegates to proxy resolver when available", async () => {
|
|
@@ -130,7 +130,7 @@ describe("getSchemaAtPath", () => {
|
|
|
130
130
|
expect(properties).toBeDefined();
|
|
131
131
|
expect(properties.enabled).toBeDefined();
|
|
132
132
|
expect(properties.voice).toBeDefined();
|
|
133
|
-
expect(properties.
|
|
133
|
+
expect(properties.safety).toBeDefined();
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
test("navigates through .transform() wrappers (ingress → object schema)", () => {
|
|
@@ -230,7 +230,7 @@ describe("z.toJSONSchema integration", () => {
|
|
|
230
230
|
expect(properties).toBeDefined();
|
|
231
231
|
expect(properties!.enabled).toBeDefined();
|
|
232
232
|
expect(properties!.voice).toBeDefined();
|
|
233
|
-
expect(properties!.
|
|
233
|
+
expect(properties!.safety).toBeDefined();
|
|
234
234
|
});
|
|
235
235
|
|
|
236
236
|
test("sub-schema at a leaf like llm.profileSession.defaultTtlSeconds produces integer schema", () => {
|
|
@@ -952,7 +952,11 @@ 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
|
+
},
|
|
955
958
|
voice: {
|
|
959
|
+
interruptSensitivity: "low",
|
|
956
960
|
telephonyStreaming: true,
|
|
957
961
|
utteranceEndMs: 1000,
|
|
958
962
|
},
|
|
@@ -974,6 +978,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
974
978
|
maxDurationSeconds: 1800,
|
|
975
979
|
userConsultTimeoutSeconds: 60,
|
|
976
980
|
disclosure: { enabled: false, text: "Custom disclosure" },
|
|
981
|
+
safety: { denyCategories: ["spam"] },
|
|
977
982
|
},
|
|
978
983
|
});
|
|
979
984
|
expect(result.calls.enabled).toBe(false);
|
|
@@ -981,6 +986,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
981
986
|
expect(result.calls.userConsultTimeoutSeconds).toBe(60);
|
|
982
987
|
expect(result.calls.disclosure.enabled).toBe(false);
|
|
983
988
|
expect(result.calls.disclosure.text).toBe("Custom disclosure");
|
|
989
|
+
expect(result.calls.safety.denyCategories).toEqual(["spam"]);
|
|
984
990
|
});
|
|
985
991
|
|
|
986
992
|
// ── Live voice config ───────────────────────────────────────────────
|
|
@@ -988,6 +994,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
988
994
|
test("applies liveVoice defaults", () => {
|
|
989
995
|
const result = AssistantConfigSchema.parse({});
|
|
990
996
|
expect(result.liveVoice).toEqual({
|
|
997
|
+
mode: "open-mic",
|
|
991
998
|
vad: {
|
|
992
999
|
speechEnergyThreshold: 800,
|
|
993
1000
|
noiseFloorMargin: 3,
|
|
@@ -998,18 +1005,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
998
1005
|
echoEmaHalfLifeMs: 400,
|
|
999
1006
|
echoDrainSlackMs: 300,
|
|
1000
1007
|
},
|
|
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({
|
|
1013
1008
|
frontModel: {
|
|
1014
1009
|
endpointDecisionTimeoutMs: 1200,
|
|
1015
1010
|
endpointExtensionMs: 1500,
|
|
@@ -1024,24 +1019,35 @@ describe("AssistantConfigSchema", () => {
|
|
|
1024
1019
|
generationTimeoutMs: 1500,
|
|
1025
1020
|
},
|
|
1026
1021
|
},
|
|
1022
|
+
flux: {
|
|
1023
|
+
turnEnd: { enabled: true },
|
|
1024
|
+
eotThreshold: 0.7,
|
|
1025
|
+
eotTimeoutMs: 5000,
|
|
1026
|
+
},
|
|
1027
|
+
maxSessionDurationSeconds: 1800,
|
|
1028
|
+
archiveAudio: false,
|
|
1027
1029
|
});
|
|
1028
1030
|
});
|
|
1029
1031
|
|
|
1030
1032
|
test("accepts valid liveVoice config overrides", () => {
|
|
1031
1033
|
const result = AssistantConfigSchema.parse({
|
|
1032
1034
|
liveVoice: {
|
|
1035
|
+
mode: "ptt",
|
|
1033
1036
|
vad: {
|
|
1034
1037
|
speechEnergyThreshold: 1500,
|
|
1035
1038
|
silenceThresholdMs: 1000,
|
|
1036
1039
|
bargeInMinSpeechMs: 120,
|
|
1037
1040
|
},
|
|
1041
|
+
maxSessionDurationSeconds: 900,
|
|
1038
1042
|
},
|
|
1039
1043
|
});
|
|
1044
|
+
expect(result.liveVoice.mode).toBe("ptt");
|
|
1040
1045
|
expect(result.liveVoice.vad.speechEnergyThreshold).toBe(1500);
|
|
1041
1046
|
expect(result.liveVoice.vad.silenceThresholdMs).toBe(1000);
|
|
1042
1047
|
expect(result.liveVoice.vad.bargeInMinSpeechMs).toBe(120);
|
|
1043
1048
|
// Unspecified vad fields still get defaults
|
|
1044
1049
|
expect(result.liveVoice.vad.maxTurnDurationMs).toBe(30000);
|
|
1050
|
+
expect(result.liveVoice.maxSessionDurationSeconds).toBe(900);
|
|
1045
1051
|
// A partial liveVoice override leaves Flux turn-end disabled by default.
|
|
1046
1052
|
expect(result.liveVoice.flux.turnEnd.enabled).toBe(true);
|
|
1047
1053
|
});
|
|
@@ -1130,10 +1136,18 @@ describe("AssistantConfigSchema", () => {
|
|
|
1130
1136
|
expect(result.success).toBe(false);
|
|
1131
1137
|
});
|
|
1132
1138
|
|
|
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
|
+
|
|
1133
1146
|
// ── Calls voice config ──────────────────────────────────────────────
|
|
1134
1147
|
|
|
1135
1148
|
test("config without calls.voice parses correctly and produces defaults", () => {
|
|
1136
1149
|
const result = AssistantConfigSchema.parse({});
|
|
1150
|
+
expect(result.calls.voice.interruptSensitivity).toBe("low");
|
|
1137
1151
|
expect(result.calls.voice.telephonyStreaming).toBe(true);
|
|
1138
1152
|
expect(result.calls.voice.utteranceEndMs).toBe(1000);
|
|
1139
1153
|
});
|
|
@@ -1151,23 +1165,9 @@ describe("AssistantConfigSchema", () => {
|
|
|
1151
1165
|
expect(result.calls.voice.utteranceEndMs).toBe(2500);
|
|
1152
1166
|
});
|
|
1153
1167
|
|
|
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
|
-
// it
|
|
1169
|
+
// The retired knob was read by nothing; Zod strips the unrecognized key
|
|
1170
|
+
// so persisted configs that still carry it keep parsing.
|
|
1171
1171
|
const result = AssistantConfigSchema.parse({
|
|
1172
1172
|
calls: { voice: { language: "es-ES" } },
|
|
1173
1173
|
});
|
|
@@ -2633,6 +2633,7 @@ 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([]);
|
|
2636
2637
|
expect(
|
|
2637
2638
|
(config.calls.voice as Record<string, unknown>).language,
|
|
2638
2639
|
).toBeUndefined();
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
afterAll,
|
|
6
|
+
afterEach,
|
|
7
|
+
beforeEach,
|
|
8
|
+
describe,
|
|
9
|
+
expect,
|
|
10
|
+
mock,
|
|
11
|
+
test,
|
|
12
|
+
} from "bun:test";
|
|
13
|
+
|
|
14
|
+
const WORKSPACE_DIR = mkdtempSync(join(tmpdir(), "vellum-skills-watch-"));
|
|
15
|
+
const SKILLS_DIR = join(WORKSPACE_DIR, "skills");
|
|
16
|
+
process.env.VELLUM_WORKSPACE_DIR = WORKSPACE_DIR;
|
|
17
|
+
|
|
18
|
+
type WatchCallback = (eventType: string, filename: string | null) => void;
|
|
19
|
+
|
|
20
|
+
interface CapturedWatcher {
|
|
21
|
+
dir: string;
|
|
22
|
+
callback: WatchCallback;
|
|
23
|
+
closed: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const capturedWatchers: CapturedWatcher[] = [];
|
|
27
|
+
let recursiveWatchAvailable = false;
|
|
28
|
+
|
|
29
|
+
// The skills watcher reacts by calling these directly; count the calls so tests
|
|
30
|
+
// can assert dispatch without driving real eviction or skill-memory reseeding.
|
|
31
|
+
let evictCalls = 0;
|
|
32
|
+
let skillsChangedCalls = 0;
|
|
33
|
+
|
|
34
|
+
mock.module("node:fs", () => {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
|
+
const actual = require("node:fs");
|
|
37
|
+
return {
|
|
38
|
+
...actual,
|
|
39
|
+
watch: (dir: string, ...args: unknown[]) => {
|
|
40
|
+
let callback: WatchCallback;
|
|
41
|
+
|
|
42
|
+
if (typeof args[0] === "function") {
|
|
43
|
+
callback = args[0] as WatchCallback;
|
|
44
|
+
} else {
|
|
45
|
+
if (
|
|
46
|
+
(args[0] as { recursive?: boolean } | undefined)?.recursive &&
|
|
47
|
+
!recursiveWatchAvailable
|
|
48
|
+
) {
|
|
49
|
+
throw new Error("recursive watch unavailable");
|
|
50
|
+
}
|
|
51
|
+
callback = args[1] as WatchCallback;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const capturedWatcher: CapturedWatcher = {
|
|
55
|
+
dir,
|
|
56
|
+
callback,
|
|
57
|
+
closed: false,
|
|
58
|
+
};
|
|
59
|
+
capturedWatchers.push(capturedWatcher);
|
|
60
|
+
return {
|
|
61
|
+
close: () => {
|
|
62
|
+
capturedWatcher.closed = true;
|
|
63
|
+
},
|
|
64
|
+
on: () => {},
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
mock.module("../persistence/embeddings/embedding-backend.js", () => ({
|
|
71
|
+
clearEmbeddingBackendCache: () => {},
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
mock.module("../persistence/cleanup-schedule-state.js", () => ({
|
|
75
|
+
resetCleanupScheduleThrottle: () => {},
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
mock.module("../providers/registry.js", () => ({
|
|
79
|
+
initializeProviders: async () => {},
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
mock.module("../daemon/mcp-reload-service.js", () => ({
|
|
83
|
+
reloadMcpServers: async () => {},
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
mock.module("../signals/bash.js", () => ({
|
|
87
|
+
handleBashSignal: () => {},
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
mock.module("../signals/cancel.js", () => ({
|
|
91
|
+
handleCancelSignal: () => {},
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
mock.module("../signals/conversation-undo.js", () => ({
|
|
95
|
+
handleConversationUndoSignal: () => {},
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
mock.module("../signals/emit-event.js", () => ({
|
|
99
|
+
handleEmitEventSignal: () => {},
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
mock.module("../signals/user-message.js", () => ({
|
|
103
|
+
handleUserMessageSignal: () => {},
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
mock.module("../daemon/conversation-store.js", () => ({
|
|
107
|
+
evictConversationsForReload: () => {
|
|
108
|
+
evictCalls++;
|
|
109
|
+
},
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
mock.module("../daemon/skill-memory-refresh.js", () => ({
|
|
113
|
+
refreshSkillCapabilityMemories: () => {
|
|
114
|
+
skillsChangedCalls++;
|
|
115
|
+
},
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
mock.module("../runtime/sync/resource-sync-events.js", () => ({
|
|
119
|
+
publishIdentityChanged: () => {},
|
|
120
|
+
publishConfigChanged: () => {},
|
|
121
|
+
publishSoundsConfigUpdated: () => {},
|
|
122
|
+
publishAvatarChanged: () => {},
|
|
123
|
+
publishWorkspaceThemeChanged: () => {},
|
|
124
|
+
}));
|
|
125
|
+
|
|
126
|
+
mock.module("../platform/sync-identity.js", () => ({
|
|
127
|
+
syncIdentityNameToPlatform: () => {},
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
const { ConfigWatcher } = await import("../daemon/config-watcher.js");
|
|
131
|
+
|
|
132
|
+
function findWatcher(dir: string): CapturedWatcher | undefined {
|
|
133
|
+
return capturedWatchers.find((w) => w.dir === dir && !w.closed);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function findCapturedWatcher(dir: string): CapturedWatcher | undefined {
|
|
137
|
+
return capturedWatchers.find((w) => w.dir === dir);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function capturedWatcherCount(dir: string): number {
|
|
141
|
+
return capturedWatchers.filter((w) => w.dir === dir).length;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
describe("ConfigWatcher skills watcher reseeding", () => {
|
|
145
|
+
let watcher: InstanceType<typeof ConfigWatcher>;
|
|
146
|
+
|
|
147
|
+
beforeEach(() => {
|
|
148
|
+
capturedWatchers.length = 0;
|
|
149
|
+
recursiveWatchAvailable = false;
|
|
150
|
+
rmSync(SKILLS_DIR, { recursive: true, force: true });
|
|
151
|
+
mkdirSync(SKILLS_DIR, { recursive: true });
|
|
152
|
+
evictCalls = 0;
|
|
153
|
+
skillsChangedCalls = 0;
|
|
154
|
+
watcher = new ConfigWatcher();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
afterEach(() => {
|
|
158
|
+
watcher.stop();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
afterAll(() => {
|
|
162
|
+
rmSync(WORKSPACE_DIR, { recursive: true, force: true });
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("recursive watcher", () => {
|
|
166
|
+
beforeEach(() => {
|
|
167
|
+
recursiveWatchAvailable = true;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("reloads on SKILL.md changes", async () => {
|
|
171
|
+
watcher.start();
|
|
172
|
+
|
|
173
|
+
const skillsWatcher = findWatcher(SKILLS_DIR);
|
|
174
|
+
expect(skillsWatcher).toBeDefined();
|
|
175
|
+
skillsWatcher!.callback("change", "example-skill/SKILL.md");
|
|
176
|
+
|
|
177
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
178
|
+
|
|
179
|
+
expect(evictCalls).toBe(1);
|
|
180
|
+
expect(skillsChangedCalls).toBe(1);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("reloads on TOOLS.json changes", async () => {
|
|
184
|
+
watcher.start();
|
|
185
|
+
|
|
186
|
+
const skillsWatcher = findWatcher(SKILLS_DIR);
|
|
187
|
+
skillsWatcher!.callback("change", "example-skill/TOOLS.json");
|
|
188
|
+
|
|
189
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
190
|
+
|
|
191
|
+
expect(evictCalls).toBe(1);
|
|
192
|
+
expect(skillsChangedCalls).toBe(1);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("ignores non-catalog files (scripts, references, build output)", async () => {
|
|
196
|
+
watcher.start();
|
|
197
|
+
|
|
198
|
+
const skillsWatcher = findWatcher(SKILLS_DIR);
|
|
199
|
+
skillsWatcher!.callback("change", "example-skill/references/foo.md");
|
|
200
|
+
skillsWatcher!.callback("change", "example-skill/dist/index.js");
|
|
201
|
+
skillsWatcher!.callback("change", "example-skill/package.json");
|
|
202
|
+
skillsWatcher!.callback("change", "example-skill/scripts/run.py");
|
|
203
|
+
|
|
204
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
205
|
+
|
|
206
|
+
expect(evictCalls).toBe(0);
|
|
207
|
+
expect(skillsChangedCalls).toBe(0);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("ignores catalog files under skipped dependency and staging paths", async () => {
|
|
211
|
+
watcher.start();
|
|
212
|
+
|
|
213
|
+
const skillsWatcher = findWatcher(SKILLS_DIR);
|
|
214
|
+
skillsWatcher!.callback(
|
|
215
|
+
"change",
|
|
216
|
+
"example-skill/node_modules/pkg/SKILL.md",
|
|
217
|
+
);
|
|
218
|
+
skillsWatcher!.callback(
|
|
219
|
+
"change",
|
|
220
|
+
".install-staging/example-skill/SKILL.md",
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
224
|
+
|
|
225
|
+
expect(evictCalls).toBe(0);
|
|
226
|
+
expect(skillsChangedCalls).toBe(0);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("coalesces multiple catalog changes into one reload", async () => {
|
|
230
|
+
watcher.start();
|
|
231
|
+
|
|
232
|
+
const skillsWatcher = findWatcher(SKILLS_DIR);
|
|
233
|
+
skillsWatcher!.callback("change", "example-skill/SKILL.md");
|
|
234
|
+
skillsWatcher!.callback("change", "example-skill/package.json");
|
|
235
|
+
skillsWatcher!.callback("change", "example-skill/TOOLS.json");
|
|
236
|
+
skillsWatcher!.callback("change", "other-skill/SKILL.md");
|
|
237
|
+
|
|
238
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
239
|
+
|
|
240
|
+
expect(evictCalls).toBe(1);
|
|
241
|
+
expect(skillsChangedCalls).toBe(1);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("per-skill-directory fallback", () => {
|
|
246
|
+
test("watches the root and each immediate skill directory, not nested subdirectories", () => {
|
|
247
|
+
const skillDir = join(SKILLS_DIR, "example-skill");
|
|
248
|
+
mkdirSync(join(skillDir, "references"), { recursive: true });
|
|
249
|
+
mkdirSync(join(skillDir, "dist"), { recursive: true });
|
|
250
|
+
|
|
251
|
+
watcher.start();
|
|
252
|
+
|
|
253
|
+
expect(findWatcher(SKILLS_DIR)).toBeDefined();
|
|
254
|
+
expect(findWatcher(skillDir)).toBeDefined();
|
|
255
|
+
expect(findWatcher(join(skillDir, "references"))).toBeUndefined();
|
|
256
|
+
expect(findWatcher(join(skillDir, "dist"))).toBeUndefined();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("does not watch dependency or staging directories", () => {
|
|
260
|
+
const skillDir = join(SKILLS_DIR, "example-skill");
|
|
261
|
+
mkdirSync(join(skillDir, "node_modules", "pkg"), { recursive: true });
|
|
262
|
+
mkdirSync(join(SKILLS_DIR, ".install-staging", "staged"), {
|
|
263
|
+
recursive: true,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
watcher.start();
|
|
267
|
+
|
|
268
|
+
expect(findWatcher(join(skillDir, "node_modules"))).toBeUndefined();
|
|
269
|
+
expect(findWatcher(join(SKILLS_DIR, ".install-staging"))).toBeUndefined();
|
|
270
|
+
expect(findWatcher(skillDir)).toBeDefined();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("reloads on SKILL.md / TOOLS.json edits within a skill directory", async () => {
|
|
274
|
+
const skillDir = join(SKILLS_DIR, "example-skill");
|
|
275
|
+
mkdirSync(skillDir, { recursive: true });
|
|
276
|
+
|
|
277
|
+
watcher.start();
|
|
278
|
+
const skillWatcher = findWatcher(skillDir);
|
|
279
|
+
expect(skillWatcher).toBeDefined();
|
|
280
|
+
|
|
281
|
+
skillWatcher!.callback("change", "SKILL.md");
|
|
282
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
283
|
+
expect(evictCalls).toBe(1);
|
|
284
|
+
expect(skillsChangedCalls).toBe(1);
|
|
285
|
+
|
|
286
|
+
skillWatcher!.callback("change", "TOOLS.json");
|
|
287
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
288
|
+
expect(evictCalls).toBe(2);
|
|
289
|
+
expect(skillsChangedCalls).toBe(2);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("ignores non-catalog file edits within a skill directory", async () => {
|
|
293
|
+
const skillDir = join(SKILLS_DIR, "example-skill");
|
|
294
|
+
mkdirSync(skillDir, { recursive: true });
|
|
295
|
+
|
|
296
|
+
watcher.start();
|
|
297
|
+
const skillWatcher = findWatcher(skillDir);
|
|
298
|
+
|
|
299
|
+
skillWatcher!.callback("change", "README.md");
|
|
300
|
+
skillWatcher!.callback("change", "package.json");
|
|
301
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
302
|
+
|
|
303
|
+
expect(evictCalls).toBe(0);
|
|
304
|
+
expect(skillsChangedCalls).toBe(0);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("watches and reloads when a skill is installed", async () => {
|
|
308
|
+
watcher.start();
|
|
309
|
+
const rootWatcher = findWatcher(SKILLS_DIR);
|
|
310
|
+
expect(rootWatcher).toBeDefined();
|
|
311
|
+
|
|
312
|
+
const newSkillDir = join(SKILLS_DIR, "new-skill");
|
|
313
|
+
mkdirSync(newSkillDir, { recursive: true });
|
|
314
|
+
rootWatcher!.callback("rename", "new-skill");
|
|
315
|
+
|
|
316
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
317
|
+
|
|
318
|
+
expect(findWatcher(newSkillDir)).toBeDefined();
|
|
319
|
+
expect(evictCalls).toBe(1);
|
|
320
|
+
expect(skillsChangedCalls).toBe(1);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("closes the watcher and reloads when a skill is removed", async () => {
|
|
324
|
+
const skillDir = join(SKILLS_DIR, "example-skill");
|
|
325
|
+
mkdirSync(skillDir, { recursive: true });
|
|
326
|
+
|
|
327
|
+
watcher.start();
|
|
328
|
+
expect(findWatcher(skillDir)).toBeDefined();
|
|
329
|
+
|
|
330
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
331
|
+
const rootWatcher = findWatcher(SKILLS_DIR);
|
|
332
|
+
rootWatcher!.callback("rename", "example-skill");
|
|
333
|
+
|
|
334
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
335
|
+
|
|
336
|
+
expect(findWatcher(skillDir)).toBeUndefined();
|
|
337
|
+
expect(findCapturedWatcher(skillDir)?.closed).toBe(true);
|
|
338
|
+
expect(evictCalls).toBe(1);
|
|
339
|
+
expect(skillsChangedCalls).toBe(1);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test("does not duplicate a watcher or reload when the root changes but the skill set does not", async () => {
|
|
343
|
+
const skillDir = join(SKILLS_DIR, "example-skill");
|
|
344
|
+
mkdirSync(skillDir, { recursive: true });
|
|
345
|
+
|
|
346
|
+
watcher.start();
|
|
347
|
+
expect(capturedWatcherCount(skillDir)).toBe(1);
|
|
348
|
+
|
|
349
|
+
// A stray file event at the root: no skill directory added or removed.
|
|
350
|
+
const rootWatcher = findWatcher(SKILLS_DIR);
|
|
351
|
+
rootWatcher!.callback("change", "README.md");
|
|
352
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
353
|
+
|
|
354
|
+
expect(capturedWatcherCount(skillDir)).toBe(1);
|
|
355
|
+
expect(evictCalls).toBe(0);
|
|
356
|
+
expect(skillsChangedCalls).toBe(0);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
});
|
|
@@ -113,11 +113,8 @@ mock.module("../providers/registry.js", () => ({
|
|
|
113
113
|
resolveProviderFromConnection: async () => null,
|
|
114
114
|
}));
|
|
115
115
|
|
|
116
|
-
let mcpReloadCallCount = 0;
|
|
117
116
|
mock.module("../daemon/mcp-reload-service.js", () => ({
|
|
118
|
-
reloadMcpServers: async () => {
|
|
119
|
-
mcpReloadCallCount += 1;
|
|
120
|
-
},
|
|
117
|
+
reloadMcpServers: async () => {},
|
|
121
118
|
}));
|
|
122
119
|
|
|
123
120
|
mock.module("../signals/conversation-undo.js", () => ({
|
|
@@ -157,6 +154,10 @@ mock.module("../runtime/sync/resource-sync-events.js", () => ({
|
|
|
157
154
|
},
|
|
158
155
|
}));
|
|
159
156
|
|
|
157
|
+
mock.module("../daemon/skill-memory-refresh.js", () => ({
|
|
158
|
+
refreshSkillCapabilityMemories: () => {},
|
|
159
|
+
}));
|
|
160
|
+
|
|
160
161
|
mock.module("../platform/sync-identity.js", () => ({
|
|
161
162
|
syncIdentityNameToPlatform: () => {},
|
|
162
163
|
}));
|
|
@@ -182,7 +183,6 @@ function findFileWatch(filePath: string): CapturedFileWatch | undefined {
|
|
|
182
183
|
|
|
183
184
|
const WORKSPACE_FILES = new Set([
|
|
184
185
|
"config.json",
|
|
185
|
-
"mcp.json",
|
|
186
186
|
"SOUL.md",
|
|
187
187
|
"IDENTITY.md",
|
|
188
188
|
"ui/theme.json",
|
|
@@ -239,7 +239,6 @@ beforeEach(() => {
|
|
|
239
239
|
evictCallCount = 0;
|
|
240
240
|
identityCallCount = 0;
|
|
241
241
|
themeCallCount = 0;
|
|
242
|
-
mcpReloadCallCount = 0;
|
|
243
242
|
watcher = new ConfigWatcher(undefined, TEST_DEBOUNCE_MS);
|
|
244
243
|
});
|
|
245
244
|
|
|
@@ -271,9 +270,9 @@ describe("ConfigWatcher workspace file handlers", () => {
|
|
|
271
270
|
|
|
272
271
|
test("unregistered workspace files are not subscribed (only the registered handler set is)", () => {
|
|
273
272
|
watcher.start();
|
|
274
|
-
// Per-file watching only registers config.json,
|
|
275
|
-
//
|
|
276
|
-
//
|
|
273
|
+
// Per-file watching only registers config.json, SOUL.md, IDENTITY.md,
|
|
274
|
+
// and ui/theme.json. The whole workspace dir must not be watched
|
|
275
|
+
// either — that was the ENXIO-on-Unix-sockets bug.
|
|
277
276
|
expect(findFileWatch(join(WORKSPACE_DIR, "OTHER.md"))).toBeUndefined();
|
|
278
277
|
expect(findWatcher(WORKSPACE_DIR)).toBeUndefined();
|
|
279
278
|
});
|
|
@@ -286,14 +285,6 @@ describe("ConfigWatcher workspace file handlers", () => {
|
|
|
286
285
|
expect(evictCallCount).toBe(0);
|
|
287
286
|
});
|
|
288
287
|
|
|
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
|
-
|
|
297
288
|
test("config.json change calls refreshConfigFromSources", async () => {
|
|
298
289
|
let refreshCalled = false;
|
|
299
290
|
watcher.refreshConfigFromSources = async () => {
|
|
@@ -341,12 +332,6 @@ describe("ConfigWatcher watcher lifecycle", () => {
|
|
|
341
332
|
expect(findFileWatch(join(WORKSPACE_DIR, "IDENTITY.md"))).toBeDefined();
|
|
342
333
|
});
|
|
343
334
|
|
|
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
|
-
|
|
350
335
|
test("stop cancels pending debounce work, no eviction fires after", async () => {
|
|
351
336
|
watcher.start();
|
|
352
337
|
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.4",
|
|
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.4"),
|
|
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("Generating title...");
|
|
63
63
|
expect(meta.type).toBe("standard");
|
|
64
64
|
expect(meta.channel).toBeNull();
|
|
65
65
|
expect(readdirSync(conversationsDir)).toEqual([expectedDirName]);
|