@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
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import {
|
|
3
|
+
appendFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
lstatSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
readlinkSync,
|
|
9
|
+
symlinkSync,
|
|
10
|
+
unlinkSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
|
|
15
|
+
import { getLogger } from "../util/logger.js";
|
|
16
|
+
|
|
17
|
+
const log = getLogger("install-symlink");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the path to the `vellum-assistant` binary that should be symlinked
|
|
21
|
+
* as `assistant`.
|
|
22
|
+
*
|
|
23
|
+
* - **Bundled (desktop app):** The daemon runs from a compiled binary inside
|
|
24
|
+
* `Vellum.app/Contents/MacOS/`. `process.execPath` is the daemon binary
|
|
25
|
+
* itself — the `vellum-assistant` CLI binary lives alongside it in the same
|
|
26
|
+
* directory.
|
|
27
|
+
*
|
|
28
|
+
* - **Dev (bun):** `process.execPath` is the bun runtime. We resolve the
|
|
29
|
+
* assistant entrypoint relative to this file's location in the source tree
|
|
30
|
+
* and return a wrapper script path that would need bun to run — so we skip
|
|
31
|
+
* symlinking in dev mode since developers manage their own PATH.
|
|
32
|
+
*/
|
|
33
|
+
function resolveAssistantBinary(): string | null {
|
|
34
|
+
const execPath = process.execPath;
|
|
35
|
+
// Detect the bundled desktop app case by checking for the app bundle path.
|
|
36
|
+
const isBundled = execPath.includes("/Contents/MacOS/");
|
|
37
|
+
|
|
38
|
+
if (isBundled) {
|
|
39
|
+
// The assistant CLI binary is a sibling of the daemon binary in
|
|
40
|
+
// Contents/MacOS/.
|
|
41
|
+
const macosDir = dirname(execPath);
|
|
42
|
+
const assistantBinary = join(macosDir, "vellum-assistant");
|
|
43
|
+
if (existsSync(assistantBinary)) {
|
|
44
|
+
return assistantBinary;
|
|
45
|
+
}
|
|
46
|
+
log.warn(
|
|
47
|
+
{ expected: assistantBinary },
|
|
48
|
+
"Bundled vellum-assistant binary not found alongside daemon",
|
|
49
|
+
);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Dev mode: resolve the assistant entrypoint from the source tree.
|
|
54
|
+
// The daemon entry is at assistant/src/daemon/main.ts and the assistant
|
|
55
|
+
// CLI entry is at assistant/src/index.ts.
|
|
56
|
+
const assistantEntry = join(dirname(__filename), "..", "index.ts");
|
|
57
|
+
if (existsSync(assistantEntry)) {
|
|
58
|
+
return assistantEntry;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Attempts to place a symlink at `symlinkPath` pointing to `target`.
|
|
65
|
+
* Returns true if the symlink was created or already points to the target.
|
|
66
|
+
*/
|
|
67
|
+
function trySymlink(target: string, symlinkPath: string): boolean {
|
|
68
|
+
try {
|
|
69
|
+
try {
|
|
70
|
+
const stats = lstatSync(symlinkPath);
|
|
71
|
+
if (!stats.isSymbolicLink()) {
|
|
72
|
+
// Real file — don't overwrite (could be a developer's local install)
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const dest = readlinkSync(symlinkPath);
|
|
76
|
+
if (dest === target) return true;
|
|
77
|
+
// Stale or dangling symlink — remove before creating new one
|
|
78
|
+
unlinkSync(symlinkPath);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
if ((e as NodeJS.ErrnoException)?.code !== "ENOENT") return false;
|
|
81
|
+
// Path doesn't exist — proceed to create symlink
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const dir = dirname(symlinkPath);
|
|
85
|
+
if (!existsSync(dir)) {
|
|
86
|
+
mkdirSync(dir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
symlinkSync(target, symlinkPath);
|
|
89
|
+
return true;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Ensures ~/.local/bin is present in the user's shell profile so that
|
|
97
|
+
* symlinks placed there are on PATH in new terminal sessions.
|
|
98
|
+
*/
|
|
99
|
+
function ensureLocalBinInShellProfile(localBinDir: string): void {
|
|
100
|
+
const shell = process.env.SHELL ?? "";
|
|
101
|
+
const home = homedir();
|
|
102
|
+
const profilePath = shell.endsWith("/zsh")
|
|
103
|
+
? join(home, ".zshrc")
|
|
104
|
+
: shell.endsWith("/bash")
|
|
105
|
+
? join(home, ".bash_profile")
|
|
106
|
+
: null;
|
|
107
|
+
if (!profilePath) return;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const contents = existsSync(profilePath)
|
|
111
|
+
? readFileSync(profilePath, "utf-8")
|
|
112
|
+
: "";
|
|
113
|
+
if (contents.includes(localBinDir)) return;
|
|
114
|
+
const line = `\nexport PATH="${localBinDir}:$PATH"\n`;
|
|
115
|
+
appendFileSync(profilePath, line);
|
|
116
|
+
log.info(
|
|
117
|
+
{ profilePath, localBinDir },
|
|
118
|
+
"Added ~/.local/bin to shell profile",
|
|
119
|
+
);
|
|
120
|
+
} catch {
|
|
121
|
+
// Not critical — user can add it manually
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Checks whether `assistant` already resolves on PATH to something other than
|
|
127
|
+
* our candidate symlink locations. If so, we skip symlinking to avoid
|
|
128
|
+
* overwriting a developer's local build.
|
|
129
|
+
*/
|
|
130
|
+
function commandResolvesElsewhere(
|
|
131
|
+
commandName: string,
|
|
132
|
+
candidatePaths: Set<string>,
|
|
133
|
+
): boolean {
|
|
134
|
+
try {
|
|
135
|
+
const resolved = execFileSync("/usr/bin/which", [commandName], {
|
|
136
|
+
encoding: "utf-8",
|
|
137
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
138
|
+
}).trim();
|
|
139
|
+
return resolved !== "" && !candidatePaths.has(resolved);
|
|
140
|
+
} catch {
|
|
141
|
+
// `which` exited non-zero — command not found, safe to proceed
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Idempotent: installs (or verifies) a symlink for the `assistant` command.
|
|
148
|
+
*
|
|
149
|
+
* Called on every daemon startup. Handles two runtime modes:
|
|
150
|
+
* - **Bundled binary** (desktop app): symlinks to the compiled
|
|
151
|
+
* `vellum-assistant` binary in the app bundle.
|
|
152
|
+
* - **Bun dev mode**: symlinks to a small wrapper script that invokes
|
|
153
|
+
* `bun run` on the assistant entrypoint.
|
|
154
|
+
*
|
|
155
|
+
* Tries `/usr/local/bin/assistant` first, then falls back to
|
|
156
|
+
* `~/.local/bin/assistant` (and patches the shell profile if needed).
|
|
157
|
+
*
|
|
158
|
+
* Skipped when VELLUM_DEV=1 (developers manage their own PATH).
|
|
159
|
+
*/
|
|
160
|
+
export function installAssistantSymlink(): void {
|
|
161
|
+
if (process.env.VELLUM_DEV === "1") return;
|
|
162
|
+
|
|
163
|
+
const target = resolveAssistantBinary();
|
|
164
|
+
if (!target) return;
|
|
165
|
+
|
|
166
|
+
const localBinDir = join(homedir(), ".local", "bin");
|
|
167
|
+
const candidateDirs = ["/usr/local/bin", localBinDir];
|
|
168
|
+
const candidatePaths = new Set(
|
|
169
|
+
candidateDirs.map((dir) => `${dir}/assistant`),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (commandResolvesElsewhere("assistant", candidatePaths)) {
|
|
173
|
+
log.info(
|
|
174
|
+
"`assistant` already resolves to a non-managed path — skipping symlink",
|
|
175
|
+
);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const dir of candidateDirs) {
|
|
180
|
+
const symlinkPath = join(dir, "assistant");
|
|
181
|
+
if (trySymlink(target, symlinkPath)) {
|
|
182
|
+
log.info({ symlinkPath, target }, "Installed assistant symlink");
|
|
183
|
+
if (dir === localBinDir) {
|
|
184
|
+
ensureLocalBinInShellProfile(localBinDir);
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
log.info(
|
|
189
|
+
{ symlinkPath },
|
|
190
|
+
"Could not install assistant symlink at candidate — trying next",
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
log.warn("Could not install assistant symlink in any candidate directory");
|
|
195
|
+
}
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { reconcileCallsOnStartup } from "../calls/call-recovery.js";
|
|
|
7
7
|
import { setRelayBroadcast } from "../calls/relay-server.js";
|
|
8
8
|
import { TwilioConversationRelayProvider } from "../calls/twilio-provider.js";
|
|
9
9
|
import { setVoiceBridgeDeps } from "../calls/voice-session-bridge.js";
|
|
10
|
-
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
11
10
|
import {
|
|
12
11
|
getQdrantHttpPortEnv,
|
|
13
12
|
getQdrantUrlEnv,
|
|
@@ -16,12 +15,27 @@ import {
|
|
|
16
15
|
setIngressPublicBaseUrl,
|
|
17
16
|
validateEnv,
|
|
18
17
|
} from "../config/env.js";
|
|
19
|
-
import { loadConfig } from "../config/loader.js";
|
|
18
|
+
import { loadConfig, mergeDefaultWorkspaceConfig } from "../config/loader.js";
|
|
19
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
20
|
+
import type { CesClient } from "../credential-execution/client.js";
|
|
21
|
+
import { createCesClient } from "../credential-execution/client.js";
|
|
22
|
+
import {
|
|
23
|
+
isCesCredentialBackendEnabled,
|
|
24
|
+
isCesToolsEnabled,
|
|
25
|
+
} from "../credential-execution/feature-gates.js";
|
|
26
|
+
import {
|
|
27
|
+
type CesProcessManager,
|
|
28
|
+
CesUnavailableError,
|
|
29
|
+
createCesProcessManager,
|
|
30
|
+
} from "../credential-execution/process-manager.js";
|
|
31
|
+
import {
|
|
32
|
+
awaitCesClientWithTimeout,
|
|
33
|
+
DEFAULT_CES_STARTUP_TIMEOUT_MS,
|
|
34
|
+
} from "../credential-execution/startup-timeout.js";
|
|
20
35
|
import { HeartbeatService } from "../heartbeat/heartbeat-service.js";
|
|
21
36
|
import { getHookManager } from "../hooks/manager.js";
|
|
22
37
|
import { installTemplates } from "../hooks/templates.js";
|
|
23
38
|
import { closeSentry, initSentry, setSentryDeviceId } from "../instrument.js";
|
|
24
|
-
import { disableLogfire, initLogfire } from "../logfire.js";
|
|
25
39
|
import { getMcpServerManager } from "../mcp/manager.js";
|
|
26
40
|
import * as attachmentsStore from "../memory/attachments-store.js";
|
|
27
41
|
import { expireAllPendingCanonicalRequests } from "../memory/canonical-guardian-store.js";
|
|
@@ -51,6 +65,7 @@ import { backfillManualTokenConnections } from "../oauth/manual-token-connection
|
|
|
51
65
|
import { seedOAuthProviders } from "../oauth/seed-providers.js";
|
|
52
66
|
import { ensurePromptFiles } from "../prompts/system-prompt.js";
|
|
53
67
|
import { syncUpdateBulletinOnStartup } from "../prompts/update-bulletin.js";
|
|
68
|
+
import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
|
|
54
69
|
import { buildAssistantEvent } from "../runtime/assistant-event.js";
|
|
55
70
|
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
56
71
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
@@ -62,6 +77,7 @@ import {
|
|
|
62
77
|
import { ensureVellumGuardianBinding } from "../runtime/guardian-vellum-migration.js";
|
|
63
78
|
import { RuntimeHttpServer } from "../runtime/http-server.js";
|
|
64
79
|
import { startScheduler } from "../schedule/scheduler.js";
|
|
80
|
+
import { setCesClient } from "../security/secure-keys.js";
|
|
65
81
|
import { seedCatalogSkillMemories } from "../skills/skill-memory.js";
|
|
66
82
|
import { UsageTelemetryReporter } from "../telemetry/usage-telemetry-reporter.js";
|
|
67
83
|
import { getDeviceId } from "../util/device-id.js";
|
|
@@ -101,6 +117,7 @@ import {
|
|
|
101
117
|
switchConversation,
|
|
102
118
|
undoLastMessage,
|
|
103
119
|
} from "./handlers/conversations.js";
|
|
120
|
+
import { installAssistantSymlink } from "./install-symlink.js";
|
|
104
121
|
import type { ServerMessage } from "./message-protocol.js";
|
|
105
122
|
import {
|
|
106
123
|
initializeProvidersAndTools,
|
|
@@ -129,6 +146,104 @@ function loadDotEnv(): void {
|
|
|
129
146
|
dotenvConfig({ path: join(getRootDir(), ".env"), quiet: true });
|
|
130
147
|
}
|
|
131
148
|
|
|
149
|
+
export interface CesStartupResult {
|
|
150
|
+
client: CesClient | undefined;
|
|
151
|
+
processManager: CesProcessManager | undefined;
|
|
152
|
+
clientPromise: Promise<CesClient | undefined> | undefined;
|
|
153
|
+
abortController: AbortController | undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Start the CES (Credential Execution Service) process and perform the RPC
|
|
158
|
+
* handshake. Returns a promise that resolves with the CES client and process
|
|
159
|
+
* manager. Callers can fire-and-forget — the daemon does not need to await
|
|
160
|
+
* this for startup to continue.
|
|
161
|
+
*
|
|
162
|
+
* The managed sidecar accepts exactly one bootstrap connection, so this must
|
|
163
|
+
* be called at the process level (not per-conversation).
|
|
164
|
+
*/
|
|
165
|
+
export async function startCesProcess(
|
|
166
|
+
config: AssistantConfig,
|
|
167
|
+
): Promise<CesStartupResult> {
|
|
168
|
+
const shouldStartCes =
|
|
169
|
+
isCesToolsEnabled(config) || isCesCredentialBackendEnabled(config);
|
|
170
|
+
if (!shouldStartCes) {
|
|
171
|
+
return {
|
|
172
|
+
client: undefined,
|
|
173
|
+
processManager: undefined,
|
|
174
|
+
clientPromise: undefined,
|
|
175
|
+
abortController: undefined,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const pm = createCesProcessManager({ assistantConfig: config });
|
|
180
|
+
const abortController = new AbortController();
|
|
181
|
+
let clientRef: CesClient | undefined;
|
|
182
|
+
|
|
183
|
+
const clientPromise = (async (): Promise<CesClient | undefined> => {
|
|
184
|
+
try {
|
|
185
|
+
const transport = await pm.start();
|
|
186
|
+
if (abortController.signal.aborted) {
|
|
187
|
+
throw new Error("CES initialization aborted during shutdown");
|
|
188
|
+
}
|
|
189
|
+
const client = createCesClient(transport);
|
|
190
|
+
clientRef = client;
|
|
191
|
+
// Resolve the assistant API key so CES can use it for platform
|
|
192
|
+
// credential materialisation. In managed mode the key is provisioned
|
|
193
|
+
// after hatch and stored in the credential store — CES can't read
|
|
194
|
+
// the env var, so we pass it via the handshake.
|
|
195
|
+
const proxyCtx = await resolveManagedProxyContext();
|
|
196
|
+
const { accepted, reason } = await client.handshake(
|
|
197
|
+
proxyCtx.assistantApiKey
|
|
198
|
+
? { assistantApiKey: proxyCtx.assistantApiKey }
|
|
199
|
+
: undefined,
|
|
200
|
+
);
|
|
201
|
+
if (abortController.signal.aborted) {
|
|
202
|
+
client.close();
|
|
203
|
+
throw new Error("CES initialization aborted during shutdown");
|
|
204
|
+
}
|
|
205
|
+
if (accepted) {
|
|
206
|
+
log.info(
|
|
207
|
+
"CES client initialized and handshake accepted (server-level)",
|
|
208
|
+
);
|
|
209
|
+
return client;
|
|
210
|
+
}
|
|
211
|
+
log.warn(
|
|
212
|
+
{ reason },
|
|
213
|
+
"CES handshake rejected — CES tools will be unavailable",
|
|
214
|
+
);
|
|
215
|
+
client.close();
|
|
216
|
+
clientRef = undefined;
|
|
217
|
+
await pm.stop();
|
|
218
|
+
return undefined;
|
|
219
|
+
} catch (err) {
|
|
220
|
+
if (err instanceof CesUnavailableError) {
|
|
221
|
+
log.info(
|
|
222
|
+
{ reason: err.message },
|
|
223
|
+
"CES is not available — CES tools will be unavailable",
|
|
224
|
+
);
|
|
225
|
+
} else {
|
|
226
|
+
log.warn(
|
|
227
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
228
|
+
"Failed to initialize CES client — CES tools will be unavailable",
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
await pm.stop().catch(() => {});
|
|
232
|
+
clientRef = undefined;
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
})();
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
get client() {
|
|
239
|
+
return clientRef;
|
|
240
|
+
},
|
|
241
|
+
processManager: pm,
|
|
242
|
+
clientPromise,
|
|
243
|
+
abortController,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
132
247
|
// Entry point for the daemon process itself
|
|
133
248
|
export async function runDaemon(): Promise<void> {
|
|
134
249
|
loadDotEnv();
|
|
@@ -140,14 +255,12 @@ export async function runDaemon(): Promise<void> {
|
|
|
140
255
|
// closeSentry() if the user has disabled it.
|
|
141
256
|
initSentry();
|
|
142
257
|
|
|
143
|
-
await initLogfire();
|
|
144
|
-
|
|
145
258
|
ensureDataDir();
|
|
146
259
|
|
|
147
260
|
// Load (or generate + persist) the auth signing key so tokens survive
|
|
148
261
|
// daemon restarts. Must happen after ensureDataDir() creates the
|
|
149
262
|
// protected directory.
|
|
150
|
-
const signingKey =
|
|
263
|
+
const signingKey = resolveSigningKey();
|
|
151
264
|
initAuthSigningKey(signingKey);
|
|
152
265
|
|
|
153
266
|
seedInterfaceFiles();
|
|
@@ -163,9 +276,19 @@ export async function runDaemon(): Promise<void> {
|
|
|
163
276
|
initializeDb();
|
|
164
277
|
// Seed well-known OAuth provider configurations (insert-if-not-exists)
|
|
165
278
|
seedOAuthProviders();
|
|
279
|
+
log.info("Daemon startup: DB initialized");
|
|
280
|
+
|
|
281
|
+
await runWorkspaceMigrations(getWorkspaceDir(), WORKSPACE_MIGRATIONS);
|
|
282
|
+
log.info("Daemon startup: workspace migrations complete");
|
|
283
|
+
|
|
166
284
|
// Backfill oauth_connection rows for manual-token providers (Telegram,
|
|
167
285
|
// Slack channel) that already have keychain credentials from before the
|
|
168
286
|
// oauth_connection migration. Safe to call on every startup.
|
|
287
|
+
//
|
|
288
|
+
// Must run AFTER workspace migrations so that migration 015 (which copies
|
|
289
|
+
// encrypted-store credentials to the keychain) has already executed.
|
|
290
|
+
// Otherwise syncManualTokenConnection sees no keychain credentials and
|
|
291
|
+
// incorrectly removes existing connection rows.
|
|
169
292
|
try {
|
|
170
293
|
await backfillManualTokenConnections();
|
|
171
294
|
} catch (err) {
|
|
@@ -174,10 +297,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
174
297
|
"Manual-token connection backfill failed — continuing startup",
|
|
175
298
|
);
|
|
176
299
|
}
|
|
177
|
-
log.info("Daemon startup: DB initialized");
|
|
178
|
-
|
|
179
|
-
await runWorkspaceMigrations(getWorkspaceDir(), WORKSPACE_MIGRATIONS);
|
|
180
|
-
log.info("Daemon startup: workspace migrations complete");
|
|
181
300
|
|
|
182
301
|
// Now that workspace migrations have run (including 003-seed-device-id
|
|
183
302
|
// which may copy the legacy installationId into device.json), it is safe
|
|
@@ -290,6 +409,11 @@ export async function runDaemon(): Promise<void> {
|
|
|
290
409
|
log.warn({ err }, "Call recovery failed — continuing startup");
|
|
291
410
|
}
|
|
292
411
|
|
|
412
|
+
// Merge CLI-provided default config (from VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH)
|
|
413
|
+
// into the workspace config file before the first loadConfig() call so
|
|
414
|
+
// onboarding preferences are persisted alongside schema defaults.
|
|
415
|
+
mergeDefaultWorkspaceConfig();
|
|
416
|
+
|
|
293
417
|
log.info("Daemon startup: loading config");
|
|
294
418
|
const config = loadConfig();
|
|
295
419
|
|
|
@@ -330,16 +454,36 @@ export async function runDaemon(): Promise<void> {
|
|
|
330
454
|
log.info("Usage telemetry reporter started");
|
|
331
455
|
}
|
|
332
456
|
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
457
|
+
// CES lifecycle — kick off early so CES handshake runs concurrently with
|
|
458
|
+
// provider/tool initialization. The CES sidecar accepts exactly one
|
|
459
|
+
// bootstrap connection, so startup must happen at the process level.
|
|
460
|
+
const cesStartupPromise = startCesProcess(config);
|
|
461
|
+
|
|
462
|
+
// When the credential backend flag is enabled, CES startup must complete
|
|
463
|
+
// BEFORE provider initialization so credential reads can go through CES.
|
|
464
|
+
// Block with a 20-second timeout — fall back to direct credential store
|
|
465
|
+
// on timeout.
|
|
466
|
+
if (isCesCredentialBackendEnabled(config)) {
|
|
467
|
+
const cesResult = await cesStartupPromise;
|
|
468
|
+
// startCesProcess() returns immediately — the actual handshake runs
|
|
469
|
+
// inside clientPromise. Await it (with a 20s timeout) so the CES client
|
|
470
|
+
// is available before provider initialization.
|
|
471
|
+
if (cesResult.clientPromise) {
|
|
472
|
+
const client = await awaitCesClientWithTimeout(
|
|
473
|
+
cesResult.clientPromise,
|
|
474
|
+
{
|
|
475
|
+
timeoutMs: DEFAULT_CES_STARTUP_TIMEOUT_MS,
|
|
476
|
+
onTimeout: () => {
|
|
477
|
+
log.warn(
|
|
478
|
+
"CES handshake timed out after 20s — falling back to direct credential store",
|
|
479
|
+
);
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
);
|
|
483
|
+
if (client) {
|
|
484
|
+
setCesClient(client);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
343
487
|
}
|
|
344
488
|
|
|
345
489
|
await initializeProvidersAndTools(config);
|
|
@@ -348,6 +492,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
348
492
|
// routes can begin accepting requests while Qdrant initializes.
|
|
349
493
|
log.info("Daemon startup: starting DaemonServer");
|
|
350
494
|
const server = new DaemonServer();
|
|
495
|
+
server.setCes(await cesStartupPromise);
|
|
351
496
|
await server.start();
|
|
352
497
|
log.info("Daemon startup: DaemonServer started");
|
|
353
498
|
|
|
@@ -371,39 +516,69 @@ export async function runDaemon(): Promise<void> {
|
|
|
371
516
|
log.info({ qdrantUrl }, "Daemon startup: initializing Qdrant");
|
|
372
517
|
const manager = new QdrantManager({ url: qdrantUrl });
|
|
373
518
|
bgRefs.qdrantManager = manager;
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
519
|
+
const QDRANT_START_MAX_ATTEMPTS = 3;
|
|
520
|
+
let qdrantStarted = false;
|
|
521
|
+
for (let attempt = 1; attempt <= QDRANT_START_MAX_ATTEMPTS; attempt++) {
|
|
522
|
+
try {
|
|
523
|
+
await manager.start();
|
|
524
|
+
qdrantStarted = true;
|
|
525
|
+
break;
|
|
526
|
+
} catch (err) {
|
|
527
|
+
if (attempt < QDRANT_START_MAX_ATTEMPTS) {
|
|
528
|
+
const backoffMs = attempt * 5_000; // 5s, 10s
|
|
529
|
+
log.warn(
|
|
530
|
+
{
|
|
531
|
+
err,
|
|
532
|
+
attempt,
|
|
533
|
+
maxAttempts: QDRANT_START_MAX_ATTEMPTS,
|
|
534
|
+
backoffMs,
|
|
535
|
+
},
|
|
536
|
+
"Qdrant startup failed, retrying",
|
|
537
|
+
);
|
|
538
|
+
await Bun.sleep(backoffMs);
|
|
539
|
+
} else {
|
|
540
|
+
log.warn(
|
|
541
|
+
{ err },
|
|
542
|
+
"Qdrant failed to start after all attempts — memory features will be unavailable",
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (qdrantStarted) {
|
|
549
|
+
try {
|
|
550
|
+
const embeddingSelection = await selectEmbeddingBackend(config);
|
|
551
|
+
const embeddingModel = embeddingSelection.backend
|
|
552
|
+
? `${embeddingSelection.backend.provider}:${embeddingSelection.backend.model}:sparse-v${SPARSE_EMBEDDING_VERSION}`
|
|
553
|
+
: undefined;
|
|
554
|
+
const qdrantClient = initQdrantClient({
|
|
555
|
+
url: qdrantUrl,
|
|
556
|
+
collection: config.memory.qdrant.collection,
|
|
557
|
+
vectorSize: config.memory.qdrant.vectorSize,
|
|
558
|
+
onDisk: config.memory.qdrant.onDisk,
|
|
559
|
+
quantization: config.memory.qdrant.quantization,
|
|
560
|
+
embeddingModel,
|
|
561
|
+
});
|
|
388
562
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
563
|
+
// Eagerly ensure the collection exists so we detect migrations
|
|
564
|
+
// (unnamed→named vectors, dimension/model changes) at startup.
|
|
565
|
+
// If a destructive migration occurred, enqueue a rebuild_index job
|
|
566
|
+
// to re-embed all memory items from the SQLite cache.
|
|
567
|
+
const { migrated } = await qdrantClient.ensureCollection();
|
|
568
|
+
if (migrated) {
|
|
569
|
+
enqueueMemoryJob("rebuild_index", {});
|
|
570
|
+
log.info(
|
|
571
|
+
"Qdrant collection was migrated — enqueued rebuild_index job",
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
log.info("Qdrant vector store initialized");
|
|
576
|
+
} catch (err) {
|
|
577
|
+
log.warn(
|
|
578
|
+
{ err },
|
|
579
|
+
"Qdrant client initialization failed — memory features will be degraded",
|
|
398
580
|
);
|
|
399
581
|
}
|
|
400
|
-
|
|
401
|
-
log.info("Qdrant vector store initialized");
|
|
402
|
-
} catch (err) {
|
|
403
|
-
log.warn(
|
|
404
|
-
{ err },
|
|
405
|
-
"Qdrant failed to start — memory features will be unavailable",
|
|
406
|
-
);
|
|
407
582
|
}
|
|
408
583
|
|
|
409
584
|
log.info("Daemon startup: starting memory worker");
|
|
@@ -833,7 +1008,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
833
1008
|
server.broadcast(msg as ServerMessage),
|
|
834
1009
|
);
|
|
835
1010
|
initSlashPairingContext(runtimeHttp.getPairingStore());
|
|
836
|
-
server.
|
|
1011
|
+
server.broadcastStatus();
|
|
837
1012
|
log.info(
|
|
838
1013
|
{ port: httpPort, hostname },
|
|
839
1014
|
"Daemon startup: runtime HTTP server listening",
|
|
@@ -849,6 +1024,14 @@ export async function runDaemon(): Promise<void> {
|
|
|
849
1024
|
writePid(process.pid);
|
|
850
1025
|
log.info({ pid: process.pid }, "Daemon started");
|
|
851
1026
|
|
|
1027
|
+
// Install the `assistant` CLI symlink idempotently on every daemon start.
|
|
1028
|
+
// Non-blocking — failures are logged but don't affect startup.
|
|
1029
|
+
try {
|
|
1030
|
+
installAssistantSymlink();
|
|
1031
|
+
} catch (err) {
|
|
1032
|
+
log.warn({ err }, "Assistant symlink installation failed — continuing");
|
|
1033
|
+
}
|
|
1034
|
+
|
|
852
1035
|
const hookManager = getHookManager();
|
|
853
1036
|
hookManager.watch();
|
|
854
1037
|
|
|
@@ -221,9 +221,8 @@ export interface PongMessage {
|
|
|
221
221
|
type: "pong";
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
export interface
|
|
225
|
-
type: "
|
|
226
|
-
httpPort?: number;
|
|
224
|
+
export interface AssistantStatusMessage {
|
|
225
|
+
type: "assistant_status";
|
|
227
226
|
version?: string;
|
|
228
227
|
keyFingerprint?: string;
|
|
229
228
|
}
|
|
@@ -372,6 +371,7 @@ export type ConversationErrorCode =
|
|
|
372
371
|
| "PROVIDER_BILLING"
|
|
373
372
|
| "PROVIDER_ORDERING"
|
|
374
373
|
| "PROVIDER_WEB_SEARCH"
|
|
374
|
+
| "PROVIDER_NOT_CONFIGURED"
|
|
375
375
|
| "CONTEXT_TOO_LARGE"
|
|
376
376
|
| "CONVERSATION_ABORTED"
|
|
377
377
|
| "CONVERSATION_PROCESSING_FAILED"
|
|
@@ -419,7 +419,7 @@ export type _ConversationsClientMessages =
|
|
|
419
419
|
export type _ConversationsServerMessages =
|
|
420
420
|
| AuthResult
|
|
421
421
|
| PongMessage
|
|
422
|
-
|
|
|
422
|
+
| AssistantStatusMessage
|
|
423
423
|
| GenerationCancelled
|
|
424
424
|
| GenerationHandoff
|
|
425
425
|
| ModelInfo
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
// Diagnostics, environment,
|
|
1
|
+
// Diagnostics, environment, and dictation types.
|
|
2
2
|
|
|
3
3
|
import type { DictationContext } from "./shared.js";
|
|
4
4
|
|
|
5
5
|
// === Client → Server ===
|
|
6
6
|
|
|
7
|
-
export interface DiagnosticsExportRequest {
|
|
8
|
-
type: "diagnostics_export_request";
|
|
9
|
-
conversationId: string;
|
|
10
|
-
anchorMessageId?: string; // if omitted, use latest assistant message
|
|
11
|
-
}
|
|
12
|
-
|
|
13
7
|
export interface EnvVarsRequest {
|
|
14
8
|
type: "env_vars_request";
|
|
15
9
|
}
|
|
@@ -23,13 +17,6 @@ export interface DictationRequest {
|
|
|
23
17
|
|
|
24
18
|
// === Server → Client ===
|
|
25
19
|
|
|
26
|
-
export interface DiagnosticsExportResponse {
|
|
27
|
-
type: "diagnostics_export_response";
|
|
28
|
-
success: boolean;
|
|
29
|
-
filePath?: string; // path to the zip file on success
|
|
30
|
-
error?: string; // error message on failure
|
|
31
|
-
}
|
|
32
|
-
|
|
33
20
|
export interface EnvVarsResponse {
|
|
34
21
|
type: "env_vars_response";
|
|
35
22
|
vars: Record<string, string>;
|
|
@@ -46,12 +33,6 @@ export interface DictationResponse {
|
|
|
46
33
|
|
|
47
34
|
// --- Domain-level union aliases (consumed by the barrel file) ---
|
|
48
35
|
|
|
49
|
-
export type _DiagnosticsClientMessages =
|
|
50
|
-
| DiagnosticsExportRequest
|
|
51
|
-
| EnvVarsRequest
|
|
52
|
-
| DictationRequest;
|
|
36
|
+
export type _DiagnosticsClientMessages = EnvVarsRequest | DictationRequest;
|
|
53
37
|
|
|
54
|
-
export type _DiagnosticsServerMessages =
|
|
55
|
-
| DiagnosticsExportResponse
|
|
56
|
-
| EnvVarsResponse
|
|
57
|
-
| DictationResponse;
|
|
38
|
+
export type _DiagnosticsServerMessages = EnvVarsResponse | DictationResponse;
|
|
@@ -13,8 +13,6 @@ export interface UserMessage {
|
|
|
13
13
|
activeSurfaceId?: string;
|
|
14
14
|
/** The page currently displayed in the WebView (e.g. "settings.html"). */
|
|
15
15
|
currentPage?: string;
|
|
16
|
-
/** When true, skip the secret-ingress check. Set by the client when the user clicks "Send Anyway". */
|
|
17
|
-
bypassSecretCheck?: boolean;
|
|
18
16
|
/** Originating channel identifier (e.g. 'vellum'). Defaults to 'vellum' when absent. */
|
|
19
17
|
channel?: ChannelId;
|
|
20
18
|
/** Originating interface identifier (e.g. 'macos'). */
|