@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
|
@@ -2,7 +2,6 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import {
|
|
3
3
|
existsSync,
|
|
4
4
|
readFileSync,
|
|
5
|
-
statSync,
|
|
6
5
|
unlinkSync,
|
|
7
6
|
writeFileSync,
|
|
8
7
|
} from "node:fs";
|
|
@@ -258,30 +257,21 @@ export class WorkspaceGitService {
|
|
|
258
257
|
);
|
|
259
258
|
}
|
|
260
259
|
|
|
261
|
-
/** Age threshold (ms) beyond which an index.lock is considered stale. */
|
|
262
|
-
private static readonly LOCK_STALE_THRESHOLD_MS = 30_000;
|
|
263
|
-
|
|
264
260
|
/**
|
|
265
|
-
* Remove `.git/index.lock`
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
261
|
+
* Remove `.git/index.lock` if it exists.
|
|
262
|
+
*
|
|
263
|
+
* This method is always called inside the mutex, so no git operation from
|
|
264
|
+
* our code can be concurrently holding the lock. Any lock file present is
|
|
265
|
+
* stale — left behind by a crashed process or an external command that
|
|
266
|
+
* has already exited.
|
|
269
267
|
*/
|
|
270
268
|
private cleanStaleLockFile(): void {
|
|
271
269
|
const lockPath = join(this.workspaceDir, ".git", "index.lock");
|
|
272
270
|
try {
|
|
273
|
-
const stat = statSync(lockPath);
|
|
274
|
-
const ageMs = Date.now() - stat.mtimeMs;
|
|
275
|
-
if (ageMs < WorkspaceGitService.LOCK_STALE_THRESHOLD_MS) {
|
|
276
|
-
log.debug(
|
|
277
|
-
`index.lock exists but is only ${Math.round(ageMs / 1000)}s old — leaving it`,
|
|
278
|
-
);
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
271
|
unlinkSync(lockPath);
|
|
282
|
-
log.debug(
|
|
272
|
+
log.debug("Removed stale index.lock");
|
|
283
273
|
} catch {
|
|
284
|
-
// File doesn't exist or can't be
|
|
274
|
+
// File doesn't exist or can't be removed — move on.
|
|
285
275
|
}
|
|
286
276
|
}
|
|
287
277
|
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
unlinkSync,
|
|
6
6
|
writeFileSync,
|
|
7
7
|
} from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
|
|
10
11
|
import { getDeviceIdBaseDir } from "../../util/device-id.js";
|
|
@@ -37,11 +38,12 @@ export const seedDeviceIdMigration: WorkspaceMigration = {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
// b. Read the lockfile to find an existing installationId.
|
|
40
|
-
//
|
|
41
|
-
//
|
|
41
|
+
// The lockfile is always under the user's home directory, never under
|
|
42
|
+
// BASE_DATA_DIR. Check both the current and legacy filenames.
|
|
43
|
+
const home = homedir();
|
|
42
44
|
const lockCandidates = [
|
|
43
|
-
join(
|
|
44
|
-
join(
|
|
45
|
+
join(home, ".vellum.lock.json"),
|
|
46
|
+
join(home, ".vellum.lockfile.json"),
|
|
45
47
|
];
|
|
46
48
|
|
|
47
49
|
let lockData: Record<string, unknown> | undefined;
|
|
@@ -7,11 +7,17 @@ import {
|
|
|
7
7
|
unlinkSync,
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
10
11
|
import { join } from "node:path";
|
|
11
12
|
|
|
12
|
-
import { getRootDir } from "../../util/platform.js";
|
|
13
13
|
import type { WorkspaceMigration } from "./types.js";
|
|
14
14
|
|
|
15
|
+
/** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
|
|
16
|
+
function getRootDir(): string {
|
|
17
|
+
const base = process.env.BASE_DATA_DIR?.trim() || homedir();
|
|
18
|
+
return join(base, ".vellum");
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
export const extractFeatureFlagsToProtectedMigration: WorkspaceMigration = {
|
|
16
22
|
id: "016-extract-feature-flags-to-protected",
|
|
17
23
|
description:
|
|
@@ -13,10 +13,8 @@ import { desc, eq } from "drizzle-orm";
|
|
|
13
13
|
import { generateUserFileSlug } from "../../contacts/contact-store.js";
|
|
14
14
|
import { getDb } from "../../memory/db.js";
|
|
15
15
|
import { contacts } from "../../memory/schema/contacts.js";
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
stripCommentLines,
|
|
19
|
-
} from "../../prompts/system-prompt.js";
|
|
16
|
+
import { isTemplateContent } from "../../prompts/system-prompt.js";
|
|
17
|
+
import { stripCommentLines } from "../../util/strip-comment-lines.js";
|
|
20
18
|
import type { WorkspaceMigration } from "./types.js";
|
|
21
19
|
|
|
22
20
|
export const seedPersonaDirsMigration: WorkspaceMigration = {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace migration 021: Move signals directory from root to workspace.
|
|
3
|
+
*
|
|
4
|
+
* Previously, `~/.vellum/signals/` lived directly under getRootDir(). This
|
|
5
|
+
* migration moves any existing signal files into `~/.vellum/workspace/signals/`
|
|
6
|
+
* so that getSignalsDir() resolves correctly under the workspace.
|
|
7
|
+
*
|
|
8
|
+
* Signal files are ephemeral IPC artifacts (written, read once, then stale),
|
|
9
|
+
* so the migration simply ensures the workspace signals directory exists and
|
|
10
|
+
* copies over any files that may still be pending. The old root-level
|
|
11
|
+
* directory is left in place (but empty) to avoid breaking concurrent
|
|
12
|
+
* watchers during the transition.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { existsSync, mkdirSync, readdirSync, renameSync } from "node:fs";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
|
|
19
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
20
|
+
|
|
21
|
+
/** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
|
|
22
|
+
function getRootDir(): string {
|
|
23
|
+
const base = process.env.BASE_DATA_DIR?.trim() || homedir();
|
|
24
|
+
return join(base, ".vellum");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const moveSignalsToWorkspaceMigration: WorkspaceMigration = {
|
|
28
|
+
id: "021-move-signals-to-workspace",
|
|
29
|
+
description: "Move signals directory from root to workspace",
|
|
30
|
+
|
|
31
|
+
run(workspaceDir: string): void {
|
|
32
|
+
const oldSignalsDir = join(getRootDir(), "signals");
|
|
33
|
+
const newSignalsDir = join(workspaceDir, "signals");
|
|
34
|
+
|
|
35
|
+
mkdirSync(newSignalsDir, { recursive: true });
|
|
36
|
+
|
|
37
|
+
if (!existsSync(oldSignalsDir)) return;
|
|
38
|
+
|
|
39
|
+
// Move any pending signal files to the new location
|
|
40
|
+
try {
|
|
41
|
+
const entries = readdirSync(oldSignalsDir);
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const oldPath = join(oldSignalsDir, entry);
|
|
44
|
+
const newPath = join(newSignalsDir, entry);
|
|
45
|
+
if (!existsSync(newPath)) {
|
|
46
|
+
try {
|
|
47
|
+
renameSync(oldPath, newPath);
|
|
48
|
+
} catch {
|
|
49
|
+
// Best-effort: file may have been consumed between readdir and rename
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Best-effort: old directory may not be readable
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
down(workspaceDir: string): void {
|
|
59
|
+
const oldSignalsDir = join(getRootDir(), "signals");
|
|
60
|
+
const newSignalsDir = join(workspaceDir, "signals");
|
|
61
|
+
|
|
62
|
+
mkdirSync(oldSignalsDir, { recursive: true });
|
|
63
|
+
|
|
64
|
+
if (!existsSync(newSignalsDir)) return;
|
|
65
|
+
|
|
66
|
+
// Move signal files back to the root-level directory
|
|
67
|
+
try {
|
|
68
|
+
const entries = readdirSync(newSignalsDir);
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const newPath = join(newSignalsDir, entry);
|
|
71
|
+
const oldPath = join(oldSignalsDir, entry);
|
|
72
|
+
if (!existsSync(oldPath)) {
|
|
73
|
+
try {
|
|
74
|
+
renameSync(newPath, oldPath);
|
|
75
|
+
} catch {
|
|
76
|
+
// Best-effort
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// Best-effort
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace migration 022: Move hooks directory from root to workspace.
|
|
3
|
+
*
|
|
4
|
+
* Previously, `~/.vellum/hooks/` lived directly under getRootDir(). This
|
|
5
|
+
* migration moves existing hook directories and files into
|
|
6
|
+
* `~/.vellum/workspace/hooks/` so that getWorkspaceHooksDir() resolves
|
|
7
|
+
* correctly under the workspace.
|
|
8
|
+
*
|
|
9
|
+
* Hooks are persistent user-installed scripts (manifests, config, executables),
|
|
10
|
+
* so the migration recursively moves all entries from the old directory to the
|
|
11
|
+
* new one. The old root-level directory is left in place (but empty) to avoid
|
|
12
|
+
* breaking any external references during the transition.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
existsSync,
|
|
17
|
+
mkdirSync,
|
|
18
|
+
readdirSync,
|
|
19
|
+
renameSync,
|
|
20
|
+
rmSync,
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
|
|
25
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
26
|
+
|
|
27
|
+
/** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
|
|
28
|
+
function getRootDir(): string {
|
|
29
|
+
const base = process.env.BASE_DATA_DIR?.trim() || homedir();
|
|
30
|
+
return join(base, ".vellum");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const moveHooksToWorkspaceMigration: WorkspaceMigration = {
|
|
34
|
+
id: "022-move-hooks-to-workspace",
|
|
35
|
+
description: "Move hooks directory from root to workspace",
|
|
36
|
+
|
|
37
|
+
run(workspaceDir: string): void {
|
|
38
|
+
const oldHooksDir = join(getRootDir(), "hooks");
|
|
39
|
+
const newHooksDir = join(workspaceDir, "hooks");
|
|
40
|
+
|
|
41
|
+
mkdirSync(newHooksDir, { recursive: true });
|
|
42
|
+
|
|
43
|
+
if (!existsSync(oldHooksDir)) return;
|
|
44
|
+
|
|
45
|
+
// Move hook entries from root to workspace. The old (user) entries take
|
|
46
|
+
// precedence over anything already at the destination (e.g. template
|
|
47
|
+
// files written by installTemplates(), which runs before migrations).
|
|
48
|
+
// We remove the destination first so renameSync succeeds atomically.
|
|
49
|
+
try {
|
|
50
|
+
const entries = readdirSync(oldHooksDir);
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const oldPath = join(oldHooksDir, entry);
|
|
53
|
+
const newPath = join(newHooksDir, entry);
|
|
54
|
+
try {
|
|
55
|
+
if (existsSync(newPath)) {
|
|
56
|
+
rmSync(newPath, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
renameSync(oldPath, newPath);
|
|
59
|
+
} catch {
|
|
60
|
+
// Best-effort: entry may have been modified concurrently
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// Best-effort: old directory may not be readable
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
down(workspaceDir: string): void {
|
|
69
|
+
const oldHooksDir = join(getRootDir(), "hooks");
|
|
70
|
+
const newHooksDir = join(workspaceDir, "hooks");
|
|
71
|
+
|
|
72
|
+
mkdirSync(oldHooksDir, { recursive: true });
|
|
73
|
+
|
|
74
|
+
if (!existsSync(newHooksDir)) return;
|
|
75
|
+
|
|
76
|
+
// Move hook entries back to the root-level directory
|
|
77
|
+
try {
|
|
78
|
+
const entries = readdirSync(newHooksDir);
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
const newPath = join(newHooksDir, entry);
|
|
81
|
+
const oldPath = join(oldHooksDir, entry);
|
|
82
|
+
if (!existsSync(oldPath)) {
|
|
83
|
+
try {
|
|
84
|
+
renameSync(newPath, oldPath);
|
|
85
|
+
} catch {
|
|
86
|
+
// Best-effort
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// Best-effort
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace migration 023: Move config/state JSON files from root to workspace.
|
|
3
|
+
*
|
|
4
|
+
* Previously, dictation-profiles.json, email-guardrails.json, and
|
|
5
|
+
* active-call-leases.json lived directly under getRootDir() (~/.vellum/).
|
|
6
|
+
* This migration moves them into the workspace directory so they are
|
|
7
|
+
* included in diagnostic exports and follow the workspace convention.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, renameSync, unlinkSync } from "node:fs";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
|
|
14
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
15
|
+
|
|
16
|
+
/** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
|
|
17
|
+
function getRootDir(): string {
|
|
18
|
+
const base = process.env.BASE_DATA_DIR?.trim() || homedir();
|
|
19
|
+
return join(base, ".vellum");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Files to move from root → workspace. */
|
|
23
|
+
const CONFIG_FILES = [
|
|
24
|
+
"dictation-profiles.json",
|
|
25
|
+
"email-guardrails.json",
|
|
26
|
+
"active-call-leases.json",
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
export const moveConfigFilesToWorkspaceMigration: WorkspaceMigration = {
|
|
30
|
+
id: "023-move-config-files-to-workspace",
|
|
31
|
+
description:
|
|
32
|
+
"Move dictation-profiles, email-guardrails, and active-call-leases from root to workspace",
|
|
33
|
+
|
|
34
|
+
run(workspaceDir: string): void {
|
|
35
|
+
const rootDir = getRootDir();
|
|
36
|
+
|
|
37
|
+
for (const file of CONFIG_FILES) {
|
|
38
|
+
const oldPath = join(rootDir, file);
|
|
39
|
+
const newPath = join(workspaceDir, file);
|
|
40
|
+
|
|
41
|
+
if (!existsSync(oldPath)) continue;
|
|
42
|
+
// Don't overwrite if the destination already exists (e.g. partial
|
|
43
|
+
// previous run or user-created file).
|
|
44
|
+
if (existsSync(newPath)) {
|
|
45
|
+
// Clean up the old file since workspace already has one.
|
|
46
|
+
try {
|
|
47
|
+
unlinkSync(oldPath);
|
|
48
|
+
} catch {
|
|
49
|
+
// Best-effort cleanup
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
renameSync(oldPath, newPath);
|
|
56
|
+
} catch {
|
|
57
|
+
// Best-effort: cross-device rename or permission issue
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
down(workspaceDir: string): void {
|
|
63
|
+
const rootDir = getRootDir();
|
|
64
|
+
|
|
65
|
+
for (const file of CONFIG_FILES) {
|
|
66
|
+
const newPath = join(workspaceDir, file);
|
|
67
|
+
const oldPath = join(rootDir, file);
|
|
68
|
+
|
|
69
|
+
if (!existsSync(newPath)) continue;
|
|
70
|
+
if (existsSync(oldPath)) {
|
|
71
|
+
try {
|
|
72
|
+
unlinkSync(newPath);
|
|
73
|
+
} catch {
|
|
74
|
+
// Best-effort cleanup
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
renameSync(newPath, oldPath);
|
|
81
|
+
} catch {
|
|
82
|
+
// Best-effort
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace migration 024: Move remaining root-level runtime files/dirs to workspace.
|
|
3
|
+
*
|
|
4
|
+
* Previously, several runtime files and directories lived directly under
|
|
5
|
+
* ~/.vellum/ (the root dir). This migration moves them into the workspace
|
|
6
|
+
* directory so that the root dir can eventually be cleaned up.
|
|
7
|
+
*
|
|
8
|
+
* Files moved:
|
|
9
|
+
* - daemon-stderr.log -> workspace/logs/daemon-stderr.log
|
|
10
|
+
* - daemon-startup.lock -> workspace/daemon-startup.lock
|
|
11
|
+
* - embed-worker.pid -> workspace/embed-worker.pid
|
|
12
|
+
*
|
|
13
|
+
* NOT moved:
|
|
14
|
+
* - .env (stays at root because it contains secrets)
|
|
15
|
+
*
|
|
16
|
+
* Directories moved:
|
|
17
|
+
* - external/ -> workspace/external/
|
|
18
|
+
* - bin/ -> workspace/bin/
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
existsSync,
|
|
23
|
+
mkdirSync,
|
|
24
|
+
readdirSync,
|
|
25
|
+
renameSync,
|
|
26
|
+
unlinkSync,
|
|
27
|
+
} from "node:fs";
|
|
28
|
+
import { homedir } from "node:os";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
|
|
31
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
32
|
+
|
|
33
|
+
/** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
|
|
34
|
+
function getRootDir(): string {
|
|
35
|
+
const base = process.env.BASE_DATA_DIR?.trim() || homedir();
|
|
36
|
+
return join(base, ".vellum");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Individual files to move from root → workspace (with optional subdirectory). */
|
|
40
|
+
const FILE_MOVES: Array<{ name: string; subdir?: string }> = [
|
|
41
|
+
{ name: "daemon-stderr.log", subdir: "logs" },
|
|
42
|
+
{ name: "daemon-startup.lock" },
|
|
43
|
+
// .env stays at root — it contains secrets (API keys) and the entire
|
|
44
|
+
// workspace directory is included in diagnostic log exports.
|
|
45
|
+
{ name: "embed-worker.pid" },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/** Directories to move from root → workspace. */
|
|
49
|
+
const DIR_MOVES = ["external", "bin"] as const;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Move a single file from oldPath to newPath. If the destination already
|
|
53
|
+
* exists, remove the old file instead of overwriting.
|
|
54
|
+
*/
|
|
55
|
+
function moveFile(oldPath: string, newPath: string): void {
|
|
56
|
+
if (!existsSync(oldPath)) return;
|
|
57
|
+
if (existsSync(newPath)) {
|
|
58
|
+
try {
|
|
59
|
+
unlinkSync(oldPath);
|
|
60
|
+
} catch {
|
|
61
|
+
// Best-effort cleanup
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
renameSync(oldPath, newPath);
|
|
67
|
+
} catch {
|
|
68
|
+
// Best-effort: cross-device rename or permission issue
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Move all entries from one directory to another. If the destination already
|
|
74
|
+
* has an entry with the same name, the source entry is removed.
|
|
75
|
+
*/
|
|
76
|
+
function moveDirContents(oldDir: string, newDir: string): void {
|
|
77
|
+
if (!existsSync(oldDir)) return;
|
|
78
|
+
mkdirSync(newDir, { recursive: true });
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const entries = readdirSync(oldDir);
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
moveFile(join(oldDir, entry), join(newDir, entry));
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// Best-effort: old directory may not be readable
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const moveRuntimeFilesToWorkspaceMigration: WorkspaceMigration = {
|
|
91
|
+
id: "024-move-runtime-files-to-workspace",
|
|
92
|
+
description:
|
|
93
|
+
"Move daemon-stderr.log, daemon-startup.lock, embed-worker.pid, external/, and bin/ from root to workspace",
|
|
94
|
+
|
|
95
|
+
run(workspaceDir: string): void {
|
|
96
|
+
const rootDir = getRootDir();
|
|
97
|
+
|
|
98
|
+
// Move individual files
|
|
99
|
+
for (const { name, subdir } of FILE_MOVES) {
|
|
100
|
+
const oldPath = join(rootDir, name);
|
|
101
|
+
const destDir = subdir ? join(workspaceDir, subdir) : workspaceDir;
|
|
102
|
+
mkdirSync(destDir, { recursive: true });
|
|
103
|
+
moveFile(oldPath, join(destDir, name));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Move directories
|
|
107
|
+
for (const dir of DIR_MOVES) {
|
|
108
|
+
moveDirContents(join(rootDir, dir), join(workspaceDir, dir));
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
down(workspaceDir: string): void {
|
|
113
|
+
const rootDir = getRootDir();
|
|
114
|
+
|
|
115
|
+
// Move individual files back
|
|
116
|
+
for (const { name, subdir } of FILE_MOVES) {
|
|
117
|
+
const srcDir = subdir ? join(workspaceDir, subdir) : workspaceDir;
|
|
118
|
+
moveFile(join(srcDir, name), join(rootDir, name));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Move directories back
|
|
122
|
+
for (const dir of DIR_MOVES) {
|
|
123
|
+
moveDirContents(join(workspaceDir, dir), join(rootDir, dir));
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
@@ -23,10 +23,6 @@ import {
|
|
|
23
23
|
} from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
25
|
|
|
26
|
-
import {
|
|
27
|
-
getBaseDataDir,
|
|
28
|
-
getWorkspaceDirOverride,
|
|
29
|
-
} from "../../config/env-registry.js";
|
|
30
26
|
import type { WorkspaceMigration } from "./types.js";
|
|
31
27
|
|
|
32
28
|
const SENTINEL_FILENAME = ".workspace-volume-migrated";
|
|
@@ -53,7 +49,8 @@ export const migrateToWorkspaceVolumeMigration: WorkspaceMigration = {
|
|
|
53
49
|
},
|
|
54
50
|
|
|
55
51
|
run(workspaceDir: string): void {
|
|
56
|
-
const workspaceDirOverride =
|
|
52
|
+
const workspaceDirOverride =
|
|
53
|
+
process.env.VELLUM_WORKSPACE_DIR?.trim() || undefined;
|
|
57
54
|
|
|
58
55
|
// Only relevant when VELLUM_WORKSPACE_DIR is explicitly set (Docker with separate volume)
|
|
59
56
|
if (!workspaceDirOverride) return;
|
|
@@ -72,7 +69,7 @@ export const migrateToWorkspaceVolumeMigration: WorkspaceMigration = {
|
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
// Resolve the old workspace location: $BASE_DATA_DIR/.vellum/workspace
|
|
75
|
-
const baseDataDir =
|
|
72
|
+
const baseDataDir = process.env.BASE_DATA_DIR?.trim() || undefined;
|
|
76
73
|
if (!baseDataDir) {
|
|
77
74
|
// No BASE_DATA_DIR means there's no old location to migrate from
|
|
78
75
|
writeSentinel(sentinelPath);
|
|
@@ -17,6 +17,10 @@ import { seedPersonaDirsMigration } from "./017-seed-persona-dirs.js";
|
|
|
17
17
|
import { rekeyCompoundCredentialKeysMigration } from "./018-rekey-compound-credential-keys.js";
|
|
18
18
|
import { scopeJournalToGuardianMigration } from "./019-scope-journal-to-guardian.js";
|
|
19
19
|
import { renameOauthSkillDirsMigration } from "./020-rename-oauth-skill-dirs.js";
|
|
20
|
+
import { moveSignalsToWorkspaceMigration } from "./021-move-signals-to-workspace.js";
|
|
21
|
+
import { moveHooksToWorkspaceMigration } from "./022-move-hooks-to-workspace.js";
|
|
22
|
+
import { moveConfigFilesToWorkspaceMigration } from "./023-move-config-files-to-workspace.js";
|
|
23
|
+
import { moveRuntimeFilesToWorkspaceMigration } from "./024-move-runtime-files-to-workspace.js";
|
|
20
24
|
import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
|
|
21
25
|
import type { WorkspaceMigration } from "./types.js";
|
|
22
26
|
|
|
@@ -45,4 +49,8 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
|
|
|
45
49
|
rekeyCompoundCredentialKeysMigration,
|
|
46
50
|
scopeJournalToGuardianMigration,
|
|
47
51
|
renameOauthSkillDirsMigration,
|
|
52
|
+
moveSignalsToWorkspaceMigration,
|
|
53
|
+
moveHooksToWorkspaceMigration,
|
|
54
|
+
moveConfigFilesToWorkspaceMigration,
|
|
55
|
+
moveRuntimeFilesToWorkspaceMigration,
|
|
48
56
|
];
|
package/src/signals/confirm.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handle confirmation decisions delivered via signal files from the CLI.
|
|
3
|
-
*
|
|
4
|
-
* The built-in CLI writes JSON to `signals/confirm` instead of making an
|
|
5
|
-
* HTTP POST to `/v1/confirm`. The daemon's ConfigWatcher detects the file
|
|
6
|
-
* change and invokes {@link handleConfirmationSignal}, which reads the
|
|
7
|
-
* payload and resolves the pending interaction in-process.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync } from "node:fs";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
|
|
13
|
-
import { getIsContainerized } from "../config/env-registry.js";
|
|
14
|
-
import type { UserDecision } from "../permissions/types.js";
|
|
15
|
-
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
16
|
-
import { getLogger } from "../util/logger.js";
|
|
17
|
-
import { getSignalsDir } from "../util/platform.js";
|
|
18
|
-
|
|
19
|
-
const log = getLogger("signal:confirm");
|
|
20
|
-
|
|
21
|
-
const VALID_DECISIONS: ReadonlySet<string> = new Set<string>([
|
|
22
|
-
"allow",
|
|
23
|
-
"allow_10m",
|
|
24
|
-
"allow_conversation",
|
|
25
|
-
"always_allow",
|
|
26
|
-
"always_allow_high_risk",
|
|
27
|
-
"deny",
|
|
28
|
-
"always_deny",
|
|
29
|
-
"temporary_override",
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
function isUserDecision(value: string): value is UserDecision {
|
|
33
|
-
return VALID_DECISIONS.has(value);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Read the `signals/confirm` file and resolve the pending interaction.
|
|
38
|
-
* Called by ConfigWatcher when the signal file is written or modified.
|
|
39
|
-
*/
|
|
40
|
-
export function handleConfirmationSignal(): void {
|
|
41
|
-
if (getIsContainerized()) return;
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const content = readFileSync(join(getSignalsDir(), "confirm"), "utf-8");
|
|
45
|
-
const parsed = JSON.parse(content) as {
|
|
46
|
-
requestId?: string;
|
|
47
|
-
decision?: string;
|
|
48
|
-
};
|
|
49
|
-
const { requestId, decision } = parsed;
|
|
50
|
-
|
|
51
|
-
if (!requestId || typeof requestId !== "string") {
|
|
52
|
-
log.warn("Confirmation signal missing requestId");
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (!decision || !isUserDecision(decision)) {
|
|
56
|
-
log.warn({ decision }, "Confirmation signal has invalid decision");
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const interaction = pendingInteractions.resolve(requestId);
|
|
61
|
-
if (!interaction) {
|
|
62
|
-
log.warn({ requestId }, "No pending interaction for confirmation signal");
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (interaction.directResolve) {
|
|
67
|
-
interaction.directResolve(decision);
|
|
68
|
-
} else {
|
|
69
|
-
interaction.conversation!.handleConfirmationResponse(
|
|
70
|
-
requestId,
|
|
71
|
-
decision,
|
|
72
|
-
undefined,
|
|
73
|
-
undefined,
|
|
74
|
-
undefined,
|
|
75
|
-
{ source: "button" },
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
log.info({ requestId, decision }, "Confirmation resolved via signal file");
|
|
79
|
-
} catch (err) {
|
|
80
|
-
log.error({ err }, "Failed to handle confirmation signal");
|
|
81
|
-
}
|
|
82
|
-
}
|