@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
|
@@ -1,178 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Query media tool — sends natural language queries against video
|
|
3
|
+
* analysis data (map output) via Claude for intelligent answers.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the retrieval layer remains fully generic.
|
|
5
|
+
* Replaces the old keyword-matching approach with an LLM-powered
|
|
6
|
+
* reduce/query step that can answer arbitrary questions about the
|
|
7
|
+
* video content.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { ToolContext, ToolExecutionResult } from '../../../../tools/types.js';
|
|
11
|
-
import {
|
|
12
|
-
import { getTrackingProfile, type CapabilityTier } from '../../../../memory/media-store.js';
|
|
13
|
-
import { getCapabilitiesByTier, getCapabilityByName } from '../services/capability-registry.js';
|
|
11
|
+
import { reduceForAsset, type ReduceOptions, type ReduceResult } from '../services/reduce.js';
|
|
14
12
|
|
|
15
13
|
// ---------------------------------------------------------------------------
|
|
16
|
-
//
|
|
14
|
+
// Exported function for job handler use (one-shot merge mode)
|
|
17
15
|
// ---------------------------------------------------------------------------
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
const EVENT_TYPE_KEYWORDS: Record<string, string> = {
|
|
21
|
-
// Basketball
|
|
22
|
-
turnover: 'turnover',
|
|
23
|
-
turnovers: 'turnover',
|
|
24
|
-
steal: 'turnover',
|
|
25
|
-
steals: 'turnover',
|
|
26
|
-
// Soccer / football
|
|
27
|
-
goal: 'goal',
|
|
28
|
-
goals: 'goal',
|
|
29
|
-
score: 'goal',
|
|
30
|
-
scores: 'goal',
|
|
31
|
-
// Generic
|
|
32
|
-
highlight: 'highlight',
|
|
33
|
-
highlights: 'highlight',
|
|
34
|
-
scene_change: 'scene_change',
|
|
35
|
-
'scene change': 'scene_change',
|
|
36
|
-
'scene changes': 'scene_change',
|
|
37
|
-
foul: 'foul',
|
|
38
|
-
fouls: 'foul',
|
|
39
|
-
shot: 'shot',
|
|
40
|
-
shots: 'shot',
|
|
41
|
-
rebound: 'rebound',
|
|
42
|
-
rebounds: 'rebound',
|
|
43
|
-
assist: 'assist',
|
|
44
|
-
assists: 'assist',
|
|
45
|
-
block: 'block',
|
|
46
|
-
blocks: 'block',
|
|
47
|
-
transition: 'transition',
|
|
48
|
-
transitions: 'transition',
|
|
49
|
-
fast_break: 'fast_break',
|
|
50
|
-
'fast break': 'fast_break',
|
|
51
|
-
'fast breaks': 'fast_break',
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Extract a numeric limit from phrases like "top 5", "first 3", "last 10".
|
|
56
|
-
* Returns undefined if no limit phrase is found.
|
|
57
|
-
*/
|
|
58
|
-
function parseLimit(query: string): number | undefined {
|
|
59
|
-
const match = query.match(/(?:top|first|last|show|find|get)\s+(\d+)(?!\s*min)/i);
|
|
60
|
-
if (match) return parseInt(match[1], 10);
|
|
61
|
-
// Also match trailing "N events/moments"
|
|
62
|
-
const trailingMatch = query.match(/(\d+)\s+(?:events?|moments?|plays?|clips?)/i);
|
|
63
|
-
if (trailingMatch) return parseInt(trailingMatch[1], 10);
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Extract a confidence threshold from the query.
|
|
69
|
-
*/
|
|
70
|
-
function parseConfidence(query: string): number | undefined {
|
|
71
|
-
const lower = query.toLowerCase();
|
|
72
|
-
if (lower.includes('high confidence') || lower.includes('very confident') || lower.includes('most confident')) {
|
|
73
|
-
return 0.8;
|
|
74
|
-
}
|
|
75
|
-
if (lower.includes('confident') || lower.includes('medium confidence')) {
|
|
76
|
-
return 0.5;
|
|
77
|
-
}
|
|
78
|
-
// Explicit threshold: "confidence > 0.7" or "confidence above 0.7"
|
|
79
|
-
const explicitMatch = lower.match(/confidence\s*(?:>|above|over|>=)\s*([\d.]+)/);
|
|
80
|
-
if (explicitMatch) return parseFloat(explicitMatch[1]);
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Extract a time range from the query (returns seconds).
|
|
86
|
-
* Supports phrases like "first half", "second half", "first N minutes",
|
|
87
|
-
* "last N minutes", "between M and N minutes".
|
|
88
|
-
*/
|
|
89
|
-
function parseTimeRange(query: string): { startTimeMin?: number; startTimeMax?: number } {
|
|
90
|
-
const lower = query.toLowerCase();
|
|
91
|
-
|
|
92
|
-
// "first half" / "second half" — these are relative and require knowing
|
|
93
|
-
// total duration, which we don't have. Use reasonable game defaults:
|
|
94
|
-
// assume ~48 min game → 2880s, half = 1440s
|
|
95
|
-
if (lower.includes('first half')) {
|
|
96
|
-
return { startTimeMin: 0, startTimeMax: 1440 };
|
|
97
|
-
}
|
|
98
|
-
if (lower.includes('second half')) {
|
|
99
|
-
return { startTimeMin: 1440 };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// "first N minutes"
|
|
103
|
-
const firstNMin = lower.match(/first\s+(\d+)\s*min/);
|
|
104
|
-
if (firstNMin) {
|
|
105
|
-
return { startTimeMin: 0, startTimeMax: parseInt(firstNMin[1], 10) * 60 };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// "last N minutes"
|
|
109
|
-
// Without knowing total duration we can't compute this precisely,
|
|
110
|
-
// so we skip it and let the retrieval return all results.
|
|
111
|
-
|
|
112
|
-
// "between M and N minutes"
|
|
113
|
-
const betweenMatch = lower.match(/between\s+(\d+)\s*(?:and|to|-)\s*(\d+)\s*min/);
|
|
114
|
-
if (betweenMatch) {
|
|
115
|
-
return {
|
|
116
|
-
startTimeMin: parseInt(betweenMatch[1], 10) * 60,
|
|
117
|
-
startTimeMax: parseInt(betweenMatch[2], 10) * 60,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return {};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Extract the event type from the query by matching known keywords.
|
|
126
|
-
*/
|
|
127
|
-
function parseEventType(query: string): string | undefined {
|
|
128
|
-
const lower = query.toLowerCase();
|
|
129
|
-
|
|
130
|
-
// Check multi-word keywords first (longer matches win)
|
|
131
|
-
const multiWordKeys = Object.keys(EVENT_TYPE_KEYWORDS)
|
|
132
|
-
.filter((k) => k.includes(' ') || k.includes('_'))
|
|
133
|
-
.sort((a, b) => b.length - a.length);
|
|
134
|
-
|
|
135
|
-
for (const key of multiWordKeys) {
|
|
136
|
-
if (lower.includes(key)) return EVENT_TYPE_KEYWORDS[key];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Check single-word keywords
|
|
140
|
-
const words = lower.replace(/[^\w\s]/g, '').split(/\s+/);
|
|
141
|
-
for (const word of words) {
|
|
142
|
-
if (EVENT_TYPE_KEYWORDS[word]) return EVENT_TYPE_KEYWORDS[word];
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Parse a natural language query into structured retrieval filters.
|
|
150
|
-
*/
|
|
151
|
-
function parseQuery(query: string, assetId?: string): RetrievalFilters {
|
|
152
|
-
const eventType = parseEventType(query);
|
|
153
|
-
const limit = parseLimit(query);
|
|
154
|
-
const minConfidence = parseConfidence(query);
|
|
155
|
-
const timeRange = parseTimeRange(query);
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
assetId,
|
|
159
|
-
eventType,
|
|
160
|
-
minConfidence,
|
|
161
|
-
limit: limit ?? 10,
|
|
162
|
-
sortBy: 'confidence',
|
|
163
|
-
...timeRange,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ---------------------------------------------------------------------------
|
|
168
|
-
// Formatting
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
|
|
171
|
-
function formatTimestamp(seconds: number): string {
|
|
172
|
-
const mins = Math.floor(seconds / 60);
|
|
173
|
-
const secs = Math.floor(seconds % 60);
|
|
174
|
-
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
175
|
-
}
|
|
17
|
+
export { reduceForAsset } from '../services/reduce.js';
|
|
176
18
|
|
|
177
19
|
// ---------------------------------------------------------------------------
|
|
178
20
|
// Tool entry point
|
|
@@ -180,121 +22,44 @@ function formatTimestamp(seconds: number): string {
|
|
|
180
22
|
|
|
181
23
|
export async function run(
|
|
182
24
|
input: Record<string, unknown>,
|
|
183
|
-
|
|
25
|
+
context: ToolContext,
|
|
184
26
|
): Promise<ToolExecutionResult> {
|
|
185
|
-
const query = input.query as string | undefined;
|
|
186
|
-
if (!query) {
|
|
187
|
-
return { content: 'query is required.', isError: true };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
27
|
const assetId = input.asset_id as string | undefined;
|
|
191
28
|
if (!assetId) {
|
|
192
|
-
return { content: 'asset_id is required
|
|
29
|
+
return { content: 'asset_id is required.', isError: true };
|
|
193
30
|
}
|
|
194
31
|
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const userLimit = filters.limit;
|
|
199
|
-
const result = retrieveEvents({ ...filters, limit: 0 });
|
|
200
|
-
|
|
201
|
-
// Determine which capabilities are allowed based on the tracking profile
|
|
202
|
-
const profile = getTrackingProfile(assetId);
|
|
203
|
-
|
|
204
|
-
let allowedEventTypes: Set<string> | null = null;
|
|
205
|
-
const tierForEventType = new Map<string, CapabilityTier>();
|
|
206
|
-
|
|
207
|
-
if (profile) {
|
|
208
|
-
// Use the explicit profile: only enabled capabilities pass
|
|
209
|
-
allowedEventTypes = new Set<string>();
|
|
210
|
-
for (const [capName, entry] of Object.entries(profile.capabilities)) {
|
|
211
|
-
if (entry.enabled) {
|
|
212
|
-
allowedEventTypes.add(capName);
|
|
213
|
-
tierForEventType.set(capName, entry.tier);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
// No profile: default to 'ready' tier capabilities only
|
|
218
|
-
const readyCaps = getCapabilitiesByTier('ready');
|
|
219
|
-
if (readyCaps.length > 0) {
|
|
220
|
-
allowedEventTypes = new Set(readyCaps.map((c) => c.name));
|
|
221
|
-
for (const cap of readyCaps) {
|
|
222
|
-
tierForEventType.set(cap.name, 'ready');
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// If no capabilities are registered at all, allow everything (pass null)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Filter events by allowed capabilities, then apply the user-requested limit
|
|
229
|
-
let filteredEvents = result.events;
|
|
230
|
-
if (allowedEventTypes !== null) {
|
|
231
|
-
filteredEvents = filteredEvents.filter((e) => allowedEventTypes!.has(e.eventType));
|
|
232
|
-
}
|
|
233
|
-
if (userLimit && filteredEvents.length > userLimit) {
|
|
234
|
-
filteredEvents = filteredEvents.slice(0, userLimit);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (filteredEvents.length === 0) {
|
|
238
|
-
const parts = ['No matching events found'];
|
|
239
|
-
if (filters.eventType) parts.push(`for event type "${filters.eventType}"`);
|
|
240
|
-
if (filters.minConfidence) parts.push(`with confidence >= ${filters.minConfidence}`);
|
|
241
|
-
if (!profile) parts.push('(defaulting to ready-tier capabilities only)');
|
|
242
|
-
parts.push(`in asset ${assetId}.`);
|
|
243
|
-
return { content: parts.join(' '), isError: false };
|
|
32
|
+
const query = input.query as string | undefined;
|
|
33
|
+
if (!query) {
|
|
34
|
+
return { content: 'query is required.', isError: true };
|
|
244
35
|
}
|
|
245
36
|
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
beta: '[Beta]',
|
|
249
|
-
experimental: '[Experimental]',
|
|
250
|
-
};
|
|
37
|
+
const systemPrompt = input.system_prompt as string | undefined;
|
|
38
|
+
const model = input.model as string | undefined;
|
|
251
39
|
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
40
|
+
const options: ReduceOptions = {
|
|
41
|
+
query,
|
|
42
|
+
systemPrompt,
|
|
43
|
+
model,
|
|
255
44
|
};
|
|
256
45
|
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
const tierLabel = tier ? tierLabels[tier] ?? `[${tier}]` : '[Ready]';
|
|
260
|
-
const disclaimer = tier ? tierDisclaimers[tier] : undefined;
|
|
46
|
+
try {
|
|
47
|
+
const result: ReduceResult = await reduceForAsset(assetId, options, context.onOutput);
|
|
261
48
|
|
|
262
49
|
return {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
metadata: e.metadata,
|
|
50
|
+
content: JSON.stringify({
|
|
51
|
+
query,
|
|
52
|
+
answer: result.answer,
|
|
53
|
+
model: result.model,
|
|
54
|
+
usage: {
|
|
55
|
+
inputTokens: result.inputTokens,
|
|
56
|
+
outputTokens: result.outputTokens,
|
|
57
|
+
},
|
|
58
|
+
}, null, 2),
|
|
59
|
+
isError: false,
|
|
274
60
|
};
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const disclaimers: string[] = [];
|
|
280
|
-
if (activeTiers.has('[Beta]')) disclaimers.push(tierDisclaimers.beta);
|
|
281
|
-
if (activeTiers.has('[Experimental]')) disclaimers.push(tierDisclaimers.experimental);
|
|
282
|
-
|
|
283
|
-
return {
|
|
284
|
-
content: JSON.stringify({
|
|
285
|
-
query,
|
|
286
|
-
parsedFilters: {
|
|
287
|
-
eventType: filters.eventType ?? null,
|
|
288
|
-
minConfidence: filters.minConfidence ?? null,
|
|
289
|
-
limit: filters.limit,
|
|
290
|
-
startTimeMin: filters.startTimeMin ?? null,
|
|
291
|
-
startTimeMax: filters.startTimeMax ?? null,
|
|
292
|
-
},
|
|
293
|
-
trackingProfile: profile ? 'custom' : 'default (ready tier only)',
|
|
294
|
-
...(disclaimers.length > 0 ? { disclaimers } : {}),
|
|
295
|
-
totalResults: eventSummaries.length,
|
|
296
|
-
events: eventSummaries,
|
|
297
|
-
}, null, 2),
|
|
298
|
-
isError: false,
|
|
299
|
-
};
|
|
61
|
+
} catch (err) {
|
|
62
|
+
const msg = (err as Error).message;
|
|
63
|
+
return { content: msg, isError: true };
|
|
64
|
+
}
|
|
300
65
|
}
|
|
@@ -20,7 +20,12 @@ Gmail, Slack, and Telegram setup all require a publicly reachable URL for OAuth
|
|
|
20
20
|
2. **If it fails because no client_id is found:** The user needs to create Google Cloud OAuth credentials first. Install and load the **google-oauth-setup** skill (which depends on **public-ingress** for the redirect URI):
|
|
21
21
|
- Call `vellum_skills_catalog` with `action: "install"` and `skill_id: "google-oauth-setup"`.
|
|
22
22
|
- Then call `skill_load` with `skill: "google-oauth-setup"`.
|
|
23
|
-
- Tell the user
|
|
23
|
+
- Tell the user Gmail isn't connected yet and briefly explain what the setup involves, then use `ui_show` with `surface_type: "confirmation"` to ask for permission to start:
|
|
24
|
+
- **message:** "Ready to set up Gmail?"
|
|
25
|
+
- **detail:** "I'll open a browser where you sign in to Google, then automate everything else — creating a project, enabling APIs, and connecting your account. Takes 2-3 minutes and you can watch in the browser preview panel."
|
|
26
|
+
- **confirmLabel:** "Get Started"
|
|
27
|
+
- **cancelLabel:** "Not Now"
|
|
28
|
+
- If the user confirms, briefly acknowledge (e.g., "Setting up Gmail now...") and proceed with the setup guide. If they decline, acknowledge and let them know they can set it up later.
|
|
24
29
|
3. **If the user provides a client_id directly in chat:** Call `credential_store` with `action: "oauth2_connect"`, `service: "gmail"`, and `client_id: "<their value>"`. Include `client_secret` too if they provide one. Everything else is auto-filled.
|
|
25
30
|
|
|
26
31
|
### Slack
|
|
@@ -28,7 +33,12 @@ Gmail, Slack, and Telegram setup all require a publicly reachable URL for OAuth
|
|
|
28
33
|
2. **If it fails because no client_id is found:** The user needs to create a Slack App first. Install and load the **slack-oauth-setup** skill (which depends on **public-ingress** for the redirect URI):
|
|
29
34
|
- Call `vellum_skills_catalog` with `action: "install"` and `skill_id: "slack-oauth-setup"`.
|
|
30
35
|
- Then call `skill_load` with `skill: "slack-oauth-setup"`.
|
|
31
|
-
- Tell the user
|
|
36
|
+
- Tell the user Slack isn't connected yet and briefly explain what the setup involves, then use `ui_show` with `surface_type: "confirmation"` to ask for permission to start:
|
|
37
|
+
- **message:** "Ready to set up Slack?"
|
|
38
|
+
- **detail:** "I'll walk you through creating a Slack App and connecting your workspace. The process takes a few minutes, and I'll ask for your approval before each step."
|
|
39
|
+
- **confirmLabel:** "Get Started"
|
|
40
|
+
- **cancelLabel:** "Not Now"
|
|
41
|
+
- Wait for the user to confirm before proceeding with the setup guide. If they decline, acknowledge and let them know they can set it up later.
|
|
32
42
|
3. **If the user provides client_id and client_secret directly in chat:** Call `credential_store` with `action: "oauth2_connect"`, `service: "slack"`, `client_id`, and `client_secret`. Everything else is auto-filled. Note: Slack always requires a client_secret.
|
|
33
43
|
|
|
34
44
|
### Telegram
|
|
@@ -7,12 +7,9 @@ import { memoryItems } from '../../../../memory/schema.js';
|
|
|
7
7
|
import { enqueueMemoryJob } from '../../../../memory/jobs-store.js';
|
|
8
8
|
import { extractStylePatterns } from '../../../../messaging/style-analyzer.js';
|
|
9
9
|
import { truncate } from '../../../../util/truncate.js';
|
|
10
|
+
import { clampUnitInterval } from '../../../../memory/validation.js';
|
|
10
11
|
import { resolveProvider, withProviderToken, ok, err } from './shared.js';
|
|
11
12
|
|
|
12
|
-
function clamp(value: number, min: number, max: number): number {
|
|
13
|
-
return Math.min(max, Math.max(min, value));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
13
|
function upsertMemoryItem(opts: {
|
|
17
14
|
kind: string;
|
|
18
15
|
subject: string;
|
|
@@ -35,7 +32,7 @@ function upsertMemoryItem(opts: {
|
|
|
35
32
|
.set({
|
|
36
33
|
statement: opts.statement,
|
|
37
34
|
status: 'active',
|
|
38
|
-
importance: Math.max(existing.importance ?? 0, opts.importance),
|
|
35
|
+
importance: clampUnitInterval(Math.max(existing.importance ?? 0, opts.importance)),
|
|
39
36
|
lastSeenAt: now,
|
|
40
37
|
verificationState: 'assistant_inferred',
|
|
41
38
|
})
|
|
@@ -51,7 +48,7 @@ function upsertMemoryItem(opts: {
|
|
|
51
48
|
statement: opts.statement,
|
|
52
49
|
status: 'active',
|
|
53
50
|
confidence: 0.8,
|
|
54
|
-
importance: opts.importance,
|
|
51
|
+
importance: clampUnitInterval(opts.importance),
|
|
55
52
|
fingerprint,
|
|
56
53
|
verificationState: 'assistant_inferred',
|
|
57
54
|
scopeId: opts.scopeId,
|
|
@@ -90,7 +87,7 @@ export async function run(input: Record<string, unknown>, context: ToolContext):
|
|
|
90
87
|
|
|
91
88
|
for (const pattern of result.stylePatterns) {
|
|
92
89
|
const subject = `${provider.id} writing style: ${pattern.aspect}`;
|
|
93
|
-
const importance =
|
|
90
|
+
const importance = clampUnitInterval(Math.min(0.85, Math.max(0.55, pattern.importance ?? 0.65)));
|
|
94
91
|
upsertMemoryItem({ kind: 'style', subject, statement: pattern.summary, importance, scopeId });
|
|
95
92
|
savedCount++;
|
|
96
93
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ToolContext, ToolExecutionResult } from '../../../../tools/types.js';
|
|
2
2
|
import { resolveProvider, withProviderToken, ok, err } from './shared.js';
|
|
3
3
|
|
|
4
|
-
export async function run(input: Record<string, unknown>,
|
|
4
|
+
export async function run(input: Record<string, unknown>, context: ToolContext): Promise<ToolExecutionResult> {
|
|
5
5
|
const platform = input.platform as string | undefined;
|
|
6
6
|
const conversationId = input.conversation_id as string;
|
|
7
7
|
const threadId = input.thread_id as string;
|
|
@@ -22,6 +22,7 @@ export async function run(input: Record<string, unknown>, _context: ToolContext)
|
|
|
22
22
|
return withProviderToken(provider, async (token) => {
|
|
23
23
|
const result = await provider.sendMessage(token, conversationId, text, {
|
|
24
24
|
threadId,
|
|
25
|
+
assistantId: context.assistantId,
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
return ok(`Reply sent (ID: ${result.id}).`);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Phone Calls"
|
|
3
|
-
description: "Set up Twilio for
|
|
3
|
+
description: "Set up Twilio for AI-powered voice calls — both outgoing calls on behalf of the user and incoming calls where the assistant answers as a receptionist"
|
|
4
4
|
user-invocable: true
|
|
5
5
|
metadata: {"vellum": {"emoji": "📞", "requires": {"config": ["calls.enabled"]}}}
|
|
6
6
|
includes: ["public-ingress"]
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
You are helping the user set up and
|
|
9
|
+
You are helping the user set up and manage phone calls via Twilio. This skill covers the full lifecycle: Twilio account setup, credential storage, public ingress configuration, enabling the calls feature, placing outbound calls, receiving inbound calls, and monitoring live transcripts.
|
|
10
10
|
|
|
11
11
|
## Prerequisites — Shared Twilio Setup
|
|
12
12
|
|
|
@@ -21,7 +21,9 @@ If Twilio is already configured (check `twilio_config` with `action: "get"`), sk
|
|
|
21
21
|
|
|
22
22
|
## Overview
|
|
23
23
|
|
|
24
|
-
The calling system uses Twilio's ConversationRelay
|
|
24
|
+
The calling system uses Twilio's ConversationRelay for both **outbound** and **inbound** voice calls. Twilio works out of the box as the default voice provider. Optionally, you can enable ElevenLabs integration for higher-quality, more natural-sounding voices — but this is entirely optional.
|
|
25
|
+
|
|
26
|
+
### Outbound calls
|
|
25
27
|
|
|
26
28
|
When a call is placed:
|
|
27
29
|
|
|
@@ -31,6 +33,17 @@ When a call is placed:
|
|
|
31
33
|
4. An LLM-driven orchestrator manages the conversation — receiving caller speech (transcribed by Deepgram), generating responses via Claude, and streaming text back for TTS playback
|
|
32
34
|
5. The transcript is relayed live to the user's conversation thread
|
|
33
35
|
|
|
36
|
+
### Inbound calls
|
|
37
|
+
|
|
38
|
+
When someone dials the assistant's Twilio phone number:
|
|
39
|
+
|
|
40
|
+
1. Twilio sends a voice webhook to the gateway at `/webhooks/twilio/voice` (no `callSessionId` in the URL)
|
|
41
|
+
2. The gateway resolves which assistant owns the dialed number via `resolveAssistantByPhoneNumber`, falling back to the standard routing chain (chat_id, user_id, default/reject). Unmapped numbers are rejected with TwiML `<Reject>`.
|
|
42
|
+
3. The runtime creates a new session keyed by the Twilio CallSid (`createInboundVoiceSession`)
|
|
43
|
+
4. Twilio opens a ConversationRelay WebSocket. The relay detects the call is inbound when `initiatedFromConversationId == null` and optionally gates the call behind **guardian voice verification** if a pending challenge exists.
|
|
44
|
+
5. Once verified (or if no challenge is pending), the LLM orchestrator greets the caller in a receptionist style: "Hello, this is [user]'s assistant. How can I help you today?"
|
|
45
|
+
6. The assistant converses naturally, using ASK_GUARDIAN to consult the user when needed, just like outbound calls.
|
|
46
|
+
|
|
34
47
|
Three voice quality modes are available:
|
|
35
48
|
- **`twilio_standard`** (default) — Fully supported. Standard Twilio TTS with Google voices. No extra setup required.
|
|
36
49
|
- **`twilio_elevenlabs_tts`** — Fully supported. Uses ElevenLabs voices through Twilio ConversationRelay for more natural speech.
|
|
@@ -181,6 +194,25 @@ credential_store action=store service=twilio field=user_phone_number value=+1415
|
|
|
181
194
|
| `calls.callerIdentity.allowPerCallOverride` | Whether per-call mode selection is allowed | `true` |
|
|
182
195
|
| `calls.callerIdentity.userNumber` | Optional E.164 phone number for user-number mode (alternative to storing via `credential_store`) | *(empty)* |
|
|
183
196
|
|
|
197
|
+
## DTMF Callee Verification
|
|
198
|
+
|
|
199
|
+
An optional verification step where the callee must enter a numeric code via their phone's keypad (DTMF tones) before the call proceeds. This ensures the intended person has answered the phone.
|
|
200
|
+
|
|
201
|
+
### How it works
|
|
202
|
+
|
|
203
|
+
1. When the call connects and DTMF verification is enabled, a random numeric code is generated (length configured by `calls.verification.codeLength`).
|
|
204
|
+
2. The verification code is shared with the guardian in the initiating conversation so they know what code was issued.
|
|
205
|
+
3. The AI voice agent speaks the code digit-by-digit to the callee and asks them to enter it on their keypad.
|
|
206
|
+
4. The callee enters the code via DTMF (phone keypad tones).
|
|
207
|
+
5. If the code matches, the call proceeds normally. If the code is incorrect, the agent may re-prompt or end the call depending on configuration.
|
|
208
|
+
|
|
209
|
+
### Configuration
|
|
210
|
+
|
|
211
|
+
| Setting | Description | Default |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| `calls.verification.enabled` | Enable DTMF callee verification | `false` |
|
|
214
|
+
| `calls.verification.codeLength` | Number of digits in the verification code | `6` |
|
|
215
|
+
|
|
184
216
|
## Optional: Higher Quality Voice with ElevenLabs
|
|
185
217
|
|
|
186
218
|
ElevenLabs integration is entirely optional. The standard Twilio-only setup works unchanged — this section is only relevant if you want to improve voice quality.
|
|
@@ -264,7 +296,7 @@ To go back to the default voice at any time:
|
|
|
264
296
|
vellum config set calls.voice.mode twilio_standard
|
|
265
297
|
```
|
|
266
298
|
|
|
267
|
-
## Making Calls
|
|
299
|
+
## Making Outbound Calls
|
|
268
300
|
|
|
269
301
|
Use the `call_start` tool to place outbound calls. Every call requires:
|
|
270
302
|
- **phone_number**: The number to call in E.164 format (e.g. `+14155551234`)
|
|
@@ -319,6 +351,30 @@ On Twilio trial accounts, outbound calls can ONLY be made to **verified numbers*
|
|
|
319
351
|
1. Tell the user they need to verify the number at https://console.twilio.com/us1/develop/phone-numbers/manage/verified
|
|
320
352
|
2. Or upgrade to a paid Twilio account to call any number
|
|
321
353
|
|
|
354
|
+
## Receiving Inbound Calls
|
|
355
|
+
|
|
356
|
+
Once Twilio is configured and the assistant has a phone number, inbound calls work automatically. When someone dials the assistant's number:
|
|
357
|
+
|
|
358
|
+
1. The gateway resolves the assistant by phone number and forwards to the runtime
|
|
359
|
+
2. A new voice session is created, keyed by the Twilio CallSid
|
|
360
|
+
3. The LLM-driven orchestrator answers in receptionist mode — greeting the caller warmly and asking how it can help
|
|
361
|
+
4. The conversation proceeds naturally, with ASK_GUARDIAN dispatches to consult the user when needed
|
|
362
|
+
|
|
363
|
+
No additional configuration is needed beyond the standard Twilio setup (Steps 1-5 above). As long as `calls.enabled` is `true` and the phone number has been provisioned/assigned, inbound calls are handled automatically.
|
|
364
|
+
|
|
365
|
+
### Guardian voice verification for inbound calls
|
|
366
|
+
|
|
367
|
+
Optionally, the user can require callers to verify themselves by entering a six-digit code before the call proceeds. This is managed through the **channel guardian verification** system:
|
|
368
|
+
|
|
369
|
+
1. The user initiates a verification challenge from the desktop UI for the `voice` channel
|
|
370
|
+
2. A six-digit code is generated and shown to the user
|
|
371
|
+
3. When the next inbound call arrives, the relay server detects the pending challenge and prompts the caller: "Please enter your six-digit verification code using your keypad, or speak the digits now."
|
|
372
|
+
4. The caller enters the code via DTMF (keypad) or by speaking the digits
|
|
373
|
+
5. If the code matches, a guardian binding is created and the call proceeds normally
|
|
374
|
+
6. If verification fails after 3 attempts, the call ends with "Verification failed. Goodbye."
|
|
375
|
+
|
|
376
|
+
This feature is separate from the outbound DTMF callee verification. It uses the `ChannelGuardianService` challenge system rather than the per-call verification config.
|
|
377
|
+
|
|
322
378
|
## Live Call Monitoring
|
|
323
379
|
|
|
324
380
|
### Showing the live transcript
|
|
@@ -343,37 +399,31 @@ By default, always show the live transcript of the call as it happens. When a ca
|
|
|
343
399
|
|
|
344
400
|
### Interacting with a live call
|
|
345
401
|
|
|
346
|
-
During an active call, the user can
|
|
347
|
-
|
|
348
|
-
#### Mode 1: Answering questions
|
|
402
|
+
During an active call, the user can interact with the AI voice agent via the HTTP API endpoints:
|
|
349
403
|
|
|
350
|
-
|
|
404
|
+
#### Answering questions
|
|
351
405
|
|
|
352
|
-
|
|
353
|
-
2. Present the question prominently to the user:
|
|
406
|
+
When the AI voice agent encounters something it needs user input for, it dispatches an **ASK_GUARDIAN** request to all configured guardian channels (mac desktop, Telegram, SMS). The call status changes to `waiting_on_user`.
|
|
354
407
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
3. The user replies directly in the chat — since there is a pending question, the reply is automatically routed as an **answer** to the AI voice agent
|
|
361
|
-
4. The AI voice agent receives the answer and continues the conversation naturally
|
|
408
|
+
1. The question is delivered simultaneously to every configured channel. The first channel to respond wins (first-response-wins semantics) -- once one channel provides an answer, the other channels receive a "already answered" notice.
|
|
409
|
+
2. On the mac desktop, a guardian request thread is created with the question. On Telegram/SMS, the question text and a request code are delivered via the gateway.
|
|
410
|
+
3. If DTMF callee verification is enabled, the callee must enter a verification code before the call proceeds (see the **DTMF Callee Verification** section above).
|
|
411
|
+
4. The guardian provides an answer through whichever channel they prefer. The answer is routed to the AI voice agent, which continues the conversation naturally.
|
|
362
412
|
|
|
363
413
|
**Important:** Respond to pending questions quickly. There is a consultation timeout (default: 2 minutes). If no answer is provided in time, the AI voice agent will move on.
|
|
364
414
|
|
|
365
|
-
####
|
|
415
|
+
#### Steering with instructions
|
|
366
416
|
|
|
367
|
-
When there is **no pending question** but the call is still active,
|
|
417
|
+
When there is **no pending question** but the call is still active, the user can send steering instructions via the HTTP API (`POST /v1/calls/:id/instruction`) to proactively guide the call in real time — for example:
|
|
368
418
|
|
|
369
419
|
- "Ask them about their cancellation policy too"
|
|
370
420
|
- "Wrap up the call, we have what we need"
|
|
371
421
|
- "Switch to asking about weekend availability instead"
|
|
372
422
|
- "Be more assertive about getting a discount"
|
|
373
423
|
|
|
374
|
-
The instruction is injected into the AI voice agent's conversation context as high-priority input, and the agent adjusts its behavior accordingly.
|
|
424
|
+
The instruction is injected into the AI voice agent's conversation context as high-priority input, and the agent adjusts its behavior accordingly.
|
|
375
425
|
|
|
376
|
-
**
|
|
426
|
+
**Note:** Mid-call steering via the desktop chat thread is no longer supported. The desktop thread only receives pointer/status messages about the call. To steer a call, use the HTTP API endpoints directly.
|
|
377
427
|
|
|
378
428
|
### Call status values
|
|
379
429
|
|
|
@@ -414,6 +464,7 @@ The `context` field is powerful — use it to give the agent background that hel
|
|
|
414
464
|
|
|
415
465
|
### Things the AI voice agent handles well
|
|
416
466
|
|
|
467
|
+
**Outbound calls:**
|
|
417
468
|
- Making reservations and appointments
|
|
418
469
|
- Checking business hours, availability, or pricing
|
|
419
470
|
- Confirming or rescheduling existing appointments
|
|
@@ -421,6 +472,12 @@ The `context` field is powerful — use it to give the agent background that hel
|
|
|
421
472
|
- Simple customer service interactions
|
|
422
473
|
- Leaving voicemails (it will speak the message if voicemail picks up)
|
|
423
474
|
|
|
475
|
+
**Inbound calls:**
|
|
476
|
+
- Answering as a receptionist and routing caller requests to the user via ASK_GUARDIAN
|
|
477
|
+
- Taking messages when the user is unavailable
|
|
478
|
+
- Answering questions the assistant already knows from memory/context
|
|
479
|
+
- Screening calls with guardian voice verification
|
|
480
|
+
|
|
424
481
|
### Things to be aware of
|
|
425
482
|
|
|
426
483
|
- Calls have a maximum duration (configurable via `calls.maxDurationSeconds`, default: 1 hour)
|
|
@@ -470,6 +527,14 @@ vellum config set calls.disclosure.text "Just so you know, this is an assistant
|
|
|
470
527
|
vellum config set calls.userConsultTimeoutSeconds 300
|
|
471
528
|
```
|
|
472
529
|
|
|
530
|
+
## Accepted Regressions
|
|
531
|
+
|
|
532
|
+
The following behavioral changes were introduced with the cross-channel guardian architecture (voice-cross-guardian):
|
|
533
|
+
|
|
534
|
+
- **No more mid-call steering via desktop chat.** The call bridge that routed desktop chat messages to the active call has been removed. The desktop chat thread only receives pointer/status messages about the call. To steer a call, use the HTTP API endpoints directly (`POST /v1/calls/:id/instruction`).
|
|
535
|
+
- **No live transcript mirror in the initiating chat.** The initiating desktop conversation no longer receives a real-time mirror of the call transcript. The initiating chat only gets pointer/status messages (call started, call ended, question asked, etc.).
|
|
536
|
+
- **Guardian questions are dispatched cross-channel.** Rather than appearing only in the initiating desktop thread, ASK_GUARDIAN questions are now dispatched to all configured guardian channels (mac desktop, Telegram, SMS) simultaneously. The first channel to respond wins.
|
|
537
|
+
|
|
473
538
|
## Troubleshooting
|
|
474
539
|
|
|
475
540
|
### "Twilio credentials not configured"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="16" height="16" fill="#ffffff"/>
|
|
3
|
+
<rect x="2" y="2" width="12" height="12" fill="#000000"/>
|
|
4
|
+
<rect x="4" y="5" width="2" height="1" fill="#ffffff"/>
|
|
5
|
+
<rect x="5" y="4" width="1" height="3" fill="#ffffff"/>
|
|
6
|
+
<rect x="6" y="5" width="2" height="1" fill="#ffffff"/>
|
|
7
|
+
<rect x="10" y="5" width="2" height="1" fill="#ffffff"/>
|
|
8
|
+
<rect x="11" y="4" width="1" height="3" fill="#ffffff"/>
|
|
9
|
+
<rect x="12" y="5" width="2" height="1" fill="#ffffff"/>
|
|
10
|
+
<rect x="4" y="9" width="1" height="2" fill="#ffffff"/>
|
|
11
|
+
<rect x="5" y="8" width="6" height="1" fill="#ffffff"/>
|
|
12
|
+
<rect x="11" y="9" width="1" height="2" fill="#ffffff"/>
|
|
13
|
+
<rect x="5" y="11" width="6" height="1" fill="#ffffff"/>
|
|
14
|
+
</svg>
|