@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
|
@@ -159,6 +159,23 @@ mock.module("../calls/relay-setup-router.js", () => ({
|
|
|
159
159
|
routeSetup: jest.fn(() => mockRouteSetupResult),
|
|
160
160
|
}));
|
|
161
161
|
|
|
162
|
+
// Mock the channel admission reader. handleStart awaits this before routing;
|
|
163
|
+
// tests override mockAdmissionPolicy to exercise floor enforcement. Returning
|
|
164
|
+
// a resolved promise introduces a microtask hop, so tests await
|
|
165
|
+
// session.whenSetupSettled() after sending the start frame.
|
|
166
|
+
let mockAdmissionPolicy: unknown = null;
|
|
167
|
+
// Optional gate: when set, getChannelAdmissionPolicy awaits this promise before
|
|
168
|
+
// resolving, letting a test dispose the session mid-read (simulating a WS close
|
|
169
|
+
// while the admission IPC read is pending).
|
|
170
|
+
let mockAdmissionGate: Promise<void> | null = null;
|
|
171
|
+
const mockGetChannelAdmissionPolicy = jest.fn(async () => {
|
|
172
|
+
if (mockAdmissionGate) await mockAdmissionGate;
|
|
173
|
+
return mockAdmissionPolicy;
|
|
174
|
+
});
|
|
175
|
+
mock.module("../calls/channel-admission-reader.js", () => ({
|
|
176
|
+
getChannelAdmissionPolicy: mockGetChannelAdmissionPolicy,
|
|
177
|
+
}));
|
|
178
|
+
|
|
162
179
|
// Mock the actor trust resolver (used by handleStart to derive trust context)
|
|
163
180
|
mock.module("../runtime/actor-trust-resolver.js", () => ({
|
|
164
181
|
toTrustContext: jest.fn(() => ({
|
|
@@ -204,6 +221,7 @@ import {
|
|
|
204
221
|
activeMediaStreamSessions,
|
|
205
222
|
MediaStreamCallSession,
|
|
206
223
|
} from "../calls/media-stream-server.js";
|
|
224
|
+
import { routeSetup } from "../calls/relay-setup-router.js";
|
|
207
225
|
|
|
208
226
|
// ---------------------------------------------------------------------------
|
|
209
227
|
// Mock WebSocket factory
|
|
@@ -304,6 +322,15 @@ function makeMarkMessage(name: string): string {
|
|
|
304
322
|
});
|
|
305
323
|
}
|
|
306
324
|
|
|
325
|
+
function makeDtmfMessage(digit: string): string {
|
|
326
|
+
return JSON.stringify({
|
|
327
|
+
event: "dtmf",
|
|
328
|
+
sequenceNumber: "60",
|
|
329
|
+
streamSid: "MZ00000000000000000000000000000000",
|
|
330
|
+
dtmf: { digit },
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
307
334
|
// ---------------------------------------------------------------------------
|
|
308
335
|
// Setup / teardown
|
|
309
336
|
// ---------------------------------------------------------------------------
|
|
@@ -325,6 +352,10 @@ beforeEach(() => {
|
|
|
325
352
|
(updateCallSession as jest.Mock).mockClear();
|
|
326
353
|
(finalizeCall as jest.Mock).mockClear();
|
|
327
354
|
(speakSystemPrompt as jest.Mock).mockClear();
|
|
355
|
+
(routeSetup as jest.Mock).mockClear();
|
|
356
|
+
mockGetChannelAdmissionPolicy.mockClear();
|
|
357
|
+
mockAdmissionPolicy = null;
|
|
358
|
+
mockAdmissionGate = null;
|
|
328
359
|
// Reset routeSetup to default normal_call
|
|
329
360
|
mockRouteSetupResult = {
|
|
330
361
|
outcome: { action: "normal_call" as const, isInbound: true },
|
|
@@ -359,7 +390,7 @@ describe("MediaStreamCallSession", () => {
|
|
|
359
390
|
});
|
|
360
391
|
|
|
361
392
|
describe("start event handling", () => {
|
|
362
|
-
test("start event registers a controller and records call_connected", () => {
|
|
393
|
+
test("start event registers a controller and records call_connected", async () => {
|
|
363
394
|
const mock = createMockWs();
|
|
364
395
|
// Set up a call session in the mock store
|
|
365
396
|
mockSessions.set("call-1", {
|
|
@@ -373,6 +404,7 @@ describe("MediaStreamCallSession", () => {
|
|
|
373
404
|
|
|
374
405
|
const session = new MediaStreamCallSession(mock.ws, "call-1");
|
|
375
406
|
session.handleMessage(makeStartMessage());
|
|
407
|
+
await session.whenSetupSettled();
|
|
376
408
|
|
|
377
409
|
// Controller should have been registered
|
|
378
410
|
expect(registerCallController).toHaveBeenCalledWith(
|
|
@@ -484,7 +516,7 @@ describe("MediaStreamCallSession", () => {
|
|
|
484
516
|
});
|
|
485
517
|
|
|
486
518
|
describe("destroy", () => {
|
|
487
|
-
test("destroys the controller and marks output as closed", () => {
|
|
519
|
+
test("destroys the controller and marks output as closed", async () => {
|
|
488
520
|
const mock = createMockWs();
|
|
489
521
|
mockSessions.set("call-1", {
|
|
490
522
|
id: "call-1",
|
|
@@ -498,6 +530,7 @@ describe("MediaStreamCallSession", () => {
|
|
|
498
530
|
const session = new MediaStreamCallSession(mock.ws, "call-1");
|
|
499
531
|
// Trigger start to create a controller
|
|
500
532
|
session.handleMessage(makeStartMessage());
|
|
533
|
+
await session.whenSetupSettled();
|
|
501
534
|
|
|
502
535
|
session.destroy();
|
|
503
536
|
expect(mockDestroy).toHaveBeenCalled();
|
|
@@ -715,7 +748,7 @@ describe("media-stream output egress", () => {
|
|
|
715
748
|
expect(clearMessages.length).toBeGreaterThanOrEqual(1);
|
|
716
749
|
});
|
|
717
750
|
|
|
718
|
-
test("barge-in via speech start clears audio and interrupts controller", () => {
|
|
751
|
+
test("barge-in via speech start clears audio and interrupts controller", async () => {
|
|
719
752
|
const mockWs = createMockWs();
|
|
720
753
|
mockSessions.set("call-interrupt-1", {
|
|
721
754
|
id: "call-interrupt-1",
|
|
@@ -728,6 +761,7 @@ describe("media-stream output egress", () => {
|
|
|
728
761
|
|
|
729
762
|
const session = new MediaStreamCallSession(mockWs.ws, "call-interrupt-1");
|
|
730
763
|
session.handleMessage(makeStartMessage());
|
|
764
|
+
await session.whenSetupSettled();
|
|
731
765
|
|
|
732
766
|
// Verify the controller is created
|
|
733
767
|
expect(session.getController()).not.toBeNull();
|
|
@@ -770,7 +804,7 @@ describe("activeMediaStreamSessions registry", () => {
|
|
|
770
804
|
|
|
771
805
|
describe("media-stream setup outcome scenarios", () => {
|
|
772
806
|
describe("deny outcome", () => {
|
|
773
|
-
test("deny outcome records inbound_acl_denied event and sets status to failed", () => {
|
|
807
|
+
test("deny outcome records inbound_acl_denied event and sets status to failed", async () => {
|
|
774
808
|
mockRouteSetupResult = {
|
|
775
809
|
outcome: {
|
|
776
810
|
action: "deny",
|
|
@@ -798,6 +832,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
798
832
|
|
|
799
833
|
const session = new MediaStreamCallSession(mockWs.ws, "call-deny-1");
|
|
800
834
|
session.handleMessage(makeStartMessage());
|
|
835
|
+
await session.whenSetupSettled();
|
|
801
836
|
|
|
802
837
|
// Should record an inbound_acl_denied event
|
|
803
838
|
expect(recordCallEvent).toHaveBeenCalledWith(
|
|
@@ -821,7 +856,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
821
856
|
expect(registerCallController).not.toHaveBeenCalled();
|
|
822
857
|
});
|
|
823
858
|
|
|
824
|
-
test("deny outcome speaks the denial message", () => {
|
|
859
|
+
test("deny outcome speaks the denial message", async () => {
|
|
825
860
|
mockRouteSetupResult = {
|
|
826
861
|
outcome: {
|
|
827
862
|
action: "deny",
|
|
@@ -852,6 +887,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
852
887
|
"call-deny-speak-1",
|
|
853
888
|
);
|
|
854
889
|
session.handleMessage(makeStartMessage());
|
|
890
|
+
await session.whenSetupSettled();
|
|
855
891
|
|
|
856
892
|
// speakSystemPrompt should be called with the denial message
|
|
857
893
|
expect(speakSystemPrompt).toHaveBeenCalledWith(
|
|
@@ -860,7 +896,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
860
896
|
);
|
|
861
897
|
});
|
|
862
898
|
|
|
863
|
-
test("deny outcome runs finalization", () => {
|
|
899
|
+
test("deny outcome runs finalization", async () => {
|
|
864
900
|
mockRouteSetupResult = {
|
|
865
901
|
outcome: {
|
|
866
902
|
action: "deny",
|
|
@@ -891,6 +927,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
891
927
|
"call-deny-finalize-1",
|
|
892
928
|
);
|
|
893
929
|
session.handleMessage(makeStartMessage());
|
|
930
|
+
await session.whenSetupSettled();
|
|
894
931
|
|
|
895
932
|
// finalizeCall should be called because early teardown runs it inline
|
|
896
933
|
expect(finalizeCall).toHaveBeenCalledWith(
|
|
@@ -901,7 +938,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
901
938
|
});
|
|
902
939
|
|
|
903
940
|
describe("unsupported interactive setup flow", () => {
|
|
904
|
-
test("verification outcome records call_failed with preflight-bypass reason", () => {
|
|
941
|
+
test("verification outcome records call_failed with preflight-bypass reason", async () => {
|
|
905
942
|
mockRouteSetupResult = {
|
|
906
943
|
outcome: {
|
|
907
944
|
action: "verification",
|
|
@@ -932,6 +969,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
932
969
|
"call-unsup-verify-1",
|
|
933
970
|
);
|
|
934
971
|
session.handleMessage(makeStartMessage());
|
|
972
|
+
await session.whenSetupSettled();
|
|
935
973
|
|
|
936
974
|
// Should record call_failed event with preflight-bypass note
|
|
937
975
|
expect(recordCallEvent).toHaveBeenCalledWith(
|
|
@@ -956,7 +994,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
956
994
|
expect(registerCallController).not.toHaveBeenCalled();
|
|
957
995
|
});
|
|
958
996
|
|
|
959
|
-
test("name_capture outcome speaks generic apology and tears down", () => {
|
|
997
|
+
test("name_capture outcome speaks generic apology and tears down", async () => {
|
|
960
998
|
mockRouteSetupResult = {
|
|
961
999
|
outcome: {
|
|
962
1000
|
action: "name_capture",
|
|
@@ -987,6 +1025,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
987
1025
|
"call-unsup-name-1",
|
|
988
1026
|
);
|
|
989
1027
|
session.handleMessage(makeStartMessage());
|
|
1028
|
+
await session.whenSetupSettled();
|
|
990
1029
|
|
|
991
1030
|
// speakSystemPrompt should be called with the generic apology
|
|
992
1031
|
expect(speakSystemPrompt).toHaveBeenCalledWith(
|
|
@@ -1001,7 +1040,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1001
1040
|
);
|
|
1002
1041
|
});
|
|
1003
1042
|
|
|
1004
|
-
test("callee_verification outcome fails with explicit reason", () => {
|
|
1043
|
+
test("callee_verification outcome fails with explicit reason", async () => {
|
|
1005
1044
|
mockRouteSetupResult = {
|
|
1006
1045
|
outcome: {
|
|
1007
1046
|
action: "callee_verification",
|
|
@@ -1031,6 +1070,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1031
1070
|
"call-unsup-callee-1",
|
|
1032
1071
|
);
|
|
1033
1072
|
session.handleMessage(makeStartMessage());
|
|
1073
|
+
await session.whenSetupSettled();
|
|
1034
1074
|
|
|
1035
1075
|
// Should record the failure with the specific action
|
|
1036
1076
|
expect(recordCallEvent).toHaveBeenCalledWith(
|
|
@@ -1048,7 +1088,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1048
1088
|
);
|
|
1049
1089
|
});
|
|
1050
1090
|
|
|
1051
|
-
test("normal_call after deny scenario still creates controller", () => {
|
|
1091
|
+
test("normal_call after deny scenario still creates controller", async () => {
|
|
1052
1092
|
// Verify that after a deny-scenario test, resetting to normal_call
|
|
1053
1093
|
// properly creates a controller (no cross-test pollution).
|
|
1054
1094
|
mockRouteSetupResult = {
|
|
@@ -1073,6 +1113,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1073
1113
|
|
|
1074
1114
|
const session = new MediaStreamCallSession(mockWs.ws, "call-reset-1");
|
|
1075
1115
|
session.handleMessage(makeStartMessage());
|
|
1116
|
+
await session.whenSetupSettled();
|
|
1076
1117
|
|
|
1077
1118
|
// Controller should be registered for normal calls
|
|
1078
1119
|
expect(registerCallController).toHaveBeenCalledWith(
|
|
@@ -1088,7 +1129,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1088
1129
|
// ── Barge-in regression ──────────────────────────────────────────
|
|
1089
1130
|
|
|
1090
1131
|
describe("barge-in gating", () => {
|
|
1091
|
-
test("immediate inbound audio after stream start does not trigger handleInterrupt", () => {
|
|
1132
|
+
test("immediate inbound audio after stream start does not trigger handleInterrupt", async () => {
|
|
1092
1133
|
const mockWs = createMockWs();
|
|
1093
1134
|
mockSessions.set("call-bargein-1", {
|
|
1094
1135
|
id: "call-bargein-1",
|
|
@@ -1103,6 +1144,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1103
1144
|
|
|
1104
1145
|
// Stream start bootstraps the controller
|
|
1105
1146
|
session.handleMessage(makeStartMessage());
|
|
1147
|
+
await session.whenSetupSettled();
|
|
1106
1148
|
expect(mockStartInitialGreeting).toHaveBeenCalled();
|
|
1107
1149
|
|
|
1108
1150
|
// Immediate inbound audio (speech-like payloads) — before the
|
|
@@ -1130,7 +1172,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1130
1172
|
session.destroy();
|
|
1131
1173
|
});
|
|
1132
1174
|
|
|
1133
|
-
test("barge-in is accepted when controller is speaking", () => {
|
|
1175
|
+
test("barge-in is accepted when controller is speaking", async () => {
|
|
1134
1176
|
// Configure mock to indicate the controller is speaking
|
|
1135
1177
|
mockHandleBargeIn.mockReturnValue(true);
|
|
1136
1178
|
|
|
@@ -1146,6 +1188,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1146
1188
|
|
|
1147
1189
|
const session = new MediaStreamCallSession(mockWs.ws, "call-bargein-2");
|
|
1148
1190
|
session.handleMessage(makeStartMessage());
|
|
1191
|
+
await session.whenSetupSettled();
|
|
1149
1192
|
|
|
1150
1193
|
// Simulate inbound speech audio while assistant is speaking.
|
|
1151
1194
|
// Use a high-amplitude mu-law payload so speech detection triggers.
|
|
@@ -1162,7 +1205,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1162
1205
|
// ── E2E regression scenario ──────────────────────────────────────
|
|
1163
1206
|
|
|
1164
1207
|
describe("end-to-end regression: connected call that stays active", () => {
|
|
1165
|
-
test("stream connects, inbound audio starts, call remains active for a turn, controller only destroyed at stop/hangup", () => {
|
|
1208
|
+
test("stream connects, inbound audio starts, call remains active for a turn, controller only destroyed at stop/hangup", async () => {
|
|
1166
1209
|
const mockWs = createMockWs();
|
|
1167
1210
|
mockSessions.set("call-e2e-1", {
|
|
1168
1211
|
id: "call-e2e-1",
|
|
@@ -1177,6 +1220,7 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1177
1220
|
|
|
1178
1221
|
// 1. Stream connects — start event arrives
|
|
1179
1222
|
session.handleMessage(makeStartMessage());
|
|
1223
|
+
await session.whenSetupSettled();
|
|
1180
1224
|
expect(registerCallController).toHaveBeenCalledWith(
|
|
1181
1225
|
"call-e2e-1",
|
|
1182
1226
|
expect.anything(),
|
|
@@ -1231,4 +1275,264 @@ describe("media-stream setup outcome scenarios", () => {
|
|
|
1231
1275
|
expect(mockDestroy).toHaveBeenCalled();
|
|
1232
1276
|
});
|
|
1233
1277
|
});
|
|
1278
|
+
|
|
1279
|
+
// ── Admission floor enforcement on the media-stream transport ────────
|
|
1280
|
+
// The phone channel is no longer exempt from per-channel admission, so the
|
|
1281
|
+
// trust floor must be enforced on this transport too — not just the
|
|
1282
|
+
// gateway's no_one kill switch. handleStart resolves the phone admission
|
|
1283
|
+
// policy and threads it into routeSetup; a floor-denied caller (e.g.
|
|
1284
|
+
// guardian_only vs a trusted_contact) produces a `deny` outcome here, which
|
|
1285
|
+
// speaks a denial + tears down and never starts a normal call.
|
|
1286
|
+
|
|
1287
|
+
describe("admission floor enforcement", () => {
|
|
1288
|
+
test("resolves the phone admission policy and threads it into routeSetup", async () => {
|
|
1289
|
+
mockAdmissionPolicy = "guardian_only";
|
|
1290
|
+
|
|
1291
|
+
const mockWs = createMockWs();
|
|
1292
|
+
mockSessions.set("call-floor-thread-1", {
|
|
1293
|
+
id: "call-floor-thread-1",
|
|
1294
|
+
conversationId: "conv-floor-thread-1",
|
|
1295
|
+
status: "initiated",
|
|
1296
|
+
task: null,
|
|
1297
|
+
startedAt: null,
|
|
1298
|
+
fromNumber: "+14155550000",
|
|
1299
|
+
toNumber: "+15550001111",
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
const session = new MediaStreamCallSession(
|
|
1303
|
+
mockWs.ws,
|
|
1304
|
+
"call-floor-thread-1",
|
|
1305
|
+
);
|
|
1306
|
+
session.handleMessage(makeStartMessage());
|
|
1307
|
+
await session.whenSetupSettled();
|
|
1308
|
+
|
|
1309
|
+
expect(mockGetChannelAdmissionPolicy).toHaveBeenCalledWith("phone");
|
|
1310
|
+
expect(routeSetup).toHaveBeenCalledWith(
|
|
1311
|
+
expect.objectContaining({
|
|
1312
|
+
callSessionId: "call-floor-thread-1",
|
|
1313
|
+
admissionPolicy: "guardian_only",
|
|
1314
|
+
}),
|
|
1315
|
+
);
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
test("guardian_only floor denies a trusted-contact caller — speaks denial, tears down, no controller", async () => {
|
|
1319
|
+
mockAdmissionPolicy = "guardian_only";
|
|
1320
|
+
// With the floor wired, the real router would return `deny` for a
|
|
1321
|
+
// below-floor (trusted_contact) caller; the mock reflects that outcome.
|
|
1322
|
+
mockRouteSetupResult = {
|
|
1323
|
+
outcome: {
|
|
1324
|
+
action: "deny",
|
|
1325
|
+
message:
|
|
1326
|
+
"This number is not authorized to reach the assistant right now.",
|
|
1327
|
+
logReason: "Inbound voice admission floor: guardian_only",
|
|
1328
|
+
},
|
|
1329
|
+
resolved: {
|
|
1330
|
+
assistantId: "self",
|
|
1331
|
+
isInbound: true,
|
|
1332
|
+
otherPartyNumber: "+14155550000",
|
|
1333
|
+
actorTrust: { trustClass: "trusted_contact", memberRecord: null },
|
|
1334
|
+
},
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
const mockWs = createMockWs();
|
|
1338
|
+
mockSessions.set("call-floor-deny-1", {
|
|
1339
|
+
id: "call-floor-deny-1",
|
|
1340
|
+
conversationId: "conv-floor-deny-1",
|
|
1341
|
+
status: "initiated",
|
|
1342
|
+
task: null,
|
|
1343
|
+
startedAt: null,
|
|
1344
|
+
fromNumber: "+14155550000",
|
|
1345
|
+
toNumber: "+15550001111",
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
const session = new MediaStreamCallSession(
|
|
1349
|
+
mockWs.ws,
|
|
1350
|
+
"call-floor-deny-1",
|
|
1351
|
+
);
|
|
1352
|
+
session.handleMessage(makeStartMessage());
|
|
1353
|
+
await session.whenSetupSettled();
|
|
1354
|
+
|
|
1355
|
+
// Policy was passed into routeSetup.
|
|
1356
|
+
expect(routeSetup).toHaveBeenCalledWith(
|
|
1357
|
+
expect.objectContaining({ admissionPolicy: "guardian_only" }),
|
|
1358
|
+
);
|
|
1359
|
+
|
|
1360
|
+
// Denial spoken, session failed, no controller, finalization ran.
|
|
1361
|
+
expect(speakSystemPrompt).toHaveBeenCalledWith(
|
|
1362
|
+
expect.anything(),
|
|
1363
|
+
"This number is not authorized to reach the assistant right now.",
|
|
1364
|
+
);
|
|
1365
|
+
expect(updateCallSession).toHaveBeenCalledWith(
|
|
1366
|
+
"call-floor-deny-1",
|
|
1367
|
+
expect.objectContaining({
|
|
1368
|
+
status: "failed",
|
|
1369
|
+
lastError: "Inbound voice admission floor: guardian_only",
|
|
1370
|
+
}),
|
|
1371
|
+
);
|
|
1372
|
+
expect(registerCallController).not.toHaveBeenCalled();
|
|
1373
|
+
expect(mockStartInitialGreeting).not.toHaveBeenCalled();
|
|
1374
|
+
expect(finalizeCall).toHaveBeenCalledWith(
|
|
1375
|
+
"call-floor-deny-1",
|
|
1376
|
+
"conv-floor-deny-1",
|
|
1377
|
+
);
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
test("null policy (no enforcement) leaves behavior unchanged — normal call proceeds", async () => {
|
|
1381
|
+
mockAdmissionPolicy = null;
|
|
1382
|
+
// routeSetup default is normal_call.
|
|
1383
|
+
|
|
1384
|
+
const mockWs = createMockWs();
|
|
1385
|
+
mockSessions.set("call-floor-null-1", {
|
|
1386
|
+
id: "call-floor-null-1",
|
|
1387
|
+
conversationId: "conv-floor-null-1",
|
|
1388
|
+
status: "initiated",
|
|
1389
|
+
task: null,
|
|
1390
|
+
startedAt: null,
|
|
1391
|
+
fromNumber: "+14155550000",
|
|
1392
|
+
toNumber: "+15550001111",
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
const session = new MediaStreamCallSession(
|
|
1396
|
+
mockWs.ws,
|
|
1397
|
+
"call-floor-null-1",
|
|
1398
|
+
);
|
|
1399
|
+
session.handleMessage(makeStartMessage());
|
|
1400
|
+
await session.whenSetupSettled();
|
|
1401
|
+
|
|
1402
|
+
// Null policy is still threaded through (router skips the floor).
|
|
1403
|
+
expect(routeSetup).toHaveBeenCalledWith(
|
|
1404
|
+
expect.objectContaining({ admissionPolicy: null }),
|
|
1405
|
+
);
|
|
1406
|
+
|
|
1407
|
+
// Normal call proceeds: controller registered + greeting fired.
|
|
1408
|
+
expect(registerCallController).toHaveBeenCalledWith(
|
|
1409
|
+
"call-floor-null-1",
|
|
1410
|
+
expect.anything(),
|
|
1411
|
+
);
|
|
1412
|
+
expect(mockStartInitialGreeting).toHaveBeenCalled();
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
test("a floor-denied caller's transcript is dropped during setup routing", async () => {
|
|
1416
|
+
mockAdmissionPolicy = "guardian_only";
|
|
1417
|
+
mockRouteSetupResult = {
|
|
1418
|
+
outcome: {
|
|
1419
|
+
action: "deny",
|
|
1420
|
+
message:
|
|
1421
|
+
"This number is not authorized to reach the assistant right now.",
|
|
1422
|
+
logReason: "Inbound voice admission floor: guardian_only",
|
|
1423
|
+
},
|
|
1424
|
+
resolved: {
|
|
1425
|
+
assistantId: "self",
|
|
1426
|
+
isInbound: true,
|
|
1427
|
+
otherPartyNumber: "+14155550000",
|
|
1428
|
+
actorTrust: { trustClass: "trusted_contact", memberRecord: null },
|
|
1429
|
+
},
|
|
1430
|
+
};
|
|
1431
|
+
|
|
1432
|
+
const mockWs = createMockWs();
|
|
1433
|
+
mockSessions.set("call-floor-drop-1", {
|
|
1434
|
+
id: "call-floor-drop-1",
|
|
1435
|
+
conversationId: "conv-floor-drop-1",
|
|
1436
|
+
status: "initiated",
|
|
1437
|
+
task: null,
|
|
1438
|
+
startedAt: null,
|
|
1439
|
+
fromNumber: "+14155550000",
|
|
1440
|
+
toNumber: "+15550001111",
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
const session = new MediaStreamCallSession(
|
|
1444
|
+
mockWs.ws,
|
|
1445
|
+
"call-floor-drop-1",
|
|
1446
|
+
);
|
|
1447
|
+
session.handleMessage(makeStartMessage());
|
|
1448
|
+
await session.whenSetupSettled();
|
|
1449
|
+
|
|
1450
|
+
// No controller exists for a denied caller, so even if a transcript
|
|
1451
|
+
// arrived it could never reach handleCallerUtterance / be persisted.
|
|
1452
|
+
expect(registerCallController).not.toHaveBeenCalled();
|
|
1453
|
+
expect(mockHandleCallerUtterance).not.toHaveBeenCalled();
|
|
1454
|
+
// No caller_spoke event recorded for the denied caller.
|
|
1455
|
+
const callerSpoke = mockEvents.filter(
|
|
1456
|
+
(e) =>
|
|
1457
|
+
e.callSessionId === "call-floor-drop-1" &&
|
|
1458
|
+
e.eventType === "caller_spoke",
|
|
1459
|
+
);
|
|
1460
|
+
expect(callerSpoke.length).toBe(0);
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
test("a DTMF digit received during setup routing is dropped", async () => {
|
|
1464
|
+
// Gate the admission read so setupRouting is still true when the DTMF
|
|
1465
|
+
// frame arrives (simulating a digit during the admission IPC read).
|
|
1466
|
+
let releaseGate!: () => void;
|
|
1467
|
+
mockAdmissionGate = new Promise<void>((resolve) => {
|
|
1468
|
+
releaseGate = resolve;
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
const mockWs = createMockWs();
|
|
1472
|
+
mockSessions.set("call-dtmf-drop-1", {
|
|
1473
|
+
id: "call-dtmf-drop-1",
|
|
1474
|
+
conversationId: "conv-dtmf-drop-1",
|
|
1475
|
+
status: "initiated",
|
|
1476
|
+
task: null,
|
|
1477
|
+
startedAt: null,
|
|
1478
|
+
fromNumber: "+14155550000",
|
|
1479
|
+
toNumber: "+15550001111",
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
const session = new MediaStreamCallSession(mockWs.ws, "call-dtmf-drop-1");
|
|
1483
|
+
session.handleMessage(makeStartMessage());
|
|
1484
|
+
|
|
1485
|
+
// DTMF arrives while the admission read is still pending.
|
|
1486
|
+
session.handleMessage(makeDtmfMessage("5"));
|
|
1487
|
+
|
|
1488
|
+
releaseGate();
|
|
1489
|
+
await session.whenSetupSettled();
|
|
1490
|
+
|
|
1491
|
+
// The digit was dropped during setup: no caller_spoke event, no
|
|
1492
|
+
// controller interaction.
|
|
1493
|
+
const callerSpoke = mockEvents.filter(
|
|
1494
|
+
(e) =>
|
|
1495
|
+
e.callSessionId === "call-dtmf-drop-1" &&
|
|
1496
|
+
e.eventType === "caller_spoke",
|
|
1497
|
+
);
|
|
1498
|
+
expect(callerSpoke.length).toBe(0);
|
|
1499
|
+
expect(mockHandleCallerUtterance).not.toHaveBeenCalled();
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
test("session disposed during the admission read aborts setup — no controller, no greeting", async () => {
|
|
1503
|
+
// Gate the admission read so the session can be disposed (as the WS
|
|
1504
|
+
// close handler does) while handleStart is awaiting it.
|
|
1505
|
+
let releaseGate!: () => void;
|
|
1506
|
+
mockAdmissionGate = new Promise<void>((resolve) => {
|
|
1507
|
+
releaseGate = resolve;
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
const mockWs = createMockWs();
|
|
1511
|
+
mockSessions.set("call-disposed-1", {
|
|
1512
|
+
id: "call-disposed-1",
|
|
1513
|
+
conversationId: "conv-disposed-1",
|
|
1514
|
+
status: "initiated",
|
|
1515
|
+
task: null,
|
|
1516
|
+
startedAt: null,
|
|
1517
|
+
fromNumber: "+14155550000",
|
|
1518
|
+
toNumber: "+15550001111",
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
const session = new MediaStreamCallSession(mockWs.ws, "call-disposed-1");
|
|
1522
|
+
session.handleMessage(makeStartMessage());
|
|
1523
|
+
|
|
1524
|
+
// Twilio closes the WebSocket mid-read: the server disposes the session.
|
|
1525
|
+
session.destroy();
|
|
1526
|
+
|
|
1527
|
+
// Now let the admission read resolve and setup routing resume.
|
|
1528
|
+
releaseGate();
|
|
1529
|
+
await session.whenSetupSettled();
|
|
1530
|
+
|
|
1531
|
+
// Setup must have aborted: routeSetup never ran, no controller, no greeting.
|
|
1532
|
+
expect(routeSetup).not.toHaveBeenCalled();
|
|
1533
|
+
expect(registerCallController).not.toHaveBeenCalled();
|
|
1534
|
+
expect(mockStartInitialGreeting).not.toHaveBeenCalled();
|
|
1535
|
+
expect(speakSystemPrompt).not.toHaveBeenCalled();
|
|
1536
|
+
});
|
|
1537
|
+
});
|
|
1234
1538
|
});
|
|
@@ -3,17 +3,15 @@ import { describe, expect, test } from "bun:test";
|
|
|
3
3
|
import { PROVIDER_SEED_DATA } from "../oauth/seed-providers.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Allowed CDN prefixes for ``logoUrl
|
|
7
|
-
* ``
|
|
6
|
+
* Allowed CDN prefixes for the ``logoUrl`` field on ``PROVIDER_SEED_DATA``
|
|
7
|
+
* (``assistant/src/oauth/seed-providers.ts``):
|
|
8
8
|
*
|
|
9
9
|
* - Simple Icons (CC0) is the default for most providers.
|
|
10
10
|
* - thesvg via jsDelivr is the documented fallback for brands Simple Icons
|
|
11
11
|
* doesn't host (e.g. Salesforce, which Simple Icons removed for
|
|
12
|
-
* trademark reasons).
|
|
13
|
-
* of figma/github/gmail/linear/notion/outlook/slack.
|
|
12
|
+
* trademark reasons).
|
|
14
13
|
*
|
|
15
|
-
* Adding another CDN should be a deliberate choice — extend this list
|
|
16
|
-
* and update the manifest in tandem.
|
|
14
|
+
* Adding another CDN should be a deliberate choice — extend this list.
|
|
17
15
|
*/
|
|
18
16
|
const ALLOWED_LOGO_URL_PREFIXES = [
|
|
19
17
|
"https://cdn.simpleicons.org/",
|
|
@@ -59,7 +59,7 @@ mock.module("../util/platform.js", () => ({
|
|
|
59
59
|
}));
|
|
60
60
|
|
|
61
61
|
mock.module("../contacts/contact-store.js", () => ({
|
|
62
|
-
|
|
62
|
+
findContactByAddress: () => null,
|
|
63
63
|
findGuardianForChannel: (channelType: string) =>
|
|
64
64
|
channelType === "vellum" ? mockVellumGuardian : null,
|
|
65
65
|
listGuardianChannels: () => null,
|
|
@@ -12,6 +12,7 @@ import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
14
|
hostPolicy,
|
|
15
|
+
resolveRealPath,
|
|
15
16
|
sandboxPolicy,
|
|
16
17
|
} from "../tools/shared/filesystem/path-policy.js";
|
|
17
18
|
|
|
@@ -238,6 +239,39 @@ describe("hostPolicy", () => {
|
|
|
238
239
|
});
|
|
239
240
|
});
|
|
240
241
|
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
// resolveRealPath
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
describe("resolveRealPath", () => {
|
|
247
|
+
test("resolves an existing file's symlink to its real target", () => {
|
|
248
|
+
const dir = makeTempDir();
|
|
249
|
+
const target = join(dir, "real.txt");
|
|
250
|
+
writeFileSync(target, "x");
|
|
251
|
+
const link = join(dir, "link.txt");
|
|
252
|
+
symlinkSync(target, link);
|
|
253
|
+
|
|
254
|
+
expect(resolveRealPath(link)).toBe(target);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("follows a symlinked ancestor for a not-yet-existing path", () => {
|
|
258
|
+
const real = makeTempDir();
|
|
259
|
+
const linkParent = makeTempDir();
|
|
260
|
+
const link = join(linkParent, "link-dir");
|
|
261
|
+
symlinkSync(real, link);
|
|
262
|
+
|
|
263
|
+
// The leaf does not exist yet; the symlinked ancestor must still resolve.
|
|
264
|
+
expect(resolveRealPath(join(link, "new-file.txt"))).toBe(
|
|
265
|
+
join(real, "new-file.txt"),
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("falls back to the lexical path when nothing on it exists", () => {
|
|
270
|
+
const phantom = join(tmpdir(), "definitely-not-here-12345", "nope.txt");
|
|
271
|
+
expect(resolveRealPath(phantom)).toBe(phantom);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
241
275
|
// ---------------------------------------------------------------------------
|
|
242
276
|
// Baseline: Skill directory paths have no special treatment (PR 1)
|
|
243
277
|
// ---------------------------------------------------------------------------
|