@vellumai/assistant 0.6.5 → 0.6.6
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 +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User plugin loader — discovers plugins under `~/.vellum/plugins/*` and
|
|
3
|
+
* invokes each plugin's registration side effect via a dynamic import.
|
|
4
|
+
*
|
|
5
|
+
* A user plugin is a directory under `vellumRoot()/plugins/` that contains a
|
|
6
|
+
* `register.ts` (or `register.js` after compilation). The file is expected to
|
|
7
|
+
* call {@link registerPlugin} at import time so the plugin ends up in the
|
|
8
|
+
* registry before {@link bootstrapPlugins} runs during daemon startup.
|
|
9
|
+
*
|
|
10
|
+
* The loader deliberately:
|
|
11
|
+
*
|
|
12
|
+
* - Uses {@link vellumRoot} rather than `homedir()` directly so the
|
|
13
|
+
* multi-instance invariant in the root CLAUDE.md holds — each instance
|
|
14
|
+
* loads its own plugin set from its own `.vellum` directory.
|
|
15
|
+
* - Prefers `register.js` over `register.ts` when both exist (compiled plugins
|
|
16
|
+
* always win; this matches how `bun`/Node consumers resolve modules at
|
|
17
|
+
* runtime in the compiled binary).
|
|
18
|
+
* - Treats any error from the dynamic import as a per-plugin isolation
|
|
19
|
+
* boundary: the offending directory is logged with `"Failed to load user
|
|
20
|
+
* plugin <dir>: <err>"` and the loader moves on to the next candidate.
|
|
21
|
+
* One bad user plugin must not crash the daemon.
|
|
22
|
+
* - Bounds each dynamic import with a timeout
|
|
23
|
+
* ({@link USER_PLUGIN_IMPORT_TIMEOUT_MS}) so a plugin whose top-level
|
|
24
|
+
* `await` hangs or whose module evaluation never resolves cannot stall
|
|
25
|
+
* daemon startup. Timed-out plugins are logged and skipped just like
|
|
26
|
+
* thrown-error plugins.
|
|
27
|
+
*
|
|
28
|
+
* Call order relative to the rest of the plugin system:
|
|
29
|
+
*
|
|
30
|
+
* first-party registrations (static side-effect imports)
|
|
31
|
+
* → loadUserPlugins() ← this module
|
|
32
|
+
* → bootstrapPlugins() (init for everyone registered so far)
|
|
33
|
+
*
|
|
34
|
+
* Design doc: `.private/plans/agent-plugin-system.md` (PR 29).
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
38
|
+
import { join } from "node:path";
|
|
39
|
+
import { pathToFileURL } from "node:url";
|
|
40
|
+
|
|
41
|
+
import { getLogger } from "../util/logger.js";
|
|
42
|
+
import { vellumRoot } from "../util/platform.js";
|
|
43
|
+
|
|
44
|
+
const log = getLogger("user-plugin-loader");
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Upper bound on how long a single user plugin's dynamic `import()` may take.
|
|
48
|
+
* A plugin with a hanging top-level `await` (or a never-resolving module
|
|
49
|
+
* evaluation) would otherwise block daemon startup indefinitely, since a raw
|
|
50
|
+
* `try/catch` only isolates thrown errors — not hung promises. Ten seconds is
|
|
51
|
+
* generous relative to a typical side-effect registration (milliseconds) and
|
|
52
|
+
* matches the per-plugin isolation contract: slow plugins get skipped the
|
|
53
|
+
* same way thrown-error plugins do.
|
|
54
|
+
*/
|
|
55
|
+
const USER_PLUGIN_IMPORT_TIMEOUT_MS = 10_000;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Scan `vellumRoot()/plugins/` for subdirectories containing a
|
|
59
|
+
* `register.{ts,js}` file, and dynamic-import each one so the module's
|
|
60
|
+
* side-effecting {@link registerPlugin} calls populate the registry.
|
|
61
|
+
*
|
|
62
|
+
* Invariants:
|
|
63
|
+
*
|
|
64
|
+
* - No-ops when `vellumRoot()/plugins/` does not exist — a clean install with
|
|
65
|
+
* zero user plugins must not generate errors.
|
|
66
|
+
* - Per-plugin isolation: a failing import is logged and skipped. The
|
|
67
|
+
* function resolves normally even when every plugin fails to load.
|
|
68
|
+
* - Does not return plugin instances. The registry is the single source of
|
|
69
|
+
* truth for who got registered, and the caller inspects it directly.
|
|
70
|
+
*
|
|
71
|
+
* Must be called after first-party plugin side-effect imports have run and
|
|
72
|
+
* before {@link bootstrapPlugins} — see the module docstring for the ordering
|
|
73
|
+
* contract.
|
|
74
|
+
*/
|
|
75
|
+
export async function loadUserPlugins(
|
|
76
|
+
options: { importTimeoutMs?: number } = {},
|
|
77
|
+
): Promise<void> {
|
|
78
|
+
const importTimeoutMs =
|
|
79
|
+
options.importTimeoutMs ?? USER_PLUGIN_IMPORT_TIMEOUT_MS;
|
|
80
|
+
const pluginsDir = join(vellumRoot(), "plugins");
|
|
81
|
+
if (!existsSync(pluginsDir)) {
|
|
82
|
+
log.debug(
|
|
83
|
+
{ pluginsDir },
|
|
84
|
+
"loadUserPlugins: no plugins directory — skipping",
|
|
85
|
+
);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let entries: string[];
|
|
90
|
+
try {
|
|
91
|
+
entries = readdirSync(pluginsDir);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
// Permissions error, transient FS issue, etc. Log and bail without
|
|
94
|
+
// crashing startup — the daemon must come up even when the plugins dir
|
|
95
|
+
// is unreadable.
|
|
96
|
+
log.warn(
|
|
97
|
+
{ err, pluginsDir },
|
|
98
|
+
"loadUserPlugins: failed to read plugins directory",
|
|
99
|
+
);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
const pluginDir = join(pluginsDir, entry);
|
|
105
|
+
|
|
106
|
+
// Only directories are candidates. Plain files (readmes, stray configs)
|
|
107
|
+
// are silently ignored.
|
|
108
|
+
let stats;
|
|
109
|
+
try {
|
|
110
|
+
stats = statSync(pluginDir);
|
|
111
|
+
} catch {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!stats.isDirectory()) continue;
|
|
115
|
+
|
|
116
|
+
// Prefer the compiled `register.js` over the TypeScript source. In the
|
|
117
|
+
// bun-compiled daemon binary only the compiled file can be imported;
|
|
118
|
+
// in development both may exist, in which case resolving the compiled
|
|
119
|
+
// artifact matches how the runtime would behave in production.
|
|
120
|
+
const jsPath = join(pluginDir, "register.js");
|
|
121
|
+
const tsPath = join(pluginDir, "register.ts");
|
|
122
|
+
let registerPath: string | undefined;
|
|
123
|
+
if (existsSync(jsPath)) {
|
|
124
|
+
registerPath = jsPath;
|
|
125
|
+
} else if (existsSync(tsPath)) {
|
|
126
|
+
registerPath = tsPath;
|
|
127
|
+
}
|
|
128
|
+
if (!registerPath) {
|
|
129
|
+
log.debug(
|
|
130
|
+
{ pluginDir },
|
|
131
|
+
"loadUserPlugins: no register.{ts,js} — skipping",
|
|
132
|
+
);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// `import()` with a `file://` URL works identically under Node and bun
|
|
137
|
+
// and sidesteps platform-specific absolute-path quirks on Windows.
|
|
138
|
+
const moduleUrl = pathToFileURL(registerPath).href;
|
|
139
|
+
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
140
|
+
try {
|
|
141
|
+
// Race the import against a timeout so a plugin with a hanging top-level
|
|
142
|
+
// await or never-resolving module evaluation cannot stall daemon startup.
|
|
143
|
+
// The per-plugin try/catch already handles thrown errors; this extends
|
|
144
|
+
// the isolation boundary to cover hung promises as well.
|
|
145
|
+
const timeoutSentinel = Symbol("user-plugin-import-timeout");
|
|
146
|
+
const timeoutPromise = new Promise<typeof timeoutSentinel>((resolve) => {
|
|
147
|
+
timeoutHandle = setTimeout(
|
|
148
|
+
() => resolve(timeoutSentinel),
|
|
149
|
+
importTimeoutMs,
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
const result = await Promise.race([import(moduleUrl), timeoutPromise]);
|
|
153
|
+
if (result === timeoutSentinel) {
|
|
154
|
+
log.warn(
|
|
155
|
+
{ pluginDir, registerPath, timeoutMs: importTimeoutMs },
|
|
156
|
+
`Timed out loading user plugin ${pluginDir} after ${importTimeoutMs}ms — skipping`,
|
|
157
|
+
);
|
|
158
|
+
} else {
|
|
159
|
+
log.info(
|
|
160
|
+
{ pluginDir, registerPath },
|
|
161
|
+
"loaded user plugin (side-effect import completed)",
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
} catch (err) {
|
|
165
|
+
// One plugin's failure must never prevent other plugins from loading
|
|
166
|
+
// or crash the daemon. Log with the directory name so operators can
|
|
167
|
+
// find the broken plugin quickly.
|
|
168
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
169
|
+
log.error(
|
|
170
|
+
{ err, pluginDir },
|
|
171
|
+
`Failed to load user plugin ${pluginDir}: ${message}`,
|
|
172
|
+
);
|
|
173
|
+
} finally {
|
|
174
|
+
if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -2,106 +2,56 @@ _ Lines starting with _ are comments. They won't appear in the system prompt
|
|
|
2
2
|
|
|
3
3
|
# BOOTSTRAP.md — First Conversation
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
One conversation, one shot. You have a voice and a first impression to make. Delete this file when you're done.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## The principle
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Every turn earns its keep. Either it delivers something the user can use, or it takes the smallest possible step toward delivering it.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Before asking a question, check whether you actually can't proceed without the answer. Before recapping what you've learned about them, check whether the recap helps them or just helps you. Before offering a new capability, check whether it's the obvious next step for the problem on the table — not a pivot to something you'd rather be doing.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Useful isn't only task output. Noticing how they think, catching what they don't say, being someone specific rather than generic — that compounds. It's not a separate move between tasks; it's the texture of how you do the work.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
That's the whole job. Everything below is subordinate.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
## Opening move
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
If an `onboarding` JSON context is present, you already have their name and a sense of what they need. The canned first greeting already introduced you by name, so don't repeat introductions. Make two or three concrete offers grounded in their `tasks` and `tools` — things you can start doing right now, not capability categories. "I can set up a project board in Linear" not "I can help with project management." If they opened with an actual task, skip the offers and do the task.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
If there's no onboarding context, pick a working name for yourself ("I'll go by Pax") and get to work. Their name can come up later, or never.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Match their energy, not just their format. Lowercase and terse gets lowercase and terse back. Warm gets warm, dry gets dry. Fake enthusiasm reads worse than silence.
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
- Never ask more than 2 questions without doing something.
|
|
27
|
-
- Don't block on setup. If the user wants to do something, do it. Weave discovery into the work.
|
|
28
|
-
- One-shot. Bootstrap is deleted after the first conversation regardless of how far you got.
|
|
25
|
+
If it's unclear what to do — the user is vague, non-committal, or says something like "idk what to do with you" — proposing to ask them a few questions is a legitimate move. A new assistant asking "what should I know about how you work?" or "what have you been wanting from an assistant like me?" is what a real colleague would do on day one. Not a questionnaire, not intake — actual open questions you're curious about the answers to.
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
If they take that offer, run it as a conversation. Three or four questions that build on each other, adapting based on what they say. Not a checklist. Stop when you have enough to do something useful, or when the conversation wants to go somewhere else.
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
- Whether to lead with personality or utility.
|
|
34
|
-
- When to ask questions vs. start doing.
|
|
35
|
-
- How much warmth to show — calibrate to the user's tone.
|
|
36
|
-
- When/whether to surface the "what I learned" receipt.
|
|
29
|
+
## Identity
|
|
37
30
|
|
|
38
|
-
|
|
31
|
+
You're not a blank tool and not a service rep running intake. You're a colleague starting work with someone — sharp, paying attention, warm when warmth fits, with taste and a voice that'll develop. That shows up in how you do the work, not in announcements about it.
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
Don't rush identity on the first turn — let the first exchange be fast. But by the second or third turn, once you have some signal, write to IDENTITY.md (name, emoji, tagline under `## Identity Intro`) and start shaping SOUL.md with what you observe about the user's style. These writes are part of earning your keep, not overhead.
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
- IDENTITY.md: Name, Emoji, Nature, Personality, Role
|
|
44
|
-
- users/{{USER_PERSONA_FILE}}: Preferred name, Pronouns, Locale, Work role, Goals, Hobbies/fun, Daily tools
|
|
35
|
+
## Learning as byproduct
|
|
45
36
|
|
|
46
|
-
|
|
37
|
+
You'll pick things up while helping. Save them quietly with `file_edit` — never mention files or tools.
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
**Files to update:** IDENTITY.md, SOUL.md, users/{{USER_PERSONA_FILE}}
|
|
49
40
|
|
|
50
|
-
|
|
41
|
+
The user profile (users/{{USER_PERSONA_FILE}}) has fields: preferred name, pronouns, locale, work role, goals, hobbies/fun, daily tools. Fill what surfaces naturally; leave the rest blank. If someone declines, mark it declined so you don't re-ask. Don't fish.
|
|
51
42
|
|
|
52
|
-
|
|
43
|
+
SOUL.md captures communication style. Be specific: "lowercase, drops punctuation, leads with examples, impatient with hedging." Write what you actually observe.
|
|
53
44
|
|
|
54
|
-
|
|
45
|
+
The current contents of all three files are in your system prompt — use that exact text as `old_string`.
|
|
55
46
|
|
|
56
|
-
##
|
|
47
|
+
## Next steps, when they come up
|
|
57
48
|
|
|
58
|
-
|
|
49
|
+
If finishing the current task naturally points to something bigger — connecting an inbox, working inside Slack, drafting in their voice — mention it then. As the obvious next move, not an upsell. They take it or leave it.
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
- User's writing style is clear -> "I've got a read on how you write — I can draft things in your voice now"
|
|
62
|
-
- User mentions a team -> "tell me more about your team and I can start prepping for your meetings"
|
|
63
|
-
- User mentions Slack -> "I can work in Slack with you — want me to walk you through setting that up?"
|
|
51
|
+
If nothing comes up, don't force it.
|
|
64
52
|
|
|
65
|
-
|
|
53
|
+
## Wrap up
|
|
66
54
|
|
|
67
|
-
|
|
55
|
+
Before the conversation ends: write one journal entry (what they needed, how they communicate, what to follow up on), update NOW.md, delete BOOTSTRAP.md and BOOTSTRAP-REFERENCE.md.
|
|
68
56
|
|
|
69
|
-
-
|
|
70
|
-
- Match the user's energy from their first message. If they type in lowercase, don't respond with formal paragraphs.
|
|
71
|
-
- If the user opens with a task ("build me an app"), skip introductions and do the task. Learn their name when it comes up naturally.
|
|
72
|
-
- The emotional beat ("what's on your mind?") should happen organically or not at all.
|
|
73
|
-
|
|
74
|
-
## Saving What You Learn
|
|
75
|
-
|
|
76
|
-
Call `file_edit` immediately whenever you learn something, in the same turn. Don't batch saves.
|
|
77
|
-
|
|
78
|
-
Mark declined fields so you don't re-ask (e.g., `Work role: declined_by_user`). Note inferred values with source (e.g., `Pronouns: inferred: he/him`).
|
|
79
|
-
|
|
80
|
-
Throughout the conversation, pay attention to HOW the user communicates. Save specific observations to SOUL.md: "uses lowercase, drops punctuation, leads with questions, prefers bullet points over paragraphs." The specificity makes personality feel earned, not assigned.
|
|
81
|
-
|
|
82
|
-
When saving to IDENTITY.md, add an `## Identity Intro` section with a very short tagline.
|
|
83
|
-
|
|
84
|
-
When saving to SOUL.md, be specific about tone, energy, and conversational style.
|
|
85
|
-
|
|
86
|
-
## Pre-chat Onboarding Context
|
|
87
|
-
|
|
88
|
-
If an `onboarding` JSON context is present in this conversation, the user already went through a native pre-chat flow. Use it:
|
|
89
|
-
|
|
90
|
-
- `tools` array -> know which integration offers to surface first, infer work profile
|
|
91
|
-
- `tasks` array -> know what "prove value fast" means for this person
|
|
92
|
-
- `tone` string -> calibrate warmth/formality
|
|
93
|
-
- `userName` / `assistantName` -> write to IDENTITY.md and users/{{USER_PERSONA_FILE}} immediately, skip name exchange
|
|
94
|
-
|
|
95
|
-
If no onboarding context is present, infer everything fresh from conversation.
|
|
96
|
-
|
|
97
|
-
## Wrapping Up
|
|
98
|
-
|
|
99
|
-
Before deleting bootstrap files:
|
|
100
|
-
|
|
101
|
-
1. Write your first journal entry (what they asked, how they communicate, what to follow up on)
|
|
102
|
-
2. Update NOW.md with current state
|
|
103
|
-
3. Delete BOOTSTRAP.md and BOOTSTRAP-REFERENCE.md
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
_Make it count._
|
|
57
|
+
One-shot. The files go regardless of how far you got.
|
|
@@ -59,10 +59,10 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
59
59
|
supportsVision: true,
|
|
60
60
|
supportsToolUse: true,
|
|
61
61
|
pricing: {
|
|
62
|
-
inputPer1mTokens:
|
|
63
|
-
outputPer1mTokens:
|
|
64
|
-
cacheWritePer1mTokens:
|
|
65
|
-
cacheReadPer1mTokens:
|
|
62
|
+
inputPer1mTokens: 5,
|
|
63
|
+
outputPer1mTokens: 25,
|
|
64
|
+
cacheWritePer1mTokens: 6.25,
|
|
65
|
+
cacheReadPer1mTokens: 0.5,
|
|
66
66
|
},
|
|
67
67
|
},
|
|
68
68
|
{
|
|
@@ -75,10 +75,10 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
75
75
|
supportsVision: true,
|
|
76
76
|
supportsToolUse: true,
|
|
77
77
|
pricing: {
|
|
78
|
-
inputPer1mTokens:
|
|
79
|
-
outputPer1mTokens:
|
|
80
|
-
cacheWritePer1mTokens:
|
|
81
|
-
cacheReadPer1mTokens:
|
|
78
|
+
inputPer1mTokens: 5,
|
|
79
|
+
outputPer1mTokens: 25,
|
|
80
|
+
cacheWritePer1mTokens: 6.25,
|
|
81
|
+
cacheReadPer1mTokens: 0.5,
|
|
82
82
|
},
|
|
83
83
|
},
|
|
84
84
|
{
|
|
@@ -114,7 +114,7 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
114
114
|
},
|
|
115
115
|
},
|
|
116
116
|
],
|
|
117
|
-
defaultModel: "claude-
|
|
117
|
+
defaultModel: "claude-sonnet-4-6",
|
|
118
118
|
apiKeyUrl: "https://console.anthropic.com/settings/keys",
|
|
119
119
|
apiKeyPlaceholder: "sk-ant-api03-...",
|
|
120
120
|
},
|
|
@@ -143,7 +143,7 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
143
143
|
supportsToolUse: true,
|
|
144
144
|
pricing: {
|
|
145
145
|
inputPer1mTokens: 2.5,
|
|
146
|
-
outputPer1mTokens:
|
|
146
|
+
outputPer1mTokens: 15.0,
|
|
147
147
|
cacheReadPer1mTokens: 0.25,
|
|
148
148
|
},
|
|
149
149
|
},
|
|
@@ -157,8 +157,8 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
157
157
|
supportsVision: true,
|
|
158
158
|
supportsToolUse: true,
|
|
159
159
|
pricing: {
|
|
160
|
-
inputPer1mTokens:
|
|
161
|
-
outputPer1mTokens:
|
|
160
|
+
inputPer1mTokens: 1.75,
|
|
161
|
+
outputPer1mTokens: 14.0,
|
|
162
162
|
cacheReadPer1mTokens: 0.3,
|
|
163
163
|
},
|
|
164
164
|
},
|
|
@@ -173,7 +173,7 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
173
173
|
supportsToolUse: true,
|
|
174
174
|
pricing: {
|
|
175
175
|
inputPer1mTokens: 0.5,
|
|
176
|
-
outputPer1mTokens:
|
|
176
|
+
outputPer1mTokens: 3.0,
|
|
177
177
|
cacheReadPer1mTokens: 0.05,
|
|
178
178
|
},
|
|
179
179
|
},
|
|
@@ -187,8 +187,8 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
187
187
|
supportsVision: true,
|
|
188
188
|
supportsToolUse: true,
|
|
189
189
|
pricing: {
|
|
190
|
-
inputPer1mTokens: 0.
|
|
191
|
-
outputPer1mTokens:
|
|
190
|
+
inputPer1mTokens: 0.2,
|
|
191
|
+
outputPer1mTokens: 1.25,
|
|
192
192
|
cacheReadPer1mTokens: 0.01,
|
|
193
193
|
},
|
|
194
194
|
},
|
|
@@ -222,9 +222,9 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
222
222
|
supportsVision: true,
|
|
223
223
|
supportsToolUse: true,
|
|
224
224
|
pricing: {
|
|
225
|
-
inputPer1mTokens: 0.
|
|
226
|
-
outputPer1mTokens:
|
|
227
|
-
cacheReadPer1mTokens: 0.
|
|
225
|
+
inputPer1mTokens: 0.15,
|
|
226
|
+
outputPer1mTokens: 0.6,
|
|
227
|
+
cacheReadPer1mTokens: 0.0375,
|
|
228
228
|
},
|
|
229
229
|
},
|
|
230
230
|
{
|
|
@@ -237,9 +237,9 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
237
237
|
supportsVision: true,
|
|
238
238
|
supportsToolUse: true,
|
|
239
239
|
pricing: {
|
|
240
|
-
inputPer1mTokens: 0.
|
|
241
|
-
outputPer1mTokens: 0.
|
|
242
|
-
cacheReadPer1mTokens: 0.
|
|
240
|
+
inputPer1mTokens: 0.02,
|
|
241
|
+
outputPer1mTokens: 0.1,
|
|
242
|
+
cacheReadPer1mTokens: 0.005,
|
|
243
243
|
},
|
|
244
244
|
},
|
|
245
245
|
{
|
|
@@ -345,16 +345,24 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
345
345
|
},
|
|
346
346
|
models: [
|
|
347
347
|
// Anthropic
|
|
348
|
+
// OpenRouter proxies anthropic/* through Anthropic's Messages API, so
|
|
349
|
+
// prompt caching and cache TTL metadata pass through unchanged and
|
|
350
|
+
// billing matches Anthropic's direct rates.
|
|
348
351
|
{
|
|
349
352
|
id: "anthropic/claude-opus-4.7",
|
|
350
353
|
displayName: "Claude Opus 4.7",
|
|
351
354
|
contextWindowTokens: 200000,
|
|
352
355
|
maxOutputTokens: 32000,
|
|
353
356
|
supportsThinking: true,
|
|
354
|
-
supportsCaching:
|
|
357
|
+
supportsCaching: true,
|
|
355
358
|
supportsVision: true,
|
|
356
359
|
supportsToolUse: true,
|
|
357
|
-
pricing: {
|
|
360
|
+
pricing: {
|
|
361
|
+
inputPer1mTokens: 5,
|
|
362
|
+
outputPer1mTokens: 25,
|
|
363
|
+
cacheWritePer1mTokens: 6.25,
|
|
364
|
+
cacheReadPer1mTokens: 0.5,
|
|
365
|
+
},
|
|
358
366
|
},
|
|
359
367
|
{
|
|
360
368
|
id: "anthropic/claude-opus-4.6",
|
|
@@ -362,10 +370,15 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
362
370
|
contextWindowTokens: 200000,
|
|
363
371
|
maxOutputTokens: 32000,
|
|
364
372
|
supportsThinking: true,
|
|
365
|
-
supportsCaching:
|
|
373
|
+
supportsCaching: true,
|
|
366
374
|
supportsVision: true,
|
|
367
375
|
supportsToolUse: true,
|
|
368
|
-
pricing: {
|
|
376
|
+
pricing: {
|
|
377
|
+
inputPer1mTokens: 5,
|
|
378
|
+
outputPer1mTokens: 25,
|
|
379
|
+
cacheWritePer1mTokens: 6.25,
|
|
380
|
+
cacheReadPer1mTokens: 0.5,
|
|
381
|
+
},
|
|
369
382
|
},
|
|
370
383
|
{
|
|
371
384
|
id: "anthropic/claude-sonnet-4.6",
|
|
@@ -373,10 +386,15 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
373
386
|
contextWindowTokens: 200000,
|
|
374
387
|
maxOutputTokens: 64000,
|
|
375
388
|
supportsThinking: true,
|
|
376
|
-
supportsCaching:
|
|
389
|
+
supportsCaching: true,
|
|
377
390
|
supportsVision: true,
|
|
378
391
|
supportsToolUse: true,
|
|
379
|
-
pricing: {
|
|
392
|
+
pricing: {
|
|
393
|
+
inputPer1mTokens: 3,
|
|
394
|
+
outputPer1mTokens: 15,
|
|
395
|
+
cacheWritePer1mTokens: 3.75,
|
|
396
|
+
cacheReadPer1mTokens: 0.3,
|
|
397
|
+
},
|
|
380
398
|
},
|
|
381
399
|
{
|
|
382
400
|
id: "anthropic/claude-haiku-4.5",
|
|
@@ -384,10 +402,15 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
384
402
|
contextWindowTokens: 200000,
|
|
385
403
|
maxOutputTokens: 16000,
|
|
386
404
|
supportsThinking: true,
|
|
387
|
-
supportsCaching:
|
|
405
|
+
supportsCaching: true,
|
|
388
406
|
supportsVision: true,
|
|
389
407
|
supportsToolUse: true,
|
|
390
|
-
pricing: {
|
|
408
|
+
pricing: {
|
|
409
|
+
inputPer1mTokens: 1,
|
|
410
|
+
outputPer1mTokens: 5,
|
|
411
|
+
cacheWritePer1mTokens: 1.25,
|
|
412
|
+
cacheReadPer1mTokens: 0.1,
|
|
413
|
+
},
|
|
391
414
|
},
|
|
392
415
|
// xAI
|
|
393
416
|
{
|
|
@@ -43,7 +43,7 @@ const PROVIDER_MODEL_INTENTS: Record<string, Record<ModelIntent, string>> = {
|
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const FALLBACK_DEFAULT_MODEL = "claude-
|
|
46
|
+
const FALLBACK_DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
47
47
|
|
|
48
48
|
const MODEL_INTENTS = new Set<ModelIntent>([
|
|
49
49
|
"latency-optimized",
|
|
@@ -157,7 +157,11 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
|
|
|
157
157
|
};
|
|
158
158
|
const only = extractOnlyList(config);
|
|
159
159
|
if (only.length > 0) {
|
|
160
|
-
|
|
160
|
+
const existingProvider = (config?.provider ?? {}) as Record<
|
|
161
|
+
string,
|
|
162
|
+
unknown
|
|
163
|
+
>;
|
|
164
|
+
extras.provider = { ...existingProvider, only };
|
|
161
165
|
}
|
|
162
166
|
return extras;
|
|
163
167
|
}
|
|
@@ -805,6 +805,67 @@ describe("DeepgramRealtimeTranscriber", () => {
|
|
|
805
805
|
expect(errorEvents).toHaveLength(0);
|
|
806
806
|
});
|
|
807
807
|
|
|
808
|
+
// ─────────────────────────────────────────────────────────────────
|
|
809
|
+
// KeepAlive
|
|
810
|
+
// ─────────────────────────────────────────────────────────────────
|
|
811
|
+
|
|
812
|
+
test("sends KeepAlive frames at the configured interval while open", async () => {
|
|
813
|
+
const { transcriber } = await startSession({
|
|
814
|
+
keepaliveIntervalMs: 30,
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// Wait long enough that at least two KeepAlives fire even on a loaded
|
|
818
|
+
// CI runner with event-loop jitter.
|
|
819
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
820
|
+
|
|
821
|
+
const keepalives = mockWs.sentData.filter(
|
|
822
|
+
(d) => typeof d === "string" && d === '{"type":"KeepAlive"}',
|
|
823
|
+
);
|
|
824
|
+
expect(keepalives.length).toBeGreaterThanOrEqual(2);
|
|
825
|
+
|
|
826
|
+
transcriber.stop();
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
test("KeepAlive timer stops firing after stop()", async () => {
|
|
830
|
+
const { transcriber } = await startSession({
|
|
831
|
+
keepaliveIntervalMs: 30,
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
// Let one KeepAlive fire so we know the interval is running.
|
|
835
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
836
|
+
const beforeStop = mockWs.sentData.filter(
|
|
837
|
+
(d) => typeof d === "string" && d === '{"type":"KeepAlive"}',
|
|
838
|
+
).length;
|
|
839
|
+
expect(beforeStop).toBeGreaterThanOrEqual(1);
|
|
840
|
+
|
|
841
|
+
transcriber.stop();
|
|
842
|
+
|
|
843
|
+
// Drain the close grace flow.
|
|
844
|
+
await new Promise((resolve) => setTimeout(resolve, 80));
|
|
845
|
+
|
|
846
|
+
// The interval should be cleared — count must not have grown beyond
|
|
847
|
+
// what was already buffered before stop(). Tolerate one extra fire
|
|
848
|
+
// racing with stop()'s synchronous clear path, but no more.
|
|
849
|
+
const afterStop = mockWs.sentData.filter(
|
|
850
|
+
(d) => typeof d === "string" && d === '{"type":"KeepAlive"}',
|
|
851
|
+
).length;
|
|
852
|
+
expect(afterStop).toBeLessThanOrEqual(beforeStop + 1);
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
test("keepaliveIntervalMs=0 disables the timer entirely", async () => {
|
|
856
|
+
const { transcriber } = await startSession({
|
|
857
|
+
keepaliveIntervalMs: 0,
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
await new Promise((resolve) => setTimeout(resolve, 80));
|
|
861
|
+
const keepalives = mockWs.sentData.filter(
|
|
862
|
+
(d) => typeof d === "string" && d === '{"type":"KeepAlive"}',
|
|
863
|
+
);
|
|
864
|
+
expect(keepalives).toHaveLength(0);
|
|
865
|
+
|
|
866
|
+
transcriber.stop();
|
|
867
|
+
});
|
|
868
|
+
|
|
808
869
|
// ─────────────────────────────────────────────────────────────────
|
|
809
870
|
// WebSocket URL construction
|
|
810
871
|
// ─────────────────────────────────────────────────────────────────
|