@vellumai/assistant 0.10.4 → 0.10.5-dev.202607022329.d89bf2f
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/ARCHITECTURE.md +94 -91
- package/Dockerfile +5 -16
- package/README.md +7 -10
- package/bun.lock +0 -4
- package/docs/architecture/security.md +20 -20
- package/docs/stt-provider-onboarding.md +4 -5
- package/docs/trusted-contact-access.md +7 -7
- package/eslint-rules/cli-no-daemon-internals.js +4 -0
- package/eslint.config.mjs +7 -1
- package/knip.json +0 -1
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +36 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +233 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +40 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -8
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +33 -0
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +216 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +39 -3
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +0 -8
- package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +1 -5
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +3 -14
- package/openapi.yaml +1073 -225
- package/package.json +1 -4
- package/scripts/test.sh +35 -13
- package/src/__tests__/access-request-seed-content-blocks.test.ts +60 -4
- package/src/__tests__/acp-session.test.ts +4 -1
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +0 -6
- package/src/__tests__/add-message-preassigned-id.test.ts +56 -0
- package/src/__tests__/agent-loop-set-system-prompt.test.ts +139 -0
- package/src/__tests__/app-conversation-ids-backfill.test.ts +3 -0
- package/src/__tests__/app-source-watcher.test.ts +11 -0
- package/src/__tests__/approval-interception-trust-gates.test.ts +148 -3
- package/src/__tests__/assistant-attachments.test.ts +62 -4
- package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -1
- package/src/__tests__/attachments-store-heic-normalize.test.ts +230 -0
- package/src/__tests__/attachments-store.test.ts +3 -0
- package/src/__tests__/background-shell-bash.test.ts +34 -0
- package/src/__tests__/background-shell-host-bash.test.ts +12 -0
- package/src/__tests__/background-tool-registry.test.ts +97 -0
- package/src/__tests__/background-tool-routes.test.ts +72 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/btw-routes.test.ts +57 -3
- package/src/__tests__/call-controller.test.ts +474 -84
- package/src/__tests__/call-domain.test.ts +48 -1
- package/src/__tests__/call-pointer-messages.test.ts +57 -19
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +15 -14
- package/src/__tests__/call-setup-flow-invite.test.ts +568 -0
- package/src/__tests__/call-setup-flow-midcall-trust.test.ts +231 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +754 -0
- package/src/__tests__/call-setup-flow-verification.test.ts +925 -0
- package/src/__tests__/call-setup-flow.test.ts +311 -0
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +4 -7
- package/src/__tests__/channel-readiness-service.test.ts +0 -6
- package/src/__tests__/channel-readiness-slack-remote.test.ts +0 -4
- package/src/__tests__/channel-retry-sweep.test.ts +76 -64
- package/src/__tests__/channel-setup-panel-ack.test.ts +184 -0
- package/src/__tests__/clear-all-lexical.test.ts +90 -0
- package/src/__tests__/compactor-media-strip.test.ts +177 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -5
- package/src/__tests__/config-schema.test.ts +17 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +27 -70
- package/src/__tests__/contacts-relay-reads.test.ts +188 -23
- package/src/__tests__/contacts-tools.test.ts +42 -1
- package/src/__tests__/contacts-write.test.ts +181 -0
- package/src/__tests__/context-search-conversations-source.test.ts +220 -81
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +39 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -1
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -0
- package/src/__tests__/conversation-clear-safety.test.ts +0 -1
- package/src/__tests__/conversation-crud-enabled-plugins.test.ts +60 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -1
- package/src/__tests__/conversation-disk-view.test.ts +1 -0
- package/src/__tests__/conversation-enabledplugins-route.test.ts +184 -0
- package/src/__tests__/conversation-error.test.ts +0 -18
- package/src/__tests__/conversation-fork-crud.test.ts +67 -0
- package/src/__tests__/conversation-history-web-search.test.ts +7 -137
- package/src/__tests__/conversation-initial-prompt.test.ts +110 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +191 -0
- package/src/__tests__/conversation-process-callsite.test.ts +37 -0
- package/src/__tests__/conversation-queue.test.ts +149 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +392 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +84 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +407 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +36 -16
- package/src/__tests__/conversation-skill-tools.test.ts +73 -0
- package/src/__tests__/conversation-wipe.test.ts +20 -5
- package/src/__tests__/credential-prompt-route.test.ts +69 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -3
- package/src/__tests__/db-drop-messages-fts.test.ts +115 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -62
- package/src/__tests__/edit-propagation.test.ts +56 -2
- package/src/__tests__/empty-state-greeting-cache.test.ts +9 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +15 -110
- package/src/__tests__/guardian-expiry-notifier.test.ts +1 -0
- package/src/__tests__/guardian-question-mode.test.ts +57 -0
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
- package/src/__tests__/guardian-wait-controller.test.ts +619 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +33 -2
- package/src/__tests__/heic-fixture.ts +55 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +0 -2
- package/src/__tests__/http-conversation-lineage.test.ts +6 -1
- package/src/__tests__/image-conversion.test.ts +170 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +72 -341
- package/src/__tests__/introduction-card-resolver.test.ts +354 -0
- package/src/__tests__/invite-redeemed-ipc.test.ts +97 -0
- package/src/__tests__/invite-routes-http.test.ts +246 -420
- package/src/__tests__/job-handler-registration-guard.test.ts +104 -0
- package/src/__tests__/lexical-index-dual-write.test.ts +426 -0
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +2 -2
- package/src/__tests__/list-messages-attachments.test.ts +159 -0
- package/src/__tests__/list-messages-background-tool-completion.test.ts +141 -0
- package/src/__tests__/list-messages-queued.test.ts +25 -0
- package/src/__tests__/managed-store.test.ts +129 -1
- package/src/__tests__/media-stream-audio-transcode.test.ts +113 -0
- package/src/__tests__/media-stream-output.test.ts +350 -19
- package/src/__tests__/media-stream-server-integration.test.ts +943 -169
- package/src/__tests__/media-stream-stt-session.test.ts +498 -1
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +2 -2
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +19 -0
- package/src/__tests__/non-member-access-request.test.ts +349 -3
- package/src/__tests__/notification-decision-fallback.test.ts +104 -8
- package/src/__tests__/notification-decision-strategy.test.ts +57 -8
- package/src/__tests__/onboarding-template-contract.test.ts +27 -43
- package/src/__tests__/openai-provider.test.ts +33 -0
- package/src/__tests__/openai-responses-provider.test.ts +40 -0
- package/src/__tests__/persistence-layering-guard.test.ts +29 -20
- package/src/__tests__/persona-resolver.test.ts +56 -2
- package/src/__tests__/personal-memory-auth-bypass.test.ts +62 -0
- package/src/__tests__/phone.test.ts +1 -15
- package/src/__tests__/plugin-bootstrap.test.ts +25 -196
- package/src/__tests__/plugin-disabled-state.test.ts +33 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +227 -0
- package/src/__tests__/plugin-event-hub-facade.test.ts +323 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +156 -42
- package/src/__tests__/plugin-injector-contribution.test.ts +4 -20
- package/src/__tests__/plugin-pipeline.test.ts +63 -1
- package/src/__tests__/plugin-route-contribution.test.ts +62 -115
- package/src/__tests__/plugin-tool-contribution.test.ts +0 -22
- package/src/__tests__/plugin-types.test.ts +0 -1
- package/src/__tests__/pointer-conversation-trust.test.ts +38 -6
- package/src/__tests__/pre-model-call-sanitize.test.ts +1 -1
- package/src/__tests__/prune-old-tool-invocations-job.test.ts +97 -0
- package/src/__tests__/public-ingress-urls.test.ts +0 -48
- package/src/__tests__/qdrant-collection-migration.test.ts +145 -2
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -1
- package/src/__tests__/resolve-trust-class.test.ts +28 -10
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +137 -0
- package/src/__tests__/schedule-retry.test.ts +38 -14
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +3 -3
- package/src/__tests__/schedule-routes.test.ts +14 -6
- package/src/__tests__/schedule-store.test.ts +3 -1
- package/src/__tests__/scheduler-disk-pressure.test.ts +17 -6
- package/src/__tests__/scheduler-recurrence.test.ts +57 -107
- package/src/__tests__/scheduler-reuse-conversation.test.ts +51 -46
- package/src/__tests__/scheduler-wake.test.ts +13 -23
- package/src/__tests__/scheduler-worker-standdown.test.ts +220 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/shell-observability.test.ts +1 -0
- package/src/__tests__/skill-boundary-guard.test.ts +3 -4
- package/src/__tests__/skill-load-plugin-scope.test.ts +238 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -0
- package/src/__tests__/skill-script-runner-host.test.ts +53 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +5 -2
- package/src/__tests__/skill-tool-factory.test.ts +73 -1
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -14
- package/src/__tests__/slack-inbound-verification.test.ts +36 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +89 -2
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +51 -1
- package/src/__tests__/stt-hints.test.ts +2 -256
- package/src/__tests__/subagent-allowlist-validation.test.ts +47 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +72 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +14 -2
- package/src/__tests__/subagent-persistence.test.ts +122 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +99 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -1
- package/src/__tests__/system-prompt.test.ts +17 -118
- package/src/__tests__/task-scheduler.test.ts +68 -89
- package/src/__tests__/telephony-credential-preflight.test.ts +312 -0
- package/src/__tests__/telephony-tts-guard.test.ts +538 -0
- package/src/__tests__/title-generate-hook.test.ts +16 -0
- package/src/__tests__/tool-approval-handler.test.ts +87 -1
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +45 -20
- package/src/__tests__/tool-grant-request-escalation.test.ts +39 -0
- package/src/__tests__/trust-context-guards.test.ts +21 -10
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -0
- package/src/__tests__/twilio-provider.test.ts +17 -17
- package/src/__tests__/twilio-routes-twiml.test.ts +6 -543
- package/src/__tests__/twilio-routes.test.ts +257 -387
- package/src/__tests__/twilio-validation.test.ts +1 -1
- package/src/__tests__/unified-turn-context-client-os.test.ts +45 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +5 -0
- package/src/__tests__/voice-approval-resolver-relay.test.ts +150 -0
- package/src/__tests__/voice-ingress-preflight.test.ts +61 -0
- package/src/__tests__/voice-quality.test.ts +2 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -7
- package/src/__tests__/voice-session-bridge-processing-wait.test.ts +27 -0
- package/src/__tests__/voice-session-bridge.test.ts +1 -10
- package/src/__tests__/workspace-git-service.test.ts +1 -1
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -0
- package/src/__tests__/workspace-migration-118-seed-now-md.test.ts +120 -0
- package/src/__tests__/workspace-migration-120-revise-onboarding-threads.test.ts +175 -0
- package/src/__tests__/workspace-migration-121-seed-default-user-guardrails.test.ts +118 -0
- package/src/__tests__/workspace-migration-122-relocate-default-user-boundary.test.ts +119 -0
- package/src/__tests__/workspace-migration-123-swap-quality-profile-to-fable.test.ts +191 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +235 -241
- package/src/acp/__tests__/prepare-agent-env.test.ts +0 -97
- package/src/acp/__tests__/session-manager-persistence.test.ts +2 -0
- package/src/acp/__tests__/session-manager-resume.test.ts +8 -0
- package/src/acp/__tests__/session-manager-usage.test.ts +6 -0
- package/src/acp/__tests__/session-manager.test.ts +6 -0
- package/src/acp/agent-process.test.ts +116 -0
- package/src/acp/agent-process.ts +71 -0
- package/src/acp/auto-install.ts +12 -8
- package/src/acp/failure-error.test.ts +109 -0
- package/src/acp/failure-error.ts +111 -0
- package/src/acp/prepare-agent-env.ts +0 -12
- package/src/acp/resolve-agent.test.ts +6 -45
- package/src/acp/resolve-agent.ts +2 -4
- package/src/acp/session-manager.test.ts +312 -0
- package/src/acp/session-manager.ts +102 -46
- package/src/agent/ax-tree-compaction.test.ts +4 -1
- package/src/agent/image-optimize.ts +17 -127
- package/src/agent/loop.ts +45 -192
- package/src/api/events/background-tool-completed.test.ts +51 -0
- package/src/api/events/background-tool-completed.ts +29 -0
- package/src/api/events/background-tool-started.test.ts +36 -0
- package/src/api/events/background-tool-started.ts +28 -0
- package/src/api/events/conversation-error.ts +0 -1
- package/src/api/events/open-panel.ts +33 -0
- package/src/api/events/subagent-status-changed.ts +4 -1
- package/src/api/index.ts +19 -0
- package/src/api/responses/conversation-message.ts +34 -0
- package/src/approvals/guardian-decision-primitive.ts +74 -24
- package/src/approvals/guardian-request-resolvers.ts +450 -122
- package/src/apps/app-store.ts +1 -0
- package/src/apps/shared-app-links-store.ts +1 -0
- package/src/avatar/identity-avatar.ts +3 -7
- package/src/background-wake/wake-intent-hooks.test.ts +1 -1
- package/src/calls/__tests__/fish-audio-client.test.ts +73 -0
- package/src/calls/__tests__/gateway-invite-reader.test.ts +170 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +126 -102
- package/src/calls/call-controller.ts +134 -48
- package/src/calls/call-domain.ts +18 -42
- package/src/calls/call-pointer-messages.ts +51 -67
- package/src/calls/call-setup-flow-types.ts +76 -0
- package/src/calls/call-setup-flow.ts +1743 -0
- package/src/calls/call-speech-output.ts +87 -23
- package/src/calls/call-store.ts +4 -0
- package/src/calls/call-transport.ts +22 -36
- package/src/calls/fish-audio-client.ts +3 -0
- package/src/calls/gateway-invite-reader.ts +98 -0
- package/src/calls/guardian-wait-controller.ts +451 -0
- package/src/calls/media-stream-audio-transcode.ts +82 -2
- package/src/calls/media-stream-output.ts +142 -36
- package/src/calls/media-stream-server.ts +297 -178
- package/src/calls/media-stream-stt-session.ts +393 -50
- package/src/calls/media-turn-detector.ts +4 -6
- package/src/calls/relay-access-wait.ts +3 -4
- package/src/calls/relay-setup-router.ts +22 -43
- package/src/calls/relay-verification.ts +17 -24
- package/src/calls/resolve-call-tts-provider.ts +96 -29
- package/src/calls/stt-hints.ts +24 -87
- package/src/calls/telephony-credential-preflight.ts +159 -0
- package/src/calls/telephony-tts-capability.ts +183 -0
- package/src/calls/tts-call-strategy.ts +18 -20
- package/src/calls/twilio-provider.ts +3 -2
- package/src/calls/twilio-routes.ts +47 -351
- package/src/calls/types.ts +5 -3
- package/src/calls/voice-ingress-preflight.ts +9 -0
- package/src/calls/voice-quality.ts +13 -18
- package/src/calls/voice-session-bridge.ts +25 -35
- package/src/channels/__tests__/types.test.ts +28 -0
- package/src/channels/channel-verification-sessions.ts +7 -2
- package/src/channels/config.ts +0 -42
- package/src/channels/slack-thread-store.ts +12 -0
- package/src/channels/types.ts +23 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -1
- package/src/cli/commands/__tests__/schedules.test.ts +83 -34
- package/src/cli/commands/__tests__/status.test.ts +42 -2
- package/src/cli/commands/contacts.ts +2 -2
- package/src/cli/commands/credentials.ts +17 -0
- package/src/cli/commands/monitoring.ts +191 -0
- package/src/cli/commands/schedules-worker.ts +175 -0
- package/src/cli/commands/schedules.ts +169 -43
- package/src/cli/commands/status.ts +12 -1
- package/src/cli/commands/tools.ts +5 -1
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +1 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +29 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +7 -0
- package/src/cli/lib/search-plugins.ts +6 -0
- package/src/cli/lib/toggle-plugin.ts +21 -2
- package/src/cli/program.ts +3 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +23 -0
- package/src/config/acp-defaults.test.ts +0 -10
- package/src/config/acp-defaults.ts +0 -6
- package/src/config/bundled-skills/acp/SKILL.md +13 -45
- package/src/config/bundled-skills/acp/TOOLS.json +3 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -3
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +9 -9
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +1 -1
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -9
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +10 -7
- package/src/config/bundled-skills/skill-management/TOOLS.json +10 -0
- package/src/config/call-site-defaults.ts +0 -2
- package/src/config/feature-flag-registry.json +16 -8
- package/src/config/loader.ts +11 -0
- package/src/config/schema.ts +9 -1
- package/src/config/schemas/__tests__/memory-v2.test.ts +2 -2
- package/src/config/schemas/__tests__/memory-v3.test.ts +64 -0
- package/src/config/schemas/call-site-catalog.ts +0 -12
- package/src/config/schemas/calls.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +1 -1
- package/src/config/schemas/llm.ts +0 -2
- package/src/config/schemas/memory-v2.ts +1 -1
- package/src/config/schemas/memory-v3.ts +82 -0
- package/src/config/schemas/monitoring.ts +61 -0
- package/src/config/schemas/schedules.ts +35 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills-plugin-filter.test.ts +96 -0
- package/src/config/skills.ts +26 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +156 -4
- package/src/contacts/canonical-guardian-store.ts +5 -0
- package/src/contacts/contact-store.ts +81 -35
- package/src/contacts/contacts-write.ts +50 -25
- package/src/contacts/member-write-relay.ts +115 -31
- package/src/contacts/types.ts +0 -8
- package/src/context/compactor.ts +29 -15
- package/src/context/outbound-sanitize.ts +202 -0
- package/src/daemon/__tests__/abort-null-controller.test.ts +167 -0
- package/src/daemon/__tests__/conversation-tool-setup-plugin-scope.test.ts +122 -0
- package/src/daemon/__tests__/embedding-reconcile.test.ts +99 -16
- package/src/daemon/__tests__/wake-conversation-ops-completion.test.ts +107 -0
- package/src/daemon/abort-watchdog.ts +7 -0
- package/src/daemon/app-source-watcher.ts +9 -0
- package/src/daemon/assistant-attachments.ts +42 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +256 -147
- package/src/daemon/conversation-agent-loop.ts +61 -68
- package/src/daemon/conversation-error.ts +1 -15
- package/src/daemon/conversation-history.ts +32 -236
- package/src/daemon/conversation-initial-prompt.ts +77 -0
- package/src/daemon/conversation-lifecycle.ts +25 -1
- package/src/daemon/conversation-messaging.ts +2 -2
- package/src/daemon/conversation-plugin-scope.ts +38 -0
- package/src/daemon/conversation-process.ts +50 -14
- package/src/daemon/conversation-runtime-assembly.ts +36 -6
- package/src/daemon/conversation-skill-tools.ts +19 -3
- package/src/daemon/conversation-store.ts +3 -3
- package/src/daemon/conversation-surfaces.ts +171 -0
- package/src/daemon/conversation-tool-setup.test.ts +95 -0
- package/src/daemon/conversation-tool-setup.ts +116 -1
- package/src/daemon/conversation-turn-finalize.ts +211 -0
- package/src/daemon/conversation.ts +92 -1
- package/src/daemon/disk-pressure-policy.ts +2 -1
- package/src/daemon/event-loop-watchdog.ts +22 -6
- package/src/daemon/external-plugins-bootstrap.ts +47 -134
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +26 -14
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +27 -14
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -5
- package/src/daemon/handlers/config-a2a.ts +7 -14
- package/src/daemon/handlers/config-channels.ts +2 -10
- package/src/daemon/handlers/conversation-history.ts +3 -1
- package/src/daemon/handlers/conversations.ts +0 -52
- package/src/daemon/handlers/shared.ts +4 -24
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +59 -472
- package/src/daemon/message-protocol.ts +3 -3
- package/src/daemon/message-provenance.ts +12 -14
- package/src/daemon/message-types/background-tools.ts +28 -0
- package/src/daemon/message-types/contacts.ts +2 -10
- package/src/daemon/message-types/conversations.ts +10 -0
- package/src/daemon/message-types/integrations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pointer-conversation-trust.ts +23 -9
- package/src/daemon/pointer-turn-runner.ts +174 -0
- package/src/daemon/providers-setup.ts +16 -0
- package/src/daemon/shutdown-handlers.ts +139 -47
- package/src/daemon/skill-memory-refresh.ts +2 -2
- package/src/daemon/sqlite-corruption-watchdog.test.ts +217 -0
- package/src/daemon/sqlite-corruption-watchdog.ts +155 -0
- package/src/daemon/tool-setup-types.ts +8 -0
- package/src/daemon/trust-context.ts +21 -6
- package/src/daemon/turn-latency-tracker.test.ts +93 -0
- package/src/daemon/turn-latency-tracker.ts +38 -0
- package/src/daemon/wake-conversation-ops.ts +10 -8
- package/src/documents/document-comments-store.test.ts +7 -1
- package/src/documents/document-comments-store.ts +8 -0
- package/src/documents/document-store.ts +15 -0
- package/src/export/__tests__/transcript-formatter.test.ts +11 -0
- package/src/export/transcript-formatter.ts +49 -67
- package/src/home/__tests__/feed-writer.test.ts +247 -1
- package/src/home/conversation-starter-checkpoints.ts +1 -0
- package/src/home/feed-writer.ts +61 -0
- package/src/home/job-handlers/conversation-starters.ts +1 -0
- package/src/hooks/hook-loader.ts +12 -0
- package/src/hooks/registry.ts +32 -4
- package/src/inbound/public-ingress-urls.ts +0 -17
- package/src/ipc/__tests__/contacts-info-ipc.test.ts +285 -0
- package/src/ipc/assistant-server.ts +28 -7
- package/src/ipc/routes/__tests__/contacts-mirror-apply-atomicity.test.ts +62 -0
- package/src/ipc/routes/__tests__/contacts-mirror-apply.test.ts +272 -0
- package/src/ipc/routes/__tests__/contacts-mirror-ipc-routes.test.ts +322 -0
- package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +60 -4
- package/src/ipc/routes/contacts-info-ipc-routes.ts +239 -0
- package/src/ipc/routes/contacts-mirror-ipc-routes.ts +202 -0
- package/src/ipc/routes/db-proxy.ts +5 -2
- package/src/ipc/routes/invite-ipc-routes.ts +53 -39
- package/src/jobs/worker.ts +9 -2
- package/src/live-voice/__tests__/live-voice-archive.test.ts +10 -1
- package/src/live-voice/live-voice-archive.ts +5 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +22 -6
- package/src/messaging/providers/channel-transport.ts +6 -0
- package/src/messaging/providers/index.ts +3 -0
- package/src/messaging/providers/slack/api.test.ts +49 -1
- package/src/messaging/providers/slack/api.ts +110 -0
- package/src/messaging/providers/slack/send.test.ts +85 -0
- package/src/messaging/providers/slack/send.ts +100 -28
- package/src/messaging/providers/slack/transport.ts +7 -13
- package/src/messaging/providers/whatsapp/send.ts +29 -9
- package/src/monitoring/__tests__/control.test.ts +235 -0
- package/src/monitoring/__tests__/sample-ring-buffer.test.ts +96 -0
- package/src/monitoring/control.ts +105 -0
- package/src/monitoring/resource-sampler.ts +258 -0
- package/src/monitoring/sample-ring-buffer.ts +108 -0
- package/src/monitoring/worker.ts +94 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/access-request-copy.ts +94 -23
- package/src/notifications/adapters/slack.ts +89 -21
- package/src/notifications/approval-card-builder.ts +33 -18
- package/src/notifications/approval-card-data.ts +48 -15
- package/src/notifications/broadcaster.ts +25 -16
- package/src/notifications/decision-engine.ts +131 -32
- package/src/notifications/guardian-question-mode.ts +58 -10
- package/src/permissions/approval-policy.ts +8 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/gateway-threshold-reader.ts +7 -6
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/secret-prompter.ts +11 -3
- package/src/permissions/types.ts +2 -2
- package/src/persistence/__tests__/conversation-queries-search.test.ts +397 -0
- package/src/persistence/__tests__/conversation-search-lexical.test.ts +103 -0
- package/src/persistence/__tests__/jobs-worker-memory-disabled.test.ts +95 -0
- package/src/persistence/__tests__/slow-query-log.test.ts +266 -0
- package/src/persistence/__tests__/slow-sync-log.test.ts +155 -0
- package/src/persistence/a2a-invite-store.ts +205 -0
- package/src/persistence/attachments-store.ts +94 -10
- package/src/persistence/checkpoints.ts +45 -0
- package/src/persistence/conversation-crud.ts +453 -260
- package/src/persistence/conversation-display-order-migration.ts +8 -2
- package/src/persistence/conversation-group-migration.test.ts +8 -0
- package/src/persistence/conversation-group-migration.ts +9 -0
- package/src/persistence/conversation-queries.ts +244 -125
- package/src/persistence/conversation-row-batch-delete.ts +9 -5
- package/src/persistence/conversation-search-lexical.ts +49 -0
- package/src/persistence/db-async-query.ts +42 -0
- package/src/persistence/db-connection.ts +13 -0
- package/src/persistence/delivery-channels.ts +0 -82
- package/src/persistence/delivery-crud.ts +27 -6
- package/src/persistence/embeddings/__tests__/embedding-identity.test.ts +18 -5
- package/src/persistence/embeddings/__tests__/messages-lexical-index.test.ts +334 -0
- package/src/persistence/embeddings/embedding-identity.ts +17 -5
- package/src/persistence/embeddings/embedding-runtime-manager.ts +31 -0
- package/src/persistence/embeddings/messages-lexical-index.ts +396 -0
- package/src/persistence/embeddings/qdrant-client.ts +81 -5
- package/src/persistence/group-crud.ts +13 -1
- package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +506 -0
- package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +164 -0
- package/src/persistence/job-handlers/__tests__/message-lexical.test.ts +259 -0
- package/src/persistence/job-handlers/cleanup.ts +57 -9
- package/src/persistence/job-handlers/message-lexical-backfill.ts +187 -0
- package/src/persistence/job-handlers/message-lexical.ts +251 -0
- package/src/persistence/jobs-store.ts +102 -8
- package/src/persistence/jobs-worker.ts +68 -28
- package/src/persistence/llm-request-log-store.ts +35 -19
- package/src/persistence/llm-usage-store.ts +11 -0
- package/src/persistence/memory-lifecycle-hooks.test.ts +65 -0
- package/src/persistence/memory-lifecycle-hooks.ts +55 -1
- package/src/persistence/migrations/028-call-session-mode.ts +1 -1
- package/src/persistence/migrations/116-messages-fts.ts +8 -1
- package/src/persistence/migrations/311-create-subagents-table.ts +50 -0
- package/src/persistence/migrations/312-drop-inbox-conversation-state-table.ts +15 -0
- package/src/persistence/migrations/313-drop-messages-fts.ts +17 -0
- package/src/persistence/migrations/314-add-conversation-enabled-plugins.ts +25 -0
- package/src/persistence/migrations/315-create-a2a-invites.ts +58 -0
- package/src/persistence/migrations/316-drop-contact-channels-invite-id.ts +28 -0
- package/src/persistence/migrations/317-canonical-guardian-requester-signals.ts +24 -0
- package/src/persistence/migrations/318-drop-contact-channel-telemetry.ts +32 -0
- package/src/persistence/migrations/__tests__/315-create-a2a-invites.test.ts +161 -0
- package/src/persistence/migrations/__tests__/316-drop-contact-channels-invite-id.test.ts +156 -0
- package/src/persistence/migrations/__tests__/318-drop-contact-channel-telemetry.test.ts +134 -0
- package/src/persistence/migrations/__tests__/migration-prefix-guard.test.ts +99 -0
- package/src/persistence/raw-query.ts +50 -11
- package/src/persistence/schema/a2a.ts +14 -0
- package/src/persistence/schema/contacts.ts +0 -62
- package/src/persistence/schema/conversation-groups.ts +12 -0
- package/src/persistence/schema/conversations.ts +2 -0
- package/src/persistence/schema/documents.ts +65 -0
- package/src/persistence/schema/guardian.ts +4 -0
- package/src/persistence/schema/index.ts +4 -0
- package/src/persistence/schema/memory-injection.ts +62 -0
- package/src/persistence/schema/workflows.ts +55 -0
- package/src/persistence/slow-query-log.ts +437 -0
- package/src/persistence/slow-sync-log.ts +204 -0
- package/src/persistence/steps.ts +16 -0
- package/src/persistence/subagent-store.ts +125 -0
- package/src/persistence/worker-control.ts +45 -238
- package/src/plugin-api/event-hub-facade.ts +172 -0
- package/src/plugin-api/index.ts +9 -1
- package/src/plugin-api/types.ts +40 -9
- package/src/plugins/defaults/default-plugin-names.ts +60 -0
- package/src/plugins/defaults/index.ts +19 -29
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +46 -67
- package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +36 -1
- package/src/plugins/defaults/memory/__tests__/find-most-recent-retrospective-for.test.ts +2 -4
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +9 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +7 -4
- package/src/plugins/defaults/memory/config.ts +11 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -1
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +124 -113
- package/src/plugins/defaults/memory/embeddings.ts +77 -0
- package/src/plugins/defaults/memory/find-most-recent-retrospective-for.ts +66 -0
- package/src/plugins/defaults/memory/frontmatter.ts +50 -0
- package/src/plugins/defaults/memory/graph/bootstrap.test.ts +36 -10
- package/src/plugins/defaults/memory/graph/bootstrap.ts +6 -5
- package/src/plugins/defaults/memory/graph/consolidation.ts +3 -5
- package/src/plugins/defaults/memory/graph/extraction.ts +3 -6
- package/src/plugins/defaults/memory/graph/graph-search.ts +12 -23
- package/src/plugins/defaults/memory/graph/narrative.ts +3 -5
- package/src/plugins/defaults/memory/graph/pattern-scan.ts +3 -5
- package/src/plugins/defaults/memory/graph/retriever.test.ts +1 -6
- package/src/plugins/defaults/memory/graph/retriever.ts +5 -10
- package/src/plugins/defaults/memory/graph/tools.ts +1 -1
- package/src/plugins/defaults/memory/hooks/init.ts +31 -5
- package/src/plugins/defaults/memory/indexer.ts +2 -3
- package/src/plugins/defaults/memory/injectors.ts +2 -2
- package/src/plugins/defaults/memory/job-handler-registration.ts +96 -0
- package/src/plugins/defaults/memory/job-handlers/backfill.ts +4 -26
- package/src/plugins/defaults/memory/job-handlers/embedding.test.ts +6 -19
- package/src/plugins/defaults/memory/job-handlers/embedding.ts +9 -22
- package/src/plugins/defaults/memory/job-handlers/index-maintenance.ts +2 -3
- package/src/plugins/defaults/memory/job-handlers.ts +13 -12
- package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +1 -1
- package/src/plugins/defaults/memory/llm-helpers.ts +76 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +8 -5
- package/src/plugins/defaults/memory/memory-retrospective-startup-cleanup.ts +2 -2
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +2 -0
- package/src/plugins/defaults/memory/persistence-hooks.ts +46 -1
- package/src/plugins/defaults/memory/pkb/pkb-index.ts +1 -5
- package/src/plugins/defaults/memory/pkb/pkb-search.ts +2 -2
- package/src/plugins/defaults/memory/routes/__tests__/memory-v2-simulate-route.test.ts +4 -6
- package/src/plugins/defaults/memory/routes/memory-item-routes.ts +1 -1
- package/src/plugins/defaults/memory/search/semantic.ts +2 -2
- package/src/plugins/defaults/memory/startup.ts +244 -0
- package/src/plugins/defaults/memory/task-memory-cleanup.ts +8 -0
- package/src/{daemon → plugins/defaults/memory/v2}/__tests__/memory-v2-startup.test.ts +6 -6
- package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +2 -13
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +4 -5
- package/src/plugins/defaults/memory/v2/__tests__/sweep-job.test.ts +1 -8
- package/src/plugins/defaults/memory/v2/activation.ts +2 -4
- package/src/plugins/defaults/memory/v2/cli-command-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/frontmatter-sweep.ts +1 -1
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.test.ts +8 -8
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.ts +20 -22
- package/src/plugins/defaults/memory/v2/migration.ts +2 -5
- package/src/plugins/defaults/memory/v2/page-store.ts +1 -1
- package/src/plugins/defaults/memory/v2/qdrant.ts +2 -3
- package/src/plugins/defaults/memory/v2/router.ts +5 -5
- package/src/plugins/defaults/memory/v2/sim.ts +2 -4
- package/src/plugins/defaults/memory/v2/skill-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/sweep-job.ts +6 -6
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +11 -9
- package/src/plugins/defaults/memory/v3/__tests__/dense.test.ts +107 -2
- package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +69 -0
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +181 -0
- package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +7 -3
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +345 -8
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -4
- package/src/plugins/defaults/memory/v3/__tests__/section-needle.test.ts +64 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +18 -13
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +62 -1
- package/src/plugins/defaults/memory/v3/card.ts +1 -4
- package/src/plugins/defaults/memory/v3/dense.ts +60 -25
- package/src/plugins/defaults/memory/v3/edge.ts +1 -1
- package/src/plugins/defaults/memory/v3/gate-flag.ts +20 -0
- package/src/plugins/defaults/memory/v3/gate.ts +158 -0
- package/src/plugins/defaults/memory/v3/maintain-job.ts +2 -4
- package/src/plugins/defaults/memory/v3/orchestrate.ts +186 -25
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +7 -9
- package/src/plugins/defaults/memory/v3/pool-select.ts +6 -6
- package/src/plugins/defaults/memory/v3/prune.ts +2 -2
- package/src/plugins/defaults/memory/v3/section-dense-store.ts +8 -11
- package/src/plugins/defaults/memory/v3/section-needle.ts +27 -7
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +10 -0
- package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +1 -4
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +5 -0
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +12 -0
- package/src/plugins/injector-registry.ts +13 -2
- package/src/plugins/mtime-cache.ts +36 -47
- package/src/plugins/pipeline.ts +15 -1
- package/src/plugins/types.ts +14 -23
- package/src/prompts/__tests__/system-prompt.test.ts +170 -32
- package/src/prompts/persona-resolver.ts +13 -9
- package/src/prompts/system-prompt.ts +30 -9
- package/src/prompts/templates/BOOTSTRAP.md +16 -119
- package/src/prompts/templates/SOUL.md +43 -13
- package/src/prompts/templates/system-sections.ts +32 -27
- package/src/prompts/templates/users/default.md +19 -0
- package/src/providers/__tests__/placeholder-sentinels.test.ts +56 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +96 -1
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +14 -1
- package/src/providers/openai/responses-provider.ts +18 -0
- package/src/providers/placeholder-sentinels.ts +18 -8
- package/src/providers/retry.ts +1 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +0 -64
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +92 -79
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +98 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +62 -3
- package/src/providers/speech-to-text/provider-catalog.ts +0 -89
- package/src/providers/speech-to-text/resolve.ts +55 -0
- package/src/providers/tool-progress-events.test.ts +50 -0
- package/src/providers/tool-progress-events.ts +99 -0
- package/src/providers/types.ts +1 -1
- package/src/providers/vellum-model-routing.test.ts +51 -0
- package/src/providers/vellum-model-routing.ts +84 -0
- package/src/runtime/AGENTS.md +5 -4
- package/src/runtime/__tests__/agent-wake.test.ts +158 -1
- package/src/runtime/__tests__/channel-verification-service.test.ts +2 -2
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +37 -8
- package/src/runtime/access-request-helper.ts +154 -3
- package/src/runtime/actor-trust-resolver.ts +7 -21
- package/src/runtime/agent-wake.ts +41 -1
- package/src/runtime/assistant-event.ts +86 -12
- package/src/runtime/auth/__tests__/route-policy.test.ts +31 -0
- package/src/runtime/channel-approval-types.ts +52 -2
- package/src/runtime/channel-approvals.ts +23 -8
- package/src/runtime/channel-invite-transport.ts +2 -2
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-readiness-service.ts +3 -5
- package/src/runtime/channel-retry-sweep.ts +18 -24
- package/src/runtime/channel-verification-service.ts +9 -11
- package/src/runtime/finalize-event-delivery.ts +30 -16
- package/src/runtime/guardian-action-service.ts +10 -11
- package/src/runtime/guardian-reply-router.ts +122 -21
- package/src/runtime/http-server.ts +44 -101
- package/src/runtime/introduction-policy.test.ts +214 -0
- package/src/runtime/introduction-policy.ts +284 -0
- package/src/runtime/invite-service.ts +104 -361
- package/src/runtime/local-actor-identity.test.ts +38 -0
- package/src/runtime/local-actor-identity.ts +14 -0
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +22 -0
- package/src/runtime/middleware/rate-limiter.ts +30 -0
- package/src/runtime/middleware/twilio-validation.ts +5 -7
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +28 -0
- package/src/runtime/migrations/origin-mode.ts +6 -6
- package/src/runtime/pre-first-message-gate.ts +1 -0
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +6 -35
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +24 -3
- package/src/runtime/routes/__tests__/contact-routes.test.ts +80 -15
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +7 -1
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -3
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +142 -1
- package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +119 -30
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +70 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +194 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +755 -29
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +4 -0
- package/src/runtime/routes/__tests__/schedule-routes-create.test.ts +70 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +161 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +1 -1
- package/src/runtime/routes/acp-routes.ts +15 -3
- package/src/runtime/routes/app-management-routes.ts +4 -7
- package/src/runtime/routes/approval-routes.ts +4 -0
- package/src/runtime/routes/attachment-routes.ts +54 -3
- package/src/runtime/routes/avatar-routes.ts +0 -4
- package/src/runtime/routes/background-tool-routes.ts +34 -5
- package/src/runtime/routes/btw-routes.ts +32 -4
- package/src/runtime/routes/channel-route-shared.ts +18 -14
- package/src/runtime/routes/channel-verification-routes.ts +0 -3
- package/src/runtime/routes/contact-routes.ts +298 -117
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +74 -37
- package/src/runtime/routes/conversation-query-routes.ts +5 -2
- package/src/runtime/routes/conversation-routes.ts +222 -82
- package/src/runtime/routes/conversations-import-routes.ts +8 -1
- package/src/runtime/routes/credential-prompt-routes.ts +20 -5
- package/src/runtime/routes/debug-routes.ts +1 -0
- package/src/runtime/routes/documents-routes.ts +5 -2
- package/src/runtime/routes/empty-state-greeting-cache.ts +37 -10
- package/src/runtime/routes/global-search-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +51 -0
- package/src/runtime/routes/home-feed-routes.ts +60 -1
- package/src/runtime/routes/identity-routes.ts +4 -87
- package/src/runtime/routes/inbound-message-handler.ts +82 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +430 -57
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +302 -572
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +76 -270
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +21 -116
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +9 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.test.ts +158 -0
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +22 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +22 -16
- package/src/runtime/routes/internal-twilio-routes.ts +0 -15
- package/src/runtime/routes/messages-lexical-routes.ts +75 -0
- package/src/runtime/routes/migration-routes.ts +69 -6
- package/src/runtime/routes/monitoring-routes.ts +237 -0
- package/src/runtime/routes/plugins-routes.ts +329 -8
- package/src/runtime/routes/schedule-routes.ts +49 -8
- package/src/runtime/routes/schedule-worker-routes.ts +210 -0
- package/src/runtime/routes/secret-routes.ts +1 -1
- package/src/runtime/routes/surface-conversation-resolver.ts +1 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +54 -9
- package/src/runtime/services/__tests__/auto-analysis-enqueue.test.ts +30 -6
- package/src/runtime/services/__tests__/conversation-analyze-job.test.ts +43 -8
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +20 -0
- package/src/runtime/services/analyze-conversation.ts +14 -4
- package/src/runtime/services/conversation-serializer.ts +5 -0
- package/src/runtime/slack-no-response.ts +47 -0
- package/src/runtime/slack-reply-session.test.ts +633 -0
- package/src/runtime/slack-reply-session.ts +382 -0
- package/src/runtime/slack-task-progress.ts +95 -0
- package/src/runtime/sync/resource-sync-events.ts +25 -0
- package/src/runtime/tool-grant-request-helper.ts +17 -1
- package/src/runtime/trust-class.test.ts +141 -0
- package/src/runtime/trust-class.ts +121 -0
- package/src/runtime/trust-verdict-consumer.ts +7 -6
- package/src/schedule/__tests__/run-script.test.ts +21 -0
- package/src/schedule/__tests__/worker-control.test.ts +241 -0
- package/src/schedule/run-script.ts +13 -6
- package/src/schedule/schedule-usage-store.ts +1 -0
- package/src/schedule/scheduler.ts +178 -127
- package/src/schedule/worker-control.ts +118 -0
- package/src/schedule/worker.ts +131 -0
- package/src/skills/__tests__/categories-cache.test.ts +38 -0
- package/src/skills/categories-cache.ts +40 -5
- package/src/skills/managed-store.ts +41 -6
- package/src/skills/validate-input.ts +47 -0
- package/src/stt/types.ts +5 -9
- package/src/subagent/manager.ts +267 -39
- package/src/subagent/types.ts +6 -2
- package/src/subagent/validate-allowlists.ts +65 -0
- package/src/telemetry/watchdog-direct-emit.test.ts +109 -0
- package/src/telemetry/watchdog-direct-emit.ts +89 -0
- package/src/tools/acp/list-agents.test.ts +0 -6
- package/src/tools/background-tool-registry.ts +61 -0
- package/src/tools/document/document-comment-tool.test.ts +4 -0
- package/src/tools/host-terminal/host-shell.test.ts +397 -0
- package/src/tools/host-terminal/host-shell.ts +203 -14
- package/src/tools/skills/find-similar-skills.test.ts +152 -1
- package/src/tools/skills/find-similar-skills.ts +30 -4
- package/src/tools/skills/load.ts +36 -0
- package/src/tools/skills/scaffold-managed.ts +66 -34
- package/src/tools/skills/skill-script-runner.ts +17 -0
- package/src/tools/skills/skill-tool-factory.ts +5 -1
- package/src/tools/terminal/safe-env.ts +3 -0
- package/src/tools/terminal/shell.test.ts +177 -0
- package/src/tools/terminal/shell.ts +98 -4
- package/src/tools/tool-approval-handler.ts +106 -29
- package/{node_modules/@vellumai/skill-host-contracts/src → src/tools}/tool-types.ts +16 -17
- package/src/tools/types.ts +29 -17
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tts/__tests__/provider-adapters.test.ts +37 -0
- package/src/tts/__tests__/provider-catalog.test.ts +21 -0
- package/src/tts/provider-catalog.ts +40 -9
- package/src/tts/providers/fish-audio-provider.ts +19 -10
- package/src/tts/types.ts +3 -4
- package/src/util/__tests__/process-tree.test.ts +10 -2
- package/src/util/__tests__/text-spacing.test.ts +53 -0
- package/src/util/cgroup-memory.ts +142 -0
- package/src/util/image-conversion.ts +281 -0
- package/src/util/phone.ts +1 -8
- package/src/util/platform.ts +33 -0
- package/src/util/process-tree.ts +10 -2
- package/src/util/text-spacing.ts +53 -0
- package/src/util/worker-process.ts +271 -0
- package/src/workflows/journal-store.ts +16 -1
- package/src/workflows/library.test.ts +35 -20
- package/src/workflows/library.ts +105 -66
- package/src/workspace/git-service.ts +1 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +83 -31
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +41 -22
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +1 -1
- package/src/workspace/migrations/118-seed-now-md.ts +64 -0
- package/src/workspace/migrations/119-strip-persisted-memory-v3-tuning-defaults.ts +169 -0
- package/src/workspace/migrations/120-revise-onboarding-threads.ts +47 -0
- package/src/workspace/migrations/121-seed-default-user-guardrails.ts +82 -0
- package/src/workspace/migrations/122-relocate-default-user-boundary.ts +111 -0
- package/src/workspace/migrations/123-swap-quality-profile-to-fable.ts +85 -0
- package/src/workspace/migrations/__tests__/119-strip-persisted-memory-v3-tuning-defaults.test.ts +181 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/drizzle/0000_dizzy_maggott.sql +0 -301
- package/drizzle/meta/0000_snapshot.json +0 -1933
- package/drizzle/meta/_journal.json +0 -13
- package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +0 -887
- package/node_modules/@vellumai/skill-host-contracts/bun.lock +0 -24
- package/node_modules/@vellumai/skill-host-contracts/package.json +0 -18
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -97
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +0 -1349
- package/node_modules/@vellumai/skill-host-contracts/src/index.ts +0 -6
- package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +0 -11
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +0 -32
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +0 -333
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +0 -20
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +0 -12
- package/src/__tests__/invite-redemption-service.test.ts +0 -1180
- package/src/__tests__/invite-service-ipc.test.ts +0 -350
- package/src/__tests__/job-handler-registry-guard.test.ts +0 -125
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -203
- package/src/__tests__/relay-server.test.ts +0 -6153
- package/src/__tests__/telephony-stt-routing.test.ts +0 -329
- package/src/__tests__/voice-invite-redemption.test.ts +0 -702
- package/src/calls/relay-server.ts +0 -2463
- package/src/calls/telephony-stt-routing.ts +0 -145
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -128
- package/src/daemon/__tests__/daemon-skill-host.test.ts +0 -273
- package/src/daemon/daemon-skill-host.ts +0 -262
- package/src/daemon/message-types/meet.ts +0 -143
- package/src/daemon/shutdown-registry.ts +0 -40
- package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -253
- package/src/ipc/__tests__/skill-server.test.ts +0 -182
- package/src/ipc/skill-ipc-types.ts +0 -54
- package/src/ipc/skill-routes/__tests__/config.test.ts +0 -146
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +0 -448
- package/src/ipc/skill-routes/__tests__/identity.test.ts +0 -62
- package/src/ipc/skill-routes/__tests__/log.test.ts +0 -133
- package/src/ipc/skill-routes/__tests__/memory.test.ts +0 -203
- package/src/ipc/skill-routes/__tests__/platform.test.ts +0 -111
- package/src/ipc/skill-routes/__tests__/providers.test.ts +0 -265
- package/src/ipc/skill-routes/__tests__/registries.test.ts +0 -400
- package/src/ipc/skill-routes/config.ts +0 -47
- package/src/ipc/skill-routes/events.ts +0 -147
- package/src/ipc/skill-routes/identity.ts +0 -21
- package/src/ipc/skill-routes/index.ts +0 -37
- package/src/ipc/skill-routes/log.ts +0 -40
- package/src/ipc/skill-routes/memory.ts +0 -92
- package/src/ipc/skill-routes/platform.ts +0 -39
- package/src/ipc/skill-routes/providers.ts +0 -162
- package/src/ipc/skill-routes/registries.ts +0 -338
- package/src/ipc/skill-server.ts +0 -831
- package/src/ipc/skill-socket-path.ts +0 -20
- package/src/jobs/register-job-handlers.ts +0 -74
- package/src/persistence/invite-store.ts +0 -481
- package/src/plugins/job-handler-registry.ts +0 -94
- package/src/providers/cache-control.ts +0 -26
- package/src/runtime/__tests__/runtime-mode.test.ts +0 -62
- package/src/runtime/invite-redemption-service.ts +0 -828
- package/src/runtime/invite-redemption-templates.ts +0 -46
- package/src/runtime/runtime-mode.ts +0 -40
- package/src/runtime/slack-dm-text-delivery.ts +0 -177
- package/src/util/voice-code.ts +0 -31
|
@@ -24,45 +24,44 @@ mock.module("../telegram/bot-username.js", () => ({
|
|
|
24
24
|
}));
|
|
25
25
|
|
|
26
26
|
// Mock startInviteCall from call-domain — test env lacks Twilio credentials.
|
|
27
|
+
// Captures the last input so trigger-call tests can assert the gateway-supplied
|
|
28
|
+
// fields flow through to the provider call.
|
|
27
29
|
let mockStartInviteCallResult:
|
|
28
30
|
| { ok: true; callSid: string }
|
|
29
31
|
| { ok: false; error: string; status?: number } = {
|
|
30
32
|
ok: true,
|
|
31
33
|
callSid: "CA_test_sid_123",
|
|
32
34
|
};
|
|
35
|
+
let lastStartInviteCallInput: Record<string, unknown> | null = null;
|
|
33
36
|
mock.module("../calls/call-domain.js", () => ({
|
|
34
|
-
startInviteCall: async () =>
|
|
37
|
+
startInviteCall: async (input: Record<string, unknown>) => {
|
|
38
|
+
lastStartInviteCallInput = input;
|
|
39
|
+
return mockStartInviteCallResult;
|
|
40
|
+
},
|
|
35
41
|
}));
|
|
36
42
|
|
|
37
|
-
// Model the gateway: the
|
|
38
|
-
// gateway
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// Model the gateway `invites_redeem` IPC: the daemon redeem route is a thin
|
|
44
|
+
// relay (the gateway redemption engine owns validation, the atomic claim,
|
|
45
|
+
// and the ACL write), so the mock captures the relayed params and serves a
|
|
46
|
+
// scripted gateway response or a scripted relay failure.
|
|
47
|
+
import { IpcCallError } from "@vellumai/gateway-client/ipc-client";
|
|
48
|
+
|
|
49
|
+
const redeemRelay: {
|
|
50
|
+
calls: Array<Record<string, unknown> | undefined>;
|
|
51
|
+
result: unknown;
|
|
52
|
+
error: Error | null;
|
|
53
|
+
} = { calls: [], result: undefined, error: null };
|
|
45
54
|
mock.module("../ipc/gateway-client.js", () => ({
|
|
46
55
|
ipcCallPersistent: async (
|
|
47
56
|
method: string,
|
|
48
57
|
params?: Record<string, unknown>,
|
|
49
58
|
) => {
|
|
50
|
-
if (method === "
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
channel: {
|
|
57
|
-
id: "gw-channel-id",
|
|
58
|
-
contactId: (params?.contactId as string) ?? "gw-contact",
|
|
59
|
-
type: (params?.type as string) ?? "telegram",
|
|
60
|
-
address: (params?.address as string) ?? "gw-addr",
|
|
61
|
-
status: "active",
|
|
62
|
-
verifiedAt: 1,
|
|
63
|
-
verifiedVia: (params?.verifiedVia as string) ?? "invite",
|
|
64
|
-
},
|
|
65
|
-
};
|
|
59
|
+
if (method === "invites_redeem") {
|
|
60
|
+
redeemRelay.calls.push(params);
|
|
61
|
+
if (redeemRelay.error) {
|
|
62
|
+
throw redeemRelay.error;
|
|
63
|
+
}
|
|
64
|
+
return redeemRelay.result;
|
|
66
65
|
}
|
|
67
66
|
return undefined;
|
|
68
67
|
},
|
|
@@ -71,54 +70,48 @@ mock.module("../ipc/gateway-client.js", () => ({
|
|
|
71
70
|
import { upsertContact } from "../contacts/contact-store.js";
|
|
72
71
|
import { getSqlite } from "../persistence/db-connection.js";
|
|
73
72
|
import { initializeDb } from "../persistence/db-init.js";
|
|
74
|
-
import { revokeInvite } from "../persistence/invite-store.js";
|
|
75
73
|
import {
|
|
76
|
-
|
|
74
|
+
composeInvitePresentation,
|
|
77
75
|
triggerInviteCall,
|
|
78
76
|
} from "../runtime/invite-service.js";
|
|
79
77
|
import { handleRedeemInvite as _handleRedeemInvite } from "../runtime/routes/contact-routes.js";
|
|
80
78
|
import { RouteError } from "../runtime/routes/errors.js";
|
|
81
79
|
|
|
82
80
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* gateway
|
|
86
|
-
*
|
|
87
|
-
* stays daemon-local
|
|
81
|
+
* Invite create/list/revoke/redeem are gateway-native (the daemon route
|
|
82
|
+
* handlers relay; the gateway engine is exercised in
|
|
83
|
+
* gateway/src/__tests__/invite-redemption-engine*.test.ts and the gateway
|
|
84
|
+
* handlers in contacts-control-plane-proxy.test.ts +
|
|
85
|
+
* ipc-invite-routes.test.ts). What stays daemon-local — the redeem relay
|
|
86
|
+
* dispatch, the outbound call trigger, and the presentation composition
|
|
87
|
+
* layered onto the gateway's create payload — is exercised here.
|
|
88
88
|
*/
|
|
89
89
|
function fakeResponse(body: unknown, status = 200) {
|
|
90
90
|
return { status, json: async () => body };
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
async function handleCreateInvite(req: Request) {
|
|
94
|
-
const body = (await req.json()) as Record<string, string | number>;
|
|
95
|
-
const result = await createIngressInvite({
|
|
96
|
-
sourceChannel: body.sourceChannel as string | undefined,
|
|
97
|
-
note: body.note as string | undefined,
|
|
98
|
-
maxUses: body.maxUses as number | undefined,
|
|
99
|
-
expiresInMs: body.expiresInMs as number | undefined,
|
|
100
|
-
expectedExternalUserId: body.expectedExternalUserId as string | undefined,
|
|
101
|
-
contactId: body.contactId as string,
|
|
102
|
-
});
|
|
103
|
-
if (!result.ok) return fakeResponse({ ok: false, error: result.error }, 400);
|
|
104
|
-
return fakeResponse({ ok: true, invite: result.data }, 201);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
93
|
async function handleRedeemInvite(req: Request) {
|
|
108
94
|
const body = (await req.json()) as Record<string, unknown>;
|
|
109
95
|
try {
|
|
110
96
|
const result = await _handleRedeemInvite({ body });
|
|
111
97
|
return fakeResponse(result);
|
|
112
98
|
} catch (err) {
|
|
113
|
-
if (err instanceof RouteError)
|
|
99
|
+
if (err instanceof RouteError) {
|
|
114
100
|
return fakeResponse({ ok: false, error: err.message }, err.statusCode);
|
|
101
|
+
}
|
|
115
102
|
throw err;
|
|
116
103
|
}
|
|
117
104
|
}
|
|
118
105
|
|
|
119
|
-
async function handleTriggerInviteCall(
|
|
120
|
-
|
|
121
|
-
|
|
106
|
+
async function handleTriggerInviteCall(params: {
|
|
107
|
+
phoneNumber?: string;
|
|
108
|
+
friendName?: string | null;
|
|
109
|
+
guardianName?: string | null;
|
|
110
|
+
}) {
|
|
111
|
+
const result = await triggerInviteCall(params);
|
|
112
|
+
if (!result.ok) {
|
|
113
|
+
return fakeResponse({ ok: false, error: result.error }, 400);
|
|
114
|
+
}
|
|
122
115
|
return fakeResponse({ ok: true, callSid: result.data.callSid });
|
|
123
116
|
}
|
|
124
117
|
|
|
@@ -130,129 +123,145 @@ function createTargetContact(displayName = "Test Contact"): string {
|
|
|
130
123
|
}
|
|
131
124
|
|
|
132
125
|
function resetTables() {
|
|
133
|
-
getSqlite().run("DELETE FROM assistant_ingress_invites");
|
|
134
126
|
getSqlite().run("DELETE FROM contact_channels");
|
|
135
127
|
getSqlite().run("DELETE FROM contacts");
|
|
136
128
|
}
|
|
137
129
|
|
|
130
|
+
function resetRedeemRelay() {
|
|
131
|
+
redeemRelay.calls.length = 0;
|
|
132
|
+
redeemRelay.result = undefined;
|
|
133
|
+
redeemRelay.error = null;
|
|
134
|
+
}
|
|
135
|
+
|
|
138
136
|
// ---------------------------------------------------------------------------
|
|
139
|
-
//
|
|
137
|
+
// Redeem relay (gateway-native redemption behind the daemon route)
|
|
140
138
|
// ---------------------------------------------------------------------------
|
|
141
139
|
|
|
142
|
-
describe("
|
|
143
|
-
beforeEach(
|
|
140
|
+
describe("invite redeem relay routes", () => {
|
|
141
|
+
beforeEach(() => {
|
|
142
|
+
resetTables();
|
|
143
|
+
resetRedeemRelay();
|
|
144
|
+
});
|
|
144
145
|
|
|
145
|
-
test("POST /v1/contacts/invites —
|
|
146
|
-
|
|
146
|
+
test("POST /v1/contacts/invites/redeem — token body relays to the gateway and returns its payload", async () => {
|
|
147
|
+
redeemRelay.result = {
|
|
148
|
+
ok: true,
|
|
149
|
+
invite: { id: "inv-gw-1", status: "redeemed", useCount: 1 },
|
|
150
|
+
type: "redeemed",
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const req = new Request("http://localhost/v1/contacts/invites/redeem", {
|
|
147
154
|
method: "POST",
|
|
148
155
|
headers: { "Content-Type": "application/json" },
|
|
149
156
|
body: JSON.stringify({
|
|
157
|
+
token: "raw-token-1",
|
|
158
|
+
externalUserId: "redeemer-1",
|
|
159
|
+
externalChatId: "chat-9",
|
|
150
160
|
sourceChannel: "telegram",
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
161
|
+
displayName: "Alice Example",
|
|
162
|
+
username: "alice",
|
|
163
|
+
notAContractField: "dropped",
|
|
154
164
|
}),
|
|
155
165
|
});
|
|
156
166
|
|
|
157
|
-
const res = await
|
|
167
|
+
const res = await handleRedeemInvite(req);
|
|
158
168
|
const body = (await res.json()) as Record<string, unknown>;
|
|
159
169
|
|
|
160
|
-
expect(res.status).toBe(
|
|
170
|
+
expect(res.status).toBe(200);
|
|
161
171
|
expect(body.ok).toBe(true);
|
|
162
|
-
|
|
163
|
-
expect(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
expect((body.invite as Record<string, unknown>).id).toBe("inv-gw-1");
|
|
173
|
+
expect(body.type).toBe("redeemed");
|
|
174
|
+
// The relay forwards every shared-contract field — including the sender
|
|
175
|
+
// identity fields (displayName/username) the gateway engine stamps onto
|
|
176
|
+
// the new member — and nothing voice-shaped or off-contract.
|
|
177
|
+
expect(redeemRelay.calls).toEqual([
|
|
178
|
+
{
|
|
179
|
+
token: "raw-token-1",
|
|
180
|
+
sourceChannel: "telegram",
|
|
181
|
+
externalUserId: "redeemer-1",
|
|
182
|
+
externalChatId: "chat-9",
|
|
183
|
+
displayName: "Alice Example",
|
|
184
|
+
username: "alice",
|
|
185
|
+
},
|
|
186
|
+
]);
|
|
170
187
|
});
|
|
171
188
|
|
|
172
|
-
test("POST /v1/contacts/invites —
|
|
173
|
-
|
|
189
|
+
test("POST /v1/contacts/invites/redeem — voice body relays code + caller and returns the voice shape", async () => {
|
|
190
|
+
redeemRelay.result = {
|
|
191
|
+
ok: true,
|
|
192
|
+
type: "redeemed",
|
|
193
|
+
memberId: "ct-target",
|
|
194
|
+
inviteId: "inv-gw-2",
|
|
195
|
+
};
|
|
196
|
+
const code = "123456";
|
|
174
197
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
198
|
+
const req = new Request("http://localhost/v1/contacts/invites/redeem", {
|
|
199
|
+
method: "POST",
|
|
200
|
+
headers: { "Content-Type": "application/json" },
|
|
201
|
+
body: JSON.stringify({
|
|
202
|
+
callerExternalUserId: "+15551234567",
|
|
203
|
+
code,
|
|
204
|
+
assistantId: "asst-1",
|
|
205
|
+
}),
|
|
206
|
+
});
|
|
185
207
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const invite = body.invite as Record<string, unknown>;
|
|
189
|
-
const token = invite.token as string;
|
|
190
|
-
const share = invite.share as Record<string, unknown>;
|
|
208
|
+
const res = await handleRedeemInvite(req);
|
|
209
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
191
210
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
expect(res.status).toBe(200);
|
|
212
|
+
expect(body).toEqual({
|
|
213
|
+
ok: true,
|
|
214
|
+
type: "redeemed",
|
|
215
|
+
memberId: "ct-target",
|
|
216
|
+
inviteId: "inv-gw-2",
|
|
217
|
+
});
|
|
218
|
+
expect(redeemRelay.calls).toEqual([
|
|
219
|
+
{ code, callerExternalUserId: "+15551234567", assistantId: "asst-1" },
|
|
220
|
+
]);
|
|
202
221
|
});
|
|
203
222
|
|
|
204
|
-
test("POST /v1/contacts/invites —
|
|
205
|
-
|
|
223
|
+
test("POST /v1/contacts/invites/redeem — gateway 400 (engine reason) surfaces as 400", async () => {
|
|
224
|
+
redeemRelay.error = new IpcCallError("invalid_or_expired", {
|
|
225
|
+
statusCode: 400,
|
|
226
|
+
errorCode: "BAD_REQUEST",
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const req = new Request("http://localhost/v1/contacts/invites/redeem", {
|
|
206
230
|
method: "POST",
|
|
207
231
|
headers: { "Content-Type": "application/json" },
|
|
208
|
-
body: JSON.stringify({
|
|
232
|
+
body: JSON.stringify({
|
|
233
|
+
callerExternalUserId: "+15551234567",
|
|
234
|
+
code: "000000",
|
|
235
|
+
}),
|
|
209
236
|
});
|
|
210
237
|
|
|
211
|
-
const res = await
|
|
238
|
+
const res = await handleRedeemInvite(req);
|
|
212
239
|
const body = (await res.json()) as { ok: boolean; error: string };
|
|
213
240
|
|
|
214
241
|
expect(res.status).toBe(400);
|
|
215
242
|
expect(body.ok).toBe(false);
|
|
216
|
-
expect(body.error).
|
|
243
|
+
expect(body.error).toBe("invalid_or_expired");
|
|
217
244
|
});
|
|
218
245
|
|
|
219
|
-
test("POST /v1/contacts/invites/redeem —
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
method: "POST",
|
|
224
|
-
headers: { "Content-Type": "application/json" },
|
|
225
|
-
body: JSON.stringify({
|
|
226
|
-
sourceChannel: "telegram",
|
|
227
|
-
contactId: createTargetContact(),
|
|
228
|
-
maxUses: 1,
|
|
229
|
-
}),
|
|
230
|
-
}),
|
|
231
|
-
);
|
|
232
|
-
const created = (await createRes.json()) as { invite: { token: string } };
|
|
246
|
+
test("POST /v1/contacts/invites/redeem — gateway unreachable fails CLOSED (500)", async () => {
|
|
247
|
+
// The gateway is the single redemption authority; a relay failure must
|
|
248
|
+
// surface as an error, never a locally-decided redemption.
|
|
249
|
+
redeemRelay.error = new IpcCallError("gateway unreachable");
|
|
233
250
|
|
|
234
251
|
const req = new Request("http://localhost/v1/contacts/invites/redeem", {
|
|
235
252
|
method: "POST",
|
|
236
253
|
headers: { "Content-Type": "application/json" },
|
|
237
254
|
body: JSON.stringify({
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
sourceChannel: "telegram",
|
|
255
|
+
callerExternalUserId: "+15551234567",
|
|
256
|
+
code: "123456",
|
|
241
257
|
}),
|
|
242
258
|
});
|
|
243
259
|
|
|
244
260
|
const res = await handleRedeemInvite(req);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
expect(res.status).toBe(200);
|
|
248
|
-
expect(body.ok).toBe(true);
|
|
249
|
-
const invite = body.invite as Record<string, unknown>;
|
|
250
|
-
expect(invite.useCount).toBe(1);
|
|
251
|
-
// Single-use invite should be fully redeemed
|
|
252
|
-
expect(invite.status).toBe("redeemed");
|
|
261
|
+
expect(res.status).toBe(500);
|
|
253
262
|
});
|
|
254
263
|
|
|
255
|
-
test("POST /v1/contacts/invites/redeem — missing token returns 400", async () => {
|
|
264
|
+
test("POST /v1/contacts/invites/redeem — missing token returns 400 without relaying", async () => {
|
|
256
265
|
const req = new Request("http://localhost/v1/contacts/invites/redeem", {
|
|
257
266
|
method: "POST",
|
|
258
267
|
headers: { "Content-Type": "application/json" },
|
|
@@ -262,284 +271,107 @@ describe("ingress invite HTTP routes", () => {
|
|
|
262
271
|
const res = await handleRedeemInvite(req);
|
|
263
272
|
const body = (await res.json()) as { ok: boolean; error: string };
|
|
264
273
|
|
|
274
|
+
// No `code` and no `token` → token path; the shared contract schema
|
|
275
|
+
// rejects daemon-side before any gateway round-trip.
|
|
265
276
|
expect(res.status).toBe(400);
|
|
266
277
|
expect(body.ok).toBe(false);
|
|
267
278
|
expect(body.error).toContain("token");
|
|
279
|
+
expect(redeemRelay.calls.length).toBe(0);
|
|
268
280
|
});
|
|
269
281
|
|
|
270
|
-
test("POST /v1/contacts/invites/redeem —
|
|
282
|
+
test("POST /v1/contacts/invites/redeem — voice code without caller identity is rejected daemon-side", async () => {
|
|
271
283
|
const req = new Request("http://localhost/v1/contacts/invites/redeem", {
|
|
272
284
|
method: "POST",
|
|
273
285
|
headers: { "Content-Type": "application/json" },
|
|
274
|
-
body: JSON.stringify({
|
|
286
|
+
body: JSON.stringify({ code: "123456" }),
|
|
275
287
|
});
|
|
276
288
|
|
|
277
289
|
const res = await handleRedeemInvite(req);
|
|
278
|
-
const body = (await res.json()) as
|
|
290
|
+
const body = (await res.json()) as { ok: boolean; error: string };
|
|
279
291
|
|
|
280
292
|
expect(res.status).toBe(400);
|
|
281
293
|
expect(body.ok).toBe(false);
|
|
294
|
+
expect(body.error).toContain("callerExternalUserId");
|
|
295
|
+
// Rejected before any gateway round-trip.
|
|
296
|
+
expect(redeemRelay.calls.length).toBe(0);
|
|
282
297
|
});
|
|
283
298
|
});
|
|
284
299
|
|
|
285
300
|
// ---------------------------------------------------------------------------
|
|
286
|
-
//
|
|
287
|
-
// ---------------------------------------------------------------------------
|
|
288
|
-
|
|
289
|
-
describe("ingress service shared logic", () => {
|
|
290
|
-
beforeEach(resetTables);
|
|
291
|
-
|
|
292
|
-
test("invite create + revoke round-trip through shared service", async () => {
|
|
293
|
-
const createRes = await handleCreateInvite(
|
|
294
|
-
new Request("http://localhost/v1/contacts/invites", {
|
|
295
|
-
method: "POST",
|
|
296
|
-
headers: { "Content-Type": "application/json" },
|
|
297
|
-
body: JSON.stringify({
|
|
298
|
-
sourceChannel: "telegram",
|
|
299
|
-
contactId: createTargetContact(),
|
|
300
|
-
}),
|
|
301
|
-
}),
|
|
302
|
-
);
|
|
303
|
-
const created = (await createRes.json()) as {
|
|
304
|
-
invite: { id: string; status: string };
|
|
305
|
-
};
|
|
306
|
-
expect(created.invite.status).toBe("active");
|
|
307
|
-
|
|
308
|
-
const revoked = revokeInvite(created.invite.id);
|
|
309
|
-
expect(revoked?.status).toBe("revoked");
|
|
310
|
-
expect(revoked?.id).toBe(created.invite.id);
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
// ---------------------------------------------------------------------------
|
|
315
|
-
// Voice invite routes
|
|
301
|
+
// Presentation composition (daemon-owned layer over the gateway mint payload)
|
|
316
302
|
// ---------------------------------------------------------------------------
|
|
317
303
|
|
|
318
|
-
describe("
|
|
304
|
+
describe("composeInvitePresentation", () => {
|
|
319
305
|
beforeEach(resetTables);
|
|
320
306
|
|
|
321
|
-
test("
|
|
322
|
-
const
|
|
323
|
-
method: "POST",
|
|
324
|
-
headers: { "Content-Type": "application/json" },
|
|
325
|
-
body: JSON.stringify({
|
|
326
|
-
sourceChannel: "phone",
|
|
327
|
-
contactId: createTargetContact("Alice"),
|
|
328
|
-
expectedExternalUserId: "+15551234567",
|
|
329
|
-
maxUses: 3,
|
|
330
|
-
}),
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
const res = await handleCreateInvite(req);
|
|
334
|
-
const body = (await res.json()) as Record<string, unknown>;
|
|
335
|
-
|
|
336
|
-
expect(res.status).toBe(201);
|
|
337
|
-
expect(body.ok).toBe(true);
|
|
338
|
-
const invite = body.invite as Record<string, unknown>;
|
|
339
|
-
expect(invite.sourceChannel).toBe("phone");
|
|
340
|
-
// Voice code should be returned (6 digits by default)
|
|
341
|
-
expect(typeof invite.voiceCode).toBe("string");
|
|
342
|
-
expect((invite.voiceCode as string).length).toBe(6);
|
|
343
|
-
expect(/^\d{6}$/.test(invite.voiceCode as string)).toBe(true);
|
|
344
|
-
// Hash should be stored
|
|
345
|
-
expect(typeof invite.tokenHash).toBe("string");
|
|
346
|
-
expect((invite.tokenHash as string).length).toBeGreaterThan(0);
|
|
347
|
-
// voiceCodeDigits should be recorded
|
|
348
|
-
expect(invite.voiceCodeDigits).toBe(6);
|
|
349
|
-
// expectedExternalUserId should be recorded
|
|
350
|
-
expect(invite.expectedExternalUserId).toBe("+15551234567");
|
|
351
|
-
// friendName is mirrored from the bound contact's displayName, not a flag
|
|
352
|
-
expect(invite.friendName).toBe("Alice");
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
test("voice invite creation requires expectedExternalUserId", async () => {
|
|
356
|
-
const req = new Request("http://localhost/v1/contacts/invites", {
|
|
357
|
-
method: "POST",
|
|
358
|
-
headers: { "Content-Type": "application/json" },
|
|
359
|
-
body: JSON.stringify({
|
|
360
|
-
sourceChannel: "phone",
|
|
361
|
-
contactId: createTargetContact(),
|
|
362
|
-
}),
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
const res = await handleCreateInvite(req);
|
|
366
|
-
const body = (await res.json()) as Record<string, unknown>;
|
|
367
|
-
|
|
368
|
-
expect(res.status).toBe(400);
|
|
369
|
-
expect(body.ok).toBe(false);
|
|
370
|
-
expect(body.error).toContain("expectedExternalUserId");
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
test("voice invite creation validates E.164 format", async () => {
|
|
374
|
-
const req = new Request("http://localhost/v1/contacts/invites", {
|
|
375
|
-
method: "POST",
|
|
376
|
-
headers: { "Content-Type": "application/json" },
|
|
377
|
-
body: JSON.stringify({
|
|
378
|
-
sourceChannel: "phone",
|
|
379
|
-
contactId: createTargetContact(),
|
|
380
|
-
expectedExternalUserId: "not-a-phone-number",
|
|
381
|
-
}),
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
const res = await handleCreateInvite(req);
|
|
385
|
-
const body = (await res.json()) as Record<string, unknown>;
|
|
386
|
-
|
|
387
|
-
expect(res.status).toBe(400);
|
|
388
|
-
expect(body.ok).toBe(false);
|
|
389
|
-
expect(body.error).toContain("E.164");
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
test("voiceCodeDigits is always 6 — custom values are ignored", async () => {
|
|
393
|
-
const req = new Request("http://localhost/v1/contacts/invites", {
|
|
394
|
-
method: "POST",
|
|
395
|
-
headers: { "Content-Type": "application/json" },
|
|
396
|
-
body: JSON.stringify({
|
|
397
|
-
sourceChannel: "phone",
|
|
398
|
-
contactId: createTargetContact(),
|
|
399
|
-
expectedExternalUserId: "+15551234567",
|
|
400
|
-
voiceCodeDigits: 8,
|
|
401
|
-
}),
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
const res = await handleCreateInvite(req);
|
|
405
|
-
const body = (await res.json()) as Record<string, unknown>;
|
|
307
|
+
test("voice invites get a guardianInstruction with the contact's first name", async () => {
|
|
308
|
+
const contactId = createTargetContact("Carolina Example");
|
|
406
309
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
expect(invite.voiceCodeDigits).toBe(6);
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
test("voice invites do NOT return token in response", async () => {
|
|
415
|
-
const req = new Request("http://localhost/v1/contacts/invites", {
|
|
416
|
-
method: "POST",
|
|
417
|
-
headers: { "Content-Type": "application/json" },
|
|
418
|
-
body: JSON.stringify({
|
|
310
|
+
const invite = await composeInvitePresentation({
|
|
311
|
+
contactId,
|
|
312
|
+
invite: {
|
|
313
|
+
id: "inv-voice-1",
|
|
419
314
|
sourceChannel: "phone",
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
315
|
+
voiceCode: "123456",
|
|
316
|
+
voiceCodeDigits: 6,
|
|
317
|
+
},
|
|
423
318
|
});
|
|
424
319
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
expect(res.status).toBe(201);
|
|
429
|
-
const invite = body.invite as Record<string, unknown>;
|
|
430
|
-
// Voice invites must not expose the raw token — callers redeem via
|
|
431
|
-
// the identity-bound voice code flow
|
|
432
|
-
expect(invite.token).toBeUndefined();
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
test("POST /v1/contacts/invites/redeem — redeems a voice invite code via unified endpoint", async () => {
|
|
436
|
-
// Create a voice invite
|
|
437
|
-
const createRes = await handleCreateInvite(
|
|
438
|
-
new Request("http://localhost/v1/contacts/invites", {
|
|
439
|
-
method: "POST",
|
|
440
|
-
headers: { "Content-Type": "application/json" },
|
|
441
|
-
body: JSON.stringify({
|
|
442
|
-
sourceChannel: "phone",
|
|
443
|
-
contactId: createTargetContact(),
|
|
444
|
-
expectedExternalUserId: "+15551234567",
|
|
445
|
-
maxUses: 1,
|
|
446
|
-
}),
|
|
447
|
-
}),
|
|
448
|
-
);
|
|
449
|
-
const created = (await createRes.json()) as {
|
|
450
|
-
invite: { voiceCode: string };
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// Redeem the voice code via the unified /redeem endpoint
|
|
454
|
-
const redeemReq = new Request(
|
|
455
|
-
"http://localhost/v1/contacts/invites/redeem",
|
|
456
|
-
{
|
|
457
|
-
method: "POST",
|
|
458
|
-
headers: { "Content-Type": "application/json" },
|
|
459
|
-
body: JSON.stringify({
|
|
460
|
-
callerExternalUserId: "+15551234567",
|
|
461
|
-
code: created.invite.voiceCode,
|
|
462
|
-
}),
|
|
463
|
-
},
|
|
320
|
+
expect(invite.guardianInstruction).toBe(
|
|
321
|
+
"Carolina will need this code when they answer. Share it with them first.",
|
|
464
322
|
);
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const body = (await res.json()) as Record<string, unknown>;
|
|
468
|
-
|
|
469
|
-
expect(res.status).toBe(200);
|
|
470
|
-
expect(body.ok).toBe(true);
|
|
471
|
-
expect(body.type).toBe("redeemed");
|
|
472
|
-
expect(typeof body.memberId).toBe("string");
|
|
473
|
-
expect(typeof body.inviteId).toBe("string");
|
|
323
|
+
// Voice invites never gain a share payload (no raw token exists).
|
|
324
|
+
expect(invite.share).toBeUndefined();
|
|
474
325
|
});
|
|
475
326
|
|
|
476
|
-
test("
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
body: JSON.stringify({ callerExternalUserId: "+15551234567" }),
|
|
327
|
+
test("voice invites fall back to the generic instruction when no contact name resolves", async () => {
|
|
328
|
+
const invite = await composeInvitePresentation({
|
|
329
|
+
contactId: "missing-contact",
|
|
330
|
+
invite: { id: "inv-voice-2", sourceChannel: "phone" },
|
|
481
331
|
});
|
|
482
332
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
// No `code` and no `token` → falls through to token-based path which requires token
|
|
487
|
-
expect(res.status).toBe(400);
|
|
488
|
-
expect(body.ok).toBe(false);
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
test("POST /v1/contacts/invites/redeem — wrong voice code returns 400", async () => {
|
|
492
|
-
// Create a voice invite
|
|
493
|
-
await handleCreateInvite(
|
|
494
|
-
new Request("http://localhost/v1/contacts/invites", {
|
|
495
|
-
method: "POST",
|
|
496
|
-
headers: { "Content-Type": "application/json" },
|
|
497
|
-
body: JSON.stringify({
|
|
498
|
-
sourceChannel: "phone",
|
|
499
|
-
contactId: createTargetContact(),
|
|
500
|
-
expectedExternalUserId: "+15551234567",
|
|
501
|
-
maxUses: 1,
|
|
502
|
-
}),
|
|
503
|
-
}),
|
|
333
|
+
expect(invite.guardianInstruction).toBe(
|
|
334
|
+
"Share this code with them — they'll need it when they answer the call.",
|
|
504
335
|
);
|
|
336
|
+
});
|
|
505
337
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
headers: { "Content-Type": "application/json" },
|
|
509
|
-
body: JSON.stringify({
|
|
510
|
-
callerExternalUserId: "+15551234567",
|
|
511
|
-
code: "000000",
|
|
512
|
-
}),
|
|
513
|
-
});
|
|
338
|
+
test("telegram invites gain share URL + guardianInstruction when bot username is configured", async () => {
|
|
339
|
+
mockTelegramBotUsername = "test_invite_bot";
|
|
514
340
|
|
|
515
|
-
|
|
516
|
-
|
|
341
|
+
try {
|
|
342
|
+
const rawToken = "raw-token-abc";
|
|
343
|
+
const invite = await composeInvitePresentation({
|
|
344
|
+
contactId: createTargetContact("Alice"),
|
|
345
|
+
invite: {
|
|
346
|
+
id: "inv-tg-1",
|
|
347
|
+
sourceChannel: "telegram",
|
|
348
|
+
token: rawToken,
|
|
349
|
+
inviteCode: "654321",
|
|
350
|
+
},
|
|
351
|
+
rawToken,
|
|
352
|
+
});
|
|
517
353
|
|
|
518
|
-
|
|
519
|
-
|
|
354
|
+
const share = invite.share as Record<string, unknown>;
|
|
355
|
+
expect(share).toBeDefined();
|
|
356
|
+
expect(share.url).toBe(`https://t.me/test_invite_bot?start=iv_${rawToken}`);
|
|
357
|
+
expect(typeof share.displayText).toBe("string");
|
|
358
|
+
expect(typeof invite.guardianInstruction).toBe("string");
|
|
359
|
+
expect((invite.guardianInstruction as string).length).toBeGreaterThan(0);
|
|
360
|
+
// One-time secrets from the gateway payload pass through untouched.
|
|
361
|
+
expect(invite.token).toBe(rawToken);
|
|
362
|
+
expect(invite.inviteCode).toBe("654321");
|
|
363
|
+
} finally {
|
|
364
|
+
mockTelegramBotUsername = undefined;
|
|
365
|
+
}
|
|
520
366
|
});
|
|
521
367
|
|
|
522
|
-
test("voice
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
sourceChannel: "phone",
|
|
528
|
-
contactId: createTargetContact("Carolina Flaherty"),
|
|
529
|
-
expectedExternalUserId: "+15551234567",
|
|
530
|
-
}),
|
|
368
|
+
test("non-voice payloads without an inviteCode pass through unchanged", async () => {
|
|
369
|
+
const payload = { id: "inv-x", sourceChannel: "telegram", status: "active" };
|
|
370
|
+
const invite = await composeInvitePresentation({
|
|
371
|
+
contactId: createTargetContact(),
|
|
372
|
+
invite: payload,
|
|
531
373
|
});
|
|
532
|
-
|
|
533
|
-
const res = await handleCreateInvite(req);
|
|
534
|
-
const body = (await res.json()) as Record<string, unknown>;
|
|
535
|
-
|
|
536
|
-
expect(res.status).toBe(201);
|
|
537
|
-
expect(body.ok).toBe(true);
|
|
538
|
-
const invite = body.invite as Record<string, unknown>;
|
|
539
|
-
// First-token of the contact's displayName is used in the instruction
|
|
540
|
-
expect(invite.guardianInstruction).toBe(
|
|
541
|
-
"Carolina will need this code when they answer. Share it with them first.",
|
|
542
|
-
);
|
|
374
|
+
expect(invite).toEqual(payload);
|
|
543
375
|
});
|
|
544
376
|
});
|
|
545
377
|
|
|
@@ -547,86 +379,80 @@ describe("voice invite HTTP routes", () => {
|
|
|
547
379
|
// Trigger invite call endpoint
|
|
548
380
|
// ---------------------------------------------------------------------------
|
|
549
381
|
|
|
382
|
+
// Invite lifecycle validation (existence/active/expiry/phone) lives in the
|
|
383
|
+
// gateway's triggerInviteCallNative — see the gateway proxy tests. The daemon
|
|
384
|
+
// only performs the provider call from the gateway-supplied fields.
|
|
550
385
|
describe("POST /v1/contacts/invites/:id/call", () => {
|
|
551
386
|
beforeEach(() => {
|
|
552
387
|
resetTables();
|
|
553
388
|
mockStartInviteCallResult = { ok: true, callSid: "CA_test_sid_123" };
|
|
389
|
+
lastStartInviteCallInput = null;
|
|
554
390
|
});
|
|
555
391
|
|
|
556
|
-
test("
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
sourceChannel: "phone",
|
|
563
|
-
contactId: createTargetContact(),
|
|
564
|
-
expectedExternalUserId: "+15551234567",
|
|
565
|
-
}),
|
|
566
|
-
}),
|
|
567
|
-
);
|
|
568
|
-
const created = (await createRes.json()) as { invite: { id: string } };
|
|
569
|
-
|
|
570
|
-
const res = await handleTriggerInviteCall(created.invite.id);
|
|
392
|
+
test("places the call from the gateway-supplied fields", async () => {
|
|
393
|
+
const res = await handleTriggerInviteCall({
|
|
394
|
+
phoneNumber: "+15551234567",
|
|
395
|
+
friendName: "Alice",
|
|
396
|
+
guardianName: "Guardian Label",
|
|
397
|
+
});
|
|
571
398
|
const body = (await res.json()) as Record<string, unknown>;
|
|
572
399
|
|
|
573
400
|
expect(res.status).toBe(200);
|
|
574
401
|
expect(body.ok).toBe(true);
|
|
575
402
|
expect(body.callSid).toBe("CA_test_sid_123");
|
|
403
|
+
expect(lastStartInviteCallInput).toEqual({
|
|
404
|
+
phoneNumber: "+15551234567",
|
|
405
|
+
friendName: "Alice",
|
|
406
|
+
guardianName: "Guardian Label",
|
|
407
|
+
});
|
|
576
408
|
});
|
|
577
409
|
|
|
578
|
-
test("returns 400
|
|
579
|
-
|
|
410
|
+
test("returns 400 when phoneNumber is missing (no local invite fallback)", async () => {
|
|
411
|
+
// The gateway row is the lifecycle authority and supplies the call fields;
|
|
412
|
+
// the daemon has no invite store to fall back on.
|
|
413
|
+
const res = await handleTriggerInviteCall({});
|
|
580
414
|
const body = (await res.json()) as Record<string, unknown>;
|
|
581
415
|
|
|
582
416
|
expect(res.status).toBe(400);
|
|
583
417
|
expect(body.ok).toBe(false);
|
|
584
|
-
expect(body.error).toBe("
|
|
418
|
+
expect(body.error).toBe("phoneNumber is required");
|
|
419
|
+
expect(lastStartInviteCallInput).toBeNull();
|
|
585
420
|
});
|
|
586
421
|
|
|
587
|
-
test("
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
sourceChannel: "phone",
|
|
594
|
-
contactId: createTargetContact(),
|
|
595
|
-
expectedExternalUserId: "+15551234567",
|
|
596
|
-
}),
|
|
597
|
-
}),
|
|
598
|
-
);
|
|
599
|
-
const created = (await createRes.json()) as { invite: { id: string } };
|
|
422
|
+
test("empty friendName falls through to the neutral-greeting contract", async () => {
|
|
423
|
+
const res = await handleTriggerInviteCall({
|
|
424
|
+
phoneNumber: "+15551234567",
|
|
425
|
+
friendName: " ",
|
|
426
|
+
guardianName: "Guardian Label",
|
|
427
|
+
});
|
|
600
428
|
|
|
601
|
-
|
|
602
|
-
|
|
429
|
+
expect(res.status).toBe(200);
|
|
430
|
+
expect(lastStartInviteCallInput).toMatchObject({ friendName: "" });
|
|
431
|
+
});
|
|
603
432
|
|
|
604
|
-
|
|
605
|
-
const
|
|
433
|
+
test("null guardianName falls back to the resolved guardian name", async () => {
|
|
434
|
+
const res = await handleTriggerInviteCall({
|
|
435
|
+
phoneNumber: "+15551234567",
|
|
436
|
+
friendName: "Alice",
|
|
437
|
+
guardianName: null,
|
|
438
|
+
});
|
|
606
439
|
|
|
607
|
-
expect(res.status).toBe(
|
|
608
|
-
|
|
609
|
-
|
|
440
|
+
expect(res.status).toBe(200);
|
|
441
|
+
// resolveGuardianName() has no persona in this env, so the fallback
|
|
442
|
+
// resolves to the empty-string default rather than the null passthrough.
|
|
443
|
+
expect(typeof lastStartInviteCallInput?.guardianName).toBe("string");
|
|
610
444
|
});
|
|
611
445
|
|
|
612
|
-
test("
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
body: JSON.stringify({
|
|
618
|
-
sourceChannel: "telegram",
|
|
619
|
-
contactId: createTargetContact(),
|
|
620
|
-
}),
|
|
621
|
-
}),
|
|
622
|
-
);
|
|
623
|
-
const created = (await createRes.json()) as { invite: { id: string } };
|
|
446
|
+
test("surfaces a failed provider call as 400", async () => {
|
|
447
|
+
mockStartInviteCallResult = {
|
|
448
|
+
ok: false,
|
|
449
|
+
error: "phone_number must be in E.164 format",
|
|
450
|
+
};
|
|
624
451
|
|
|
625
|
-
const res = await handleTriggerInviteCall(
|
|
452
|
+
const res = await handleTriggerInviteCall({ phoneNumber: "+15551234567" });
|
|
626
453
|
const body = (await res.json()) as Record<string, unknown>;
|
|
627
454
|
|
|
628
455
|
expect(res.status).toBe(400);
|
|
629
|
-
expect(body.
|
|
630
|
-
expect(body.error).toBe("Only phone invites support call triggering");
|
|
456
|
+
expect(body.error).toBe("phone_number must be in E.164 format");
|
|
631
457
|
});
|
|
632
458
|
});
|