@vellumai/assistant 0.5.6 → 0.5.8
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 +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +3 -2
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docker-entrypoint.sh +9 -0
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/memory.md +13 -11
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +0 -114
- package/src/__tests__/approval-cascade.test.ts +0 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/ces-startup-timeout.test.ts +40 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -2
- package/src/__tests__/config-schema.test.ts +3 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +2 -4
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
- package/src/__tests__/conversation-queue.test.ts +0 -1
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-slash-queue.test.ts +0 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/credential-execution-client.test.ts +5 -2
- package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
- package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
- package/src/__tests__/credential-security-e2e.test.ts +1 -67
- package/src/__tests__/credential-security-invariants.test.ts +6 -50
- package/src/__tests__/credentials-cli.test.ts +82 -3
- package/src/__tests__/daemon-credential-client.test.ts +123 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/journal-context.test.ts +335 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
- package/src/__tests__/memory-recall-quality.test.ts +48 -17
- package/src/__tests__/memory-regressions.test.ts +408 -363
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +2 -7
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/notification-decision-strategy.test.ts +71 -0
- package/src/__tests__/oauth-cli.test.ts +5 -1
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
- package/src/__tests__/provider-error-scenarios.test.ts +0 -267
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/relay-server.test.ts +1 -2
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -1
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +95 -272
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-memory.test.ts +17 -3
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/stale-approval-dedup.test.ts +171 -0
- package/src/__tests__/stt-hints.test.ts +437 -0
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/task-memory-cleanup.test.ts +14 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-quality.test.ts +58 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +220 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/acp/agent-process.ts +9 -1
- package/src/agent/loop.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +164 -38
- package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +90 -8
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +129 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/stt-hints.ts +189 -0
- package/src/calls/tts-text-sanitizer.ts +61 -0
- package/src/calls/twilio-routes.ts +34 -5
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +38 -5
- package/src/calls/voice-session-bridge.ts +7 -12
- package/src/cli/commands/avatar.ts +2 -2
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +128 -82
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/keys.ts +7 -7
- package/src/cli/commands/memory.ts +1 -1
- package/src/cli/commands/oauth/connections.ts +11 -29
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +525 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/cli/lib/daemon-credential-client.ts +284 -0
- package/src/cli.ts +1 -1
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/AGENTS.md +34 -0
- package/src/config/bundled-skills/acp/SKILL.md +10 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +15 -2
- package/src/config/bundled-skills/settings/TOOLS.json +47 -2
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-skills/slack/SKILL.md +1 -1
- package/src/config/bundled-tool-registry.ts +5 -11
- package/src/config/defaults.ts +0 -2
- package/src/config/env-registry.ts +5 -5
- package/src/config/env.ts +21 -14
- package/src/config/feature-flag-registry.json +49 -9
- package/src/config/loader.ts +106 -42
- package/src/config/schema.ts +9 -29
- package/src/config/schemas/calls.ts +30 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/inference.ts +2 -2
- package/src/config/schemas/journal.ts +16 -0
- package/src/config/schemas/memory-processing.ts +2 -2
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +1 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/credential-execution/startup-timeout.ts +36 -0
- package/src/daemon/approval-generators.ts +3 -9
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-error.ts +13 -1
- package/src/daemon/conversation-memory.ts +1 -2
- package/src/daemon/conversation-process.ts +18 -1
- package/src/daemon/conversation-surfaces.ts +30 -1
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +21 -1
- package/src/daemon/guardian-action-generators.ts +3 -9
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +234 -51
- package/src/daemon/message-types/conversations.ts +4 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +32 -95
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/app-store.ts +31 -0
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +16 -0
- package/src/memory/indexer.ts +19 -10
- package/src/memory/items-extractor.ts +328 -321
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/job-handlers/summarization.ts +26 -16
- package/src/memory/jobs-store.ts +63 -6
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/journal-memory.ts +214 -0
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +98 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/retriever.test.ts +37 -25
- package/src/memory/retriever.ts +24 -49
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +2 -0
- package/src/memory/search/formatting.ts +7 -44
- package/src/memory/search/staleness.ts +4 -0
- package/src/memory/search/tier-classifier.ts +10 -2
- package/src/memory/search/types.ts +2 -5
- package/src/memory/task-memory-cleanup.ts +4 -3
- package/src/notifications/adapters/slack.ts +168 -6
- package/src/notifications/broadcaster.ts +1 -0
- package/src/notifications/copy-composer.ts +59 -2
- package/src/notifications/decision-engine.ts +4 -1
- package/src/notifications/signal.ts +2 -0
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/journal-context.ts +133 -0
- package/src/prompts/persona-resolver.ts +194 -0
- package/src/prompts/system-prompt.ts +44 -4
- package/src/prompts/templates/SOUL.md +10 -0
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/provider-send-message.ts +3 -32
- package/src/providers/registry.ts +29 -179
- package/src/providers/types.ts +1 -1
- package/src/runtime/access-request-helper.ts +4 -0
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +17 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/gateway-client.ts +47 -4
- package/src/runtime/guardian-decision-types.ts +45 -4
- package/src/runtime/http-server.ts +31 -3
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/access-request-decision.ts +2 -2
- package/src/runtime/routes/app-management-routes.ts +2 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/channel-readiness-routes.ts +9 -4
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/debug-routes.ts +12 -9
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/guardian-approval-interception.ts +168 -11
- package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
- package/src/runtime/routes/identity-routes.ts +19 -30
- package/src/runtime/routes/inbound-message-handler.ts +31 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/twilio.ts +52 -10
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.test.ts +3 -3
- package/src/runtime/routes/memory-item-routes.ts +46 -14
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/secret-routes.ts +141 -10
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +96 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/security/ces-credential-client.ts +75 -29
- package/src/security/ces-rpc-credential-backend.ts +86 -0
- package/src/security/credential-backend.ts +22 -92
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +113 -115
- package/src/skills/catalog-install.ts +6 -32
- package/src/skills/skill-memory.ts +1 -0
- package/src/subagent/manager.ts +2 -5
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/acp/spawn.ts +78 -1
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/vault.ts +5 -3
- package/src/tools/executor.ts +0 -4
- package/src/tools/memory/definitions.ts +3 -2
- package/src/tools/memory/handlers.ts +10 -7
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/types.ts +0 -8
- package/src/util/browser.ts +15 -0
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +4 -51
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
- package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
- package/src/workspace/migrations/registry.ts +12 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/workspace/provider-commit-message-generator.ts +12 -21
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
- package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/memory/search/lexical.ts +0 -48
- package/src/providers/failover.ts +0 -186
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
|
@@ -9,7 +9,6 @@ import { buildMemoryRecall } from "../../memory/retriever.js";
|
|
|
9
9
|
import { memoryItems } from "../../memory/schema.js";
|
|
10
10
|
import type { ScopePolicyOverride } from "../../memory/search/types.js";
|
|
11
11
|
import { getLogger } from "../../util/logger.js";
|
|
12
|
-
import { truncate } from "../../util/truncate.js";
|
|
13
12
|
import type { ToolExecutionResult } from "../types.js";
|
|
14
13
|
|
|
15
14
|
const log = getLogger("memory-tools");
|
|
@@ -39,6 +38,7 @@ export async function handleMemorySave(
|
|
|
39
38
|
"decision",
|
|
40
39
|
"constraint",
|
|
41
40
|
"event",
|
|
41
|
+
"journal",
|
|
42
42
|
]);
|
|
43
43
|
if (typeof rawKind !== "string") {
|
|
44
44
|
return {
|
|
@@ -60,14 +60,14 @@ export async function handleMemorySave(
|
|
|
60
60
|
|
|
61
61
|
const subject =
|
|
62
62
|
typeof args.subject === "string" && args.subject.trim().length > 0
|
|
63
|
-
?
|
|
63
|
+
? args.subject.trim()
|
|
64
64
|
: inferSubjectFromStatement(statement.trim());
|
|
65
65
|
|
|
66
66
|
try {
|
|
67
67
|
const db = getDb();
|
|
68
68
|
const id = uuid();
|
|
69
69
|
const now = Date.now();
|
|
70
|
-
const trimmedStatement =
|
|
70
|
+
const trimmedStatement = statement.trim();
|
|
71
71
|
|
|
72
72
|
const fingerprint = computeMemoryFingerprint(
|
|
73
73
|
scopeId,
|
|
@@ -93,6 +93,7 @@ export async function handleMemorySave(
|
|
|
93
93
|
status: "active",
|
|
94
94
|
importance: 0.8,
|
|
95
95
|
lastSeenAt: now,
|
|
96
|
+
sourceType: "tool",
|
|
96
97
|
verificationState: "user_confirmed",
|
|
97
98
|
})
|
|
98
99
|
.where(eq(memoryItems.id, existing.id))
|
|
@@ -115,6 +116,7 @@ export async function handleMemorySave(
|
|
|
115
116
|
confidence: 0.95, // explicit saves have high confidence
|
|
116
117
|
importance: 0.8, // explicit saves are high importance
|
|
117
118
|
fingerprint,
|
|
119
|
+
sourceType: "tool",
|
|
118
120
|
verificationState: "user_confirmed",
|
|
119
121
|
scopeId,
|
|
120
122
|
firstSeenAt: now,
|
|
@@ -187,7 +189,7 @@ export async function handleMemoryUpdate(
|
|
|
187
189
|
}
|
|
188
190
|
|
|
189
191
|
const now = Date.now();
|
|
190
|
-
const trimmedStatement =
|
|
192
|
+
const trimmedStatement = statement.trim();
|
|
191
193
|
|
|
192
194
|
const fingerprint = computeMemoryFingerprint(
|
|
193
195
|
scopeId,
|
|
@@ -221,6 +223,7 @@ export async function handleMemoryUpdate(
|
|
|
221
223
|
fingerprint,
|
|
222
224
|
lastSeenAt: now,
|
|
223
225
|
importance: 0.8,
|
|
226
|
+
sourceType: "tool",
|
|
224
227
|
verificationState: "user_confirmed",
|
|
225
228
|
})
|
|
226
229
|
.where(eq(memoryItems.id, existing.id))
|
|
@@ -308,7 +311,7 @@ export async function handleMemoryRecall(
|
|
|
308
311
|
items: [],
|
|
309
312
|
sources: {
|
|
310
313
|
semantic: recall.semanticHits,
|
|
311
|
-
recency:
|
|
314
|
+
recency: 0,
|
|
312
315
|
},
|
|
313
316
|
};
|
|
314
317
|
return {
|
|
@@ -328,7 +331,7 @@ export async function handleMemoryRecall(
|
|
|
328
331
|
})),
|
|
329
332
|
sources: {
|
|
330
333
|
semantic: recall.semanticHits,
|
|
331
|
-
recency:
|
|
334
|
+
recency: 0,
|
|
332
335
|
},
|
|
333
336
|
};
|
|
334
337
|
|
|
@@ -416,7 +419,7 @@ export async function handleMemoryDelete(
|
|
|
416
419
|
function inferSubjectFromStatement(statement: string): string {
|
|
417
420
|
// Take first few words as a subject label
|
|
418
421
|
const words = statement.split(/\s+/).slice(0, 6).join(" ");
|
|
419
|
-
return
|
|
422
|
+
return words;
|
|
420
423
|
}
|
|
421
424
|
|
|
422
425
|
/**
|
|
@@ -61,10 +61,25 @@ const store = new SessionStore();
|
|
|
61
61
|
/**
|
|
62
62
|
* Host patterns that are allowed by default through the proxy policy engine,
|
|
63
63
|
* regardless of session configuration. Supports exact matches (e.g.
|
|
64
|
-
* `"localhost"`) and wildcard subdomain patterns (e.g. `"*.
|
|
65
|
-
* matches `
|
|
64
|
+
* `"localhost"`) and wildcard subdomain patterns (e.g. `"*.example.com"`
|
|
65
|
+
* matches `api.example.com`, `dev.example.com`, etc.).
|
|
66
|
+
*
|
|
67
|
+
* Additional patterns can be added via the `PROXY_ALLOWED_HOSTS` env var
|
|
68
|
+
* (comma-separated, e.g. `"*.example.com,api.foo.bar"`).
|
|
66
69
|
*/
|
|
67
|
-
const ALLOWED_HOST_PATTERNS: readonly string[] =
|
|
70
|
+
const ALLOWED_HOST_PATTERNS: readonly string[] = (() => {
|
|
71
|
+
const extra = process.env.PROXY_ALLOWED_HOSTS?.trim();
|
|
72
|
+
const defaults = ["localhost"];
|
|
73
|
+
if (extra) {
|
|
74
|
+
defaults.push(
|
|
75
|
+
...extra
|
|
76
|
+
.split(",")
|
|
77
|
+
.map((h) => h.trim())
|
|
78
|
+
.filter(Boolean),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return defaults;
|
|
82
|
+
})();
|
|
68
83
|
|
|
69
84
|
/**
|
|
70
85
|
* Returns `true` when `hostname` matches any entry in
|
|
@@ -73,7 +88,7 @@ const ALLOWED_HOST_PATTERNS: readonly string[] = ["*.vellum.ai", "localhost"];
|
|
|
73
88
|
function isAllowedHost(hostname: string): boolean {
|
|
74
89
|
for (const pattern of ALLOWED_HOST_PATTERNS) {
|
|
75
90
|
if (pattern.startsWith("*.")) {
|
|
76
|
-
const suffix = pattern.slice(1); // e.g. ".
|
|
91
|
+
const suffix = pattern.slice(1); // e.g. ".example.com"
|
|
77
92
|
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
78
93
|
return true;
|
|
79
94
|
}
|
|
@@ -573,7 +573,9 @@ export async function executeWebFetch(
|
|
|
573
573
|
Accept:
|
|
574
574
|
"text/markdown, text/html;q=0.9, application/xhtml+xml;q=0.9, text/plain;q=0.8, application/json;q=0.7, */*;q=0.6",
|
|
575
575
|
"Accept-Encoding": "identity",
|
|
576
|
-
"User-Agent":
|
|
576
|
+
"User-Agent":
|
|
577
|
+
process.env.HTTP_USER_AGENT ||
|
|
578
|
+
"VellumAssistant/1.0 (+https://vellum.ai)",
|
|
577
579
|
};
|
|
578
580
|
|
|
579
581
|
let currentUrl = new URL(requestedUrl);
|
|
@@ -30,7 +30,7 @@ export class SkillExecuteTool implements Tool {
|
|
|
30
30
|
activity: {
|
|
31
31
|
type: "string",
|
|
32
32
|
description:
|
|
33
|
-
"Brief non-technical explanation of what you are doing and why, shown to the user as a
|
|
33
|
+
"Brief non-technical explanation of what you are doing and why, shown to the user as a progress update.",
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
36
|
required: ["tool", "input", "activity"],
|
package/src/tools/types.ts
CHANGED
|
@@ -118,14 +118,6 @@ export interface ToolContext {
|
|
|
118
118
|
proxyToolResolver?: ProxyToolResolver;
|
|
119
119
|
/** When set, only tools in this set may execute. Tools outside the set are blocked with an error. */
|
|
120
120
|
allowedToolNames?: Set<string>;
|
|
121
|
-
/** Request user confirmation for a sub-tool operation (used by claude_code tool). */
|
|
122
|
-
requestConfirmation?: (req: {
|
|
123
|
-
toolName: string;
|
|
124
|
-
input: Record<string, unknown>;
|
|
125
|
-
riskLevel: string;
|
|
126
|
-
executionTarget?: ExecutionTarget;
|
|
127
|
-
principal?: string;
|
|
128
|
-
}) => Promise<{ decision: "allow" | "deny" }>;
|
|
129
121
|
/** Prompt the user for a secret value via native SecureField UI. */
|
|
130
122
|
requestSecret?: (params: {
|
|
131
123
|
service: string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isLinux, isMacOS } from "./platform.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Open a URL in the user's default browser, falling back to printing the URL
|
|
5
|
+
* to stderr on unsupported platforms.
|
|
6
|
+
*/
|
|
7
|
+
export function openInBrowser(url: string): void {
|
|
8
|
+
if (isMacOS()) {
|
|
9
|
+
Bun.spawn(["open", url], { stdout: "ignore", stderr: "ignore" });
|
|
10
|
+
} else if (isLinux()) {
|
|
11
|
+
Bun.spawn(["xdg-open", url], { stdout: "ignore", stderr: "ignore" });
|
|
12
|
+
} else {
|
|
13
|
+
process.stderr.write(`Open this URL to authorize:\n\n${url}\n`);
|
|
14
|
+
}
|
|
15
|
+
}
|
package/src/util/errors.ts
CHANGED
|
@@ -20,9 +20,6 @@ export enum ErrorCode {
|
|
|
20
20
|
// WASM integrity check failures
|
|
21
21
|
INTEGRITY_ERROR = "INTEGRITY_ERROR",
|
|
22
22
|
|
|
23
|
-
// Secret detected in inbound content
|
|
24
|
-
INGRESS_BLOCKED = "INGRESS_BLOCKED",
|
|
25
|
-
|
|
26
23
|
// Internal/unexpected errors
|
|
27
24
|
INTERNAL_ERROR = "INTERNAL_ERROR",
|
|
28
25
|
}
|
|
@@ -178,12 +175,3 @@ export class IntegrityError extends AssistantError {
|
|
|
178
175
|
}
|
|
179
176
|
}
|
|
180
177
|
|
|
181
|
-
export class IngressBlockedError extends AssistantError {
|
|
182
|
-
constructor(
|
|
183
|
-
message: string,
|
|
184
|
-
public readonly detectedTypes: string[],
|
|
185
|
-
) {
|
|
186
|
-
super(message, ErrorCode.INGRESS_BLOCKED);
|
|
187
|
-
this.name = "IngressBlockedError";
|
|
188
|
-
}
|
|
189
|
-
}
|
package/src/util/platform.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
existsSync,
|
|
4
4
|
mkdirSync,
|
|
5
5
|
readFileSync,
|
|
6
|
-
writeFileSync,
|
|
7
6
|
} from "node:fs";
|
|
8
7
|
import { homedir } from "node:os";
|
|
9
8
|
import { join } from "node:path";
|
|
@@ -45,25 +44,6 @@ export function getClipboardCommand(): string | null {
|
|
|
45
44
|
return null;
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
/**
|
|
49
|
-
* Read and parse the lockfile (~/.vellum.lock.json).
|
|
50
|
-
* Respects BASE_DATA_DIR for non-standard home directories.
|
|
51
|
-
* Returns null if the file doesn't exist or is malformed.
|
|
52
|
-
*/
|
|
53
|
-
export function readLockfile(): Record<string, unknown> | null {
|
|
54
|
-
const base = getBaseDataDir() || homedir();
|
|
55
|
-
const lockPath = join(base, ".vellum.lock.json");
|
|
56
|
-
if (!existsSync(lockPath)) return null;
|
|
57
|
-
try {
|
|
58
|
-
const raw = JSON.parse(readFileSync(lockPath, "utf-8"));
|
|
59
|
-
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
60
|
-
return raw as Record<string, unknown>;
|
|
61
|
-
}
|
|
62
|
-
} catch {
|
|
63
|
-
// malformed JSON
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
47
|
|
|
68
48
|
/**
|
|
69
49
|
* Resolve the instance data directory from the lockfile.
|
|
@@ -155,45 +135,18 @@ export function resolveInstanceDataDir(): string | undefined {
|
|
|
155
135
|
* (see migration 007-assistant-id-to-self). However, the desktop UI
|
|
156
136
|
* sends the real assistant ID (e.g., "vellum-true-eel") while the
|
|
157
137
|
* inbound call path resolves phone numbers to config keys (typically
|
|
158
|
-
* "self"). This function maps
|
|
138
|
+
* "self"). This function maps the current assistant's ID to "self"
|
|
159
139
|
* so both sides use a consistent DB key.
|
|
160
|
-
*
|
|
161
|
-
* Multi-instance safety: each daemon process runs with a scoped
|
|
162
|
-
* BASE_DATA_DIR, so readLockfile() only sees the lockfile for this
|
|
163
|
-
* instance. The mapping to "self" is correct because each daemon is
|
|
164
|
-
* single-tenant — it only manages its own instance's data.
|
|
165
140
|
*/
|
|
166
141
|
export function normalizeAssistantId(assistantId: string): string {
|
|
167
142
|
if (assistantId === "self") return "self";
|
|
168
143
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const assistants = lockData?.assistants as
|
|
172
|
-
| Array<Record<string, unknown>>
|
|
173
|
-
| undefined;
|
|
174
|
-
if (assistants) {
|
|
175
|
-
for (const entry of assistants) {
|
|
176
|
-
if (entry.assistantId === assistantId) return "self";
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
} catch {
|
|
180
|
-
// lockfile unreadable — return as-is
|
|
181
|
-
}
|
|
144
|
+
const ownName = process.env.VELLUM_ASSISTANT_NAME;
|
|
145
|
+
if (ownName && assistantId === ownName) return "self";
|
|
182
146
|
|
|
183
147
|
return assistantId;
|
|
184
148
|
}
|
|
185
149
|
|
|
186
|
-
/**
|
|
187
|
-
* Write data to the primary lockfile (~/.vellum.lock.json).
|
|
188
|
-
* Respects BASE_DATA_DIR for non-standard home directories.
|
|
189
|
-
*/
|
|
190
|
-
export function writeLockfile(data: Record<string, unknown>): void {
|
|
191
|
-
const base = getBaseDataDir() || homedir();
|
|
192
|
-
writeFileSync(
|
|
193
|
-
join(base, ".vellum.lock.json"),
|
|
194
|
-
JSON.stringify(data, null, 2) + "\n",
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
150
|
|
|
198
151
|
/**
|
|
199
152
|
* Returns the root ~/.vellum directory. User-facing files (config, prompt
|
|
@@ -371,7 +324,7 @@ export function getSignalsDir(): string {
|
|
|
371
324
|
/**
|
|
372
325
|
* Returns the workspace root for user-facing state.
|
|
373
326
|
*
|
|
374
|
-
* When the
|
|
327
|
+
* When the VELLUM_WORKSPACE_DIR env var is set, returns that value (used in
|
|
375
328
|
* containerized deployments where the workspace is a separate volume).
|
|
376
329
|
* Otherwise falls back to ~/.vellum/workspace.
|
|
377
330
|
*
|
|
@@ -753,8 +753,11 @@ export class WorkspaceGitService {
|
|
|
753
753
|
* Must be called with the mutex lock held.
|
|
754
754
|
*/
|
|
755
755
|
private async ensureCommitIdentityLocked(): Promise<void> {
|
|
756
|
-
|
|
757
|
-
|
|
756
|
+
const gitName = process.env.ASSISTANT_GIT_USER_NAME || "Vellum Assistant";
|
|
757
|
+
const gitEmail =
|
|
758
|
+
process.env.ASSISTANT_GIT_USER_EMAIL || "assistant@vellum.ai";
|
|
759
|
+
await this.execGit(["config", "user.name", gitName]);
|
|
760
|
+
await this.execGit(["config", "user.email", gitEmail]);
|
|
758
761
|
}
|
|
759
762
|
|
|
760
763
|
/**
|
|
@@ -22,4 +22,19 @@ export const avatarRenameMigration: WorkspaceMigration = {
|
|
|
22
22
|
renameSync(oldTraits, newTraits);
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
|
+
down(workspaceDir: string): void {
|
|
26
|
+
const avatarDir = join(workspaceDir, "data", "avatar");
|
|
27
|
+
|
|
28
|
+
const newImage = join(avatarDir, "avatar-image.png");
|
|
29
|
+
const oldImage = join(avatarDir, "custom-avatar.png");
|
|
30
|
+
if (existsSync(newImage) && !existsSync(oldImage)) {
|
|
31
|
+
renameSync(newImage, oldImage);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const newTraits = join(avatarDir, "character-traits.json");
|
|
35
|
+
const oldTraits = join(avatarDir, "avatar-components.json");
|
|
36
|
+
if (existsSync(newTraits) && !existsSync(oldTraits)) {
|
|
37
|
+
renameSync(newTraits, oldTraits);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
25
40
|
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
unlinkSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
2
8
|
import { join } from "node:path";
|
|
3
9
|
|
|
4
10
|
import { getDeviceIdBaseDir } from "../../util/device-id.js";
|
|
@@ -97,4 +103,14 @@ export const seedDeviceIdMigration: WorkspaceMigration = {
|
|
|
97
103
|
// Best-effort — getDeviceId() will generate a new one if this fails.
|
|
98
104
|
}
|
|
99
105
|
},
|
|
106
|
+
down(_workspaceDir: string): void {
|
|
107
|
+
// The forward migration seeds deviceId in ~/.vellum/device.json from the
|
|
108
|
+
// lockfile. Reverse by removing device.json entirely — getDeviceId() will
|
|
109
|
+
// generate a fresh one on next startup if needed.
|
|
110
|
+
const base = getDeviceIdBaseDir();
|
|
111
|
+
const devicePath = join(base, ".vellum", "device.json");
|
|
112
|
+
if (existsSync(devicePath)) {
|
|
113
|
+
unlinkSync(devicePath);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
100
116
|
};
|
|
@@ -45,6 +45,39 @@ export const extractCollectUsageDataMigration: WorkspaceMigration = {
|
|
|
45
45
|
delete config.assistantFeatureFlagValues;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
49
|
+
},
|
|
50
|
+
down(workspaceDir: string): void {
|
|
51
|
+
const configPath = join(workspaceDir, "config.json");
|
|
52
|
+
if (!existsSync(configPath)) return;
|
|
53
|
+
|
|
54
|
+
let config: Record<string, unknown>;
|
|
55
|
+
try {
|
|
56
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
57
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
58
|
+
config = raw as Record<string, unknown>;
|
|
59
|
+
} catch {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Only reverse if collectUsageData was explicitly set to false
|
|
64
|
+
// (the forward migration only persisted false).
|
|
65
|
+
if (!("collectUsageData" in config)) return;
|
|
66
|
+
const value = config.collectUsageData;
|
|
67
|
+
if (typeof value !== "boolean") return;
|
|
68
|
+
|
|
69
|
+
// Restore the feature flag value
|
|
70
|
+
const FLAG_KEY = "feature_flags.collect-usage-data.enabled";
|
|
71
|
+
const flagValues = (config.assistantFeatureFlagValues ?? {}) as Record<
|
|
72
|
+
string,
|
|
73
|
+
unknown
|
|
74
|
+
>;
|
|
75
|
+
flagValues[FLAG_KEY] = value;
|
|
76
|
+
config.assistantFeatureFlagValues = flagValues;
|
|
77
|
+
|
|
78
|
+
// Remove the extracted top-level key
|
|
79
|
+
delete config.collectUsageData;
|
|
80
|
+
|
|
48
81
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
49
82
|
},
|
|
50
83
|
};
|
|
@@ -9,4 +9,7 @@ export const addSendDiagnosticsMigration: WorkspaceMigration = {
|
|
|
9
9
|
// will sync the UserDefaults value on first startup. This migration exists
|
|
10
10
|
// as a checkpoint marker for future reference.
|
|
11
11
|
},
|
|
12
|
+
down(_workspaceDir: string): void {
|
|
13
|
+
// No-op — the forward migration is a checkpoint marker with no data changes.
|
|
14
|
+
},
|
|
12
15
|
};
|
|
@@ -132,6 +132,55 @@ export const servicesConfigMigration: WorkspaceMigration = {
|
|
|
132
132
|
delete config.imageGenModel;
|
|
133
133
|
delete config.webSearchProvider;
|
|
134
134
|
|
|
135
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
136
|
+
},
|
|
137
|
+
down(workspaceDir: string): void {
|
|
138
|
+
const configPath = join(workspaceDir, "config.json");
|
|
139
|
+
if (!existsSync(configPath)) return;
|
|
140
|
+
|
|
141
|
+
let config: Record<string, unknown>;
|
|
142
|
+
try {
|
|
143
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
144
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
145
|
+
config = raw as Record<string, unknown>;
|
|
146
|
+
} catch {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const services = config.services;
|
|
151
|
+
if (!services || typeof services !== "object" || Array.isArray(services))
|
|
152
|
+
return;
|
|
153
|
+
|
|
154
|
+
const svc = services as Record<string, Record<string, unknown>>;
|
|
155
|
+
|
|
156
|
+
// Extract inference provider and model back to top-level fields.
|
|
157
|
+
// Note: inferenceMode is lost in this rollback — the original config did
|
|
158
|
+
// not store a mode field. This is an accepted lossy reversal.
|
|
159
|
+
if (svc.inference) {
|
|
160
|
+
if (typeof svc.inference.provider === "string") {
|
|
161
|
+
config.provider = svc.inference.provider;
|
|
162
|
+
}
|
|
163
|
+
if (typeof svc.inference.model === "string") {
|
|
164
|
+
config.model = svc.inference.model;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Extract image generation model back to top-level
|
|
169
|
+
if (svc["image-generation"]) {
|
|
170
|
+
if (typeof svc["image-generation"].model === "string") {
|
|
171
|
+
config.imageGenModel = svc["image-generation"].model;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Extract web search provider back to top-level
|
|
176
|
+
if (svc["web-search"]) {
|
|
177
|
+
if (typeof svc["web-search"].provider === "string") {
|
|
178
|
+
config.webSearchProvider = svc["web-search"].provider;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
delete config.services;
|
|
183
|
+
|
|
135
184
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
136
185
|
},
|
|
137
186
|
};
|
|
@@ -34,4 +34,31 @@ export const webSearchProviderRenameMigration: WorkspaceMigration = {
|
|
|
34
34
|
ws.provider = "inference-provider-native";
|
|
35
35
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
36
36
|
},
|
|
37
|
+
down(workspaceDir: string): void {
|
|
38
|
+
const configPath = join(workspaceDir, "config.json");
|
|
39
|
+
if (!existsSync(configPath)) return;
|
|
40
|
+
|
|
41
|
+
let config: Record<string, unknown>;
|
|
42
|
+
try {
|
|
43
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
44
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
45
|
+
config = raw as Record<string, unknown>;
|
|
46
|
+
} catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const services = config.services;
|
|
51
|
+
if (!services || typeof services !== "object" || Array.isArray(services))
|
|
52
|
+
return;
|
|
53
|
+
|
|
54
|
+
const webSearch = (services as Record<string, unknown>)["web-search"];
|
|
55
|
+
if (!webSearch || typeof webSearch !== "object" || Array.isArray(webSearch))
|
|
56
|
+
return;
|
|
57
|
+
|
|
58
|
+
const ws = webSearch as Record<string, unknown>;
|
|
59
|
+
if (ws.provider !== "inference-provider-native") return;
|
|
60
|
+
|
|
61
|
+
ws.provider = "anthropic-native";
|
|
62
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
63
|
+
},
|
|
37
64
|
};
|
|
@@ -9,4 +9,7 @@ export const voiceTimeoutAndMaxStepsMigration: WorkspaceMigration = {
|
|
|
9
9
|
// Existing users: macOS client will sync UserDefaults values
|
|
10
10
|
// to config on next startup via settings sync endpoints.
|
|
11
11
|
},
|
|
12
|
+
down(_workspaceDir: string): void {
|
|
13
|
+
// No-op — the forward migration is a checkpoint marker with no data changes.
|
|
14
|
+
},
|
|
12
15
|
};
|
|
@@ -7,4 +7,8 @@ export const backfillConversationDiskViewMigration: WorkspaceMigration = {
|
|
|
7
7
|
run(_workspaceDir: string): void {
|
|
8
8
|
rebuildConversationDiskViewFromDb();
|
|
9
9
|
},
|
|
10
|
+
// No-op: the disk view is a derived cache that can be regenerated from the
|
|
11
|
+
// database at any time. Removing it would only cause unnecessary I/O churn
|
|
12
|
+
// since the next forward migration (or startup rebuild) will recreate it.
|
|
13
|
+
down(_workspaceDir: string): void {},
|
|
10
14
|
};
|
|
@@ -76,6 +76,84 @@ export const appDirRenameMigration: WorkspaceMigration = {
|
|
|
76
76
|
description:
|
|
77
77
|
"Rename UUID-based app directories and files to human-readable slugified names",
|
|
78
78
|
|
|
79
|
+
down(workspaceDir: string): void {
|
|
80
|
+
const appsDir = join(workspaceDir, "data", "apps");
|
|
81
|
+
if (!existsSync(appsDir)) return;
|
|
82
|
+
|
|
83
|
+
const jsonFiles = readdirSync(appsDir)
|
|
84
|
+
.filter((f) => f.endsWith(".json"))
|
|
85
|
+
.sort();
|
|
86
|
+
|
|
87
|
+
if (jsonFiles.length === 0) return;
|
|
88
|
+
|
|
89
|
+
for (const jsonFile of jsonFiles) {
|
|
90
|
+
const jsonPath = join(appsDir, jsonFile);
|
|
91
|
+
let raw: string;
|
|
92
|
+
try {
|
|
93
|
+
raw = readFileSync(jsonPath, "utf-8");
|
|
94
|
+
} catch {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let parsed: {
|
|
99
|
+
id?: string;
|
|
100
|
+
name?: string;
|
|
101
|
+
dirName?: string;
|
|
102
|
+
};
|
|
103
|
+
try {
|
|
104
|
+
parsed = JSON.parse(raw);
|
|
105
|
+
} catch {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const appId = parsed.id;
|
|
110
|
+
if (!appId || !parsed.dirName || !isValidDirName(parsed.dirName)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const dirName = parsed.dirName;
|
|
115
|
+
|
|
116
|
+
// 1. Rename the app directory: {dirName}/ -> {appId}/
|
|
117
|
+
const slugDir = join(appsDir, dirName);
|
|
118
|
+
const uuidDir = join(appsDir, appId);
|
|
119
|
+
if (existsSync(slugDir) && !existsSync(uuidDir) && slugDir !== uuidDir) {
|
|
120
|
+
renameSync(slugDir, uuidDir);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 2. Rename the preview file: {dirName}.preview -> {appId}.preview
|
|
124
|
+
const slugPreview = join(appsDir, `${dirName}.preview`);
|
|
125
|
+
const uuidPreview = join(appsDir, `${appId}.preview`);
|
|
126
|
+
if (
|
|
127
|
+
existsSync(slugPreview) &&
|
|
128
|
+
!existsSync(uuidPreview) &&
|
|
129
|
+
slugPreview !== uuidPreview
|
|
130
|
+
) {
|
|
131
|
+
renameSync(slugPreview, uuidPreview);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 3. Remove dirName from JSON and rename file: {dirName}.json -> {appId}.json
|
|
135
|
+
const updatedParsed = { ...parsed };
|
|
136
|
+
delete updatedParsed.dirName;
|
|
137
|
+
const updatedJson = JSON.stringify(updatedParsed, null, 2);
|
|
138
|
+
|
|
139
|
+
const uuidJsonFile = `${appId}.json`;
|
|
140
|
+
const uuidJsonPath = join(appsDir, uuidJsonFile);
|
|
141
|
+
|
|
142
|
+
if (jsonFile !== uuidJsonFile) {
|
|
143
|
+
writeFileSync(uuidJsonPath, updatedJson, "utf-8");
|
|
144
|
+
if (existsSync(jsonPath) && jsonPath !== uuidJsonPath) {
|
|
145
|
+
try {
|
|
146
|
+
unlinkSync(jsonPath);
|
|
147
|
+
} catch {
|
|
148
|
+
// Old file cleanup is best-effort
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
writeFileSync(uuidJsonPath, updatedJson, "utf-8");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
79
157
|
run(workspaceDir: string): void {
|
|
80
158
|
const appsDir = join(workspaceDir, "data", "apps");
|
|
81
159
|
if (!existsSync(appsDir)) return;
|
|
@@ -14,6 +14,17 @@ export const backfillInstallationIdMigration: WorkspaceMigration = {
|
|
|
14
14
|
id: "011-backfill-installation-id",
|
|
15
15
|
description:
|
|
16
16
|
"Backfill installationId into lockfile from SQLite checkpoint and clean up stale row",
|
|
17
|
+
|
|
18
|
+
down(_workspaceDir: string): void {
|
|
19
|
+
// The forward migration moved an installationId from a SQLite checkpoint
|
|
20
|
+
// into the lockfile entry. Rolling back by removing installationId from
|
|
21
|
+
// the lockfile would break telemetry continuity and the field is harmless
|
|
22
|
+
// to leave in place. The SQLite checkpoint was already deleted and
|
|
23
|
+
// cannot be restored.
|
|
24
|
+
//
|
|
25
|
+
// No-op: leaving installationId in the lockfile is safe and non-disruptive.
|
|
26
|
+
},
|
|
27
|
+
|
|
17
28
|
run(_workspaceDir: string): void {
|
|
18
29
|
// a. Read existing installation ID from SQLite, or generate a new one.
|
|
19
30
|
// On fresh installs the memory_checkpoints table may not exist yet,
|