@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
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterAll, mock } from 'bun:test';
|
|
2
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const testDir = mkdtempSync(join(tmpdir(), 'guardian-dispatch-test-'));
|
|
7
|
+
|
|
8
|
+
mock.module('../util/platform.js', () => ({
|
|
9
|
+
getDataDir: () => testDir,
|
|
10
|
+
isMacOS: () => process.platform === 'darwin',
|
|
11
|
+
isLinux: () => process.platform === 'linux',
|
|
12
|
+
isWindows: () => process.platform === 'win32',
|
|
13
|
+
getSocketPath: () => join(testDir, 'test.sock'),
|
|
14
|
+
getPidPath: () => join(testDir, 'test.pid'),
|
|
15
|
+
getDbPath: () => join(testDir, 'test.db'),
|
|
16
|
+
getLogPath: () => join(testDir, 'test.log'),
|
|
17
|
+
ensureDataDir: () => {},
|
|
18
|
+
readHttpToken: () => null,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
mock.module('../util/logger.js', () => ({
|
|
22
|
+
getLogger: () =>
|
|
23
|
+
new Proxy({} as Record<string, unknown>, {
|
|
24
|
+
get: () => () => {},
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
mock.module('../config/env.js', () => ({
|
|
29
|
+
getGatewayInternalBaseUrl: () => 'http://localhost:3000',
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
let mockTelegramBinding: unknown = null;
|
|
33
|
+
let mockSmsBinding: unknown = null;
|
|
34
|
+
|
|
35
|
+
mock.module('../memory/channel-guardian-store.js', () => ({
|
|
36
|
+
getActiveBinding: (_assistantId: string, channel: string) => {
|
|
37
|
+
if (channel === 'telegram') return mockTelegramBinding;
|
|
38
|
+
if (channel === 'sms') return mockSmsBinding;
|
|
39
|
+
return null;
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
mock.module('../config/loader.js', () => ({
|
|
44
|
+
getConfig: () => ({
|
|
45
|
+
calls: {
|
|
46
|
+
userConsultTimeoutSeconds: 120,
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
const deliveredMessages: Array<{ url: string; body: Record<string, unknown> }> = [];
|
|
52
|
+
|
|
53
|
+
mock.module('../runtime/gateway-client.js', () => ({
|
|
54
|
+
deliverChannelReply: async (url: string, body: Record<string, unknown>) => {
|
|
55
|
+
deliveredMessages.push({ url, body });
|
|
56
|
+
},
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
// Mock guardian-question-copy to return deterministic values without hitting a real provider.
|
|
60
|
+
// Only generateGuardianCopy (the async LLM call) is mocked; buildFallbackCopy is the real
|
|
61
|
+
// implementation passed through so guardian-dispatch can use it if needed.
|
|
62
|
+
let mockGuardianCopy = {
|
|
63
|
+
threadTitle: '\u{1F6A8} Caller needs the gate code',
|
|
64
|
+
initialMessage: 'Your assistant needs your input during a live phone call.\n\nQuestion: What is the gate code?\n\nReply to this message with your answer.',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
mock.module('../calls/guardian-question-copy.js', () => ({
|
|
68
|
+
generateGuardianCopy: async (questionText: string) => ({
|
|
69
|
+
threadTitle: mockGuardianCopy.threadTitle,
|
|
70
|
+
initialMessage: mockGuardianCopy.initialMessage.includes(questionText)
|
|
71
|
+
? mockGuardianCopy.initialMessage
|
|
72
|
+
: mockGuardianCopy.initialMessage.replace(/Question: .*/, `Question: ${questionText}`),
|
|
73
|
+
}),
|
|
74
|
+
// Pass through the real buildFallbackCopy implementation (tested in guardian-question-copy.test.ts)
|
|
75
|
+
buildFallbackCopy: (questionText: string) => ({
|
|
76
|
+
threadTitle: `\u26A0\uFE0F ${questionText.slice(0, 70)}`,
|
|
77
|
+
initialMessage: [
|
|
78
|
+
'Your assistant needs your input during a phone call.',
|
|
79
|
+
'',
|
|
80
|
+
`Question: ${questionText}`,
|
|
81
|
+
'',
|
|
82
|
+
'Reply to this message with your answer.',
|
|
83
|
+
].join('\n'),
|
|
84
|
+
}),
|
|
85
|
+
}));
|
|
86
|
+
|
|
87
|
+
import { initializeDb, getDb, resetDb } from '../memory/db.js';
|
|
88
|
+
import { conversations } from '../memory/schema.js';
|
|
89
|
+
import { createCallSession, createPendingQuestion } from '../calls/call-store.js';
|
|
90
|
+
import { dispatchGuardianQuestion } from '../calls/guardian-dispatch.js';
|
|
91
|
+
import { getMessages } from '../memory/conversation-store.js';
|
|
92
|
+
|
|
93
|
+
initializeDb();
|
|
94
|
+
|
|
95
|
+
function ensureConversation(id: string): void {
|
|
96
|
+
const db = getDb();
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
db.insert(conversations).values({
|
|
99
|
+
id,
|
|
100
|
+
title: `Conversation ${id}`,
|
|
101
|
+
createdAt: now,
|
|
102
|
+
updatedAt: now,
|
|
103
|
+
}).run();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resetTables(): void {
|
|
107
|
+
const db = getDb();
|
|
108
|
+
db.run('DELETE FROM guardian_action_deliveries');
|
|
109
|
+
db.run('DELETE FROM guardian_action_requests');
|
|
110
|
+
db.run('DELETE FROM call_pending_questions');
|
|
111
|
+
db.run('DELETE FROM call_events');
|
|
112
|
+
db.run('DELETE FROM call_sessions');
|
|
113
|
+
db.run('DELETE FROM messages');
|
|
114
|
+
db.run('DELETE FROM conversations');
|
|
115
|
+
mockTelegramBinding = null;
|
|
116
|
+
mockSmsBinding = null;
|
|
117
|
+
deliveredMessages.length = 0;
|
|
118
|
+
mockGuardianCopy = {
|
|
119
|
+
threadTitle: '\u{1F6A8} Caller needs the gate code',
|
|
120
|
+
initialMessage: 'Your assistant needs your input during a live phone call.\n\nQuestion: What is the gate code?\n\nReply to this message with your answer.',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
describe('guardian-dispatch', () => {
|
|
125
|
+
beforeEach(() => {
|
|
126
|
+
resetTables();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
afterAll(() => {
|
|
130
|
+
resetDb();
|
|
131
|
+
try { rmSync(testDir, { recursive: true }); } catch { /* best effort */ }
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('creates a guardian action request with mac delivery', async () => {
|
|
135
|
+
const convId = 'conv-dispatch-1';
|
|
136
|
+
ensureConversation(convId);
|
|
137
|
+
|
|
138
|
+
const session = createCallSession({
|
|
139
|
+
conversationId: convId,
|
|
140
|
+
provider: 'twilio',
|
|
141
|
+
fromNumber: '+15550001111',
|
|
142
|
+
toNumber: '+15550002222',
|
|
143
|
+
});
|
|
144
|
+
const pq = createPendingQuestion(session.id, 'What is the gate code?');
|
|
145
|
+
|
|
146
|
+
await dispatchGuardianQuestion({
|
|
147
|
+
callSessionId: session.id,
|
|
148
|
+
conversationId: convId,
|
|
149
|
+
assistantId: 'self',
|
|
150
|
+
pendingQuestion: pq,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Should have created a guardian action request in the DB
|
|
154
|
+
const db = getDb();
|
|
155
|
+
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
156
|
+
const requests = raw.query('SELECT * FROM guardian_action_requests WHERE call_session_id = ?').all(session.id) as Array<{ id: string; status: string; question_text: string }>;
|
|
157
|
+
expect(requests).toHaveLength(1);
|
|
158
|
+
expect(requests[0].status).toBe('pending');
|
|
159
|
+
expect(requests[0].question_text).toBe('What is the gate code?');
|
|
160
|
+
|
|
161
|
+
// Should have at least a mac delivery
|
|
162
|
+
const deliveries = raw.query('SELECT * FROM guardian_action_deliveries WHERE request_id = ?').all(requests[0].id) as Array<{ destination_channel: string; status: string }>;
|
|
163
|
+
const macDelivery = deliveries.find(d => d.destination_channel === 'macos');
|
|
164
|
+
expect(macDelivery).toBeDefined();
|
|
165
|
+
expect(macDelivery!.status).toBe('sent');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('creates telegram delivery when binding exists', async () => {
|
|
169
|
+
const convId = 'conv-dispatch-2';
|
|
170
|
+
ensureConversation(convId);
|
|
171
|
+
|
|
172
|
+
mockTelegramBinding = {
|
|
173
|
+
guardianDeliveryChatId: 'tg-chat-999',
|
|
174
|
+
guardianExternalUserId: 'tg-user-888',
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const session = createCallSession({
|
|
178
|
+
conversationId: convId,
|
|
179
|
+
provider: 'twilio',
|
|
180
|
+
fromNumber: '+15550001111',
|
|
181
|
+
toNumber: '+15550002222',
|
|
182
|
+
});
|
|
183
|
+
const pq = createPendingQuestion(session.id, 'Should I proceed?');
|
|
184
|
+
|
|
185
|
+
await dispatchGuardianQuestion({
|
|
186
|
+
callSessionId: session.id,
|
|
187
|
+
conversationId: convId,
|
|
188
|
+
assistantId: 'self',
|
|
189
|
+
pendingQuestion: pq,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Wait briefly for async delivery
|
|
193
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
194
|
+
|
|
195
|
+
// Should have sent to telegram via gateway
|
|
196
|
+
expect(deliveredMessages.length).toBeGreaterThanOrEqual(1);
|
|
197
|
+
const telegramMessage = deliveredMessages.find(m => m.url.includes('/deliver/telegram'));
|
|
198
|
+
expect(telegramMessage).toBeDefined();
|
|
199
|
+
expect(telegramMessage!.body.chatId).toBe('tg-chat-999');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('emits IPC event via broadcast for mac delivery', async () => {
|
|
203
|
+
const convId = 'conv-dispatch-3';
|
|
204
|
+
ensureConversation(convId);
|
|
205
|
+
|
|
206
|
+
const session = createCallSession({
|
|
207
|
+
conversationId: convId,
|
|
208
|
+
provider: 'twilio',
|
|
209
|
+
fromNumber: '+15550001111',
|
|
210
|
+
toNumber: '+15550002222',
|
|
211
|
+
});
|
|
212
|
+
const pq = createPendingQuestion(session.id, 'Approve action?');
|
|
213
|
+
|
|
214
|
+
const broadcastedMessages: unknown[] = [];
|
|
215
|
+
const broadcastFn = (msg: unknown) => { broadcastedMessages.push(msg); };
|
|
216
|
+
|
|
217
|
+
await dispatchGuardianQuestion({
|
|
218
|
+
callSessionId: session.id,
|
|
219
|
+
conversationId: convId,
|
|
220
|
+
assistantId: 'self',
|
|
221
|
+
pendingQuestion: pq,
|
|
222
|
+
broadcast: broadcastFn,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(broadcastedMessages).toHaveLength(1);
|
|
226
|
+
const msg = broadcastedMessages[0] as Record<string, unknown>;
|
|
227
|
+
expect(msg.type).toBe('guardian_request_thread_created');
|
|
228
|
+
expect(msg.callSessionId).toBe(session.id);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('adds initial guardian message to mac conversation', async () => {
|
|
232
|
+
const convId = 'conv-dispatch-4';
|
|
233
|
+
ensureConversation(convId);
|
|
234
|
+
|
|
235
|
+
const session = createCallSession({
|
|
236
|
+
conversationId: convId,
|
|
237
|
+
provider: 'twilio',
|
|
238
|
+
fromNumber: '+15550001111',
|
|
239
|
+
toNumber: '+15550002222',
|
|
240
|
+
});
|
|
241
|
+
const pq = createPendingQuestion(session.id, 'What is the password?');
|
|
242
|
+
|
|
243
|
+
const broadcastedMessages: unknown[] = [];
|
|
244
|
+
const broadcastFn = (msg: unknown) => { broadcastedMessages.push(msg); };
|
|
245
|
+
|
|
246
|
+
await dispatchGuardianQuestion({
|
|
247
|
+
callSessionId: session.id,
|
|
248
|
+
conversationId: convId,
|
|
249
|
+
assistantId: 'self',
|
|
250
|
+
pendingQuestion: pq,
|
|
251
|
+
broadcast: broadcastFn,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Get the mac conversation ID from the broadcast
|
|
255
|
+
const msg = broadcastedMessages[0] as Record<string, unknown>;
|
|
256
|
+
const macConvId = msg.conversationId as string;
|
|
257
|
+
|
|
258
|
+
// The mac conversation should have a message with the question text
|
|
259
|
+
const messages = getMessages(macConvId);
|
|
260
|
+
expect(messages.length).toBeGreaterThanOrEqual(1);
|
|
261
|
+
const content = messages[0].content;
|
|
262
|
+
expect(content).toContain('What is the password?');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('does not throw on dispatch errors (fire-and-forget)', async () => {
|
|
266
|
+
const convId = 'conv-dispatch-5';
|
|
267
|
+
ensureConversation(convId);
|
|
268
|
+
|
|
269
|
+
const session = createCallSession({
|
|
270
|
+
conversationId: convId,
|
|
271
|
+
provider: 'twilio',
|
|
272
|
+
fromNumber: '+15550001111',
|
|
273
|
+
toNumber: '+15550002222',
|
|
274
|
+
});
|
|
275
|
+
const pq = createPendingQuestion(session.id, 'This should not throw');
|
|
276
|
+
|
|
277
|
+
// Even without any bindings, dispatch should complete without throwing
|
|
278
|
+
await expect(dispatchGuardianQuestion({
|
|
279
|
+
callSessionId: session.id,
|
|
280
|
+
conversationId: convId,
|
|
281
|
+
assistantId: 'self',
|
|
282
|
+
pendingQuestion: pq,
|
|
283
|
+
})).resolves.toBeUndefined();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test('broadcast title is emoji-prefixed and does not start with "Guardian question:"', async () => {
|
|
287
|
+
const convId = 'conv-dispatch-6';
|
|
288
|
+
ensureConversation(convId);
|
|
289
|
+
|
|
290
|
+
const session = createCallSession({
|
|
291
|
+
conversationId: convId,
|
|
292
|
+
provider: 'twilio',
|
|
293
|
+
fromNumber: '+15550001111',
|
|
294
|
+
toNumber: '+15550002222',
|
|
295
|
+
});
|
|
296
|
+
const pq = createPendingQuestion(session.id, 'What is the gate code?');
|
|
297
|
+
|
|
298
|
+
const broadcastedMessages: unknown[] = [];
|
|
299
|
+
const broadcastFn = (msg: unknown) => { broadcastedMessages.push(msg); };
|
|
300
|
+
|
|
301
|
+
await dispatchGuardianQuestion({
|
|
302
|
+
callSessionId: session.id,
|
|
303
|
+
conversationId: convId,
|
|
304
|
+
assistantId: 'self',
|
|
305
|
+
pendingQuestion: pq,
|
|
306
|
+
broadcast: broadcastFn,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const msg = broadcastedMessages[0] as Record<string, unknown>;
|
|
310
|
+
const title = msg.title as string;
|
|
311
|
+
|
|
312
|
+
// Title must NOT start with the old static "Guardian question:" prefix
|
|
313
|
+
expect(title.startsWith('Guardian question:')).toBe(false);
|
|
314
|
+
|
|
315
|
+
// Title must start with an emoji (code point > 127 or common emoji ranges)
|
|
316
|
+
const firstCodePoint = title.codePointAt(0)!;
|
|
317
|
+
expect(firstCodePoint).toBeGreaterThan(127);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test('broadcast includes questionText field matching the original question', async () => {
|
|
321
|
+
const convId = 'conv-dispatch-7';
|
|
322
|
+
ensureConversation(convId);
|
|
323
|
+
|
|
324
|
+
const questionText = 'What is the WiFi password?';
|
|
325
|
+
const session = createCallSession({
|
|
326
|
+
conversationId: convId,
|
|
327
|
+
provider: 'twilio',
|
|
328
|
+
fromNumber: '+15550001111',
|
|
329
|
+
toNumber: '+15550002222',
|
|
330
|
+
});
|
|
331
|
+
const pq = createPendingQuestion(session.id, questionText);
|
|
332
|
+
|
|
333
|
+
const broadcastedMessages: unknown[] = [];
|
|
334
|
+
const broadcastFn = (msg: unknown) => { broadcastedMessages.push(msg); };
|
|
335
|
+
|
|
336
|
+
await dispatchGuardianQuestion({
|
|
337
|
+
callSessionId: session.id,
|
|
338
|
+
conversationId: convId,
|
|
339
|
+
assistantId: 'self',
|
|
340
|
+
pendingQuestion: pq,
|
|
341
|
+
broadcast: broadcastFn,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
expect(broadcastedMessages).toHaveLength(1);
|
|
345
|
+
const msg = broadcastedMessages[0] as Record<string, unknown>;
|
|
346
|
+
expect(msg.type).toBe('guardian_request_thread_created');
|
|
347
|
+
expect(msg.questionText).toBe(questionText);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test('initial message in mac conversation contains question text from generative copy', async () => {
|
|
351
|
+
const convId = 'conv-dispatch-8';
|
|
352
|
+
ensureConversation(convId);
|
|
353
|
+
|
|
354
|
+
// Set mock copy to a known generative-style message
|
|
355
|
+
mockGuardianCopy = {
|
|
356
|
+
threadTitle: '\u{1F4DE} Live call: Gate code needed',
|
|
357
|
+
initialMessage: 'You have an active phone call that needs your help.\n\nThe caller is asking: What is the gate code?\n\nPlease reply with your answer to resume the call.',
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const session = createCallSession({
|
|
361
|
+
conversationId: convId,
|
|
362
|
+
provider: 'twilio',
|
|
363
|
+
fromNumber: '+15550001111',
|
|
364
|
+
toNumber: '+15550002222',
|
|
365
|
+
});
|
|
366
|
+
const pq = createPendingQuestion(session.id, 'What is the gate code?');
|
|
367
|
+
|
|
368
|
+
const broadcastedMessages: unknown[] = [];
|
|
369
|
+
const broadcastFn = (msg: unknown) => { broadcastedMessages.push(msg); };
|
|
370
|
+
|
|
371
|
+
await dispatchGuardianQuestion({
|
|
372
|
+
callSessionId: session.id,
|
|
373
|
+
conversationId: convId,
|
|
374
|
+
assistantId: 'self',
|
|
375
|
+
pendingQuestion: pq,
|
|
376
|
+
broadcast: broadcastFn,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const msg = broadcastedMessages[0] as Record<string, unknown>;
|
|
380
|
+
const macConvId = msg.conversationId as string;
|
|
381
|
+
|
|
382
|
+
const messages = getMessages(macConvId);
|
|
383
|
+
expect(messages.length).toBeGreaterThanOrEqual(1);
|
|
384
|
+
const content = messages[0].content;
|
|
385
|
+
// The generative copy should be used as the initial message
|
|
386
|
+
expect(content).toContain('What is the gate code?');
|
|
387
|
+
expect(content).toContain('active phone call');
|
|
388
|
+
});
|
|
389
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { buildFallbackCopy } from '../calls/guardian-question-copy.js';
|
|
3
|
+
|
|
4
|
+
describe('buildFallbackCopy', () => {
|
|
5
|
+
test('threadTitle starts with warning emoji', () => {
|
|
6
|
+
const result = buildFallbackCopy('What is the gate code?');
|
|
7
|
+
expect(result.threadTitle.startsWith('\u26A0\uFE0F')).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('threadTitle does not start with "Guardian question:"', () => {
|
|
11
|
+
const result = buildFallbackCopy('What is the gate code?');
|
|
12
|
+
expect(result.threadTitle.startsWith('Guardian question:')).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('threadTitle is under 80 characters for reasonable input', () => {
|
|
16
|
+
const result = buildFallbackCopy('What is the gate code?');
|
|
17
|
+
expect(result.threadTitle.length).toBeLessThan(80);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('initialMessage contains the question text', () => {
|
|
21
|
+
const question = 'Should I let the delivery driver in?';
|
|
22
|
+
const result = buildFallbackCopy(question);
|
|
23
|
+
expect(result.initialMessage).toContain(question);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('initialMessage contains "Reply to this message" instruction', () => {
|
|
27
|
+
const result = buildFallbackCopy('Any question here');
|
|
28
|
+
expect(result.initialMessage).toContain('Reply to this message');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('very long question text gets truncated in title', () => {
|
|
32
|
+
const longQuestion = 'A'.repeat(200);
|
|
33
|
+
const result = buildFallbackCopy(longQuestion);
|
|
34
|
+
|
|
35
|
+
// Title should use questionText.slice(0, 70), so the question portion is at most 70 chars
|
|
36
|
+
// Plus the emoji prefix and space, should still be well under 80
|
|
37
|
+
expect(result.threadTitle.length).toBeLessThanOrEqual(
|
|
38
|
+
'\u26A0\uFE0F '.length + 70,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// The full question should NOT appear in the title
|
|
42
|
+
expect(result.threadTitle).not.toContain(longQuestion);
|
|
43
|
+
|
|
44
|
+
// But the full question should still appear in the initial message
|
|
45
|
+
expect(result.initialMessage).toContain(longQuestion);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -783,6 +783,7 @@ describe('Telegram config handler', () => {
|
|
|
783
783
|
expect(setCommandsCalled).toBe(true);
|
|
784
784
|
expect((setCommandsBody as { commands: Array<{ command: string; description: string }> }).commands).toEqual([
|
|
785
785
|
{ command: 'new', description: 'Start a new conversation' },
|
|
786
|
+
{ command: 'help', description: 'Show available commands' },
|
|
786
787
|
{ command: 'guardian_verify', description: 'Verify your guardian identity' },
|
|
787
788
|
]);
|
|
788
789
|
});
|
|
@@ -863,7 +864,7 @@ describe('Telegram config handler', () => {
|
|
|
863
864
|
expect(res.error).toContain('Failed to set bot commands');
|
|
864
865
|
});
|
|
865
866
|
|
|
866
|
-
test('default command registration includes /new and /guardian_verify', async () => {
|
|
867
|
+
test('default command registration includes /new, /help, and /guardian_verify', async () => {
|
|
867
868
|
secureKeyStore['credential:telegram:bot_token'] = 'test-bot-token';
|
|
868
869
|
secureKeyStore['credential:telegram:webhook_secret'] = 'test-webhook-secret';
|
|
869
870
|
|
|
@@ -891,9 +892,10 @@ describe('Telegram config handler', () => {
|
|
|
891
892
|
expect(res.success).toBe(true);
|
|
892
893
|
|
|
893
894
|
const commands = (setCommandsBody as { commands: Array<{ command: string; description: string }> }).commands;
|
|
894
|
-
expect(commands).toHaveLength(
|
|
895
|
+
expect(commands).toHaveLength(3);
|
|
895
896
|
expect(commands).toEqual([
|
|
896
897
|
{ command: 'new', description: 'Start a new conversation' },
|
|
898
|
+
{ command: 'help', description: 'Show available commands' },
|
|
897
899
|
{ command: 'guardian_verify', description: 'Verify your guardian identity' },
|
|
898
900
|
]);
|
|
899
901
|
});
|