@vellumai/assistant 0.3.4 → 0.3.6
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/Dockerfile +2 -0
- package/README.md +88 -2
- package/eslint.config.mjs +31 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
- package/scripts/ipc/generate-swift.ts +31 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +799 -249
- package/src/__tests__/call-pointer-messages.test.ts +148 -0
- package/src/__tests__/call-recovery.test.ts +3 -0
- package/src/__tests__/call-routes-http.test.ts +32 -2
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1277 -98
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +36 -50
- package/src/__tests__/channel-guardian.test.ts +630 -22
- package/src/__tests__/channel-readiness-service.test.ts +324 -0
- package/src/__tests__/checker.test.ts +14 -7
- package/src/__tests__/clarification-resolver.test.ts +44 -24
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
- package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
- package/src/__tests__/config-schema.test.ts +14 -8
- package/src/__tests__/context-window-manager.test.ts +30 -2
- package/src/__tests__/contradiction-checker.test.ts +20 -5
- package/src/__tests__/credential-security-invariants.test.ts +7 -2
- package/src/__tests__/daemon-lifecycle.test.ts +13 -12
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/entity-search.test.ts +615 -0
- package/src/__tests__/fuzzy-match-property.test.ts +5 -5
- package/src/__tests__/guardian-action-store.test.ts +123 -0
- package/src/__tests__/guardian-action-sweep.test.ts +277 -0
- package/src/__tests__/guardian-dispatch.test.ts +389 -0
- package/src/__tests__/guardian-question-copy.test.ts +47 -0
- package/src/__tests__/handlers-telegram-config.test.ts +4 -2
- package/src/__tests__/handlers-twilio-config.test.ts +533 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +291 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -0
- package/src/__tests__/model-intents.test.ts +96 -0
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
- package/src/__tests__/provider-error-scenarios.test.ts +621 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
- package/src/__tests__/qdrant-manager.test.ts +27 -20
- package/src/__tests__/relay-server.test.ts +779 -40
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
- package/src/__tests__/run-orchestrator.test.ts +42 -4
- package/src/__tests__/runtime-runs-http.test.ts +17 -1
- package/src/__tests__/runtime-runs.test.ts +16 -0
- package/src/__tests__/schedule-store.test.ts +18 -4
- package/src/__tests__/scheduler-recurrence.test.ts +13 -4
- package/src/__tests__/session-abort-tool-results.test.ts +6 -0
- package/src/__tests__/session-agent-loop.test.ts +857 -0
- package/src/__tests__/session-conflict-gate.test.ts +6 -0
- package/src/__tests__/session-pre-run-repair.test.ts +6 -0
- package/src/__tests__/session-profile-injection.test.ts +6 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/session-queue.test.ts +6 -0
- package/src/__tests__/session-runtime-assembly.test.ts +321 -13
- package/src/__tests__/session-slash-known.test.ts +6 -0
- package/src/__tests__/session-slash-queue.test.ts +6 -0
- package/src/__tests__/session-slash-unknown.test.ts +6 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/session-workspace-injection.test.ts +6 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/skills.test.ts +2 -0
- package/src/__tests__/sms-messaging-provider.test.ts +126 -0
- package/src/__tests__/starter-task-flow.test.ts +2 -0
- package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +2 -0
- package/src/__tests__/task-management-tools.test.ts +2 -2
- package/src/__tests__/task-runner.test.ts +14 -4
- package/src/__tests__/terminal-tools.test.ts +25 -19
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
- package/src/__tests__/tool-executor.test.ts +23 -24
- package/src/__tests__/trust-store.test.ts +3 -3
- package/src/__tests__/twilio-rest.test.ts +29 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
- package/src/__tests__/twilio-routes.test.ts +167 -11
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- package/src/__tests__/user-reference.test.ts +2 -0
- package/src/__tests__/voice-quality.test.ts +222 -0
- package/src/__tests__/web-search.test.ts +46 -30
- package/src/__tests__/work-item-output.test.ts +110 -0
- package/src/agent/loop.ts +1 -1
- package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
- package/src/amazon/client.ts +1418 -0
- package/src/amazon/request-extractor.ts +135 -0
- package/src/amazon/session.ts +109 -0
- package/src/autonomy/autonomy-store.ts +5 -5
- package/src/browser-extension-relay/client.ts +124 -0
- package/src/browser-extension-relay/protocol.ts +63 -0
- package/src/browser-extension-relay/server.ts +177 -0
- package/src/bundler/app-bundler.ts +3 -3
- package/src/bundler/bundle-signer.ts +1 -1
- package/src/bundler/signature-verifier.ts +1 -1
- package/src/calls/call-conversation-messages.ts +33 -0
- package/src/calls/call-domain.ts +114 -10
- package/src/calls/call-orchestrator.ts +268 -59
- package/src/calls/call-pointer-messages.ts +53 -0
- package/src/calls/call-recovery.ts +3 -8
- package/src/calls/call-store.ts +69 -87
- package/src/calls/elevenlabs-config.ts +3 -2
- package/src/calls/guardian-action-sweep.ts +105 -0
- package/src/calls/guardian-dispatch.ts +203 -0
- package/src/calls/guardian-question-copy.ts +133 -0
- package/src/calls/relay-server.ts +466 -8
- package/src/calls/speaker-identification.ts +1 -1
- package/src/calls/twilio-config.ts +22 -14
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +308 -7
- package/src/calls/twilio-routes.ts +65 -12
- package/src/calls/types.ts +3 -1
- package/src/channels/types.ts +25 -0
- package/src/cli/amazon.ts +815 -0
- package/src/cli/config-commands.ts +2 -2
- package/src/cli/core-commands.ts +4 -3
- package/src/cli/influencer.ts +244 -0
- package/src/cli/map.ts +89 -6
- package/src/cli.ts +1 -1
- package/src/config/agent-schema.ts +171 -0
- package/src/config/bundled-skills/amazon/SKILL.md +127 -0
- package/src/config/bundled-skills/amazon/icon.svg +13 -0
- package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
- package/src/config/bundled-skills/browser/SKILL.md +1 -0
- package/src/config/bundled-skills/browser/TOOLS.json +17 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
- package/src/config/bundled-skills/doordash/SKILL.md +51 -51
- package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
- package/src/config/bundled-skills/influencer/SKILL.md +144 -0
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
- package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
- package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
- package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
- package/src/config/bundled-skills/messaging/SKILL.md +33 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- package/src/config/bundled-skills/twitter/icon.svg +14 -0
- package/src/config/bundled-tool-registry.ts +310 -0
- package/src/config/calls-schema.ts +181 -0
- package/src/config/core-schema.ts +309 -0
- package/src/config/defaults.ts +28 -3
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +158 -1133
- package/src/config/skill-state.ts +1 -1
- package/src/config/skills-schema.ts +32 -0
- package/src/config/skills.ts +35 -24
- package/src/config/system-prompt.ts +131 -56
- package/src/config/templates/IDENTITY.md +2 -2
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +1 -0
- package/src/config/user-reference.ts +4 -9
- package/src/config/vellum-skills/catalog.json +6 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
- package/src/context/window-manager.ts +27 -7
- package/src/daemon/approval-generators.ts +186 -0
- package/src/daemon/approved-devices-store.ts +140 -0
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/classifier.ts +35 -32
- package/src/daemon/config-watcher.ts +1 -1
- package/src/daemon/daemon-control.ts +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -1689
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +180 -0
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +11 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +56 -5
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/handlers/twitter-auth.ts +2 -0
- package/src/daemon/handlers/work-items.ts +17 -9
- package/src/daemon/handlers/workspace-files.ts +4 -3
- package/src/daemon/install-cli-launchers.ts +113 -0
- package/src/daemon/ipc-contract/apps.ts +356 -0
- package/src/daemon/ipc-contract/browser.ts +74 -0
- package/src/daemon/ipc-contract/computer-use.ts +151 -0
- package/src/daemon/ipc-contract/diagnostics.ts +56 -0
- package/src/daemon/ipc-contract/documents.ts +74 -0
- package/src/daemon/ipc-contract/inbox.ts +209 -0
- package/src/daemon/ipc-contract/integrations.ts +284 -0
- package/src/daemon/ipc-contract/memory.ts +48 -0
- package/src/daemon/ipc-contract/messages.ts +211 -0
- package/src/daemon/ipc-contract/pairing.ts +45 -0
- package/src/daemon/ipc-contract/parental-control.ts +95 -0
- package/src/daemon/ipc-contract/schedules.ts +97 -0
- package/src/daemon/ipc-contract/sessions.ts +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +70 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +229 -2426
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -377
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +68 -3
- package/src/daemon/server.ts +66 -46
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +117 -275
- package/src/daemon/session-dynamic-profile.ts +1 -1
- package/src/daemon/session-history.ts +1 -1
- package/src/daemon/session-media-retry.ts +1 -1
- package/src/daemon/session-messaging.ts +37 -2
- package/src/daemon/session-notifiers.ts +5 -25
- package/src/daemon/session-process.ts +99 -59
- package/src/daemon/session-queue-manager.ts +96 -4
- package/src/daemon/session-runtime-assembly.ts +199 -10
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +30 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +35 -2
- package/src/daemon/shutdown-handlers.ts +122 -0
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/watch-handler.ts +36 -33
- package/src/doordash/cart-queries.ts +787 -0
- package/src/doordash/client.ts +144 -127
- package/src/doordash/order-queries.ts +85 -0
- package/src/doordash/queries.ts +10 -1308
- package/src/doordash/search-queries.ts +203 -0
- package/src/doordash/session.ts +3 -2
- package/src/doordash/store-queries.ts +246 -0
- package/src/doordash/types.ts +367 -0
- package/src/email/providers/agentmail.ts +2 -1
- package/src/email/providers/index.ts +3 -2
- package/src/email/service.ts +3 -2
- package/src/errors.ts +43 -0
- package/src/home-base/prebuilt/seed.ts +1 -1
- package/src/hooks/cli.ts +6 -5
- package/src/hooks/config.ts +6 -8
- package/src/hooks/discovery.ts +6 -5
- package/src/hooks/manager.ts +4 -3
- package/src/hooks/runner.ts +2 -2
- package/src/hooks/templates.ts +5 -5
- package/src/inbound/public-ingress-urls.ts +6 -4
- package/src/index.ts +4 -2
- package/src/influencer/client.ts +1104 -0
- package/src/instrument.ts +4 -3
- package/src/logfire.ts +4 -3
- package/src/memory/admin.ts +25 -35
- package/src/memory/attachments-store.ts +4 -7
- package/src/memory/channel-delivery-store.ts +30 -1
- package/src/memory/channel-guardian-store.ts +202 -2
- package/src/memory/clarification-resolver.ts +37 -33
- package/src/memory/conflict-store.ts +67 -61
- package/src/memory/contradiction-checker.ts +141 -117
- package/src/memory/conversation-store.ts +335 -51
- package/src/memory/db-connection.ts +27 -4
- package/src/memory/db-init.ts +265 -4
- package/src/memory/db.ts +14 -1
- package/src/memory/embedding-backend.ts +27 -5
- package/src/memory/embedding-ollama.ts +2 -1
- package/src/memory/entity-extractor.ts +38 -35
- package/src/memory/guardian-action-store.ts +430 -0
- package/src/memory/inbox-escalation-projection.ts +59 -0
- package/src/memory/inbox-thread-store.ts +218 -0
- package/src/memory/ingress-invite-store.ts +338 -0
- package/src/memory/ingress-member-store.ts +350 -0
- package/src/memory/items-extractor.ts +91 -97
- package/src/memory/job-handlers/index-maintenance.ts +3 -3
- package/src/memory/job-handlers/media-processing.ts +69 -0
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +8 -10
- package/src/memory/jobs-worker.ts +55 -36
- package/src/memory/media-store.ts +759 -0
- package/src/memory/migrations/001-job-deferrals.ts +45 -0
- package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
- package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
- package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
- package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
- package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
- package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
- package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +165 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +228 -7
- package/src/memory/search/entity.ts +205 -31
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +27 -23
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/search/types.ts +24 -0
- package/src/memory/shared-app-links-store.ts +4 -5
- package/src/memory/validation.ts +19 -0
- package/src/messaging/draft-store.ts +5 -6
- package/src/messaging/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +201 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
- package/src/messaging/providers/whatsapp/adapter.ts +136 -0
- package/src/messaging/providers/whatsapp/client.ts +67 -0
- package/src/messaging/style-analyzer.ts +5 -4
- package/src/messaging/thread-summarizer.ts +61 -69
- package/src/messaging/triage-engine.ts +62 -71
- package/src/migrations/config-merge.ts +53 -0
- package/src/migrations/data-layout.ts +68 -0
- package/src/migrations/data-merge.ts +33 -0
- package/src/migrations/hooks-merge.ts +90 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/log.ts +23 -0
- package/src/migrations/skills-merge.ts +33 -0
- package/src/migrations/workspace-layout.ts +79 -0
- package/src/permissions/checker.ts +133 -11
- package/src/permissions/prompter.ts +14 -0
- package/src/permissions/shell-identity.ts +31 -1
- package/src/permissions/trust-store.ts +21 -1
- package/src/providers/anthropic/client.ts +4 -4
- package/src/providers/failover.ts +2 -2
- package/src/providers/model-intents.ts +70 -0
- package/src/providers/ollama/client.ts +2 -1
- package/src/providers/provider-send-message.ts +176 -0
- package/src/providers/registry.ts +71 -30
- package/src/providers/retry.ts +35 -1
- package/src/providers/types.ts +12 -1
- package/src/runtime/approval-conversation-turn.ts +97 -0
- package/src/runtime/approval-message-composer.ts +253 -0
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +11 -24
- package/src/runtime/channel-guardian-service.ts +88 -21
- package/src/runtime/channel-readiness-service.ts +418 -0
- package/src/runtime/channel-readiness-types.ts +35 -0
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +275 -717
- package/src/runtime/http-types.ts +59 -3
- package/src/runtime/middleware/auth.ts +116 -0
- package/src/runtime/middleware/error-handler.ts +33 -0
- package/src/runtime/middleware/twilio-validation.ts +127 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +51 -7
- package/src/runtime/routes/channel-delivery-routes.ts +170 -0
- package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
- package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
- package/src/runtime/routes/channel-route-shared.ts +144 -0
- package/src/runtime/routes/channel-routes.ts +32 -1588
- package/src/runtime/routes/conversation-routes.ts +50 -7
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +126 -0
- package/src/runtime/routes/pairing-routes.ts +143 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +86 -35
- package/src/schedule/schedule-store.ts +36 -32
- package/src/schedule/scheduler.ts +3 -3
- package/src/security/encrypted-store.ts +5 -7
- package/src/security/oauth2.ts +45 -15
- package/src/security/parental-control-store.ts +183 -0
- package/src/security/secret-allowlist.ts +4 -3
- package/src/security/secret-scanner.ts +5 -5
- package/src/security/secure-keys.ts +1 -1
- package/src/security/token-manager.ts +3 -2
- package/src/services/vercel-deploy.ts +6 -2
- package/src/skills/tool-manifest.ts +3 -3
- package/src/skills/vellum-catalog-remote.ts +75 -16
- package/src/slack/slack-webhook.ts +2 -1
- package/src/swarm/orchestrator.ts +92 -1
- package/src/swarm/router-planner.ts +6 -9
- package/src/swarm/worker-prompts.ts +9 -12
- package/src/tasks/task-compiler.ts +19 -28
- package/src/tasks/task-runner.ts +1 -1
- package/src/tools/assets/materialize.ts +2 -2
- package/src/tools/assets/search.ts +15 -14
- package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
- package/src/tools/browser/auto-navigate.ts +1 -0
- package/src/tools/browser/browser-execution.ts +10 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -0
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/broker.ts +11 -2
- package/src/tools/credentials/metadata-store.ts +18 -14
- package/src/tools/credentials/post-connect-hooks.ts +61 -0
- package/src/tools/credentials/vault.ts +49 -23
- package/src/tools/execution-target.ts +11 -1
- package/src/tools/executor.ts +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +8 -4
- package/src/tools/reminder/reminder-store.ts +14 -15
- package/src/tools/schedule/create.ts +1 -0
- package/src/tools/schedule/list.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
- package/src/tools/skills/skill-script-runner.ts +24 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/tasks/work-item-enqueue.ts +2 -2
- package/src/tools/terminal/evaluate-typescript.ts +21 -12
- package/src/tools/terminal/parser.ts +50 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- package/src/twitter/router.ts +1 -1
- package/src/twitter/session.ts +4 -3
- package/src/util/clipboard.ts +1 -1
- package/src/util/errors.ts +65 -8
- package/src/util/fs.ts +40 -0
- package/src/util/json.ts +10 -0
- package/src/util/log-redact.ts +189 -0
- package/src/util/logger.ts +19 -17
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +105 -363
- package/src/util/pricing.ts +1 -1
- package/src/util/promise-guard.ts +1 -1
- package/src/util/retry.ts +19 -0
- package/src/util/row-mapper.ts +79 -0
- package/src/util/silently.ts +21 -0
- package/src/watcher/engine.ts +5 -1
- package/src/watcher/provider-types.ts +20 -0
- package/src/watcher/providers/github.ts +156 -0
- package/src/watcher/providers/gmail.ts +1 -0
- package/src/watcher/providers/google-calendar.ts +1 -0
- package/src/watcher/providers/linear.ts +460 -0
- package/src/watcher/providers/slack.ts +1 -0
- package/src/work-items/work-item-runner.ts +1 -1
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/provider-commit-message-generator.ts +51 -22
- package/src/__tests__/call-bridge.test.ts +0 -517
- package/src/__tests__/session-process-bridge.test.ts +0 -244
- package/src/calls/call-bridge.ts +0 -168
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getConfig } from '../config/loader.js';
|
|
1
|
+
import { getConfiguredProvider, createTimeout, extractToolUse, userMessage } from '../providers/provider-send-message.js';
|
|
3
2
|
import { getLogger } from '../util/logger.js';
|
|
4
3
|
import { truncate } from '../util/truncate.js';
|
|
5
4
|
import type { ThreadMessage, ThreadSummary } from './types.js';
|
|
6
5
|
|
|
7
6
|
const log = getLogger('thread-summarizer');
|
|
8
7
|
|
|
9
|
-
const
|
|
8
|
+
const SUMMARIZATION_MODEL_INTENT = 'latency-optimized' as const;
|
|
10
9
|
const SUMMARIZATION_TIMEOUT_MS = 20_000;
|
|
11
10
|
const DEFAULT_MAX_TOKENS = 4000;
|
|
12
11
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -189,78 +188,71 @@ async function summarizeWithLLM(
|
|
|
189
188
|
messages: ThreadMessage[],
|
|
190
189
|
maxTokens: number,
|
|
191
190
|
): Promise<ThreadSummary> {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
log.warn('No Anthropic API key available for thread summarization, returning basic summary');
|
|
191
|
+
const provider = getConfiguredProvider();
|
|
192
|
+
if (!provider) {
|
|
193
|
+
log.warn('Configured provider unavailable for thread summarization, returning basic summary');
|
|
196
194
|
return buildFallbackSummary(messages);
|
|
197
195
|
}
|
|
198
196
|
|
|
199
|
-
const
|
|
200
|
-
const transcript = formatTranscript(
|
|
197
|
+
const truncatedMsgs = truncateMessages(messages, maxTokens);
|
|
198
|
+
const transcript = formatTranscript(truncatedMsgs);
|
|
201
199
|
|
|
202
200
|
try {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const response = await Promise.race([
|
|
221
|
-
apiCall.finally(() => clearTimeout(timer)),
|
|
222
|
-
new Promise<never>((_, reject) => {
|
|
223
|
-
timer = setTimeout(() => {
|
|
224
|
-
abortController.abort();
|
|
225
|
-
reject(new Error('Thread summarization LLM timeout'));
|
|
226
|
-
}, SUMMARIZATION_TIMEOUT_MS);
|
|
227
|
-
}),
|
|
228
|
-
]);
|
|
229
|
-
|
|
230
|
-
const toolBlock = response.content.find((b) => b.type === 'tool_use');
|
|
231
|
-
if (!toolBlock || toolBlock.type !== 'tool_use') {
|
|
232
|
-
log.warn('No tool_use block in summarization response, returning fallback');
|
|
233
|
-
return buildFallbackSummary(messages);
|
|
234
|
-
}
|
|
201
|
+
const { signal, cleanup } = createTimeout(SUMMARIZATION_TIMEOUT_MS);
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const response = await provider.sendMessage(
|
|
205
|
+
[userMessage(`Summarize this conversation thread (${messages.length} messages):\n\n${transcript}`)],
|
|
206
|
+
[STORE_SUMMARY_TOOL],
|
|
207
|
+
SYSTEM_PROMPT,
|
|
208
|
+
{
|
|
209
|
+
config: {
|
|
210
|
+
modelIntent: SUMMARIZATION_MODEL_INTENT,
|
|
211
|
+
max_tokens: 1024,
|
|
212
|
+
tool_choice: { type: 'tool' as const, name: 'store_thread_summary' },
|
|
213
|
+
},
|
|
214
|
+
signal,
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
cleanup();
|
|
235
218
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
? input.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
219
|
+
const toolBlock = extractToolUse(response);
|
|
220
|
+
if (!toolBlock) {
|
|
221
|
+
log.warn('No tool_use block in summarization response, returning fallback');
|
|
222
|
+
return buildFallbackSummary(messages);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const input = toolBlock.input as {
|
|
226
|
+
summary?: string;
|
|
227
|
+
participants?: Array<{ name: string; role?: string }>;
|
|
228
|
+
openQuestions?: string[];
|
|
229
|
+
lastAction?: string;
|
|
230
|
+
sentiment?: string;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const validSentiments = new Set(['positive', 'neutral', 'negative', 'mixed']);
|
|
234
|
+
const sentiment = validSentiments.has(input.sentiment ?? '')
|
|
235
|
+
? (input.sentiment as ThreadSummary['sentiment'])
|
|
236
|
+
: 'neutral';
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
summary: truncate(String(input.summary ?? ''), 2000, ''),
|
|
240
|
+
participants: Array.isArray(input.participants)
|
|
241
|
+
? input.participants.map((p) => ({
|
|
242
|
+
name: String(p.name),
|
|
243
|
+
...(p.role ? { role: String(p.role) } : {}),
|
|
244
|
+
}))
|
|
245
|
+
: extractParticipants(messages),
|
|
246
|
+
openQuestions: Array.isArray(input.openQuestions)
|
|
247
|
+
? input.openQuestions.map((q) => String(q))
|
|
248
|
+
: [],
|
|
249
|
+
lastAction: truncate(String(input.lastAction ?? ''), 500, ''),
|
|
250
|
+
sentiment,
|
|
251
|
+
messageCount: messages.length,
|
|
252
|
+
};
|
|
253
|
+
} finally {
|
|
254
|
+
cleanup();
|
|
255
|
+
}
|
|
264
256
|
} catch (err) {
|
|
265
257
|
const message = err instanceof Error ? err.message : String(err);
|
|
266
258
|
log.warn({ err: message }, 'Thread summarization LLM call failed, returning fallback');
|
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
* Channel-agnostic message triage engine.
|
|
3
3
|
*
|
|
4
4
|
* Classifies an inbound message by combining sender context from the
|
|
5
|
-
* contact graph, matching action playbooks, and an LLM call
|
|
5
|
+
* contact graph, matching action playbooks, and an LLM call
|
|
6
6
|
* for final classification. Results are persisted to the triageResults
|
|
7
7
|
* table for accuracy review.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { getConfiguredProvider, createTimeout, extractToolUse, userMessage } from '../providers/provider-send-message.js';
|
|
11
11
|
import { truncate } from '../util/truncate.js';
|
|
12
12
|
import { v4 as uuid } from 'uuid';
|
|
13
13
|
import { and, eq, isNull, desc } from 'drizzle-orm';
|
|
14
|
-
import { getConfig } from '../config/loader.js';
|
|
15
14
|
import { getLogger } from '../util/logger.js';
|
|
16
15
|
import { getDb } from '../memory/db.js';
|
|
17
16
|
import { memoryItems, triageResults } from '../memory/schema.js';
|
|
@@ -24,7 +23,7 @@ import { DEFAULT_TRIAGE_CATEGORIES } from './types.js';
|
|
|
24
23
|
|
|
25
24
|
const log = getLogger('triage-engine');
|
|
26
25
|
|
|
27
|
-
const
|
|
26
|
+
const TRIAGE_MODEL_INTENT = 'latency-optimized' as const;
|
|
28
27
|
const TRIAGE_CLASSIFICATION_TIMEOUT_MS = 15_000;
|
|
29
28
|
|
|
30
29
|
// ── Playbook fetching ────────────────────────────────────────────────
|
|
@@ -189,10 +188,9 @@ export async function triageMessage(
|
|
|
189
188
|
const playbookMatches = fetchMatchingPlaybooks(message.channel, scopeId);
|
|
190
189
|
|
|
191
190
|
// Step 3: Classify with LLM
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
log.warn('No Anthropic API key available for triage classification, returning fallback');
|
|
191
|
+
const provider = getConfiguredProvider();
|
|
192
|
+
if (!provider) {
|
|
193
|
+
log.warn('Configured provider unavailable for triage classification, returning fallback');
|
|
196
194
|
const result = buildFallbackResult();
|
|
197
195
|
persistTriageResult(message, result, playbookMatches);
|
|
198
196
|
return result;
|
|
@@ -200,7 +198,7 @@ export async function triageMessage(
|
|
|
200
198
|
|
|
201
199
|
let result: TriageResult;
|
|
202
200
|
try {
|
|
203
|
-
result = await classifyWithLLM(message, contact, playbookMatches
|
|
201
|
+
result = await classifyWithLLM(message, contact, playbookMatches);
|
|
204
202
|
} catch (err) {
|
|
205
203
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
206
204
|
log.warn({ err: errMsg }, 'Triage LLM call failed, returning fallback');
|
|
@@ -217,77 +215,70 @@ async function classifyWithLLM(
|
|
|
217
215
|
message: InboundMessage,
|
|
218
216
|
contact: ContactWithChannels | null,
|
|
219
217
|
playbookMatches: PlaybookMatch[],
|
|
220
|
-
apiKey: string,
|
|
221
218
|
): Promise<TriageResult> {
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
let timer: ReturnType<typeof setTimeout>;
|
|
219
|
+
const provider = getConfiguredProvider()!;
|
|
220
|
+
const { signal, cleanup } = createTimeout(TRIAGE_CLASSIFICATION_TIMEOUT_MS);
|
|
225
221
|
|
|
226
222
|
const systemPrompt = buildSystemPrompt(contact, playbookMatches);
|
|
227
223
|
const userPrompt = buildUserPrompt(message);
|
|
228
224
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
apiCall.finally(() => clearTimeout(timer)),
|
|
245
|
-
new Promise<never>((_, reject) => {
|
|
246
|
-
timer = setTimeout(() => {
|
|
247
|
-
abortController.abort();
|
|
248
|
-
reject(new Error('Triage classification LLM timeout'));
|
|
249
|
-
}, TRIAGE_CLASSIFICATION_TIMEOUT_MS);
|
|
250
|
-
}),
|
|
251
|
-
]);
|
|
252
|
-
|
|
253
|
-
const toolBlock = response.content.find((b) => b.type === 'tool_use');
|
|
254
|
-
if (!toolBlock || toolBlock.type !== 'tool_use') {
|
|
255
|
-
log.warn('No tool_use block in triage response, returning fallback');
|
|
256
|
-
return buildFallbackResult();
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const input = toolBlock.input as {
|
|
260
|
-
category?: string;
|
|
261
|
-
confidence?: number;
|
|
262
|
-
suggestedAction?: string;
|
|
263
|
-
matchedPlaybookTriggers?: string[];
|
|
264
|
-
};
|
|
225
|
+
try {
|
|
226
|
+
const response = await provider.sendMessage(
|
|
227
|
+
[userMessage(userPrompt)],
|
|
228
|
+
[STORE_TRIAGE_TOOL],
|
|
229
|
+
systemPrompt,
|
|
230
|
+
{
|
|
231
|
+
config: {
|
|
232
|
+
modelIntent: TRIAGE_MODEL_INTENT,
|
|
233
|
+
max_tokens: 1024,
|
|
234
|
+
tool_choice: { type: 'tool' as const, name: 'store_triage_result' },
|
|
235
|
+
},
|
|
236
|
+
signal,
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
cleanup();
|
|
265
240
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
241
|
+
const toolBlock = extractToolUse(response);
|
|
242
|
+
if (!toolBlock) {
|
|
243
|
+
log.warn('No tool_use block in triage response, returning fallback');
|
|
244
|
+
return buildFallbackResult();
|
|
245
|
+
}
|
|
269
246
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
autonomyLevel: playbook.autonomyLevel,
|
|
277
|
-
}));
|
|
247
|
+
const input = toolBlock.input as {
|
|
248
|
+
category?: string;
|
|
249
|
+
confidence?: number;
|
|
250
|
+
suggestedAction?: string;
|
|
251
|
+
matchedPlaybookTriggers?: string[];
|
|
252
|
+
};
|
|
278
253
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
254
|
+
const matchedTriggers = new Set(
|
|
255
|
+
Array.isArray(input.matchedPlaybookTriggers) ? input.matchedPlaybookTriggers : [],
|
|
256
|
+
);
|
|
282
257
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
258
|
+
// Map LLM-identified triggers back to the full playbook data
|
|
259
|
+
const matchedPlaybooks = playbookMatches
|
|
260
|
+
.filter(({ playbook }) => matchedTriggers.has(playbook.trigger))
|
|
261
|
+
.map(({ playbook }) => ({
|
|
262
|
+
trigger: playbook.trigger,
|
|
263
|
+
action: playbook.action,
|
|
264
|
+
autonomyLevel: playbook.autonomyLevel,
|
|
265
|
+
}));
|
|
266
|
+
|
|
267
|
+
const confidence = typeof input.confidence === 'number'
|
|
268
|
+
? Math.max(0, Math.min(1, input.confidence))
|
|
269
|
+
: 0.5;
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
category: typeof input.category === 'string' ? input.category : 'needs_response',
|
|
273
|
+
confidence,
|
|
274
|
+
suggestedAction: typeof input.suggestedAction === 'string'
|
|
275
|
+
? truncate(input.suggestedAction, 500, '')
|
|
276
|
+
: 'Review manually',
|
|
277
|
+
matchedPlaybooks,
|
|
278
|
+
};
|
|
279
|
+
} finally {
|
|
280
|
+
cleanup();
|
|
281
|
+
}
|
|
291
282
|
}
|
|
292
283
|
|
|
293
284
|
// ── Persistence ─────────────────────────────────────────────────────
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { isPlainObject } from '../util/object.js';
|
|
3
|
+
import { migrationLog } from './log.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* When migratePath skips config.json because the workspace copy already
|
|
7
|
+
* exists, the legacy root config may still contain keys (e.g. slackWebhookUrl)
|
|
8
|
+
* that were never written to the workspace config. This merges any missing
|
|
9
|
+
* top-level keys from the legacy file into the workspace file so they are
|
|
10
|
+
* not silently lost during upgrade.
|
|
11
|
+
*/
|
|
12
|
+
export function mergeSkippedConfigKeys(legacyPath: string, workspacePath: string): void {
|
|
13
|
+
if (!existsSync(legacyPath) || !existsSync(workspacePath)) return;
|
|
14
|
+
|
|
15
|
+
let legacy: Record<string, unknown>;
|
|
16
|
+
let workspace: Record<string, unknown>;
|
|
17
|
+
try {
|
|
18
|
+
const legacyRaw = JSON.parse(readFileSync(legacyPath, 'utf-8'));
|
|
19
|
+
const workspaceRaw = JSON.parse(readFileSync(workspacePath, 'utf-8'));
|
|
20
|
+
if (!isPlainObject(legacyRaw) || !isPlainObject(workspaceRaw)) return;
|
|
21
|
+
legacy = legacyRaw;
|
|
22
|
+
workspace = workspaceRaw;
|
|
23
|
+
} catch {
|
|
24
|
+
return; // malformed JSON — skip silently
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const merged: string[] = [];
|
|
28
|
+
for (const key of Object.keys(legacy)) {
|
|
29
|
+
if (!(key in workspace)) {
|
|
30
|
+
workspace[key] = legacy[key];
|
|
31
|
+
merged.push(key);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (merged.length > 0) {
|
|
36
|
+
try {
|
|
37
|
+
writeFileSync(workspacePath, JSON.stringify(workspace, null, 2) + '\n');
|
|
38
|
+
// Remove merged keys from legacy config so they are not resurrected
|
|
39
|
+
// if a user later deletes them from the workspace config.
|
|
40
|
+
for (const key of merged) {
|
|
41
|
+
delete legacy[key];
|
|
42
|
+
}
|
|
43
|
+
if (Object.keys(legacy).length === 0) {
|
|
44
|
+
unlinkSync(legacyPath);
|
|
45
|
+
} else {
|
|
46
|
+
writeFileSync(legacyPath, JSON.stringify(legacy, null, 2) + '\n');
|
|
47
|
+
}
|
|
48
|
+
migrationLog('info', 'Merged legacy config keys into workspace config', { keys: merged });
|
|
49
|
+
} catch (err) {
|
|
50
|
+
migrationLog('warn', 'Failed to merge legacy config keys', { err: String(err), keys: merged });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, renameSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { getRootDir } from '../util/platform.js';
|
|
4
|
+
import { migrationLog } from './log.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Migrate files from the old flat ~/.vellum layout to the new structured
|
|
8
|
+
* layout with data/ and protected/ subdirectories.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent: skips items that have already been migrated.
|
|
11
|
+
* Uses renameSync for atomic moves (same filesystem).
|
|
12
|
+
*/
|
|
13
|
+
export function migrateToDataLayout(): void {
|
|
14
|
+
const root = getRootDir();
|
|
15
|
+
const data = join(root, 'data');
|
|
16
|
+
|
|
17
|
+
if (!existsSync(root)) return;
|
|
18
|
+
|
|
19
|
+
function migrateItem(oldPath: string, newPath: string): void {
|
|
20
|
+
if (!existsSync(oldPath)) return;
|
|
21
|
+
if (existsSync(newPath)) return;
|
|
22
|
+
try {
|
|
23
|
+
const newDir = dirname(newPath);
|
|
24
|
+
if (!existsSync(newDir)) {
|
|
25
|
+
mkdirSync(newDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
renameSync(oldPath, newPath);
|
|
28
|
+
migrationLog('info', 'Migrated path', { from: oldPath, to: newPath });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
migrationLog('warn', 'Failed to migrate path', { err: String(err), from: oldPath, to: newPath });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// DB: ~/.vellum/data/assistant.db → ~/.vellum/data/db/assistant.db
|
|
35
|
+
migrateItem(join(data, 'assistant.db'), join(data, 'db', 'assistant.db'));
|
|
36
|
+
migrateItem(join(data, 'assistant.db-wal'), join(data, 'db', 'assistant.db-wal'));
|
|
37
|
+
migrateItem(join(data, 'assistant.db-shm'), join(data, 'db', 'assistant.db-shm'));
|
|
38
|
+
|
|
39
|
+
// Qdrant PID: ~/.vellum/qdrant.pid → ~/.vellum/data/qdrant/qdrant.pid
|
|
40
|
+
migrateItem(join(root, 'qdrant.pid'), join(data, 'qdrant', 'qdrant.pid'));
|
|
41
|
+
|
|
42
|
+
// Qdrant binary: ~/.vellum/bin/ → ~/.vellum/data/qdrant/bin/
|
|
43
|
+
migrateItem(join(root, 'bin'), join(data, 'qdrant', 'bin'));
|
|
44
|
+
|
|
45
|
+
// Logs: ~/.vellum/logs/ → ~/.vellum/data/logs/
|
|
46
|
+
migrateItem(join(root, 'logs'), join(data, 'logs'));
|
|
47
|
+
|
|
48
|
+
// Memory: ~/.vellum/memory/ → ~/.vellum/data/memory/
|
|
49
|
+
migrateItem(join(root, 'memory'), join(data, 'memory'));
|
|
50
|
+
|
|
51
|
+
// Apps: ~/.vellum/apps/ → ~/.vellum/data/apps/
|
|
52
|
+
migrateItem(join(root, 'apps'), join(data, 'apps'));
|
|
53
|
+
|
|
54
|
+
// Browser auth: ~/.vellum/browser-auth/ → ~/.vellum/data/browser-auth/
|
|
55
|
+
migrateItem(join(root, 'browser-auth'), join(data, 'browser-auth'));
|
|
56
|
+
|
|
57
|
+
// Browser profile: ~/.vellum/browser-profile/ → ~/.vellum/data/browser-profile/
|
|
58
|
+
migrateItem(join(root, 'browser-profile'), join(data, 'browser-profile'));
|
|
59
|
+
|
|
60
|
+
// History: ~/.vellum/history → ~/.vellum/data/history
|
|
61
|
+
migrateItem(join(root, 'history'), join(data, 'history'));
|
|
62
|
+
|
|
63
|
+
// Protected files: ~/.vellum/X → ~/.vellum/protected/X
|
|
64
|
+
const protectedDir = join(root, 'protected');
|
|
65
|
+
migrateItem(join(root, 'trust.json'), join(protectedDir, 'trust.json'));
|
|
66
|
+
migrateItem(join(root, 'keys.enc'), join(protectedDir, 'keys.enc'));
|
|
67
|
+
migrateItem(join(root, 'secret-allowlist.json'), join(protectedDir, 'secret-allowlist.json'));
|
|
68
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { existsSync, readdirSync, renameSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { migrationLog } from './log.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* When migratePath skips the data directory because workspace/data already
|
|
7
|
+
* exists (e.g. the user's project had a data/ folder that was extracted from
|
|
8
|
+
* sandbox/fs), the legacy data directory may still contain internal state
|
|
9
|
+
* subdirectories (db/, logs/, sandbox/, etc.) that need to be preserved.
|
|
10
|
+
* This merges any missing entries from the legacy data path into workspace/data.
|
|
11
|
+
*/
|
|
12
|
+
export function mergeLegacyDataEntries(legacyDir: string, workspaceDir: string): void {
|
|
13
|
+
if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
|
|
14
|
+
|
|
15
|
+
let entries: import('node:fs').Dirent[];
|
|
16
|
+
try {
|
|
17
|
+
entries = readdirSync(legacyDir, { withFileTypes: true });
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const src = join(legacyDir, entry.name);
|
|
24
|
+
const dest = join(workspaceDir, entry.name);
|
|
25
|
+
if (existsSync(dest)) continue; // already present in workspace
|
|
26
|
+
try {
|
|
27
|
+
renameSync(src, dest);
|
|
28
|
+
migrationLog('info', 'Merged legacy data entry into workspace', { from: src, to: dest });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
migrationLog('warn', 'Failed to merge legacy data entry', { err: String(err), from: src, to: dest });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { isPlainObject } from '../util/object.js';
|
|
4
|
+
import { migrationLog } from './log.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Merge missing hook entries from a legacy hooks/config.json into the
|
|
8
|
+
* workspace hooks/config.json. Only adds hooks that don't already exist
|
|
9
|
+
* in the workspace config so user changes are never overwritten.
|
|
10
|
+
*/
|
|
11
|
+
export function mergeHooksConfig(legacyPath: string, workspacePath: string): void {
|
|
12
|
+
let legacy: Record<string, unknown>;
|
|
13
|
+
let workspace: Record<string, unknown>;
|
|
14
|
+
try {
|
|
15
|
+
const legacyRaw = JSON.parse(readFileSync(legacyPath, 'utf-8'));
|
|
16
|
+
const workspaceRaw = JSON.parse(readFileSync(workspacePath, 'utf-8'));
|
|
17
|
+
if (!isPlainObject(legacyRaw) || !isPlainObject(workspaceRaw)) return;
|
|
18
|
+
legacy = legacyRaw;
|
|
19
|
+
workspace = workspaceRaw;
|
|
20
|
+
} catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const legacyHooks = legacy.hooks;
|
|
25
|
+
const wsHooks = workspace.hooks;
|
|
26
|
+
if (!isPlainObject(legacyHooks) || !isPlainObject(wsHooks)) return;
|
|
27
|
+
|
|
28
|
+
const merged: string[] = [];
|
|
29
|
+
for (const hookName of Object.keys(legacyHooks)) {
|
|
30
|
+
if (!(hookName in wsHooks)) {
|
|
31
|
+
wsHooks[hookName] = legacyHooks[hookName];
|
|
32
|
+
merged.push(hookName);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (merged.length > 0) {
|
|
37
|
+
try {
|
|
38
|
+
writeFileSync(workspacePath, JSON.stringify(workspace, null, 2) + '\n');
|
|
39
|
+
// Remove merged hooks from legacy config to prevent resurrection
|
|
40
|
+
for (const hookName of merged) {
|
|
41
|
+
delete legacyHooks[hookName];
|
|
42
|
+
}
|
|
43
|
+
if (Object.keys(legacyHooks).length === 0) {
|
|
44
|
+
unlinkSync(legacyPath);
|
|
45
|
+
} else {
|
|
46
|
+
writeFileSync(legacyPath, JSON.stringify(legacy, null, 2) + '\n');
|
|
47
|
+
}
|
|
48
|
+
migrationLog('info', 'Merged legacy hooks config entries into workspace', { hooks: merged });
|
|
49
|
+
} catch (err) {
|
|
50
|
+
migrationLog('warn', 'Failed to merge legacy hooks config', { err: String(err), hooks: merged });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* When migratePath skips the hooks directory because the workspace copy
|
|
57
|
+
* already exists (e.g. pre-created by ensureDataDir), the legacy hooks
|
|
58
|
+
* directory may still contain individual hook files/subdirectories that
|
|
59
|
+
* were never moved. This merges any missing entries from the legacy
|
|
60
|
+
* path into the workspace hooks path so they are not silently lost.
|
|
61
|
+
*/
|
|
62
|
+
export function mergeLegacyHooks(legacyDir: string, workspaceDir: string): void {
|
|
63
|
+
if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
|
|
64
|
+
|
|
65
|
+
let entries: import('node:fs').Dirent[];
|
|
66
|
+
try {
|
|
67
|
+
entries = readdirSync(legacyDir, { withFileTypes: true });
|
|
68
|
+
} catch {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
const src = join(legacyDir, entry.name);
|
|
74
|
+
const dest = join(workspaceDir, entry.name);
|
|
75
|
+
if (existsSync(dest)) {
|
|
76
|
+
// config.json needs a merge rather than a skip — the legacy file may
|
|
77
|
+
// contain hook enabled/settings entries that the workspace copy lacks.
|
|
78
|
+
if (entry.name === 'config.json') {
|
|
79
|
+
mergeHooksConfig(src, dest);
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
renameSync(src, dest);
|
|
85
|
+
migrationLog('info', 'Merged legacy hook into workspace', { from: src, to: dest });
|
|
86
|
+
} catch (err) {
|
|
87
|
+
migrationLog('warn', 'Failed to merge legacy hook', { err: String(err), from: src, to: dest });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { migrateToDataLayout } from './data-layout.js';
|
|
2
|
+
export { migrateToWorkspaceLayout, migratePath } from './workspace-layout.js';
|
|
3
|
+
export { mergeSkippedConfigKeys } from './config-merge.js';
|
|
4
|
+
export { mergeLegacyHooks, mergeHooksConfig } from './hooks-merge.js';
|
|
5
|
+
export { mergeLegacySkills } from './skills-merge.js';
|
|
6
|
+
export { mergeLegacyDataEntries } from './data-merge.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
import { logSerializers } from '../util/log-redact.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Standalone pino instance for migration code. This must NOT use getLogger()
|
|
6
|
+
* because that triggers ensureDataDir(), which pre-creates workspace
|
|
7
|
+
* destination directories and causes migration moves to no-op.
|
|
8
|
+
*
|
|
9
|
+
* Writes to stderr only — no log files that might not exist yet.
|
|
10
|
+
*/
|
|
11
|
+
const migrationLogger: pino.Logger = pino(
|
|
12
|
+
{ name: 'migration', level: 'info', serializers: logSerializers },
|
|
13
|
+
pino.destination(2),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export function migrationLog(level: 'info' | 'warn' | 'debug', msg: string, data?: Record<string, unknown>): void {
|
|
17
|
+
if (level === 'debug') return;
|
|
18
|
+
if (data) {
|
|
19
|
+
migrationLogger[level](data, msg);
|
|
20
|
+
} else {
|
|
21
|
+
migrationLogger[level](msg);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { existsSync, readdirSync, renameSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { migrationLog } from './log.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* When migratePath skips the skills directory because the workspace copy
|
|
7
|
+
* already exists (e.g. pre-created by ensureDataDir), the legacy skills
|
|
8
|
+
* directory may still contain individual skill subdirectories that were
|
|
9
|
+
* never moved. This merges any missing skill subdirectories from the
|
|
10
|
+
* legacy path into the workspace skills path so they are not stranded.
|
|
11
|
+
*/
|
|
12
|
+
export function mergeLegacySkills(legacyDir: string, workspaceDir: string): void {
|
|
13
|
+
if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
|
|
14
|
+
|
|
15
|
+
let entries: import('node:fs').Dirent[];
|
|
16
|
+
try {
|
|
17
|
+
entries = readdirSync(legacyDir, { withFileTypes: true });
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const src = join(legacyDir, entry.name);
|
|
24
|
+
const dest = join(workspaceDir, entry.name);
|
|
25
|
+
if (existsSync(dest)) continue; // already present in workspace
|
|
26
|
+
try {
|
|
27
|
+
renameSync(src, dest);
|
|
28
|
+
migrationLog('info', 'Merged legacy skill into workspace', { from: src, to: dest });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
migrationLog('warn', 'Failed to merge legacy skill', { err: String(err), from: src, to: dest });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|