@vellumai/assistant 0.5.13 → 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/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- 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 +4 -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__/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 +64 -88
- 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-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__/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__/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 +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -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/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 +16 -0
- 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/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/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- 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/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/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/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 +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -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 +3 -14
- package/src/runtime/http-types.ts +1 -0
- 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/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/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 +71 -33
- 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
|
@@ -3,7 +3,7 @@ import type { ToolDefinition } from "../../providers/types.js";
|
|
|
3
3
|
export const memoryRecallDefinition: ToolDefinition = {
|
|
4
4
|
name: "memory_recall",
|
|
5
5
|
description:
|
|
6
|
-
"Semantic search across memory for specific information. Relevant memories are auto-injected each turn, so only call this when the auto-injected context doesn't contain what you need - e.g. the user references a past session, or you need deeper recall. Be specific in your query for best results. Returns formatted memory context with item IDs for use with memory_manage.",
|
|
6
|
+
"Semantic search across memory for specific information. Relevant memories are auto-injected each turn, so only call this when the auto-injected context doesn't contain what you need - e.g. the user references a past session, or you need deeper recall. Be specific in your query for best results. Returns formatted memory context with item IDs for use with memory_manage. Results include source conversation titles so you can identify which conversation a memory originated from.",
|
|
7
7
|
input_schema: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
@@ -375,9 +375,9 @@ describe("handleMemoryRecall", () => {
|
|
|
375
375
|
// Not degraded because embeddings are optional
|
|
376
376
|
expect(parsed.degraded).toBe(false);
|
|
377
377
|
expect(parsed.sources.semantic).toBe(0);
|
|
378
|
-
// Qdrant is mocked empty
|
|
379
|
-
//
|
|
380
|
-
expect(parsed.resultCount).toBe(
|
|
378
|
+
// Qdrant is mocked empty so hybrid search returns no candidates, but
|
|
379
|
+
// serendipity sampling may pick up seeded items from the DB directly.
|
|
380
|
+
expect(typeof parsed.resultCount).toBe("number");
|
|
381
381
|
});
|
|
382
382
|
|
|
383
383
|
test("gracefully returns empty in degraded mode without embeddings", async () => {
|
|
@@ -452,11 +452,11 @@ describe("handleMemoryRecall", () => {
|
|
|
452
452
|
|
|
453
453
|
expect(result.isError).toBe(false);
|
|
454
454
|
const parsed = parseResult(result.content);
|
|
455
|
-
// With
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
expect(parsed.
|
|
459
|
-
expect(parsed.
|
|
455
|
+
// With Qdrant mocked empty, segments inserted directly into the DB are
|
|
456
|
+
// not found via hybrid search. Verify the handler returns a valid result
|
|
457
|
+
// shape without erroring — the scope policy is still passed through.
|
|
458
|
+
expect(typeof parsed.resultCount).toBe("number");
|
|
459
|
+
expect(typeof parsed.sources.recency).toBe("number");
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
test("default scope handler invocation does not error", async () => {
|
|
@@ -523,6 +523,48 @@ describe("handleMemoryRecall", () => {
|
|
|
523
523
|
expect(parsed.sources.recency).toBe(0);
|
|
524
524
|
});
|
|
525
525
|
|
|
526
|
+
// ── Source conversation info ───────────────────────────────────────
|
|
527
|
+
// These tests must come after normal tests and before the error test,
|
|
528
|
+
// because mock.module replaces the retriever for all subsequent imports.
|
|
529
|
+
|
|
530
|
+
test("items include sourceConversationTitle when sourceLabel is present", async () => {
|
|
531
|
+
mock.module("../../memory/retriever.js", () => ({
|
|
532
|
+
buildMemoryRecall: async () => ({
|
|
533
|
+
injectedText: "<memory>test memory</memory>",
|
|
534
|
+
selectedCount: 2,
|
|
535
|
+
semanticHits: 2,
|
|
536
|
+
degraded: false,
|
|
537
|
+
topCandidates: [
|
|
538
|
+
{
|
|
539
|
+
key: "item:abc-1",
|
|
540
|
+
type: "item",
|
|
541
|
+
kind: "preference",
|
|
542
|
+
id: "abc-1",
|
|
543
|
+
sourceLabel: "API Design Discussion",
|
|
544
|
+
},
|
|
545
|
+
{ key: "item:abc-2", type: "item", kind: "identity", id: "abc-2" },
|
|
546
|
+
],
|
|
547
|
+
}),
|
|
548
|
+
}));
|
|
549
|
+
|
|
550
|
+
const { handleMemoryRecall: recallWithLabels } =
|
|
551
|
+
await import("./handlers.js");
|
|
552
|
+
|
|
553
|
+
const result = await recallWithLabels({ query: "test" }, TEST_CONFIG);
|
|
554
|
+
expect(result.isError).toBe(false);
|
|
555
|
+
|
|
556
|
+
const parsed = parseResult(result.content);
|
|
557
|
+
expect(parsed.items).toHaveLength(2);
|
|
558
|
+
|
|
559
|
+
// First item has a sourceLabel -> should have sourceConversationTitle
|
|
560
|
+
expect(parsed.items[0].sourceConversationTitle).toBe(
|
|
561
|
+
"API Design Discussion",
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
// Second item has no sourceLabel -> should not have sourceConversationTitle
|
|
565
|
+
expect(parsed.items[1].sourceConversationTitle).toBeUndefined();
|
|
566
|
+
});
|
|
567
|
+
|
|
526
568
|
// ── Error handling ────────────────────────────────────────────────
|
|
527
569
|
// This test must be last: mock.module replaces the retriever for all
|
|
528
570
|
// subsequent imports and cannot be cleanly reverted within the same
|
|
@@ -252,7 +252,7 @@ export interface MemoryRecallToolResult {
|
|
|
252
252
|
text: string;
|
|
253
253
|
resultCount: number;
|
|
254
254
|
degraded: boolean;
|
|
255
|
-
items: Array<{ id: string; type: string; kind: string }>;
|
|
255
|
+
items: Array<{ id: string; type: string; kind: string; sourceConversationTitle?: string }>;
|
|
256
256
|
sources: {
|
|
257
257
|
semantic: number;
|
|
258
258
|
recency: number;
|
|
@@ -298,6 +298,7 @@ export async function handleMemoryRecall(
|
|
|
298
298
|
{
|
|
299
299
|
scopeId,
|
|
300
300
|
scopePolicyOverride,
|
|
301
|
+
hydeEnabled: true,
|
|
301
302
|
},
|
|
302
303
|
);
|
|
303
304
|
|
|
@@ -328,6 +329,7 @@ export async function handleMemoryRecall(
|
|
|
328
329
|
id: c.key,
|
|
329
330
|
type: c.type,
|
|
330
331
|
kind: c.kind,
|
|
332
|
+
...(c.sourceLabel ? { sourceConversationTitle: c.sourceLabel } : {}),
|
|
331
333
|
})),
|
|
332
334
|
sources: {
|
|
333
335
|
semantic: recall.semanticHits,
|
|
@@ -47,12 +47,7 @@ export function isSideEffectTool(
|
|
|
47
47
|
// Action-aware checks for mixed-action tools
|
|
48
48
|
if (toolName === "credential_store") {
|
|
49
49
|
const action = input?.action;
|
|
50
|
-
return
|
|
51
|
-
action === "store" ||
|
|
52
|
-
action === "delete" ||
|
|
53
|
-
action === "prompt" ||
|
|
54
|
-
action === "oauth2_connect"
|
|
55
|
-
);
|
|
50
|
+
return action === "store" || action === "delete" || action === "prompt";
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
return false;
|
|
@@ -48,8 +48,9 @@ export function buildSanitizedEnv(): Record<string, string> {
|
|
|
48
48
|
// Always inject an internal gateway base for local control-plane/API calls.
|
|
49
49
|
const internalGatewayBase = getGatewayInternalBaseUrl();
|
|
50
50
|
env.INTERNAL_GATEWAY_BASE_URL = internalGatewayBase;
|
|
51
|
-
//
|
|
52
|
-
//
|
|
51
|
+
// @deprecated — VELLUM_DATA_DIR is equivalent to $VELLUM_WORKSPACE_DIR/data.
|
|
52
|
+
// Removing this requires an LLM-based migration or declarative migration
|
|
53
|
+
// file to update existing user-authored skills to use VELLUM_WORKSPACE_DIR.
|
|
53
54
|
env.VELLUM_DATA_DIR = getDataDir();
|
|
54
55
|
// Expose the workspace directory so skills and child processes can read/write
|
|
55
56
|
// workspace-scoped files (e.g. avatar traits, user data).
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
3
4
|
|
|
4
5
|
import { getConfig } from "../../config/loader.js";
|
|
5
6
|
import { isCesShellLockdownEnabled } from "../../credential-execution/feature-gates.js";
|
|
@@ -8,7 +9,7 @@ import type { ToolDefinition } from "../../providers/types.js";
|
|
|
8
9
|
import { isUntrustedTrustClass } from "../../runtime/actor-trust-resolver.js";
|
|
9
10
|
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
10
11
|
import { getLogger } from "../../util/logger.js";
|
|
11
|
-
import { getDataDir,
|
|
12
|
+
import { getDataDir, getWorkspaceDir } from "../../util/platform.js";
|
|
12
13
|
import { resolveCredentialRef } from "../credentials/resolve.js";
|
|
13
14
|
import {
|
|
14
15
|
getOrStartSession,
|
|
@@ -38,20 +39,16 @@ function buildCredentialRefTrace(
|
|
|
38
39
|
* Build the list of absolute paths that should be blocked from read access
|
|
39
40
|
* inside the sandbox when CES shell lockdown is active.
|
|
40
41
|
*
|
|
41
|
-
*
|
|
42
|
-
* -
|
|
43
|
-
* CES data root at ~/.vellum/protected/credential-executor/)
|
|
42
|
+
* Blocked paths include:
|
|
43
|
+
* - Gateway security directory (credential store secrets, CES data)
|
|
44
44
|
* - ~/.vellum/workspace/data/db/ - database files that may contain credential metadata
|
|
45
|
-
* - CES bootstrap socket directory (/run/ces-bootstrap/ or CES_BOOTSTRAP_SOCKET_DIR)
|
|
46
|
-
*
|
|
47
|
-
* - CES managed-mode data root (CES_DATA_DIR, or /ces-data when
|
|
48
|
-
* CES_MANAGED_MODE is set) - prevents access to CES-private state in
|
|
49
|
-
* managed deployments (local-mode is already covered by the protected/
|
|
50
|
-
* entry)
|
|
45
|
+
* - CES bootstrap socket directory (/run/ces-bootstrap/ or CES_BOOTSTRAP_SOCKET_DIR)
|
|
46
|
+
* - CES managed-mode data root (CES_DATA_DIR, or /ces-data when CES_MANAGED_MODE is set)
|
|
51
47
|
*/
|
|
52
48
|
function buildCesProtectedPaths(): string[] {
|
|
53
|
-
const
|
|
54
|
-
|
|
49
|
+
const securityDir =
|
|
50
|
+
process.env.GATEWAY_SECURITY_DIR || join(homedir(), ".vellum", "protected");
|
|
51
|
+
const paths = [securityDir, join(getWorkspaceDir(), "data", "db")];
|
|
55
52
|
|
|
56
53
|
// CES bootstrap socket directory - block access to the Unix socket that
|
|
57
54
|
// accepts RPC commands from the assistant process.
|
|
@@ -70,7 +67,7 @@ function buildCesProtectedPaths(): string[] {
|
|
|
70
67
|
|
|
71
68
|
// CES managed-mode private data root - in managed deployments the CES
|
|
72
69
|
// data lives outside the Vellum root, so it isn't covered by the
|
|
73
|
-
//
|
|
70
|
+
// gateway security directory entry above.
|
|
74
71
|
const cesDataDir = process.env["CES_DATA_DIR"];
|
|
75
72
|
if (cesDataDir) {
|
|
76
73
|
paths.push(cesDataDir);
|
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
} from "../memory/canonical-guardian-store.js";
|
|
7
7
|
import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
|
|
8
8
|
import { createOrReuseToolGrantRequest } from "../runtime/tool-grant-request-helper.js";
|
|
9
|
+
import { redactSecrets } from "../security/secret-scanner.js";
|
|
9
10
|
import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
|
|
10
11
|
import { getTaskRunRules } from "../tasks/ephemeral-permissions.js";
|
|
11
12
|
import { getLogger } from "../util/logger.js";
|
|
12
13
|
import { getAllTools, getTool } from "./registry.js";
|
|
13
14
|
import { isSideEffectTool } from "./side-effects.js";
|
|
15
|
+
import { summarizeToolInput } from "./tool-input-summary.js";
|
|
14
16
|
import type {
|
|
15
17
|
ExecutionTarget,
|
|
16
18
|
Tool,
|
|
@@ -22,6 +24,22 @@ import { enforceVerificationControlPlanePolicy } from "./verification-control-pl
|
|
|
22
24
|
|
|
23
25
|
const log = getLogger("tool-approval-handler");
|
|
24
26
|
|
|
27
|
+
function buildToolGrantQuestionText(
|
|
28
|
+
toolName: string,
|
|
29
|
+
input: Record<string, unknown>,
|
|
30
|
+
context: ToolContext,
|
|
31
|
+
): string {
|
|
32
|
+
const senderLabel =
|
|
33
|
+
context.requesterDisplayName ||
|
|
34
|
+
context.requesterIdentifier ||
|
|
35
|
+
context.requesterExternalUserId ||
|
|
36
|
+
"A trusted contact";
|
|
37
|
+
const inputSummary = redactSecrets(summarizeToolInput(toolName, input));
|
|
38
|
+
return inputSummary
|
|
39
|
+
? `${senderLabel} wants to use "${toolName}": ${inputSummary}`
|
|
40
|
+
: `${senderLabel} is requesting permission to use "${toolName}"`;
|
|
41
|
+
}
|
|
42
|
+
|
|
25
43
|
/** Default polling interval for inline grant wait (ms). */
|
|
26
44
|
export const TC_GRANT_WAIT_INTERVAL_MS = 500;
|
|
27
45
|
/** Default maximum wait time for inline grant wait (ms). */
|
|
@@ -494,7 +512,8 @@ export class ToolApprovalHandler {
|
|
|
494
512
|
requesterChatId: context.requesterChatId,
|
|
495
513
|
toolName: name,
|
|
496
514
|
inputDigest,
|
|
497
|
-
questionText:
|
|
515
|
+
questionText: buildToolGrantQuestionText(name, input, context),
|
|
516
|
+
requesterIdentifier: context.requesterDisplayName || context.requesterIdentifier,
|
|
498
517
|
});
|
|
499
518
|
|
|
500
519
|
// Only wait inline if the escalation succeeded (created or deduped).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summarizes tool input into a concise string for guardian approval display.
|
|
3
|
+
*
|
|
4
|
+
* Returns unredacted text — callers that persist the result (e.g. to the
|
|
5
|
+
* canonical_guardian_requests table) must apply redactSecrets() before writing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function truncate(value: string, maxLen: number): string {
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
if (trimmed.length <= maxLen) return trimmed;
|
|
11
|
+
return `${trimmed.slice(0, maxLen)}…`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function extractString(
|
|
15
|
+
input: Record<string, unknown>,
|
|
16
|
+
...keys: string[]
|
|
17
|
+
): string | undefined {
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
const val = input[key];
|
|
20
|
+
if (typeof val === "string" && val.trim().length > 0) {
|
|
21
|
+
return val;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function firstStringValue(input: Record<string, unknown>): string | undefined {
|
|
28
|
+
for (const val of Object.values(input)) {
|
|
29
|
+
if (typeof val === "string" && val.trim().length > 0) {
|
|
30
|
+
return val;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function summarizeToolInput(
|
|
37
|
+
toolName: string,
|
|
38
|
+
input: Record<string, unknown>,
|
|
39
|
+
): string {
|
|
40
|
+
switch (toolName) {
|
|
41
|
+
case "bash":
|
|
42
|
+
case "terminal":
|
|
43
|
+
case "host_bash": {
|
|
44
|
+
const cmd = extractString(input, "command");
|
|
45
|
+
return cmd ? truncate(cmd, 120) : "";
|
|
46
|
+
}
|
|
47
|
+
case "file_read":
|
|
48
|
+
case "file_write":
|
|
49
|
+
case "file_edit":
|
|
50
|
+
case "host_file_read":
|
|
51
|
+
case "host_file_write":
|
|
52
|
+
case "host_file_edit": {
|
|
53
|
+
const path = extractString(input, "file_path", "path");
|
|
54
|
+
return path ? path.trim() : "";
|
|
55
|
+
}
|
|
56
|
+
case "web_fetch":
|
|
57
|
+
case "network_request": {
|
|
58
|
+
const url = extractString(input, "url");
|
|
59
|
+
return url ? truncate(url, 100) : "";
|
|
60
|
+
}
|
|
61
|
+
default: {
|
|
62
|
+
const fallback = firstStringValue(input);
|
|
63
|
+
return fallback ? truncate(fallback, 80) : "";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/tools/types.ts
CHANGED
|
@@ -169,6 +169,10 @@ export interface ToolContext {
|
|
|
169
169
|
requesterExternalUserId?: string;
|
|
170
170
|
/** Chat ID of the requester (non-guardian actor). Used for tool grant request escalation notifications. */
|
|
171
171
|
requesterChatId?: string;
|
|
172
|
+
/** Human-readable identifier for the requester (e.g., @username). */
|
|
173
|
+
requesterIdentifier?: string;
|
|
174
|
+
/** Preferred display name for the requester. */
|
|
175
|
+
requesterDisplayName?: string;
|
|
172
176
|
/** Slack channel ID for channel-scoped permission enforcement. When set, tools are checked against the channel's permission profile. */
|
|
173
177
|
channelPermissionChannelId?: string;
|
|
174
178
|
/** The tool_use block ID from the LLM response, used to correlate confirmation prompts with specific tool invocations. */
|
package/src/usage/types.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface PricingUsage {
|
|
|
19
19
|
cacheCreationInputTokens: number;
|
|
20
20
|
cacheReadInputTokens: number;
|
|
21
21
|
anthropicCacheCreation: AnthropicCacheCreationTokenDetails | null;
|
|
22
|
+
/** Anthropic fast mode speed indicator from the API response. */
|
|
23
|
+
speed?: "fast" | "standard" | null;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -36,6 +38,8 @@ export interface UsageEventInput {
|
|
|
36
38
|
conversationId: string | null;
|
|
37
39
|
runId: string | null;
|
|
38
40
|
requestId: string | null;
|
|
41
|
+
/** Number of actual LLM API calls represented by this event (defaults to 1). */
|
|
42
|
+
llmCallCount?: number;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/**
|
package/src/util/device-id.ts
CHANGED
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
* Path resolution:
|
|
9
9
|
* - Containerized (IS_CONTAINERIZED=true): uses /home/assistant (the assistant
|
|
10
10
|
* user's persistent home dir) so device.json lives on the assistant's own
|
|
11
|
-
* filesystem rather than the shared data volume. Falls back to
|
|
12
|
-
* for migration
|
|
11
|
+
* filesystem rather than the shared data volume. Falls back to the legacy
|
|
12
|
+
* BASE_DATA_DIR location for migration.
|
|
13
13
|
* - Local (single or multi-instance): uses homedir() so all instances on the
|
|
14
|
-
* same machine share a single device ID
|
|
15
|
-
* an instance-scoped directory.
|
|
14
|
+
* same machine share a single device ID.
|
|
16
15
|
*
|
|
17
16
|
* The value is cached in memory after the first successful read/write.
|
|
18
17
|
* Falls back to a generated UUID if the file cannot be read or written.
|
|
@@ -23,7 +22,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
23
22
|
import { homedir } from "node:os";
|
|
24
23
|
import { join } from "node:path";
|
|
25
24
|
|
|
26
|
-
import {
|
|
25
|
+
import { getIsContainerized } from "../config/env-registry.js";
|
|
27
26
|
import { getLogger } from "./logger.js";
|
|
28
27
|
|
|
29
28
|
const log = getLogger("device-id");
|
|
@@ -49,16 +48,17 @@ export function getDeviceIdBaseDir(): string {
|
|
|
49
48
|
/**
|
|
50
49
|
* Resolve the legacy base directory for device.json migration.
|
|
51
50
|
*
|
|
52
|
-
* Returns the old containerized path (BASE_DATA_DIR) so we can
|
|
53
|
-
* reading device.json from the shared volume if it hasn't been
|
|
54
|
-
* Returns undefined when not containerized or when no legacy
|
|
51
|
+
* Returns the old containerized path (via BASE_DATA_DIR env var) so we can
|
|
52
|
+
* fall back to reading device.json from the shared volume if it hasn't been
|
|
53
|
+
* migrated yet. Returns undefined when not containerized or when no legacy
|
|
54
|
+
* path exists.
|
|
55
55
|
*/
|
|
56
56
|
function getLegacyDeviceIdBaseDir(): string | undefined {
|
|
57
57
|
if (!getIsContainerized()) {
|
|
58
58
|
return undefined;
|
|
59
59
|
}
|
|
60
|
-
const baseDataDir =
|
|
61
|
-
return baseDataDir
|
|
60
|
+
const baseDataDir = process.env.BASE_DATA_DIR?.trim() || undefined;
|
|
61
|
+
return baseDataDir;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
package/src/util/platform.ts
CHANGED
|
@@ -2,11 +2,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
getBaseDataDir,
|
|
7
|
-
getIsContainerized,
|
|
8
|
-
getWorkspaceDirOverride,
|
|
9
|
-
} from "../config/env-registry.js";
|
|
5
|
+
import { getWorkspaceDirOverride } from "../config/env-registry.js";
|
|
10
6
|
|
|
11
7
|
export function isMacOS(): boolean {
|
|
12
8
|
return process.platform === "darwin";
|
|
@@ -59,11 +55,14 @@ export function normalizeAssistantId(assistantId: string): string {
|
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
58
|
+
* Compute the root ~/.vellum directory path.
|
|
59
|
+
*
|
|
60
|
+
* This is a simple inline computation — not a shared function — so each
|
|
61
|
+
* helper is self-contained and the module has no hidden coupling to
|
|
62
|
+
* env-var indirection.
|
|
64
63
|
*/
|
|
65
|
-
|
|
66
|
-
return join(
|
|
64
|
+
function vellumRoot(): string {
|
|
65
|
+
return join(homedir(), ".vellum");
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
/**
|
|
@@ -130,7 +129,7 @@ export function getTCPPort(): number {
|
|
|
130
129
|
* the shell: `touch ~/.vellum/tcp-enabled && kill -USR1 <daemon-pid>`.
|
|
131
130
|
*/
|
|
132
131
|
export function isTCPEnabled(): boolean {
|
|
133
|
-
return existsSync(join(
|
|
132
|
+
return existsSync(join(vellumRoot(), "tcp-enabled"));
|
|
134
133
|
}
|
|
135
134
|
|
|
136
135
|
/**
|
|
@@ -156,7 +155,7 @@ export function getTCPHost(): string {
|
|
|
156
155
|
* access without exposing the daemon to the LAN.
|
|
157
156
|
*/
|
|
158
157
|
export function isIOSPairingEnabled(): boolean {
|
|
159
|
-
return existsSync(join(
|
|
158
|
+
return existsSync(join(vellumRoot(), "ios-pairing-enabled"));
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
/**
|
|
@@ -176,7 +175,7 @@ function getXdgPlatformTokenPath(): string {
|
|
|
176
175
|
* instances that may have the token written here by the desktop app.
|
|
177
176
|
*/
|
|
178
177
|
export function getPlatformTokenPath(): string {
|
|
179
|
-
return join(
|
|
178
|
+
return join(vellumRoot(), "platform-token");
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
/**
|
|
@@ -198,7 +197,7 @@ export function readPlatformToken(): string | null {
|
|
|
198
197
|
}
|
|
199
198
|
|
|
200
199
|
export function getPidPath(): string {
|
|
201
|
-
return join(
|
|
200
|
+
return join(vellumRoot(), "vellum.pid");
|
|
202
201
|
}
|
|
203
202
|
|
|
204
203
|
export function getDbPath(): string {
|
|
@@ -213,23 +212,58 @@ export function getHistoryPath(): string {
|
|
|
213
212
|
return join(getDataDir(), "history");
|
|
214
213
|
}
|
|
215
214
|
|
|
216
|
-
export function getHooksDir(): string {
|
|
217
|
-
return join(getRootDir(), "hooks");
|
|
218
|
-
}
|
|
219
|
-
|
|
220
215
|
/**
|
|
221
|
-
* Returns
|
|
216
|
+
* Returns the protected directory (~/.vellum/protected). Security-sensitive
|
|
217
|
+
* files — trust rules, encrypted credential store, signing keys, feature-flag
|
|
218
|
+
* overrides, device approval lists — live here.
|
|
222
219
|
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
220
|
+
* This directory is:
|
|
221
|
+
* - Outside the workspace (not included in diagnostic exports)
|
|
222
|
+
* - Outside the sandbox write boundary (tools cannot modify it)
|
|
223
|
+
* - Skipped in containerized mode (credentials via CES, trust via gateway)
|
|
226
224
|
*/
|
|
225
|
+
export function getProtectedDir(): string {
|
|
226
|
+
return join(vellumRoot(), "protected");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Returns ~/.vellum/workspace/signals — the directory for IPC signal files. */
|
|
227
230
|
export function getSignalsDir(): string {
|
|
228
|
-
return join(
|
|
231
|
+
return join(getWorkspaceDir(), "signals");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// --- Root-level runtime path helpers ---
|
|
235
|
+
// These expose specific root-level file paths so callers don't need to
|
|
236
|
+
// import getRootDir() directly. getRootDir() is intentionally unexported.
|
|
237
|
+
|
|
238
|
+
/** Returns the path to the daemon stderr log (~/.vellum/workspace/logs/daemon-stderr.log). */
|
|
239
|
+
export function getDaemonStderrLogPath(): string {
|
|
240
|
+
return join(getWorkspaceDir(), "logs", "daemon-stderr.log");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Returns the path to the daemon startup lock file (~/.vellum/workspace/daemon-startup.lock). */
|
|
244
|
+
export function getDaemonStartupLockPath(): string {
|
|
245
|
+
return join(getWorkspaceDir(), "daemon-startup.lock");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Returns the directory for externally-installed packages (~/.vellum/workspace/external). */
|
|
249
|
+
export function getExternalDir(): string {
|
|
250
|
+
return join(getWorkspaceDir(), "external");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Returns the directory for installed binaries (~/.vellum/workspace/bin). */
|
|
254
|
+
export function getBinDir(): string {
|
|
255
|
+
return join(getWorkspaceDir(), "bin");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Returns the path to the dot-env file (~/.vellum/.env). Stays at root because it contains secrets. */
|
|
259
|
+
export function getDotEnvPath(): string {
|
|
260
|
+
return join(vellumRoot(), ".env");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Returns the path to the embed-worker PID file (~/.vellum/workspace/embed-worker.pid). */
|
|
264
|
+
export function getEmbedWorkerPidPath(): string {
|
|
265
|
+
return join(getWorkspaceDir(), "embed-worker.pid");
|
|
229
266
|
}
|
|
230
|
-
// --- Workspace path primitives ---
|
|
231
|
-
// These will become the canonical paths after workspace migration.
|
|
232
|
-
// Currently not used by call-sites; wired in later PRs.
|
|
233
267
|
|
|
234
268
|
/**
|
|
235
269
|
* Returns the workspace root for user-facing state.
|
|
@@ -245,7 +279,7 @@ export function getSignalsDir(): string {
|
|
|
245
279
|
export function getWorkspaceDir(): string {
|
|
246
280
|
const override = getWorkspaceDirOverride();
|
|
247
281
|
if (override) return override;
|
|
248
|
-
return join(
|
|
282
|
+
return join(vellumRoot(), "workspace");
|
|
249
283
|
}
|
|
250
284
|
|
|
251
285
|
/**
|
|
@@ -281,6 +315,11 @@ export function getWorkspaceHooksDir(): string {
|
|
|
281
315
|
return join(getWorkspaceDir(), "hooks");
|
|
282
316
|
}
|
|
283
317
|
|
|
318
|
+
/** Returns ~/.vellum/workspace/deprecated — transitional files slated for removal. */
|
|
319
|
+
export function getDeprecatedDir(): string {
|
|
320
|
+
return join(getWorkspaceDir(), "deprecated");
|
|
321
|
+
}
|
|
322
|
+
|
|
284
323
|
/** Returns ~/.vellum/workspace/conversations */
|
|
285
324
|
export function getConversationsDir(): string {
|
|
286
325
|
return join(getWorkspaceDir(), "conversations");
|
|
@@ -292,23 +331,22 @@ export function getWorkspacePromptPath(file: string): string {
|
|
|
292
331
|
}
|
|
293
332
|
|
|
294
333
|
export function ensureDataDir(): void {
|
|
295
|
-
const root =
|
|
334
|
+
const root = vellumRoot();
|
|
296
335
|
const workspace = getWorkspaceDir();
|
|
297
336
|
const wsData = join(workspace, "data");
|
|
298
|
-
const containerized = getIsContainerized();
|
|
299
337
|
const dirs = [
|
|
300
338
|
// Root-level dirs (runtime)
|
|
301
339
|
root,
|
|
302
|
-
// signals dir is needed everywhere (MCP reload, user-message signals)
|
|
303
|
-
join(root, "signals"),
|
|
304
|
-
// protected, hooks are local-only — skip in containerized mode
|
|
305
|
-
// (credentials via CES HTTP API, trust via gateway API)
|
|
306
|
-
...(containerized ? [] : [join(root, "protected"), join(root, "hooks")]),
|
|
307
340
|
// Workspace dirs
|
|
308
341
|
workspace,
|
|
342
|
+
join(workspace, "signals"),
|
|
343
|
+
join(workspace, "hooks"),
|
|
309
344
|
join(workspace, "skills"),
|
|
310
345
|
join(workspace, "embedding-models"),
|
|
311
346
|
join(workspace, "conversations"),
|
|
347
|
+
join(workspace, "logs"),
|
|
348
|
+
join(workspace, "external"),
|
|
349
|
+
join(workspace, "bin"),
|
|
312
350
|
// Data sub-dirs under workspace
|
|
313
351
|
wsData,
|
|
314
352
|
join(wsData, "db"),
|
package/src/util/pricing.ts
CHANGED
|
@@ -12,6 +12,9 @@ const ANTHROPIC_PROMPT_CACHE_MULTIPLIERS = {
|
|
|
12
12
|
write1h: 2,
|
|
13
13
|
} as const;
|
|
14
14
|
|
|
15
|
+
/** Fast mode pricing is 6x standard rates for all token types. */
|
|
16
|
+
const ANTHROPIC_FAST_MODE_MULTIPLIER = 6;
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Multi-provider pricing catalog keyed by provider then model pattern.
|
|
17
20
|
* Model patterns are matched by exact match first, then by prefix.
|
|
@@ -147,12 +150,22 @@ function calculateUsageCost(
|
|
|
147
150
|
pricing: ModelPricing,
|
|
148
151
|
usage: PricingUsage,
|
|
149
152
|
): number {
|
|
153
|
+
// Anthropic fast mode: 6x multiplier on base rates (cache multipliers stack on top)
|
|
154
|
+
const speedMultiplier =
|
|
155
|
+
provider === "anthropic" && usage.speed === "fast"
|
|
156
|
+
? ANTHROPIC_FAST_MODE_MULTIPLIER
|
|
157
|
+
: 1;
|
|
158
|
+
const effectivePricing: ModelPricing = {
|
|
159
|
+
inputPer1M: pricing.inputPer1M * speedMultiplier,
|
|
160
|
+
outputPer1M: pricing.outputPer1M * speedMultiplier,
|
|
161
|
+
};
|
|
162
|
+
|
|
150
163
|
const directInputCost = calculateTokenCost(
|
|
151
|
-
|
|
164
|
+
effectivePricing.inputPer1M,
|
|
152
165
|
usage.directInputTokens,
|
|
153
166
|
);
|
|
154
167
|
const outputCost = calculateTokenCost(
|
|
155
|
-
|
|
168
|
+
effectivePricing.outputPer1M,
|
|
156
169
|
usage.outputTokens,
|
|
157
170
|
);
|
|
158
171
|
|
|
@@ -161,7 +174,7 @@ function calculateUsageCost(
|
|
|
161
174
|
directInputCost +
|
|
162
175
|
outputCost +
|
|
163
176
|
calculateTokenCost(
|
|
164
|
-
|
|
177
|
+
effectivePricing.inputPer1M,
|
|
165
178
|
usage.cacheCreationInputTokens + usage.cacheReadInputTokens,
|
|
166
179
|
)
|
|
167
180
|
);
|
|
@@ -174,15 +187,15 @@ function calculateUsageCost(
|
|
|
174
187
|
directInputCost +
|
|
175
188
|
outputCost +
|
|
176
189
|
calculateTokenCost(
|
|
177
|
-
|
|
190
|
+
effectivePricing.inputPer1M * ANTHROPIC_PROMPT_CACHE_MULTIPLIERS.read,
|
|
178
191
|
usage.cacheReadInputTokens,
|
|
179
192
|
) +
|
|
180
193
|
calculateTokenCost(
|
|
181
|
-
|
|
194
|
+
effectivePricing.inputPer1M * ANTHROPIC_PROMPT_CACHE_MULTIPLIERS.write5m,
|
|
182
195
|
ephemeral5mInputTokens,
|
|
183
196
|
) +
|
|
184
197
|
calculateTokenCost(
|
|
185
|
-
|
|
198
|
+
effectivePricing.inputPer1M * ANTHROPIC_PROMPT_CACHE_MULTIPLIERS.write1h,
|
|
186
199
|
ephemeral1hInputTokens,
|
|
187
200
|
)
|
|
188
201
|
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip lines starting with `_` (comment convention for prompt .md files)
|
|
3
|
+
* and collapse any resulting consecutive blank lines.
|
|
4
|
+
*
|
|
5
|
+
* Lines inside fenced code blocks (``` or ~~~ delimiters per CommonMark)
|
|
6
|
+
* are never stripped, so code examples with `_`-prefixed identifiers are preserved.
|
|
7
|
+
*/
|
|
8
|
+
export function stripCommentLines(content: string): string {
|
|
9
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
10
|
+
let openFenceChar: string | null = null;
|
|
11
|
+
const filtered = normalized.split("\n").filter((line) => {
|
|
12
|
+
const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
13
|
+
if (fenceMatch) {
|
|
14
|
+
const char = fenceMatch[1][0];
|
|
15
|
+
if (!openFenceChar) {
|
|
16
|
+
openFenceChar = char;
|
|
17
|
+
} else if (char === openFenceChar) {
|
|
18
|
+
openFenceChar = null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (openFenceChar) return true;
|
|
22
|
+
return !line.trimStart().startsWith("_");
|
|
23
|
+
});
|
|
24
|
+
return filtered
|
|
25
|
+
.join("\n")
|
|
26
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
27
|
+
.trim();
|
|
28
|
+
}
|