@vellumai/assistant 0.12.1 → 0.12.2-staging.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 +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ── Mutable state for injected vault ────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
const vaultStore = new Map<string, string>();
|
|
6
|
+
const writtenKeys = new Map<string, string>();
|
|
7
|
+
|
|
8
|
+
// ── Import under test ────────────────────────────────────────────────────────
|
|
9
|
+
//
|
|
10
|
+
// No mock.module — prevents all bun worker contamination. Vault access is
|
|
11
|
+
// injected through _injectVaultAccessors / _resetVaultAccessors instead.
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
getValidServiceAccountToken,
|
|
15
|
+
parseServiceAccountKey,
|
|
16
|
+
_resetServiceAccountMutex,
|
|
17
|
+
_injectVaultAccessors,
|
|
18
|
+
_resetVaultAccessors,
|
|
19
|
+
} = await import("../providers/inference/service-account-token.js");
|
|
20
|
+
|
|
21
|
+
// ── Hooks ────────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vaultStore.clear();
|
|
25
|
+
writtenKeys.clear();
|
|
26
|
+
_resetServiceAccountMutex();
|
|
27
|
+
_injectVaultAccessors(
|
|
28
|
+
async (key: string) => vaultStore.get(key),
|
|
29
|
+
async (key: string, value: string) => {
|
|
30
|
+
vaultStore.set(key, value);
|
|
31
|
+
writtenKeys.set(key, value);
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
_resetVaultAccessors();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ── Real 2048-bit RSA PKCS#8 key generated offline for unit tests only ───────
|
|
42
|
+
|
|
43
|
+
const TEST_PRIVATE_KEY =
|
|
44
|
+
"-----BEGIN PRIVATE KEY-----\n" +
|
|
45
|
+
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDFnEC0wI/cNKyO\n" +
|
|
46
|
+
"XzkqOGAvsTTx4Q+DPjDBPVoy3yHVk7cGYIf5sg3nsrQlE4ARMEQ9MXKUvw+VY59c\n" +
|
|
47
|
+
"zzpy9/JnCN9v5ymZUrowF/87Xn0muklHOhYPFz+73BunncH0Dk0mgCRWEfd/xoGB\n" +
|
|
48
|
+
"bMI40mI2hPMN5aGU4moTmVzTsgvatp0tAk90oaUQBZ/Y3PVjoR+SHbKhQtZW8eiO\n" +
|
|
49
|
+
"zUGyBhzmlmMZcquWxHui8QbO//kEKg7jv2+7npkdW/S0meoDeKZzqRNACKci1CQB\n" +
|
|
50
|
+
"N/skFqy6B8qYekhVYtAaIQOLtuD+VdLL7azMBqIHZSVQQXD6KisN8XSj95k/Fyeb\n" +
|
|
51
|
+
"UEmhvlwrAgMBAAECggEALKr7PWBhpGcLskr4AwEnTOCDlUFIXmRKZQPXRbSGU17G\n" +
|
|
52
|
+
"elyLn/eNiRgt/EuCIWCFrEdnHRRrE1unhDUbgy1kz8GDsCnSj2PRzlk+Sk87fuuQ\n" +
|
|
53
|
+
"8NbCcbrRn0dLwQG5+87XxaIllIcPKQTX9Ud+R5XWuXgn8LY9Zggf7jo2b2vJnjBO\n" +
|
|
54
|
+
"2SIS4M24a8x/1hHLjrTKtyO1yF3TI/9z72z4+yYiXAHxBugCcg6kv27pMEtOl5hv\n" +
|
|
55
|
+
"HW6ogBIDKVIVp3DmjaWxPawy1ytxpI1mR1lfGKoNI3iutLNfuHl3MwinYL7oCiL/\n" +
|
|
56
|
+
"rejuz37rXicFZ40YTKHDqKy+brLS6Ahg0R7XxSNtcQKBgQD7i4oh4stD+geFoPoJ\n" +
|
|
57
|
+
"o/grtOazn2GKuMOrcbBSaTfs4PZfcSRT8Egzhl3Vf/3Z30T3ChugFKkpN9naDEL3\n" +
|
|
58
|
+
"0SEH5sR5oMhGzzE3ncwTPR4fClpiX61JIY6iVL5AQukXIK+v+hCuhRu9YepMbGWq\n" +
|
|
59
|
+
"ndl/uH4LofuEctJYaovnghKedwKBgQDJHC7NiqNhGB6YzwgvYK2GVrFLdPpcfLbl\n" +
|
|
60
|
+
"y5DZKaVYCs3cLFCH7qHEdsmVEWkQ9UkRhxNPGpgQadvYNAd9Bbf2au2lfNuCt0Kn\n" +
|
|
61
|
+
"dYDgkxHAo4PByEznOBgQYoG5IjF8bdubQJbJHQh/bj5EkzeKDggD37ED0fTdWIiJ\n" +
|
|
62
|
+
"Nhh0/q2Y7QKBgGlLMsoWbjGJMqbcCG4KpfJdqghcWe9Agh982mrUwmU0kczHxoYg\n" +
|
|
63
|
+
"WYi/4P1iszcE/5BNOd0baOfpChb89PdZIfN1xJ6JD+ycBCUct3UCK16g7c6p8BZh\n" +
|
|
64
|
+
"ppwKzwyFNZ7JvuDx0xPKgh8XIoVVBoWRx55v8ssZxffePHBoQSHqT7LnAoGAB1vO\n" +
|
|
65
|
+
"opq2C7F0PScmJ4WkE8xMRHSNTKELmDDwpeGOOUB789hzQeYLaULncVrnu0UeXVPR\n" +
|
|
66
|
+
"8w0ID2GwN6L/uyVga9XRyEJfTdMizznPc2guOBxDj+2iUruMtdzE3l9P7Dug03dz\n" +
|
|
67
|
+
"jPxQ+UixYpT4bUbCKFIhCgC+svKwXETsdCt1cqkCgYBVSSabJNSvFfqYz/VJbRK7\n" +
|
|
68
|
+
"cvxEr3mcV/I/+5BoyTmTKQGkIRl6xCMhqZk019hXbdN66jMQ9zg6wni4UKA43cXI\n" +
|
|
69
|
+
"/3NHY86IvdpLORlYqKVHycZsqSVQE6Z3V1G4C2wVKUZtWsoT6Wr+Ind+V+VzbDD2\n" +
|
|
70
|
+
"wHbW4FhxHN9iwdHfmJyq+g==\n" +
|
|
71
|
+
"-----END PRIVATE KEY-----\n";
|
|
72
|
+
|
|
73
|
+
const TEST_SERVICE_ACCOUNT = JSON.stringify({
|
|
74
|
+
type: "service_account",
|
|
75
|
+
client_email: "test@example.com",
|
|
76
|
+
private_key: TEST_PRIVATE_KEY,
|
|
77
|
+
token_uri: "https://oauth2.googleapis.com/token",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
describe("service-account-token", () => {
|
|
83
|
+
// -- parseServiceAccountKey ------------------------------------------------
|
|
84
|
+
|
|
85
|
+
describe("parseServiceAccountKey", () => {
|
|
86
|
+
test("returns null for empty string", () => {
|
|
87
|
+
expect(parseServiceAccountKey("")).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("returns null for invalid JSON", () => {
|
|
91
|
+
expect(parseServiceAccountKey("not-json")).toBeNull();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("returns null when required fields are missing", () => {
|
|
95
|
+
expect(
|
|
96
|
+
parseServiceAccountKey(
|
|
97
|
+
JSON.stringify({ client_email: "test@example.com" }),
|
|
98
|
+
),
|
|
99
|
+
).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("returns the key when all required fields are present", () => {
|
|
103
|
+
const key = parseServiceAccountKey(TEST_SERVICE_ACCOUNT);
|
|
104
|
+
expect(key).not.toBeNull();
|
|
105
|
+
expect(key?.client_email).toBe("test@example.com");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// -- getValidServiceAccountToken -------------------------------------------
|
|
110
|
+
|
|
111
|
+
test("returns not_found when no credential is stored", async () => {
|
|
112
|
+
const result = await getValidServiceAccountToken("credential/vertex-ai");
|
|
113
|
+
expect(result).toEqual({ ok: false, reason: "not_found" });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("returns invalid_config when credential is not valid JSON", async () => {
|
|
117
|
+
vaultStore.set("credential/vertex-ai", "not-json");
|
|
118
|
+
const result = await getValidServiceAccountToken("credential/vertex-ai");
|
|
119
|
+
expect(result).toEqual({ ok: false, reason: "invalid_config" });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("returns invalid_config when credential JSON is missing required fields", async () => {
|
|
123
|
+
vaultStore.set(
|
|
124
|
+
"credential/vertex-ai",
|
|
125
|
+
JSON.stringify({ client_email: "missing-other-fields@example.com" }),
|
|
126
|
+
);
|
|
127
|
+
const result = await getValidServiceAccountToken("credential/vertex-ai");
|
|
128
|
+
expect(result).toEqual({ ok: false, reason: "invalid_config" });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("returns cached token when the cache blob is still fresh", async () => {
|
|
132
|
+
const futureExpiry = Math.floor(Date.now() / 1000) + 7200;
|
|
133
|
+
vaultStore.set("credential/vertex-ai", TEST_SERVICE_ACCOUNT);
|
|
134
|
+
vaultStore.set(
|
|
135
|
+
"credential/vertex-ai/token_cache",
|
|
136
|
+
JSON.stringify({
|
|
137
|
+
access_token: "cached-token",
|
|
138
|
+
expires_at: futureExpiry,
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const result = await getValidServiceAccountToken("credential/vertex-ai");
|
|
143
|
+
expect(result).toEqual({ ok: true, token: "cached-token" });
|
|
144
|
+
// Nothing new should have been written (no exchange performed).
|
|
145
|
+
expect(writtenKeys.size).toBe(0);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("returns cached token when cache blob has no expiry", async () => {
|
|
149
|
+
vaultStore.set("credential/vertex-ai", TEST_SERVICE_ACCOUNT);
|
|
150
|
+
vaultStore.set(
|
|
151
|
+
"credential/vertex-ai/token_cache",
|
|
152
|
+
JSON.stringify({ access_token: "no-expiry-token" }),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const result = await getValidServiceAccountToken("credential/vertex-ai");
|
|
156
|
+
expect(result).toEqual({ ok: true, token: "no-expiry-token" });
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("coalesces concurrent callers for the same credential onto one exchange", async () => {
|
|
160
|
+
const pastExpiry = Math.floor(Date.now() / 1000) - 600;
|
|
161
|
+
vaultStore.set("credential/vertex-ai", TEST_SERVICE_ACCOUNT);
|
|
162
|
+
vaultStore.set(
|
|
163
|
+
"credential/vertex-ai/token_cache",
|
|
164
|
+
JSON.stringify({ access_token: "stale-token", expires_at: pastExpiry }),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
let fetchCallCount = 0;
|
|
168
|
+
const originalFetch = globalThis.fetch;
|
|
169
|
+
globalThis.fetch = (async () => {
|
|
170
|
+
fetchCallCount += 1;
|
|
171
|
+
return {
|
|
172
|
+
ok: true,
|
|
173
|
+
json: async () => ({ access_token: "new-token", expires_in: 3600 }),
|
|
174
|
+
} as Response;
|
|
175
|
+
}) as unknown as typeof fetch;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const [r1, r2] = await Promise.all([
|
|
179
|
+
getValidServiceAccountToken("credential/vertex-ai"),
|
|
180
|
+
getValidServiceAccountToken("credential/vertex-ai"),
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
expect(r1).toEqual({ ok: true, token: "new-token" });
|
|
184
|
+
expect(r2).toEqual({ ok: true, token: "new-token" });
|
|
185
|
+
expect(fetchCallCount).toBe(1);
|
|
186
|
+
} finally {
|
|
187
|
+
globalThis.fetch = originalFetch;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("concurrent callers for different credentials exchange independently", async () => {
|
|
192
|
+
vaultStore.set("credential/vertex-a", TEST_SERVICE_ACCOUNT);
|
|
193
|
+
vaultStore.set("credential/vertex-b", TEST_SERVICE_ACCOUNT);
|
|
194
|
+
|
|
195
|
+
let fetchCallCount = 0;
|
|
196
|
+
const originalFetch = globalThis.fetch;
|
|
197
|
+
globalThis.fetch = (async () => {
|
|
198
|
+
fetchCallCount += 1;
|
|
199
|
+
return {
|
|
200
|
+
ok: true,
|
|
201
|
+
json: async () => ({
|
|
202
|
+
access_token: `token-${fetchCallCount}`,
|
|
203
|
+
expires_in: 3600,
|
|
204
|
+
}),
|
|
205
|
+
} as Response;
|
|
206
|
+
}) as unknown as typeof fetch;
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
const [r1, r2] = await Promise.all([
|
|
210
|
+
getValidServiceAccountToken("credential/vertex-a"),
|
|
211
|
+
getValidServiceAccountToken("credential/vertex-b"),
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
expect(r1.ok).toBe(true);
|
|
215
|
+
expect(r2.ok).toBe(true);
|
|
216
|
+
// Each credential triggers its own exchange.
|
|
217
|
+
expect(fetchCallCount).toBe(2);
|
|
218
|
+
} finally {
|
|
219
|
+
globalThis.fetch = originalFetch;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("caches token+expiry as a single JSON blob", async () => {
|
|
224
|
+
vaultStore.set("credential/vertex-ai", TEST_SERVICE_ACCOUNT);
|
|
225
|
+
|
|
226
|
+
const originalFetch = globalThis.fetch;
|
|
227
|
+
globalThis.fetch = (async () => ({
|
|
228
|
+
ok: true,
|
|
229
|
+
json: async () => ({ access_token: "fresh-token", expires_in: 3600 }),
|
|
230
|
+
})) as unknown as typeof fetch;
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
await getValidServiceAccountToken("credential/vertex-ai");
|
|
234
|
+
|
|
235
|
+
const blob = vaultStore.get("credential/vertex-ai/token_cache");
|
|
236
|
+
expect(blob).toBeDefined();
|
|
237
|
+
const parsed = JSON.parse(blob!) as {
|
|
238
|
+
access_token: string;
|
|
239
|
+
expires_at: number;
|
|
240
|
+
};
|
|
241
|
+
expect(parsed.access_token).toBe("fresh-token");
|
|
242
|
+
expect(typeof parsed.expires_at).toBe("number");
|
|
243
|
+
} finally {
|
|
244
|
+
globalThis.fetch = originalFetch;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
});
|
|
@@ -60,7 +60,7 @@ function makeConfig(overrides: Partial<AssistantConfig> = {}): AssistantConfig {
|
|
|
60
60
|
return {
|
|
61
61
|
skills: {
|
|
62
62
|
entries: {},
|
|
63
|
-
load: { extraDirs: []
|
|
63
|
+
load: { extraDirs: [] },
|
|
64
64
|
install: { nodeManager: "npm" },
|
|
65
65
|
allowBundled: null,
|
|
66
66
|
remoteProviders: {
|
|
@@ -28,7 +28,7 @@ function makeConfig(overrides: Partial<AssistantConfig> = {}): AssistantConfig {
|
|
|
28
28
|
return {
|
|
29
29
|
skills: {
|
|
30
30
|
entries: {},
|
|
31
|
-
load: { extraDirs: []
|
|
31
|
+
load: { extraDirs: [] },
|
|
32
32
|
install: { nodeManager: "npm" },
|
|
33
33
|
allowBundled: null,
|
|
34
34
|
remoteProviders: {
|
|
@@ -233,7 +233,7 @@ describe("resolveSkillStates with feature flags", () => {
|
|
|
233
233
|
const config = makeConfig({
|
|
234
234
|
skills: {
|
|
235
235
|
entries: { [DECLARED_SKILL_ID]: { enabled: true } },
|
|
236
|
-
load: { extraDirs: []
|
|
236
|
+
load: { extraDirs: [] },
|
|
237
237
|
install: { nodeManager: "npm" },
|
|
238
238
|
allowBundled: null,
|
|
239
239
|
remoteProviders: {
|
|
@@ -875,7 +875,9 @@ describe("always-candidate frontmatter parsing", () => {
|
|
|
875
875
|
// drops trailing hints and the whole avoid-when list.
|
|
876
876
|
const { DEFAULT_CARD_CHARS, buildSkillContent } =
|
|
877
877
|
await import("../plugins/defaults/memory/substrate/skill-content.js");
|
|
878
|
-
const schedule = loadSkillCatalog().find(
|
|
878
|
+
const schedule = loadSkillCatalog().find(
|
|
879
|
+
(skill) => skill.id === "schedule",
|
|
880
|
+
);
|
|
879
881
|
expect(schedule).toBeDefined();
|
|
880
882
|
|
|
881
883
|
const card = buildSkillContent(schedule!);
|
|
@@ -886,7 +888,9 @@ describe("always-candidate frontmatter parsing", () => {
|
|
|
886
888
|
test("the bundled schedule card carries its monitoring vocabulary", async () => {
|
|
887
889
|
const { buildSkillContent } =
|
|
888
890
|
await import("../plugins/defaults/memory/substrate/skill-content.js");
|
|
889
|
-
const schedule = loadSkillCatalog().find(
|
|
891
|
+
const schedule = loadSkillCatalog().find(
|
|
892
|
+
(skill) => skill.id === "schedule",
|
|
893
|
+
);
|
|
890
894
|
const card = buildSkillContent(schedule!).toLowerCase();
|
|
891
895
|
|
|
892
896
|
for (const term of ["monitor", "page", "dashboard", "status", "alert"]) {
|
|
@@ -1087,14 +1091,14 @@ describe("bundled computer-use skill", () => {
|
|
|
1087
1091
|
expect(cuSkill!.bundled).toBe(true);
|
|
1088
1092
|
});
|
|
1089
1093
|
|
|
1090
|
-
test("computer-use skill has a valid tool manifest with
|
|
1094
|
+
test("computer-use skill has a valid tool manifest with 12 tools", () => {
|
|
1091
1095
|
const catalog = loadSkillCatalog();
|
|
1092
1096
|
const cuSkill = catalog.find((s) => s.id === "computer-use");
|
|
1093
1097
|
expect(cuSkill).toBeDefined();
|
|
1094
1098
|
expect(cuSkill!.toolManifest).toBeDefined();
|
|
1095
1099
|
expect(cuSkill!.toolManifest!.present).toBe(true);
|
|
1096
1100
|
expect(cuSkill!.toolManifest!.valid).toBe(true);
|
|
1097
|
-
expect(cuSkill!.toolManifest!.toolCount).toBe(
|
|
1101
|
+
expect(cuSkill!.toolManifest!.toolCount).toBe(12);
|
|
1098
1102
|
expect(cuSkill!.toolManifest!.toolNames).toEqual([
|
|
1099
1103
|
"computer_use_observe",
|
|
1100
1104
|
"computer_use_click",
|
|
@@ -1105,6 +1109,7 @@ describe("bundled computer-use skill", () => {
|
|
|
1105
1109
|
"computer_use_wait",
|
|
1106
1110
|
"computer_use_open_app",
|
|
1107
1111
|
"computer_use_run_applescript",
|
|
1112
|
+
"computer_use_sequence",
|
|
1108
1113
|
"computer_use_done",
|
|
1109
1114
|
"computer_use_respond",
|
|
1110
1115
|
]);
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for app source fingerprinting: walk exclusions, comparison, inspect
|
|
3
|
+
* statuses, and the compile-time sidecar format.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
mkdirSync,
|
|
8
|
+
mkdtempSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
symlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
compareAppSourceFingerprint,
|
|
20
|
+
computeAppSourceFingerprint,
|
|
21
|
+
inspectAppSource,
|
|
22
|
+
parseAppSourceFingerprint,
|
|
23
|
+
SOURCE_FINGERPRINT_FILENAME,
|
|
24
|
+
writeAppSourceFingerprint,
|
|
25
|
+
} from "../apps/source-fingerprint.js";
|
|
26
|
+
|
|
27
|
+
let root: string;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
root = mkdtempSync(join(tmpdir(), "app-source-fingerprint-"));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
rmSync(root, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function write(rel: string, contents: string): void {
|
|
38
|
+
const abs = join(root, rel);
|
|
39
|
+
mkdirSync(join(abs, ".."), { recursive: true });
|
|
40
|
+
writeFileSync(abs, contents);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("computeAppSourceFingerprint", () => {
|
|
44
|
+
test("digests source files with POSIX-relative keys", () => {
|
|
45
|
+
write("src/main.tsx", "export const x = 1;");
|
|
46
|
+
write("src/index.html", "<html></html>");
|
|
47
|
+
|
|
48
|
+
const fp = computeAppSourceFingerprint(root);
|
|
49
|
+
|
|
50
|
+
expect(Object.keys(fp.files).sort()).toEqual([
|
|
51
|
+
"src/index.html",
|
|
52
|
+
"src/main.tsx",
|
|
53
|
+
]);
|
|
54
|
+
expect(fp.algorithm).toBe("sha256");
|
|
55
|
+
expect(fp.files["src/main.tsx"]).toMatch(/^[0-9a-f]{64}$/);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("skips dist, records, node_modules, and .git", () => {
|
|
59
|
+
write("src/main.tsx", "src");
|
|
60
|
+
write("dist/main.js", "compiled");
|
|
61
|
+
write("records/rec-1.json", "{}");
|
|
62
|
+
write("node_modules/pkg/index.js", "dep");
|
|
63
|
+
write("src/node_modules/nested/index.js", "nested");
|
|
64
|
+
write(".git/HEAD", "ref");
|
|
65
|
+
write("src/.git/config", "nope");
|
|
66
|
+
|
|
67
|
+
const fp = computeAppSourceFingerprint(root);
|
|
68
|
+
|
|
69
|
+
expect(Object.keys(fp.files)).toEqual(["src/main.tsx"]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("skips symlinks", () => {
|
|
73
|
+
write("src/real.tsx", "hello");
|
|
74
|
+
mkdirSync(join(root, "src"), { recursive: true });
|
|
75
|
+
symlinkSync(join(root, "src/real.tsx"), join(root, "src/link.tsx"));
|
|
76
|
+
|
|
77
|
+
const fp = computeAppSourceFingerprint(root);
|
|
78
|
+
|
|
79
|
+
expect(Object.keys(fp.files)).toEqual(["src/real.tsx"]);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("compareAppSourceFingerprint", () => {
|
|
84
|
+
test("reports modified, added, and removed paths", () => {
|
|
85
|
+
write("src/main.tsx", "one");
|
|
86
|
+
write("src/keep.tsx", "keep");
|
|
87
|
+
const baseline = computeAppSourceFingerprint(root);
|
|
88
|
+
|
|
89
|
+
write("src/main.tsx", "two");
|
|
90
|
+
write("src/new.tsx", "new");
|
|
91
|
+
rmSync(join(root, "src/keep.tsx"));
|
|
92
|
+
|
|
93
|
+
const diff = compareAppSourceFingerprint(root, baseline);
|
|
94
|
+
expect(diff.clean).toBe(false);
|
|
95
|
+
expect(diff.modified).toEqual(["src/main.tsx"]);
|
|
96
|
+
expect(diff.added).toEqual(["src/new.tsx"]);
|
|
97
|
+
expect(diff.removed).toEqual(["src/keep.tsx"]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("is clean when source matches the baseline", () => {
|
|
101
|
+
write("src/main.tsx", "same");
|
|
102
|
+
const baseline = computeAppSourceFingerprint(root);
|
|
103
|
+
const diff = compareAppSourceFingerprint(root, baseline);
|
|
104
|
+
expect(diff.clean).toBe(true);
|
|
105
|
+
expect(diff.modified).toEqual([]);
|
|
106
|
+
expect(diff.added).toEqual([]);
|
|
107
|
+
expect(diff.removed).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe("inspectAppSource", () => {
|
|
112
|
+
test("reports never_compiled when dist index is missing even if a fingerprint exists", () => {
|
|
113
|
+
write("src/main.tsx", "src");
|
|
114
|
+
mkdirSync(join(root, "dist"), { recursive: true });
|
|
115
|
+
writeAppSourceFingerprint(root, join(root, "dist"));
|
|
116
|
+
|
|
117
|
+
const result = inspectAppSource(root);
|
|
118
|
+
expect(result.status).toBe("never_compiled");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("reports unknown_baseline when dist exists without a fingerprint", () => {
|
|
122
|
+
write("src/main.tsx", "src");
|
|
123
|
+
write("dist/index.html", "<html></html>");
|
|
124
|
+
const result = inspectAppSource(root);
|
|
125
|
+
expect(result.status).toBe("unknown_baseline");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("reports clean after writing a fingerprint that matches source", () => {
|
|
129
|
+
write("src/main.tsx", "src");
|
|
130
|
+
mkdirSync(join(root, "dist"), { recursive: true });
|
|
131
|
+
writeFileSync(join(root, "dist", "index.html"), "<html></html>");
|
|
132
|
+
writeAppSourceFingerprint(root, join(root, "dist"));
|
|
133
|
+
|
|
134
|
+
const result = inspectAppSource(root);
|
|
135
|
+
expect(result.status).toBe("clean");
|
|
136
|
+
expect(result.compiledAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("reports stale after a source edit", () => {
|
|
140
|
+
write("src/main.tsx", "before");
|
|
141
|
+
mkdirSync(join(root, "dist"), { recursive: true });
|
|
142
|
+
writeFileSync(join(root, "dist", "index.html"), "<html></html>");
|
|
143
|
+
writeAppSourceFingerprint(root, join(root, "dist"));
|
|
144
|
+
write("src/main.tsx", "after");
|
|
145
|
+
|
|
146
|
+
const result = inspectAppSource(root);
|
|
147
|
+
expect(result.status).toBe("stale");
|
|
148
|
+
expect(result.modified).toEqual(["src/main.tsx"]);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("parseAppSourceFingerprint", () => {
|
|
153
|
+
test("accepts a compile sidecar with compiledAt", () => {
|
|
154
|
+
const parsed = parseAppSourceFingerprint({
|
|
155
|
+
algorithm: "sha256",
|
|
156
|
+
compiledAt: "2026-09-09T00:00:00.000Z",
|
|
157
|
+
files: { "src/main.tsx": "abc" },
|
|
158
|
+
});
|
|
159
|
+
expect(parsed).toEqual({
|
|
160
|
+
algorithm: "sha256",
|
|
161
|
+
compiledAt: "2026-09-09T00:00:00.000Z",
|
|
162
|
+
files: { "src/main.tsx": "abc" },
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("returns null for an invalid sidecar", () => {
|
|
167
|
+
expect(parseAppSourceFingerprint({ algorithm: "md5", files: {} })).toBe(
|
|
168
|
+
null,
|
|
169
|
+
);
|
|
170
|
+
expect(parseAppSourceFingerprint(null)).toBeNull();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("writeAppSourceFingerprint records a sidecar the parser accepts", () => {
|
|
174
|
+
write("src/main.tsx", "src");
|
|
175
|
+
mkdirSync(join(root, "dist"), { recursive: true });
|
|
176
|
+
writeAppSourceFingerprint(root, join(root, "dist"));
|
|
177
|
+
const raw = JSON.parse(
|
|
178
|
+
readFileSync(join(root, "dist", SOURCE_FINGERPRINT_FILENAME), "utf-8"),
|
|
179
|
+
);
|
|
180
|
+
expect(parseAppSourceFingerprint(raw)?.files["src/main.tsx"]).toMatch(
|
|
181
|
+
/^[0-9a-f]{64}$/,
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -249,14 +249,23 @@ describe("createResolveToolsCallback — toolContextPin", () => {
|
|
|
249
249
|
});
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
test("control: without a pin, a clientless fork drops
|
|
252
|
+
test("control: without a pin, a clientless fork drops client-gated tools but keeps host and UI tools", () => {
|
|
253
253
|
projectedSkillToolNames = [];
|
|
254
254
|
const resolve = createResolveToolsCallback(
|
|
255
255
|
CLIENT_GATED_DEFS,
|
|
256
256
|
clientlessExecutionCtx(),
|
|
257
257
|
)!;
|
|
258
258
|
|
|
259
|
-
|
|
259
|
+
// Host tool definitions stay on the wire for background turns, and
|
|
260
|
+
// ui_show stays because background UI surfaces persist and return. The
|
|
261
|
+
// executor rejects host tools on explicitly non-interactive turns before
|
|
262
|
+
// dispatch; ask_question and request_system_permission remain
|
|
263
|
+
// client-gated at resolution.
|
|
264
|
+
expect(resolve(EMPTY_HISTORY).map((t) => t.name)).toEqual([
|
|
265
|
+
"remember",
|
|
266
|
+
"host_bash",
|
|
267
|
+
"ui_show",
|
|
268
|
+
]);
|
|
260
269
|
});
|
|
261
270
|
|
|
262
271
|
test("a desktop-source pin restores the host/UI/client tool defs on the wire", () => {
|
|
@@ -291,7 +300,14 @@ describe("createResolveToolsCallback — toolContextPin", () => {
|
|
|
291
300
|
}),
|
|
292
301
|
)!;
|
|
293
302
|
|
|
294
|
-
|
|
303
|
+
// Host tool definitions stay on the wire for background turns, and
|
|
304
|
+
// ui_show survives the clientless pin: it persists and returns. The
|
|
305
|
+
// executor rejects host tools on explicitly non-interactive turns.
|
|
306
|
+
expect(resolve(EMPTY_HISTORY).map((t) => t.name)).toEqual([
|
|
307
|
+
"remember",
|
|
308
|
+
"host_bash",
|
|
309
|
+
"ui_show",
|
|
310
|
+
]);
|
|
295
311
|
});
|
|
296
312
|
|
|
297
313
|
test("invariant: a pinned-in tool is on the wire but can never execute", async () => {
|
|
@@ -3447,8 +3447,9 @@ describe("Subagent advisor-role consult", () => {
|
|
|
3447
3447
|
});
|
|
3448
3448
|
|
|
3449
3449
|
test("advisor consults still run when the parent conversation is gone", async () => {
|
|
3450
|
-
//
|
|
3451
|
-
//
|
|
3450
|
+
// Advisor consults do not look up the parent conversation for a skill
|
|
3451
|
+
// catalog snapshot. An evicted parent still gets a consult; the context
|
|
3452
|
+
// pack loads the catalog from disk independently.
|
|
3452
3453
|
mockFindConversation = () => undefined;
|
|
3453
3454
|
const { captured, restore } = stubSpawn();
|
|
3454
3455
|
try {
|
|
@@ -455,10 +455,11 @@ describe("resolveCallTtsProvider with requiresPcmAudio", () => {
|
|
|
455
455
|
expect(result.provider?.id).toBe("elevenlabs");
|
|
456
456
|
});
|
|
457
457
|
|
|
458
|
-
test("without requiresPcmAudio the configured provider is not swapped", async () => {
|
|
458
|
+
test("without requiresPcmAudio the configured provider is not swapped for playability", async () => {
|
|
459
459
|
testConfig.services.tts.provider = "deepgram";
|
|
460
|
-
|
|
461
|
-
// media-stream playability
|
|
460
|
+
storedKeys["deepgram"] = "dg-key";
|
|
461
|
+
// The CR transport path does not consult the media-stream playability
|
|
462
|
+
// capability.
|
|
462
463
|
registerStubProvider("deepgram", async () => {
|
|
463
464
|
return { audio: Buffer.from("x"), contentType: "audio/mpeg" };
|
|
464
465
|
});
|
|
@@ -468,6 +469,37 @@ describe("resolveCallTtsProvider with requiresPcmAudio", () => {
|
|
|
468
469
|
expect(result.useSynthesizedPath).toBe(true);
|
|
469
470
|
expect(result.audioFormat).toBe("mp3");
|
|
470
471
|
});
|
|
472
|
+
|
|
473
|
+
test("managed speech stands in for a configured provider whose credential is missing", async () => {
|
|
474
|
+
testConfig.services.tts.provider = "deepgram";
|
|
475
|
+
// No deepgram key, managed speech available: the phone speaks through
|
|
476
|
+
// managed speech, the same substitution live voice makes.
|
|
477
|
+
registerStubProvider("deepgram", async () => {
|
|
478
|
+
throw new Error("should not be used");
|
|
479
|
+
});
|
|
480
|
+
registerStubProvider("vellum", async () => {
|
|
481
|
+
return { audio: Buffer.from("x"), contentType: "audio/mpeg" };
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
const result = await resolveCallTtsProvider();
|
|
485
|
+
expect(result.provider?.id).toBe("vellum");
|
|
486
|
+
expect(result.useSynthesizedPath).toBe(true);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("without managed speech a credential-less configured provider is kept", async () => {
|
|
490
|
+
mockManagedSpeechAvailable = false;
|
|
491
|
+
try {
|
|
492
|
+
testConfig.services.tts.provider = "deepgram";
|
|
493
|
+
registerStubProvider("deepgram", async () => {
|
|
494
|
+
return { audio: Buffer.from("x"), contentType: "audio/mpeg" };
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const result = await resolveCallTtsProvider();
|
|
498
|
+
expect(result.provider?.id).toBe("deepgram");
|
|
499
|
+
} finally {
|
|
500
|
+
mockManagedSpeechAvailable = true;
|
|
501
|
+
}
|
|
502
|
+
});
|
|
471
503
|
});
|
|
472
504
|
|
|
473
505
|
// ---------------------------------------------------------------------------
|
|
@@ -584,6 +616,7 @@ describe("speakSystemPrompt mid-stream failure after audio started", () => {
|
|
|
584
616
|
test("non-PCM transport: skips the native text fallback — no full-prompt re-speak", async () => {
|
|
585
617
|
testConfig.services.tts.provider = "fish-audio";
|
|
586
618
|
testConfig.services.tts.providers["fish-audio"].referenceId = "ref-123";
|
|
619
|
+
storedKeys["fish-audio"] = "fa-key";
|
|
587
620
|
|
|
588
621
|
const fishStream = jest.fn(failAfterFirstChunk());
|
|
589
622
|
registerStreamingStubProvider("fish-audio", fishStream);
|
|
@@ -657,6 +690,7 @@ describe("speakSystemPrompt aborted synthesis", () => {
|
|
|
657
690
|
test("non-PCM transport with native-fallback provider: abort skips the text fallback and end-of-turn", async () => {
|
|
658
691
|
testConfig.services.tts.provider = "fish-audio";
|
|
659
692
|
testConfig.services.tts.providers["fish-audio"].referenceId = "ref-123";
|
|
693
|
+
storedKeys["fish-audio"] = "fa-key";
|
|
660
694
|
|
|
661
695
|
const controller = new AbortController();
|
|
662
696
|
controller.abort();
|
|
@@ -742,6 +776,7 @@ describe("speakSystemPrompt aborted synthesis", () => {
|
|
|
742
776
|
test("provider AbortError without any signal is a failure — non-PCM native fallback still runs", async () => {
|
|
743
777
|
testConfig.services.tts.provider = "fish-audio";
|
|
744
778
|
testConfig.services.tts.providers["fish-audio"].referenceId = "ref-123";
|
|
779
|
+
storedKeys["fish-audio"] = "fa-key";
|
|
745
780
|
|
|
746
781
|
const fishSynthesize = jest.fn(async () => {
|
|
747
782
|
throw new DOMException("socket torn down", "AbortError");
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Reusable constants and helpers for the computer-use skill migration test suite.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
/** The
|
|
5
|
+
/** The 12 computer_use_* action tool names provided by the bundled computer-use skill. */
|
|
6
6
|
export const COMPUTER_USE_TOOL_NAMES = [
|
|
7
7
|
"computer_use_observe",
|
|
8
8
|
"computer_use_click",
|
|
@@ -13,9 +13,10 @@ export const COMPUTER_USE_TOOL_NAMES = [
|
|
|
13
13
|
"computer_use_wait",
|
|
14
14
|
"computer_use_open_app",
|
|
15
15
|
"computer_use_run_applescript",
|
|
16
|
+
"computer_use_sequence",
|
|
16
17
|
"computer_use_done",
|
|
17
18
|
"computer_use_respond",
|
|
18
19
|
] as const;
|
|
19
20
|
|
|
20
21
|
/** Number of computer_use_* tools. */
|
|
21
|
-
export const COMPUTER_USE_TOOL_COUNT = COMPUTER_USE_TOOL_NAMES.length; //
|
|
22
|
+
export const COMPUTER_USE_TOOL_COUNT = COMPUTER_USE_TOOL_NAMES.length; // 12
|