@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,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inArray } from 'drizzle-orm';
|
|
2
2
|
import { getDb } from '../db.js';
|
|
3
3
|
import { getQdrantClient } from '../qdrant-client.js';
|
|
4
|
+
import type { QdrantSearchResult } from '../qdrant-client.js';
|
|
4
5
|
import {
|
|
5
6
|
memoryItems,
|
|
6
7
|
memoryItemSources,
|
|
@@ -9,6 +10,84 @@ import {
|
|
|
9
10
|
} from '../schema.js';
|
|
10
11
|
import type { Candidate } from './types.js';
|
|
11
12
|
import { computeRecencyScore } from './ranking.js';
|
|
13
|
+
import { getLogger } from '../../util/logger.js';
|
|
14
|
+
|
|
15
|
+
const log = getLogger('qdrant-circuit-breaker');
|
|
16
|
+
|
|
17
|
+
// ── Qdrant circuit breaker ───────────────────────────────────────────
|
|
18
|
+
// After FAILURE_THRESHOLD consecutive Qdrant failures, stop sending
|
|
19
|
+
// requests (open state). After COOLDOWN_MS, allow a single probe
|
|
20
|
+
// request (half-open). If the probe succeeds, close the circuit; if it
|
|
21
|
+
// fails, re-open and restart the cooldown.
|
|
22
|
+
|
|
23
|
+
const FAILURE_THRESHOLD = 5;
|
|
24
|
+
const COOLDOWN_MS = 60_000;
|
|
25
|
+
|
|
26
|
+
type BreakerState = 'closed' | 'open' | 'half-open';
|
|
27
|
+
|
|
28
|
+
let breakerState: BreakerState = 'closed';
|
|
29
|
+
let consecutiveFailures = 0;
|
|
30
|
+
let openedAt = 0;
|
|
31
|
+
// Ensures only one request passes through during half-open state
|
|
32
|
+
let halfOpenProbeInFlight = false;
|
|
33
|
+
|
|
34
|
+
function qdrantBreakerAllows(): boolean {
|
|
35
|
+
if (breakerState === 'closed') return true;
|
|
36
|
+
if (breakerState === 'open') {
|
|
37
|
+
if (Date.now() - openedAt >= COOLDOWN_MS) {
|
|
38
|
+
breakerState = 'half-open';
|
|
39
|
+
halfOpenProbeInFlight = true;
|
|
40
|
+
log.info('Qdrant circuit breaker entering half-open state — allowing probe request');
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// half-open: only allow through if no probe is already in flight
|
|
46
|
+
if (halfOpenProbeInFlight) return false;
|
|
47
|
+
halfOpenProbeInFlight = true;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function qdrantBreakerRecordSuccess(): void {
|
|
52
|
+
if (breakerState !== 'closed') {
|
|
53
|
+
log.info({ previousFailures: consecutiveFailures }, 'Qdrant circuit breaker closed — search succeeded');
|
|
54
|
+
}
|
|
55
|
+
consecutiveFailures = 0;
|
|
56
|
+
breakerState = 'closed';
|
|
57
|
+
openedAt = 0;
|
|
58
|
+
halfOpenProbeInFlight = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function qdrantBreakerRecordFailure(): void {
|
|
62
|
+
consecutiveFailures++;
|
|
63
|
+
halfOpenProbeInFlight = false;
|
|
64
|
+
if (consecutiveFailures >= FAILURE_THRESHOLD) {
|
|
65
|
+
breakerState = 'open';
|
|
66
|
+
openedAt = Date.now();
|
|
67
|
+
log.warn(
|
|
68
|
+
{ consecutiveFailures, cooldownMs: COOLDOWN_MS },
|
|
69
|
+
'Qdrant circuit breaker opened — semantic search disabled until probe succeeds',
|
|
70
|
+
);
|
|
71
|
+
} else if (breakerState === 'half-open') {
|
|
72
|
+
// Probe failed — re-open
|
|
73
|
+
breakerState = 'open';
|
|
74
|
+
openedAt = Date.now();
|
|
75
|
+
log.warn('Qdrant circuit breaker re-opened — half-open probe failed');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @internal Test-only: reset circuit breaker state */
|
|
80
|
+
export function _resetQdrantBreaker(): void {
|
|
81
|
+
breakerState = 'closed';
|
|
82
|
+
consecutiveFailures = 0;
|
|
83
|
+
openedAt = 0;
|
|
84
|
+
halfOpenProbeInFlight = false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** @internal Test-only: get breaker state */
|
|
88
|
+
export function _getQdrantBreakerState(): { state: BreakerState; consecutiveFailures: number } {
|
|
89
|
+
return { state: breakerState, consecutiveFailures };
|
|
90
|
+
}
|
|
12
91
|
|
|
13
92
|
export async function semanticSearch(
|
|
14
93
|
queryVector: number[],
|
|
@@ -20,18 +99,79 @@ export async function semanticSearch(
|
|
|
20
99
|
): Promise<Candidate[]> {
|
|
21
100
|
if (limit <= 0) return [];
|
|
22
101
|
|
|
102
|
+
// Circuit breaker: throw so the caller's .catch() marks the result as degraded
|
|
103
|
+
if (!qdrantBreakerAllows()) {
|
|
104
|
+
log.debug('Qdrant circuit breaker open — skipping semantic search');
|
|
105
|
+
throw new Error('Qdrant circuit breaker open');
|
|
106
|
+
}
|
|
107
|
+
|
|
23
108
|
const qdrant = getQdrantClient();
|
|
24
109
|
|
|
25
110
|
// Overfetch to account for items filtered out post-query (invalidated, excluded, etc.)
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
111
|
+
// Use 3x when exclusions are active to ensure enough results survive filtering
|
|
112
|
+
const overfetchMultiplier = excludedMessageIds.length > 0 ? 3 : 2;
|
|
113
|
+
const fetchLimit = limit * overfetchMultiplier;
|
|
114
|
+
let results: QdrantSearchResult[];
|
|
115
|
+
try {
|
|
116
|
+
results = await qdrant.searchWithFilter(
|
|
117
|
+
queryVector,
|
|
118
|
+
fetchLimit,
|
|
119
|
+
['item', 'summary', 'segment'],
|
|
120
|
+
excludedMessageIds,
|
|
121
|
+
);
|
|
122
|
+
qdrantBreakerRecordSuccess();
|
|
123
|
+
} catch (err) {
|
|
124
|
+
qdrantBreakerRecordFailure();
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
33
127
|
|
|
34
128
|
const db = getDb();
|
|
129
|
+
|
|
130
|
+
// Batch-fetch all backing records upfront to avoid N+1 queries per result
|
|
131
|
+
const itemTargetIds: string[] = [];
|
|
132
|
+
const summaryTargetIds: string[] = [];
|
|
133
|
+
const segmentTargetIds: string[] = [];
|
|
134
|
+
for (const r of results) {
|
|
135
|
+
if (r.payload.target_type === 'item') itemTargetIds.push(r.payload.target_id);
|
|
136
|
+
else if (r.payload.target_type === 'summary') summaryTargetIds.push(r.payload.target_id);
|
|
137
|
+
else segmentTargetIds.push(r.payload.target_id);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const itemsMap = new Map<string, typeof memoryItems.$inferSelect>();
|
|
141
|
+
if (itemTargetIds.length > 0) {
|
|
142
|
+
const allItems = db.select().from(memoryItems).where(inArray(memoryItems.id, itemTargetIds)).all();
|
|
143
|
+
for (const item of allItems) itemsMap.set(item.id, item);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const sourcesMap = new Map<string, string[]>();
|
|
147
|
+
if (itemTargetIds.length > 0) {
|
|
148
|
+
const allSources = db.select({
|
|
149
|
+
memoryItemId: memoryItemSources.memoryItemId,
|
|
150
|
+
messageId: memoryItemSources.messageId,
|
|
151
|
+
}).from(memoryItemSources)
|
|
152
|
+
.where(inArray(memoryItemSources.memoryItemId, itemTargetIds))
|
|
153
|
+
.all();
|
|
154
|
+
for (const s of allSources) {
|
|
155
|
+
const existing = sourcesMap.get(s.memoryItemId);
|
|
156
|
+
if (existing) existing.push(s.messageId);
|
|
157
|
+
else sourcesMap.set(s.memoryItemId, [s.messageId]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const summariesMap = new Map<string, typeof memorySummaries.$inferSelect>();
|
|
162
|
+
if (scopeIds && summaryTargetIds.length > 0) {
|
|
163
|
+
const allSummaries = db.select().from(memorySummaries).where(inArray(memorySummaries.id, summaryTargetIds)).all();
|
|
164
|
+
for (const s of allSummaries) summariesMap.set(s.id, s);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const segmentsMap = new Map<string, typeof memorySegments.$inferSelect>();
|
|
168
|
+
if (scopeIds && segmentTargetIds.length > 0) {
|
|
169
|
+
const allSegments = db.select().from(memorySegments).where(inArray(memorySegments.id, segmentTargetIds)).all();
|
|
170
|
+
for (const seg of allSegments) segmentsMap.set(seg.id, seg);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const excludedSet = excludedMessageIds.length > 0 ? new Set(excludedMessageIds) : null;
|
|
174
|
+
|
|
35
175
|
const candidates: Candidate[] = [];
|
|
36
176
|
for (const result of results) {
|
|
37
177
|
const { payload, score } = result;
|
|
@@ -39,16 +179,14 @@ export async function semanticSearch(
|
|
|
39
179
|
const createdAt = payload.created_at ?? Date.now();
|
|
40
180
|
|
|
41
181
|
if (payload.target_type === 'item') {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (!item || item.status !== 'active' || item.invalidAt !== null) continue;
|
|
182
|
+
const item = itemsMap.get(payload.target_id);
|
|
183
|
+
if (!item || item.status !== 'active' || item.invalidAt != null) continue;
|
|
45
184
|
if (scopeIds && !scopeIds.includes(item.scopeId)) continue;
|
|
46
|
-
const sources =
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (nonExcluded.length === 0) continue;
|
|
185
|
+
const sources = sourcesMap.get(payload.target_id);
|
|
186
|
+
if (!sources || sources.length === 0) continue;
|
|
187
|
+
if (excludedSet) {
|
|
188
|
+
const hasNonExcluded = sources.some((msgId) => !excludedSet.has(msgId));
|
|
189
|
+
if (!hasNonExcluded) continue;
|
|
52
190
|
}
|
|
53
191
|
candidates.push({
|
|
54
192
|
key: `item:${payload.target_id}`,
|
|
@@ -67,7 +205,7 @@ export async function semanticSearch(
|
|
|
67
205
|
});
|
|
68
206
|
} else if (payload.target_type === 'summary') {
|
|
69
207
|
if (scopeIds) {
|
|
70
|
-
const summary =
|
|
208
|
+
const summary = summariesMap.get(payload.target_id);
|
|
71
209
|
if (!summary || !scopeIds.includes(summary.scopeId)) continue;
|
|
72
210
|
}
|
|
73
211
|
candidates.push({
|
|
@@ -87,7 +225,7 @@ export async function semanticSearch(
|
|
|
87
225
|
});
|
|
88
226
|
} else {
|
|
89
227
|
if (scopeIds) {
|
|
90
|
-
const segment =
|
|
228
|
+
const segment = segmentsMap.get(payload.target_id);
|
|
91
229
|
if (!segment || !scopeIds.includes(segment.scopeId)) continue;
|
|
92
230
|
}
|
|
93
231
|
candidates.push({
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { eq, lte, or, and, isNull } from 'drizzle-orm';
|
|
8
8
|
import { randomUUID, randomBytes } from 'node:crypto';
|
|
9
|
-
import { getDb } from './db.js';
|
|
9
|
+
import { getDb, rawRun } from './db.js';
|
|
10
10
|
import { sharedAppLinks } from './schema.js';
|
|
11
11
|
import type { AppManifest } from '../bundler/manifest.js';
|
|
12
12
|
|
|
@@ -130,9 +130,8 @@ export function deleteSharedAppLinkByToken(shareToken: string): boolean {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
export function incrementDownloadCount(shareToken: string): void {
|
|
133
|
-
|
|
134
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
135
|
-
raw.prepare(
|
|
133
|
+
rawRun(
|
|
136
134
|
`UPDATE shared_app_links SET download_count = download_count + 1 WHERE share_token = ?`,
|
|
137
|
-
|
|
135
|
+
shareToken,
|
|
136
|
+
);
|
|
138
137
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unit interval [0, 1] — used for confidence and importance fields on memory items.
|
|
5
|
+
* Coerces out-of-range numbers to the nearest bound rather than rejecting,
|
|
6
|
+
* since LLM-generated values occasionally exceed the range.
|
|
7
|
+
*/
|
|
8
|
+
export const unitInterval = z.number().transform((v) => Math.min(1, Math.max(0, v)));
|
|
9
|
+
|
|
10
|
+
/** Zod schema for validating confidence/importance values on memory items. */
|
|
11
|
+
export const memoryItemScores = z.object({
|
|
12
|
+
confidence: unitInterval,
|
|
13
|
+
importance: unitInterval,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/** Clamp a numeric value to [0, 1]. */
|
|
17
|
+
export function clampUnitInterval(value: number): number {
|
|
18
|
+
return Math.min(1, Math.max(0, value));
|
|
19
|
+
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Works across all platforms — Slack, Gmail, Discord, etc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { readFileSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
|
+
import { ensureDir, pathExists } from '../util/fs.js';
|
|
10
11
|
import { randomUUID } from 'node:crypto';
|
|
11
12
|
import { getRootDir } from '../util/platform.js';
|
|
12
13
|
|
|
@@ -24,9 +25,7 @@ export interface Draft {
|
|
|
24
25
|
|
|
25
26
|
function getDraftsDir(platform: string): string {
|
|
26
27
|
const dir = join(getRootDir(), 'workspace', 'data', 'drafts', platform);
|
|
27
|
-
|
|
28
|
-
mkdirSync(dir, { recursive: true });
|
|
29
|
-
}
|
|
28
|
+
ensureDir(dir);
|
|
30
29
|
return dir;
|
|
31
30
|
}
|
|
32
31
|
|
|
@@ -61,7 +60,7 @@ export function createDraft(opts: {
|
|
|
61
60
|
|
|
62
61
|
export function getDraft(platform: string, id: string): Draft | null {
|
|
63
62
|
const path = getDraftPath(platform, id);
|
|
64
|
-
if (!
|
|
63
|
+
if (!pathExists(path)) return null;
|
|
65
64
|
return JSON.parse(readFileSync(path, 'utf-8')) as Draft;
|
|
66
65
|
}
|
|
67
66
|
|
|
@@ -82,7 +81,7 @@ export function listDrafts(platform: string): Draft[] {
|
|
|
82
81
|
|
|
83
82
|
export function deleteDraft(platform: string, id: string): boolean {
|
|
84
83
|
const path = getDraftPath(platform, id);
|
|
85
|
-
if (!
|
|
84
|
+
if (!pathExists(path)) return false;
|
|
86
85
|
unlinkSync(path);
|
|
87
86
|
return true;
|
|
88
87
|
}
|
|
@@ -29,17 +29,14 @@ import type {
|
|
|
29
29
|
import { getSecureKey } from '../../../security/secure-keys.js';
|
|
30
30
|
import { readHttpToken } from '../../../util/platform.js';
|
|
31
31
|
import { loadConfig } from '../../../config/loader.js';
|
|
32
|
+
import { getGatewayInternalBaseUrl, getTwilioPhoneNumberEnv } from '../../../config/env.js';
|
|
32
33
|
import { getOrCreateConversation } from '../../../memory/conversation-key-store.js';
|
|
33
34
|
import * as externalConversationStore from '../../../memory/external-conversation-store.js';
|
|
34
35
|
import * as sms from './client.js';
|
|
35
36
|
|
|
36
37
|
/** Resolve the gateway base URL, preferring GATEWAY_INTERNAL_BASE_URL if set. */
|
|
37
38
|
function getGatewayUrl(): string {
|
|
38
|
-
|
|
39
|
-
return process.env.GATEWAY_INTERNAL_BASE_URL.replace(/\/+$/, '');
|
|
40
|
-
}
|
|
41
|
-
const port = Number(process.env.GATEWAY_PORT) || 7830;
|
|
42
|
-
return `http://127.0.0.1:${port}`;
|
|
39
|
+
return getGatewayInternalBaseUrl();
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
/** Read the runtime HTTP bearer token used to authenticate with the gateway. */
|
|
@@ -75,7 +72,7 @@ function getPhoneNumber(assistantId?: string): string | undefined {
|
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
const fromEnv =
|
|
75
|
+
const fromEnv = getTwilioPhoneNumberEnv();
|
|
79
76
|
if (fromEnv) return fromEnv;
|
|
80
77
|
|
|
81
78
|
try {
|
|
@@ -25,17 +25,14 @@ import type {
|
|
|
25
25
|
} from '../../provider-types.js';
|
|
26
26
|
import { getSecureKey } from '../../../security/secure-keys.js';
|
|
27
27
|
import { readHttpToken } from '../../../util/platform.js';
|
|
28
|
+
import { getGatewayInternalBaseUrl } from '../../../config/env.js';
|
|
28
29
|
import { getOrCreateConversation } from '../../../memory/conversation-key-store.js';
|
|
29
30
|
import * as externalConversationStore from '../../../memory/external-conversation-store.js';
|
|
30
31
|
import * as telegram from './client.js';
|
|
31
32
|
|
|
32
33
|
/** Resolve the gateway base URL, preferring GATEWAY_INTERNAL_BASE_URL if set. */
|
|
33
34
|
function getGatewayUrl(): string {
|
|
34
|
-
|
|
35
|
-
return process.env.GATEWAY_INTERNAL_BASE_URL.replace(/\/+$/, "");
|
|
36
|
-
}
|
|
37
|
-
const port = Number(process.env.GATEWAY_PORT) || 7830;
|
|
38
|
-
return `http://127.0.0.1:${port}`;
|
|
35
|
+
return getGatewayInternalBaseUrl();
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
/** Read the runtime HTTP bearer token used to authenticate with the gateway. */
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WhatsApp Business messaging provider adapter.
|
|
3
|
+
*
|
|
4
|
+
* Enables proactive outbound WhatsApp messaging via the gateway's /deliver/whatsapp
|
|
5
|
+
* endpoint. Delivery is proxied through the gateway which owns the Meta Cloud API
|
|
6
|
+
* credentials (phone_number_id + access_token).
|
|
7
|
+
*
|
|
8
|
+
* The `token` parameter in MessagingProvider methods is unused for WhatsApp
|
|
9
|
+
* because delivery is authenticated via the gateway's bearer token, not
|
|
10
|
+
* a per-user OAuth token.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { MessagingProvider } from '../../provider.js';
|
|
14
|
+
import type {
|
|
15
|
+
Conversation,
|
|
16
|
+
Message,
|
|
17
|
+
SearchResult,
|
|
18
|
+
SendResult,
|
|
19
|
+
ConnectionInfo,
|
|
20
|
+
ListOptions,
|
|
21
|
+
HistoryOptions,
|
|
22
|
+
SearchOptions,
|
|
23
|
+
SendOptions,
|
|
24
|
+
} from '../../provider-types.js';
|
|
25
|
+
import { getSecureKey } from '../../../security/secure-keys.js';
|
|
26
|
+
import { readHttpToken } from '../../../util/platform.js';
|
|
27
|
+
import { getGatewayInternalBaseUrl } from '../../../config/env.js';
|
|
28
|
+
import { getOrCreateConversation } from '../../../memory/conversation-key-store.js';
|
|
29
|
+
import * as externalConversationStore from '../../../memory/external-conversation-store.js';
|
|
30
|
+
import * as whatsapp from './client.js';
|
|
31
|
+
|
|
32
|
+
/** Resolve the gateway base URL. */
|
|
33
|
+
function getGatewayUrl(): string {
|
|
34
|
+
return getGatewayInternalBaseUrl();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Read the runtime HTTP bearer token used to authenticate with the gateway. */
|
|
38
|
+
function getBearerToken(): string {
|
|
39
|
+
const token = readHttpToken();
|
|
40
|
+
if (!token) {
|
|
41
|
+
throw new Error('No runtime HTTP bearer token available — is the daemon running?');
|
|
42
|
+
}
|
|
43
|
+
return token;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Check whether WhatsApp credentials are stored. */
|
|
47
|
+
function hasWhatsAppCredentials(): boolean {
|
|
48
|
+
return (
|
|
49
|
+
!!getSecureKey('credential:whatsapp:phone_number_id') &&
|
|
50
|
+
!!getSecureKey('credential:whatsapp:access_token')
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const whatsappMessagingProvider: MessagingProvider = {
|
|
55
|
+
id: 'whatsapp',
|
|
56
|
+
displayName: 'WhatsApp',
|
|
57
|
+
credentialService: 'whatsapp',
|
|
58
|
+
capabilities: new Set(['send']),
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* WhatsApp is connected when Meta Cloud API credentials are stored.
|
|
62
|
+
*/
|
|
63
|
+
isConnected(): boolean {
|
|
64
|
+
return hasWhatsAppCredentials();
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async testConnection(_token: string): Promise<ConnectionInfo> {
|
|
68
|
+
if (!hasWhatsAppCredentials()) {
|
|
69
|
+
return {
|
|
70
|
+
connected: false,
|
|
71
|
+
user: 'unknown',
|
|
72
|
+
platform: 'whatsapp',
|
|
73
|
+
metadata: { error: 'No WhatsApp credentials found. Configure WHATSAPP_PHONE_NUMBER_ID and WHATSAPP_ACCESS_TOKEN.' },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const phoneNumberId = getSecureKey('credential:whatsapp:phone_number_id')!;
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
connected: true,
|
|
81
|
+
user: phoneNumberId,
|
|
82
|
+
platform: 'whatsapp',
|
|
83
|
+
metadata: {
|
|
84
|
+
phoneNumberId: phoneNumberId.slice(0, 6) + '...',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async sendMessage(_token: string, conversationId: string, text: string, options?: SendOptions): Promise<SendResult> {
|
|
90
|
+
const gatewayUrl = getGatewayUrl();
|
|
91
|
+
const bearerToken = getBearerToken();
|
|
92
|
+
const assistantId = options?.assistantId;
|
|
93
|
+
|
|
94
|
+
await whatsapp.sendMessage(gatewayUrl, bearerToken, conversationId, text, assistantId);
|
|
95
|
+
|
|
96
|
+
// Upsert external conversation binding so the conversation key mapping
|
|
97
|
+
// exists for the next inbound WhatsApp message from this number.
|
|
98
|
+
try {
|
|
99
|
+
const sourceChannel = 'whatsapp';
|
|
100
|
+
const conversationKey = assistantId && assistantId !== 'self'
|
|
101
|
+
? `asst:${assistantId}:${sourceChannel}:${conversationId}`
|
|
102
|
+
: `${sourceChannel}:${conversationId}`;
|
|
103
|
+
const { conversationId: internalId } = getOrCreateConversation(conversationKey);
|
|
104
|
+
if (!assistantId || assistantId === 'self') {
|
|
105
|
+
externalConversationStore.upsertOutboundBinding({
|
|
106
|
+
conversationId: internalId,
|
|
107
|
+
sourceChannel,
|
|
108
|
+
externalChatId: conversationId,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// Best-effort — don't fail the send if binding upsert fails
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
id: `whatsapp-${Date.now()}`,
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
conversationId,
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// WhatsApp does not support listing conversations via this provider.
|
|
123
|
+
async listConversations(_token: string, _options?: ListOptions): Promise<Conversation[]> {
|
|
124
|
+
return [];
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// WhatsApp does not provide message history retrieval via the gateway.
|
|
128
|
+
async getHistory(_token: string, _conversationId: string, _options?: HistoryOptions): Promise<Message[]> {
|
|
129
|
+
return [];
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// WhatsApp does not support message search.
|
|
133
|
+
async search(_token: string, _query: string, _options?: SearchOptions): Promise<SearchResult> {
|
|
134
|
+
return { total: 0, messages: [], hasMore: false };
|
|
135
|
+
},
|
|
136
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level WhatsApp operations.
|
|
3
|
+
*
|
|
4
|
+
* Outbound message delivery routes through the gateway's /deliver/whatsapp
|
|
5
|
+
* endpoint, which handles WhatsApp credential management and the Meta Cloud API.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const DELIVERY_TIMEOUT_MS = 30_000;
|
|
9
|
+
|
|
10
|
+
export class WhatsAppApiError extends Error {
|
|
11
|
+
constructor(
|
|
12
|
+
public readonly status: number,
|
|
13
|
+
message: string,
|
|
14
|
+
) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'WhatsAppApiError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Payload accepted by the gateway's /deliver/whatsapp endpoint. */
|
|
21
|
+
interface DeliverPayload {
|
|
22
|
+
to: string;
|
|
23
|
+
text: string;
|
|
24
|
+
assistantId?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Result returned by sendMessage. */
|
|
28
|
+
export interface WhatsAppSendResult {
|
|
29
|
+
ok: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Send a WhatsApp message via the gateway's /deliver/whatsapp endpoint.
|
|
34
|
+
*/
|
|
35
|
+
export async function sendMessage(
|
|
36
|
+
gatewayUrl: string,
|
|
37
|
+
bearerToken: string,
|
|
38
|
+
to: string,
|
|
39
|
+
text: string,
|
|
40
|
+
assistantId?: string,
|
|
41
|
+
): Promise<WhatsAppSendResult> {
|
|
42
|
+
const payload: DeliverPayload = { to, text };
|
|
43
|
+
if (assistantId) {
|
|
44
|
+
payload.assistantId = assistantId;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const url = `${gatewayUrl}/deliver/whatsapp`;
|
|
48
|
+
const resp = await fetch(url, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify(payload),
|
|
55
|
+
signal: AbortSignal.timeout(DELIVERY_TIMEOUT_MS),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!resp.ok) {
|
|
59
|
+
const body = await resp.text().catch(() => '<unreadable>');
|
|
60
|
+
throw new WhatsAppApiError(
|
|
61
|
+
resp.status,
|
|
62
|
+
`Gateway /deliver/whatsapp failed (${resp.status}): ${body}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { ok: true };
|
|
67
|
+
}
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
import type { Message as ProviderMessage } from './provider-types.js';
|
|
10
10
|
import type { Message, ToolDefinition } from '../providers/types.js';
|
|
11
11
|
import { truncate } from '../util/truncate.js';
|
|
12
|
-
import {
|
|
13
|
-
import { getConfig } from '../config/loader.js';
|
|
12
|
+
import { getConfiguredProvider } from '../providers/provider-send-message.js';
|
|
14
13
|
|
|
15
14
|
export interface StylePattern {
|
|
16
15
|
aspect: string;
|
|
@@ -118,8 +117,10 @@ export async function extractStylePatterns(
|
|
|
118
117
|
|
|
119
118
|
const corpus = corpusEntries.map((e, i) => `--- Message ${i + 1} ---\n${e}`).join('\n\n');
|
|
120
119
|
|
|
121
|
-
const
|
|
122
|
-
|
|
120
|
+
const provider = getConfiguredProvider();
|
|
121
|
+
if (!provider) {
|
|
122
|
+
return { stylePatterns: [], contactObservations: [] };
|
|
123
|
+
}
|
|
123
124
|
const promptMessages: Message[] = [{
|
|
124
125
|
role: 'user',
|
|
125
126
|
content: [{ type: 'text', text: `Analyze these ${corpusEntries.length} sent messages for writing style patterns:\n\n${corpus}` }],
|