@vellumai/assistant 0.9.0 → 0.10.0-staging.2
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 +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +49 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import { buildToolApprovalSeedContentBlocks } from "../notifications/
|
|
3
|
+
import { buildToolApprovalSeedContentBlocks } from "../notifications/approval-card-data.js";
|
|
4
4
|
|
|
5
5
|
describe("buildToolApprovalSeedContentBlocks", () => {
|
|
6
6
|
const toolApprovalPayload: Record<string, unknown> = {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
// Toggle for the
|
|
3
|
+
// Toggle for the share_analytics opt-out gate the listener consults when
|
|
4
4
|
// populating the telemetry columns.
|
|
5
|
-
let
|
|
5
|
+
let shareAnalytics = true;
|
|
6
6
|
|
|
7
|
-
mock.module("../
|
|
8
|
-
|
|
7
|
+
mock.module("../platform/consent-cache.js", () => ({
|
|
8
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
11
|
import { createToolAuditListener } from "../events/tool-audit-listener.js";
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
|
|
18
18
|
describe("tool audit listener", () => {
|
|
19
19
|
beforeEach(() => {
|
|
20
|
-
|
|
20
|
+
shareAnalytics = true;
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
test("records executed events with truncated output", () => {
|
|
@@ -361,8 +361,8 @@ describe("tool audit listener", () => {
|
|
|
361
361
|
);
|
|
362
362
|
});
|
|
363
363
|
|
|
364
|
-
test("persists NULL telemetry columns when
|
|
365
|
-
|
|
364
|
+
test("persists NULL telemetry columns when share_analytics is opted out", () => {
|
|
365
|
+
shareAnalytics = false;
|
|
366
366
|
const records: ToolInvocationRecord[] = [];
|
|
367
367
|
const listener = createToolAuditListener((record) => records.push(record));
|
|
368
368
|
|
|
@@ -34,9 +34,6 @@ const mockConfig = {
|
|
|
34
34
|
permissions: {
|
|
35
35
|
mode: "workspace" as const,
|
|
36
36
|
},
|
|
37
|
-
// The audit listener nulls the telemetry columns when this is false; the
|
|
38
|
-
// end-to-end listener tests below assert the opted-in sizing behavior.
|
|
39
|
-
collectUsageData: true,
|
|
40
37
|
};
|
|
41
38
|
|
|
42
39
|
let checkerDecision: "allow" | "prompt" | "deny" = "allow";
|
|
@@ -56,6 +53,12 @@ mock.module("../config/loader.js", () => ({
|
|
|
56
53
|
setNestedValue: () => {},
|
|
57
54
|
}));
|
|
58
55
|
|
|
56
|
+
// Analytics consent is granted so the audit listener populates the telemetry
|
|
57
|
+
// columns; the end-to-end listener tests below assert the opted-in sizing.
|
|
58
|
+
mock.module("../platform/consent-cache.js", () => ({
|
|
59
|
+
getCachedShareAnalytics: () => true,
|
|
60
|
+
}));
|
|
61
|
+
|
|
59
62
|
mock.module("../util/logger.js", () => ({
|
|
60
63
|
getLogger: () =>
|
|
61
64
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -587,36 +587,6 @@ describe("isSideEffectTool", () => {
|
|
|
587
587
|
expect(isSideEffectTool("nonexistent_tool")).toBe(false);
|
|
588
588
|
expect(isSideEffectTool("")).toBe(false);
|
|
589
589
|
});
|
|
590
|
-
|
|
591
|
-
describe("action-aware classification for mixed-action tools", () => {
|
|
592
|
-
test("credential_store store is a side-effect", () => {
|
|
593
|
-
expect(isSideEffectTool("credential_store", { action: "store" })).toBe(
|
|
594
|
-
true,
|
|
595
|
-
);
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
test("credential_store delete is a side-effect", () => {
|
|
599
|
-
expect(isSideEffectTool("credential_store", { action: "delete" })).toBe(
|
|
600
|
-
true,
|
|
601
|
-
);
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
test("credential_store prompt is a side-effect", () => {
|
|
605
|
-
expect(isSideEffectTool("credential_store", { action: "prompt" })).toBe(
|
|
606
|
-
true,
|
|
607
|
-
);
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
test("credential_store list is NOT a side-effect", () => {
|
|
611
|
-
expect(isSideEffectTool("credential_store", { action: "list" })).toBe(
|
|
612
|
-
false,
|
|
613
|
-
);
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
test("credential_store without input is NOT a side-effect", () => {
|
|
617
|
-
expect(isSideEffectTool("credential_store")).toBe(false);
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
590
|
});
|
|
621
591
|
|
|
622
592
|
// ---------------------------------------------------------------------------
|
|
@@ -751,10 +721,6 @@ describe("ToolExecutor forcePromptSideEffects enforcement", () => {
|
|
|
751
721
|
{ name: "document_create", input: { title: "doc", content: "body" } },
|
|
752
722
|
{ name: "document_update", input: { id: "doc-1", content: "updated" } },
|
|
753
723
|
{ name: "document_delete", input: { surface_id: "doc-1" } },
|
|
754
|
-
{
|
|
755
|
-
name: "credential_store",
|
|
756
|
-
input: { action: "store", name: "api-key", value: "secret" },
|
|
757
|
-
},
|
|
758
724
|
];
|
|
759
725
|
|
|
760
726
|
for (const { name, input } of sideEffectTools) {
|
|
@@ -817,51 +783,6 @@ describe("ToolExecutor forcePromptSideEffects enforcement", () => {
|
|
|
817
783
|
expect(promptCalled).toBe(true);
|
|
818
784
|
});
|
|
819
785
|
|
|
820
|
-
// ── Credential store action-aware (PR fix9) ──────────
|
|
821
|
-
|
|
822
|
-
test("credential_store store forces prompt under forcePromptSideEffects", async () => {
|
|
823
|
-
checkResultOverride = { decision: "allow", reason: "Matched trust rule" };
|
|
824
|
-
|
|
825
|
-
const executor = new ToolExecutor(makeTrackingPrompter());
|
|
826
|
-
const result = await executor.execute(
|
|
827
|
-
"credential_store",
|
|
828
|
-
{ action: "store", name: "api-key", value: "sk-secret-123" },
|
|
829
|
-
makeContext({ forcePromptSideEffects: true }),
|
|
830
|
-
);
|
|
831
|
-
|
|
832
|
-
expect(result.isError).toBe(false);
|
|
833
|
-
expect(promptCalled).toBe(true);
|
|
834
|
-
});
|
|
835
|
-
|
|
836
|
-
test("credential_store delete forces prompt under forcePromptSideEffects", async () => {
|
|
837
|
-
checkResultOverride = { decision: "allow", reason: "Matched trust rule" };
|
|
838
|
-
|
|
839
|
-
const executor = new ToolExecutor(makeTrackingPrompter());
|
|
840
|
-
const result = await executor.execute(
|
|
841
|
-
"credential_store",
|
|
842
|
-
{ action: "delete", name: "api-key" },
|
|
843
|
-
makeContext({ forcePromptSideEffects: true }),
|
|
844
|
-
);
|
|
845
|
-
|
|
846
|
-
expect(result.isError).toBe(false);
|
|
847
|
-
expect(promptCalled).toBe(true);
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
test("credential_store list does NOT force prompt under forcePromptSideEffects", async () => {
|
|
851
|
-
checkResultOverride = { decision: "allow", reason: "Matched trust rule" };
|
|
852
|
-
|
|
853
|
-
const executor = new ToolExecutor(makeTrackingPrompter());
|
|
854
|
-
const result = await executor.execute(
|
|
855
|
-
"credential_store",
|
|
856
|
-
{ action: "list" },
|
|
857
|
-
makeContext({ forcePromptSideEffects: true }),
|
|
858
|
-
);
|
|
859
|
-
|
|
860
|
-
expect(result.isError).toBe(false);
|
|
861
|
-
// list is read-only — must NOT trigger forced prompting
|
|
862
|
-
expect(promptCalled).toBe(false);
|
|
863
|
-
});
|
|
864
|
-
|
|
865
786
|
// ── Workspace mode + forcePromptSideEffects interaction ──────────
|
|
866
787
|
|
|
867
788
|
test("workspace mode allow → prompt promotion still works for side-effect tools under forcePromptSideEffects", async () => {
|
|
@@ -169,8 +169,10 @@ async function simulateNotifierPoll(params: {
|
|
|
169
169
|
notifiedRequestIds,
|
|
170
170
|
} = params;
|
|
171
171
|
|
|
172
|
-
// Gate check: only
|
|
173
|
-
|
|
172
|
+
// Gate check: only identity-known non-guardian contacts with guardian route
|
|
173
|
+
const isIdentityKnownNonGuardian =
|
|
174
|
+
trustClass === "trusted_contact" || trustClass === "unverified_contact";
|
|
175
|
+
if (!isIdentityKnownNonGuardian || !guardianExternalUserId) {
|
|
174
176
|
return false;
|
|
175
177
|
}
|
|
176
178
|
|
|
@@ -92,7 +92,7 @@ mock.module("../runtime/channel-verification-service.js", () => ({
|
|
|
92
92
|
return null;
|
|
93
93
|
},
|
|
94
94
|
createOutboundSession: () => ({
|
|
95
|
-
|
|
95
|
+
sessionId: "test-session",
|
|
96
96
|
secret: "123456",
|
|
97
97
|
}),
|
|
98
98
|
bindSessionIdentity: () => {},
|
|
@@ -108,16 +108,23 @@ mock.module("../runtime/channel-verification-service.js", () => ({
|
|
|
108
108
|
}),
|
|
109
109
|
}));
|
|
110
110
|
|
|
111
|
-
// Mock gateway client — capture delivery calls
|
|
111
|
+
// Mock gateway client — capture delivery calls. `failDeliveryWhen` lets a test
|
|
112
|
+
// simulate a delivery failure for a specific payload (e.g. a DM that can't be
|
|
113
|
+
// opened) so fallback paths can be exercised.
|
|
112
114
|
const deliveredReplies: Array<{
|
|
113
115
|
url: string;
|
|
114
116
|
payload: Record<string, unknown>;
|
|
115
117
|
}> = [];
|
|
118
|
+
let failDeliveryWhen: ((payload: Record<string, unknown>) => boolean) | null =
|
|
119
|
+
null;
|
|
116
120
|
mock.module("../runtime/gateway-client.js", () => ({
|
|
117
121
|
deliverChannelReply: async (
|
|
118
122
|
url: string,
|
|
119
123
|
payload: Record<string, unknown>,
|
|
120
124
|
) => {
|
|
125
|
+
if (failDeliveryWhen?.(payload)) {
|
|
126
|
+
throw new Error("simulated delivery failure");
|
|
127
|
+
}
|
|
121
128
|
deliveredReplies.push({ url, payload });
|
|
122
129
|
return { ok: true };
|
|
123
130
|
},
|
|
@@ -150,6 +157,7 @@ mock.module("../config/env.js", () => ({
|
|
|
150
157
|
import { applyCanonicalGuardianDecision } from "../approvals/guardian-decision-primitive.js";
|
|
151
158
|
import type { ActorContext } from "../approvals/guardian-request-resolvers.js";
|
|
152
159
|
import { getResolver } from "../approvals/guardian-request-resolvers.js";
|
|
160
|
+
import { upsertContactChannel } from "../contacts/contacts-write.js";
|
|
153
161
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
154
162
|
import {
|
|
155
163
|
createCanonicalGuardianRequest,
|
|
@@ -564,7 +572,7 @@ describe("(d) unknown actor flow: fail-closed with no interactive approval", ()
|
|
|
564
572
|
|
|
565
573
|
expect("skipped" in result && result.skipped).toBe(true);
|
|
566
574
|
if ("skipped" in result) {
|
|
567
|
-
expect(result.reason).toBe("
|
|
575
|
+
expect(result.reason).toBe("not_bridgeable_trust_class");
|
|
568
576
|
}
|
|
569
577
|
});
|
|
570
578
|
});
|
|
@@ -1088,3 +1096,212 @@ describe("cross-milestone integration checks", () => {
|
|
|
1088
1096
|
});
|
|
1089
1097
|
});
|
|
1090
1098
|
|
|
1099
|
+
// ===========================================================================
|
|
1100
|
+
// (g) access_request resolver: requester verification-code delivery
|
|
1101
|
+
//
|
|
1102
|
+
// On approval the requester must receive the 6-digit code so the guardian
|
|
1103
|
+
// never has to relay it by hand. On Slack the code is DM'd straight to the
|
|
1104
|
+
// requester (a private path is guaranteed via their user ID); other channels
|
|
1105
|
+
// keep the courier message because a private path to the requester is not
|
|
1106
|
+
// guaranteed there (e.g. a group chat would leak the secret).
|
|
1107
|
+
// ===========================================================================
|
|
1108
|
+
|
|
1109
|
+
describe("(g) access_request resolver: requester code delivery", () => {
|
|
1110
|
+
const REQUESTER_UID = "U_REQUESTER";
|
|
1111
|
+
const GUARDIAN_UID = "U_GUARDIAN";
|
|
1112
|
+
|
|
1113
|
+
function createAccessRequest(overrides: Record<string, unknown> = {}) {
|
|
1114
|
+
return createCanonicalGuardianRequest({
|
|
1115
|
+
id: `access-req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
1116
|
+
kind: "access_request",
|
|
1117
|
+
sourceType: "channel",
|
|
1118
|
+
sourceChannel: "slack",
|
|
1119
|
+
conversationId: "conv-access-slack",
|
|
1120
|
+
requesterExternalUserId: REQUESTER_UID,
|
|
1121
|
+
requesterChatId: "C_SHARED_CHANNEL",
|
|
1122
|
+
guardianExternalUserId: GUARDIAN_UID,
|
|
1123
|
+
guardianPrincipalId: "test-principal-id",
|
|
1124
|
+
toolName: "ingress_access_request",
|
|
1125
|
+
expiresAt: Date.now() + 60_000,
|
|
1126
|
+
...overrides,
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
beforeEach(() => {
|
|
1131
|
+
resetTables();
|
|
1132
|
+
deliveredReplies.length = 0;
|
|
1133
|
+
emittedSignals.length = 0;
|
|
1134
|
+
failDeliveryWhen = null;
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
test("on-channel Slack approval DMs the verification code to the requester", async () => {
|
|
1138
|
+
const req = createAccessRequest();
|
|
1139
|
+
|
|
1140
|
+
const result = await applyCanonicalGuardianDecision({
|
|
1141
|
+
requestId: req.id,
|
|
1142
|
+
action: "approve_once",
|
|
1143
|
+
actorContext: guardianActor({
|
|
1144
|
+
channel: "slack",
|
|
1145
|
+
actorExternalUserId: GUARDIAN_UID,
|
|
1146
|
+
}),
|
|
1147
|
+
channelDeliveryContext: {
|
|
1148
|
+
replyCallbackUrl:
|
|
1149
|
+
"http://localhost:3000/deliver/slack?threadTs=111.222",
|
|
1150
|
+
guardianChatId: "C_SHARED_CHANNEL",
|
|
1151
|
+
assistantId: "self",
|
|
1152
|
+
},
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
expect(result.applied).toBe(true);
|
|
1156
|
+
|
|
1157
|
+
// The requester receives the actual code in their DM (chatId = user ID),
|
|
1158
|
+
// not a "ask the guardian" courier message.
|
|
1159
|
+
const requesterCodeReply = deliveredReplies.find(
|
|
1160
|
+
(r) =>
|
|
1161
|
+
r.payload.chatId === REQUESTER_UID &&
|
|
1162
|
+
typeof r.payload.text === "string" &&
|
|
1163
|
+
(r.payload.text as string).includes("123456"),
|
|
1164
|
+
);
|
|
1165
|
+
expect(requesterCodeReply).toBeDefined();
|
|
1166
|
+
expect(requesterCodeReply!.payload.text).toContain(
|
|
1167
|
+
"your access request was approved",
|
|
1168
|
+
);
|
|
1169
|
+
// The code DM is durable, never ephemeral.
|
|
1170
|
+
expect(requesterCodeReply!.payload.ephemeral).toBeUndefined();
|
|
1171
|
+
// threadTs (the guardian's channel thread) is stripped for the DM.
|
|
1172
|
+
expect(requesterCodeReply!.url).not.toContain("threadTs");
|
|
1173
|
+
|
|
1174
|
+
// No courier "receive from the guardian" message goes to the requester.
|
|
1175
|
+
const courier = deliveredReplies.find(
|
|
1176
|
+
(r) =>
|
|
1177
|
+
typeof r.payload.text === "string" &&
|
|
1178
|
+
(r.payload.text as string).includes("receive from the guardian"),
|
|
1179
|
+
);
|
|
1180
|
+
expect(courier).toBeUndefined();
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
test("desktop-decided approval DMs the code to the Slack requester via the deliver path", async () => {
|
|
1184
|
+
const req = createAccessRequest();
|
|
1185
|
+
|
|
1186
|
+
const result = await applyCanonicalGuardianDecision({
|
|
1187
|
+
requestId: req.id,
|
|
1188
|
+
action: "approve_once",
|
|
1189
|
+
// Desktop decision: no channelDeliveryContext, actor on the vellum channel.
|
|
1190
|
+
actorContext: guardianActor({
|
|
1191
|
+
channel: "vellum",
|
|
1192
|
+
actorExternalUserId: undefined,
|
|
1193
|
+
}),
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
expect(result.applied).toBe(true);
|
|
1197
|
+
|
|
1198
|
+
const requesterCodeReply = deliveredReplies.find(
|
|
1199
|
+
(r) =>
|
|
1200
|
+
r.payload.chatId === REQUESTER_UID &&
|
|
1201
|
+
typeof r.payload.text === "string" &&
|
|
1202
|
+
(r.payload.text as string).includes("123456"),
|
|
1203
|
+
);
|
|
1204
|
+
expect(requesterCodeReply).toBeDefined();
|
|
1205
|
+
expect(requesterCodeReply!.url).toContain("/deliver/slack");
|
|
1206
|
+
expect(requesterCodeReply!.payload.text).toContain(
|
|
1207
|
+
"your access request was approved",
|
|
1208
|
+
);
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
test("non-Slack channel keeps the courier message and never delivers the code to the requester chat", async () => {
|
|
1212
|
+
const req = createAccessRequest({
|
|
1213
|
+
sourceChannel: "telegram",
|
|
1214
|
+
requesterChatId: "requester-chat-1",
|
|
1215
|
+
conversationId: "conv-access-telegram",
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
const result = await applyCanonicalGuardianDecision({
|
|
1219
|
+
requestId: req.id,
|
|
1220
|
+
action: "approve_once",
|
|
1221
|
+
actorContext: guardianActor({
|
|
1222
|
+
channel: "telegram",
|
|
1223
|
+
actorExternalUserId: GUARDIAN_UID,
|
|
1224
|
+
}),
|
|
1225
|
+
channelDeliveryContext: {
|
|
1226
|
+
replyCallbackUrl: "http://localhost:3000/deliver/telegram",
|
|
1227
|
+
guardianChatId: "guardian-chat-1",
|
|
1228
|
+
assistantId: "self",
|
|
1229
|
+
},
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
expect(result.applied).toBe(true);
|
|
1233
|
+
|
|
1234
|
+
// The requester is told to expect the code from the guardian; the secret is
|
|
1235
|
+
// never delivered to the requester's (possibly group) chat.
|
|
1236
|
+
const requesterReply = deliveredReplies.find(
|
|
1237
|
+
(r) => r.payload.chatId === "requester-chat-1",
|
|
1238
|
+
);
|
|
1239
|
+
expect(requesterReply).toBeDefined();
|
|
1240
|
+
expect(requesterReply!.payload.text).toContain("receive from the guardian");
|
|
1241
|
+
expect(requesterReply!.payload.text).not.toContain("123456");
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
test("Slack shared-channel fallback posts an ephemeral notice to the channel when the DM fails", async () => {
|
|
1245
|
+
const req = createAccessRequest();
|
|
1246
|
+
|
|
1247
|
+
// Make the direct DM (to the U... user ID) fail so the courier fallback runs.
|
|
1248
|
+
failDeliveryWhen = (payload) => payload.chatId === REQUESTER_UID;
|
|
1249
|
+
|
|
1250
|
+
const result = await applyCanonicalGuardianDecision({
|
|
1251
|
+
requestId: req.id,
|
|
1252
|
+
action: "approve_once",
|
|
1253
|
+
actorContext: guardianActor({
|
|
1254
|
+
channel: "slack",
|
|
1255
|
+
actorExternalUserId: GUARDIAN_UID,
|
|
1256
|
+
}),
|
|
1257
|
+
channelDeliveryContext: {
|
|
1258
|
+
replyCallbackUrl:
|
|
1259
|
+
"http://localhost:3000/deliver/slack?threadTs=111.222",
|
|
1260
|
+
guardianChatId: "C_SHARED_CHANNEL",
|
|
1261
|
+
assistantId: "self",
|
|
1262
|
+
},
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
expect(result.applied).toBe(true);
|
|
1266
|
+
|
|
1267
|
+
// The courier notice falls back to an ephemeral message targeting the
|
|
1268
|
+
// originating channel (C...), since chat.postEphemeral needs a channel ID —
|
|
1269
|
+
// not the requester's user ID.
|
|
1270
|
+
const courier = deliveredReplies.find(
|
|
1271
|
+
(r) =>
|
|
1272
|
+
typeof r.payload.text === "string" &&
|
|
1273
|
+
(r.payload.text as string).includes("receive from the guardian"),
|
|
1274
|
+
);
|
|
1275
|
+
expect(courier).toBeDefined();
|
|
1276
|
+
expect(courier!.payload.chatId).toBe("C_SHARED_CHANNEL");
|
|
1277
|
+
expect(courier!.payload.ephemeral).toBe(true);
|
|
1278
|
+
expect(courier!.payload.user).toBe(REQUESTER_UID);
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
test("guardian-facing reply uses the requester's display name, not the raw ID", async () => {
|
|
1282
|
+
// Seed a contact so the resolver can resolve a display name.
|
|
1283
|
+
upsertContactChannel({
|
|
1284
|
+
sourceChannel: "slack",
|
|
1285
|
+
externalUserId: REQUESTER_UID,
|
|
1286
|
+
displayName: "Alice",
|
|
1287
|
+
status: "unverified",
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
const req = createAccessRequest();
|
|
1291
|
+
|
|
1292
|
+
const result = await applyCanonicalGuardianDecision({
|
|
1293
|
+
requestId: req.id,
|
|
1294
|
+
action: "approve_once",
|
|
1295
|
+
// Desktop decision → resolver returns guardianReplyText for assertion.
|
|
1296
|
+
actorContext: guardianActor({
|
|
1297
|
+
channel: "vellum",
|
|
1298
|
+
actorExternalUserId: undefined,
|
|
1299
|
+
}),
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
expect(result.applied).toBe(true);
|
|
1303
|
+
const replyText = result.applied ? result.resolverReplyText : undefined;
|
|
1304
|
+
expect(replyText).toContain("Alice");
|
|
1305
|
+
expect(replyText).not.toContain(REQUESTER_UID);
|
|
1306
|
+
});
|
|
1307
|
+
});
|
|
@@ -266,7 +266,7 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
266
266
|
|
|
267
267
|
const contactResult = findContactChannel({
|
|
268
268
|
channelType: config.channel,
|
|
269
|
-
|
|
269
|
+
address: config.senderExternalUserId,
|
|
270
270
|
});
|
|
271
271
|
|
|
272
272
|
expect(contactResult).not.toBeNull();
|
|
@@ -288,7 +288,7 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
288
288
|
// Should be found on this channel
|
|
289
289
|
const sameChanResult = findContactChannel({
|
|
290
290
|
channelType: config.channel,
|
|
291
|
-
|
|
291
|
+
address: config.senderExternalUserId,
|
|
292
292
|
});
|
|
293
293
|
expect(sameChanResult).not.toBeNull();
|
|
294
294
|
|
|
@@ -296,7 +296,7 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
296
296
|
const otherChannel = config.channel === "telegram" ? "slack" : "telegram";
|
|
297
297
|
const crossChanResult = findContactChannel({
|
|
298
298
|
channelType: otherChannel,
|
|
299
|
-
|
|
299
|
+
address: config.senderExternalUserId,
|
|
300
300
|
});
|
|
301
301
|
expect(crossChanResult).toBeNull();
|
|
302
302
|
});
|
|
@@ -102,13 +102,13 @@ describe("trusted contact verification → member activation", () => {
|
|
|
102
102
|
// Verify: active member record exists
|
|
103
103
|
const contactResult = findContactChannel({
|
|
104
104
|
channelType: "telegram",
|
|
105
|
-
|
|
105
|
+
address: "requester-user-123",
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
expect(contactResult).not.toBeNull();
|
|
109
109
|
expect(contactResult!.channel.status).toBe("active");
|
|
110
110
|
expect(contactResult!.channel.policy).toBe("allow");
|
|
111
|
-
expect(contactResult!.channel.
|
|
111
|
+
expect(contactResult!.channel.address).toBe("requester-user-123");
|
|
112
112
|
expect(contactResult!.channel.externalChatId).toBe("requester-chat-123");
|
|
113
113
|
expect(contactResult!.contact.displayName).toBe("Requester Name");
|
|
114
114
|
expect(contactResult!.channel.type).toBe("telegram");
|
|
@@ -236,7 +236,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
236
236
|
// Simulate the ACL check that inbound-message-handler performs
|
|
237
237
|
const contactResult = findContactChannel({
|
|
238
238
|
channelType: "telegram",
|
|
239
|
-
|
|
239
|
+
address: "requester-user-456",
|
|
240
240
|
});
|
|
241
241
|
|
|
242
242
|
expect(contactResult).not.toBeNull();
|
|
@@ -274,7 +274,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
274
274
|
// Member should be found via contacts
|
|
275
275
|
const contactResult = findContactChannel({
|
|
276
276
|
channelType: "telegram",
|
|
277
|
-
|
|
277
|
+
address: "user-cross-test",
|
|
278
278
|
});
|
|
279
279
|
expect(contactResult).not.toBeNull();
|
|
280
280
|
expect(contactResult!.channel.status).toBe("active");
|
|
@@ -282,7 +282,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
282
282
|
// Member should NOT be found for a different channel type
|
|
283
283
|
const otherChannel = findContactChannel({
|
|
284
284
|
channelType: "slack",
|
|
285
|
-
|
|
285
|
+
address: "user-cross-test",
|
|
286
286
|
});
|
|
287
287
|
expect(otherChannel).toBeNull();
|
|
288
288
|
});
|
|
@@ -306,7 +306,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
306
306
|
// Verify the member is indeed revoked (ACL would reject)
|
|
307
307
|
const revokedResult = findContactChannel({
|
|
308
308
|
channelType: "telegram",
|
|
309
|
-
|
|
309
|
+
address: "user-revoked",
|
|
310
310
|
});
|
|
311
311
|
expect(revokedResult).not.toBeNull();
|
|
312
312
|
expect(revokedResult!.channel.status).toBe("revoked");
|
|
@@ -345,7 +345,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
345
345
|
// Verify: member is now active again
|
|
346
346
|
const reactivatedResult = findContactChannel({
|
|
347
347
|
channelType: "telegram",
|
|
348
|
-
|
|
348
|
+
address: "user-revoked",
|
|
349
349
|
});
|
|
350
350
|
expect(reactivatedResult).not.toBeNull();
|
|
351
351
|
expect(reactivatedResult!.channel.status).toBe("active");
|
|
@@ -390,9 +390,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
390
390
|
// The original guardian binding should remain intact
|
|
391
391
|
const guardianResult = findGuardianForChannel("telegram");
|
|
392
392
|
expect(guardianResult).not.toBeNull();
|
|
393
|
-
expect(guardianResult!.channel.
|
|
394
|
-
"guardian-user-original",
|
|
395
|
-
);
|
|
393
|
+
expect(guardianResult!.channel.address).toBe("guardian-user-original");
|
|
396
394
|
});
|
|
397
395
|
|
|
398
396
|
test("guardian inbound verification succeeds but does not create binding", async () => {
|
|
@@ -92,8 +92,21 @@ let mockRouteSetupResult: {
|
|
|
92
92
|
},
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
// Captures the last context passed to routeSetup so preflight tests can
|
|
96
|
+
// assert the resolved phone admission policy was threaded through.
|
|
97
|
+
let lastRouteSetupCtx: Record<string, unknown> | null = null;
|
|
95
98
|
mock.module("../calls/relay-setup-router.js", () => ({
|
|
96
|
-
routeSetup: () =>
|
|
99
|
+
routeSetup: (ctx: Record<string, unknown>) => {
|
|
100
|
+
lastRouteSetupCtx = ctx;
|
|
101
|
+
return mockRouteSetupResult;
|
|
102
|
+
},
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
// Mock the phone channel admission reader used by the media-stream preflight.
|
|
106
|
+
// Tests override mockAdmissionPolicy to exercise floor classification.
|
|
107
|
+
let mockAdmissionPolicy: unknown = null;
|
|
108
|
+
mock.module("../calls/channel-admission-reader.js", () => ({
|
|
109
|
+
getChannelAdmissionPolicy: async () => mockAdmissionPolicy,
|
|
97
110
|
}));
|
|
98
111
|
|
|
99
112
|
mock.module("../config/env.js", () => ({
|
|
@@ -454,6 +467,9 @@ describe("twilio webhook routes", () => {
|
|
|
454
467
|
mockTwilioApiValidationBody = JSON.stringify({ sid: "AC_validated" });
|
|
455
468
|
// Reset STT config to defaults between tests
|
|
456
469
|
mockConfigObj.services.stt.provider = "deepgram" as any;
|
|
470
|
+
// Reset admission policy + captured routeSetup context between tests
|
|
471
|
+
mockAdmissionPolicy = null;
|
|
472
|
+
lastRouteSetupCtx = null;
|
|
457
473
|
// Reset routeSetup mock to default normal_call
|
|
458
474
|
mockRouteSetupResult = {
|
|
459
475
|
outcome: { action: "normal_call", isInbound: true },
|
|
@@ -1228,6 +1244,70 @@ describe("twilio webhook routes", () => {
|
|
|
1228
1244
|
expect(twiml).not.toContain("<ConversationRelay");
|
|
1229
1245
|
});
|
|
1230
1246
|
|
|
1247
|
+
test("media-stream: resolves the phone admission floor and threads it into the preflight routeSetup", async () => {
|
|
1248
|
+
mockConfigObj.services.stt.provider = "openai-whisper" as any;
|
|
1249
|
+
mockAdmissionPolicy = "guardian_only";
|
|
1250
|
+
mockRouteSetupResult = {
|
|
1251
|
+
outcome: { action: "normal_call", isInbound: true },
|
|
1252
|
+
resolved: {
|
|
1253
|
+
assistantId: "self",
|
|
1254
|
+
isInbound: true,
|
|
1255
|
+
otherPartyNumber: "+15559998888",
|
|
1256
|
+
actorTrust: { trustClass: "guardian", memberRecord: null },
|
|
1257
|
+
},
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
const session = createTestSession("conv-ms-floor-1", "CA_ms_floor_1");
|
|
1261
|
+
const req = makeVoiceRequest(session.id, { CallSid: "CA_ms_floor_1" });
|
|
1262
|
+
|
|
1263
|
+
const res = await handleVoiceWebhook(req);
|
|
1264
|
+
expect(res.status).toBe(200);
|
|
1265
|
+
|
|
1266
|
+
// The resolved floor was passed into the preflight routeSetup so its
|
|
1267
|
+
// classification matches the real stream-start enforcement.
|
|
1268
|
+
expect(lastRouteSetupCtx).not.toBeNull();
|
|
1269
|
+
expect(lastRouteSetupCtx?.admissionPolicy).toBe("guardian_only");
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
test("media-stream: floor-denied caller classified deny still produces Stream TwiML (deny handled at stream level)", async () => {
|
|
1273
|
+
mockConfigObj.services.stt.provider = "openai-whisper" as any;
|
|
1274
|
+
mockAdmissionPolicy = "guardian_only";
|
|
1275
|
+
// With the floor wired, the real router returns `deny` for a
|
|
1276
|
+
// below-floor trusted_contact caller; the mock reflects that.
|
|
1277
|
+
mockRouteSetupResult = {
|
|
1278
|
+
outcome: {
|
|
1279
|
+
action: "deny",
|
|
1280
|
+
message:
|
|
1281
|
+
"This number is not authorized to reach the assistant right now.",
|
|
1282
|
+
logReason: "Inbound voice admission floor: guardian_only",
|
|
1283
|
+
},
|
|
1284
|
+
resolved: {
|
|
1285
|
+
assistantId: "self",
|
|
1286
|
+
isInbound: true,
|
|
1287
|
+
otherPartyNumber: "+14155550000",
|
|
1288
|
+
actorTrust: { trustClass: "trusted_contact", memberRecord: null },
|
|
1289
|
+
},
|
|
1290
|
+
};
|
|
1291
|
+
|
|
1292
|
+
const session = createTestSession(
|
|
1293
|
+
"conv-ms-floor-deny-1",
|
|
1294
|
+
"CA_ms_floor_deny_1",
|
|
1295
|
+
);
|
|
1296
|
+
const req = makeVoiceRequest(session.id, {
|
|
1297
|
+
CallSid: "CA_ms_floor_deny_1",
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
const res = await handleVoiceWebhook(req);
|
|
1301
|
+
expect(res.status).toBe(200);
|
|
1302
|
+
|
|
1303
|
+
// Policy was threaded; deny is supported on media-stream, so Stream
|
|
1304
|
+
// TwiML is still produced (denial spoken + torn down at stream start).
|
|
1305
|
+
expect(lastRouteSetupCtx?.admissionPolicy).toBe("guardian_only");
|
|
1306
|
+
const twiml = await res.text();
|
|
1307
|
+
expect(twiml).toContain("<Stream");
|
|
1308
|
+
expect(twiml).not.toContain("<ConversationRelay");
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1231
1311
|
test("media-stream: deny setup proceeds with Stream TwiML (deny is handled at stream level)", async () => {
|
|
1232
1312
|
mockConfigObj.services.stt.provider = "openai-whisper" as any;
|
|
1233
1313
|
mockRouteSetupResult = {
|
|
@@ -180,7 +180,7 @@ describe("redeemVoiceInviteCode", () => {
|
|
|
180
180
|
|
|
181
181
|
const channelResult = findContactChannel({
|
|
182
182
|
channelType: "phone",
|
|
183
|
-
|
|
183
|
+
address: phone,
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
expect(channelResult).not.toBeNull();
|
|
@@ -368,7 +368,6 @@ describe("redeemVoiceInviteCode", () => {
|
|
|
368
368
|
{
|
|
369
369
|
type: "phone",
|
|
370
370
|
address: phone,
|
|
371
|
-
externalUserId: phone,
|
|
372
371
|
status: "revoked",
|
|
373
372
|
},
|
|
374
373
|
],
|
|
@@ -399,7 +398,7 @@ describe("redeemVoiceInviteCode", () => {
|
|
|
399
398
|
// Verify the redeemer's phone is now bound to Mom's contact
|
|
400
399
|
const contactResult = findContactChannel({
|
|
401
400
|
channelType: "phone",
|
|
402
|
-
|
|
401
|
+
address: phone,
|
|
403
402
|
});
|
|
404
403
|
expect(contactResult).not.toBeNull();
|
|
405
404
|
expect(contactResult!.contact.id).toBe(momContact.id);
|