@vellumai/assistant 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +26 -2
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +156 -1137
- 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 +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -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 +54 -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 +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +60 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +226 -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 +96 -4
- package/src/daemon/session-runtime-assembly.ts +149 -15
- package/src/daemon/session-surfaces.ts +19 -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 +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +160 -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 +119 -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/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 +275 -743
- 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 +143 -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 +10 -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 +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +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 +19 -17
- 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
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* Parses inbound user text to determine whether it matches an approval,
|
|
5
5
|
* rejection, or "approve always" intent. This module is transport-agnostic
|
|
6
6
|
* and can be used by any channel adapter (Telegram, SMS, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Both the standard and guardian approval flows now use the conversational
|
|
9
|
+
* approval engine as the primary classifier. This deterministic parser is
|
|
10
|
+
* retained only as a legacy fallback for when the conversational engine is
|
|
11
|
+
* not injected (i.e. approvalConversationGenerator is undefined).
|
|
7
12
|
*/
|
|
8
13
|
|
|
9
14
|
import type { ApprovalAction, ApprovalDecisionResult } from './channel-approval-types.js';
|
|
@@ -44,6 +49,30 @@ const PHRASE_MAP = buildPhraseMap();
|
|
|
44
49
|
// Public API
|
|
45
50
|
// ---------------------------------------------------------------------------
|
|
46
51
|
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Run-reference tag extraction
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Pattern matching a `[ref:<runId>]` disambiguation tag appended to
|
|
58
|
+
* plain-text approval prompts. Guardians can include this tag in their
|
|
59
|
+
* reply so that `handleApprovalInterception` can resolve the correct
|
|
60
|
+
* pending approval when multiple approvals target the same chat.
|
|
61
|
+
*/
|
|
62
|
+
const REF_TAG_RE = /\[ref:([^\]]+)\]/i;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Extract a run-reference tag from the text and return the cleaned
|
|
66
|
+
* decision text plus the extracted runId (if any).
|
|
67
|
+
*/
|
|
68
|
+
function extractRefTag(text: string): { cleaned: string; runId?: string } {
|
|
69
|
+
const match = REF_TAG_RE.exec(text);
|
|
70
|
+
if (!match) return { cleaned: text };
|
|
71
|
+
const runId = match[1].trim();
|
|
72
|
+
const cleaned = text.replace(REF_TAG_RE, '').trim();
|
|
73
|
+
return { cleaned, runId: runId || undefined };
|
|
74
|
+
}
|
|
75
|
+
|
|
47
76
|
/**
|
|
48
77
|
* Parse a plain-text message into an approval decision.
|
|
49
78
|
*
|
|
@@ -51,10 +80,15 @@ const PHRASE_MAP = buildPhraseMap();
|
|
|
51
80
|
* of the known intent phrases, or `null` if it does not match.
|
|
52
81
|
*
|
|
53
82
|
* Matching is case-insensitive with leading/trailing whitespace trimmed.
|
|
83
|
+
*
|
|
84
|
+
* When the text contains a `[ref:<runId>]` tag (appended by the
|
|
85
|
+
* plain-text fallback path), the extracted runId is included in the
|
|
86
|
+
* result so the caller can disambiguate among multiple pending approvals.
|
|
54
87
|
*/
|
|
55
88
|
export function parseApprovalDecision(text: string): ApprovalDecisionResult | null {
|
|
56
|
-
const
|
|
89
|
+
const { cleaned, runId } = extractRefTag(text);
|
|
90
|
+
const normalised = cleaned.trim().toLowerCase();
|
|
57
91
|
const action = PHRASE_MAP.get(normalised);
|
|
58
92
|
if (!action) return null;
|
|
59
|
-
return { action, source: 'plain_text' };
|
|
93
|
+
return { action, source: 'plain_text', ...(runId ? { runId } : {}) };
|
|
60
94
|
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* 1. Detect pending confirmations for a conversation
|
|
8
8
|
* 2. Build human-readable approval prompts with action buttons
|
|
9
9
|
* 3. Consume user decisions and apply them to the underlying run
|
|
10
|
-
* 4. Build reminder prompts when non-decision messages arrive
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
12
|
import { getPendingConfirmationsByConversation, getRun } from '../memory/runs-store.js';
|
|
@@ -206,23 +205,3 @@ const RICH_APPROVAL_CHANNELS: ReadonlySet<string> = new Set(['telegram']);
|
|
|
206
205
|
export function channelSupportsRichApprovalUI(channel: string): boolean {
|
|
207
206
|
return RICH_APPROVAL_CHANNELS.has(channel);
|
|
208
207
|
}
|
|
209
|
-
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
// 6. Reminder prompt for non-decision messages
|
|
212
|
-
// ---------------------------------------------------------------------------
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Build a reminder prompt when the user sends a non-decision message while
|
|
216
|
-
* an approval is pending. Reuses the original actions and fallback text
|
|
217
|
-
* but prefixes the prompt text with a reminder.
|
|
218
|
-
*/
|
|
219
|
-
export function buildReminderPrompt(
|
|
220
|
-
pendingPrompt: ChannelApprovalPrompt,
|
|
221
|
-
): ChannelApprovalPrompt {
|
|
222
|
-
const reminderPrefix = composeApprovalMessage({ scenario: 'reminder_prompt' });
|
|
223
|
-
return {
|
|
224
|
-
promptText: `${reminderPrefix}\n\n${pendingPrompt.promptText}`,
|
|
225
|
-
actions: pendingPrompt.actions,
|
|
226
|
-
plainTextFallback: `${reminderPrefix}\n\n${pendingPrompt.plainTextFallback}`,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
@@ -12,14 +12,16 @@ import {
|
|
|
12
12
|
createBinding,
|
|
13
13
|
getActiveBinding,
|
|
14
14
|
revokeBinding as storeRevokeBinding,
|
|
15
|
+
revokePendingChallenges as storeRevokePendingChallenges,
|
|
15
16
|
createChallenge,
|
|
16
17
|
findPendingChallengeByHash,
|
|
18
|
+
findPendingChallengeForChannel,
|
|
17
19
|
consumeChallenge,
|
|
18
20
|
getRateLimit,
|
|
19
21
|
recordInvalidAttempt,
|
|
20
22
|
resetRateLimit,
|
|
21
23
|
} from '../memory/channel-guardian-store.js';
|
|
22
|
-
import type { GuardianBinding } from '../memory/channel-guardian-store.js';
|
|
24
|
+
import type { GuardianBinding, VerificationChallenge } from '../memory/channel-guardian-store.js';
|
|
23
25
|
import { composeApprovalMessage } from './approval-message-composer.js';
|
|
24
26
|
|
|
25
27
|
// ---------------------------------------------------------------------------
|
|
@@ -66,19 +68,33 @@ function hashSecret(secret: string): string {
|
|
|
66
68
|
// Service
|
|
67
69
|
// ---------------------------------------------------------------------------
|
|
68
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Generate a six-digit numeric secret for voice channel challenges.
|
|
73
|
+
* Uses cryptographic randomness to pick a number in [100000, 999999].
|
|
74
|
+
*/
|
|
75
|
+
function generateVoiceSecret(): string {
|
|
76
|
+
const buf = randomBytes(4);
|
|
77
|
+
const num = buf.readUInt32BE(0);
|
|
78
|
+
// Map to the range [100000, 999999] (900000 possible values)
|
|
79
|
+
return String(100000 + (num % 900000));
|
|
80
|
+
}
|
|
81
|
+
|
|
69
82
|
/**
|
|
70
83
|
* Create a new verification challenge for a guardian candidate.
|
|
71
84
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
85
|
+
* For voice channels, generates a six-digit numeric secret that can be
|
|
86
|
+
* spoken aloud. For all other channels, generates a 32-byte hex secret.
|
|
87
|
+
*
|
|
88
|
+
* Hashes the secret (SHA-256) and stores the challenge record with a
|
|
89
|
+
* 10-minute TTL. The raw secret is returned so it can be displayed to
|
|
90
|
+
* the user; only the hash is persisted.
|
|
75
91
|
*/
|
|
76
92
|
export function createVerificationChallenge(
|
|
77
93
|
assistantId: string,
|
|
78
94
|
channel: string,
|
|
79
95
|
sessionId?: string,
|
|
80
96
|
): CreateChallengeResult {
|
|
81
|
-
const secret = randomBytes(32).toString('hex');
|
|
97
|
+
const secret = channel === 'voice' ? generateVoiceSecret() : randomBytes(32).toString('hex');
|
|
82
98
|
const challengeHash = hashSecret(secret);
|
|
83
99
|
const challengeId = uuid();
|
|
84
100
|
const expiresAt = Date.now() + CHALLENGE_TTL_MS;
|
|
@@ -102,6 +118,7 @@ export function createVerificationChallenge(
|
|
|
102
118
|
ttlSeconds,
|
|
103
119
|
instruction: composeApprovalMessage({
|
|
104
120
|
scenario: 'guardian_verify_challenge_setup',
|
|
121
|
+
channel,
|
|
105
122
|
verifyCommand,
|
|
106
123
|
ttlSeconds,
|
|
107
124
|
}),
|
|
@@ -131,7 +148,7 @@ export function validateAndConsumeChallenge(
|
|
|
131
148
|
): ValidateChallengeResult {
|
|
132
149
|
// ── Rate-limit check ──
|
|
133
150
|
const existing = getRateLimit(assistantId, channel, actorExternalUserId, actorChatId);
|
|
134
|
-
if (existing && existing.lockedUntil
|
|
151
|
+
if (existing && existing.lockedUntil != null && Date.now() < existing.lockedUntil) {
|
|
135
152
|
// Use the same generic failure message to avoid leaking whether the
|
|
136
153
|
// actor is rate-limited vs. the code is genuinely wrong.
|
|
137
154
|
return {
|
|
@@ -224,7 +241,7 @@ export function isGuardian(
|
|
|
224
241
|
externalUserId: string,
|
|
225
242
|
): boolean {
|
|
226
243
|
const binding = getActiveBinding(assistantId, channel);
|
|
227
|
-
return binding
|
|
244
|
+
return binding != null && binding.guardianExternalUserId === externalUserId;
|
|
228
245
|
}
|
|
229
246
|
|
|
230
247
|
/**
|
|
@@ -236,3 +253,27 @@ export function revokeBinding(
|
|
|
236
253
|
): boolean {
|
|
237
254
|
return storeRevokeBinding(assistantId, channel);
|
|
238
255
|
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Revoke all pending challenges for a given assistant and channel.
|
|
259
|
+
* Called when the user cancels verification so that stale challenges
|
|
260
|
+
* don't gate inbound calls.
|
|
261
|
+
*/
|
|
262
|
+
export function revokePendingChallenges(
|
|
263
|
+
assistantId: string,
|
|
264
|
+
channel: string,
|
|
265
|
+
): void {
|
|
266
|
+
storeRevokePendingChallenges(assistantId, channel);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Look up a pending (non-expired) verification challenge for a given
|
|
271
|
+
* assistant and channel. Used by relay setup to detect whether an active
|
|
272
|
+
* voice verification session exists.
|
|
273
|
+
*/
|
|
274
|
+
export function getPendingChallenge(
|
|
275
|
+
assistantId: string,
|
|
276
|
+
channel: string,
|
|
277
|
+
): VerificationChallenge | null {
|
|
278
|
+
return findPendingChallengeForChannel(assistantId, channel);
|
|
279
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ChannelId,
|
|
3
3
|
ChannelProbe,
|
|
4
|
+
ChannelProbeContext,
|
|
4
5
|
ChannelReadinessSnapshot,
|
|
5
6
|
ReadinessCheckResult,
|
|
6
7
|
} from './channel-readiness-types.js';
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
} from '../calls/twilio-rest.js';
|
|
12
13
|
import { getSecureKey } from '../security/secure-keys.js';
|
|
13
14
|
import { loadRawConfig } from '../config/loader.js';
|
|
15
|
+
import { getTwilioPhoneNumberEnv } from '../config/env.js';
|
|
14
16
|
|
|
15
17
|
/** Remote check results are cached for 5 minutes before being considered stale. */
|
|
16
18
|
export const REMOTE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -29,9 +31,54 @@ function hasIngressConfigured(): boolean {
|
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
function getAssistantMappedPhoneNumber(
|
|
35
|
+
smsConfig: Record<string, unknown>,
|
|
36
|
+
assistantId?: string,
|
|
37
|
+
): string | undefined {
|
|
38
|
+
if (!assistantId) return undefined;
|
|
39
|
+
const mapping = (smsConfig.assistantPhoneNumbers as Record<string, string> | undefined) ?? {};
|
|
40
|
+
return mapping[assistantId];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function hasAnyAssistantMappedPhoneNumber(smsConfig: Record<string, unknown>): boolean {
|
|
44
|
+
const mapping = (smsConfig.assistantPhoneNumbers as Record<string, string> | undefined) ?? {};
|
|
45
|
+
return Object.keys(mapping).length > 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function hasAnyAssistantMappedPhoneNumberSafe(): boolean {
|
|
49
|
+
try {
|
|
50
|
+
const raw = loadRawConfig();
|
|
51
|
+
const smsConfig = (raw?.sms ?? {}) as Record<string, unknown>;
|
|
52
|
+
return hasAnyAssistantMappedPhoneNumber(smsConfig);
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Resolve SMS from-number with canonical precedence:
|
|
60
|
+
* assistant mapping -> env override -> config sms.phoneNumber -> secure key fallback.
|
|
61
|
+
*/
|
|
62
|
+
function resolveSmsPhoneNumber(assistantId?: string): string {
|
|
63
|
+
try {
|
|
64
|
+
const raw = loadRawConfig();
|
|
65
|
+
const smsConfig = (raw?.sms ?? {}) as Record<string, unknown>;
|
|
66
|
+
const mapped = getAssistantMappedPhoneNumber(smsConfig, assistantId);
|
|
67
|
+
return mapped
|
|
68
|
+
|| getTwilioPhoneNumberEnv()
|
|
69
|
+
|| (smsConfig.phoneNumber as string)
|
|
70
|
+
|| getSecureKey('credential:twilio:phone_number')
|
|
71
|
+
|| '';
|
|
72
|
+
} catch {
|
|
73
|
+
return getTwilioPhoneNumberEnv()
|
|
74
|
+
|| getSecureKey('credential:twilio:phone_number')
|
|
75
|
+
|| '';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
32
79
|
const smsProbe: ChannelProbe = {
|
|
33
80
|
channel: 'sms',
|
|
34
|
-
runLocalChecks(): ReadinessCheckResult[] {
|
|
81
|
+
runLocalChecks(context?: ChannelProbeContext): ReadinessCheckResult[] {
|
|
35
82
|
const results: ReadinessCheckResult[] = [];
|
|
36
83
|
|
|
37
84
|
const hasCreds = hasTwilioCredentials();
|
|
@@ -43,23 +90,18 @@ const smsProbe: ChannelProbe = {
|
|
|
43
90
|
: 'Twilio Account SID and Auth Token are not configured',
|
|
44
91
|
});
|
|
45
92
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const raw = loadRawConfig();
|
|
50
|
-
const smsConfig = (raw?.sms ?? {}) as Record<string, unknown>;
|
|
51
|
-
hasPhone = !!(smsConfig.phoneNumber as string);
|
|
52
|
-
} catch { /* ignore */ }
|
|
53
|
-
}
|
|
54
|
-
if (!hasPhone) {
|
|
55
|
-
hasPhone = !!getSecureKey('credential:twilio:phone_number');
|
|
56
|
-
}
|
|
93
|
+
const resolvedNumber = resolveSmsPhoneNumber(context?.assistantId);
|
|
94
|
+
const hasPhone = !!resolvedNumber || (!context?.assistantId && hasAnyAssistantMappedPhoneNumberSafe());
|
|
57
95
|
results.push({
|
|
58
96
|
name: 'phone_number',
|
|
59
97
|
passed: hasPhone,
|
|
60
98
|
message: hasPhone
|
|
61
|
-
?
|
|
62
|
-
|
|
99
|
+
? (context?.assistantId && !resolvedNumber
|
|
100
|
+
? `Assistant ${context.assistantId} has no direct mapping, but SMS phone numbers are assigned`
|
|
101
|
+
: 'Phone number is assigned')
|
|
102
|
+
: (context?.assistantId
|
|
103
|
+
? `No phone number assigned for assistant ${context.assistantId}`
|
|
104
|
+
: 'No phone number assigned'),
|
|
63
105
|
});
|
|
64
106
|
|
|
65
107
|
const hasIngress = hasIngressConfigured();
|
|
@@ -73,20 +115,14 @@ const smsProbe: ChannelProbe = {
|
|
|
73
115
|
|
|
74
116
|
return results;
|
|
75
117
|
},
|
|
76
|
-
async runRemoteChecks(): Promise<ReadinessCheckResult[]> {
|
|
118
|
+
async runRemoteChecks(context?: ChannelProbeContext): Promise<ReadinessCheckResult[]> {
|
|
77
119
|
if (!hasTwilioCredentials()) return [];
|
|
78
120
|
|
|
79
121
|
const accountSid = getSecureKey('credential:twilio:account_sid');
|
|
80
122
|
const authToken = getSecureKey('credential:twilio:auth_token');
|
|
81
123
|
if (!accountSid || !authToken) return [];
|
|
82
124
|
|
|
83
|
-
|
|
84
|
-
const raw = loadRawConfig();
|
|
85
|
-
const smsConfig = (raw?.sms ?? {}) as Record<string, unknown>;
|
|
86
|
-
const phoneNumber = (smsConfig.phoneNumber as string)
|
|
87
|
-
|| getSecureKey('credential:twilio:phone_number')
|
|
88
|
-
|| process.env.TWILIO_PHONE_NUMBER
|
|
89
|
-
|| '';
|
|
125
|
+
const phoneNumber = resolveSmsPhoneNumber(context?.assistantId);
|
|
90
126
|
if (!phoneNumber) return [];
|
|
91
127
|
|
|
92
128
|
// Only toll-free numbers need verification checks
|
|
@@ -130,6 +166,73 @@ const smsProbe: ChannelProbe = {
|
|
|
130
166
|
},
|
|
131
167
|
};
|
|
132
168
|
|
|
169
|
+
// ── Voice Probe ─────────────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Resolve voice from-number with the same precedence as SMS:
|
|
173
|
+
* assistant mapping -> env override -> config sms.phoneNumber -> secure key fallback.
|
|
174
|
+
*
|
|
175
|
+
* Voice and SMS share the same Twilio phone number infrastructure, so the
|
|
176
|
+
* resolution logic is identical to resolveSmsPhoneNumber.
|
|
177
|
+
*/
|
|
178
|
+
function resolveVoicePhoneNumber(assistantId?: string): string {
|
|
179
|
+
try {
|
|
180
|
+
const raw = loadRawConfig();
|
|
181
|
+
const smsConfig = (raw?.sms ?? {}) as Record<string, unknown>;
|
|
182
|
+
const mapped = getAssistantMappedPhoneNumber(smsConfig, assistantId);
|
|
183
|
+
return mapped
|
|
184
|
+
|| getTwilioPhoneNumberEnv()
|
|
185
|
+
|| (smsConfig.phoneNumber as string)
|
|
186
|
+
|| getSecureKey('credential:twilio:phone_number')
|
|
187
|
+
|| '';
|
|
188
|
+
} catch {
|
|
189
|
+
return getTwilioPhoneNumberEnv()
|
|
190
|
+
|| getSecureKey('credential:twilio:phone_number')
|
|
191
|
+
|| '';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const voiceProbe: ChannelProbe = {
|
|
196
|
+
channel: 'voice',
|
|
197
|
+
runLocalChecks(context?: ChannelProbeContext): ReadinessCheckResult[] {
|
|
198
|
+
const results: ReadinessCheckResult[] = [];
|
|
199
|
+
|
|
200
|
+
const hasCreds = hasTwilioCredentials();
|
|
201
|
+
results.push({
|
|
202
|
+
name: 'twilio_credentials',
|
|
203
|
+
passed: hasCreds,
|
|
204
|
+
message: hasCreds
|
|
205
|
+
? 'Twilio credentials are configured'
|
|
206
|
+
: 'Twilio Account SID and Auth Token are not configured',
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const resolvedNumber = resolveVoicePhoneNumber(context?.assistantId);
|
|
210
|
+
const hasPhone = !!resolvedNumber || (!context?.assistantId && hasAnyAssistantMappedPhoneNumberSafe());
|
|
211
|
+
results.push({
|
|
212
|
+
name: 'phone_number',
|
|
213
|
+
passed: hasPhone,
|
|
214
|
+
message: hasPhone
|
|
215
|
+
? (context?.assistantId && !resolvedNumber
|
|
216
|
+
? `Assistant ${context.assistantId} has no direct mapping, but phone numbers are assigned`
|
|
217
|
+
: 'Phone number is assigned for voice calls')
|
|
218
|
+
: (context?.assistantId
|
|
219
|
+
? `No phone number assigned for assistant ${context.assistantId}`
|
|
220
|
+
: 'No phone number assigned for voice calls'),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const hasIngress = hasIngressConfigured();
|
|
224
|
+
results.push({
|
|
225
|
+
name: 'ingress',
|
|
226
|
+
passed: hasIngress,
|
|
227
|
+
message: hasIngress
|
|
228
|
+
? 'Public ingress URL is configured'
|
|
229
|
+
: 'Public ingress URL is not configured or disabled',
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return results;
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
133
236
|
// ── Telegram Probe ──────────────────────────────────────────────────────────
|
|
134
237
|
|
|
135
238
|
const telegramProbe: ChannelProbe = {
|
|
@@ -173,7 +276,7 @@ const telegramProbe: ChannelProbe = {
|
|
|
173
276
|
|
|
174
277
|
export class ChannelReadinessService {
|
|
175
278
|
private probes = new Map<ChannelId, ChannelProbe>();
|
|
176
|
-
private snapshots = new Map<
|
|
279
|
+
private snapshots = new Map<string, ChannelReadinessSnapshot>();
|
|
177
280
|
|
|
178
281
|
registerProbe(probe: ChannelProbe): void {
|
|
179
282
|
this.probes.set(probe.channel, probe);
|
|
@@ -187,6 +290,7 @@ export class ChannelReadinessService {
|
|
|
187
290
|
async getReadiness(
|
|
188
291
|
channel?: ChannelId,
|
|
189
292
|
includeRemote?: boolean,
|
|
293
|
+
assistantId?: string,
|
|
190
294
|
): Promise<ChannelReadinessSnapshot[]> {
|
|
191
295
|
const channels = channel
|
|
192
296
|
? [channel]
|
|
@@ -200,32 +304,40 @@ export class ChannelReadinessService {
|
|
|
200
304
|
continue;
|
|
201
305
|
}
|
|
202
306
|
|
|
203
|
-
const
|
|
307
|
+
const probeContext: ChannelProbeContext = { assistantId };
|
|
308
|
+
const localChecks = probe.runLocalChecks(probeContext);
|
|
204
309
|
let remoteChecks: ReadinessCheckResult[] | undefined;
|
|
205
310
|
let remoteChecksFreshlyFetched = false;
|
|
311
|
+
let remoteChecksAffectReadiness = false;
|
|
206
312
|
let stale = false;
|
|
207
313
|
|
|
208
|
-
const
|
|
314
|
+
const cacheKey = this.snapshotCacheKey(ch, assistantId);
|
|
315
|
+
const cached = this.snapshots.get(cacheKey);
|
|
209
316
|
const now = Date.now();
|
|
210
317
|
|
|
211
318
|
if (includeRemote && probe.runRemoteChecks) {
|
|
212
319
|
const cacheExpired = !cached || !cached.remoteChecks || (now - cached.checkedAt) >= REMOTE_TTL_MS;
|
|
213
320
|
if (cacheExpired) {
|
|
214
|
-
remoteChecks = await probe.runRemoteChecks();
|
|
321
|
+
remoteChecks = await probe.runRemoteChecks(probeContext);
|
|
215
322
|
remoteChecksFreshlyFetched = true;
|
|
323
|
+
remoteChecksAffectReadiness = true;
|
|
216
324
|
} else {
|
|
217
325
|
// Reuse cached remote checks
|
|
218
326
|
remoteChecks = cached.remoteChecks;
|
|
327
|
+
remoteChecksAffectReadiness = true;
|
|
219
328
|
}
|
|
220
329
|
} else if (cached?.remoteChecks) {
|
|
221
|
-
// Surface cached remote checks
|
|
222
|
-
//
|
|
330
|
+
// Surface cached remote checks for visibility but never let them affect
|
|
331
|
+
// readiness when the caller explicitly opted out of remote checks.
|
|
223
332
|
remoteChecks = cached.remoteChecks;
|
|
224
333
|
stale = (now - cached.checkedAt) >= REMOTE_TTL_MS;
|
|
334
|
+
remoteChecksAffectReadiness = false;
|
|
225
335
|
}
|
|
226
336
|
|
|
227
337
|
const allLocalPassed = localChecks.every((c) => c.passed);
|
|
228
|
-
const allRemotePassed = remoteChecks
|
|
338
|
+
const allRemotePassed = (remoteChecks && remoteChecksAffectReadiness)
|
|
339
|
+
? remoteChecks.every((c) => c.passed)
|
|
340
|
+
: true;
|
|
229
341
|
const ready = allLocalPassed && allRemotePassed;
|
|
230
342
|
|
|
231
343
|
const reasons: Array<{ code: string; text: string }> = [];
|
|
@@ -234,7 +346,7 @@ export class ChannelReadinessService {
|
|
|
234
346
|
reasons.push({ code: check.name, text: check.message });
|
|
235
347
|
}
|
|
236
348
|
}
|
|
237
|
-
if (remoteChecks) {
|
|
349
|
+
if (remoteChecks && remoteChecksAffectReadiness) {
|
|
238
350
|
for (const check of remoteChecks) {
|
|
239
351
|
if (!check.passed) {
|
|
240
352
|
reasons.push({ code: check.name, text: check.message });
|
|
@@ -252,7 +364,7 @@ export class ChannelReadinessService {
|
|
|
252
364
|
remoteChecks,
|
|
253
365
|
};
|
|
254
366
|
|
|
255
|
-
this.snapshots.set(
|
|
367
|
+
this.snapshots.set(cacheKey, snapshot);
|
|
256
368
|
results.push(snapshot);
|
|
257
369
|
}
|
|
258
370
|
|
|
@@ -260,8 +372,17 @@ export class ChannelReadinessService {
|
|
|
260
372
|
}
|
|
261
373
|
|
|
262
374
|
/** Clear cached snapshot for a specific channel, forcing re-evaluation on next call. */
|
|
263
|
-
invalidateChannel(channel: ChannelId): void {
|
|
264
|
-
|
|
375
|
+
invalidateChannel(channel: ChannelId, assistantId?: string): void {
|
|
376
|
+
if (assistantId) {
|
|
377
|
+
this.snapshots.delete(this.snapshotCacheKey(channel, assistantId));
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const prefix = `${channel}::`;
|
|
381
|
+
for (const key of this.snapshots.keys()) {
|
|
382
|
+
if (key.startsWith(prefix)) {
|
|
383
|
+
this.snapshots.delete(key);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
265
386
|
}
|
|
266
387
|
|
|
267
388
|
/** Clear all cached snapshots. */
|
|
@@ -279,14 +400,19 @@ export class ChannelReadinessService {
|
|
|
279
400
|
localChecks: [],
|
|
280
401
|
};
|
|
281
402
|
}
|
|
403
|
+
|
|
404
|
+
private snapshotCacheKey(channel: ChannelId, assistantId?: string): string {
|
|
405
|
+
return `${channel}::${assistantId ?? '__default__'}`;
|
|
406
|
+
}
|
|
282
407
|
}
|
|
283
408
|
|
|
284
409
|
// ── Factory ─────────────────────────────────────────────────────────────────
|
|
285
410
|
|
|
286
|
-
/** Create a service instance with built-in SMS and Telegram probes registered. */
|
|
411
|
+
/** Create a service instance with built-in SMS, Voice, and Telegram probes registered. */
|
|
287
412
|
export function createReadinessService(): ChannelReadinessService {
|
|
288
413
|
const service = new ChannelReadinessService();
|
|
289
414
|
service.registerProbe(smsProbe);
|
|
415
|
+
service.registerProbe(voiceProbe);
|
|
290
416
|
service.registerProbe(telegramProbe);
|
|
291
417
|
return service;
|
|
292
418
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Channel readiness types — reusable primitive for all channels.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import type { ChannelId } from '../channels/types.js';
|
|
4
|
+
|
|
5
|
+
export type { ChannelId };
|
|
5
6
|
|
|
6
7
|
/** Result of a single readiness check (local or remote). */
|
|
7
8
|
export interface ReadinessCheckResult {
|
|
@@ -21,9 +22,14 @@ export interface ChannelReadinessSnapshot {
|
|
|
21
22
|
remoteChecks?: ReadinessCheckResult[];
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/** Optional probe context for assistant-scoped readiness checks. */
|
|
26
|
+
export interface ChannelProbeContext {
|
|
27
|
+
assistantId?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
/** Probe interface that channels implement to provide readiness checks. */
|
|
25
31
|
export interface ChannelProbe {
|
|
26
32
|
channel: ChannelId;
|
|
27
|
-
runLocalChecks(): ReadinessCheckResult[];
|
|
28
|
-
runRemoteChecks?(): Promise<ReadinessCheckResult[]>;
|
|
33
|
+
runLocalChecks(context?: ChannelProbeContext): ReadinessCheckResult[];
|
|
34
|
+
runRemoteChecks?(context?: ChannelProbeContext): Promise<ReadinessCheckResult[]>;
|
|
29
35
|
}
|