@vellumai/assistant 0.9.1-staging.1 → 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/docs/activation-funnel-telemetry.md +24 -18
- 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 +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- 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 +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- 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__/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 +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- 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-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-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- 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 +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- 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__/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 +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- 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__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -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__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- 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 +150 -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__/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__/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-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -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 +33 -33
- 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 +26 -0
- package/src/api/responses/home.ts +26 -0
- 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 +181 -78
- 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-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- 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 +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- 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/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -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/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/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- 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-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -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 +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.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/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/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 +34 -0
- 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/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 +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- 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 +10 -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 +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- 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/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/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- 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 +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- 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/index.ts +27 -0
- 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 +35 -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 +75 -7
- 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 +37 -4
- 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/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- 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/__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 +49 -21
- 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/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- 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 +6 -22
- 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 +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- 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-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- 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/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- 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 +40 -7
- 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 +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- 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 +11 -4
- 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/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- 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.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- 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 -3
- package/src/workflows/run-manager.ts +64 -0
- 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/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -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
|
+
});
|
|
@@ -108,7 +108,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
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");
|
|
@@ -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 = {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { isWeakOpenModel } from "../providers/weak-open-model.js";
|
|
4
|
+
|
|
5
|
+
describe("isWeakOpenModel", () => {
|
|
6
|
+
test("matches weak open models across provider naming conventions", () => {
|
|
7
|
+
for (const model of [
|
|
8
|
+
"accounts/fireworks/models/minimax-m3",
|
|
9
|
+
"minimax/minimax-m3",
|
|
10
|
+
"moonshotai/kimi-k2.6",
|
|
11
|
+
"accounts/fireworks/models/kimi-k2p6",
|
|
12
|
+
"deepseek/deepseek-chat",
|
|
13
|
+
"accounts/fireworks/models/glm-5p2",
|
|
14
|
+
]) {
|
|
15
|
+
expect(isWeakOpenModel(model)).toBe(true);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("does not match capable models", () => {
|
|
20
|
+
for (const model of ["claude-opus-4-8", "claude-sonnet-4-6", "gpt-5.5"]) {
|
|
21
|
+
expect(isWeakOpenModel(model)).toBe(false);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("is false for null/undefined/empty", () => {
|
|
26
|
+
expect(isWeakOpenModel(null)).toBe(false);
|
|
27
|
+
expect(isWeakOpenModel(undefined)).toBe(false);
|
|
28
|
+
expect(isWeakOpenModel("")).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for workspace migration `105-enable-memory-v3-live-for-new-workspaces`.
|
|
3
|
+
*
|
|
4
|
+
* The schema default for `memory.v3.live` is false so existing assistants stay
|
|
5
|
+
* on v2. The migration persists `memory.v3.live = true` for FRESH workspaces
|
|
6
|
+
* only (switching new assistants onto memory-v3 at creation), creating
|
|
7
|
+
* `config.json` when it does not exist, leaving existing workspaces and
|
|
8
|
+
* explicit values untouched, and being idempotent.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
existsSync,
|
|
13
|
+
mkdtempSync,
|
|
14
|
+
readFileSync,
|
|
15
|
+
rmSync,
|
|
16
|
+
writeFileSync,
|
|
17
|
+
} from "node:fs";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
21
|
+
|
|
22
|
+
import { MemoryV3ConfigSchema } from "../config/schemas/memory-v3.js";
|
|
23
|
+
import { enableMemoryV3LiveForNewWorkspacesMigration } from "../workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.js";
|
|
24
|
+
import type { MigrationRunContext } from "../workspace/migrations/types.js";
|
|
25
|
+
|
|
26
|
+
const NEW_WORKSPACE_CTX: MigrationRunContext = { isNewWorkspace: true };
|
|
27
|
+
const UPGRADE_CTX: MigrationRunContext = { isNewWorkspace: false };
|
|
28
|
+
|
|
29
|
+
let workspaceDir: string;
|
|
30
|
+
let configPath: string;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
workspaceDir = mkdtempSync(join(tmpdir(), "vellum-migration-105-test-"));
|
|
34
|
+
configPath = join(workspaceDir, "config.json");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
if (existsSync(workspaceDir)) {
|
|
39
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function readConfig(): Record<string, unknown> {
|
|
44
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("105-enable-memory-v3-live-for-new-workspaces migration", () => {
|
|
48
|
+
test("has correct id and description", () => {
|
|
49
|
+
expect(enableMemoryV3LiveForNewWorkspacesMigration.id).toBe(
|
|
50
|
+
"105-enable-memory-v3-live-for-new-workspaces",
|
|
51
|
+
);
|
|
52
|
+
expect(enableMemoryV3LiveForNewWorkspacesMigration.description).toContain(
|
|
53
|
+
"memory.v3.live",
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("schema default is off so existing assistants stay on v2", () => {
|
|
58
|
+
expect(MemoryV3ConfigSchema.parse({}).live).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("creates config.json with memory.v3.live=true for a fresh workspace", () => {
|
|
62
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(
|
|
63
|
+
workspaceDir,
|
|
64
|
+
NEW_WORKSPACE_CTX,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(readConfig()).toEqual({ memory: { v3: { live: true } } });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("adds memory.v3.live to a fresh workspace's existing config, preserving siblings", () => {
|
|
71
|
+
writeFileSync(
|
|
72
|
+
configPath,
|
|
73
|
+
JSON.stringify({ name: "Assistant", memory: { enabled: true } }),
|
|
74
|
+
"utf-8",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(
|
|
78
|
+
workspaceDir,
|
|
79
|
+
NEW_WORKSPACE_CTX,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(readConfig()).toEqual({
|
|
83
|
+
name: "Assistant",
|
|
84
|
+
memory: { enabled: true, v3: { live: true } },
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("skips existing workspaces so they stay on v2 (no config written)", () => {
|
|
89
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(workspaceDir, UPGRADE_CTX);
|
|
90
|
+
|
|
91
|
+
expect(existsSync(configPath)).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("treats a missing context as an existing workspace", () => {
|
|
95
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(workspaceDir);
|
|
96
|
+
|
|
97
|
+
expect(existsSync(configPath)).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("leaves an explicit memory.v3.live value untouched on a fresh workspace", () => {
|
|
101
|
+
const original = JSON.stringify({ memory: { v3: { live: false } } });
|
|
102
|
+
writeFileSync(configPath, original, "utf-8");
|
|
103
|
+
|
|
104
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(
|
|
105
|
+
workspaceDir,
|
|
106
|
+
NEW_WORKSPACE_CTX,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(readFileSync(configPath, "utf-8")).toBe(original);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("leaves malformed config.json untouched", () => {
|
|
113
|
+
writeFileSync(configPath, "{ not json", "utf-8");
|
|
114
|
+
|
|
115
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(
|
|
116
|
+
workspaceDir,
|
|
117
|
+
NEW_WORKSPACE_CTX,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
expect(readFileSync(configPath, "utf-8")).toBe("{ not json");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("is idempotent", () => {
|
|
124
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(
|
|
125
|
+
workspaceDir,
|
|
126
|
+
NEW_WORKSPACE_CTX,
|
|
127
|
+
);
|
|
128
|
+
const afterFirst = readFileSync(configPath, "utf-8");
|
|
129
|
+
|
|
130
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(
|
|
131
|
+
workspaceDir,
|
|
132
|
+
NEW_WORKSPACE_CTX,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(readFileSync(configPath, "utf-8")).toBe(afterFirst);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("down is a no-op", () => {
|
|
139
|
+
enableMemoryV3LiveForNewWorkspacesMigration.run(
|
|
140
|
+
workspaceDir,
|
|
141
|
+
NEW_WORKSPACE_CTX,
|
|
142
|
+
);
|
|
143
|
+
const before = readFileSync(configPath, "utf-8");
|
|
144
|
+
|
|
145
|
+
enableMemoryV3LiveForNewWorkspacesMigration.down(workspaceDir);
|
|
146
|
+
|
|
147
|
+
expect(readFileSync(configPath, "utf-8")).toBe(before);
|
|
148
|
+
});
|
|
149
|
+
});
|