@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
|
@@ -2,6 +2,8 @@ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
4
4
|
|
|
5
|
+
import { ruleScope } from "@vellumai/ces-contracts";
|
|
6
|
+
|
|
5
7
|
// Create a temp directory for the trust file
|
|
6
8
|
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
7
9
|
|
|
@@ -129,27 +131,13 @@ describe("Trust Store", () => {
|
|
|
129
131
|
expect(userRules[2].priority).toBe(0);
|
|
130
132
|
});
|
|
131
133
|
|
|
132
|
-
test("
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
expect(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const rules = getAllRules();
|
|
140
|
-
const found = rules.find((r) => r.id === rule.id);
|
|
141
|
-
expect(found).toBeDefined();
|
|
142
|
-
expect(found!.allowHighRisk).toBe(true);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("addRule without allowHighRisk option does not set the field", () => {
|
|
146
|
-
const rule = addRule("bash", "git *", "/tmp");
|
|
147
|
-
expect(rule.allowHighRisk).toBeUndefined();
|
|
148
|
-
// Verify on disk
|
|
149
|
-
const raw = JSON.parse(readFileSync(trustPath, "utf-8"));
|
|
150
|
-
const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
|
|
151
|
-
expect(diskRule).toBeDefined();
|
|
152
|
-
expect(diskRule).not.toHaveProperty("allowHighRisk");
|
|
134
|
+
test("allowHighRisk option is stripped during normalization", () => {
|
|
135
|
+
// allowHighRisk is no longer persisted — the parser strips it.
|
|
136
|
+
const rule = addRule("bash", "sudo *", "everywhere", "allow", 100);
|
|
137
|
+
// Verify the field is not on the rule
|
|
138
|
+
expect(
|
|
139
|
+
(rule as unknown as Record<string, unknown>).allowHighRisk,
|
|
140
|
+
).toBeUndefined();
|
|
153
141
|
});
|
|
154
142
|
|
|
155
143
|
test("at same priority deny rules sort before allow rules", () => {
|
|
@@ -176,7 +164,7 @@ describe("Trust Store", () => {
|
|
|
176
164
|
expect(found!.executionTarget).toBe("sandbox");
|
|
177
165
|
});
|
|
178
166
|
|
|
179
|
-
test("accepts
|
|
167
|
+
test("accepts executionTarget option (allowHighRisk no longer supported)", () => {
|
|
180
168
|
const rule = addRule(
|
|
181
169
|
"risky_tool",
|
|
182
170
|
"risky_tool:*",
|
|
@@ -184,18 +172,16 @@ describe("Trust Store", () => {
|
|
|
184
172
|
"allow",
|
|
185
173
|
100,
|
|
186
174
|
{
|
|
187
|
-
allowHighRisk: true,
|
|
188
175
|
executionTarget: "host",
|
|
189
176
|
},
|
|
190
177
|
);
|
|
191
|
-
expect(rule.allowHighRisk).toBe(true);
|
|
192
178
|
expect(rule.executionTarget).toBe("host");
|
|
193
179
|
|
|
194
|
-
// Verify on disk
|
|
180
|
+
// Verify on disk — allowHighRisk should not appear
|
|
195
181
|
const raw = JSON.parse(readFileSync(trustPath, "utf-8"));
|
|
196
182
|
const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
|
|
197
183
|
expect(diskRule).toBeDefined();
|
|
198
|
-
expect(diskRule
|
|
184
|
+
expect(diskRule).not.toHaveProperty("allowHighRisk");
|
|
199
185
|
expect(diskRule.executionTarget).toBe("host");
|
|
200
186
|
});
|
|
201
187
|
|
|
@@ -305,6 +291,56 @@ describe("Trust Store", () => {
|
|
|
305
291
|
expect(updated.priority).toBe(100);
|
|
306
292
|
expect(updated.createdAt).toBe(rule.createdAt);
|
|
307
293
|
});
|
|
294
|
+
|
|
295
|
+
test("does not set userModifiedAt on non-default rules", () => {
|
|
296
|
+
const rule = addRule("bash", "git *", "/tmp");
|
|
297
|
+
const updated = updateRule(rule.id, { decision: "deny" });
|
|
298
|
+
expect(updated.userModifiedAt).toBeUndefined();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("sets userModifiedAt when updating a default rule", () => {
|
|
302
|
+
const before = Date.now();
|
|
303
|
+
const updated = updateRule("default:ask-host_bash-global", {
|
|
304
|
+
decision: "allow",
|
|
305
|
+
});
|
|
306
|
+
expect(updated.userModifiedAt).toBeGreaterThanOrEqual(before);
|
|
307
|
+
expect(updated.userModifiedAt).toBeLessThanOrEqual(Date.now());
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("persists userModifiedAt to disk for default rules", () => {
|
|
311
|
+
updateRule("default:ask-host_bash-global", { decision: "allow" });
|
|
312
|
+
clearCache();
|
|
313
|
+
const rules = getAllRules();
|
|
314
|
+
const found = rules.find((r) => r.id === "default:ask-host_bash-global");
|
|
315
|
+
expect(found).toBeDefined();
|
|
316
|
+
expect(found!.userModifiedAt).toBeGreaterThan(0);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("does not set userModifiedAt on no-op default rule update", () => {
|
|
320
|
+
// Updating a default rule with values identical to the template
|
|
321
|
+
// should NOT set userModifiedAt — the rule hasn't actually diverged.
|
|
322
|
+
const updated = updateRule("default:ask-host_bash-global", {
|
|
323
|
+
decision: "ask",
|
|
324
|
+
});
|
|
325
|
+
expect(updated.userModifiedAt).toBeUndefined();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("clears userModifiedAt when default rule is reset to template values", () => {
|
|
329
|
+
// First, modify the rule to diverge from the template
|
|
330
|
+
updateRule("default:ask-host_bash-global", { decision: "allow" });
|
|
331
|
+
let found = getAllRules().find(
|
|
332
|
+
(r) => r.id === "default:ask-host_bash-global",
|
|
333
|
+
)!;
|
|
334
|
+
expect(found.userModifiedAt).toBeGreaterThan(0);
|
|
335
|
+
|
|
336
|
+
// Now reset it back to the template value
|
|
337
|
+
updateRule("default:ask-host_bash-global", { decision: "ask" });
|
|
338
|
+
found = getAllRules().find(
|
|
339
|
+
(r) => r.id === "default:ask-host_bash-global",
|
|
340
|
+
)!;
|
|
341
|
+
// userModifiedAt should be cleared since rule matches template again
|
|
342
|
+
expect(found.userModifiedAt).toBeUndefined();
|
|
343
|
+
});
|
|
308
344
|
});
|
|
309
345
|
|
|
310
346
|
// ── findMatchingRule ────────────────────────────────────────────
|
|
@@ -724,9 +760,12 @@ describe("Trust Store", () => {
|
|
|
724
760
|
rule.id === "default:allow-bash-rm-bootstrap" ||
|
|
725
761
|
rule.id === "default:allow-bash-rm-updates"
|
|
726
762
|
) {
|
|
727
|
-
expect(rule
|
|
763
|
+
expect(ruleScope(rule)).toBe(testDir);
|
|
728
764
|
} else {
|
|
729
|
-
|
|
765
|
+
// Non-scoped tool families (managed skill tools, skill_load) no
|
|
766
|
+
// longer carry an explicit scope field after normalization, but
|
|
767
|
+
// ruleScope() returns "everywhere" for rules without scope.
|
|
768
|
+
expect(ruleScope(rule)).toBe("everywhere");
|
|
730
769
|
}
|
|
731
770
|
}
|
|
732
771
|
});
|
|
@@ -740,23 +779,6 @@ describe("Trust Store", () => {
|
|
|
740
779
|
].sort();
|
|
741
780
|
expect(defaultTools).toEqual([
|
|
742
781
|
"bash",
|
|
743
|
-
"browser_attach",
|
|
744
|
-
"browser_click",
|
|
745
|
-
"browser_close",
|
|
746
|
-
"browser_detach",
|
|
747
|
-
"browser_extract",
|
|
748
|
-
"browser_fill_credential",
|
|
749
|
-
"browser_hover",
|
|
750
|
-
"browser_navigate",
|
|
751
|
-
"browser_press_key",
|
|
752
|
-
"browser_screenshot",
|
|
753
|
-
"browser_scroll",
|
|
754
|
-
"browser_select_option",
|
|
755
|
-
"browser_snapshot",
|
|
756
|
-
"browser_status",
|
|
757
|
-
"browser_type",
|
|
758
|
-
"browser_wait_for",
|
|
759
|
-
"browser_wait_for_download",
|
|
760
782
|
"computer_use_click",
|
|
761
783
|
"computer_use_drag",
|
|
762
784
|
"computer_use_key",
|
|
@@ -911,7 +933,6 @@ describe("Trust Store", () => {
|
|
|
911
933
|
expect(match).not.toBeNull();
|
|
912
934
|
expect(match!.id).toBe("default:allow-bash-rm-bootstrap");
|
|
913
935
|
expect(match!.decision).toBe("allow");
|
|
914
|
-
expect(match!.allowHighRisk).toBe(true);
|
|
915
936
|
// Outside workspace, the bootstrap rule doesn't match — without
|
|
916
937
|
// IS_CONTAINERIZED there is no catch-all bash allow rule either.
|
|
917
938
|
const other = findHighestPriorityRule(
|
|
@@ -932,7 +953,6 @@ describe("Trust Store", () => {
|
|
|
932
953
|
expect(match).not.toBeNull();
|
|
933
954
|
expect(match!.id).toBe("default:allow-bash-rm-updates");
|
|
934
955
|
expect(match!.decision).toBe("allow");
|
|
935
|
-
expect(match!.allowHighRisk).toBe(true);
|
|
936
956
|
// Outside workspace, should NOT match the updates rule — without
|
|
937
957
|
// IS_CONTAINERIZED there is no catch-all bash allow rule either.
|
|
938
958
|
const other = findHighestPriorityRule(
|
|
@@ -1053,7 +1073,8 @@ describe("Trust Store", () => {
|
|
|
1053
1073
|
expect(skillLoadRule!.tool).toBe("skill_load");
|
|
1054
1074
|
expect(skillLoadRule!.pattern).toBe("skill_load:*");
|
|
1055
1075
|
expect(skillLoadRule!.decision).toBe("allow");
|
|
1056
|
-
|
|
1076
|
+
// skill_load is a non-scoped tool — template omits scope
|
|
1077
|
+
expect(skillLoadRule!.scope).toBeUndefined();
|
|
1057
1078
|
});
|
|
1058
1079
|
|
|
1059
1080
|
test("findHighestPriorityRule matches default allow for skill_load", () => {
|
|
@@ -1079,58 +1100,6 @@ describe("Trust Store", () => {
|
|
|
1079
1100
|
expect(match!.decision).toBe("allow");
|
|
1080
1101
|
});
|
|
1081
1102
|
|
|
1082
|
-
// ── default allow: browser tools ────────────────────────────
|
|
1083
|
-
|
|
1084
|
-
test("all browser tools have default allow rules", () => {
|
|
1085
|
-
const templates = getDefaultRuleTemplates();
|
|
1086
|
-
const browserTools = [
|
|
1087
|
-
"browser_navigate",
|
|
1088
|
-
"browser_snapshot",
|
|
1089
|
-
"browser_screenshot",
|
|
1090
|
-
"browser_close",
|
|
1091
|
-
"browser_attach",
|
|
1092
|
-
"browser_detach",
|
|
1093
|
-
"browser_click",
|
|
1094
|
-
"browser_type",
|
|
1095
|
-
"browser_press_key",
|
|
1096
|
-
"browser_scroll",
|
|
1097
|
-
"browser_select_option",
|
|
1098
|
-
"browser_hover",
|
|
1099
|
-
"browser_wait_for",
|
|
1100
|
-
"browser_extract",
|
|
1101
|
-
"browser_wait_for_download",
|
|
1102
|
-
"browser_fill_credential",
|
|
1103
|
-
"browser_status",
|
|
1104
|
-
];
|
|
1105
|
-
|
|
1106
|
-
for (const tool of browserTools) {
|
|
1107
|
-
const rule = templates.find(
|
|
1108
|
-
(t) => t.id === `default:allow-${tool}-global`,
|
|
1109
|
-
);
|
|
1110
|
-
expect(rule).toBeDefined();
|
|
1111
|
-
expect(rule!.tool).toBe(tool);
|
|
1112
|
-
// browser_navigate uses standalone "**" because its candidates
|
|
1113
|
-
// contain URLs with "/" that single "*" cannot match.
|
|
1114
|
-
const expectedPattern =
|
|
1115
|
-
tool === "browser_navigate" ? "**" : `${tool}:*`;
|
|
1116
|
-
expect(rule!.pattern).toBe(expectedPattern);
|
|
1117
|
-
expect(rule!.decision).toBe("allow");
|
|
1118
|
-
expect(rule!.scope).toBe("everywhere");
|
|
1119
|
-
}
|
|
1120
|
-
});
|
|
1121
|
-
|
|
1122
|
-
test("browser tool default rules match via findHighestPriorityRule", () => {
|
|
1123
|
-
// Use a candidate without slashes so the `browser_snapshot:*` pattern
|
|
1124
|
-
// matches (minimatch `*` does not cross `/` boundaries).
|
|
1125
|
-
const result = findHighestPriorityRule(
|
|
1126
|
-
"browser_snapshot",
|
|
1127
|
-
["browser_snapshot:"],
|
|
1128
|
-
"/tmp",
|
|
1129
|
-
);
|
|
1130
|
-
expect(result).toBeDefined();
|
|
1131
|
-
expect(result!.decision).toBe("allow");
|
|
1132
|
-
});
|
|
1133
|
-
|
|
1134
1103
|
test("no default ask rules exist for file_read on skill source paths", () => {
|
|
1135
1104
|
const rules = getAllRules();
|
|
1136
1105
|
// There should be no default rules with IDs matching file_read for skill sources
|
|
@@ -1167,16 +1136,80 @@ describe("Trust Store", () => {
|
|
|
1167
1136
|
expect(match!.id).toBe("default:ask-file_edit-managed-skills");
|
|
1168
1137
|
expect(match!.decision).toBe("ask");
|
|
1169
1138
|
});
|
|
1139
|
+
|
|
1140
|
+
// ── userModifiedAt and backfill migration ──────────────────────
|
|
1141
|
+
|
|
1142
|
+
test("default rules without userModifiedAt are migrated when template changes", () => {
|
|
1143
|
+
// First load backfills defaults
|
|
1144
|
+
getAllRules();
|
|
1145
|
+
// Manually alter a default rule on disk to simulate a template change
|
|
1146
|
+
// (the rule on disk has old values, template has new ones)
|
|
1147
|
+
const raw = JSON.parse(readFileSync(trustPath, "utf-8"));
|
|
1148
|
+
const idx = raw.rules.findIndex(
|
|
1149
|
+
(r: { id: string }) => r.id === "default:ask-host_bash-global",
|
|
1150
|
+
);
|
|
1151
|
+
expect(idx).toBeGreaterThanOrEqual(0);
|
|
1152
|
+
// Manually set an old priority to simulate the rule diverging from template
|
|
1153
|
+
raw.rules[idx].priority = 9999;
|
|
1154
|
+
writeFileSync(trustPath, JSON.stringify(raw, null, 2));
|
|
1155
|
+
clearCache();
|
|
1156
|
+
const rules = getAllRules();
|
|
1157
|
+
const found = rules.find((r) => r.id === "default:ask-host_bash-global");
|
|
1158
|
+
expect(found).toBeDefined();
|
|
1159
|
+
// Should be migrated back to the template priority (50)
|
|
1160
|
+
expect(found!.priority).toBe(50);
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
test("default rules with userModifiedAt are preserved during backfill migration", () => {
|
|
1164
|
+
// First load backfills defaults
|
|
1165
|
+
getAllRules();
|
|
1166
|
+
// Modify the rule via updateRule to set userModifiedAt
|
|
1167
|
+
updateRule("default:ask-host_bash-global", { decision: "allow" });
|
|
1168
|
+
// Verify userModifiedAt is set
|
|
1169
|
+
let rules = getAllRules();
|
|
1170
|
+
let found = rules.find((r) => r.id === "default:ask-host_bash-global");
|
|
1171
|
+
expect(found).toBeDefined();
|
|
1172
|
+
expect(found!.userModifiedAt).toBeGreaterThan(0);
|
|
1173
|
+
expect(found!.decision).toBe("allow");
|
|
1174
|
+
|
|
1175
|
+
// Now simulate a template change by altering what the template expects:
|
|
1176
|
+
// on disk the rule has decision=allow + userModifiedAt, but the template
|
|
1177
|
+
// would try to migrate it back to decision=ask. Since userModifiedAt is
|
|
1178
|
+
// set, backfillDefaults should skip it.
|
|
1179
|
+
clearCache();
|
|
1180
|
+
rules = getAllRules();
|
|
1181
|
+
found = rules.find((r) => r.id === "default:ask-host_bash-global");
|
|
1182
|
+
expect(found).toBeDefined();
|
|
1183
|
+
// The user's override should be preserved
|
|
1184
|
+
expect(found!.decision).toBe("allow");
|
|
1185
|
+
expect(found!.userModifiedAt).toBeGreaterThan(0);
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
test("userModifiedAt survives round-trip through disk", () => {
|
|
1189
|
+
getAllRules(); // backfill
|
|
1190
|
+
updateRule("default:ask-host_bash-global", { priority: 999 });
|
|
1191
|
+
const before = getAllRules().find(
|
|
1192
|
+
(r) => r.id === "default:ask-host_bash-global",
|
|
1193
|
+
)!;
|
|
1194
|
+
expect(before.userModifiedAt).toBeGreaterThan(0);
|
|
1195
|
+
|
|
1196
|
+
// Round-trip through disk
|
|
1197
|
+
clearCache();
|
|
1198
|
+
const after = getAllRules().find(
|
|
1199
|
+
(r) => r.id === "default:ask-host_bash-global",
|
|
1200
|
+
)!;
|
|
1201
|
+
expect(after.userModifiedAt).toBe(before.userModifiedAt);
|
|
1202
|
+
expect(after.priority).toBe(999);
|
|
1203
|
+
});
|
|
1170
1204
|
});
|
|
1171
1205
|
|
|
1172
1206
|
// ── trust rule schema v3 (PR 14) ──────────────────────────────
|
|
1173
1207
|
|
|
1174
1208
|
describe("trust rule schema v3 (PR 14)", () => {
|
|
1175
|
-
test("new rules can include v3 optional fields", () => {
|
|
1209
|
+
test("new rules can include v3 optional fields (executionTarget)", () => {
|
|
1176
1210
|
const rule = addRule("bash", "git *", "/tmp");
|
|
1177
|
-
// Manually set v3 optional
|
|
1211
|
+
// Manually set v3 optional field on the rule and persist
|
|
1178
1212
|
rule.executionTarget = "/usr/local/bin/node";
|
|
1179
|
-
rule.allowHighRisk = true;
|
|
1180
1213
|
// Re-persist the updated rules
|
|
1181
1214
|
const rules = getAllRules().map((r) => (r.id === rule.id ? rule : r));
|
|
1182
1215
|
// Write directly to verify round-trip
|
|
@@ -1187,7 +1220,6 @@ describe("Trust Store", () => {
|
|
|
1187
1220
|
const found = reloaded.find((r) => r.id === rule.id);
|
|
1188
1221
|
expect(found).toBeDefined();
|
|
1189
1222
|
expect(found!.executionTarget).toBe("/usr/local/bin/node");
|
|
1190
|
-
expect(found!.allowHighRisk).toBe(true);
|
|
1191
1223
|
});
|
|
1192
1224
|
|
|
1193
1225
|
test("trust file persists with version 3", () => {
|
|
@@ -1222,7 +1254,6 @@ describe("Trust Store", () => {
|
|
|
1222
1254
|
priority: 100,
|
|
1223
1255
|
createdAt: 7000,
|
|
1224
1256
|
executionTarget: "/usr/bin/node",
|
|
1225
|
-
allowHighRisk: false,
|
|
1226
1257
|
},
|
|
1227
1258
|
{
|
|
1228
1259
|
id: "v3-without-options",
|
|
@@ -1242,7 +1273,6 @@ describe("Trust Store", () => {
|
|
|
1242
1273
|
const withOptions = rules.find((r) => r.id === "v3-with-options");
|
|
1243
1274
|
expect(withOptions).toBeDefined();
|
|
1244
1275
|
expect(withOptions!.executionTarget).toBe("/usr/bin/node");
|
|
1245
|
-
expect(withOptions!.allowHighRisk).toBe(false);
|
|
1246
1276
|
|
|
1247
1277
|
// Rule without optional fields should remain without them
|
|
1248
1278
|
const withoutOptions = rules.find((r) => r.id === "v3-without-options");
|
|
@@ -1528,7 +1558,7 @@ describe("Trust Store", () => {
|
|
|
1528
1558
|
|
|
1529
1559
|
test("single-star wildcard matches flat candidates only", () => {
|
|
1530
1560
|
// "network_request:*" won't match URLs with slashes — consistent
|
|
1531
|
-
// with the behavior of web_fetch:*
|
|
1561
|
+
// with the behavior of web_fetch:* patterns.
|
|
1532
1562
|
addRule("network_request", "network_request:*", "everywhere");
|
|
1533
1563
|
const noSlashMatch = findHighestPriorityRule(
|
|
1534
1564
|
"network_request",
|
|
@@ -1630,7 +1660,7 @@ describe("Trust Store", () => {
|
|
|
1630
1660
|
expect(rule!.decision).toBe("allow");
|
|
1631
1661
|
});
|
|
1632
1662
|
|
|
1633
|
-
test("
|
|
1663
|
+
test("network_request rules match regardless of working directory (URL tools ignore scope)", () => {
|
|
1634
1664
|
addRule(
|
|
1635
1665
|
"network_request",
|
|
1636
1666
|
"network_request:https://api.example.com/*",
|
|
@@ -1643,12 +1673,15 @@ describe("Trust Store", () => {
|
|
|
1643
1673
|
);
|
|
1644
1674
|
expect(inScope).not.toBeNull();
|
|
1645
1675
|
|
|
1676
|
+
// URL tools (network_request) do not support scope — the rule matches
|
|
1677
|
+
// regardless of working directory because scope is stripped during
|
|
1678
|
+
// normalization.
|
|
1646
1679
|
const outOfScope = findHighestPriorityRule(
|
|
1647
1680
|
"network_request",
|
|
1648
1681
|
["network_request:https://api.example.com/*"],
|
|
1649
1682
|
"/tmp/other",
|
|
1650
1683
|
);
|
|
1651
|
-
expect(outOfScope).toBeNull();
|
|
1684
|
+
expect(outOfScope).not.toBeNull();
|
|
1652
1685
|
});
|
|
1653
1686
|
});
|
|
1654
1687
|
});
|
|
@@ -1678,3 +1711,303 @@ describe("computer-use tool trust rule matching", () => {
|
|
|
1678
1711
|
}
|
|
1679
1712
|
});
|
|
1680
1713
|
});
|
|
1714
|
+
|
|
1715
|
+
// ── canonical parser normalization-on-load ─────────────────────────────────
|
|
1716
|
+
|
|
1717
|
+
describe("canonical parser normalization-on-load", () => {
|
|
1718
|
+
beforeEach(() => {
|
|
1719
|
+
clearCache();
|
|
1720
|
+
try {
|
|
1721
|
+
rmSync(trustPath);
|
|
1722
|
+
} catch {
|
|
1723
|
+
/* may not exist */
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
|
|
1727
|
+
test("URL rule with executionTarget is stripped on load and re-saved", () => {
|
|
1728
|
+
// A URL rule (web_fetch) should not carry executionTarget — the canonical
|
|
1729
|
+
// parser strips it and marks the file for re-save.
|
|
1730
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1731
|
+
writeFileSync(
|
|
1732
|
+
trustPath,
|
|
1733
|
+
JSON.stringify({
|
|
1734
|
+
version: 3,
|
|
1735
|
+
rules: [
|
|
1736
|
+
{
|
|
1737
|
+
id: "url-rule-with-et",
|
|
1738
|
+
tool: "web_fetch",
|
|
1739
|
+
pattern: "web_fetch:https://example.com/*",
|
|
1740
|
+
scope: "everywhere",
|
|
1741
|
+
decision: "allow",
|
|
1742
|
+
priority: 100,
|
|
1743
|
+
createdAt: 1000,
|
|
1744
|
+
executionTarget: "/usr/bin/node",
|
|
1745
|
+
},
|
|
1746
|
+
],
|
|
1747
|
+
}),
|
|
1748
|
+
);
|
|
1749
|
+
clearCache();
|
|
1750
|
+
const rules = getAllRules();
|
|
1751
|
+
const found = rules.find((r) => r.id === "url-rule-with-et");
|
|
1752
|
+
expect(found).toBeDefined();
|
|
1753
|
+
// executionTarget should have been stripped by the canonical parser
|
|
1754
|
+
expect(found).not.toHaveProperty("executionTarget");
|
|
1755
|
+
|
|
1756
|
+
// Verify the re-save persisted the normalized rule
|
|
1757
|
+
const disk = JSON.parse(readFileSync(trustPath, "utf-8"));
|
|
1758
|
+
const diskRule = disk.rules.find(
|
|
1759
|
+
(r: { id: string }) => r.id === "url-rule-with-et",
|
|
1760
|
+
);
|
|
1761
|
+
expect(diskRule).toBeDefined();
|
|
1762
|
+
expect(diskRule).not.toHaveProperty("executionTarget");
|
|
1763
|
+
});
|
|
1764
|
+
|
|
1765
|
+
test("URL rule with allowHighRisk is stripped on load (normalized)", () => {
|
|
1766
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1767
|
+
writeFileSync(
|
|
1768
|
+
trustPath,
|
|
1769
|
+
JSON.stringify({
|
|
1770
|
+
version: 3,
|
|
1771
|
+
rules: [
|
|
1772
|
+
{
|
|
1773
|
+
id: "url-rule-with-ahr",
|
|
1774
|
+
tool: "web_fetch",
|
|
1775
|
+
pattern: "**",
|
|
1776
|
+
scope: "everywhere",
|
|
1777
|
+
decision: "allow",
|
|
1778
|
+
priority: 100,
|
|
1779
|
+
createdAt: 2000,
|
|
1780
|
+
allowHighRisk: true,
|
|
1781
|
+
},
|
|
1782
|
+
],
|
|
1783
|
+
}),
|
|
1784
|
+
);
|
|
1785
|
+
clearCache();
|
|
1786
|
+
const rules = getAllRules();
|
|
1787
|
+
const found = rules.find((r) => r.id === "url-rule-with-ahr");
|
|
1788
|
+
expect(found).toBeDefined();
|
|
1789
|
+
// allowHighRisk is stripped during normalization
|
|
1790
|
+
expect(
|
|
1791
|
+
(found as unknown as Record<string, unknown>).allowHighRisk,
|
|
1792
|
+
).toBeUndefined();
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
test("scoped rule preserves executionTarget but strips allowHighRisk on load", () => {
|
|
1796
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1797
|
+
writeFileSync(
|
|
1798
|
+
trustPath,
|
|
1799
|
+
JSON.stringify({
|
|
1800
|
+
version: 3,
|
|
1801
|
+
rules: [
|
|
1802
|
+
{
|
|
1803
|
+
id: "scoped-rule-with-opts",
|
|
1804
|
+
tool: "bash",
|
|
1805
|
+
pattern: "npm *",
|
|
1806
|
+
scope: "/tmp",
|
|
1807
|
+
decision: "allow",
|
|
1808
|
+
priority: 100,
|
|
1809
|
+
createdAt: 3000,
|
|
1810
|
+
executionTarget: "/usr/local/bin/node",
|
|
1811
|
+
allowHighRisk: true,
|
|
1812
|
+
},
|
|
1813
|
+
],
|
|
1814
|
+
}),
|
|
1815
|
+
);
|
|
1816
|
+
clearCache();
|
|
1817
|
+
const rules = getAllRules();
|
|
1818
|
+
const found = rules.find((r) => r.id === "scoped-rule-with-opts");
|
|
1819
|
+
expect(found).toBeDefined();
|
|
1820
|
+
expect((found as { executionTarget?: string }).executionTarget).toBe(
|
|
1821
|
+
"/usr/local/bin/node",
|
|
1822
|
+
);
|
|
1823
|
+
// allowHighRisk is stripped during normalization
|
|
1824
|
+
expect(
|
|
1825
|
+
(found as unknown as Record<string, unknown>).allowHighRisk,
|
|
1826
|
+
).toBeUndefined();
|
|
1827
|
+
});
|
|
1828
|
+
|
|
1829
|
+
test("normalization on v2 file triggers re-save as v3", () => {
|
|
1830
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1831
|
+
writeFileSync(
|
|
1832
|
+
trustPath,
|
|
1833
|
+
JSON.stringify({
|
|
1834
|
+
version: 2,
|
|
1835
|
+
rules: [
|
|
1836
|
+
{
|
|
1837
|
+
id: "v2-url-rule",
|
|
1838
|
+
tool: "network_request",
|
|
1839
|
+
pattern: "network_request:https://api.test.com/*",
|
|
1840
|
+
scope: "everywhere",
|
|
1841
|
+
decision: "allow",
|
|
1842
|
+
priority: 100,
|
|
1843
|
+
createdAt: 4000,
|
|
1844
|
+
executionTarget: "stale-value",
|
|
1845
|
+
},
|
|
1846
|
+
],
|
|
1847
|
+
}),
|
|
1848
|
+
);
|
|
1849
|
+
clearCache();
|
|
1850
|
+
getAllRules();
|
|
1851
|
+
|
|
1852
|
+
// File should be re-saved as v3 with normalized rules
|
|
1853
|
+
const disk = JSON.parse(readFileSync(trustPath, "utf-8"));
|
|
1854
|
+
expect(disk.version).toBe(3);
|
|
1855
|
+
const diskRule = disk.rules.find(
|
|
1856
|
+
(r: { id: string }) => r.id === "v2-url-rule",
|
|
1857
|
+
);
|
|
1858
|
+
expect(diskRule).toBeDefined();
|
|
1859
|
+
expect(diskRule).not.toHaveProperty("executionTarget");
|
|
1860
|
+
});
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
// ── optional-scope matching fallback ──────────────────────────────────────
|
|
1864
|
+
|
|
1865
|
+
describe("optional-scope matching fallback", () => {
|
|
1866
|
+
beforeEach(() => {
|
|
1867
|
+
clearCache();
|
|
1868
|
+
try {
|
|
1869
|
+
rmSync(trustPath);
|
|
1870
|
+
} catch {
|
|
1871
|
+
/* may not exist */
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
test("rule with missing scope is normalized to everywhere and matches any dir", () => {
|
|
1876
|
+
// Simulate a persisted rule that somehow lacks a scope field —
|
|
1877
|
+
// the canonical parser normalizes it to "everywhere".
|
|
1878
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1879
|
+
writeFileSync(
|
|
1880
|
+
trustPath,
|
|
1881
|
+
JSON.stringify({
|
|
1882
|
+
version: 3,
|
|
1883
|
+
rules: [
|
|
1884
|
+
{
|
|
1885
|
+
id: "no-scope-rule",
|
|
1886
|
+
tool: "bash",
|
|
1887
|
+
pattern: "echo *",
|
|
1888
|
+
decision: "allow",
|
|
1889
|
+
priority: 200,
|
|
1890
|
+
createdAt: 5000,
|
|
1891
|
+
},
|
|
1892
|
+
],
|
|
1893
|
+
}),
|
|
1894
|
+
);
|
|
1895
|
+
clearCache();
|
|
1896
|
+
const rules = getAllRules();
|
|
1897
|
+
const found = rules.find((r) => r.id === "no-scope-rule");
|
|
1898
|
+
expect(found).toBeDefined();
|
|
1899
|
+
expect(found!.scope).toBe("everywhere");
|
|
1900
|
+
|
|
1901
|
+
// Should match any working directory since scope defaults to everywhere
|
|
1902
|
+
const match = findHighestPriorityRule(
|
|
1903
|
+
"bash",
|
|
1904
|
+
["echo hello"],
|
|
1905
|
+
"/any/random/dir",
|
|
1906
|
+
);
|
|
1907
|
+
expect(match).not.toBeNull();
|
|
1908
|
+
expect(match!.id).toBe("no-scope-rule");
|
|
1909
|
+
});
|
|
1910
|
+
|
|
1911
|
+
test("rule with empty-string scope is normalized to everywhere", () => {
|
|
1912
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1913
|
+
writeFileSync(
|
|
1914
|
+
trustPath,
|
|
1915
|
+
JSON.stringify({
|
|
1916
|
+
version: 3,
|
|
1917
|
+
rules: [
|
|
1918
|
+
{
|
|
1919
|
+
id: "empty-scope-rule",
|
|
1920
|
+
tool: "bash",
|
|
1921
|
+
pattern: "ls *",
|
|
1922
|
+
scope: "",
|
|
1923
|
+
decision: "allow",
|
|
1924
|
+
priority: 200,
|
|
1925
|
+
createdAt: 6000,
|
|
1926
|
+
},
|
|
1927
|
+
],
|
|
1928
|
+
}),
|
|
1929
|
+
);
|
|
1930
|
+
clearCache();
|
|
1931
|
+
const rules = getAllRules();
|
|
1932
|
+
const found = rules.find((r) => r.id === "empty-scope-rule");
|
|
1933
|
+
expect(found).toBeDefined();
|
|
1934
|
+
// The ruleScope helper treats "" as "everywhere"
|
|
1935
|
+
const match = findHighestPriorityRule(
|
|
1936
|
+
"bash",
|
|
1937
|
+
["ls -la"],
|
|
1938
|
+
"/some/other/dir",
|
|
1939
|
+
);
|
|
1940
|
+
expect(match).not.toBeNull();
|
|
1941
|
+
expect(match!.id).toBe("empty-scope-rule");
|
|
1942
|
+
});
|
|
1943
|
+
});
|
|
1944
|
+
|
|
1945
|
+
// ── unknown-version no-overwrite semantics ────────────────────────────────
|
|
1946
|
+
|
|
1947
|
+
describe("unknown-version no-overwrite semantics", () => {
|
|
1948
|
+
beforeEach(() => {
|
|
1949
|
+
clearCache();
|
|
1950
|
+
try {
|
|
1951
|
+
rmSync(trustPath);
|
|
1952
|
+
} catch {
|
|
1953
|
+
/* may not exist */
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
test("unknown version file is never overwritten even if normalization would apply", () => {
|
|
1958
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1959
|
+
// A future version file with rules that would normally be normalized —
|
|
1960
|
+
// the unknown-version guard must prevent any disk writes.
|
|
1961
|
+
const originalContent = JSON.stringify({
|
|
1962
|
+
version: 9999,
|
|
1963
|
+
rules: [
|
|
1964
|
+
{
|
|
1965
|
+
id: "future-url-rule",
|
|
1966
|
+
tool: "web_fetch",
|
|
1967
|
+
pattern: "web_fetch:https://future.io/*",
|
|
1968
|
+
scope: "everywhere",
|
|
1969
|
+
decision: "allow",
|
|
1970
|
+
priority: 50,
|
|
1971
|
+
createdAt: 7000,
|
|
1972
|
+
executionTarget: "should-be-stripped-but-file-not-overwritten",
|
|
1973
|
+
},
|
|
1974
|
+
],
|
|
1975
|
+
});
|
|
1976
|
+
writeFileSync(trustPath, originalContent);
|
|
1977
|
+
clearCache();
|
|
1978
|
+
const rules = getAllRules();
|
|
1979
|
+
// Defaults should be present in-memory
|
|
1980
|
+
const defaults = rules.filter((r) => r.id.startsWith("default:"));
|
|
1981
|
+
expect(defaults).toHaveLength(NUM_DEFAULTS);
|
|
1982
|
+
// The on-disk file must NOT be overwritten
|
|
1983
|
+
const diskContent = readFileSync(trustPath, "utf-8");
|
|
1984
|
+
expect(diskContent).toBe(originalContent);
|
|
1985
|
+
});
|
|
1986
|
+
|
|
1987
|
+
test("unknown version returns only in-memory defaults, not file rules", () => {
|
|
1988
|
+
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1989
|
+
writeFileSync(
|
|
1990
|
+
trustPath,
|
|
1991
|
+
JSON.stringify({
|
|
1992
|
+
version: 42,
|
|
1993
|
+
rules: [
|
|
1994
|
+
{
|
|
1995
|
+
id: "v42-rule",
|
|
1996
|
+
tool: "bash",
|
|
1997
|
+
pattern: "dangerous *",
|
|
1998
|
+
scope: "everywhere",
|
|
1999
|
+
decision: "allow",
|
|
2000
|
+
priority: 500,
|
|
2001
|
+
createdAt: 8000,
|
|
2002
|
+
},
|
|
2003
|
+
],
|
|
2004
|
+
}),
|
|
2005
|
+
);
|
|
2006
|
+
clearCache();
|
|
2007
|
+
const rules = getAllRules();
|
|
2008
|
+
// The file's rules should NOT appear in the loaded set
|
|
2009
|
+
expect(rules.find((r) => r.id === "v42-rule")).toBeUndefined();
|
|
2010
|
+
// Only defaults should be present
|
|
2011
|
+
expect(rules.every((r) => r.id.startsWith("default:"))).toBe(true);
|
|
2012
|
+
});
|
|
2013
|
+
});
|