@vellumai/assistant 0.3.5 → 0.3.7
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/README.md +51 -0
- 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 +18 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-orchestrator.test.ts +752 -271
- 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 +5 -0
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1260 -85
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +4 -65
- package/src/__tests__/channel-guardian.test.ts +556 -0
- package/src/__tests__/channel-readiness-service.test.ts +74 -7
- 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 +12 -7
- 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 +6 -2
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -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 +126 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +228 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -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 +2 -0
- package/src/__tests__/run-orchestrator.test.ts +20 -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 +237 -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 +2 -1
- 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 +141 -21
- 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 +45 -29
- 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 +106 -5
- package/src/calls/call-orchestrator.ts +252 -54
- 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 +7 -5
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +40 -15
- package/src/calls/twilio-routes.ts +60 -45
- 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/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
- package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
- 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 +7 -9
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
- package/src/config/bundled-skills/messaging/SKILL.md +12 -2
- 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/phone-calls/SKILL.md +86 -21
- 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 +27 -3
- package/src/config/env-registry.ts +169 -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 +157 -1138
- 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 +107 -56
- 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 +0 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
- package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
- 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 +254 -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 -2463
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +4 -6
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/index.ts +9 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +74 -5
- package/src/daemon/handlers/shared.ts +3 -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 +2 -2
- 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 +321 -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 +62 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +227 -2527
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -379
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +67 -2
- package/src/daemon/server.ts +60 -44
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +113 -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 +98 -4
- package/src/daemon/session-runtime-assembly.ts +149 -15
- package/src/daemon/session-surfaces.ts +26 -4
- package/src/daemon/session-tool-setup.ts +28 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +24 -1
- 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 +3 -1
- 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 +200 -1
- 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 +121 -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 +11 -42
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +6 -9
- package/src/memory/jobs-worker.ts +51 -36
- 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 +12 -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 +163 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +130 -7
- package/src/memory/search/entity.ts +10 -19
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +21 -22
- package/src/memory/search/semantic.ts +157 -19
- 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/providers/sms/adapter.ts +3 -6
- 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 +126 -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 +115 -5
- package/src/runtime/assistant-event-hub.ts +3 -1
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +0 -21
- package/src/runtime/channel-guardian-service.ts +48 -7
- package/src/runtime/channel-readiness-service.ts +160 -34
- package/src/runtime/channel-readiness-types.ts +10 -4
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +289 -745
- package/src/runtime/http-types.ts +56 -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 +49 -6
- 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 -1634
- 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 +144 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +52 -34
- 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/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 +13 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -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/executor.ts +80 -18
- 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 +7 -3
- 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/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- 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 +25 -18
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +72 -365
- 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/bundled-skills/media-processing/services/capability-registry.ts +0 -137
- package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
- package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
- package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
- package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
- package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
- package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
- package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
- package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
- package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
package/src/util/platform.ts
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { mkdirSync, existsSync, statSync, unlinkSync,
|
|
2
|
-
import { join
|
|
1
|
+
import { mkdirSync, existsSync, statSync, unlinkSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const prefix = level === 'warn' ? 'WARN' : 'INFO';
|
|
13
|
-
const extra = data ? ' ' + JSON.stringify(data) : '';
|
|
14
|
-
process.stderr.write(`[migration] ${prefix}: ${msg}${extra}\n`);
|
|
15
|
-
}
|
|
4
|
+
import {
|
|
5
|
+
getBaseDataDir,
|
|
6
|
+
getDaemonSocket,
|
|
7
|
+
getDaemonTcpPort,
|
|
8
|
+
getDaemonTcpEnabled,
|
|
9
|
+
getDaemonTcpHost,
|
|
10
|
+
getDaemonIosPairing,
|
|
11
|
+
} from '../config/env-registry.js';
|
|
16
12
|
|
|
17
13
|
export function isMacOS(): boolean {
|
|
18
14
|
return process.platform === 'darwin';
|
|
@@ -52,7 +48,7 @@ export function getClipboardCommand(): string | null {
|
|
|
52
48
|
* Returns null if neither file exists or both are malformed.
|
|
53
49
|
*/
|
|
54
50
|
export function readLockfile(): Record<string, unknown> | null {
|
|
55
|
-
const base =
|
|
51
|
+
const base = getBaseDataDir() || homedir();
|
|
56
52
|
const candidates = [
|
|
57
53
|
join(base, '.vellum.lock.json'),
|
|
58
54
|
join(base, '.vellum.lockfile.json'),
|
|
@@ -71,12 +67,40 @@ export function readLockfile(): Record<string, unknown> | null {
|
|
|
71
67
|
return null;
|
|
72
68
|
}
|
|
73
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Normalize an assistant ID to its canonical form for DB operations.
|
|
72
|
+
*
|
|
73
|
+
* The system uses "self" as the canonical single-tenant identifier
|
|
74
|
+
* (see migration 007-assistant-id-to-self). However, the desktop UI
|
|
75
|
+
* sends the real assistant ID (e.g., "vellum-true-eel") while the
|
|
76
|
+
* inbound call path resolves phone numbers to config keys (typically
|
|
77
|
+
* "self"). This function maps any known lockfile assistant ID to "self"
|
|
78
|
+
* so both sides use a consistent DB key.
|
|
79
|
+
*/
|
|
80
|
+
export function normalizeAssistantId(assistantId: string): string {
|
|
81
|
+
if (assistantId === 'self') return 'self';
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const lockData = readLockfile();
|
|
85
|
+
const assistants = lockData?.assistants as Array<Record<string, unknown>> | undefined;
|
|
86
|
+
if (assistants) {
|
|
87
|
+
for (const entry of assistants) {
|
|
88
|
+
if (entry.assistantId === assistantId) return 'self';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// lockfile unreadable — return as-is
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return assistantId;
|
|
96
|
+
}
|
|
97
|
+
|
|
74
98
|
/**
|
|
75
99
|
* Write data to the primary lockfile (~/.vellum.lock.json).
|
|
76
100
|
* Respects BASE_DATA_DIR for non-standard home directories.
|
|
77
101
|
*/
|
|
78
102
|
export function writeLockfile(data: Record<string, unknown>): void {
|
|
79
|
-
const base =
|
|
103
|
+
const base = getBaseDataDir() || homedir();
|
|
80
104
|
writeFileSync(join(base, '.vellum.lock.json'), JSON.stringify(data, null, 2) + '\n');
|
|
81
105
|
}
|
|
82
106
|
|
|
@@ -85,7 +109,7 @@ export function writeLockfile(data: Record<string, unknown>): void {
|
|
|
85
109
|
* files, skills) and runtime files (socket, PID) live here.
|
|
86
110
|
*/
|
|
87
111
|
export function getRootDir(): string {
|
|
88
|
-
return join(
|
|
112
|
+
return join(getBaseDataDir() || homedir(), '.vellum');
|
|
89
113
|
}
|
|
90
114
|
|
|
91
115
|
/**
|
|
@@ -126,7 +150,7 @@ export function getInterfacesDir(): string {
|
|
|
126
150
|
}
|
|
127
151
|
|
|
128
152
|
export function getSocketPath(): string {
|
|
129
|
-
const override =
|
|
153
|
+
const override = getDaemonSocket();
|
|
130
154
|
if (override) {
|
|
131
155
|
return expandHomePath(override);
|
|
132
156
|
}
|
|
@@ -142,12 +166,7 @@ export function getSessionTokenPath(): string {
|
|
|
142
166
|
* Reads VELLUM_DAEMON_TCP_PORT env var; defaults to 8765.
|
|
143
167
|
*/
|
|
144
168
|
export function getTCPPort(): number {
|
|
145
|
-
|
|
146
|
-
if (override) {
|
|
147
|
-
const port = parseInt(override, 10);
|
|
148
|
-
if (!isNaN(port) && port > 0 && port <= 65535) return port;
|
|
149
|
-
}
|
|
150
|
-
return 8765;
|
|
169
|
+
return getDaemonTcpPort();
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
/**
|
|
@@ -162,9 +181,8 @@ export function getTCPPort(): number {
|
|
|
162
181
|
* The macOS CLI (AssistantCli) also sets the env var for bundled-binary deployments.
|
|
163
182
|
*/
|
|
164
183
|
export function isTCPEnabled(): boolean {
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
if (override === 'false' || override === '0') return false;
|
|
184
|
+
const envValue = getDaemonTcpEnabled();
|
|
185
|
+
if (envValue !== undefined) return envValue;
|
|
168
186
|
return existsSync(join(getRootDir(), 'tcp-enabled'));
|
|
169
187
|
}
|
|
170
188
|
|
|
@@ -176,7 +194,7 @@ export function isTCPEnabled(): boolean {
|
|
|
176
194
|
* 3. Default: '127.0.0.1' (localhost only)
|
|
177
195
|
*/
|
|
178
196
|
export function getTCPHost(): string {
|
|
179
|
-
const override =
|
|
197
|
+
const override = getDaemonTcpHost();
|
|
180
198
|
if (override) return override;
|
|
181
199
|
if (isIOSPairingEnabled()) return '0.0.0.0';
|
|
182
200
|
return '127.0.0.1';
|
|
@@ -197,9 +215,8 @@ export function getTCPHost(): string {
|
|
|
197
215
|
* access without exposing the daemon to the LAN.
|
|
198
216
|
*/
|
|
199
217
|
export function isIOSPairingEnabled(): boolean {
|
|
200
|
-
const
|
|
201
|
-
if (
|
|
202
|
-
if (override === 'false' || override === '0') return false;
|
|
218
|
+
const envValue = getDaemonIosPairing();
|
|
219
|
+
if (envValue !== undefined) return envValue;
|
|
203
220
|
return existsSync(join(getRootDir(), 'ios-pairing-enabled'));
|
|
204
221
|
}
|
|
205
222
|
|
|
@@ -207,6 +224,27 @@ export function getHttpTokenPath(): string {
|
|
|
207
224
|
return join(getRootDir(), 'http-token');
|
|
208
225
|
}
|
|
209
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Returns the path to the platform API token file (~/.vellum/platform-token).
|
|
229
|
+
* This token is the X-Session-Token used to authenticate with the Vellum
|
|
230
|
+
* Platform API (e.g. assistant.vellum.ai).
|
|
231
|
+
*/
|
|
232
|
+
export function getPlatformTokenPath(): string {
|
|
233
|
+
return join(getRootDir(), 'platform-token');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Read the platform API token from disk. Returns null if the file
|
|
238
|
+
* doesn't exist or can't be read.
|
|
239
|
+
*/
|
|
240
|
+
export function readPlatformToken(): string | null {
|
|
241
|
+
try {
|
|
242
|
+
return readFileSync(getPlatformTokenPath(), 'utf-8').trim();
|
|
243
|
+
} catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
210
248
|
/**
|
|
211
249
|
* Read the daemon session token from disk. Returns null if the file
|
|
212
250
|
* doesn't exist or can't be read (daemon not running).
|
|
@@ -302,276 +340,9 @@ export function getWorkspacePromptPath(file: string): string {
|
|
|
302
340
|
return join(getWorkspaceDir(), file);
|
|
303
341
|
}
|
|
304
342
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
* - No-op if destination already exists (avoids clobbering).
|
|
309
|
-
* - Creates destination parent directories as needed.
|
|
310
|
-
* - Logs warning on failure instead of throwing.
|
|
311
|
-
*
|
|
312
|
-
* Exported for testing; not intended for general use outside migrations.
|
|
313
|
-
*/
|
|
314
|
-
export function migratePath(source: string, destination: string): void {
|
|
315
|
-
if (!existsSync(source)) return;
|
|
316
|
-
if (existsSync(destination)) {
|
|
317
|
-
migrationLog('debug', 'Migration skipped: destination already exists', { source, destination });
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
try {
|
|
321
|
-
const destDir = dirname(destination);
|
|
322
|
-
if (!existsSync(destDir)) {
|
|
323
|
-
mkdirSync(destDir, { recursive: true });
|
|
324
|
-
}
|
|
325
|
-
renameSync(source, destination);
|
|
326
|
-
migrationLog('info', 'Migrated path', { from: source, to: destination });
|
|
327
|
-
} catch (err) {
|
|
328
|
-
migrationLog('warn', 'Failed to migrate path', { err: String(err), from: source, to: destination });
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* When migratePath skips config.json because the workspace copy already
|
|
334
|
-
* exists, the legacy root config may still contain keys (e.g. slackWebhookUrl)
|
|
335
|
-
* that were never written to the workspace config. This merges any missing
|
|
336
|
-
* top-level keys from the legacy file into the workspace file so they are
|
|
337
|
-
* not silently lost during upgrade.
|
|
338
|
-
*/
|
|
339
|
-
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
340
|
-
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function mergeSkippedConfigKeys(legacyPath: string, workspacePath: string): void {
|
|
344
|
-
if (!existsSync(legacyPath) || !existsSync(workspacePath)) return;
|
|
345
|
-
|
|
346
|
-
let legacy: Record<string, unknown>;
|
|
347
|
-
let workspace: Record<string, unknown>;
|
|
348
|
-
try {
|
|
349
|
-
const legacyRaw = JSON.parse(readFileSync(legacyPath, 'utf-8'));
|
|
350
|
-
const workspaceRaw = JSON.parse(readFileSync(workspacePath, 'utf-8'));
|
|
351
|
-
if (!isPlainObject(legacyRaw) || !isPlainObject(workspaceRaw)) return;
|
|
352
|
-
legacy = legacyRaw;
|
|
353
|
-
workspace = workspaceRaw;
|
|
354
|
-
} catch {
|
|
355
|
-
return; // malformed JSON — skip silently
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const merged: string[] = [];
|
|
359
|
-
for (const key of Object.keys(legacy)) {
|
|
360
|
-
if (!(key in workspace)) {
|
|
361
|
-
workspace[key] = legacy[key];
|
|
362
|
-
merged.push(key);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (merged.length > 0) {
|
|
367
|
-
try {
|
|
368
|
-
writeFileSync(workspacePath, JSON.stringify(workspace, null, 2) + '\n');
|
|
369
|
-
// Remove merged keys from legacy config so they are not resurrected
|
|
370
|
-
// if a user later deletes them from the workspace config.
|
|
371
|
-
for (const key of merged) {
|
|
372
|
-
delete legacy[key];
|
|
373
|
-
}
|
|
374
|
-
if (Object.keys(legacy).length === 0) {
|
|
375
|
-
unlinkSync(legacyPath);
|
|
376
|
-
} else {
|
|
377
|
-
writeFileSync(legacyPath, JSON.stringify(legacy, null, 2) + '\n');
|
|
378
|
-
}
|
|
379
|
-
migrationLog('info', 'Merged legacy config keys into workspace config', { keys: merged });
|
|
380
|
-
} catch (err) {
|
|
381
|
-
migrationLog('warn', 'Failed to merge legacy config keys', { err: String(err), keys: merged });
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* When migratePath skips the hooks directory because the workspace copy
|
|
388
|
-
* already exists (e.g. pre-created by ensureDataDir), the legacy hooks
|
|
389
|
-
* directory may still contain individual hook files/subdirectories that
|
|
390
|
-
* were never moved. This merges any missing entries from the legacy
|
|
391
|
-
* path into the workspace hooks path so they are not silently lost.
|
|
392
|
-
*/
|
|
393
|
-
function mergeLegacyHooks(legacyDir: string, workspaceDir: string): void {
|
|
394
|
-
if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
|
|
395
|
-
|
|
396
|
-
let entries: import('node:fs').Dirent[];
|
|
397
|
-
try {
|
|
398
|
-
entries = readdirSync(legacyDir, { withFileTypes: true });
|
|
399
|
-
} catch {
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
for (const entry of entries) {
|
|
404
|
-
const src = join(legacyDir, entry.name);
|
|
405
|
-
const dest = join(workspaceDir, entry.name);
|
|
406
|
-
if (existsSync(dest)) {
|
|
407
|
-
// config.json needs a merge rather than a skip — the legacy file may
|
|
408
|
-
// contain hook enabled/settings entries that the workspace copy lacks.
|
|
409
|
-
if (entry.name === 'config.json') {
|
|
410
|
-
mergeHooksConfig(src, dest);
|
|
411
|
-
}
|
|
412
|
-
continue;
|
|
413
|
-
}
|
|
414
|
-
try {
|
|
415
|
-
renameSync(src, dest);
|
|
416
|
-
migrationLog('info', 'Merged legacy hook into workspace', { from: src, to: dest });
|
|
417
|
-
} catch (err) {
|
|
418
|
-
migrationLog('warn', 'Failed to merge legacy hook', { err: String(err), from: src, to: dest });
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Merge missing hook entries from a legacy hooks/config.json into the
|
|
425
|
-
* workspace hooks/config.json. Only adds hooks that don't already exist
|
|
426
|
-
* in the workspace config so user changes are never overwritten.
|
|
427
|
-
*/
|
|
428
|
-
function mergeHooksConfig(legacyPath: string, workspacePath: string): void {
|
|
429
|
-
let legacy: Record<string, unknown>;
|
|
430
|
-
let workspace: Record<string, unknown>;
|
|
431
|
-
try {
|
|
432
|
-
const legacyRaw = JSON.parse(readFileSync(legacyPath, 'utf-8'));
|
|
433
|
-
const workspaceRaw = JSON.parse(readFileSync(workspacePath, 'utf-8'));
|
|
434
|
-
if (!isPlainObject(legacyRaw) || !isPlainObject(workspaceRaw)) return;
|
|
435
|
-
legacy = legacyRaw;
|
|
436
|
-
workspace = workspaceRaw;
|
|
437
|
-
} catch {
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const legacyHooks = legacy.hooks;
|
|
442
|
-
const wsHooks = workspace.hooks;
|
|
443
|
-
if (!isPlainObject(legacyHooks) || !isPlainObject(wsHooks)) return;
|
|
444
|
-
|
|
445
|
-
const merged: string[] = [];
|
|
446
|
-
for (const hookName of Object.keys(legacyHooks)) {
|
|
447
|
-
if (!(hookName in wsHooks)) {
|
|
448
|
-
wsHooks[hookName] = legacyHooks[hookName];
|
|
449
|
-
merged.push(hookName);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (merged.length > 0) {
|
|
454
|
-
try {
|
|
455
|
-
writeFileSync(workspacePath, JSON.stringify(workspace, null, 2) + '\n');
|
|
456
|
-
// Remove merged hooks from legacy config to prevent resurrection
|
|
457
|
-
for (const hookName of merged) {
|
|
458
|
-
delete legacyHooks[hookName];
|
|
459
|
-
}
|
|
460
|
-
if (Object.keys(legacyHooks).length === 0) {
|
|
461
|
-
unlinkSync(legacyPath);
|
|
462
|
-
} else {
|
|
463
|
-
writeFileSync(legacyPath, JSON.stringify(legacy, null, 2) + '\n');
|
|
464
|
-
}
|
|
465
|
-
migrationLog('info', 'Merged legacy hooks config entries into workspace', { hooks: merged });
|
|
466
|
-
} catch (err) {
|
|
467
|
-
migrationLog('warn', 'Failed to merge legacy hooks config', { err: String(err), hooks: merged });
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* When migratePath skips the skills directory because the workspace copy
|
|
474
|
-
* already exists (e.g. pre-created by ensureDataDir), the legacy skills
|
|
475
|
-
* directory may still contain individual skill subdirectories that were
|
|
476
|
-
* never moved. This merges any missing skill subdirectories from the
|
|
477
|
-
* legacy path into the workspace skills path so they are not stranded.
|
|
478
|
-
*/
|
|
479
|
-
function mergeLegacySkills(legacyDir: string, workspaceDir: string): void {
|
|
480
|
-
if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
|
|
481
|
-
|
|
482
|
-
let entries: import('node:fs').Dirent[];
|
|
483
|
-
try {
|
|
484
|
-
entries = readdirSync(legacyDir, { withFileTypes: true });
|
|
485
|
-
} catch {
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
for (const entry of entries) {
|
|
490
|
-
const src = join(legacyDir, entry.name);
|
|
491
|
-
const dest = join(workspaceDir, entry.name);
|
|
492
|
-
if (existsSync(dest)) continue; // already present in workspace
|
|
493
|
-
try {
|
|
494
|
-
renameSync(src, dest);
|
|
495
|
-
migrationLog('info', 'Merged legacy skill into workspace', { from: src, to: dest });
|
|
496
|
-
} catch (err) {
|
|
497
|
-
migrationLog('warn', 'Failed to merge legacy skill', { err: String(err), from: src, to: dest });
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* When migratePath skips the data directory because workspace/data already
|
|
504
|
-
* exists (e.g. the user's project had a data/ folder that was extracted from
|
|
505
|
-
* sandbox/fs), the legacy data directory may still contain internal state
|
|
506
|
-
* subdirectories (db/, logs/, sandbox/, etc.) that need to be preserved.
|
|
507
|
-
* This merges any missing entries from the legacy data path into workspace/data.
|
|
508
|
-
*/
|
|
509
|
-
function mergeLegacyDataEntries(legacyDir: string, workspaceDir: string): void {
|
|
510
|
-
if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
|
|
511
|
-
|
|
512
|
-
let entries: import('node:fs').Dirent[];
|
|
513
|
-
try {
|
|
514
|
-
entries = readdirSync(legacyDir, { withFileTypes: true });
|
|
515
|
-
} catch {
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
for (const entry of entries) {
|
|
520
|
-
const src = join(legacyDir, entry.name);
|
|
521
|
-
const dest = join(workspaceDir, entry.name);
|
|
522
|
-
if (existsSync(dest)) continue; // already present in workspace
|
|
523
|
-
try {
|
|
524
|
-
renameSync(src, dest);
|
|
525
|
-
migrationLog('info', 'Merged legacy data entry into workspace', { from: src, to: dest });
|
|
526
|
-
} catch (err) {
|
|
527
|
-
migrationLog('warn', 'Failed to merge legacy data entry', { err: String(err), from: src, to: dest });
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Migrate from the flat ~/.vellum layout to the workspace-based layout.
|
|
534
|
-
*
|
|
535
|
-
* Step (a) is special: if the workspace dir doesn't exist yet but the old
|
|
536
|
-
* sandbox working dir (data/sandbox/fs) does, its contents are "extracted"
|
|
537
|
-
* to become the new workspace root via rename. All subsequent moves then
|
|
538
|
-
* land inside that workspace directory.
|
|
539
|
-
*
|
|
540
|
-
* Idempotent: safe to call on every startup — already-migrated items are
|
|
541
|
-
* skipped, and a second run is a no-op.
|
|
542
|
-
*/
|
|
543
|
-
export function migrateToWorkspaceLayout(): void {
|
|
544
|
-
const root = getRootDir();
|
|
545
|
-
if (!existsSync(root)) return;
|
|
546
|
-
|
|
547
|
-
const ws = getWorkspaceDir();
|
|
548
|
-
|
|
549
|
-
// (a) Extract data/sandbox/fs -> workspace (only when workspace doesn't exist yet)
|
|
550
|
-
if (!existsSync(ws)) {
|
|
551
|
-
const sandboxFs = join(root, 'data', 'sandbox', 'fs');
|
|
552
|
-
if (existsSync(sandboxFs)) {
|
|
553
|
-
try {
|
|
554
|
-
renameSync(sandboxFs, ws);
|
|
555
|
-
migrationLog('info', 'Extracted sandbox/fs as workspace root', { from: sandboxFs, to: ws });
|
|
556
|
-
} catch (err) {
|
|
557
|
-
migrationLog('warn', 'Failed to extract sandbox/fs', { err: String(err), from: sandboxFs, to: ws });
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// (b)-(h) Move legacy root-level items into workspace
|
|
563
|
-
migratePath(join(root, 'config.json'), join(ws, 'config.json'));
|
|
564
|
-
mergeSkippedConfigKeys(join(root, 'config.json'), join(ws, 'config.json'));
|
|
565
|
-
migratePath(join(root, 'data'), join(ws, 'data'));
|
|
566
|
-
mergeLegacyDataEntries(join(root, 'data'), join(ws, 'data'));
|
|
567
|
-
migratePath(join(root, 'hooks'), join(ws, 'hooks'));
|
|
568
|
-
mergeLegacyHooks(join(root, 'hooks'), join(ws, 'hooks'));
|
|
569
|
-
migratePath(join(root, 'IDENTITY.md'), join(ws, 'IDENTITY.md'));
|
|
570
|
-
migratePath(join(root, 'skills'), join(ws, 'skills'));
|
|
571
|
-
mergeLegacySkills(join(root, 'skills'), join(ws, 'skills'));
|
|
572
|
-
migratePath(join(root, 'SOUL.md'), join(ws, 'SOUL.md'));
|
|
573
|
-
migratePath(join(root, 'USER.md'), join(ws, 'USER.md'));
|
|
574
|
-
}
|
|
343
|
+
// Re-export migration functions so existing consumers don't break.
|
|
344
|
+
export { migratePath, migrateToWorkspaceLayout } from '../migrations/workspace-layout.js';
|
|
345
|
+
export { migrateToDataLayout } from '../migrations/data-layout.js';
|
|
575
346
|
|
|
576
347
|
export function ensureDataDir(): void {
|
|
577
348
|
const root = getRootDir();
|
|
@@ -608,67 +379,3 @@ export function ensureDataDir(): void {
|
|
|
608
379
|
// Non-fatal: some filesystems don't support Unix permissions
|
|
609
380
|
}
|
|
610
381
|
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* Migrate files from the old flat ~/.vellum layout to the new structured
|
|
614
|
-
* layout with data/ and protected/ subdirectories.
|
|
615
|
-
*
|
|
616
|
-
* Idempotent: skips items that have already been migrated.
|
|
617
|
-
* Uses renameSync for atomic moves (same filesystem).
|
|
618
|
-
*/
|
|
619
|
-
export function migrateToDataLayout(): void {
|
|
620
|
-
const root = getRootDir();
|
|
621
|
-
const data = join(root, 'data');
|
|
622
|
-
|
|
623
|
-
if (!existsSync(root)) return;
|
|
624
|
-
|
|
625
|
-
function migrateItem(oldPath: string, newPath: string): void {
|
|
626
|
-
if (!existsSync(oldPath)) return;
|
|
627
|
-
if (existsSync(newPath)) return;
|
|
628
|
-
try {
|
|
629
|
-
const newDir = dirname(newPath);
|
|
630
|
-
if (!existsSync(newDir)) {
|
|
631
|
-
mkdirSync(newDir, { recursive: true });
|
|
632
|
-
}
|
|
633
|
-
renameSync(oldPath, newPath);
|
|
634
|
-
migrationLog('info', 'Migrated path', { from: oldPath, to: newPath });
|
|
635
|
-
} catch (err) {
|
|
636
|
-
migrationLog('warn', 'Failed to migrate path', { err: String(err), from: oldPath, to: newPath });
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// DB: ~/.vellum/data/assistant.db → ~/.vellum/data/db/assistant.db
|
|
641
|
-
migrateItem(join(data, 'assistant.db'), join(data, 'db', 'assistant.db'));
|
|
642
|
-
migrateItem(join(data, 'assistant.db-wal'), join(data, 'db', 'assistant.db-wal'));
|
|
643
|
-
migrateItem(join(data, 'assistant.db-shm'), join(data, 'db', 'assistant.db-shm'));
|
|
644
|
-
|
|
645
|
-
// Qdrant PID: ~/.vellum/qdrant.pid → ~/.vellum/data/qdrant/qdrant.pid
|
|
646
|
-
migrateItem(join(root, 'qdrant.pid'), join(data, 'qdrant', 'qdrant.pid'));
|
|
647
|
-
|
|
648
|
-
// Qdrant binary: ~/.vellum/bin/ → ~/.vellum/data/qdrant/bin/
|
|
649
|
-
migrateItem(join(root, 'bin'), join(data, 'qdrant', 'bin'));
|
|
650
|
-
|
|
651
|
-
// Logs: ~/.vellum/logs/ → ~/.vellum/data/logs/
|
|
652
|
-
migrateItem(join(root, 'logs'), join(data, 'logs'));
|
|
653
|
-
|
|
654
|
-
// Memory: ~/.vellum/memory/ → ~/.vellum/data/memory/
|
|
655
|
-
migrateItem(join(root, 'memory'), join(data, 'memory'));
|
|
656
|
-
|
|
657
|
-
// Apps: ~/.vellum/apps/ → ~/.vellum/data/apps/
|
|
658
|
-
migrateItem(join(root, 'apps'), join(data, 'apps'));
|
|
659
|
-
|
|
660
|
-
// Browser auth: ~/.vellum/browser-auth/ → ~/.vellum/data/browser-auth/
|
|
661
|
-
migrateItem(join(root, 'browser-auth'), join(data, 'browser-auth'));
|
|
662
|
-
|
|
663
|
-
// Browser profile: ~/.vellum/browser-profile/ → ~/.vellum/data/browser-profile/
|
|
664
|
-
migrateItem(join(root, 'browser-profile'), join(data, 'browser-profile'));
|
|
665
|
-
|
|
666
|
-
// History: ~/.vellum/history → ~/.vellum/data/history
|
|
667
|
-
migrateItem(join(root, 'history'), join(data, 'history'));
|
|
668
|
-
|
|
669
|
-
// Protected files: ~/.vellum/X → ~/.vellum/protected/X
|
|
670
|
-
const protectedDir = join(root, 'protected');
|
|
671
|
-
migrateItem(join(root, 'trust.json'), join(protectedDir, 'trust.json'));
|
|
672
|
-
migrateItem(join(root, 'keys.enc'), join(protectedDir, 'keys.enc'));
|
|
673
|
-
migrateItem(join(root, 'secret-allowlist.json'), join(protectedDir, 'secret-allowlist.json'));
|
|
674
|
-
}
|
package/src/util/pricing.ts
CHANGED
|
@@ -143,7 +143,7 @@ export function estimateCost(
|
|
|
143
143
|
provider: string,
|
|
144
144
|
): number {
|
|
145
145
|
const result = resolvePricing(provider, model, inputTokens, outputTokens);
|
|
146
|
-
if (result.pricingStatus === 'priced' && result.estimatedCostUsd
|
|
146
|
+
if (result.pricingStatus === 'priced' && result.estimatedCostUsd != null) {
|
|
147
147
|
return result.estimatedCostUsd;
|
|
148
148
|
}
|
|
149
149
|
return 0;
|
package/src/util/retry.ts
CHANGED
|
@@ -95,4 +95,23 @@ export function sleep(ms: number): Promise<void> {
|
|
|
95
95
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Like `sleep` but resolves early when an AbortSignal fires.
|
|
100
|
+
* Resolves (not rejects) on abort so callers can check the signal
|
|
101
|
+
* themselves and decide what to do.
|
|
102
|
+
*/
|
|
103
|
+
export function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
104
|
+
if (!signal) return sleep(ms);
|
|
105
|
+
if (signal.aborted) return Promise.resolve();
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
const timer = setTimeout(onDone, ms);
|
|
108
|
+
signal.addEventListener('abort', onDone, { once: true });
|
|
109
|
+
function onDone() {
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
signal!.removeEventListener('abort', onDone);
|
|
112
|
+
resolve();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
98
117
|
export { DEFAULT_MAX_RETRIES, DEFAULT_BASE_DELAY_MS };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic DB row mapper — replaces repetitive parse* functions across store files.
|
|
3
|
+
*
|
|
4
|
+
* Each field in the schema is described by either a source column name (passthrough)
|
|
5
|
+
* or a transform descriptor. The mapper produces a function that converts a raw
|
|
6
|
+
* Drizzle row into a typed domain object.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// A field descriptor is either a key of the source row (passthrough) or a transform.
|
|
10
|
+
type FieldDescriptor<TRow, TOut> =
|
|
11
|
+
| (keyof TRow & string)
|
|
12
|
+
| { from: keyof TRow & string; transform: (value: TRow[keyof TRow]) => TOut };
|
|
13
|
+
|
|
14
|
+
// The schema maps each output field to a field descriptor.
|
|
15
|
+
type MapperSchema<TRow, TDomain> = {
|
|
16
|
+
[K in keyof TDomain]: FieldDescriptor<TRow, TDomain[K]>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a row-to-domain mapper from a declarative schema.
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* ```ts
|
|
24
|
+
* const parseReminder = createRowMapper<typeof reminders.$inferSelect, ReminderRow>({
|
|
25
|
+
* id: 'id',
|
|
26
|
+
* label: 'label',
|
|
27
|
+
* mode: { from: 'mode', transform: (v) => v as ReminderRow['mode'] },
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function createRowMapper<TRow, TDomain>(
|
|
32
|
+
schema: MapperSchema<TRow, TDomain>,
|
|
33
|
+
): (row: TRow) => TDomain {
|
|
34
|
+
const entries = Object.entries(schema) as Array<
|
|
35
|
+
[string, FieldDescriptor<TRow, unknown>]
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
return (row: TRow): TDomain => {
|
|
39
|
+
const result = {} as Record<string, unknown>;
|
|
40
|
+
for (const [key, descriptor] of entries) {
|
|
41
|
+
if (typeof descriptor === 'string') {
|
|
42
|
+
result[key] = row[descriptor as keyof TRow];
|
|
43
|
+
} else {
|
|
44
|
+
const d = descriptor as { from: keyof TRow & string; transform: (value: TRow[keyof TRow]) => unknown };
|
|
45
|
+
result[key] = d.transform(row[d.from]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result as TDomain;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Convenience: cast a value to a narrower type (for string union columns). */
|
|
53
|
+
export function cast<T>() {
|
|
54
|
+
return (value: unknown) => value as T;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Convenience: parse a JSON string column with a fallback value on parse failure. */
|
|
58
|
+
export function parseJson<T>(fallback: T): (value: unknown) => T {
|
|
59
|
+
return (value: unknown): T => {
|
|
60
|
+
if (typeof value !== 'string' || !value) return fallback;
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(value) as T;
|
|
63
|
+
} catch {
|
|
64
|
+
return fallback;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Convenience: parse a JSON string column, returning null on parse failure. */
|
|
70
|
+
export function parseJsonNullable<T>(): (value: unknown) => T | null {
|
|
71
|
+
return (value: unknown): T | null => {
|
|
72
|
+
if (typeof value !== 'string' || !value) return null;
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(value) as T;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getLogger } from './logger.js';
|
|
2
|
+
|
|
3
|
+
const log = getLogger('silently');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Attaches a `.catch()` to `promise` that emits a debug-level log instead of
|
|
7
|
+
* swallowing the rejection completely. Use this in place of bare
|
|
8
|
+
* `.catch(() => {})` when you need fire-and-forget semantics but still want
|
|
9
|
+
* visibility into unexpected errors during debugging.
|
|
10
|
+
*
|
|
11
|
+
* The original promise is returned unchanged so callers can still chain it.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* silentlyWithLog(stopSession(id), 'idle session cleanup');
|
|
15
|
+
*/
|
|
16
|
+
export function silentlyWithLog<T>(promise: Promise<T>, context: string): Promise<T> {
|
|
17
|
+
promise.catch((err: unknown) => {
|
|
18
|
+
log.debug({ err, context }, 'Suppressed async error');
|
|
19
|
+
});
|
|
20
|
+
return promise;
|
|
21
|
+
}
|
package/src/watcher/engine.ts
CHANGED
|
@@ -85,7 +85,7 @@ export async function runWatchersOnce(
|
|
|
85
85
|
log.info({ watcherId: watcher.id, watermark }, 'Initialized watermark');
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
const result = await provider.fetchNew(watcher.credentialService, watermark, config);
|
|
88
|
+
const result = await provider.fetchNew(watcher.credentialService, watermark, config, watcher.id);
|
|
89
89
|
|
|
90
90
|
// Store new events with dedup
|
|
91
91
|
let newEvents = 0;
|
|
@@ -118,6 +118,10 @@ export async function runWatchersOnce(
|
|
|
118
118
|
if ((watcher.consecutiveErrors + 1) >= MAX_CONSECUTIVE_ERRORS) {
|
|
119
119
|
const reason = `Disabled after ${MAX_CONSECUTIVE_ERRORS} consecutive errors. Last: ${message}`;
|
|
120
120
|
disableWatcher(watcher.id, reason);
|
|
121
|
+
// Do NOT call provider.cleanup() here — auto-disable is reversible.
|
|
122
|
+
// If the watcher is re-enabled later, it must diff against the same
|
|
123
|
+
// baseline to avoid missing events that occurred while disabled.
|
|
124
|
+
// Cleanup is only correct on true deletion (see tools/watcher/delete.ts).
|
|
121
125
|
log.warn({ watcherId: watcher.id, name: watcher.name }, 'Watcher disabled by circuit breaker');
|
|
122
126
|
notify({
|
|
123
127
|
title: `Watcher disabled: ${watcher.name}`,
|