@vellumai/assistant 0.6.4 → 0.6.5
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/.prettierignore +5 -0
- package/ARCHITECTURE.md +32 -36
- package/Dockerfile +12 -0
- package/README.md +3 -4
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +1 -20
- package/docs/architecture/security.md +16 -16
- package/docs/error-handling.md +111 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/knip.json +9 -2
- package/node_modules/@vellumai/ces-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
- package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
- package/node_modules/@vellumai/credential-storage/package.json +2 -2
- package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
- package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
- package/node_modules/@vellumai/egress-proxy/package.json +2 -2
- package/openapi.yaml +123 -11
- package/package.json +6 -3
- package/scripts/generate-openapi.ts +50 -11
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
- package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
- package/src/__tests__/agent-loop.test.ts +112 -1
- package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
- package/src/__tests__/anthropic-provider.test.ts +171 -2
- package/src/__tests__/approval-cascade.test.ts +31 -10
- package/src/__tests__/approval-routes-http.test.ts +134 -10
- package/src/__tests__/assistant-attachments.test.ts +44 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
- package/src/__tests__/browser-skill-endstate.test.ts +51 -182
- package/src/__tests__/btw-routes.test.ts +47 -1
- package/src/__tests__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +27 -4
- package/src/__tests__/channel-approval-routes.test.ts +4 -4
- package/src/__tests__/channel-reply-delivery.test.ts +300 -2
- package/src/__tests__/checker.test.ts +428 -501
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/config-analysis.test.ts +11 -28
- package/src/__tests__/config-loader-backfill.test.ts +174 -0
- package/src/__tests__/config-loader-corrupt.test.ts +183 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +427 -114
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +530 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
- package/src/__tests__/conversation-agent-loop.test.ts +412 -82
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
- package/src/__tests__/conversation-process-callsite.test.ts +306 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
- package/src/__tests__/conversation-queue.test.ts +41 -26
- package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
- package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +34 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
- package/src/__tests__/conversation-speed-override.test.ts +30 -11
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -2
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
- package/src/__tests__/conversation-unread-route.test.ts +2 -2
- package/src/__tests__/conversation-usage.test.ts +3 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
- package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
- package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
- package/src/__tests__/credential-vault-unit.test.ts +135 -19
- package/src/__tests__/credentials-cli.test.ts +1 -9
- package/src/__tests__/cross-provider-web-search.test.ts +84 -0
- package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
- package/src/__tests__/delete-propagation.test.ts +437 -0
- package/src/__tests__/dm-backfill.test.ts +417 -0
- package/src/__tests__/dm-persistence.test.ts +227 -0
- package/src/__tests__/edit-propagation.test.ts +280 -0
- package/src/__tests__/ephemeral-permissions.test.ts +93 -3
- package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
- package/src/__tests__/estimator-calibration.test.ts +213 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/gemini-provider.test.ts +0 -3
- package/src/__tests__/guardian-grant-minting.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/intent-routing.test.ts +1 -40
- package/src/__tests__/llm-catalog-parity.test.ts +174 -0
- package/src/__tests__/llm-context-normalization.test.ts +121 -0
- package/src/__tests__/llm-resolver.test.ts +214 -0
- package/src/__tests__/llm-schema.test.ts +223 -0
- package/src/__tests__/managed-proxy-context.test.ts +6 -2
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +684 -0
- package/src/__tests__/model-intents.test.ts +9 -83
- package/src/__tests__/notification-decision-fallback.test.ts +0 -10
- package/src/__tests__/notification-decision-identity.test.ts +0 -9
- package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
- package/src/__tests__/oauth-store.test.ts +10 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/openai-provider.test.ts +7 -0
- package/src/__tests__/openai-responses-provider.test.ts +396 -0
- package/src/__tests__/openrouter-provider-only.test.ts +135 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +50 -3
- package/src/__tests__/profiler-routes.test.ts +1 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
- package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
- package/src/__tests__/provider-error-scenarios.test.ts +135 -6
- package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
- package/src/__tests__/provider-registry-ollama.test.ts +1 -2
- package/src/__tests__/proxy-approval-callback.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +560 -0
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
- package/src/__tests__/risk-classifier-parity.test.ts +230 -0
- package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
- package/src/__tests__/secret-ingress-http.test.ts +28 -0
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
- package/src/__tests__/secret-scanner-executor.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +29 -1
- package/src/__tests__/server-history-render.test.ts +31 -0
- package/src/__tests__/shell-parser-property.test.ts +13 -13
- package/src/__tests__/skill-cache-store.test.ts +182 -0
- package/src/__tests__/skills.test.ts +19 -33
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-skill.test.ts +3 -8
- package/src/__tests__/starter-bundle.test.ts +35 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
- package/src/__tests__/suggestion-routes.test.ts +160 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor.test.ts +60 -94
- package/src/__tests__/trust-store.test.ts +442 -109
- package/src/__tests__/update-bulletin-job.test.ts +389 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
- package/src/__tests__/voice-session-bridge.test.ts +39 -0
- package/src/__tests__/volume-security-guard.test.ts +3 -2
- package/src/__tests__/web-search-history.test.ts +337 -0
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
- package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
- package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +1 -13
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +209 -17
- package/src/avatar/resvg-lazy.test.ts +136 -0
- package/src/avatar/resvg-lazy.ts +82 -9
- package/src/avatar/traits-png-sync.ts +21 -1
- package/src/browser/__tests__/operations.test.ts +163 -0
- package/src/browser/identifiers.ts +51 -0
- package/src/browser/operations.ts +660 -0
- package/src/browser/types.ts +81 -0
- package/src/calls/guardian-question-copy.ts +2 -2
- package/src/calls/telephony-stt-routing.ts +1 -1
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/browser.test.ts +554 -0
- package/src/cli/commands/__tests__/cache.test.ts +623 -0
- package/src/cli/commands/__tests__/email-list.test.ts +6 -0
- package/src/cli/commands/__tests__/email-send.test.ts +93 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
- package/src/cli/commands/__tests__/task.test.ts +913 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
- package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
- package/src/cli/commands/__tests__/ui.test.ts +1215 -0
- package/src/cli/commands/__tests__/watchers.test.ts +716 -0
- package/src/cli/commands/attachment.ts +182 -0
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +14 -1
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +300 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
- package/src/cli/commands/stt.ts +339 -0
- package/src/cli/commands/task.ts +795 -0
- package/src/cli/commands/trust.ts +50 -19
- package/src/cli/commands/tts.ts +273 -0
- package/src/cli/commands/ui.ts +670 -0
- package/src/cli/commands/watchers.ts +509 -0
- package/src/cli/lib/daemon-credential-client.ts +0 -19
- package/src/cli/program.ts +23 -4
- package/src/cli.ts +0 -37
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -175
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/llm-resolver.ts +128 -0
- package/src/config/loader.ts +194 -10
- package/src/config/raw-config-utils.ts +30 -2
- package/src/config/sanitize-for-transfer.ts +35 -0
- package/src/config/schema.ts +30 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/filing.ts +2 -7
- package/src/config/schemas/heartbeat.ts +0 -5
- package/src/config/schemas/inference.ts +3 -23
- package/src/config/schemas/llm.ts +318 -0
- package/src/config/schemas/memory-processing.ts +1 -9
- package/src/config/schemas/notifications.ts +4 -11
- package/src/config/schemas/platform.ts +3 -9
- package/src/config/schemas/security.ts +33 -0
- package/src/config/schemas/services.ts +9 -4
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/schemas/tts.ts +53 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skills.ts +2 -2
- package/src/context/__tests__/compact-prompt.test.ts +45 -0
- package/src/context/__tests__/microcompact.test.ts +805 -0
- package/src/context/estimator-calibration.ts +136 -0
- package/src/context/microcompact.ts +443 -0
- package/src/context/prompts/compact.md +12 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/window-manager.ts +229 -25
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/executable-discovery.ts +19 -8
- package/src/credential-execution/process-manager.test.ts +109 -0
- package/src/credential-execution/process-manager.ts +65 -2
- package/src/daemon/approval-generators.ts +29 -4
- package/src/daemon/assistant-attachments.ts +24 -13
- package/src/daemon/classifier.ts +2 -2
- package/src/daemon/config-watcher.ts +0 -1
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
- package/src/daemon/conversation-agent-loop.ts +462 -80
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-lifecycle.ts +30 -6
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-process.ts +10 -4
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +760 -29
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +10 -5
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +118 -30
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/guardian-action-generators.ts +34 -14
- package/src/daemon/handlers/config-model.test.ts +86 -0
- package/src/daemon/handlers/config-model.ts +54 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +2 -2
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +76 -14
- package/src/daemon/message-types/conversations.ts +14 -0
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/trust.ts +0 -2
- package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
- package/src/daemon/pkb-context-tracker.test.ts +169 -0
- package/src/daemon/pkb-context-tracker.ts +125 -0
- package/src/daemon/pkb-reminder-builder.test.ts +70 -0
- package/src/daemon/pkb-reminder-builder.ts +31 -0
- package/src/daemon/providers-setup.ts +6 -0
- package/src/daemon/server.ts +117 -9
- package/src/daemon/tool-side-effects.ts +0 -9
- package/src/daemon/watch-handler.ts +4 -4
- package/src/daemon/web-search-history.ts +126 -0
- package/src/events/domain-events.ts +0 -1
- package/src/filing/filing-service.ts +9 -10
- package/src/heartbeat/heartbeat-service.ts +76 -28
- package/src/home/__tests__/feed-scheduler.test.ts +39 -11
- package/src/home/__tests__/rollup-producer.test.ts +44 -0
- package/src/home/assistant-feed-authoring.ts +4 -0
- package/src/home/emit-feed-event.ts +4 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +56 -2
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rollup-producer.ts +34 -5
- package/src/home/suggested-prompts.ts +101 -0
- package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
- package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
- package/src/ipc/__tests__/socket-path.test.ts +73 -0
- package/src/ipc/__tests__/task-ipc.test.ts +577 -0
- package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
- package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
- package/src/ipc/cli-client.ts +2 -1
- package/src/ipc/cli-server.ts +26 -8
- package/src/ipc/gateway-client.ts +4 -4
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +61 -0
- package/src/ipc/routes/browser.ts +96 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/index.ts +17 -1
- package/src/ipc/routes/task-queue.ts +226 -0
- package/src/ipc/routes/task.ts +173 -0
- package/src/ipc/routes/ui-request.ts +50 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +100 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
- package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
- package/src/memory/admin.ts +18 -0
- package/src/memory/conversation-analyze-job.ts +14 -13
- package/src/memory/conversation-attention-store.ts +13 -6
- package/src/memory/conversation-crud.ts +103 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-title-service.ts +7 -4
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/graph/compaction.ts +299 -0
- package/src/memory/graph/consolidation.ts +4 -4
- package/src/memory/graph/conversation-graph-memory.ts +89 -29
- package/src/memory/graph/extraction.test.ts +272 -2
- package/src/memory/graph/extraction.ts +173 -51
- package/src/memory/graph/graph-search.test.ts +92 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +459 -0
- package/src/memory/graph/retriever.ts +230 -48
- package/src/memory/graph/store.ts +41 -0
- package/src/memory/graph/tool-handlers.ts +27 -0
- package/src/memory/graph/tools.ts +6 -1
- package/src/memory/indexer.ts +5 -5
- package/src/memory/job-handlers/conversation-starters.ts +23 -20
- package/src/memory/job-handlers/summarization.ts +2 -2
- package/src/memory/job-utils.ts +7 -1
- package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
- package/src/memory/jobs/embed-pkb-file.ts +54 -0
- package/src/memory/jobs-store.ts +44 -3
- package/src/memory/jobs-worker.ts +4 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-index.test.ts +368 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +438 -0
- package/src/memory/pkb/pkb-search.ts +137 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.ts +122 -1
- package/src/memory/slack-thread-store.ts +37 -0
- package/src/messaging/providers/gmail/adapter.ts +6 -16
- package/src/messaging/providers/gmail/client.ts +22 -0
- package/src/messaging/providers/gmail/types.ts +7 -0
- package/src/messaging/providers/slack/adapter.ts +14 -2
- package/src/messaging/providers/slack/backfill.test.ts +257 -0
- package/src/messaging/providers/slack/backfill.ts +101 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
- package/src/messaging/providers/slack/message-metadata.ts +123 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
- package/src/messaging/providers/slack/render-transcript.ts +443 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/oauth/oauth-store.ts +1 -0
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/seed-providers.ts +4 -2
- package/src/permissions/approval-policy.test.ts +948 -0
- package/src/permissions/approval-policy.ts +257 -0
- package/src/permissions/bash-risk-classifier.test.ts +1208 -0
- package/src/permissions/bash-risk-classifier.ts +707 -0
- package/src/permissions/checker.ts +217 -708
- package/src/permissions/command-registry.test.ts +535 -0
- package/src/permissions/command-registry.ts +825 -0
- package/src/permissions/defaults.ts +26 -78
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/risk-types.ts +205 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/skill-risk-classifier.test.ts +311 -0
- package/src/permissions/skill-risk-classifier.ts +214 -0
- package/src/permissions/trust-client.ts +52 -25
- package/src/permissions/trust-store-interface.ts +1 -6
- package/src/permissions/trust-store.ts +161 -62
- package/src/permissions/types.ts +23 -14
- package/src/permissions/web-risk-classifier.test.ts +170 -0
- package/src/permissions/web-risk-classifier.ts +89 -0
- package/src/permissions/workspace-policy.ts +1 -16
- package/src/platform/client.ts +19 -1
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/SOUL.md +2 -2
- package/src/prompts/update-bulletin-job.ts +190 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
- package/src/providers/__tests__/retry-callsite.test.ts +424 -0
- package/src/providers/anthropic/client.ts +183 -14
- package/src/providers/call-site-routing.ts +71 -0
- package/src/providers/gemini/client.ts +65 -2
- package/src/providers/managed-proxy/constants.ts +2 -1
- package/src/providers/model-catalog.ts +501 -33
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +57 -1
- package/src/providers/openai/responses-provider.ts +86 -9
- package/src/providers/openrouter/client.ts +76 -9
- package/src/providers/provider-env-vars.ts +56 -0
- package/src/providers/provider-send-message.ts +22 -5
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/registry.ts +19 -8
- package/src/providers/retry.ts +174 -39
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +17 -0
- package/src/providers/speech-to-text/resolve.ts +7 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
- package/src/providers/speech-to-text/xai-realtime.ts +796 -0
- package/src/providers/speech-to-text/xai.test.ts +155 -0
- package/src/providers/speech-to-text/xai.ts +97 -0
- package/src/providers/types.ts +93 -3
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
- package/src/runtime/agent-wake.ts +63 -22
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/btw-sidechain.ts +13 -3
- package/src/runtime/channel-reply-delivery.ts +106 -2
- package/src/runtime/decision-token.ts +116 -0
- package/src/runtime/gateway-client.ts +2 -2
- package/src/runtime/http-router.ts +32 -0
- package/src/runtime/http-server.ts +52 -1
- package/src/runtime/http-types.ts +23 -1
- package/src/runtime/interactive-ui.ts +362 -0
- package/src/runtime/invite-instruction-generator.ts +2 -2
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
- package/src/runtime/migrations/gcs-signed-url.ts +162 -0
- package/src/runtime/migrations/vbundle-importer.ts +154 -9
- package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
- package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
- package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
- package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
- package/src/runtime/migrations/vbundle-validator.ts +15 -6
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
- package/src/runtime/routes/approval-routes.ts +12 -17
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
- package/src/runtime/routes/avatar-routes.ts +20 -4
- package/src/runtime/routes/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +133 -27
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +6 -4
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/guardian-approval-interception.ts +33 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
- package/src/runtime/routes/home-feed-routes.ts +120 -2
- package/src/runtime/routes/inbound-message-handler.ts +912 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
- package/src/runtime/routes/integrations/slack/channel.ts +25 -3
- package/src/runtime/routes/llm-context-normalization.ts +23 -1
- package/src/runtime/routes/migration-routes.ts +720 -124
- package/src/runtime/routes/settings-routes.ts +4 -2
- package/src/runtime/routes/trust-rules-routes.ts +30 -14
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +3 -2
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
- package/src/runtime/services/analyze-conversation.ts +12 -16
- package/src/runtime/skill-route-registry.ts +28 -6
- package/src/schedule/scheduler.ts +8 -0
- package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
- package/src/security/__tests__/untrusted-content.test.ts +109 -0
- package/src/security/oauth2.ts +98 -35
- package/src/security/secure-keys.ts +7 -8
- package/src/security/token-manager.ts +27 -13
- package/src/security/untrusted-content.ts +102 -0
- package/src/skills/catalog-cache.ts +26 -7
- package/src/skills/catalog-install.ts +31 -3
- package/src/skills/skill-cache-store.ts +97 -0
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
- package/src/stt/daemon-batch-transcriber.ts +33 -0
- package/src/stt/stt-stream-session.ts +8 -1
- package/src/stt/types.ts +5 -1
- package/src/subagent/manager.ts +41 -13
- package/src/tasks/ephemeral-permissions.ts +9 -4
- package/src/telemetry/usage-telemetry-reporter.ts +27 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
- package/src/tools/browser/browser-execution.ts +65 -38
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +4 -0
- package/src/tools/filesystem/write.ts +52 -0
- package/src/tools/host-terminal/host-shell.ts +45 -5
- package/src/tools/memory/register.test.ts +185 -0
- package/src/tools/memory/register.ts +3 -1
- package/src/tools/network/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +36 -15
- package/src/tools/policy-context.ts +25 -8
- package/src/tools/registry.ts +55 -3
- package/src/tools/side-effects.ts +0 -11
- package/src/tools/skills/execute.ts +2 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/terminal/backends/native.ts +51 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +1 -0
- package/src/tools/tool-manifest.ts +6 -21
- package/src/tools/types.ts +12 -3
- package/src/tools/verification-control-plane-policy.ts +1 -1
- package/src/tts/__tests__/provider-adapters.test.ts +240 -13
- package/src/tts/provider-catalog.ts +18 -0
- package/src/tts/providers/index.ts +2 -0
- package/src/tts/providers/xai-provider.ts +224 -0
- package/src/tts/types.ts +46 -0
- package/src/types/tar-stream.d.ts +66 -0
- package/src/util/json.ts +17 -0
- package/src/util/platform.ts +2 -2
- package/src/util/pricing.ts +15 -5
- package/src/watcher/engine.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +134 -8
- package/src/watcher/providers/outlook-calendar.ts +42 -2
- package/src/workspace/git-service.ts +23 -4
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
- package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
- package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
- package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
- package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
- package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
- package/src/__tests__/gmail-archive-gate.test.ts +0 -246
- package/src/__tests__/gmail-preferences.test.ts +0 -117
- package/src/__tests__/outlook-attachments.test.ts +0 -301
- package/src/__tests__/outlook-automation-tools.test.ts +0 -425
- package/src/__tests__/outlook-categories.test.ts +0 -212
- package/src/__tests__/outlook-compose-tools.test.ts +0 -325
- package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
- package/src/__tests__/outlook-follow-up.test.ts +0 -196
- package/src/__tests__/outlook-trash.test.ts +0 -77
- package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
- package/src/__tests__/update-bulletin-format.test.ts +0 -181
- package/src/__tests__/update-bulletin-state.test.ts +0 -135
- package/src/__tests__/update-bulletin.test.ts +0 -478
- package/src/__tests__/update-template-contract.test.ts +0 -29
- package/src/cli/commands/doctor.ts +0 -341
- package/src/config/bundled-skills/browser/SKILL.md +0 -88
- package/src/config/bundled-skills/browser/TOOLS.json +0 -516
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
- package/src/config/bundled-skills/gmail/SKILL.md +0 -221
- package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
- package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
- package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
- package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/google-calendar/types.ts +0 -97
- package/src/config/bundled-skills/outlook/SKILL.md +0 -196
- package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
- package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
- package/src/config/bundled-skills/slack/SKILL.md +0 -108
- package/src/config/bundled-skills/tasks/SKILL.md +0 -37
- package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
- package/src/config/bundled-skills/tasks/icon.svg +0 -34
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
- package/src/config/bundled-skills/watcher/SKILL.md +0 -31
- package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
- package/src/prompts/templates/UPDATES.md +0 -50
- package/src/prompts/update-bulletin-format.ts +0 -85
- package/src/prompts/update-bulletin-state.ts +0 -58
- package/src/prompts/update-bulletin-template-path.ts +0 -13
- package/src/prompts/update-bulletin.ts +0 -139
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/tools/watcher/create.ts +0 -86
- package/src/tools/watcher/delete.ts +0 -36
- package/src/tools/watcher/digest.ts +0 -54
- package/src/tools/watcher/list.ts +0 -83
- package/src/tools/watcher/update.ts +0 -71
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for platform credential filtering during bundle import.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* `migration-routes.ts` pushes every bundle credential through a filter that
|
|
5
|
+
* must exclude platform-identity (`vellum:*`) entries so they can't overwrite
|
|
6
|
+
* the target's own Django-provisioned identity. The filter runs against the
|
|
7
|
+
* raw CES account format — `credential/{service}/{field}` — produced by
|
|
8
|
+
* `credentialKey()`, which is what `listSecureKeysAsync()` returns and what
|
|
9
|
+
* `extractCredentialsFromBundle` surfaces back as `account`.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* The constant is duplicated here (rather than imported) because
|
|
12
|
+
* `migration-routes.ts` has heavy transitive imports that are expensive to
|
|
13
|
+
* resolve in a test and would require wide mocking. Instead we bind the two
|
|
14
|
+
* copies with a regression assertion: if `credentialKey()`'s output format
|
|
15
|
+
* changes, the `startsWith(...)` prefix must change in lockstep. That
|
|
16
|
+
* regression is explicit at the bottom of this file.
|
|
17
|
+
*
|
|
18
|
+
* Covered:
|
|
19
|
+
* - Platform (vellum:*) credentials stored under the real `credential/vellum/...`
|
|
20
|
+
* key format are excluded.
|
|
21
|
+
* - User credentials (any other prefix) pass through unchanged.
|
|
22
|
+
* - Mixed bundles correctly split platform vs user credentials.
|
|
23
|
+
* - skippedPlatform count matches the number of excluded entries.
|
|
24
|
+
* - Regression: the prefix constant matches `credentialKey("vellum", "")`.
|
|
16
25
|
*/
|
|
17
26
|
|
|
18
27
|
import { describe, expect, test } from "bun:test";
|
|
19
28
|
|
|
29
|
+
import { credentialKey } from "../../../security/credential-key.js";
|
|
20
30
|
import { extractCredentialsFromBundle } from "../../migrations/vbundle-importer.js";
|
|
21
31
|
import type {
|
|
22
32
|
ManifestType,
|
|
@@ -24,10 +34,11 @@ import type {
|
|
|
24
34
|
} from "../../migrations/vbundle-validator.js";
|
|
25
35
|
|
|
26
36
|
// ---------------------------------------------------------------------------
|
|
27
|
-
// The same constant used by migration-routes.ts
|
|
37
|
+
// The same constant used by migration-routes.ts — kept in sync via the
|
|
38
|
+
// regression assertion at the bottom of this file.
|
|
28
39
|
// ---------------------------------------------------------------------------
|
|
29
40
|
|
|
30
|
-
const PLATFORM_CREDENTIAL_PREFIX = "vellum
|
|
41
|
+
const PLATFORM_CREDENTIAL_PREFIX = credentialKey("vellum", "");
|
|
31
42
|
|
|
32
43
|
// ---------------------------------------------------------------------------
|
|
33
44
|
// Helpers (same pattern as vbundle-import-credentials.test.ts)
|
|
@@ -52,6 +63,15 @@ function makeManifest(paths: string[]): ManifestType {
|
|
|
52
63
|
} as ManifestType;
|
|
53
64
|
}
|
|
54
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Build a bundle archive entry path for a credential whose CES account is
|
|
68
|
+
* `account`. `vbundle-builder.ts` stores credentials under
|
|
69
|
+
* `credentials/<account>`; the importer reverses that split.
|
|
70
|
+
*/
|
|
71
|
+
function bundlePathFor(account: string): string {
|
|
72
|
+
return `credentials/${account}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
55
75
|
/**
|
|
56
76
|
* Simulate the filtering logic from migration-routes.ts:
|
|
57
77
|
*
|
|
@@ -74,49 +94,45 @@ function filterCredentials(
|
|
|
74
94
|
// ---------------------------------------------------------------------------
|
|
75
95
|
|
|
76
96
|
describe("migration import credential filtering", () => {
|
|
77
|
-
test("vellum
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
entries.set(
|
|
89
|
-
"credentials/vellum:platform_organization_id",
|
|
90
|
-
makeTarEntry("org-3"),
|
|
97
|
+
test("platform (vellum:*) credentials are excluded", () => {
|
|
98
|
+
const vellumFields = [
|
|
99
|
+
"assistant_api_key",
|
|
100
|
+
"platform_assistant_id",
|
|
101
|
+
"platform_base_url",
|
|
102
|
+
"platform_organization_id",
|
|
103
|
+
"platform_user_id",
|
|
104
|
+
"webhook_secret",
|
|
105
|
+
] as const;
|
|
106
|
+
const vellumPaths = vellumFields.map((f) =>
|
|
107
|
+
bundlePathFor(credentialKey("vellum", f)),
|
|
91
108
|
);
|
|
92
|
-
entries.set("credentials/vellum:platform_user_id", makeTarEntry("user-4"));
|
|
93
|
-
entries.set("credentials/vellum:webhook_secret", makeTarEntry("whsec-5"));
|
|
94
109
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"credentials/vellum:webhook_secret",
|
|
102
|
-
]);
|
|
110
|
+
const entries = new Map<string, VBundleTarEntry>();
|
|
111
|
+
for (const path of vellumPaths) {
|
|
112
|
+
entries.set(path, makeTarEntry(`value-for-${path}`));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const manifest = makeManifest(vellumPaths);
|
|
103
116
|
|
|
104
117
|
const bundleCredentials = extractCredentialsFromBundle(entries, manifest);
|
|
105
118
|
const { userCredentials, skippedPlatform } =
|
|
106
119
|
filterCredentials(bundleCredentials);
|
|
107
120
|
|
|
108
121
|
expect(userCredentials).toHaveLength(0);
|
|
109
|
-
expect(skippedPlatform).toBe(
|
|
122
|
+
expect(skippedPlatform).toBe(vellumFields.length);
|
|
110
123
|
});
|
|
111
124
|
|
|
112
|
-
test("user credentials
|
|
125
|
+
test("user credentials pass through unchanged", () => {
|
|
126
|
+
const openaiAccount = credentialKey("openai", "api_key");
|
|
127
|
+
const anthropicAccount = credentialKey("anthropic", "api_key");
|
|
128
|
+
|
|
113
129
|
const entries = new Map<string, VBundleTarEntry>();
|
|
114
|
-
entries.set(
|
|
115
|
-
entries.set(
|
|
130
|
+
entries.set(bundlePathFor(openaiAccount), makeTarEntry("sk-user-123"));
|
|
131
|
+
entries.set(bundlePathFor(anthropicAccount), makeTarEntry("sk-ant-456"));
|
|
116
132
|
|
|
117
133
|
const manifest = makeManifest([
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
bundlePathFor(openaiAccount),
|
|
135
|
+
bundlePathFor(anthropicAccount),
|
|
120
136
|
]);
|
|
121
137
|
|
|
122
138
|
const bundleCredentials = extractCredentialsFromBundle(entries, manifest);
|
|
@@ -125,50 +141,50 @@ describe("migration import credential filtering", () => {
|
|
|
125
141
|
|
|
126
142
|
expect(userCredentials).toHaveLength(2);
|
|
127
143
|
expect(userCredentials).toContainEqual({
|
|
128
|
-
account:
|
|
144
|
+
account: openaiAccount,
|
|
129
145
|
value: "sk-user-123",
|
|
130
146
|
});
|
|
131
147
|
expect(userCredentials).toContainEqual({
|
|
132
|
-
account:
|
|
148
|
+
account: anthropicAccount,
|
|
133
149
|
value: "sk-ant-456",
|
|
134
150
|
});
|
|
135
151
|
expect(skippedPlatform).toBe(0);
|
|
136
152
|
});
|
|
137
153
|
|
|
138
|
-
test("mixed bundle with both
|
|
154
|
+
test("mixed bundle with both platform and user credentials correctly splits", () => {
|
|
155
|
+
const vellumApiKey = credentialKey("vellum", "assistant_api_key");
|
|
156
|
+
const vellumUserId = credentialKey("vellum", "platform_user_id");
|
|
157
|
+
const openaiKey = credentialKey("openai", "api_key");
|
|
158
|
+
const anthropicKey = credentialKey("anthropic", "api_key");
|
|
159
|
+
const githubToken = credentialKey("github", "api_token");
|
|
160
|
+
|
|
139
161
|
const entries = new Map<string, VBundleTarEntry>();
|
|
140
|
-
entries.set(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
);
|
|
144
|
-
entries.set(
|
|
145
|
-
"credentials/vellum:platform_user_id",
|
|
146
|
-
makeTarEntry("platform-user"),
|
|
147
|
-
);
|
|
148
|
-
entries.set("credentials/openai-key", makeTarEntry("sk-user-123"));
|
|
149
|
-
entries.set("credentials/anthropic-key", makeTarEntry("sk-ant-456"));
|
|
150
|
-
entries.set("credentials/github-token", makeTarEntry("ghp-789"));
|
|
162
|
+
entries.set(bundlePathFor(vellumApiKey), makeTarEntry("platform-key"));
|
|
163
|
+
entries.set(bundlePathFor(vellumUserId), makeTarEntry("platform-user"));
|
|
164
|
+
entries.set(bundlePathFor(openaiKey), makeTarEntry("sk-user-123"));
|
|
165
|
+
entries.set(bundlePathFor(anthropicKey), makeTarEntry("sk-ant-456"));
|
|
166
|
+
entries.set(bundlePathFor(githubToken), makeTarEntry("ghp-789"));
|
|
151
167
|
|
|
152
168
|
const manifest = makeManifest([
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
169
|
+
bundlePathFor(vellumApiKey),
|
|
170
|
+
bundlePathFor(vellumUserId),
|
|
171
|
+
bundlePathFor(openaiKey),
|
|
172
|
+
bundlePathFor(anthropicKey),
|
|
173
|
+
bundlePathFor(githubToken),
|
|
158
174
|
]);
|
|
159
175
|
|
|
160
176
|
const bundleCredentials = extractCredentialsFromBundle(entries, manifest);
|
|
161
177
|
const { userCredentials, skippedPlatform } =
|
|
162
178
|
filterCredentials(bundleCredentials);
|
|
163
179
|
|
|
164
|
-
// Only user credentials should pass through
|
|
180
|
+
// Only user credentials should pass through.
|
|
165
181
|
expect(userCredentials).toHaveLength(3);
|
|
166
182
|
const accounts = userCredentials.map((c) => c.account).sort();
|
|
167
|
-
expect(accounts).toEqual([
|
|
183
|
+
expect(accounts).toEqual([anthropicKey, githubToken, openaiKey].sort());
|
|
168
184
|
|
|
169
|
-
// No
|
|
185
|
+
// No platform credentials in the filtered output.
|
|
170
186
|
const vellumCreds = userCredentials.filter((c) =>
|
|
171
|
-
c.account.startsWith(
|
|
187
|
+
c.account.startsWith(PLATFORM_CREDENTIAL_PREFIX),
|
|
172
188
|
);
|
|
173
189
|
expect(vellumCreds).toHaveLength(0);
|
|
174
190
|
|
|
@@ -176,17 +192,22 @@ describe("migration import credential filtering", () => {
|
|
|
176
192
|
});
|
|
177
193
|
|
|
178
194
|
test("skippedPlatform count is accurate with mixed credentials", () => {
|
|
195
|
+
const vellumApiKey = credentialKey("vellum", "assistant_api_key");
|
|
196
|
+
const vellumBaseUrl = credentialKey("vellum", "platform_base_url");
|
|
197
|
+
const vellumWebhook = credentialKey("vellum", "webhook_secret");
|
|
198
|
+
const userKey = credentialKey("github", "api_token");
|
|
199
|
+
|
|
179
200
|
const entries = new Map<string, VBundleTarEntry>();
|
|
180
|
-
entries.set(
|
|
181
|
-
entries.set(
|
|
182
|
-
entries.set(
|
|
183
|
-
entries.set(
|
|
201
|
+
entries.set(bundlePathFor(vellumApiKey), makeTarEntry("v1"));
|
|
202
|
+
entries.set(bundlePathFor(vellumBaseUrl), makeTarEntry("v2"));
|
|
203
|
+
entries.set(bundlePathFor(vellumWebhook), makeTarEntry("v3"));
|
|
204
|
+
entries.set(bundlePathFor(userKey), makeTarEntry("user-val"));
|
|
184
205
|
|
|
185
206
|
const manifest = makeManifest([
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
207
|
+
bundlePathFor(vellumApiKey),
|
|
208
|
+
bundlePathFor(vellumBaseUrl),
|
|
209
|
+
bundlePathFor(vellumWebhook),
|
|
210
|
+
bundlePathFor(userKey),
|
|
190
211
|
]);
|
|
191
212
|
|
|
192
213
|
const bundleCredentials = extractCredentialsFromBundle(entries, manifest);
|
|
@@ -196,7 +217,7 @@ describe("migration import credential filtering", () => {
|
|
|
196
217
|
expect(skippedPlatform).toBe(3);
|
|
197
218
|
expect(userCredentials).toHaveLength(1);
|
|
198
219
|
expect(userCredentials[0]).toEqual({
|
|
199
|
-
account:
|
|
220
|
+
account: userKey,
|
|
200
221
|
value: "user-val",
|
|
201
222
|
});
|
|
202
223
|
|
|
@@ -205,4 +226,22 @@ describe("migration import credential filtering", () => {
|
|
|
205
226
|
userCredentials.length + skippedPlatform,
|
|
206
227
|
);
|
|
207
228
|
});
|
|
229
|
+
|
|
230
|
+
test("regression: the prefix matches credentialKey('vellum', '') so format changes propagate", () => {
|
|
231
|
+
// If credentialKey()'s format ever changes (e.g. slash → something else),
|
|
232
|
+
// this assertion will fail and the duplicated constant in
|
|
233
|
+
// migration-routes.ts must be updated to stay in sync.
|
|
234
|
+
expect(PLATFORM_CREDENTIAL_PREFIX).toBe("credential/vellum/");
|
|
235
|
+
expect(
|
|
236
|
+
credentialKey("vellum", "assistant_api_key").startsWith(
|
|
237
|
+
PLATFORM_CREDENTIAL_PREFIX,
|
|
238
|
+
),
|
|
239
|
+
).toBe(true);
|
|
240
|
+
|
|
241
|
+
// The raw "vellum:" string is not a valid CES account prefix — only
|
|
242
|
+
// the full "credential/vellum/" format from credentialKey() is correct.
|
|
243
|
+
expect(
|
|
244
|
+
credentialKey("vellum", "assistant_api_key").startsWith("vellum:"),
|
|
245
|
+
).toBe(false);
|
|
246
|
+
});
|
|
208
247
|
});
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the post-import vellum metadata reconciliation helper.
|
|
3
|
+
*
|
|
4
|
+
* After every bundle import, `reconcileVellumMetadataFromCes` walks the
|
|
5
|
+
* platform-identity fields the gateway requires and, for each one that
|
|
6
|
+
* CES already holds a value for, ensures `metadata.json` lists a
|
|
7
|
+
* matching entry. This closes the race where a provisioning write to
|
|
8
|
+
* CES completes successfully but its metadata upsert gets clobbered by
|
|
9
|
+
* the import's in-place clear or atomic swap. The reconciled set covers
|
|
10
|
+
* both the Django-provisioned fields (assistant_api_key,
|
|
11
|
+
* platform_assistant_id, platform_base_url, webhook_secret) and the
|
|
12
|
+
* client-injected identity fields (platform_organization_id,
|
|
13
|
+
* platform_user_id).
|
|
14
|
+
*
|
|
15
|
+
* We test the reconcile logic in isolation by mocking the secure-keys
|
|
16
|
+
* and metadata-store modules — the real migration handler wires the
|
|
17
|
+
* same imports, so the behavior under test matches production.
|
|
18
|
+
*
|
|
19
|
+
* Covered:
|
|
20
|
+
* - CES has all 6 fields + metadata empty → all 6 upserted.
|
|
21
|
+
* - CES has all 6 + metadata has 2 → only the missing 4 upserted.
|
|
22
|
+
* - CES has no values → nothing upserted.
|
|
23
|
+
* - CES has values + metadata already has them → no-op (no duplicate
|
|
24
|
+
* upserts).
|
|
25
|
+
* - upsert throws for one field → warning recorded, loop continues.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
29
|
+
|
|
30
|
+
import { credentialKey } from "../../../security/credential-key.js";
|
|
31
|
+
|
|
32
|
+
type MetadataRecord = {
|
|
33
|
+
credentialId: string;
|
|
34
|
+
service: string;
|
|
35
|
+
field: string;
|
|
36
|
+
allowedTools: string[];
|
|
37
|
+
allowedDomains: string[];
|
|
38
|
+
createdAt: number;
|
|
39
|
+
updatedAt: number;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const VELLUM_FIELDS = [
|
|
43
|
+
"platform_base_url",
|
|
44
|
+
"assistant_api_key",
|
|
45
|
+
"platform_assistant_id",
|
|
46
|
+
"platform_organization_id",
|
|
47
|
+
"platform_user_id",
|
|
48
|
+
"webhook_secret",
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
const upsertCalls: Array<{ service: string; field: string }> = [];
|
|
52
|
+
let metadataStore: Map<string, MetadataRecord> = new Map();
|
|
53
|
+
let cesValues: Map<string, string> = new Map();
|
|
54
|
+
let upsertImpl: (service: string, field: string) => void = (service, field) => {
|
|
55
|
+
const key = `${service}:${field}`;
|
|
56
|
+
metadataStore.set(key, {
|
|
57
|
+
credentialId: `id-${key}`,
|
|
58
|
+
service,
|
|
59
|
+
field,
|
|
60
|
+
allowedTools: [],
|
|
61
|
+
allowedDomains: [],
|
|
62
|
+
createdAt: Date.now(),
|
|
63
|
+
updatedAt: Date.now(),
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
mock.module("../../../security/secure-keys.js", () => ({
|
|
68
|
+
bulkSetSecureKeysAsync: async () => [],
|
|
69
|
+
deleteSecureKeyAsync: async () => "ok",
|
|
70
|
+
getActiveBackendName: () => "test",
|
|
71
|
+
getMaskedProviderKey: async () => null,
|
|
72
|
+
getProviderKeyAsync: async () => null,
|
|
73
|
+
getSecureKeyAsync: async (key: string) => cesValues.get(key) ?? null,
|
|
74
|
+
getSecureKeyResultAsync: async () => ({ ok: true, value: null }),
|
|
75
|
+
listSecureKeysAsync: async () => [],
|
|
76
|
+
onCesClientChanged: () => {},
|
|
77
|
+
setCesClient: () => {},
|
|
78
|
+
setCesReconnect: () => {},
|
|
79
|
+
setSecureKeyAsync: async () => true,
|
|
80
|
+
_resetBackend: () => {},
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
mock.module("../../../tools/credentials/metadata-store.js", () => ({
|
|
84
|
+
getCredentialMetadata: (service: string, field: string) =>
|
|
85
|
+
metadataStore.get(`${service}:${field}`),
|
|
86
|
+
upsertCredentialMetadata: (
|
|
87
|
+
service: string,
|
|
88
|
+
field: string,
|
|
89
|
+
_policy?: unknown,
|
|
90
|
+
) => {
|
|
91
|
+
upsertCalls.push({ service, field });
|
|
92
|
+
upsertImpl(service, field);
|
|
93
|
+
return metadataStore.get(`${service}:${field}`);
|
|
94
|
+
},
|
|
95
|
+
}));
|
|
96
|
+
|
|
97
|
+
// Import under test AFTER the mocks are set up.
|
|
98
|
+
const { reconcileVellumMetadataFromCes } =
|
|
99
|
+
(await import("../migration-routes.js")) as unknown as {
|
|
100
|
+
reconcileVellumMetadataFromCes: (sink: {
|
|
101
|
+
warnings: string[];
|
|
102
|
+
}) => Promise<void>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
beforeEach(() => {
|
|
106
|
+
upsertCalls.length = 0;
|
|
107
|
+
metadataStore = new Map();
|
|
108
|
+
cesValues = new Map();
|
|
109
|
+
upsertImpl = (service, field) => {
|
|
110
|
+
const key = `${service}:${field}`;
|
|
111
|
+
metadataStore.set(key, {
|
|
112
|
+
credentialId: `id-${key}`,
|
|
113
|
+
service,
|
|
114
|
+
field,
|
|
115
|
+
allowedTools: [],
|
|
116
|
+
allowedDomains: [],
|
|
117
|
+
createdAt: Date.now(),
|
|
118
|
+
updatedAt: Date.now(),
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
upsertCalls.length = 0;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
function seedAllInCes(): void {
|
|
128
|
+
for (const field of VELLUM_FIELDS) {
|
|
129
|
+
cesValues.set(credentialKey("vellum", field), `value-for-${field}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
describe("reconcileVellumMetadataFromCes", () => {
|
|
134
|
+
test("CES holds all fields, metadata empty → all upserted", async () => {
|
|
135
|
+
seedAllInCes();
|
|
136
|
+
const sink = { warnings: [] as string[] };
|
|
137
|
+
|
|
138
|
+
await reconcileVellumMetadataFromCes(sink);
|
|
139
|
+
|
|
140
|
+
expect(upsertCalls).toHaveLength(VELLUM_FIELDS.length);
|
|
141
|
+
expect(new Set(upsertCalls.map((c) => c.field))).toEqual(
|
|
142
|
+
new Set(VELLUM_FIELDS),
|
|
143
|
+
);
|
|
144
|
+
expect(sink.warnings).toHaveLength(0);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("CES holds all, metadata has 2 → only the missing ones upserted", async () => {
|
|
148
|
+
seedAllInCes();
|
|
149
|
+
// Pre-populate metadata for 2 of the fields.
|
|
150
|
+
const prepopulated = ["platform_base_url", "assistant_api_key"] as const;
|
|
151
|
+
for (const field of prepopulated) {
|
|
152
|
+
metadataStore.set(`vellum:${field}`, {
|
|
153
|
+
credentialId: `id-vellum:${field}`,
|
|
154
|
+
service: "vellum",
|
|
155
|
+
field,
|
|
156
|
+
allowedTools: [],
|
|
157
|
+
allowedDomains: [],
|
|
158
|
+
createdAt: 1,
|
|
159
|
+
updatedAt: 1,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const sink = { warnings: [] as string[] };
|
|
164
|
+
await reconcileVellumMetadataFromCes(sink);
|
|
165
|
+
|
|
166
|
+
const expectedMissing = VELLUM_FIELDS.filter(
|
|
167
|
+
(f) => !(prepopulated as readonly string[]).includes(f),
|
|
168
|
+
);
|
|
169
|
+
expect(upsertCalls).toHaveLength(expectedMissing.length);
|
|
170
|
+
expect(new Set(upsertCalls.map((c) => c.field))).toEqual(
|
|
171
|
+
new Set(expectedMissing),
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("covers both Django-provisioned and client-injected identity fields", async () => {
|
|
176
|
+
seedAllInCes();
|
|
177
|
+
const sink = { warnings: [] as string[] };
|
|
178
|
+
await reconcileVellumMetadataFromCes(sink);
|
|
179
|
+
|
|
180
|
+
const reconciled = new Set(upsertCalls.map((c) => c.field));
|
|
181
|
+
// Django-provisioned quartet.
|
|
182
|
+
expect(reconciled).toContain("platform_base_url");
|
|
183
|
+
expect(reconciled).toContain("assistant_api_key");
|
|
184
|
+
expect(reconciled).toContain("platform_assistant_id");
|
|
185
|
+
expect(reconciled).toContain("webhook_secret");
|
|
186
|
+
// Client-injected identity fields (onboarding / teleport / transfer).
|
|
187
|
+
expect(reconciled).toContain("platform_organization_id");
|
|
188
|
+
expect(reconciled).toContain("platform_user_id");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("CES empty → nothing upserted", async () => {
|
|
192
|
+
const sink = { warnings: [] as string[] };
|
|
193
|
+
await reconcileVellumMetadataFromCes(sink);
|
|
194
|
+
expect(upsertCalls).toHaveLength(0);
|
|
195
|
+
expect(sink.warnings).toHaveLength(0);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("CES has values, metadata already has entries → no duplicate upserts", async () => {
|
|
199
|
+
seedAllInCes();
|
|
200
|
+
for (const field of VELLUM_FIELDS) {
|
|
201
|
+
metadataStore.set(`vellum:${field}`, {
|
|
202
|
+
credentialId: `id-vellum:${field}`,
|
|
203
|
+
service: "vellum",
|
|
204
|
+
field,
|
|
205
|
+
allowedTools: [],
|
|
206
|
+
allowedDomains: [],
|
|
207
|
+
createdAt: 1,
|
|
208
|
+
updatedAt: 1,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const sink = { warnings: [] as string[] };
|
|
213
|
+
await reconcileVellumMetadataFromCes(sink);
|
|
214
|
+
|
|
215
|
+
expect(upsertCalls).toHaveLength(0);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("upsert throws for one field → warning recorded, loop continues", async () => {
|
|
219
|
+
seedAllInCes();
|
|
220
|
+
let calls = 0;
|
|
221
|
+
upsertImpl = (service, field) => {
|
|
222
|
+
calls += 1;
|
|
223
|
+
if (field === "assistant_api_key") {
|
|
224
|
+
throw new Error("simulated metadata write failure");
|
|
225
|
+
}
|
|
226
|
+
const key = `${service}:${field}`;
|
|
227
|
+
metadataStore.set(key, {
|
|
228
|
+
credentialId: `id-${key}`,
|
|
229
|
+
service,
|
|
230
|
+
field,
|
|
231
|
+
allowedTools: [],
|
|
232
|
+
allowedDomains: [],
|
|
233
|
+
createdAt: Date.now(),
|
|
234
|
+
updatedAt: Date.now(),
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const sink = { warnings: [] as string[] };
|
|
239
|
+
await reconcileVellumMetadataFromCes(sink);
|
|
240
|
+
|
|
241
|
+
// Every field was attempted (loop did not abort).
|
|
242
|
+
expect(calls).toBe(VELLUM_FIELDS.length);
|
|
243
|
+
expect(sink.warnings).toHaveLength(1);
|
|
244
|
+
expect(sink.warnings[0]).toContain("vellum:assistant_api_key");
|
|
245
|
+
});
|
|
246
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory tracker for approval prompt message timestamps.
|
|
3
|
+
*
|
|
4
|
+
* Scopes guardian reaction approvals so only reactions on a known
|
|
5
|
+
* approval prompt can resolve a pending request. Without this, a stray
|
|
6
|
+
* 👍/✅ on any message in the guardian chat could approve a pending
|
|
7
|
+
* request (since reactions are now admitted from any subscribed channel,
|
|
8
|
+
* not just tracked bot threads).
|
|
9
|
+
*
|
|
10
|
+
* Entries expire after `APPROVAL_PROMPT_TS_TTL_MS` (matches the guardian
|
|
11
|
+
* approval TTL of 30 minutes, plus grace). Populated when an approval
|
|
12
|
+
* prompt is successfully delivered; consulted before applying a guardian
|
|
13
|
+
* reaction decision.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const APPROVAL_PROMPT_TS_TTL_MS = 35 * 60 * 1000;
|
|
17
|
+
|
|
18
|
+
const tracked = new Map<string, number>();
|
|
19
|
+
|
|
20
|
+
function key(channel: string, chatId: string, ts: string): string {
|
|
21
|
+
return `${channel}\u0000${chatId}\u0000${ts}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function pruneExpired(now: number): void {
|
|
25
|
+
for (const [k, expiresAt] of tracked) {
|
|
26
|
+
if (expiresAt <= now) tracked.delete(k);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function trackApprovalPromptTs(
|
|
31
|
+
channel: string,
|
|
32
|
+
chatId: string,
|
|
33
|
+
ts: string,
|
|
34
|
+
): void {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
pruneExpired(now);
|
|
37
|
+
tracked.set(key(channel, chatId, ts), now + APPROVAL_PROMPT_TS_TTL_MS);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isTrackedApprovalPromptTs(
|
|
41
|
+
channel: string,
|
|
42
|
+
chatId: string,
|
|
43
|
+
ts: string,
|
|
44
|
+
): boolean {
|
|
45
|
+
const k = key(channel, chatId, ts);
|
|
46
|
+
const expiresAt = tracked.get(k);
|
|
47
|
+
if (expiresAt === undefined) return false;
|
|
48
|
+
if (expiresAt <= Date.now()) {
|
|
49
|
+
tracked.delete(k);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @internal Test-only — clear all tracked entries. */
|
|
56
|
+
export function _clearApprovalPromptTsTrackerForTesting(): void {
|
|
57
|
+
tracked.clear();
|
|
58
|
+
}
|
|
@@ -50,9 +50,7 @@ function canonicalizeV2ConfirmDecision(params: {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (
|
|
53
|
-
(decision === "always_allow" ||
|
|
54
|
-
decision === "always_allow_high_risk" ||
|
|
55
|
-
decision === "always_deny") &&
|
|
53
|
+
(decision === "always_allow" || decision === "always_deny") &&
|
|
56
54
|
details.persistentDecisionsAllowed
|
|
57
55
|
) {
|
|
58
56
|
return decision === "always_deny" ? "deny" : "allow";
|
|
@@ -79,7 +77,11 @@ export async function handleConfirm(
|
|
|
79
77
|
selectedScope?: string;
|
|
80
78
|
};
|
|
81
79
|
|
|
82
|
-
const { requestId,
|
|
80
|
+
const { requestId, selectedPattern, selectedScope } = body;
|
|
81
|
+
// Normalize legacy decision: older clients may still send
|
|
82
|
+
// "always_allow_high_risk" for high-risk prompts.
|
|
83
|
+
const decision =
|
|
84
|
+
body.decision === "always_allow_high_risk" ? "always_allow" : body.decision;
|
|
83
85
|
|
|
84
86
|
if (!requestId || typeof requestId !== "string") {
|
|
85
87
|
return httpError("BAD_REQUEST", "requestId is required", 400);
|
|
@@ -111,7 +113,6 @@ export async function handleConfirm(
|
|
|
111
113
|
"deny",
|
|
112
114
|
"always_allow",
|
|
113
115
|
"always_deny",
|
|
114
|
-
"always_allow_high_risk",
|
|
115
116
|
];
|
|
116
117
|
if (
|
|
117
118
|
(v2Enabled && effectiveDecision == null) ||
|
|
@@ -152,9 +153,7 @@ export async function handleConfirm(
|
|
|
152
153
|
// and selectedScope are among the options the server actually offered.
|
|
153
154
|
// This prevents a crafted request from injecting overly-broad rules.
|
|
154
155
|
const persistsRule =
|
|
155
|
-
decision === "always_allow" ||
|
|
156
|
-
decision === "always_deny" ||
|
|
157
|
-
decision === "always_allow_high_risk";
|
|
156
|
+
decision === "always_allow" || decision === "always_deny";
|
|
158
157
|
if (persistsRule && (selectedPattern || selectedScope)) {
|
|
159
158
|
const confirmation = peeked.confirmationDetails;
|
|
160
159
|
if (!confirmation) {
|
|
@@ -312,10 +311,9 @@ export async function handleTrustRule(
|
|
|
312
311
|
pattern?: string;
|
|
313
312
|
scope?: string;
|
|
314
313
|
decision?: string;
|
|
315
|
-
allowHighRisk?: boolean;
|
|
316
314
|
};
|
|
317
315
|
|
|
318
|
-
const { requestId, pattern, scope, decision
|
|
316
|
+
const { requestId, pattern, scope, decision } = body;
|
|
319
317
|
|
|
320
318
|
if (!requestId || typeof requestId !== "string") {
|
|
321
319
|
return httpError("BAD_REQUEST", "requestId is required", 400);
|
|
@@ -396,9 +394,10 @@ export async function handleTrustRule(
|
|
|
396
394
|
const tool = getTool(confirmation.toolName);
|
|
397
395
|
const executionTarget =
|
|
398
396
|
tool?.origin === "skill" ? confirmation.executionTarget : undefined;
|
|
397
|
+
|
|
398
|
+
// Canonicalization is handled inside addRule — no need to pre-parse here.
|
|
399
399
|
addRule(confirmation.toolName, pattern, scope, decision, undefined, {
|
|
400
|
-
|
|
401
|
-
executionTarget,
|
|
400
|
+
...(executionTarget != null ? { executionTarget } : {}),
|
|
402
401
|
});
|
|
403
402
|
log.info(
|
|
404
403
|
{ tool: confirmation.toolName, pattern, scope, decision, requestId },
|
|
@@ -504,7 +503,7 @@ export function approvalRouteDefinitions(): RouteDefinition[] {
|
|
|
504
503
|
decision: z
|
|
505
504
|
.string()
|
|
506
505
|
.describe(
|
|
507
|
-
"One of: allow, allow_10m, allow_conversation, deny, always_allow, always_deny
|
|
506
|
+
"One of: allow, allow_10m, allow_conversation, deny, always_allow, always_deny",
|
|
508
507
|
),
|
|
509
508
|
selectedPattern: z
|
|
510
509
|
.string()
|
|
@@ -551,10 +550,6 @@ export function approvalRouteDefinitions(): RouteDefinition[] {
|
|
|
551
550
|
pattern: z.string().describe("Allowlist pattern"),
|
|
552
551
|
scope: z.string().describe("Scope for the rule"),
|
|
553
552
|
decision: z.string().describe("allow or deny"),
|
|
554
|
-
allowHighRisk: z
|
|
555
|
-
.boolean()
|
|
556
|
-
.describe("Allow high-risk invocations")
|
|
557
|
-
.optional(),
|
|
558
553
|
}),
|
|
559
554
|
responseBody: z.object({
|
|
560
555
|
accepted: z.boolean(),
|