@vellumai/assistant 0.5.12 → 0.5.14
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/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/Dockerfile +41 -9
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/node_modules/@vellumai/ces-contracts/src/index.ts +7 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +5 -0
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +11 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/navigate-settings-tab.test.ts +6 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +62 -251
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-feature-flags.test.ts +8 -0
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +212 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +250 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +290 -0
- package/src/cli/commands/platform/__tests__/status.test.ts +282 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/settings/TOOLS.json +5 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +4 -2
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/client.ts +14 -2
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/first-greeting.ts +6 -1
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +18 -15
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/index.ts +0 -12
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/conversation-queries.ts +6 -6
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/journal-memory.ts +8 -2
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +54 -36
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +38 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +10 -29
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/rebind-secrets-screen.ts +2 -2
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/secret-routes.ts +9 -2
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +72 -124
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Guard test: SKILL.md files must never instruct the assistant to accept
|
|
6
|
+
* secrets (passwords, API keys, tokens, etc.) pasted directly in chat.
|
|
7
|
+
*
|
|
8
|
+
* Secrets must always be collected via `credential_store prompt`, which
|
|
9
|
+
* presents a secure native UI that keeps the value out of conversation
|
|
10
|
+
* history and LLM context.
|
|
11
|
+
*
|
|
12
|
+
* This guard prevents regressions like the gmail/messaging bundled skill
|
|
13
|
+
* violation where SKILL.md contained "Include client_secret too if they
|
|
14
|
+
* provide one" — directing the assistant to accept a secret value from
|
|
15
|
+
* the chat stream.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** SKILL.md files permitted to contain otherwise-violating patterns. */
|
|
19
|
+
const ALLOWLIST = new Set<string>([
|
|
20
|
+
// Add paths here only if there is a genuine, documented exception.
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Words that indicate the line is about a secret/credential value.
|
|
25
|
+
*/
|
|
26
|
+
const SECRET_WORDS =
|
|
27
|
+
"secret|password|api[_\\s-]?key|auth[_\\s-]?token|private[_\\s-]?key|access[_\\s-]?token|client[_\\s-]?secret|signing[_\\s-]?key|bearer[_\\s-]?token";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Patterns that indicate the assistant is being told to accept a secret
|
|
31
|
+
* value directly in chat, rather than via credential_store prompt.
|
|
32
|
+
*/
|
|
33
|
+
const VIOLATION_PATTERNS: RegExp[] = [
|
|
34
|
+
// "accept <secret> in/via/from chat/plaintext/the conversation"
|
|
35
|
+
new RegExp(
|
|
36
|
+
`accept\\s+.*(?:${SECRET_WORDS}).*\\b(?:in|via|from)\\s+(?:chat|plaintext|the\\s+conversation)`,
|
|
37
|
+
"i",
|
|
38
|
+
),
|
|
39
|
+
new RegExp(
|
|
40
|
+
`accept\\s+.*\\b(?:in|via|from)\\s+(?:chat|plaintext|the\\s+conversation).*(?:${SECRET_WORDS})`,
|
|
41
|
+
"i",
|
|
42
|
+
),
|
|
43
|
+
// "ask (the user|them) (for|to share/send/paste/type/provide) <secret>" where destination is chat
|
|
44
|
+
// Must have "the user" or "them" as the object to avoid matching third-party descriptions
|
|
45
|
+
// like "Discord will ask for a 2FA code before revealing the secret"
|
|
46
|
+
new RegExp(
|
|
47
|
+
`ask\\s+(?:the\\s+user|them)\\s+(?:for|to\\s+(?:share|send|paste|type|provide))\\s+(?:the\\s+|their\\s+|a\\s+)?(?:${SECRET_WORDS})`,
|
|
48
|
+
"i",
|
|
49
|
+
),
|
|
50
|
+
// "Include <secret> too if they provide one" — the original gmail violation pattern
|
|
51
|
+
new RegExp(
|
|
52
|
+
`include\\s+(?:the\\s+)?(?:${SECRET_WORDS})\\s+(?:too|as\\s+well|also)\\s+if\\s+they\\s+provide`,
|
|
53
|
+
"i",
|
|
54
|
+
),
|
|
55
|
+
// "<secret> pasted/typed/sent in chat/conversation/plaintext"
|
|
56
|
+
new RegExp(
|
|
57
|
+
`(?:${SECRET_WORDS})\\s+(?:pasted|typed|sent|provided|shared)\\s+(?:in|via|through)\\s+(?:chat|conversation|plaintext)`,
|
|
58
|
+
"i",
|
|
59
|
+
),
|
|
60
|
+
// "paste/type/send <secret> in chat/here/the conversation"
|
|
61
|
+
new RegExp(
|
|
62
|
+
`(?:paste|type|send|share|provide)\\s+(?:the\\s+|your\\s+|their\\s+)?(?:${SECRET_WORDS})\\s+(?:in\\s+(?:chat|the\\s+conversation)|here)`,
|
|
63
|
+
"i",
|
|
64
|
+
),
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Lines containing these negation words are typically instructing the
|
|
69
|
+
* assistant NOT to do something — these are not violations.
|
|
70
|
+
*/
|
|
71
|
+
const NEGATION_PATTERNS =
|
|
72
|
+
/\b(?:never|do\s+not|don['']t|must\s+not|should\s+not|shouldn['']t)\b|\bNOT\b/;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Lines that are YAML-style field values within a credential_store prompt
|
|
76
|
+
* block (label, description, placeholder). These contain secret-related
|
|
77
|
+
* words but are secure UI text, not chat instructions.
|
|
78
|
+
*/
|
|
79
|
+
const CREDENTIAL_STORE_UI_FIELD =
|
|
80
|
+
/^\s*(?:[-*]\s+)?(?:label|description|placeholder)\s*[:=]\s*/i;
|
|
81
|
+
|
|
82
|
+
interface Violation {
|
|
83
|
+
file: string;
|
|
84
|
+
line: number;
|
|
85
|
+
text: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function findViolations(): Violation[] {
|
|
89
|
+
const repoRoot = process.cwd() + "/..";
|
|
90
|
+
|
|
91
|
+
// Find all SKILL.md files tracked by git
|
|
92
|
+
let skillFiles: string[];
|
|
93
|
+
try {
|
|
94
|
+
const output = execSync(`git grep -l "" -- '*/SKILL.md'`, {
|
|
95
|
+
encoding: "utf-8",
|
|
96
|
+
cwd: repoRoot,
|
|
97
|
+
}).trim();
|
|
98
|
+
skillFiles = output.split("\n").filter((f) => f.length > 0);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
if ((err as { status?: number }).status === 1) {
|
|
101
|
+
return []; // no SKILL.md files found
|
|
102
|
+
}
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Filter to skills/ and assistant/src/config/bundled-skills/ directories
|
|
107
|
+
skillFiles = skillFiles.filter(
|
|
108
|
+
(f) =>
|
|
109
|
+
f.startsWith("skills/") ||
|
|
110
|
+
f.startsWith("assistant/src/config/bundled-skills/"),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const violations: Violation[] = [];
|
|
114
|
+
|
|
115
|
+
for (const filePath of skillFiles) {
|
|
116
|
+
if (ALLOWLIST.has(filePath)) continue;
|
|
117
|
+
|
|
118
|
+
let content: string;
|
|
119
|
+
try {
|
|
120
|
+
content = execSync(`git show HEAD:${filePath}`, {
|
|
121
|
+
encoding: "utf-8",
|
|
122
|
+
cwd: repoRoot,
|
|
123
|
+
});
|
|
124
|
+
} catch {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const lines = content.split("\n");
|
|
129
|
+
|
|
130
|
+
// Track whether we're inside a credential_store prompt block
|
|
131
|
+
// (indented YAML-like content after a credential_store mention)
|
|
132
|
+
let inCredentialStoreBlock = false;
|
|
133
|
+
let blockIndent = 0;
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < lines.length; i++) {
|
|
136
|
+
const line = lines[i];
|
|
137
|
+
const lineNumber = i + 1;
|
|
138
|
+
|
|
139
|
+
// Track credential_store prompt blocks
|
|
140
|
+
if (/credential_store\s+prompt/i.test(line)) {
|
|
141
|
+
inCredentialStoreBlock = true;
|
|
142
|
+
blockIndent = line.search(/\S/);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Exit credential_store block when indentation returns to same or lesser level
|
|
147
|
+
if (inCredentialStoreBlock) {
|
|
148
|
+
const currentIndent = line.search(/\S/);
|
|
149
|
+
if (
|
|
150
|
+
currentIndent !== -1 &&
|
|
151
|
+
currentIndent <= blockIndent &&
|
|
152
|
+
line.trim().length > 0
|
|
153
|
+
) {
|
|
154
|
+
inCredentialStoreBlock = false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Skip empty lines
|
|
159
|
+
if (line.trim().length === 0) continue;
|
|
160
|
+
|
|
161
|
+
// Skip negation lines — these instruct NOT to do something
|
|
162
|
+
if (NEGATION_PATTERNS.test(line)) continue;
|
|
163
|
+
|
|
164
|
+
// Skip credential_store UI field lines (label:, description:, placeholder:)
|
|
165
|
+
if (inCredentialStoreBlock && CREDENTIAL_STORE_UI_FIELD.test(line))
|
|
166
|
+
continue;
|
|
167
|
+
|
|
168
|
+
// Strip markdown backticks before pattern matching so that
|
|
169
|
+
// violations like `client_secret` are caught the same as bare words.
|
|
170
|
+
const stripped = line.replace(/`/g, "");
|
|
171
|
+
|
|
172
|
+
// Check against violation patterns
|
|
173
|
+
for (const pattern of VIOLATION_PATTERNS) {
|
|
174
|
+
if (pattern.test(stripped)) {
|
|
175
|
+
violations.push({
|
|
176
|
+
file: filePath,
|
|
177
|
+
line: lineNumber,
|
|
178
|
+
text: line.trim(),
|
|
179
|
+
});
|
|
180
|
+
break; // one violation per line is enough
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return violations;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
describe("SKILL.md secret handling guard", () => {
|
|
190
|
+
test("no SKILL.md files instruct accepting secrets in chat", () => {
|
|
191
|
+
const violations = findViolations();
|
|
192
|
+
|
|
193
|
+
if (violations.length > 0) {
|
|
194
|
+
const message = [
|
|
195
|
+
"Found SKILL.md files that instruct accepting secrets directly in chat.",
|
|
196
|
+
"Secrets must always be collected via `credential_store prompt`, which",
|
|
197
|
+
"presents a secure native UI that keeps values out of conversation history.",
|
|
198
|
+
"",
|
|
199
|
+
"Violations:",
|
|
200
|
+
...violations.map((v) => ` - ${v.file}:${v.line}: ${v.text}`),
|
|
201
|
+
"",
|
|
202
|
+
"To fix: replace chat-based secret collection with a `credential_store prompt` call.",
|
|
203
|
+
"See any *-setup skill (e.g. skills/slack-app-setup/SKILL.md) for the correct pattern.",
|
|
204
|
+
"",
|
|
205
|
+
"If this is a genuine exception, add the file path to the ALLOWLIST in",
|
|
206
|
+
"skill-secret-handling-guard.test.ts.",
|
|
207
|
+
].join("\n");
|
|
208
|
+
|
|
209
|
+
expect(violations, message).toEqual([]);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -12,10 +12,10 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
|
12
12
|
import { uninstallSkillLocally } from "../skills/catalog-install.js";
|
|
13
13
|
|
|
14
14
|
let tempDir: string;
|
|
15
|
-
let
|
|
15
|
+
let originalWorkspaceDir: string | undefined;
|
|
16
16
|
|
|
17
17
|
function getSkillsDir(): string {
|
|
18
|
-
return join(tempDir, "
|
|
18
|
+
return join(tempDir, "skills");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function getSkillsIndexPath(): string {
|
|
@@ -38,15 +38,17 @@ beforeEach(() => {
|
|
|
38
38
|
tmpdir(),
|
|
39
39
|
`skills-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
40
40
|
);
|
|
41
|
-
mkdirSync(join(tempDir, "
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
originalBaseDataDir = process.env.BASE_DATA_DIR;
|
|
45
|
-
process.env.BASE_DATA_DIR = tempDir;
|
|
41
|
+
mkdirSync(join(tempDir, "skills"), { recursive: true });
|
|
42
|
+
originalWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
|
|
43
|
+
process.env.VELLUM_WORKSPACE_DIR = tempDir;
|
|
46
44
|
});
|
|
47
45
|
|
|
48
46
|
afterEach(() => {
|
|
49
|
-
|
|
47
|
+
if (originalWorkspaceDir === undefined) {
|
|
48
|
+
delete process.env.VELLUM_WORKSPACE_DIR;
|
|
49
|
+
} else {
|
|
50
|
+
process.env.VELLUM_WORKSPACE_DIR = originalWorkspaceDir;
|
|
51
|
+
}
|
|
50
52
|
rmSync(tempDir, { recursive: true, force: true });
|
|
51
53
|
});
|
|
52
54
|
|
|
@@ -16,7 +16,7 @@ const TEST_DIR = join(tmpdir(), `vellum-skills-test-${crypto.randomUUID()}`);
|
|
|
16
16
|
const realPlatform = require("../util/platform.js");
|
|
17
17
|
mock.module("../util/platform.js", () => ({
|
|
18
18
|
...realPlatform,
|
|
19
|
-
|
|
19
|
+
getProtectedDir: () => join(TEST_DIR, "protected"),
|
|
20
20
|
getDataDir: () => TEST_DIR,
|
|
21
21
|
|
|
22
22
|
getSandboxRootDir: () => join(TEST_DIR, "sandbox"),
|
|
@@ -60,7 +60,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
60
60
|
}));
|
|
61
61
|
|
|
62
62
|
mock.module("../util/platform.js", () => ({
|
|
63
|
-
|
|
63
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
64
64
|
getDataDir: () => testDir,
|
|
65
65
|
|
|
66
66
|
isMacOS: () => process.platform === "darwin",
|
|
@@ -8,29 +8,12 @@
|
|
|
8
8
|
* 4. Notify the guardian of the access attempt
|
|
9
9
|
* 5. When the user replies with the code in the DM, verify and activate
|
|
10
10
|
*/
|
|
11
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
12
|
-
import { tmpdir } from "node:os";
|
|
13
|
-
import { join } from "node:path";
|
|
14
11
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
15
12
|
|
|
16
13
|
// ---------------------------------------------------------------------------
|
|
17
14
|
// Test isolation: in-memory SQLite via temp directory
|
|
18
15
|
// ---------------------------------------------------------------------------
|
|
19
16
|
|
|
20
|
-
const testDir = mkdtempSync(join(tmpdir(), "slack-inbound-verification-test-"));
|
|
21
|
-
|
|
22
|
-
mock.module("../util/platform.js", () => ({
|
|
23
|
-
getRootDir: () => testDir,
|
|
24
|
-
getDataDir: () => testDir,
|
|
25
|
-
isMacOS: () => process.platform === "darwin",
|
|
26
|
-
isLinux: () => process.platform === "linux",
|
|
27
|
-
isWindows: () => process.platform === "win32",
|
|
28
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
29
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
30
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
31
|
-
ensureDataDir: () => {},
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
17
|
mock.module("../util/logger.js", () => ({
|
|
35
18
|
getLogger: () =>
|
|
36
19
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -81,11 +64,6 @@ initializeDb();
|
|
|
81
64
|
|
|
82
65
|
afterAll(() => {
|
|
83
66
|
resetDb();
|
|
84
|
-
try {
|
|
85
|
-
rmSync(testDir, { recursive: true });
|
|
86
|
-
} catch {
|
|
87
|
-
/* best effort */
|
|
88
|
-
}
|
|
89
67
|
});
|
|
90
68
|
|
|
91
69
|
// ---------------------------------------------------------------------------
|
|
@@ -14,9 +14,12 @@ const TRUST_PATH = join(TEST_ROOT, "protected", "trust.json");
|
|
|
14
14
|
// We need to mock getRootDir before importing trust-store
|
|
15
15
|
import { mock } from "bun:test";
|
|
16
16
|
|
|
17
|
+
// Point the file-based trust backend at the test temp dir.
|
|
18
|
+
process.env.GATEWAY_SECURITY_DIR = join(TEST_ROOT, "protected");
|
|
19
|
+
|
|
17
20
|
// Mock the platform module to use our test root
|
|
18
21
|
mock.module("../util/platform.js", () => ({
|
|
19
|
-
|
|
22
|
+
getProtectedDir: () => join(TEST_ROOT, "protected"),
|
|
20
23
|
}));
|
|
21
24
|
|
|
22
25
|
// Mock the skills config module used by defaults.ts
|
|
@@ -81,6 +81,7 @@ mock.module("../daemon/handlers/shared.js", () => ({
|
|
|
81
81
|
textSegments: [],
|
|
82
82
|
contentOrder: [],
|
|
83
83
|
surfaces: [],
|
|
84
|
+
thinkingSegments: [],
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
return {
|
|
@@ -90,6 +91,7 @@ mock.module("../daemon/handlers/shared.js", () => ({
|
|
|
90
91
|
textSegments: [],
|
|
91
92
|
contentOrder: [],
|
|
92
93
|
surfaces: [],
|
|
94
|
+
thinkingSegments: [],
|
|
93
95
|
};
|
|
94
96
|
},
|
|
95
97
|
}));
|
|
@@ -18,7 +18,7 @@ import { mock } from "bun:test";
|
|
|
18
18
|
const realPlatform = require("../util/platform.js");
|
|
19
19
|
mock.module("../util/platform.js", () => ({
|
|
20
20
|
...realPlatform,
|
|
21
|
-
|
|
21
|
+
getProtectedDir: () => join(TEST_DIR, "protected"),
|
|
22
22
|
getDataDir: () => TEST_DIR,
|
|
23
23
|
getWorkspaceDir: () => TEST_DIR,
|
|
24
24
|
getWorkspaceConfigPath: () => join(TEST_DIR, "config.json"),
|
|
@@ -19,7 +19,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
19
19
|
const testTmpDir = mkdtempSync(join(tmpdir(), "terminal-test-"));
|
|
20
20
|
|
|
21
21
|
mock.module("../util/platform.js", () => ({
|
|
22
|
-
|
|
22
|
+
getProtectedDir: () => join(testTmpDir, "protected"),
|
|
23
23
|
getDataDir: () => join(testTmpDir, "data"),
|
|
24
24
|
getSandboxWorkingDir: () => join(testTmpDir, "sandbox"),
|
|
25
25
|
isMacOS: () => process.platform === "darwin",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test preload — runs before every test file.
|
|
3
|
+
*
|
|
4
|
+
* Creates a per-file temporary directory and sets VELLUM_WORKSPACE_DIR so that
|
|
5
|
+
* all workspace-derived helpers (getDataDir, getDbPath, getConversationsDir, …)
|
|
6
|
+
* resolve under the temp dir instead of the real ~/.vellum/workspace.
|
|
7
|
+
*
|
|
8
|
+
* Individual test files can retrieve the workspace dir via getWorkspaceDir()
|
|
9
|
+
* from platform.ts, or directly from process.env.VELLUM_WORKSPACE_DIR.
|
|
10
|
+
*
|
|
11
|
+
* Cleanup: the temp dir is removed after all tests in the file complete.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { afterAll } from "bun:test";
|
|
18
|
+
|
|
19
|
+
const testDir = realpathSync(
|
|
20
|
+
mkdtempSync(join(tmpdir(), "vellum-test-workspace-")),
|
|
21
|
+
);
|
|
22
|
+
process.env.VELLUM_WORKSPACE_DIR = testDir;
|
|
23
|
+
|
|
24
|
+
afterAll(() => {
|
|
25
|
+
delete process.env.VELLUM_WORKSPACE_DIR;
|
|
26
|
+
try {
|
|
27
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
28
|
+
} catch {
|
|
29
|
+
/* best-effort cleanup */
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -205,7 +205,7 @@ function makeSystemPrompt(size: "small" | "production" = "small"): string {
|
|
|
205
205
|
"### OAuth Setup",
|
|
206
206
|
"Most integrations use OAuth for authentication.",
|
|
207
207
|
"Guide the user through the OAuth flow when setting up a new integration:",
|
|
208
|
-
"1. Navigate to Settings >
|
|
208
|
+
"1. Navigate to Settings > Models & Services",
|
|
209
209
|
"2. Click 'Connect' for the desired service",
|
|
210
210
|
"3. Authorize in the browser popup",
|
|
211
211
|
"4. Confirm the connection is active",
|
|
@@ -76,7 +76,7 @@ mock.module("../tools/network/script-proxy/index.js", () => ({
|
|
|
76
76
|
}));
|
|
77
77
|
|
|
78
78
|
mock.module("../util/platform.js", () => ({
|
|
79
|
-
|
|
79
|
+
getProtectedDir: () => "/tmp/protected",
|
|
80
80
|
getDataDir: () => "/tmp",
|
|
81
81
|
getWorkspaceDir: () => "/tmp/workspace",
|
|
82
82
|
getConversationsDir: () => "/tmp/workspace/conversations",
|
|
@@ -237,7 +237,7 @@ describe("Tool execution pipeline benchmark", () => {
|
|
|
237
237
|
|
|
238
238
|
const p50 = percentile(timings, 50);
|
|
239
239
|
expect(p50).toBeLessThan(5);
|
|
240
|
-
expect(results[0]).toBe(RiskLevel.
|
|
240
|
+
expect(results[0]).toBe(RiskLevel.Low);
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
test("check: full permission check for low-risk tool", async () => {
|
|
@@ -1209,12 +1209,6 @@ describe("isSideEffectTool", () => {
|
|
|
1209
1209
|
);
|
|
1210
1210
|
});
|
|
1211
1211
|
|
|
1212
|
-
test("credential_store oauth2_connect is a side-effect", () => {
|
|
1213
|
-
expect(
|
|
1214
|
-
isSideEffectTool("credential_store", { action: "oauth2_connect" }),
|
|
1215
|
-
).toBe(true);
|
|
1216
|
-
});
|
|
1217
|
-
|
|
1218
1212
|
test("credential_store list is NOT a side-effect", () => {
|
|
1219
1213
|
expect(isSideEffectTool("credential_store", { action: "list" })).toBe(
|
|
1220
1214
|
false,
|
|
@@ -1731,20 +1725,6 @@ describe("ToolExecutor forcePromptSideEffects enforcement", () => {
|
|
|
1731
1725
|
expect(promptCalled).toBe(true);
|
|
1732
1726
|
});
|
|
1733
1727
|
|
|
1734
|
-
test("credential_store oauth2_connect forces prompt in private conversation", async () => {
|
|
1735
|
-
checkResultOverride = { decision: "allow", reason: "Matched trust rule" };
|
|
1736
|
-
|
|
1737
|
-
const executor = new ToolExecutor(makeTrackingPrompter());
|
|
1738
|
-
const result = await executor.execute(
|
|
1739
|
-
"credential_store",
|
|
1740
|
-
{ action: "oauth2_connect", provider: "google" },
|
|
1741
|
-
makeContext({ forcePromptSideEffects: true }),
|
|
1742
|
-
);
|
|
1743
|
-
|
|
1744
|
-
expect(result.isError).toBe(false);
|
|
1745
|
-
expect(promptCalled).toBe(true);
|
|
1746
|
-
});
|
|
1747
|
-
|
|
1748
1728
|
test("credential_store list does NOT force prompt in private conversation", async () => {
|
|
1749
1729
|
checkResultOverride = { decision: "allow", reason: "Matched trust rule" };
|
|
1750
1730
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { summarizeToolInput } from "../tools/tool-input-summary.js";
|
|
4
|
+
|
|
5
|
+
describe("summarizeToolInput", () => {
|
|
6
|
+
test("bash with short command returns full command", () => {
|
|
7
|
+
expect(summarizeToolInput("bash", { command: "ls -la" })).toBe("ls -la");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("bash with long command truncates with ellipsis", () => {
|
|
11
|
+
const longCmd = "a".repeat(200);
|
|
12
|
+
const result = summarizeToolInput("bash", { command: longCmd });
|
|
13
|
+
expect(result.length).toBe(121); // 120 chars + ellipsis
|
|
14
|
+
expect(result.endsWith("…")).toBe(true);
|
|
15
|
+
expect(result.startsWith("a".repeat(120))).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("terminal tool behaves like bash", () => {
|
|
19
|
+
expect(summarizeToolInput("terminal", { command: "echo hello" })).toBe(
|
|
20
|
+
"echo hello",
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("file_read returns file path", () => {
|
|
25
|
+
expect(
|
|
26
|
+
summarizeToolInput("file_read", { file_path: "/src/index.ts" }),
|
|
27
|
+
).toBe("/src/index.ts");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("file_write returns file path from path key", () => {
|
|
31
|
+
expect(summarizeToolInput("file_write", { path: "/src/main.ts" })).toBe(
|
|
32
|
+
"/src/main.ts",
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("file_edit prefers file_path over path", () => {
|
|
37
|
+
expect(
|
|
38
|
+
summarizeToolInput("file_edit", {
|
|
39
|
+
file_path: "/preferred.ts",
|
|
40
|
+
path: "/fallback.ts",
|
|
41
|
+
}),
|
|
42
|
+
).toBe("/preferred.ts");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("web_fetch returns URL truncated to 100 chars", () => {
|
|
46
|
+
const longUrl = `https://example.com/${"x".repeat(200)}`;
|
|
47
|
+
const result = summarizeToolInput("web_fetch", { url: longUrl });
|
|
48
|
+
expect(result.length).toBe(101); // 100 chars + ellipsis
|
|
49
|
+
expect(result.endsWith("…")).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("web_fetch with short URL returns full URL", () => {
|
|
53
|
+
expect(
|
|
54
|
+
summarizeToolInput("web_fetch", { url: "https://example.com" }),
|
|
55
|
+
).toBe("https://example.com");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("network_request behaves like web_fetch", () => {
|
|
59
|
+
expect(
|
|
60
|
+
summarizeToolInput("network_request", { url: "https://api.test.com" }),
|
|
61
|
+
).toBe("https://api.test.com");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("empty input returns empty string", () => {
|
|
65
|
+
expect(summarizeToolInput("bash", {})).toBe("");
|
|
66
|
+
expect(summarizeToolInput("file_read", {})).toBe("");
|
|
67
|
+
expect(summarizeToolInput("web_fetch", {})).toBe("");
|
|
68
|
+
expect(summarizeToolInput("unknown_tool", {})).toBe("");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("unknown tool with string input returns first string value", () => {
|
|
72
|
+
expect(
|
|
73
|
+
summarizeToolInput("custom_tool", {
|
|
74
|
+
query: "search for something",
|
|
75
|
+
count: 10,
|
|
76
|
+
}),
|
|
77
|
+
).toBe("search for something");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("unknown tool with long string truncates to 80 chars", () => {
|
|
81
|
+
const longVal = "b".repeat(150);
|
|
82
|
+
const result = summarizeToolInput("custom_tool", { data: longVal });
|
|
83
|
+
expect(result.length).toBe(81); // 80 chars + ellipsis
|
|
84
|
+
expect(result.endsWith("…")).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("input with no string values returns empty string", () => {
|
|
88
|
+
expect(
|
|
89
|
+
summarizeToolInput("custom_tool", { count: 42, flag: true, obj: {} }),
|
|
90
|
+
).toBe("");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("whitespace-only string values are treated as empty", () => {
|
|
94
|
+
expect(summarizeToolInput("bash", { command: " " })).toBe("");
|
|
95
|
+
expect(summarizeToolInput("custom_tool", { data: " \n\t " })).toBe("");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("host_bash behaves like bash", () => {
|
|
99
|
+
expect(summarizeToolInput("host_bash", { command: "git status" })).toBe(
|
|
100
|
+
"git status",
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("host_file_read behaves like file_read", () => {
|
|
105
|
+
expect(
|
|
106
|
+
summarizeToolInput("host_file_read", { file_path: "/src/index.ts" }),
|
|
107
|
+
).toBe("/src/index.ts");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("host_file_write behaves like file_write", () => {
|
|
111
|
+
expect(
|
|
112
|
+
summarizeToolInput("host_file_write", { path: "/src/main.ts" }),
|
|
113
|
+
).toBe("/src/main.ts");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("host_file_edit behaves like file_edit", () => {
|
|
117
|
+
expect(
|
|
118
|
+
summarizeToolInput("host_file_edit", {
|
|
119
|
+
file_path: "/preferred.ts",
|
|
120
|
+
path: "/fallback.ts",
|
|
121
|
+
}),
|
|
122
|
+
).toBe("/preferred.ts");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
* - handleToolResult includes toolUseId in emitted tool_result
|
|
9
9
|
* - Event ordering: tool_use_preview_start → input_json_delta → tool_use
|
|
10
10
|
*/
|
|
11
|
+
import { join } from "node:path";
|
|
11
12
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
12
13
|
|
|
13
14
|
// ── Mock platform (must precede imports that read it) ─────────────────────────
|
|
14
15
|
mock.module("../util/platform.js", () => ({
|
|
15
16
|
getSessionTokenPath: () => "/tmp/test-token",
|
|
16
|
-
|
|
17
|
+
getProtectedDir: () => join("/tmp/test", "protected"),
|
|
17
18
|
getDataDir: () => "/tmp/test",
|
|
18
19
|
getWorkspaceDir: () => "/tmp/test/workspace",
|
|
19
20
|
getWorkspaceSkillsDir: () => "/tmp/test/skills",
|
|
@@ -12,9 +12,15 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
12
12
|
// Create a temp directory for the trust file
|
|
13
13
|
const testDir = mkdtempSync(join(tmpdir(), "trust-store-test-"));
|
|
14
14
|
|
|
15
|
+
// Point the file-based trust backend at the test temp dir so
|
|
16
|
+
// getGatewaySecurityDir() (which checks this env var first) writes
|
|
17
|
+
// trust.json under the test directory instead of ~/.vellum/protected.
|
|
18
|
+
process.env.GATEWAY_SECURITY_DIR = join(testDir, "protected");
|
|
19
|
+
|
|
15
20
|
// Mock platform module so trust-store writes to temp dir instead of ~/.vellum
|
|
16
21
|
mock.module("../util/platform.js", () => ({
|
|
17
|
-
|
|
22
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
23
|
+
getWorkspaceDir: () => join(testDir, "workspace"),
|
|
18
24
|
getDataDir: () => testDir,
|
|
19
25
|
isMacOS: () => process.platform === "darwin",
|
|
20
26
|
isLinux: () => process.platform === "linux",
|
|
@@ -30,7 +30,7 @@ const testDir = mkdtempSync(join(tmpdir(), "tc-inline-approval-integration-"));
|
|
|
30
30
|
|
|
31
31
|
mock.module("../util/platform.js", () => ({
|
|
32
32
|
getDataDir: () => testDir,
|
|
33
|
-
|
|
33
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
34
34
|
isMacOS: () => process.platform === "darwin",
|
|
35
35
|
isLinux: () => process.platform === "linux",
|
|
36
36
|
isWindows: () => process.platform === "win32",
|
|
@@ -23,7 +23,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
23
23
|
const testDir = mkdtempSync(join(tmpdir(), "trusted-contact-lifecycle-notif-"));
|
|
24
24
|
|
|
25
25
|
mock.module("../util/platform.js", () => ({
|
|
26
|
-
|
|
26
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
27
27
|
getDataDir: () => testDir,
|
|
28
28
|
isMacOS: () => process.platform === "darwin",
|
|
29
29
|
isLinux: () => process.platform === "linux",
|
|
@@ -18,7 +18,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
18
18
|
const testDir = mkdtempSync(join(tmpdir(), "trusted-contact-multichannel-"));
|
|
19
19
|
|
|
20
20
|
mock.module("../util/platform.js", () => ({
|
|
21
|
-
|
|
21
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
22
22
|
getDataDir: () => testDir,
|
|
23
23
|
isMacOS: () => process.platform === "darwin",
|
|
24
24
|
isLinux: () => process.platform === "linux",
|
|
@@ -21,7 +21,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
21
21
|
const testDir = mkdtempSync(join(tmpdir(), "trusted-contact-verify-test-"));
|
|
22
22
|
|
|
23
23
|
mock.module("../util/platform.js", () => ({
|
|
24
|
-
|
|
24
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
25
25
|
getDataDir: () => testDir,
|
|
26
26
|
isMacOS: () => process.platform === "darwin",
|
|
27
27
|
isLinux: () => process.platform === "linux",
|
|
@@ -10,7 +10,7 @@ const workspaceDir = join(testDir, ".vellum", "workspace");
|
|
|
10
10
|
const conversationsDir = join(workspaceDir, "conversations");
|
|
11
11
|
|
|
12
12
|
mock.module("../util/platform.js", () => ({
|
|
13
|
-
|
|
13
|
+
getProtectedDir: () => join(join(testDir, ".vellum"), "protected"),
|
|
14
14
|
getDataDir: () => join(workspaceDir, "data"),
|
|
15
15
|
getWorkspaceDir: () => workspaceDir,
|
|
16
16
|
getConversationsDir: () => conversationsDir,
|