@vellumai/assistant 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +2 -0
- package/README.md +88 -2
- package/eslint.config.mjs +31 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
- package/scripts/ipc/generate-swift.ts +31 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +799 -249
- package/src/__tests__/call-pointer-messages.test.ts +148 -0
- package/src/__tests__/call-recovery.test.ts +3 -0
- package/src/__tests__/call-routes-http.test.ts +32 -2
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1277 -98
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +36 -50
- package/src/__tests__/channel-guardian.test.ts +630 -22
- package/src/__tests__/channel-readiness-service.test.ts +324 -0
- package/src/__tests__/checker.test.ts +14 -7
- package/src/__tests__/clarification-resolver.test.ts +44 -24
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
- package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
- package/src/__tests__/config-schema.test.ts +14 -8
- package/src/__tests__/context-window-manager.test.ts +30 -2
- package/src/__tests__/contradiction-checker.test.ts +20 -5
- package/src/__tests__/credential-security-invariants.test.ts +7 -2
- package/src/__tests__/daemon-lifecycle.test.ts +13 -12
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/entity-search.test.ts +615 -0
- package/src/__tests__/fuzzy-match-property.test.ts +5 -5
- package/src/__tests__/guardian-action-store.test.ts +123 -0
- package/src/__tests__/guardian-action-sweep.test.ts +277 -0
- package/src/__tests__/guardian-dispatch.test.ts +389 -0
- package/src/__tests__/guardian-question-copy.test.ts +47 -0
- package/src/__tests__/handlers-telegram-config.test.ts +4 -2
- package/src/__tests__/handlers-twilio-config.test.ts +533 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +291 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -0
- package/src/__tests__/model-intents.test.ts +96 -0
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
- package/src/__tests__/provider-error-scenarios.test.ts +621 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
- package/src/__tests__/qdrant-manager.test.ts +27 -20
- package/src/__tests__/relay-server.test.ts +779 -40
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
- package/src/__tests__/run-orchestrator.test.ts +42 -4
- package/src/__tests__/runtime-runs-http.test.ts +17 -1
- package/src/__tests__/runtime-runs.test.ts +16 -0
- package/src/__tests__/schedule-store.test.ts +18 -4
- package/src/__tests__/scheduler-recurrence.test.ts +13 -4
- package/src/__tests__/session-abort-tool-results.test.ts +6 -0
- package/src/__tests__/session-agent-loop.test.ts +857 -0
- package/src/__tests__/session-conflict-gate.test.ts +6 -0
- package/src/__tests__/session-pre-run-repair.test.ts +6 -0
- package/src/__tests__/session-profile-injection.test.ts +6 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/session-queue.test.ts +6 -0
- package/src/__tests__/session-runtime-assembly.test.ts +321 -13
- package/src/__tests__/session-slash-known.test.ts +6 -0
- package/src/__tests__/session-slash-queue.test.ts +6 -0
- package/src/__tests__/session-slash-unknown.test.ts +6 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/session-workspace-injection.test.ts +6 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/skills.test.ts +2 -0
- package/src/__tests__/sms-messaging-provider.test.ts +126 -0
- package/src/__tests__/starter-task-flow.test.ts +2 -0
- package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +2 -0
- package/src/__tests__/task-management-tools.test.ts +2 -2
- package/src/__tests__/task-runner.test.ts +14 -4
- package/src/__tests__/terminal-tools.test.ts +25 -19
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
- package/src/__tests__/tool-executor.test.ts +23 -24
- package/src/__tests__/trust-store.test.ts +3 -3
- package/src/__tests__/twilio-rest.test.ts +29 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
- package/src/__tests__/twilio-routes.test.ts +167 -11
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- package/src/__tests__/user-reference.test.ts +2 -0
- package/src/__tests__/voice-quality.test.ts +222 -0
- package/src/__tests__/web-search.test.ts +46 -30
- package/src/__tests__/work-item-output.test.ts +110 -0
- package/src/agent/loop.ts +1 -1
- package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
- package/src/amazon/client.ts +1418 -0
- package/src/amazon/request-extractor.ts +135 -0
- package/src/amazon/session.ts +109 -0
- package/src/autonomy/autonomy-store.ts +5 -5
- package/src/browser-extension-relay/client.ts +124 -0
- package/src/browser-extension-relay/protocol.ts +63 -0
- package/src/browser-extension-relay/server.ts +177 -0
- package/src/bundler/app-bundler.ts +3 -3
- package/src/bundler/bundle-signer.ts +1 -1
- package/src/bundler/signature-verifier.ts +1 -1
- package/src/calls/call-conversation-messages.ts +33 -0
- package/src/calls/call-domain.ts +114 -10
- package/src/calls/call-orchestrator.ts +268 -59
- package/src/calls/call-pointer-messages.ts +53 -0
- package/src/calls/call-recovery.ts +3 -8
- package/src/calls/call-store.ts +69 -87
- package/src/calls/elevenlabs-config.ts +3 -2
- package/src/calls/guardian-action-sweep.ts +105 -0
- package/src/calls/guardian-dispatch.ts +203 -0
- package/src/calls/guardian-question-copy.ts +133 -0
- package/src/calls/relay-server.ts +466 -8
- package/src/calls/speaker-identification.ts +1 -1
- package/src/calls/twilio-config.ts +22 -14
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +308 -7
- package/src/calls/twilio-routes.ts +65 -12
- package/src/calls/types.ts +3 -1
- package/src/channels/types.ts +25 -0
- package/src/cli/amazon.ts +815 -0
- package/src/cli/config-commands.ts +2 -2
- package/src/cli/core-commands.ts +4 -3
- package/src/cli/influencer.ts +244 -0
- package/src/cli/map.ts +89 -6
- package/src/cli.ts +1 -1
- package/src/config/agent-schema.ts +171 -0
- package/src/config/bundled-skills/amazon/SKILL.md +127 -0
- package/src/config/bundled-skills/amazon/icon.svg +13 -0
- package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
- package/src/config/bundled-skills/browser/SKILL.md +1 -0
- package/src/config/bundled-skills/browser/TOOLS.json +17 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
- package/src/config/bundled-skills/doordash/SKILL.md +51 -51
- package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
- package/src/config/bundled-skills/influencer/SKILL.md +144 -0
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
- package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
- package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
- package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
- package/src/config/bundled-skills/messaging/SKILL.md +33 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- package/src/config/bundled-skills/twitter/icon.svg +14 -0
- package/src/config/bundled-tool-registry.ts +310 -0
- package/src/config/calls-schema.ts +181 -0
- package/src/config/core-schema.ts +309 -0
- package/src/config/defaults.ts +28 -3
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +158 -1133
- package/src/config/skill-state.ts +1 -1
- package/src/config/skills-schema.ts +32 -0
- package/src/config/skills.ts +35 -24
- package/src/config/system-prompt.ts +131 -56
- package/src/config/templates/IDENTITY.md +2 -2
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +1 -0
- package/src/config/user-reference.ts +4 -9
- package/src/config/vellum-skills/catalog.json +6 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
- package/src/context/window-manager.ts +27 -7
- package/src/daemon/approval-generators.ts +186 -0
- package/src/daemon/approved-devices-store.ts +140 -0
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/classifier.ts +35 -32
- package/src/daemon/config-watcher.ts +1 -1
- package/src/daemon/daemon-control.ts +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -1689
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +180 -0
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +11 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +56 -5
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/handlers/twitter-auth.ts +2 -0
- package/src/daemon/handlers/work-items.ts +17 -9
- package/src/daemon/handlers/workspace-files.ts +4 -3
- package/src/daemon/install-cli-launchers.ts +113 -0
- package/src/daemon/ipc-contract/apps.ts +356 -0
- package/src/daemon/ipc-contract/browser.ts +74 -0
- package/src/daemon/ipc-contract/computer-use.ts +151 -0
- package/src/daemon/ipc-contract/diagnostics.ts +56 -0
- package/src/daemon/ipc-contract/documents.ts +74 -0
- package/src/daemon/ipc-contract/inbox.ts +209 -0
- package/src/daemon/ipc-contract/integrations.ts +284 -0
- package/src/daemon/ipc-contract/memory.ts +48 -0
- package/src/daemon/ipc-contract/messages.ts +211 -0
- package/src/daemon/ipc-contract/pairing.ts +45 -0
- package/src/daemon/ipc-contract/parental-control.ts +95 -0
- package/src/daemon/ipc-contract/schedules.ts +97 -0
- package/src/daemon/ipc-contract/sessions.ts +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +70 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +229 -2426
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -377
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +68 -3
- package/src/daemon/server.ts +66 -46
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +117 -275
- package/src/daemon/session-dynamic-profile.ts +1 -1
- package/src/daemon/session-history.ts +1 -1
- package/src/daemon/session-media-retry.ts +1 -1
- package/src/daemon/session-messaging.ts +37 -2
- package/src/daemon/session-notifiers.ts +5 -25
- package/src/daemon/session-process.ts +99 -59
- package/src/daemon/session-queue-manager.ts +96 -4
- package/src/daemon/session-runtime-assembly.ts +199 -10
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +30 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +35 -2
- package/src/daemon/shutdown-handlers.ts +122 -0
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/watch-handler.ts +36 -33
- package/src/doordash/cart-queries.ts +787 -0
- package/src/doordash/client.ts +144 -127
- package/src/doordash/order-queries.ts +85 -0
- package/src/doordash/queries.ts +10 -1308
- package/src/doordash/search-queries.ts +203 -0
- package/src/doordash/session.ts +3 -2
- package/src/doordash/store-queries.ts +246 -0
- package/src/doordash/types.ts +367 -0
- package/src/email/providers/agentmail.ts +2 -1
- package/src/email/providers/index.ts +3 -2
- package/src/email/service.ts +3 -2
- package/src/errors.ts +43 -0
- package/src/home-base/prebuilt/seed.ts +1 -1
- package/src/hooks/cli.ts +6 -5
- package/src/hooks/config.ts +6 -8
- package/src/hooks/discovery.ts +6 -5
- package/src/hooks/manager.ts +4 -3
- package/src/hooks/runner.ts +2 -2
- package/src/hooks/templates.ts +5 -5
- package/src/inbound/public-ingress-urls.ts +6 -4
- package/src/index.ts +4 -2
- package/src/influencer/client.ts +1104 -0
- package/src/instrument.ts +4 -3
- package/src/logfire.ts +4 -3
- package/src/memory/admin.ts +25 -35
- package/src/memory/attachments-store.ts +4 -7
- package/src/memory/channel-delivery-store.ts +30 -1
- package/src/memory/channel-guardian-store.ts +202 -2
- package/src/memory/clarification-resolver.ts +37 -33
- package/src/memory/conflict-store.ts +67 -61
- package/src/memory/contradiction-checker.ts +141 -117
- package/src/memory/conversation-store.ts +335 -51
- package/src/memory/db-connection.ts +27 -4
- package/src/memory/db-init.ts +265 -4
- package/src/memory/db.ts +14 -1
- package/src/memory/embedding-backend.ts +27 -5
- package/src/memory/embedding-ollama.ts +2 -1
- package/src/memory/entity-extractor.ts +38 -35
- package/src/memory/guardian-action-store.ts +430 -0
- package/src/memory/inbox-escalation-projection.ts +59 -0
- package/src/memory/inbox-thread-store.ts +218 -0
- package/src/memory/ingress-invite-store.ts +338 -0
- package/src/memory/ingress-member-store.ts +350 -0
- package/src/memory/items-extractor.ts +91 -97
- package/src/memory/job-handlers/index-maintenance.ts +3 -3
- package/src/memory/job-handlers/media-processing.ts +69 -0
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +8 -10
- package/src/memory/jobs-worker.ts +55 -36
- package/src/memory/media-store.ts +759 -0
- package/src/memory/migrations/001-job-deferrals.ts +45 -0
- package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
- package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
- package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
- package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
- package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
- package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
- package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +165 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +228 -7
- package/src/memory/search/entity.ts +205 -31
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +27 -23
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/search/types.ts +24 -0
- package/src/memory/shared-app-links-store.ts +4 -5
- package/src/memory/validation.ts +19 -0
- package/src/messaging/draft-store.ts +5 -6
- package/src/messaging/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +201 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
- package/src/messaging/providers/whatsapp/adapter.ts +136 -0
- package/src/messaging/providers/whatsapp/client.ts +67 -0
- package/src/messaging/style-analyzer.ts +5 -4
- package/src/messaging/thread-summarizer.ts +61 -69
- package/src/messaging/triage-engine.ts +62 -71
- package/src/migrations/config-merge.ts +53 -0
- package/src/migrations/data-layout.ts +68 -0
- package/src/migrations/data-merge.ts +33 -0
- package/src/migrations/hooks-merge.ts +90 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/log.ts +23 -0
- package/src/migrations/skills-merge.ts +33 -0
- package/src/migrations/workspace-layout.ts +79 -0
- package/src/permissions/checker.ts +133 -11
- package/src/permissions/prompter.ts +14 -0
- package/src/permissions/shell-identity.ts +31 -1
- package/src/permissions/trust-store.ts +21 -1
- package/src/providers/anthropic/client.ts +4 -4
- package/src/providers/failover.ts +2 -2
- package/src/providers/model-intents.ts +70 -0
- package/src/providers/ollama/client.ts +2 -1
- package/src/providers/provider-send-message.ts +176 -0
- package/src/providers/registry.ts +71 -30
- package/src/providers/retry.ts +35 -1
- package/src/providers/types.ts +12 -1
- package/src/runtime/approval-conversation-turn.ts +97 -0
- package/src/runtime/approval-message-composer.ts +253 -0
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +11 -24
- package/src/runtime/channel-guardian-service.ts +88 -21
- package/src/runtime/channel-readiness-service.ts +418 -0
- package/src/runtime/channel-readiness-types.ts +35 -0
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +275 -717
- package/src/runtime/http-types.ts +59 -3
- package/src/runtime/middleware/auth.ts +116 -0
- package/src/runtime/middleware/error-handler.ts +33 -0
- package/src/runtime/middleware/twilio-validation.ts +127 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +51 -7
- package/src/runtime/routes/channel-delivery-routes.ts +170 -0
- package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
- package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
- package/src/runtime/routes/channel-route-shared.ts +144 -0
- package/src/runtime/routes/channel-routes.ts +32 -1588
- package/src/runtime/routes/conversation-routes.ts +50 -7
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +126 -0
- package/src/runtime/routes/pairing-routes.ts +143 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +86 -35
- package/src/schedule/schedule-store.ts +36 -32
- package/src/schedule/scheduler.ts +3 -3
- package/src/security/encrypted-store.ts +5 -7
- package/src/security/oauth2.ts +45 -15
- package/src/security/parental-control-store.ts +183 -0
- package/src/security/secret-allowlist.ts +4 -3
- package/src/security/secret-scanner.ts +5 -5
- package/src/security/secure-keys.ts +1 -1
- package/src/security/token-manager.ts +3 -2
- package/src/services/vercel-deploy.ts +6 -2
- package/src/skills/tool-manifest.ts +3 -3
- package/src/skills/vellum-catalog-remote.ts +75 -16
- package/src/slack/slack-webhook.ts +2 -1
- package/src/swarm/orchestrator.ts +92 -1
- package/src/swarm/router-planner.ts +6 -9
- package/src/swarm/worker-prompts.ts +9 -12
- package/src/tasks/task-compiler.ts +19 -28
- package/src/tasks/task-runner.ts +1 -1
- package/src/tools/assets/materialize.ts +2 -2
- package/src/tools/assets/search.ts +15 -14
- package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
- package/src/tools/browser/auto-navigate.ts +1 -0
- package/src/tools/browser/browser-execution.ts +10 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -0
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/broker.ts +11 -2
- package/src/tools/credentials/metadata-store.ts +18 -14
- package/src/tools/credentials/post-connect-hooks.ts +61 -0
- package/src/tools/credentials/vault.ts +49 -23
- package/src/tools/execution-target.ts +11 -1
- package/src/tools/executor.ts +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +8 -4
- package/src/tools/reminder/reminder-store.ts +14 -15
- package/src/tools/schedule/create.ts +1 -0
- package/src/tools/schedule/list.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
- package/src/tools/skills/skill-script-runner.ts +24 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/tasks/work-item-enqueue.ts +2 -2
- package/src/tools/terminal/evaluate-typescript.ts +21 -12
- package/src/tools/terminal/parser.ts +50 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- package/src/twitter/router.ts +1 -1
- package/src/twitter/session.ts +4 -3
- package/src/util/clipboard.ts +1 -1
- package/src/util/errors.ts +65 -8
- package/src/util/fs.ts +40 -0
- package/src/util/json.ts +10 -0
- package/src/util/log-redact.ts +189 -0
- package/src/util/logger.ts +19 -17
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +105 -363
- package/src/util/pricing.ts +1 -1
- package/src/util/promise-guard.ts +1 -1
- package/src/util/retry.ts +19 -0
- package/src/util/row-mapper.ts +79 -0
- package/src/util/silently.ts +21 -0
- package/src/watcher/engine.ts +5 -1
- package/src/watcher/provider-types.ts +20 -0
- package/src/watcher/providers/github.ts +156 -0
- package/src/watcher/providers/gmail.ts +1 -0
- package/src/watcher/providers/google-calendar.ts +1 -0
- package/src/watcher/providers/linear.ts +460 -0
- package/src/watcher/providers/slack.ts +1 -0
- package/src/work-items/work-item-runner.ts +1 -1
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/provider-commit-message-generator.ts +51 -22
- package/src/__tests__/call-bridge.test.ts +0 -517
- package/src/__tests__/session-process-bridge.test.ts +0 -244
- package/src/calls/call-bridge.ts +0 -168
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
package/src/calls/call-store.ts
CHANGED
|
@@ -1,56 +1,53 @@
|
|
|
1
|
-
import { eq, and, notInArray, desc } from 'drizzle-orm';
|
|
1
|
+
import { eq, and, or, notInArray, desc } from 'drizzle-orm';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
|
-
import { getDb } from '../memory/db.js';
|
|
3
|
+
import { getDb, rawChanges, rawGet, rawRun } from '../memory/db.js';
|
|
4
4
|
import { callSessions, callEvents, callPendingQuestions } from '../memory/schema.js';
|
|
5
5
|
import type { CallSession, CallEvent, CallPendingQuestion, CallEventType, CallStatus } from './types.js';
|
|
6
6
|
import { validateTransition } from './call-state-machine.js';
|
|
7
7
|
import { getLogger } from '../util/logger.js';
|
|
8
|
+
import { createRowMapper, cast } from '../util/row-mapper.js';
|
|
8
9
|
|
|
9
10
|
const log = getLogger('call-store');
|
|
10
11
|
|
|
11
12
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
answeredAt: row.answeredAt,
|
|
51
|
-
answerText: row.answerText,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
14
|
+
const parseCallSession = createRowMapper<typeof callSessions.$inferSelect, CallSession>({
|
|
15
|
+
id: 'id',
|
|
16
|
+
conversationId: 'conversationId',
|
|
17
|
+
provider: 'provider',
|
|
18
|
+
providerCallSid: 'providerCallSid',
|
|
19
|
+
fromNumber: 'fromNumber',
|
|
20
|
+
toNumber: 'toNumber',
|
|
21
|
+
task: 'task',
|
|
22
|
+
status: { from: 'status', transform: cast<CallSession['status']>() },
|
|
23
|
+
callerIdentityMode: 'callerIdentityMode',
|
|
24
|
+
callerIdentitySource: 'callerIdentitySource',
|
|
25
|
+
assistantId: 'assistantId',
|
|
26
|
+
initiatedFromConversationId: 'initiatedFromConversationId',
|
|
27
|
+
startedAt: 'startedAt',
|
|
28
|
+
endedAt: 'endedAt',
|
|
29
|
+
lastError: 'lastError',
|
|
30
|
+
createdAt: 'createdAt',
|
|
31
|
+
updatedAt: 'updatedAt',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const parseCallEvent = createRowMapper<typeof callEvents.$inferSelect, CallEvent>({
|
|
35
|
+
id: 'id',
|
|
36
|
+
callSessionId: 'callSessionId',
|
|
37
|
+
eventType: { from: 'eventType', transform: cast<CallEvent['eventType']>() },
|
|
38
|
+
payloadJson: 'payloadJson',
|
|
39
|
+
createdAt: 'createdAt',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const parsePendingQuestion = createRowMapper<typeof callPendingQuestions.$inferSelect, CallPendingQuestion>({
|
|
43
|
+
id: 'id',
|
|
44
|
+
callSessionId: 'callSessionId',
|
|
45
|
+
questionText: 'questionText',
|
|
46
|
+
status: { from: 'status', transform: cast<CallPendingQuestion['status']>() },
|
|
47
|
+
askedAt: 'askedAt',
|
|
48
|
+
answeredAt: 'answeredAt',
|
|
49
|
+
answerText: 'answerText',
|
|
50
|
+
});
|
|
54
51
|
|
|
55
52
|
// ── Call Sessions ────────────────────────────────────────────────────
|
|
56
53
|
|
|
@@ -62,6 +59,8 @@ export function createCallSession(opts: {
|
|
|
62
59
|
task?: string;
|
|
63
60
|
callerIdentityMode?: string;
|
|
64
61
|
callerIdentitySource?: string;
|
|
62
|
+
assistantId?: string;
|
|
63
|
+
initiatedFromConversationId?: string;
|
|
65
64
|
}): CallSession {
|
|
66
65
|
const db = getDb();
|
|
67
66
|
const now = Date.now();
|
|
@@ -76,6 +75,8 @@ export function createCallSession(opts: {
|
|
|
76
75
|
status: 'initiated' as const,
|
|
77
76
|
callerIdentityMode: opts.callerIdentityMode ?? null,
|
|
78
77
|
callerIdentitySource: opts.callerIdentitySource ?? null,
|
|
78
|
+
assistantId: opts.assistantId ?? null,
|
|
79
|
+
initiatedFromConversationId: opts.initiatedFromConversationId ?? null,
|
|
79
80
|
startedAt: null,
|
|
80
81
|
endedAt: null,
|
|
81
82
|
lastError: null,
|
|
@@ -111,7 +112,10 @@ export function getActiveCallSessionForConversation(conversationId: string): Cal
|
|
|
111
112
|
.from(callSessions)
|
|
112
113
|
.where(
|
|
113
114
|
and(
|
|
114
|
-
|
|
115
|
+
or(
|
|
116
|
+
eq(callSessions.conversationId, conversationId),
|
|
117
|
+
eq(callSessions.initiatedFromConversationId, conversationId),
|
|
118
|
+
),
|
|
115
119
|
notInArray(callSessions.status, ['completed', 'failed', 'cancelled']),
|
|
116
120
|
),
|
|
117
121
|
)
|
|
@@ -123,7 +127,7 @@ export function getActiveCallSessionForConversation(conversationId: string): Cal
|
|
|
123
127
|
|
|
124
128
|
export function updateCallSession(
|
|
125
129
|
id: string,
|
|
126
|
-
updates: Partial<Pick<CallSession, 'status' | 'providerCallSid' | 'startedAt' | 'endedAt' | 'lastError'>>,
|
|
130
|
+
updates: Partial<Pick<CallSession, 'status' | 'providerCallSid' | 'startedAt' | 'endedAt' | 'lastError' | 'conversationId' | 'initiatedFromConversationId'>>,
|
|
127
131
|
): void {
|
|
128
132
|
const db = getDb();
|
|
129
133
|
|
|
@@ -246,10 +250,7 @@ export function answerPendingQuestion(id: string, answerText: string): void {
|
|
|
246
250
|
),
|
|
247
251
|
)
|
|
248
252
|
.run();
|
|
249
|
-
|
|
250
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
251
|
-
const changes = raw.query('SELECT changes() as c').get() as { c: number };
|
|
252
|
-
if (changes.c === 0) {
|
|
253
|
+
if (rawChanges() === 0) {
|
|
253
254
|
log.warn({ questionId: id }, 'answerPendingQuestion: no rows updated — question may have already been answered or expired');
|
|
254
255
|
}
|
|
255
256
|
}
|
|
@@ -292,13 +293,7 @@ export function buildCallbackDedupeKey(
|
|
|
292
293
|
* Returns true if the key already exists, false otherwise.
|
|
293
294
|
*/
|
|
294
295
|
export function isCallbackProcessed(dedupeKey: string): boolean {
|
|
295
|
-
|
|
296
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
297
|
-
|
|
298
|
-
const row = raw.query(
|
|
299
|
-
`SELECT 1 FROM processed_callbacks WHERE dedupe_key = ?`,
|
|
300
|
-
).get(dedupeKey);
|
|
301
|
-
return row != null;
|
|
296
|
+
return rawGet<{ 1: number }>(`SELECT 1 FROM processed_callbacks WHERE dedupe_key = ?`, dedupeKey) != null;
|
|
302
297
|
}
|
|
303
298
|
|
|
304
299
|
/**
|
|
@@ -312,12 +307,10 @@ export function recordProcessedCallback(
|
|
|
312
307
|
dedupeKey: string,
|
|
313
308
|
callSessionId: string,
|
|
314
309
|
): void {
|
|
315
|
-
|
|
316
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
317
|
-
|
|
318
|
-
raw.query(
|
|
310
|
+
rawRun(
|
|
319
311
|
`INSERT OR IGNORE INTO processed_callbacks (id, dedupe_key, call_session_id, created_at) VALUES (?, ?, ?, ?)`,
|
|
320
|
-
|
|
312
|
+
uuid(), dedupeKey, callSessionId, Date.now(),
|
|
313
|
+
);
|
|
321
314
|
}
|
|
322
315
|
|
|
323
316
|
/**
|
|
@@ -334,15 +327,10 @@ export function tryRecordProcessedCallback(
|
|
|
334
327
|
dedupeKey: string,
|
|
335
328
|
callSessionId: string,
|
|
336
329
|
): boolean {
|
|
337
|
-
|
|
338
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
339
|
-
|
|
340
|
-
raw.query(
|
|
330
|
+
return rawRun(
|
|
341
331
|
`INSERT OR IGNORE INTO processed_callbacks (id, dedupe_key, call_session_id, created_at) VALUES (?, ?, ?, ?)`,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const changes = raw.query('SELECT changes() as c').get() as { c: number };
|
|
345
|
-
return changes.c > 0;
|
|
332
|
+
uuid(), dedupeKey, callSessionId, Date.now(),
|
|
333
|
+
) > 0;
|
|
346
334
|
}
|
|
347
335
|
|
|
348
336
|
/**
|
|
@@ -362,20 +350,18 @@ export function tryRecordProcessedCallback(
|
|
|
362
350
|
* finalize a claim that was reclaimed by handler B after expiry.
|
|
363
351
|
*/
|
|
364
352
|
export function claimCallback(dedupeKey: string, callSessionId: string): string | null {
|
|
365
|
-
const db = getDb();
|
|
366
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
367
|
-
|
|
368
353
|
// Clear any expired orphaned claims so they can be reprocessed
|
|
369
|
-
|
|
354
|
+
rawRun(
|
|
370
355
|
`DELETE FROM processed_callbacks WHERE dedupe_key = ? AND created_at < ?`,
|
|
371
|
-
|
|
356
|
+
dedupeKey, Date.now() - CLAIM_EXPIRY_MS,
|
|
357
|
+
);
|
|
372
358
|
|
|
373
359
|
const claimId = uuid();
|
|
374
|
-
|
|
360
|
+
const changes = rawRun(
|
|
375
361
|
`INSERT OR IGNORE INTO processed_callbacks (id, dedupe_key, call_session_id, claim_id, created_at) VALUES (?, ?, ?, ?, ?)`,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return changes
|
|
362
|
+
uuid(), dedupeKey, callSessionId, claimId, Date.now(),
|
|
363
|
+
);
|
|
364
|
+
return changes > 0 ? claimId : null;
|
|
379
365
|
}
|
|
380
366
|
|
|
381
367
|
/**
|
|
@@ -386,9 +372,7 @@ export function claimCallback(dedupeKey: string, callSessionId: string): string
|
|
|
386
372
|
* handler A from releasing a claim that was reclaimed by handler B.
|
|
387
373
|
*/
|
|
388
374
|
export function releaseCallbackClaim(dedupeKey: string, claimId: string): void {
|
|
389
|
-
|
|
390
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
391
|
-
raw.query(`DELETE FROM processed_callbacks WHERE dedupe_key = ? AND claim_id = ?`).run(dedupeKey, claimId);
|
|
375
|
+
rawRun(`DELETE FROM processed_callbacks WHERE dedupe_key = ? AND claim_id = ?`, dedupeKey, claimId);
|
|
392
376
|
}
|
|
393
377
|
|
|
394
378
|
/**
|
|
@@ -406,15 +390,13 @@ export function releaseCallbackClaim(dedupeKey: string, claimId: string): void {
|
|
|
406
390
|
* else, so duplicate processing may occur on later retries.
|
|
407
391
|
*/
|
|
408
392
|
export function finalizeCallbackClaim(dedupeKey: string, claimId: string): boolean {
|
|
409
|
-
const db = getDb();
|
|
410
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
411
393
|
// Set created_at far in the future so expiry check never matches
|
|
412
394
|
const NEVER_EXPIRE = Date.now() + 100 * 365 * 24 * 60 * 60 * 1000; // ~100 years
|
|
413
|
-
|
|
395
|
+
const changes = rawRun(
|
|
414
396
|
`UPDATE processed_callbacks SET created_at = ? WHERE dedupe_key = ? AND claim_id = ?`,
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (changes
|
|
397
|
+
NEVER_EXPIRE, dedupeKey, claimId,
|
|
398
|
+
);
|
|
399
|
+
if (changes === 0) {
|
|
418
400
|
log.warn({ dedupeKey, claimId }, 'finalizeCallbackClaim: claim was lost — another handler reclaimed this key after expiry');
|
|
419
401
|
return false;
|
|
420
402
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getConfig } from '../config/loader.js';
|
|
2
2
|
import { getSecureKey } from '../security/secure-keys.js';
|
|
3
|
+
import { ConfigError } from '../util/errors.js';
|
|
3
4
|
|
|
4
5
|
export interface ElevenLabsConfig {
|
|
5
6
|
apiKey: string;
|
|
@@ -14,12 +15,12 @@ export function getElevenLabsConfig(): ElevenLabsConfig {
|
|
|
14
15
|
|
|
15
16
|
const apiKey = getSecureKey('credential:elevenlabs:api_key') ?? '';
|
|
16
17
|
if (!apiKey) {
|
|
17
|
-
throw new
|
|
18
|
+
throw new ConfigError('ElevenLabs API key is not configured. Set credential:elevenlabs:api_key in the secure key store.');
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const agentId = voice.elevenlabs.agentId;
|
|
21
22
|
if (!agentId) {
|
|
22
|
-
throw new
|
|
23
|
+
throw new ConfigError('ElevenLabs agent ID is not configured. Set calls.voice.elevenlabs.agentId in config.');
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
return {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Periodic sweep for expired guardian action requests.
|
|
3
|
+
*
|
|
4
|
+
* Runs on a 60-second interval. When a request has passed its expiresAt
|
|
5
|
+
* timestamp:
|
|
6
|
+
* 1. Expires the request and all its deliveries in the store
|
|
7
|
+
* 2. Expires the associated pending question so the call-side timeout fires
|
|
8
|
+
* 3. Sends expiry notices to external delivery destinations (telegram, sms)
|
|
9
|
+
* 4. Adds an expiry message to mac guardian thread conversations
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getLogger } from '../util/logger.js';
|
|
13
|
+
import {
|
|
14
|
+
getExpiredGuardianActionRequests,
|
|
15
|
+
expireGuardianActionRequest,
|
|
16
|
+
getDeliveriesByRequestId,
|
|
17
|
+
} from '../memory/guardian-action-store.js';
|
|
18
|
+
import { expirePendingQuestions } from './call-store.js';
|
|
19
|
+
import { deliverChannelReply } from '../runtime/gateway-client.js';
|
|
20
|
+
import { addMessage } from '../memory/conversation-store.js';
|
|
21
|
+
|
|
22
|
+
const log = getLogger('guardian-action-sweep');
|
|
23
|
+
|
|
24
|
+
const SWEEP_INTERVAL_MS = 60_000;
|
|
25
|
+
|
|
26
|
+
let sweepTimer: ReturnType<typeof setInterval> | null = null;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sweep expired guardian action requests and clean up.
|
|
30
|
+
*/
|
|
31
|
+
export function sweepExpiredGuardianActions(
|
|
32
|
+
gatewayBaseUrl: string,
|
|
33
|
+
bearerToken?: string,
|
|
34
|
+
): void {
|
|
35
|
+
const expired = getExpiredGuardianActionRequests();
|
|
36
|
+
|
|
37
|
+
for (const request of expired) {
|
|
38
|
+
// Capture deliveries before expiring (since expiry changes their status)
|
|
39
|
+
const deliveries = getDeliveriesByRequestId(request.id);
|
|
40
|
+
|
|
41
|
+
// Expire the request and all deliveries
|
|
42
|
+
expireGuardianActionRequest(request.id);
|
|
43
|
+
|
|
44
|
+
// Expire associated pending questions
|
|
45
|
+
expirePendingQuestions(request.callSessionId);
|
|
46
|
+
|
|
47
|
+
log.info(
|
|
48
|
+
{ requestId: request.id, callSessionId: request.callSessionId },
|
|
49
|
+
'Expired guardian action request',
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Send expiry notices to each delivery destination
|
|
53
|
+
for (const delivery of deliveries) {
|
|
54
|
+
if (delivery.status !== 'sent' && delivery.status !== 'pending') continue;
|
|
55
|
+
|
|
56
|
+
if ((delivery.destinationChannel === 'macos' || delivery.destinationChannel === 'mac') && delivery.destinationConversationId) {
|
|
57
|
+
// Add expiry message to mac guardian thread
|
|
58
|
+
addMessage(
|
|
59
|
+
delivery.destinationConversationId,
|
|
60
|
+
'assistant',
|
|
61
|
+
JSON.stringify([{ type: 'text', text: 'This guardian question has expired without a response.' }]),
|
|
62
|
+
{ userMessageChannel: 'voice', assistantMessageChannel: 'macos' },
|
|
63
|
+
);
|
|
64
|
+
} else if (delivery.destinationChatId) {
|
|
65
|
+
// External channel — send expiry notice
|
|
66
|
+
const deliverUrl = `${gatewayBaseUrl}/deliver/${delivery.destinationChannel}`;
|
|
67
|
+
void (async () => {
|
|
68
|
+
try {
|
|
69
|
+
await deliverChannelReply(deliverUrl, {
|
|
70
|
+
chatId: delivery.destinationChatId!,
|
|
71
|
+
text: 'The guardian question has expired without a response. The call has moved on.',
|
|
72
|
+
assistantId: request.assistantId,
|
|
73
|
+
}, bearerToken);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
log.error(
|
|
76
|
+
{ err, deliveryId: delivery.id, channel: delivery.destinationChannel },
|
|
77
|
+
'Failed to deliver guardian action expiry notice',
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function startGuardianActionSweep(
|
|
87
|
+
gatewayBaseUrl: string,
|
|
88
|
+
bearerToken?: string,
|
|
89
|
+
): void {
|
|
90
|
+
if (sweepTimer) return;
|
|
91
|
+
sweepTimer = setInterval(() => {
|
|
92
|
+
try {
|
|
93
|
+
sweepExpiredGuardianActions(gatewayBaseUrl, bearerToken);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
log.error({ err }, 'Guardian action sweep failed');
|
|
96
|
+
}
|
|
97
|
+
}, SWEEP_INTERVAL_MS);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function stopGuardianActionSweep(): void {
|
|
101
|
+
if (sweepTimer) {
|
|
102
|
+
clearInterval(sweepTimer);
|
|
103
|
+
sweepTimer = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardian dispatch engine for cross-channel voice calls.
|
|
3
|
+
*
|
|
4
|
+
* When a call orchestrator detects ASK_GUARDIAN, this module:
|
|
5
|
+
* 1. Creates a guardian_action_request
|
|
6
|
+
* 2. Determines delivery destinations (telegram, sms, macos)
|
|
7
|
+
* 3. Creates guardian_action_delivery rows for each destination
|
|
8
|
+
* 4. Sends HTTP POST to gateway for external channels
|
|
9
|
+
* 5. Emits IPC events for the mac channel
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getLogger } from '../util/logger.js';
|
|
13
|
+
import { getGatewayInternalBaseUrl } from '../config/env.js';
|
|
14
|
+
import { getActiveBinding } from '../memory/channel-guardian-store.js';
|
|
15
|
+
import {
|
|
16
|
+
createGuardianActionRequest,
|
|
17
|
+
createGuardianActionDelivery,
|
|
18
|
+
updateDeliveryStatus,
|
|
19
|
+
} from '../memory/guardian-action-store.js';
|
|
20
|
+
import { deliverChannelReply } from '../runtime/gateway-client.js';
|
|
21
|
+
import { getUserConsultationTimeoutMs } from './call-constants.js';
|
|
22
|
+
import { getOrCreateConversation } from '../memory/conversation-key-store.js';
|
|
23
|
+
import { addMessage } from '../memory/conversation-store.js';
|
|
24
|
+
import type { CallPendingQuestion } from './types.js';
|
|
25
|
+
import { readHttpToken } from '../util/platform.js';
|
|
26
|
+
import type { ServerMessage } from '../daemon/ipc-contract.js';
|
|
27
|
+
import { generateGuardianCopy } from './guardian-question-copy.js';
|
|
28
|
+
|
|
29
|
+
const log = getLogger('guardian-dispatch');
|
|
30
|
+
|
|
31
|
+
/** Resolve the gateway base URL for internal delivery callbacks. */
|
|
32
|
+
function getGatewayBaseUrl(): string {
|
|
33
|
+
return getGatewayInternalBaseUrl();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface GuardianDispatchParams {
|
|
37
|
+
callSessionId: string;
|
|
38
|
+
conversationId: string;
|
|
39
|
+
assistantId: string;
|
|
40
|
+
pendingQuestion: CallPendingQuestion;
|
|
41
|
+
/** Broadcast function to emit IPC events to connected clients. */
|
|
42
|
+
broadcast?: (msg: ServerMessage) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Dispatch a guardian action request to all configured channels.
|
|
47
|
+
* Fire-and-forget: errors are logged but do not propagate.
|
|
48
|
+
*/
|
|
49
|
+
export async function dispatchGuardianQuestion(params: GuardianDispatchParams): Promise<void> {
|
|
50
|
+
const {
|
|
51
|
+
callSessionId,
|
|
52
|
+
conversationId,
|
|
53
|
+
assistantId,
|
|
54
|
+
pendingQuestion,
|
|
55
|
+
broadcast,
|
|
56
|
+
} = params;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const expiresAt = Date.now() + getUserConsultationTimeoutMs();
|
|
60
|
+
|
|
61
|
+
// Create the action request
|
|
62
|
+
const request = createGuardianActionRequest({
|
|
63
|
+
assistantId,
|
|
64
|
+
kind: 'ask_guardian',
|
|
65
|
+
sourceChannel: 'voice',
|
|
66
|
+
sourceConversationId: conversationId,
|
|
67
|
+
callSessionId,
|
|
68
|
+
pendingQuestionId: pendingQuestion.id,
|
|
69
|
+
questionText: pendingQuestion.questionText,
|
|
70
|
+
expiresAt,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
log.info(
|
|
74
|
+
{ requestId: request.id, requestCode: request.requestCode, callSessionId },
|
|
75
|
+
'Created guardian action request',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Determine delivery destinations
|
|
79
|
+
const destinations: Array<{
|
|
80
|
+
channel: string;
|
|
81
|
+
chatId?: string;
|
|
82
|
+
externalUserId?: string;
|
|
83
|
+
}> = [];
|
|
84
|
+
|
|
85
|
+
// Telegram guardian binding
|
|
86
|
+
const telegramBinding = getActiveBinding(assistantId, 'telegram');
|
|
87
|
+
if (telegramBinding) {
|
|
88
|
+
destinations.push({
|
|
89
|
+
channel: 'telegram',
|
|
90
|
+
chatId: telegramBinding.guardianDeliveryChatId,
|
|
91
|
+
externalUserId: telegramBinding.guardianExternalUserId,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// SMS guardian binding
|
|
96
|
+
const smsBinding = getActiveBinding(assistantId, 'sms');
|
|
97
|
+
if (smsBinding) {
|
|
98
|
+
destinations.push({
|
|
99
|
+
channel: 'sms',
|
|
100
|
+
chatId: smsBinding.guardianDeliveryChatId,
|
|
101
|
+
externalUserId: smsBinding.guardianExternalUserId,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Mac (internal) delivery — always created
|
|
106
|
+
destinations.push({ channel: 'macos' });
|
|
107
|
+
|
|
108
|
+
// Start LLM copy generation concurrently — only awaited in the macOS branch
|
|
109
|
+
// so external channels (Telegram, SMS) dispatch without LLM latency.
|
|
110
|
+
const guardianCopyPromise = generateGuardianCopy(
|
|
111
|
+
pendingQuestion.questionText,
|
|
112
|
+
request.requestCode,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Create delivery rows and dispatch
|
|
116
|
+
for (const dest of destinations) {
|
|
117
|
+
if (dest.channel === 'macos') {
|
|
118
|
+
// Create conversation and delivery row synchronously so they exist
|
|
119
|
+
// before awaiting LLM copy — prevents a race where an external channel
|
|
120
|
+
// reply resolves the request before the macOS delivery is created.
|
|
121
|
+
const macConvKey = `asst:${assistantId}:guardian:request:${request.id}`;
|
|
122
|
+
const { conversationId: macConversationId } = getOrCreateConversation(macConvKey);
|
|
123
|
+
|
|
124
|
+
const delivery = createGuardianActionDelivery({
|
|
125
|
+
requestId: request.id,
|
|
126
|
+
destinationChannel: 'macos',
|
|
127
|
+
destinationConversationId: macConversationId,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Now await LLM-generated copy for the message content and thread title
|
|
131
|
+
const guardianCopy = await guardianCopyPromise;
|
|
132
|
+
|
|
133
|
+
// Add the guardian question as the initial message in the thread
|
|
134
|
+
addMessage(
|
|
135
|
+
macConversationId,
|
|
136
|
+
'assistant',
|
|
137
|
+
JSON.stringify([{ type: 'text', text: guardianCopy.initialMessage }]),
|
|
138
|
+
{ userMessageChannel: 'voice', assistantMessageChannel: 'macos' },
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Emit IPC event for the mac client with the server-created conversation
|
|
142
|
+
if (broadcast) {
|
|
143
|
+
broadcast({
|
|
144
|
+
type: 'guardian_request_thread_created',
|
|
145
|
+
conversationId: macConversationId,
|
|
146
|
+
requestId: request.id,
|
|
147
|
+
callSessionId,
|
|
148
|
+
title: guardianCopy.threadTitle,
|
|
149
|
+
questionText: request.questionText,
|
|
150
|
+
} as ServerMessage);
|
|
151
|
+
}
|
|
152
|
+
updateDeliveryStatus(delivery.id, 'sent');
|
|
153
|
+
log.info({ deliveryId: delivery.id, channel: 'macos', macConversationId }, 'Mac guardian delivery emitted');
|
|
154
|
+
} else {
|
|
155
|
+
const delivery = createGuardianActionDelivery({
|
|
156
|
+
requestId: request.id,
|
|
157
|
+
destinationChannel: dest.channel,
|
|
158
|
+
destinationChatId: dest.chatId,
|
|
159
|
+
destinationExternalUserId: dest.externalUserId,
|
|
160
|
+
});
|
|
161
|
+
// External channel — POST to gateway
|
|
162
|
+
void deliverToExternalChannel(delivery.id, dest.channel, dest.chatId!, request.questionText, request.requestCode, assistantId, readHttpToken() ?? undefined);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch (err) {
|
|
166
|
+
log.error({ err, callSessionId }, 'Failed to dispatch guardian question');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function deliverToExternalChannel(
|
|
171
|
+
deliveryId: string,
|
|
172
|
+
channel: string,
|
|
173
|
+
chatId: string,
|
|
174
|
+
questionText: string,
|
|
175
|
+
requestCode: string,
|
|
176
|
+
assistantId: string,
|
|
177
|
+
bearerToken?: string,
|
|
178
|
+
): Promise<void> {
|
|
179
|
+
const gatewayBase = getGatewayBaseUrl();
|
|
180
|
+
const deliverUrl = `${gatewayBase}/deliver/${channel}`;
|
|
181
|
+
|
|
182
|
+
const messageText = [
|
|
183
|
+
`Your assistant needs your input during a phone call.`,
|
|
184
|
+
``,
|
|
185
|
+
`Question: ${questionText}`,
|
|
186
|
+
``,
|
|
187
|
+
`Reply to this message with your answer. (ref: ${requestCode})`,
|
|
188
|
+
].join('\n');
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
await deliverChannelReply(deliverUrl, {
|
|
192
|
+
chatId,
|
|
193
|
+
text: messageText,
|
|
194
|
+
assistantId,
|
|
195
|
+
}, bearerToken);
|
|
196
|
+
updateDeliveryStatus(deliveryId, 'sent');
|
|
197
|
+
log.info({ deliveryId, channel, chatId }, 'External guardian delivery sent');
|
|
198
|
+
} catch (err) {
|
|
199
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
200
|
+
updateDeliveryStatus(deliveryId, 'failed', errorMsg);
|
|
201
|
+
log.error({ err, deliveryId, channel, chatId }, 'External guardian delivery failed');
|
|
202
|
+
}
|
|
203
|
+
}
|