@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
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store for cross-channel guardian action requests and deliveries.
|
|
3
|
+
*
|
|
4
|
+
* Guardian action requests are created when a voice call's ASK_GUARDIAN
|
|
5
|
+
* marker fires, and deliveries track per-channel dispatch (telegram, sms, mac).
|
|
6
|
+
* Resolution uses first-response-wins semantics: the first channel to
|
|
7
|
+
* answer resolves the request and all other deliveries are marked answered.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { and, eq, lt, inArray } from 'drizzle-orm';
|
|
11
|
+
import { v4 as uuid } from 'uuid';
|
|
12
|
+
import { getDb, rawChanges } from './db.js';
|
|
13
|
+
import {
|
|
14
|
+
guardianActionRequests,
|
|
15
|
+
guardianActionDeliveries,
|
|
16
|
+
} from './schema.js';
|
|
17
|
+
import { getLogger } from '../util/logger.js';
|
|
18
|
+
|
|
19
|
+
const log = getLogger('guardian-action-store');
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Types
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
export type GuardianActionRequestStatus = 'pending' | 'answered' | 'expired' | 'cancelled';
|
|
26
|
+
export type GuardianActionDeliveryStatus = 'pending' | 'sent' | 'failed' | 'answered' | 'expired' | 'cancelled';
|
|
27
|
+
|
|
28
|
+
export interface GuardianActionRequest {
|
|
29
|
+
id: string;
|
|
30
|
+
assistantId: string;
|
|
31
|
+
kind: string;
|
|
32
|
+
sourceChannel: string;
|
|
33
|
+
sourceConversationId: string;
|
|
34
|
+
callSessionId: string;
|
|
35
|
+
pendingQuestionId: string;
|
|
36
|
+
questionText: string;
|
|
37
|
+
requestCode: string;
|
|
38
|
+
status: GuardianActionRequestStatus;
|
|
39
|
+
answerText: string | null;
|
|
40
|
+
answeredByChannel: string | null;
|
|
41
|
+
answeredByExternalUserId: string | null;
|
|
42
|
+
answeredAt: number | null;
|
|
43
|
+
expiresAt: number;
|
|
44
|
+
createdAt: number;
|
|
45
|
+
updatedAt: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface GuardianActionDelivery {
|
|
49
|
+
id: string;
|
|
50
|
+
requestId: string;
|
|
51
|
+
destinationChannel: string;
|
|
52
|
+
destinationConversationId: string | null;
|
|
53
|
+
destinationChatId: string | null;
|
|
54
|
+
destinationExternalUserId: string | null;
|
|
55
|
+
status: GuardianActionDeliveryStatus;
|
|
56
|
+
sentAt: number | null;
|
|
57
|
+
respondedAt: number | null;
|
|
58
|
+
lastError: string | null;
|
|
59
|
+
createdAt: number;
|
|
60
|
+
updatedAt: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Helpers
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
function rowToRequest(row: typeof guardianActionRequests.$inferSelect): GuardianActionRequest {
|
|
68
|
+
return {
|
|
69
|
+
id: row.id,
|
|
70
|
+
assistantId: row.assistantId,
|
|
71
|
+
kind: row.kind,
|
|
72
|
+
sourceChannel: row.sourceChannel,
|
|
73
|
+
sourceConversationId: row.sourceConversationId,
|
|
74
|
+
callSessionId: row.callSessionId,
|
|
75
|
+
pendingQuestionId: row.pendingQuestionId,
|
|
76
|
+
questionText: row.questionText,
|
|
77
|
+
requestCode: row.requestCode,
|
|
78
|
+
status: row.status as GuardianActionRequestStatus,
|
|
79
|
+
answerText: row.answerText,
|
|
80
|
+
answeredByChannel: row.answeredByChannel,
|
|
81
|
+
answeredByExternalUserId: row.answeredByExternalUserId,
|
|
82
|
+
answeredAt: row.answeredAt,
|
|
83
|
+
expiresAt: row.expiresAt,
|
|
84
|
+
createdAt: row.createdAt,
|
|
85
|
+
updatedAt: row.updatedAt,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function rowToDelivery(row: typeof guardianActionDeliveries.$inferSelect): GuardianActionDelivery {
|
|
90
|
+
return {
|
|
91
|
+
id: row.id,
|
|
92
|
+
requestId: row.requestId,
|
|
93
|
+
destinationChannel: row.destinationChannel,
|
|
94
|
+
destinationConversationId: row.destinationConversationId,
|
|
95
|
+
destinationChatId: row.destinationChatId,
|
|
96
|
+
destinationExternalUserId: row.destinationExternalUserId,
|
|
97
|
+
status: row.status as GuardianActionDeliveryStatus,
|
|
98
|
+
sentAt: row.sentAt,
|
|
99
|
+
respondedAt: row.respondedAt,
|
|
100
|
+
lastError: row.lastError,
|
|
101
|
+
createdAt: row.createdAt,
|
|
102
|
+
updatedAt: row.updatedAt,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Generate a short human-readable request code (6 hex chars). */
|
|
107
|
+
function generateRequestCode(): string {
|
|
108
|
+
return uuid().replace(/-/g, '').slice(0, 6).toUpperCase();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Guardian Action Requests
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
export function createGuardianActionRequest(params: {
|
|
116
|
+
assistantId?: string;
|
|
117
|
+
kind: string;
|
|
118
|
+
sourceChannel: string;
|
|
119
|
+
sourceConversationId: string;
|
|
120
|
+
callSessionId: string;
|
|
121
|
+
pendingQuestionId: string;
|
|
122
|
+
questionText: string;
|
|
123
|
+
expiresAt: number;
|
|
124
|
+
}): GuardianActionRequest {
|
|
125
|
+
const db = getDb();
|
|
126
|
+
const now = Date.now();
|
|
127
|
+
const id = uuid();
|
|
128
|
+
|
|
129
|
+
const row = {
|
|
130
|
+
id,
|
|
131
|
+
assistantId: params.assistantId ?? 'self',
|
|
132
|
+
kind: params.kind,
|
|
133
|
+
sourceChannel: params.sourceChannel,
|
|
134
|
+
sourceConversationId: params.sourceConversationId,
|
|
135
|
+
callSessionId: params.callSessionId,
|
|
136
|
+
pendingQuestionId: params.pendingQuestionId,
|
|
137
|
+
questionText: params.questionText,
|
|
138
|
+
requestCode: generateRequestCode(),
|
|
139
|
+
status: 'pending' as const,
|
|
140
|
+
answerText: null,
|
|
141
|
+
answeredByChannel: null,
|
|
142
|
+
answeredByExternalUserId: null,
|
|
143
|
+
answeredAt: null,
|
|
144
|
+
expiresAt: params.expiresAt,
|
|
145
|
+
createdAt: now,
|
|
146
|
+
updatedAt: now,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
db.insert(guardianActionRequests).values(row).run();
|
|
150
|
+
return rowToRequest(row);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function getGuardianActionRequest(id: string): GuardianActionRequest | null {
|
|
154
|
+
const db = getDb();
|
|
155
|
+
const row = db
|
|
156
|
+
.select()
|
|
157
|
+
.from(guardianActionRequests)
|
|
158
|
+
.where(eq(guardianActionRequests.id, id))
|
|
159
|
+
.get();
|
|
160
|
+
return row ? rowToRequest(row) : null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function getByPendingQuestionId(questionId: string): GuardianActionRequest | null {
|
|
164
|
+
const db = getDb();
|
|
165
|
+
const row = db
|
|
166
|
+
.select()
|
|
167
|
+
.from(guardianActionRequests)
|
|
168
|
+
.where(eq(guardianActionRequests.pendingQuestionId, questionId))
|
|
169
|
+
.get();
|
|
170
|
+
return row ? rowToRequest(row) : null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* First-response-wins resolution. Checks that the request is still
|
|
175
|
+
* 'pending' before updating; returns the updated request on success
|
|
176
|
+
* or null if the request was already resolved.
|
|
177
|
+
*/
|
|
178
|
+
export function resolveGuardianActionRequest(
|
|
179
|
+
id: string,
|
|
180
|
+
answerText: string,
|
|
181
|
+
answeredByChannel: string,
|
|
182
|
+
answeredByExternalUserId?: string,
|
|
183
|
+
): GuardianActionRequest | null {
|
|
184
|
+
const db = getDb();
|
|
185
|
+
const now = Date.now();
|
|
186
|
+
|
|
187
|
+
// Atomically check-and-update: only update if status is still 'pending'
|
|
188
|
+
db.update(guardianActionRequests)
|
|
189
|
+
.set({
|
|
190
|
+
status: 'answered',
|
|
191
|
+
answerText,
|
|
192
|
+
answeredByChannel,
|
|
193
|
+
answeredByExternalUserId: answeredByExternalUserId ?? null,
|
|
194
|
+
answeredAt: now,
|
|
195
|
+
updatedAt: now,
|
|
196
|
+
})
|
|
197
|
+
.where(
|
|
198
|
+
and(
|
|
199
|
+
eq(guardianActionRequests.id, id),
|
|
200
|
+
eq(guardianActionRequests.status, 'pending'),
|
|
201
|
+
),
|
|
202
|
+
)
|
|
203
|
+
.run();
|
|
204
|
+
|
|
205
|
+
// Check if the update took effect
|
|
206
|
+
if (rawChanges() === 0) return null;
|
|
207
|
+
|
|
208
|
+
// Mark all deliveries as 'answered'
|
|
209
|
+
db.update(guardianActionDeliveries)
|
|
210
|
+
.set({ status: 'answered', respondedAt: now, updatedAt: now })
|
|
211
|
+
.where(eq(guardianActionDeliveries.requestId, id))
|
|
212
|
+
.run();
|
|
213
|
+
|
|
214
|
+
return getGuardianActionRequest(id);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Expire a guardian action request and all its deliveries.
|
|
219
|
+
*/
|
|
220
|
+
export function expireGuardianActionRequest(id: string): void {
|
|
221
|
+
const db = getDb();
|
|
222
|
+
const now = Date.now();
|
|
223
|
+
|
|
224
|
+
db.update(guardianActionRequests)
|
|
225
|
+
.set({ status: 'expired', updatedAt: now })
|
|
226
|
+
.where(
|
|
227
|
+
and(
|
|
228
|
+
eq(guardianActionRequests.id, id),
|
|
229
|
+
eq(guardianActionRequests.status, 'pending'),
|
|
230
|
+
),
|
|
231
|
+
)
|
|
232
|
+
.run();
|
|
233
|
+
|
|
234
|
+
db.update(guardianActionDeliveries)
|
|
235
|
+
.set({ status: 'expired', updatedAt: now })
|
|
236
|
+
.where(
|
|
237
|
+
and(
|
|
238
|
+
eq(guardianActionDeliveries.requestId, id),
|
|
239
|
+
inArray(guardianActionDeliveries.status, ['pending', 'sent']),
|
|
240
|
+
),
|
|
241
|
+
)
|
|
242
|
+
.run();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get all pending guardian action requests that have expired.
|
|
247
|
+
*/
|
|
248
|
+
export function getExpiredGuardianActionRequests(): GuardianActionRequest[] {
|
|
249
|
+
const db = getDb();
|
|
250
|
+
const now = Date.now();
|
|
251
|
+
return db
|
|
252
|
+
.select()
|
|
253
|
+
.from(guardianActionRequests)
|
|
254
|
+
.where(
|
|
255
|
+
and(
|
|
256
|
+
eq(guardianActionRequests.status, 'pending'),
|
|
257
|
+
lt(guardianActionRequests.expiresAt, now),
|
|
258
|
+
),
|
|
259
|
+
)
|
|
260
|
+
.all()
|
|
261
|
+
.map(rowToRequest);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get all deliveries for a specific request.
|
|
266
|
+
*/
|
|
267
|
+
export function getDeliveriesByRequestId(requestId: string): GuardianActionDelivery[] {
|
|
268
|
+
const db = getDb();
|
|
269
|
+
return db
|
|
270
|
+
.select()
|
|
271
|
+
.from(guardianActionDeliveries)
|
|
272
|
+
.where(eq(guardianActionDeliveries.requestId, requestId))
|
|
273
|
+
.all()
|
|
274
|
+
.map(rowToDelivery);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Cancel a guardian action request and all its deliveries.
|
|
279
|
+
*/
|
|
280
|
+
export function cancelGuardianActionRequest(id: string): void {
|
|
281
|
+
const db = getDb();
|
|
282
|
+
const now = Date.now();
|
|
283
|
+
|
|
284
|
+
db.update(guardianActionRequests)
|
|
285
|
+
.set({ status: 'cancelled', updatedAt: now })
|
|
286
|
+
.where(
|
|
287
|
+
and(
|
|
288
|
+
eq(guardianActionRequests.id, id),
|
|
289
|
+
eq(guardianActionRequests.status, 'pending'),
|
|
290
|
+
),
|
|
291
|
+
)
|
|
292
|
+
.run();
|
|
293
|
+
|
|
294
|
+
db.update(guardianActionDeliveries)
|
|
295
|
+
.set({ status: 'cancelled', updatedAt: now })
|
|
296
|
+
.where(
|
|
297
|
+
and(
|
|
298
|
+
eq(guardianActionDeliveries.requestId, id),
|
|
299
|
+
inArray(guardianActionDeliveries.status, ['pending', 'sent']),
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
.run();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
// Guardian Action Deliveries
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
export function createGuardianActionDelivery(params: {
|
|
310
|
+
requestId: string;
|
|
311
|
+
destinationChannel: string;
|
|
312
|
+
destinationConversationId?: string;
|
|
313
|
+
destinationChatId?: string;
|
|
314
|
+
destinationExternalUserId?: string;
|
|
315
|
+
}): GuardianActionDelivery {
|
|
316
|
+
const db = getDb();
|
|
317
|
+
const now = Date.now();
|
|
318
|
+
const id = uuid();
|
|
319
|
+
|
|
320
|
+
const row = {
|
|
321
|
+
id,
|
|
322
|
+
requestId: params.requestId,
|
|
323
|
+
destinationChannel: params.destinationChannel,
|
|
324
|
+
destinationConversationId: params.destinationConversationId ?? null,
|
|
325
|
+
destinationChatId: params.destinationChatId ?? null,
|
|
326
|
+
destinationExternalUserId: params.destinationExternalUserId ?? null,
|
|
327
|
+
status: 'pending' as const,
|
|
328
|
+
sentAt: null,
|
|
329
|
+
respondedAt: null,
|
|
330
|
+
lastError: null,
|
|
331
|
+
createdAt: now,
|
|
332
|
+
updatedAt: now,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
db.insert(guardianActionDeliveries).values(row).run();
|
|
336
|
+
return rowToDelivery(row);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Look up pending deliveries for a specific destination.
|
|
341
|
+
* Used by inbound message routing to match incoming answers to deliveries.
|
|
342
|
+
*/
|
|
343
|
+
export function getPendingDeliveriesByDestination(
|
|
344
|
+
assistantId: string,
|
|
345
|
+
channel: string,
|
|
346
|
+
chatId: string,
|
|
347
|
+
): GuardianActionDelivery[] {
|
|
348
|
+
try {
|
|
349
|
+
const db = getDb();
|
|
350
|
+
|
|
351
|
+
// Join deliveries with requests to filter by assistantId
|
|
352
|
+
const rows = db
|
|
353
|
+
.select({
|
|
354
|
+
delivery: guardianActionDeliveries,
|
|
355
|
+
})
|
|
356
|
+
.from(guardianActionDeliveries)
|
|
357
|
+
.innerJoin(
|
|
358
|
+
guardianActionRequests,
|
|
359
|
+
eq(guardianActionDeliveries.requestId, guardianActionRequests.id),
|
|
360
|
+
)
|
|
361
|
+
.where(
|
|
362
|
+
and(
|
|
363
|
+
eq(guardianActionRequests.assistantId, assistantId),
|
|
364
|
+
eq(guardianActionRequests.status, 'pending'),
|
|
365
|
+
eq(guardianActionDeliveries.destinationChannel, channel),
|
|
366
|
+
eq(guardianActionDeliveries.destinationChatId, chatId),
|
|
367
|
+
eq(guardianActionDeliveries.status, 'sent'),
|
|
368
|
+
),
|
|
369
|
+
)
|
|
370
|
+
.all();
|
|
371
|
+
|
|
372
|
+
return rows.map((r) => rowToDelivery(r.delivery));
|
|
373
|
+
} catch (err) {
|
|
374
|
+
if (err instanceof Error && err.message.includes('no such table')) {
|
|
375
|
+
log.warn({ err }, 'guardian tables not yet created');
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
throw err;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Look up a pending delivery by destination conversation ID (for mac channel routing).
|
|
384
|
+
*/
|
|
385
|
+
export function getPendingDeliveryByConversation(conversationId: string): GuardianActionDelivery | null {
|
|
386
|
+
try {
|
|
387
|
+
const db = getDb();
|
|
388
|
+
const rows = db
|
|
389
|
+
.select({ delivery: guardianActionDeliveries })
|
|
390
|
+
.from(guardianActionDeliveries)
|
|
391
|
+
.innerJoin(
|
|
392
|
+
guardianActionRequests,
|
|
393
|
+
eq(guardianActionDeliveries.requestId, guardianActionRequests.id),
|
|
394
|
+
)
|
|
395
|
+
.where(
|
|
396
|
+
and(
|
|
397
|
+
eq(guardianActionDeliveries.destinationConversationId, conversationId),
|
|
398
|
+
eq(guardianActionDeliveries.status, 'sent'),
|
|
399
|
+
eq(guardianActionRequests.status, 'pending'),
|
|
400
|
+
),
|
|
401
|
+
)
|
|
402
|
+
.all();
|
|
403
|
+
return rows.length > 0 ? rowToDelivery(rows[0].delivery) : null;
|
|
404
|
+
} catch (err) {
|
|
405
|
+
if (err instanceof Error && err.message.includes('no such table')) {
|
|
406
|
+
log.warn({ err }, 'guardian tables not yet created');
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
throw err;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export function updateDeliveryStatus(
|
|
414
|
+
deliveryId: string,
|
|
415
|
+
status: GuardianActionDeliveryStatus,
|
|
416
|
+
error?: string,
|
|
417
|
+
): void {
|
|
418
|
+
const db = getDb();
|
|
419
|
+
const now = Date.now();
|
|
420
|
+
|
|
421
|
+
const updates: Record<string, unknown> = { status, updatedAt: now };
|
|
422
|
+
if (status === 'sent') updates.sentAt = now;
|
|
423
|
+
if (status === 'answered') updates.respondedAt = now;
|
|
424
|
+
if (error !== undefined) updates.lastError = error;
|
|
425
|
+
|
|
426
|
+
db.update(guardianActionDeliveries)
|
|
427
|
+
.set(updates)
|
|
428
|
+
.where(eq(guardianActionDeliveries.id, deliveryId))
|
|
429
|
+
.run();
|
|
430
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projects escalation state from channel_guardian_approval_requests onto
|
|
3
|
+
* assistant_inbox_thread_state.
|
|
4
|
+
*
|
|
5
|
+
* Keeps the inbox UI's escalation badges (pending_escalation_count,
|
|
6
|
+
* has_pending_escalation) in sync with the current set of pending
|
|
7
|
+
* guardian approval requests.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { countPendingByConversation } from './channel-guardian-store.js';
|
|
11
|
+
import { updateEscalationState } from './inbox-thread-store.js';
|
|
12
|
+
import { getDb } from './db.js';
|
|
13
|
+
import { getSqliteFrom } from './db-connection.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Recompute pending escalation counts for all inbox threads and update
|
|
17
|
+
* the thread state rows. Called periodically or after approval decisions
|
|
18
|
+
* to keep the inbox UI's escalation badges accurate.
|
|
19
|
+
*
|
|
20
|
+
* Uses a single SQL UPDATE to set counts for all threads in one shot,
|
|
21
|
+
* handling both new escalations and cleared ones atomically.
|
|
22
|
+
*/
|
|
23
|
+
export function refreshEscalationProjection(assistantId: string = 'self'): void {
|
|
24
|
+
const db = getDb();
|
|
25
|
+
const sqlite = getSqliteFrom(db);
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
|
|
28
|
+
sqlite
|
|
29
|
+
.query(
|
|
30
|
+
`UPDATE assistant_inbox_thread_state
|
|
31
|
+
SET pending_escalation_count = (
|
|
32
|
+
SELECT COUNT(*) FROM channel_guardian_approval_requests
|
|
33
|
+
WHERE channel_guardian_approval_requests.conversation_id = assistant_inbox_thread_state.conversation_id
|
|
34
|
+
AND channel_guardian_approval_requests.status = 'pending'
|
|
35
|
+
AND channel_guardian_approval_requests.assistant_id = ?1
|
|
36
|
+
),
|
|
37
|
+
has_pending_escalation = CASE WHEN (
|
|
38
|
+
SELECT COUNT(*) FROM channel_guardian_approval_requests
|
|
39
|
+
WHERE channel_guardian_approval_requests.conversation_id = assistant_inbox_thread_state.conversation_id
|
|
40
|
+
AND channel_guardian_approval_requests.status = 'pending'
|
|
41
|
+
AND channel_guardian_approval_requests.assistant_id = ?1
|
|
42
|
+
) > 0 THEN 1 ELSE 0 END,
|
|
43
|
+
updated_at = ?2
|
|
44
|
+
WHERE assistant_id = ?1`,
|
|
45
|
+
)
|
|
46
|
+
.run(assistantId, now);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Refresh escalation state for a single thread. More efficient than
|
|
51
|
+
* refreshing all threads when only one conversation changed.
|
|
52
|
+
*/
|
|
53
|
+
export function refreshThreadEscalation(
|
|
54
|
+
conversationId: string,
|
|
55
|
+
assistantId: string = 'self',
|
|
56
|
+
): void {
|
|
57
|
+
const pendingCount = countPendingByConversation(conversationId, assistantId);
|
|
58
|
+
updateEscalationState(conversationId, pendingCount);
|
|
59
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query helpers for the assistant_inbox_thread_state table.
|
|
3
|
+
*
|
|
4
|
+
* Provides CRUD operations for inbox thread state — the denormalized
|
|
5
|
+
* view that powers the assistant's inbox UI with per-thread metadata
|
|
6
|
+
* (unread counts, escalation state, last activity timestamps).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { and, eq, sql } from 'drizzle-orm';
|
|
10
|
+
import { getDb } from './db.js';
|
|
11
|
+
import { assistantInboxThreadState } from './schema.js';
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Types
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
export type InboxThread = typeof assistantInboxThreadState.$inferSelect;
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Queries
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* List inbox threads ordered by most recent activity.
|
|
25
|
+
* NULL last_message_at values sort last.
|
|
26
|
+
*/
|
|
27
|
+
export function listThreads(params?: {
|
|
28
|
+
assistantId?: string;
|
|
29
|
+
limit?: number;
|
|
30
|
+
offset?: number;
|
|
31
|
+
}): InboxThread[] {
|
|
32
|
+
const db = getDb();
|
|
33
|
+
const assistantId = params?.assistantId ?? 'self';
|
|
34
|
+
const limit = params?.limit ?? 50;
|
|
35
|
+
const offset = params?.offset ?? 0;
|
|
36
|
+
|
|
37
|
+
return db
|
|
38
|
+
.select()
|
|
39
|
+
.from(assistantInboxThreadState)
|
|
40
|
+
.where(eq(assistantInboxThreadState.assistantId, assistantId))
|
|
41
|
+
.orderBy(sql`${assistantInboxThreadState.lastMessageAt} DESC NULLS LAST`)
|
|
42
|
+
.limit(limit)
|
|
43
|
+
.offset(offset)
|
|
44
|
+
.all();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get a single thread by its conversation ID (the primary key).
|
|
49
|
+
*/
|
|
50
|
+
export function getThread(conversationId: string): InboxThread | null {
|
|
51
|
+
const db = getDb();
|
|
52
|
+
const row = db
|
|
53
|
+
.select()
|
|
54
|
+
.from(assistantInboxThreadState)
|
|
55
|
+
.where(eq(assistantInboxThreadState.conversationId, conversationId))
|
|
56
|
+
.get();
|
|
57
|
+
|
|
58
|
+
return row ?? null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Look up a thread by its unique binding (assistant_id, source_channel, external_chat_id).
|
|
63
|
+
*/
|
|
64
|
+
export function getThreadByBinding(
|
|
65
|
+
assistantId: string,
|
|
66
|
+
sourceChannel: string,
|
|
67
|
+
externalChatId: string,
|
|
68
|
+
): InboxThread | null {
|
|
69
|
+
const db = getDb();
|
|
70
|
+
const row = db
|
|
71
|
+
.select()
|
|
72
|
+
.from(assistantInboxThreadState)
|
|
73
|
+
.where(
|
|
74
|
+
and(
|
|
75
|
+
eq(assistantInboxThreadState.assistantId, assistantId),
|
|
76
|
+
eq(assistantInboxThreadState.sourceChannel, sourceChannel),
|
|
77
|
+
eq(assistantInboxThreadState.externalChatId, externalChatId),
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
.get();
|
|
81
|
+
|
|
82
|
+
return row ?? null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create or update an inbox thread state row.
|
|
87
|
+
* If a row with the given conversationId exists, updates display fields and updated_at.
|
|
88
|
+
* Otherwise, creates a new row with counters initialized to 0.
|
|
89
|
+
*/
|
|
90
|
+
export function upsertThread(params: {
|
|
91
|
+
conversationId: string;
|
|
92
|
+
assistantId?: string;
|
|
93
|
+
sourceChannel: string;
|
|
94
|
+
externalChatId: string;
|
|
95
|
+
externalUserId?: string;
|
|
96
|
+
displayName?: string;
|
|
97
|
+
username?: string;
|
|
98
|
+
}): InboxThread {
|
|
99
|
+
const db = getDb();
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
const assistantId = params.assistantId ?? 'self';
|
|
102
|
+
|
|
103
|
+
const existing = getThread(params.conversationId);
|
|
104
|
+
|
|
105
|
+
if (existing) {
|
|
106
|
+
db.update(assistantInboxThreadState)
|
|
107
|
+
.set({
|
|
108
|
+
displayName: params.displayName ?? existing.displayName,
|
|
109
|
+
username: params.username ?? existing.username,
|
|
110
|
+
externalUserId: params.externalUserId ?? existing.externalUserId,
|
|
111
|
+
updatedAt: now,
|
|
112
|
+
})
|
|
113
|
+
.where(eq(assistantInboxThreadState.conversationId, params.conversationId))
|
|
114
|
+
.run();
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
...existing,
|
|
118
|
+
displayName: params.displayName ?? existing.displayName,
|
|
119
|
+
username: params.username ?? existing.username,
|
|
120
|
+
externalUserId: params.externalUserId ?? existing.externalUserId,
|
|
121
|
+
updatedAt: now,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const row = {
|
|
126
|
+
conversationId: params.conversationId,
|
|
127
|
+
assistantId,
|
|
128
|
+
sourceChannel: params.sourceChannel,
|
|
129
|
+
externalChatId: params.externalChatId,
|
|
130
|
+
externalUserId: params.externalUserId ?? null,
|
|
131
|
+
displayName: params.displayName ?? null,
|
|
132
|
+
username: params.username ?? null,
|
|
133
|
+
lastInboundAt: null,
|
|
134
|
+
lastOutboundAt: null,
|
|
135
|
+
lastMessageAt: null,
|
|
136
|
+
unreadCount: 0,
|
|
137
|
+
pendingEscalationCount: 0,
|
|
138
|
+
hasPendingEscalation: 0,
|
|
139
|
+
createdAt: now,
|
|
140
|
+
updatedAt: now,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
db.insert(assistantInboxThreadState).values(row).run();
|
|
144
|
+
|
|
145
|
+
return row;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Record message activity on a thread.
|
|
150
|
+
* - inbound: updates last_inbound_at, last_message_at, increments unread_count
|
|
151
|
+
* - outbound: updates last_outbound_at, last_message_at, resets unread_count to 0
|
|
152
|
+
*/
|
|
153
|
+
export function updateThreadActivity(
|
|
154
|
+
conversationId: string,
|
|
155
|
+
direction: 'inbound' | 'outbound',
|
|
156
|
+
): void {
|
|
157
|
+
const db = getDb();
|
|
158
|
+
const now = Date.now();
|
|
159
|
+
|
|
160
|
+
if (direction === 'inbound') {
|
|
161
|
+
db.update(assistantInboxThreadState)
|
|
162
|
+
.set({
|
|
163
|
+
lastInboundAt: now,
|
|
164
|
+
lastMessageAt: now,
|
|
165
|
+
unreadCount: sql`${assistantInboxThreadState.unreadCount} + 1`,
|
|
166
|
+
updatedAt: now,
|
|
167
|
+
})
|
|
168
|
+
.where(eq(assistantInboxThreadState.conversationId, conversationId))
|
|
169
|
+
.run();
|
|
170
|
+
} else {
|
|
171
|
+
db.update(assistantInboxThreadState)
|
|
172
|
+
.set({
|
|
173
|
+
lastOutboundAt: now,
|
|
174
|
+
lastMessageAt: now,
|
|
175
|
+
unreadCount: 0,
|
|
176
|
+
updatedAt: now,
|
|
177
|
+
})
|
|
178
|
+
.where(eq(assistantInboxThreadState.conversationId, conversationId))
|
|
179
|
+
.run();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Mark a thread as read by resetting its unread count.
|
|
185
|
+
*/
|
|
186
|
+
export function markThreadRead(conversationId: string): void {
|
|
187
|
+
const db = getDb();
|
|
188
|
+
const now = Date.now();
|
|
189
|
+
|
|
190
|
+
db.update(assistantInboxThreadState)
|
|
191
|
+
.set({
|
|
192
|
+
unreadCount: 0,
|
|
193
|
+
updatedAt: now,
|
|
194
|
+
})
|
|
195
|
+
.where(eq(assistantInboxThreadState.conversationId, conversationId))
|
|
196
|
+
.run();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Update the escalation state for a thread.
|
|
201
|
+
* Sets pending_escalation_count and derives has_pending_escalation from it.
|
|
202
|
+
*/
|
|
203
|
+
export function updateEscalationState(
|
|
204
|
+
conversationId: string,
|
|
205
|
+
pendingCount: number,
|
|
206
|
+
): void {
|
|
207
|
+
const db = getDb();
|
|
208
|
+
const now = Date.now();
|
|
209
|
+
|
|
210
|
+
db.update(assistantInboxThreadState)
|
|
211
|
+
.set({
|
|
212
|
+
pendingEscalationCount: pendingCount,
|
|
213
|
+
hasPendingEscalation: pendingCount > 0 ? 1 : 0,
|
|
214
|
+
updatedAt: now,
|
|
215
|
+
})
|
|
216
|
+
.where(eq(assistantInboxThreadState.conversationId, conversationId))
|
|
217
|
+
.run();
|
|
218
|
+
}
|