@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
|
@@ -828,7 +828,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
828
828
|
replyCtx({
|
|
829
829
|
messageText: "approve",
|
|
830
830
|
conversationId: "conv-guardian-conversation",
|
|
831
|
-
|
|
831
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
832
832
|
approvalConversationGenerator: undefined,
|
|
833
833
|
}),
|
|
834
834
|
);
|
|
@@ -857,7 +857,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
857
857
|
replyCtx({
|
|
858
858
|
messageText: "ok, what is this for?",
|
|
859
859
|
conversationId: "conv-guardian-conversation",
|
|
860
|
-
|
|
860
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
861
861
|
approvalConversationGenerator: undefined,
|
|
862
862
|
}),
|
|
863
863
|
);
|
|
@@ -870,7 +870,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
870
870
|
expect(unchanged!.status).toBe("pending");
|
|
871
871
|
});
|
|
872
872
|
|
|
873
|
-
test("explicit
|
|
873
|
+
test("explicit blocked scope stays fail-closed for desktop actors", async () => {
|
|
874
874
|
createCanonicalGuardianRequest({
|
|
875
875
|
kind: "tool_approval",
|
|
876
876
|
sourceType: "channel",
|
|
@@ -887,7 +887,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
887
887
|
messageText: "approve",
|
|
888
888
|
actor: trustedActor(),
|
|
889
889
|
conversationId: "conv-unrelated",
|
|
890
|
-
|
|
890
|
+
pendingScope: { mode: "blocked" },
|
|
891
891
|
approvalConversationGenerator: undefined,
|
|
892
892
|
}),
|
|
893
893
|
);
|
|
@@ -897,6 +897,38 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
897
897
|
expect(result.decisionApplied).toBe(false);
|
|
898
898
|
});
|
|
899
899
|
|
|
900
|
+
test("explicit request code still resolves under a blocked scope (cross-chat carve-out)", async () => {
|
|
901
|
+
// The Slack cross-chat guard blocks identity fallback, but an explicit
|
|
902
|
+
// request code carries its own target and must still resolve — otherwise a
|
|
903
|
+
// guardian could not approve-by-code from a chat where no card was delivered.
|
|
904
|
+
const req = createCanonicalGuardianRequest({
|
|
905
|
+
kind: "tool_approval",
|
|
906
|
+
sourceType: "channel",
|
|
907
|
+
conversationId: "conv-other",
|
|
908
|
+
guardianExternalUserId: "guardian-1",
|
|
909
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
910
|
+
requestCode: "ABC123",
|
|
911
|
+
toolName: "shell",
|
|
912
|
+
expiresAt: Date.now() + 60_000,
|
|
913
|
+
});
|
|
914
|
+
registerPendingToolApprovalInteraction(req.id, "conv-other", "shell");
|
|
915
|
+
|
|
916
|
+
const result = await routeGuardianReply(
|
|
917
|
+
replyCtx({
|
|
918
|
+
messageText: "ABC123 approve",
|
|
919
|
+
actor: trustedActor(),
|
|
920
|
+
conversationId: "conv-unrelated",
|
|
921
|
+
pendingScope: { mode: "blocked" },
|
|
922
|
+
approvalConversationGenerator: undefined,
|
|
923
|
+
}),
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
expect(result.consumed).toBe(true);
|
|
927
|
+
expect(result.requestId).toBe(req.id);
|
|
928
|
+
expect(result.decisionApplied).toBe(true);
|
|
929
|
+
expect(getCanonicalGuardianRequest(req.id)!.status).toBe("approved");
|
|
930
|
+
});
|
|
931
|
+
|
|
900
932
|
test("multiple hinted pending requests with plain-text approve returns disambiguation", async () => {
|
|
901
933
|
const req1 = createCanonicalGuardianRequest({
|
|
902
934
|
kind: "tool_approval",
|
|
@@ -924,7 +956,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
924
956
|
replyCtx({
|
|
925
957
|
messageText: "approve",
|
|
926
958
|
conversationId: "conv-guardian-conversation",
|
|
927
|
-
|
|
959
|
+
pendingScope: { mode: "scoped", requestIds: [req1.id, req2.id] },
|
|
928
960
|
approvalConversationGenerator: undefined,
|
|
929
961
|
}),
|
|
930
962
|
);
|
|
@@ -977,7 +1009,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
977
1009
|
replyCtx({
|
|
978
1010
|
messageText: "yes approve it",
|
|
979
1011
|
conversationId: "conv-1",
|
|
980
|
-
|
|
1012
|
+
pendingScope: { mode: "scoped", requestIds: [req1.id, req2.id] },
|
|
981
1013
|
approvalConversationGenerator: mockGenerator as any,
|
|
982
1014
|
}),
|
|
983
1015
|
);
|
|
@@ -1031,7 +1063,10 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
1031
1063
|
replyCtx({
|
|
1032
1064
|
messageText: "approve",
|
|
1033
1065
|
conversationId: "conv-guardian-conversation",
|
|
1034
|
-
|
|
1066
|
+
pendingScope: {
|
|
1067
|
+
mode: "scoped",
|
|
1068
|
+
requestIds: [answerRequest.id, approvalRequest.id],
|
|
1069
|
+
},
|
|
1035
1070
|
approvalConversationGenerator: undefined,
|
|
1036
1071
|
}),
|
|
1037
1072
|
);
|
|
@@ -1075,7 +1110,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
1075
1110
|
replyCtx({
|
|
1076
1111
|
messageText: "yes",
|
|
1077
1112
|
conversationId: "conv-1",
|
|
1078
|
-
|
|
1113
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1079
1114
|
approvalConversationGenerator: mockGenerator as any,
|
|
1080
1115
|
}),
|
|
1081
1116
|
);
|
|
@@ -1104,7 +1139,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
1104
1139
|
replyCtx({
|
|
1105
1140
|
messageText: "go for it",
|
|
1106
1141
|
conversationId: "conv-1",
|
|
1107
|
-
|
|
1142
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1108
1143
|
approvalConversationGenerator: undefined,
|
|
1109
1144
|
}),
|
|
1110
1145
|
);
|
|
@@ -1117,7 +1152,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
1117
1152
|
expect(resolved!.status).toBe("approved");
|
|
1118
1153
|
});
|
|
1119
1154
|
|
|
1120
|
-
test("code-based routing is constrained to caller-provided
|
|
1155
|
+
test("code-based routing is constrained to caller-provided scope", async () => {
|
|
1121
1156
|
const inScope = createCanonicalGuardianRequest({
|
|
1122
1157
|
kind: "tool_approval",
|
|
1123
1158
|
sourceType: "channel",
|
|
@@ -1145,7 +1180,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
|
|
|
1145
1180
|
replyCtx({
|
|
1146
1181
|
messageText: "222BBB approve",
|
|
1147
1182
|
conversationId: "conv-guardian-conversation",
|
|
1148
|
-
|
|
1183
|
+
pendingScope: { mode: "scoped", requestIds: [inScope.id] },
|
|
1149
1184
|
approvalConversationGenerator: undefined,
|
|
1150
1185
|
}),
|
|
1151
1186
|
);
|
|
@@ -1438,7 +1473,7 @@ describe("routing invariant: directResolve interactions resolve via guardian dec
|
|
|
1438
1473
|
describe("routing invariant: destination hints do not bypass tool_approval principal binding", () => {
|
|
1439
1474
|
beforeEach(() => resetTables());
|
|
1440
1475
|
|
|
1441
|
-
test("explicit
|
|
1476
|
+
test("explicit scope still fails closed when guardianPrincipalId does not match", async () => {
|
|
1442
1477
|
// Voice-originated tool approval with a different principal than the actor.
|
|
1443
1478
|
const req = createCanonicalGuardianRequest({
|
|
1444
1479
|
kind: "tool_approval",
|
|
@@ -1452,15 +1487,15 @@ describe("routing invariant: destination hints do not bypass tool_approval princ
|
|
|
1452
1487
|
});
|
|
1453
1488
|
registerPendingToolApprovalInteraction(req.id, "conv-voice-1", "shell");
|
|
1454
1489
|
|
|
1455
|
-
// The channel inbound router would compute
|
|
1456
|
-
// delivery-scoped lookup and pass
|
|
1490
|
+
// The channel inbound router would compute the pending scope from
|
|
1491
|
+
// delivery-scoped lookup and pass it here. Simulate that.
|
|
1457
1492
|
const result = await routeGuardianReply(
|
|
1458
1493
|
replyCtx({
|
|
1459
1494
|
messageText: "approve",
|
|
1460
1495
|
channel: "telegram",
|
|
1461
1496
|
actor: guardianActor({ guardianPrincipalId: "different-principal" }),
|
|
1462
1497
|
conversationId: "conv-guardian-chat",
|
|
1463
|
-
|
|
1498
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1464
1499
|
approvalConversationGenerator: undefined,
|
|
1465
1500
|
}),
|
|
1466
1501
|
);
|
|
@@ -1486,7 +1521,7 @@ describe("routing invariant: destination hints do not bypass tool_approval princ
|
|
|
1486
1521
|
expiresAt: Date.now() + 60_000,
|
|
1487
1522
|
});
|
|
1488
1523
|
|
|
1489
|
-
// No
|
|
1524
|
+
// No pendingScope passed — identity-based fallback uses
|
|
1490
1525
|
// actor.actorExternalUserId which does not match any request's
|
|
1491
1526
|
// guardianExternalUserId (since it's null).
|
|
1492
1527
|
const result = await routeGuardianReply(
|
|
@@ -1495,7 +1530,7 @@ describe("routing invariant: destination hints do not bypass tool_approval princ
|
|
|
1495
1530
|
channel: "telegram",
|
|
1496
1531
|
actor: guardianActor({ actorExternalUserId: "guardian-tg-user" }),
|
|
1497
1532
|
conversationId: "conv-guardian-chat",
|
|
1498
|
-
//
|
|
1533
|
+
// pendingScope omitted — no delivery hints
|
|
1499
1534
|
approvalConversationGenerator: undefined,
|
|
1500
1535
|
}),
|
|
1501
1536
|
);
|
|
@@ -1534,7 +1569,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
|
|
|
1534
1569
|
replyCtx({
|
|
1535
1570
|
messageText: "open invite flow",
|
|
1536
1571
|
conversationId: "conv-guardian-conversation",
|
|
1537
|
-
|
|
1572
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1538
1573
|
approvalConversationGenerator: undefined,
|
|
1539
1574
|
}),
|
|
1540
1575
|
);
|
|
@@ -1569,7 +1604,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
|
|
|
1569
1604
|
replyCtx({
|
|
1570
1605
|
messageText: phrase,
|
|
1571
1606
|
conversationId: "conv-test",
|
|
1572
|
-
|
|
1607
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1573
1608
|
approvalConversationGenerator: undefined,
|
|
1574
1609
|
}),
|
|
1575
1610
|
);
|
|
@@ -1595,7 +1630,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
|
|
|
1595
1630
|
replyCtx({
|
|
1596
1631
|
messageText: "open invite flow",
|
|
1597
1632
|
conversationId: "conv-guardian-conversation",
|
|
1598
|
-
|
|
1633
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1599
1634
|
approvalConversationGenerator: undefined,
|
|
1600
1635
|
}),
|
|
1601
1636
|
);
|
|
@@ -1625,7 +1660,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
|
|
|
1625
1660
|
replyCtx({
|
|
1626
1661
|
messageText: "A00B01 approve",
|
|
1627
1662
|
conversationId: "conv-guardian-conversation",
|
|
1628
|
-
|
|
1663
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1629
1664
|
approvalConversationGenerator: undefined,
|
|
1630
1665
|
}),
|
|
1631
1666
|
);
|
|
@@ -1655,7 +1690,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
|
|
|
1655
1690
|
channel: "vellum",
|
|
1656
1691
|
actor: trustedActor({ channel: "vellum" }),
|
|
1657
1692
|
conversationId: "conv-guardian-conversation",
|
|
1658
|
-
|
|
1693
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1659
1694
|
approvalConversationGenerator: undefined,
|
|
1660
1695
|
});
|
|
1661
1696
|
|
|
@@ -1694,7 +1729,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
|
|
|
1694
1729
|
channel: "vellum",
|
|
1695
1730
|
actor: trustedActor({ channel: "vellum" }),
|
|
1696
1731
|
conversationId: "conv-guardian-conversation",
|
|
1697
|
-
|
|
1732
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1698
1733
|
approvalConversationGenerator: approvalConversationGenerator as any,
|
|
1699
1734
|
});
|
|
1700
1735
|
|
|
@@ -1746,7 +1781,7 @@ describe("routing invariant: expired requests are excluded from pending discover
|
|
|
1746
1781
|
replyCtx({
|
|
1747
1782
|
messageText: "approve",
|
|
1748
1783
|
conversationId: "conv-guardian-conversation",
|
|
1749
|
-
|
|
1784
|
+
pendingScope: { mode: "scoped", requestIds: [expired.id, active.id] },
|
|
1750
1785
|
approvalConversationGenerator: undefined,
|
|
1751
1786
|
}),
|
|
1752
1787
|
);
|
|
@@ -1781,7 +1816,7 @@ describe("routing invariant: expired requests are excluded from pending discover
|
|
|
1781
1816
|
replyCtx({
|
|
1782
1817
|
messageText: "`approve`",
|
|
1783
1818
|
conversationId: "conv-guardian-conversation",
|
|
1784
|
-
|
|
1819
|
+
pendingScope: { mode: "scoped", requestIds: [req.id] },
|
|
1785
1820
|
approvalConversationGenerator: undefined,
|
|
1786
1821
|
}),
|
|
1787
1822
|
);
|
|
@@ -1821,7 +1856,10 @@ describe("routing invariant: expired requests are excluded from pending discover
|
|
|
1821
1856
|
replyCtx({
|
|
1822
1857
|
messageText: "approve",
|
|
1823
1858
|
conversationId: "conv-guardian-conversation",
|
|
1824
|
-
|
|
1859
|
+
pendingScope: {
|
|
1860
|
+
mode: "scoped",
|
|
1861
|
+
requestIds: [expired1.id, expired2.id],
|
|
1862
|
+
},
|
|
1825
1863
|
approvalConversationGenerator: undefined,
|
|
1826
1864
|
}),
|
|
1827
1865
|
);
|
|
@@ -75,6 +75,16 @@ mock.module("../tools/browser/cdp-client/factory.js", () => ({
|
|
|
75
75
|
},
|
|
76
76
|
}));
|
|
77
77
|
|
|
78
|
+
// The real proxy is an always-present singleton; stub the grace wait so
|
|
79
|
+
// extension-pinned acquisition doesn't poll for the full window here.
|
|
80
|
+
mock.module("../daemon/host-browser-proxy.js", () => ({
|
|
81
|
+
HostBrowserProxy: {
|
|
82
|
+
get instance() {
|
|
83
|
+
return { waitForExtensionClient: async () => true };
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}));
|
|
87
|
+
|
|
78
88
|
// ── Minimal browserManager stub ──────────────────────────────────────
|
|
79
89
|
|
|
80
90
|
/** Mutable memo shared between mock methods and tests. */
|
|
@@ -58,9 +58,14 @@ let mockExtensionAvailable = false;
|
|
|
58
58
|
mock.module("../daemon/host-browser-proxy.js", () => ({
|
|
59
59
|
HostBrowserProxy: {
|
|
60
60
|
get instance() {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
// The real proxy is a lazily-created singleton that always exists;
|
|
62
|
+
// `isAvailable`/`waitForExtensionClient` reflect whether an extension
|
|
63
|
+
// is connected rather than the instance being absent.
|
|
64
|
+
return {
|
|
65
|
+
isAvailable: () => mockExtensionAvailable,
|
|
66
|
+
waitForExtensionClient: async () => mockExtensionAvailable,
|
|
67
|
+
request: mock(async () => ({})),
|
|
68
|
+
};
|
|
64
69
|
},
|
|
65
70
|
},
|
|
66
71
|
}));
|
|
@@ -46,6 +46,8 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
|
46
46
|
mockClients.filter((c) => c.capabilities.includes(cap)),
|
|
47
47
|
listClientsByInterface: (interfaceId: string) =>
|
|
48
48
|
mockClients.filter((c) => c.interfaceId === interfaceId),
|
|
49
|
+
getClientById: (clientId: string) =>
|
|
50
|
+
mockClients.find((c) => c.clientId === clientId),
|
|
49
51
|
getActorPrincipalIdForClient: (clientId: string) =>
|
|
50
52
|
mockClients.find((c) => c.clientId === clientId)?.actorPrincipalId,
|
|
51
53
|
},
|
|
@@ -108,6 +110,91 @@ describe("HostBrowserProxy", () => {
|
|
|
108
110
|
pendingInteractions.clear();
|
|
109
111
|
});
|
|
110
112
|
|
|
113
|
+
describe("waitForExtensionClient", () => {
|
|
114
|
+
const EXTENSION: MockClient = {
|
|
115
|
+
clientId: "ext-client",
|
|
116
|
+
interfaceId: "chrome-extension",
|
|
117
|
+
capabilities: ["host_browser"],
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
test("returns true immediately when an extension is connected", async () => {
|
|
121
|
+
mockClients = [EXTENSION];
|
|
122
|
+
expect(await proxy.waitForExtensionClient(undefined, undefined, 1_000)).toBe(
|
|
123
|
+
true,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("returns false after the timeout when none connects", async () => {
|
|
128
|
+
mockClients = [DEFAULT_CLIENT]; // macos only, no extension
|
|
129
|
+
expect(await proxy.waitForExtensionClient(undefined, undefined, 100)).toBe(
|
|
130
|
+
false,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("returns true when an extension appears within the window", async () => {
|
|
135
|
+
mockClients = [DEFAULT_CLIENT];
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
mockClients = [DEFAULT_CLIENT, EXTENSION];
|
|
138
|
+
}, 50);
|
|
139
|
+
expect(await proxy.waitForExtensionClient(undefined, undefined, 2_000)).toBe(
|
|
140
|
+
true,
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("respects sourceActorPrincipalId", async () => {
|
|
145
|
+
mockClients = [
|
|
146
|
+
{
|
|
147
|
+
clientId: "other-actor-ext",
|
|
148
|
+
interfaceId: "chrome-extension",
|
|
149
|
+
actorPrincipalId: "actor-b",
|
|
150
|
+
capabilities: ["host_browser"],
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
// Caller is actor-a; the connected extension belongs to actor-b.
|
|
154
|
+
expect(await proxy.waitForExtensionClient("actor-a", undefined, 100)).toBe(
|
|
155
|
+
false,
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("with a targetClientId, waits for that exact client (not a sibling)", async () => {
|
|
160
|
+
// A sibling extension is connected, but the targeted client is not —
|
|
161
|
+
// the wait must not return early on the sibling's presence.
|
|
162
|
+
mockClients = [
|
|
163
|
+
{
|
|
164
|
+
clientId: "sibling-ext",
|
|
165
|
+
interfaceId: "chrome-extension",
|
|
166
|
+
capabilities: ["host_browser"],
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
expect(
|
|
170
|
+
await proxy.waitForExtensionClient(undefined, "target-ext", 100),
|
|
171
|
+
).toBe(false);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("with a targetClientId, returns true once that client appears", async () => {
|
|
175
|
+
mockClients = [
|
|
176
|
+
{
|
|
177
|
+
clientId: "sibling-ext",
|
|
178
|
+
interfaceId: "chrome-extension",
|
|
179
|
+
capabilities: ["host_browser"],
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
mockClients = [
|
|
184
|
+
...mockClients,
|
|
185
|
+
{
|
|
186
|
+
clientId: "target-ext",
|
|
187
|
+
interfaceId: "chrome-extension",
|
|
188
|
+
capabilities: ["host_browser"],
|
|
189
|
+
},
|
|
190
|
+
];
|
|
191
|
+
}, 50);
|
|
192
|
+
expect(
|
|
193
|
+
await proxy.waitForExtensionClient(undefined, "target-ext", 2_000),
|
|
194
|
+
).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
111
198
|
describe("request/resolve lifecycle (happy path)", () => {
|
|
112
199
|
test("sends host_browser_request and resolves with content", async () => {
|
|
113
200
|
const resultPromise = proxy.request(
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Tests for the memory-v3 step-0 branch in `applyRuntimeInjections`:
|
|
3
3
|
* spotlight strip + v2 tail suppression.
|
|
4
4
|
*
|
|
5
|
-
* When
|
|
5
|
+
* When `memory.v3.live` is on AND the v3 injector (id `memory-v3`,
|
|
6
6
|
* placement `after-memory-prefix`) produces a block — possibly EMPTY-TEXT on
|
|
7
7
|
* an all-repeat turn — runtime assembly strips the v2 `<memory>` prefix from
|
|
8
8
|
* the TAIL user message only before splicing the v3 block. Historical user
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
* block lands at the tail.
|
|
17
17
|
*
|
|
18
18
|
* v2 suppression stays keyed off whether v3 produced a block, NOT off the
|
|
19
|
-
*
|
|
20
|
-
* (fallback-to-v2). The
|
|
19
|
+
* gate alone: a v3 failure (`produce()` → null) leaves v2's block intact
|
|
20
|
+
* (fallback-to-v2). The v3-off path must be byte-for-byte identical to
|
|
21
21
|
* today — that is the load-bearing regression guard. `applyRuntimeInjections`
|
|
22
|
-
* reads the
|
|
22
|
+
* reads the v3-live gate itself, so these tests drive it through the
|
|
23
|
+
* `isMemoryV3Live` mock slot.
|
|
23
24
|
*
|
|
24
25
|
* The strip discriminates v2's dynamic block by IDENTITY, not by prefix: v2's
|
|
25
26
|
* `INJECTION_HEADER` and v3's `V3_CARDS_INJECTION_HEADER` are deliberately
|
|
@@ -42,7 +43,6 @@ import type {
|
|
|
42
43
|
TurnContext,
|
|
43
44
|
} from "../plugins/types.js";
|
|
44
45
|
import type { Message } from "../providers/types.js";
|
|
45
|
-
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
46
46
|
|
|
47
47
|
// Drive the suppression branch by controlling the static injector chain that
|
|
48
48
|
// `applyRuntimeInjections` walks. The slot is mutated per-test to stand in for
|
|
@@ -52,6 +52,13 @@ mock.module("../plugins/defaults/memory-retrieval/injector-chain.js", () => ({
|
|
|
52
52
|
getInjectorChain: () => injectorChainSlot,
|
|
53
53
|
}));
|
|
54
54
|
|
|
55
|
+
// `applyRuntimeInjections` reads the v3-live gate (`config.memory.v3.live`)
|
|
56
|
+
// via `isMemoryV3Live`; drive it directly through this slot per-test.
|
|
57
|
+
let memoryV3LiveSlot = false;
|
|
58
|
+
mock.module("../config/memory-v3-gate.js", () => ({
|
|
59
|
+
isMemoryV3Live: () => memoryV3LiveSlot,
|
|
60
|
+
}));
|
|
61
|
+
|
|
55
62
|
const { applyRuntimeInjections } =
|
|
56
63
|
await import("../daemon/conversation-runtime-assembly.js");
|
|
57
64
|
|
|
@@ -141,9 +148,9 @@ function tailTexts(messages: Message[]): string[] {
|
|
|
141
148
|
describe("memory-v3-live v2 suppression", () => {
|
|
142
149
|
beforeEach(() => {
|
|
143
150
|
injectorChainSlot.length = 0;
|
|
144
|
-
// Clean baseline:
|
|
145
|
-
//
|
|
146
|
-
|
|
151
|
+
// Clean baseline: v3-live off (registry/config default). Each test seeds
|
|
152
|
+
// the gate value it needs.
|
|
153
|
+
memoryV3LiveSlot = false;
|
|
147
154
|
});
|
|
148
155
|
|
|
149
156
|
afterEach(() => {
|
|
@@ -152,7 +159,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
152
159
|
});
|
|
153
160
|
|
|
154
161
|
test("flag ON + v3 produced a block → TAIL v2 stripped, historical memory blocks frozen in place", async () => {
|
|
155
|
-
|
|
162
|
+
memoryV3LiveSlot = true;
|
|
156
163
|
injectorChainSlot.push(v3Injector("net-new cards"));
|
|
157
164
|
seedV2Identity("fresh recalled fact");
|
|
158
165
|
|
|
@@ -194,7 +201,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
194
201
|
});
|
|
195
202
|
|
|
196
203
|
test("flag ON + EMPTY-TEXT v3 block (all-repeat turn) → tail v2 stripped, nothing attached", async () => {
|
|
197
|
-
|
|
204
|
+
memoryV3LiveSlot = true;
|
|
198
205
|
injectorChainSlot.push(v3Injector(""));
|
|
199
206
|
seedV2Identity("fresh recalled fact");
|
|
200
207
|
|
|
@@ -217,7 +224,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
217
224
|
});
|
|
218
225
|
|
|
219
226
|
test("stale spotlight blocks are stripped from EVERY user message; the new spotlight lands at the tail", async () => {
|
|
220
|
-
|
|
227
|
+
memoryV3LiveSlot = true;
|
|
221
228
|
injectorChainSlot.push(v3Injector(""), spotlightInjector("fresh sections"));
|
|
222
229
|
|
|
223
230
|
const staleSpotlight = {
|
|
@@ -253,7 +260,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
253
260
|
});
|
|
254
261
|
|
|
255
262
|
test("convergence re-entry: a tail leading with this turn's frozen v3 cards (and <info>) is NOT stripped", async () => {
|
|
256
|
-
|
|
263
|
+
memoryV3LiveSlot = true;
|
|
257
264
|
// Re-entry shape: the memo returns the same selections, every slug is now
|
|
258
265
|
// in the everInjected store, so the injector produces an EMPTY block —
|
|
259
266
|
// while the tail still carries the v3 card block frozen on first entry
|
|
@@ -292,7 +299,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
292
299
|
});
|
|
293
300
|
|
|
294
301
|
test("first entry with a v2 prefix AND a frozen v3 block strips ONLY the v2 block", async () => {
|
|
295
|
-
|
|
302
|
+
memoryV3LiveSlot = true;
|
|
296
303
|
injectorChainSlot.push(v3Injector(""));
|
|
297
304
|
seedV2Identity("fresh recalled fact");
|
|
298
305
|
|
|
@@ -319,7 +326,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
319
326
|
});
|
|
320
327
|
|
|
321
328
|
test("REGRESSION: a v2 block leading with the REAL summary header (byte-identical to v3's) is stripped; v3 cards and <info> survive (first entry)", async () => {
|
|
322
|
-
|
|
329
|
+
memoryV3LiveSlot = true;
|
|
323
330
|
injectorChainSlot.push(v3Injector(""));
|
|
324
331
|
|
|
325
332
|
// The collision this guards: v2's router block leads with
|
|
@@ -358,7 +365,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
358
365
|
});
|
|
359
366
|
|
|
360
367
|
test("REGRESSION: re-entry with a header-bearing v2 identity keeps the first entry's frozen v3 cards", async () => {
|
|
361
|
-
|
|
368
|
+
memoryV3LiveSlot = true;
|
|
362
369
|
// Re-entry: produce() returns the EMPTY block (cards already claimed by
|
|
363
370
|
// the store) while the tail carries the FIRST entry's v3 block — and the
|
|
364
371
|
// graph handle still holds the summary-bearing v2 identity from this
|
|
@@ -391,7 +398,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
391
398
|
});
|
|
392
399
|
|
|
393
400
|
test("v2 memory-image groups and legacy <memory __injected> blocks are stripped even without an identity", async () => {
|
|
394
|
-
|
|
401
|
+
memoryV3LiveSlot = true;
|
|
395
402
|
injectorChainSlot.push(v3Injector("net-new cards"));
|
|
396
403
|
// No live graph handle: identity unknown (null). Image groups and legacy
|
|
397
404
|
// blocks are unambiguously v2's and are stripped regardless; the shared
|
|
@@ -430,7 +437,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
430
437
|
});
|
|
431
438
|
|
|
432
439
|
test("the v3 block's attachment-commit callback fires on a user tail", async () => {
|
|
433
|
-
|
|
440
|
+
memoryV3LiveSlot = true;
|
|
434
441
|
const commit = mock(() => {});
|
|
435
442
|
injectorChainSlot.push(v3Injector("net-new cards", commit));
|
|
436
443
|
|
|
@@ -443,7 +450,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
443
450
|
});
|
|
444
451
|
|
|
445
452
|
test("the commit callback is SKIPPED when the tail is not a user message (block never attaches)", async () => {
|
|
446
|
-
|
|
453
|
+
memoryV3LiveSlot = true;
|
|
447
454
|
const commit = mock(() => {});
|
|
448
455
|
injectorChainSlot.push(v3Injector("net-new cards", commit));
|
|
449
456
|
|
|
@@ -465,7 +472,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
465
472
|
});
|
|
466
473
|
|
|
467
474
|
test("flag ON but v3 produced NOTHING → v2 block left intact (fallback-to-v2)", async () => {
|
|
468
|
-
|
|
475
|
+
memoryV3LiveSlot = true;
|
|
469
476
|
injectorChainSlot.push(v3Injector(null));
|
|
470
477
|
|
|
471
478
|
const runMessages: Message[] = [
|
|
@@ -524,7 +531,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
524
531
|
test("no v3 injector registered + flag ON → no stripping, messages untouched", async () => {
|
|
525
532
|
// No injector named memory-v3 at all (e.g. plugin not loaded): the
|
|
526
533
|
// suppression branch keys off the produced block, so nothing is stripped.
|
|
527
|
-
|
|
534
|
+
memoryV3LiveSlot = true;
|
|
528
535
|
expect(injectorChainSlot).toHaveLength(0);
|
|
529
536
|
|
|
530
537
|
const runMessages: Message[] = [
|
|
@@ -7,23 +7,15 @@ mock.module("../util/logger.js", () => ({
|
|
|
7
7
|
}),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
|
-
// Toggle for the
|
|
10
|
+
// Toggle for the share_analytics opt-out the real store consults. The store
|
|
11
11
|
// module is intentionally NOT mocked here — it has its own DB-backed tests, and
|
|
12
12
|
// Bun's `mock.module` is process-global, so mocking it would leak into those
|
|
13
13
|
// tests when files share an invocation. Exercising the real store keeps every
|
|
14
14
|
// auth-fallback test order-independent.
|
|
15
|
-
let
|
|
15
|
+
let shareAnalytics = true;
|
|
16
16
|
|
|
17
|
-
mock.module("../
|
|
18
|
-
|
|
19
|
-
ui: {},
|
|
20
|
-
model: "test",
|
|
21
|
-
provider: "test",
|
|
22
|
-
memory: { enabled: false },
|
|
23
|
-
rateLimit: { maxRequestsPerMinute: 0 },
|
|
24
|
-
secretDetection: { enabled: false },
|
|
25
|
-
collectUsageData,
|
|
26
|
-
}),
|
|
17
|
+
mock.module("../platform/consent-cache.js", () => ({
|
|
18
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
27
19
|
}));
|
|
28
20
|
|
|
29
21
|
import { queryUnreportedAuthFallbackEvents } from "../memory/auth-fallback-events-store.js";
|
|
@@ -61,7 +53,7 @@ const VALID_BODY = {
|
|
|
61
53
|
|
|
62
54
|
describe("internal-telemetry-routes: auth-fallback", () => {
|
|
63
55
|
beforeEach(() => {
|
|
64
|
-
|
|
56
|
+
shareAnalytics = true;
|
|
65
57
|
getDb().delete(authFallbackEvents).run();
|
|
66
58
|
});
|
|
67
59
|
|
|
@@ -90,7 +82,7 @@ describe("internal-telemetry-routes: auth-fallback", () => {
|
|
|
90
82
|
});
|
|
91
83
|
|
|
92
84
|
test("returns skipped and persists nothing under the opt-out", () => {
|
|
93
|
-
|
|
85
|
+
shareAnalytics = false;
|
|
94
86
|
expect(call(VALID_BODY)).toEqual({ skipped: true });
|
|
95
87
|
expect(queryUnreportedAuthFallbackEvents(0, undefined, 100).length).toBe(0);
|
|
96
88
|
});
|
|
@@ -292,7 +292,6 @@ describe("invite-redemption-service", () => {
|
|
|
292
292
|
{
|
|
293
293
|
type: "telegram",
|
|
294
294
|
address: "guardian-tg-id",
|
|
295
|
-
externalUserId: "guardian-tg-id",
|
|
296
295
|
status: "revoked",
|
|
297
296
|
},
|
|
298
297
|
],
|
|
@@ -346,7 +345,6 @@ describe("invite-redemption-service", () => {
|
|
|
346
345
|
{
|
|
347
346
|
type: "telegram",
|
|
348
347
|
address: "guardian-own-id",
|
|
349
|
-
externalUserId: "guardian-own-id",
|
|
350
348
|
status: "revoked",
|
|
351
349
|
},
|
|
352
350
|
],
|
|
@@ -381,7 +379,6 @@ describe("invite-redemption-service", () => {
|
|
|
381
379
|
{
|
|
382
380
|
type: "telegram",
|
|
383
381
|
address: "guardian-code-id",
|
|
384
|
-
externalUserId: "guardian-code-id",
|
|
385
382
|
status: "revoked",
|
|
386
383
|
},
|
|
387
384
|
],
|