@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
package/src/instrument.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getPlatformUserId,
|
|
8
8
|
getSentryDsn,
|
|
9
9
|
} from "./config/env.js";
|
|
10
|
+
import { getCachedShareDiagnostics } from "./platform/consent-cache.js";
|
|
10
11
|
import { APP_VERSION, COMMIT_SHA } from "./version.js";
|
|
11
12
|
|
|
12
13
|
/** Patterns that match sensitive data in Sentry event values. */
|
|
@@ -41,9 +42,11 @@ function redactObject(obj: unknown): unknown {
|
|
|
41
42
|
/**
|
|
42
43
|
* Call after dotenv has loaded so SENTRY_DSN_ASSISTANT is available.
|
|
43
44
|
* Initializes Sentry when the DSN is set; no-ops when empty/unset so
|
|
44
|
-
* local dev builds don't send crash reports.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
45
|
+
* local dev builds don't send crash reports. Events are dropped by
|
|
46
|
+
* beforeSend until the platform share_diagnostics consent confirms opt-in
|
|
47
|
+
* (re-read from the consent cache per event, so a revocation takes effect
|
|
48
|
+
* within one refresh cycle); VELLUM_DEV=1 and legacyDiagnosticsOptOut
|
|
49
|
+
* hard-disable via closeSentry() after config loads.
|
|
47
50
|
*/
|
|
48
51
|
export function initSentry(): void {
|
|
49
52
|
const dsn = getSentryDsn();
|
|
@@ -54,6 +57,14 @@ export function initSentry(): void {
|
|
|
54
57
|
dist: COMMIT_SHA,
|
|
55
58
|
environment: process.env.VELLUM_ENVIRONMENT ?? "production",
|
|
56
59
|
sendDefaultPii: false,
|
|
60
|
+
// Fail-closed: suppress client-report pings (discarded-event counts) so an
|
|
61
|
+
// assistant with unconfirmed share_diagnostics consent stays network-silent.
|
|
62
|
+
sendClientReports: false,
|
|
63
|
+
// Drop the default ProcessSession integration: it starts a release-health
|
|
64
|
+
// session on init and flushes a session envelope on process exit (a network
|
|
65
|
+
// request not covered by the beforeSend event gate), violating fail-closed.
|
|
66
|
+
integrations: (defaults) =>
|
|
67
|
+
defaults.filter((i) => i.name !== "ProcessSession"),
|
|
57
68
|
serverName: hostname(),
|
|
58
69
|
initialScope: {
|
|
59
70
|
tags: {
|
|
@@ -77,6 +88,7 @@ export function initSentry(): void {
|
|
|
77
88
|
},
|
|
78
89
|
},
|
|
79
90
|
beforeSend(event) {
|
|
91
|
+
if (!getCachedShareDiagnostics()) return null;
|
|
80
92
|
if (event.exception?.values) {
|
|
81
93
|
event.exception.values = event.exception.values.map((ex) => ({
|
|
82
94
|
...ex,
|
|
@@ -101,9 +113,9 @@ export function initSentry(): void {
|
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
/**
|
|
104
|
-
* Stop capturing future Sentry events. Called after config loads
|
|
105
|
-
*
|
|
106
|
-
*
|
|
116
|
+
* Stop capturing future Sentry events. Called after config loads for the
|
|
117
|
+
* legacyDiagnosticsOptOut local opt-out (or VELLUM_DEV=1) to hard-disable
|
|
118
|
+
* crash reporting independent of the share_diagnostics consent gate.
|
|
107
119
|
*/
|
|
108
120
|
export async function closeSentry(): Promise<void> {
|
|
109
121
|
await Sentry.close();
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for how `AssistantIpcServer.sendResult` handles a binary or
|
|
3
|
+
* streaming handler result over the IPC transport.
|
|
4
|
+
*
|
|
5
|
+
* A `RouteResponse` (or a bare `Uint8Array` / `ReadableStream` / `Blob`)
|
|
6
|
+
* wraps raw bytes that can't be carried as a JSON `result` field. Rather than
|
|
7
|
+
* silently serialize the body into garbage, the server reports a structured
|
|
8
|
+
* `BINARY_UNSUPPORTED_OVER_IPC` error; the gateway IPC proxy uses that signal
|
|
9
|
+
* to fall back to the HTTP proxy, which streams binary responses correctly.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, expect, test } from "bun:test";
|
|
13
|
+
|
|
14
|
+
import { RouteResponse } from "../../runtime/routes/types.js";
|
|
15
|
+
import { AssistantIpcServer } from "../assistant-server.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `sendResult` is private; access it through an interface cast so the test
|
|
19
|
+
* exercises the real production path without a test-only API on the class.
|
|
20
|
+
*/
|
|
21
|
+
type PrivateApi = {
|
|
22
|
+
sendResult(
|
|
23
|
+
socket: unknown,
|
|
24
|
+
reader: unknown,
|
|
25
|
+
requestId: string,
|
|
26
|
+
value: unknown,
|
|
27
|
+
): void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Drive `sendResult` with a capturing socket in legacy (newline-delimited
|
|
32
|
+
* JSON) mode — the same wire shape the gateway's IPC client speaks — and
|
|
33
|
+
* return the parsed response envelope.
|
|
34
|
+
*/
|
|
35
|
+
function captureSendResult(value: unknown): Record<string, unknown> {
|
|
36
|
+
const server = new AssistantIpcServer() as unknown as PrivateApi;
|
|
37
|
+
const writes: string[] = [];
|
|
38
|
+
const socket = {
|
|
39
|
+
destroyed: false,
|
|
40
|
+
write: (chunk: string) => {
|
|
41
|
+
writes.push(chunk);
|
|
42
|
+
return true;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const reader = { isLegacy: true };
|
|
46
|
+
|
|
47
|
+
server.sendResult(socket, reader, "req-1", value);
|
|
48
|
+
|
|
49
|
+
expect(writes).toHaveLength(1);
|
|
50
|
+
return JSON.parse(writes[0]) as Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("AssistantIpcServer.sendResult binary handling", () => {
|
|
54
|
+
test("a binary RouteResponse is reported as BINARY_UNSUPPORTED_OVER_IPC", () => {
|
|
55
|
+
const env = captureSendResult(
|
|
56
|
+
new RouteResponse(new Uint8Array([137, 80, 78, 71]), {
|
|
57
|
+
"content-type": "image/png",
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(env.id).toBe("req-1");
|
|
62
|
+
expect(env.statusCode).toBe(421);
|
|
63
|
+
expect(env.errorCode).toBe("BINARY_UNSUPPORTED_OVER_IPC");
|
|
64
|
+
// The binary body must never be JSON-serialized into a `result` field.
|
|
65
|
+
expect("result" in env).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("a bare Uint8Array result is also reported as unsupported", () => {
|
|
69
|
+
const env = captureSendResult(new Uint8Array([1, 2, 3]));
|
|
70
|
+
|
|
71
|
+
expect(env.errorCode).toBe("BINARY_UNSUPPORTED_OVER_IPC");
|
|
72
|
+
expect("result" in env).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("a plain JSON result still serializes into `result`", () => {
|
|
76
|
+
const env = captureSendResult({ ok: true, count: 2 });
|
|
77
|
+
|
|
78
|
+
expect(env.result).toEqual({ ok: true, count: 2 });
|
|
79
|
+
expect(env.errorCode).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -166,4 +166,24 @@ describe("assistant clients list over IPC — same-user filter", () => {
|
|
|
166
166
|
const ids = parsed.clients.map((c) => c.clientId).sort();
|
|
167
167
|
expect(ids).toEqual(["client-other", "client-self"]);
|
|
168
168
|
});
|
|
169
|
+
|
|
170
|
+
test("--json output carries the degraded flag (false for fresh clients)", async () => {
|
|
171
|
+
registerClient({
|
|
172
|
+
clientId: "client-self-1",
|
|
173
|
+
actorPrincipalId: "guardian-local",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await startServer();
|
|
177
|
+
|
|
178
|
+
const { stdout } = await runAssistantCommandFull(
|
|
179
|
+
"clients",
|
|
180
|
+
"list",
|
|
181
|
+
"--json",
|
|
182
|
+
);
|
|
183
|
+
const parsed = JSON.parse(stdout.trim()) as {
|
|
184
|
+
clients: Array<{ clientId: string; degraded?: boolean }>;
|
|
185
|
+
};
|
|
186
|
+
expect(parsed.clients).toHaveLength(1);
|
|
187
|
+
expect(parsed.clients[0].degraded).toBe(false);
|
|
188
|
+
});
|
|
169
189
|
});
|
|
@@ -31,10 +31,7 @@
|
|
|
31
31
|
import { existsSync, unlinkSync } from "node:fs";
|
|
32
32
|
import { createServer, type Server, type Socket } from "node:net";
|
|
33
33
|
|
|
34
|
-
import {
|
|
35
|
-
ensureSocketDir,
|
|
36
|
-
SocketWatchdog,
|
|
37
|
-
} from "@vellumai/ipc-server-utils";
|
|
34
|
+
import { ensureSocketDir, SocketWatchdog } from "@vellumai/ipc-server-utils";
|
|
38
35
|
|
|
39
36
|
import { findLocalGuardianPrincipalId } from "../runtime/local-actor-identity.js";
|
|
40
37
|
import { RouteError } from "../runtime/routes/errors.js";
|
|
@@ -43,6 +40,7 @@ import type {
|
|
|
43
40
|
RouteDefinition,
|
|
44
41
|
RouteHandlerArgs,
|
|
45
42
|
} from "../runtime/routes/types.js";
|
|
43
|
+
import { RouteResponse } from "../runtime/routes/types.js";
|
|
46
44
|
import { getLogger } from "../util/logger.js";
|
|
47
45
|
import {
|
|
48
46
|
type IpcEnvelope,
|
|
@@ -134,6 +132,26 @@ function isIpcBinaryResponse(value: unknown): value is IpcBinaryResponse {
|
|
|
134
132
|
);
|
|
135
133
|
}
|
|
136
134
|
|
|
135
|
+
/**
|
|
136
|
+
* A handler result whose body is raw bytes or a stream rather than a JSON
|
|
137
|
+
* value — `RouteResponse` (e.g. `workspace/file/content` returns a
|
|
138
|
+
* `Bun.file`), or a bare `Uint8Array` / `ReadableStream` / `Blob`.
|
|
139
|
+
*
|
|
140
|
+
* These cannot be carried as a JSON `result` field, so over the IPC
|
|
141
|
+
* transport they are reported as a structured error rather than silently
|
|
142
|
+
* JSON-serialized into garbage. Distinct from the `IpcBinaryResponse` /
|
|
143
|
+
* `IpcStreamingResponse` wrappers, which are explicit binary envelopes the
|
|
144
|
+
* framing protocol does transmit.
|
|
145
|
+
*/
|
|
146
|
+
function isNonJsonIpcResult(value: unknown): boolean {
|
|
147
|
+
return (
|
|
148
|
+
value instanceof RouteResponse ||
|
|
149
|
+
value instanceof Uint8Array ||
|
|
150
|
+
value instanceof ReadableStream ||
|
|
151
|
+
value instanceof Blob
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
137
155
|
// ---------------------------------------------------------------------------
|
|
138
156
|
// Server
|
|
139
157
|
// ---------------------------------------------------------------------------
|
|
@@ -401,6 +419,8 @@ export class AssistantIpcServer {
|
|
|
401
419
|
* Route a handler result to the appropriate send path:
|
|
402
420
|
* - IpcStreamingResponse → chunked binary frames
|
|
403
421
|
* - IpcBinaryResponse → single binary frame with content-length
|
|
422
|
+
* - A raw binary/stream result (RouteResponse, Uint8Array, ...) → structured
|
|
423
|
+
* BINARY_UNSUPPORTED_OVER_IPC error (the transport can't carry it as JSON)
|
|
404
424
|
* - Everything else → JSON response
|
|
405
425
|
*/
|
|
406
426
|
private sendResult(
|
|
@@ -420,6 +440,19 @@ export class AssistantIpcServer {
|
|
|
420
440
|
},
|
|
421
441
|
};
|
|
422
442
|
this.sendResponse(socket, reader, envelope, value.binary);
|
|
443
|
+
} else if (isNonJsonIpcResult(value)) {
|
|
444
|
+
// A binary/streaming handler result (e.g. a file-content RouteResponse
|
|
445
|
+
// wrapping a Bun.file) cannot be carried as a JSON `result`. Report a
|
|
446
|
+
// structured error instead of silently serializing it into garbage; the
|
|
447
|
+
// gateway IPC proxy treats this code as a signal to retry over HTTP,
|
|
448
|
+
// which streams binary correctly.
|
|
449
|
+
this.sendResponse(socket, reader, {
|
|
450
|
+
id: requestId,
|
|
451
|
+
error:
|
|
452
|
+
"Binary/streaming responses are not supported over the IPC transport; use HTTP",
|
|
453
|
+
statusCode: 421,
|
|
454
|
+
errorCode: "BINARY_UNSUPPORTED_OVER_IPC",
|
|
455
|
+
});
|
|
423
456
|
} else {
|
|
424
457
|
this.sendResponse(socket, reader, { id: requestId, result: value });
|
|
425
458
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { connect, type Socket } from "node:net";
|
|
2
2
|
|
|
3
3
|
import { refreshOverridesFromGateway } from "../config/assistant-feature-flags.js";
|
|
4
|
+
import { reconcileFlagGatedProfiles } from "../config/sync-gated-profiles.js";
|
|
4
5
|
import { SYNC_TAGS } from "../daemon/message-types/sync.js";
|
|
6
|
+
import { publishConfigChanged } from "../runtime/sync/resource-sync-events.js";
|
|
5
7
|
import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
|
|
6
8
|
import { syncFlagGatedTools } from "../tools/registry.js";
|
|
7
9
|
import { getLogger } from "../util/logger.js";
|
|
@@ -19,11 +21,25 @@ const log = getLogger("gateway-flag-listener");
|
|
|
19
21
|
* they live in a flag-gated bundled skill surfaced via `skill_load` against the
|
|
20
22
|
* live flag cache, so they need no registry sync here. `syncFlagGatedTools` is
|
|
21
23
|
* idempotent and enable-only, so re-running it on every refresh is safe; it also
|
|
22
|
-
* never throws (it logs internally).
|
|
24
|
+
* never throws (it logs internally). After tools sync, `reconcileFlagGatedProfiles`
|
|
25
|
+
* adds or removes the flag-gated managed profile (OS Beta); when it reports a
|
|
26
|
+
* change a `config_changed` broadcast refreshes the profile picker on clients.
|
|
27
|
+
* The profile reconcile runs only when the refresh confirmed flags loaded from
|
|
28
|
+
* the gateway — a transient IPC failure leaves the cache unset and resolves
|
|
29
|
+
* `os-beta` to its registry default `false`, which would remove the user's
|
|
30
|
+
* profile and reset their selection. Tool sync tolerates the default and stays
|
|
31
|
+
* unconditional.
|
|
23
32
|
*/
|
|
24
33
|
function refreshFlagsAndSyncTools(context: string): void {
|
|
25
34
|
refreshOverridesFromGateway()
|
|
26
|
-
.then(() =>
|
|
35
|
+
.then(async (loaded) => {
|
|
36
|
+
await syncFlagGatedTools();
|
|
37
|
+
if (loaded && reconcileFlagGatedProfiles()) {
|
|
38
|
+
// Reuse the config-changed broadcast clients already consume so the
|
|
39
|
+
// profile picker reflects the added/removed managed profile.
|
|
40
|
+
publishConfigChanged();
|
|
41
|
+
}
|
|
42
|
+
})
|
|
27
43
|
.catch((err) => {
|
|
28
44
|
log.warn(
|
|
29
45
|
{ err },
|
|
@@ -76,17 +76,6 @@ mock.module("../jobs-store.js", () => ({
|
|
|
76
76
|
},
|
|
77
77
|
}));
|
|
78
78
|
|
|
79
|
-
// Mirror production semantics from `isUntrustedTrustClass` in
|
|
80
|
-
// actor-trust-resolver.ts: anything that isn't `guardian` is untrusted.
|
|
81
|
-
// Keeping these in sync guards the compaction trust boundary — a drifting
|
|
82
|
-
// mock would let regressions pass as false positives.
|
|
83
|
-
mock.module("../../runtime/actor-trust-resolver.js", () => ({
|
|
84
|
-
isUntrustedTrustClass: (trustClass: string | undefined) =>
|
|
85
|
-
trustClass === "trusted_contact" ||
|
|
86
|
-
trustClass === "unknown" ||
|
|
87
|
-
trustClass === undefined,
|
|
88
|
-
}));
|
|
89
|
-
|
|
90
79
|
import {
|
|
91
80
|
enqueueAutoAnalysisIfEnabled,
|
|
92
81
|
enqueueAutoAnalysisOnCompaction,
|
|
@@ -315,9 +304,9 @@ describe("enqueueAutoAnalysisOnCompaction", () => {
|
|
|
315
304
|
});
|
|
316
305
|
|
|
317
306
|
test("undefined trust class — skips (fail-closed when trust is unresolved)", () => {
|
|
318
|
-
// `
|
|
319
|
-
// compaction-triggered analysis must NOT fire when the
|
|
320
|
-
// establish a trust class.
|
|
307
|
+
// `resolveCapabilities(undefined).canAccessMemory` is false in
|
|
308
|
+
// production, so compaction-triggered analysis must NOT fire when the
|
|
309
|
+
// caller cannot establish a trust class.
|
|
321
310
|
enqueueAutoAnalysisOnCompaction("c1", undefined);
|
|
322
311
|
|
|
323
312
|
expect(enqueueCalls).toHaveLength(0);
|
|
@@ -332,8 +321,8 @@ describe("enqueueAutoAnalysisOnCompaction", () => {
|
|
|
332
321
|
});
|
|
333
322
|
|
|
334
323
|
test("trusted_contact trust class — skips (only guardian is trusted)", () => {
|
|
335
|
-
// trusted_contact
|
|
336
|
-
// `
|
|
324
|
+
// trusted_contact lacks `canAccessMemory` per production
|
|
325
|
+
// `resolveCapabilities`, so compaction-triggered analysis must NOT
|
|
337
326
|
// fire. Only `guardian` passes the gate.
|
|
338
327
|
enqueueAutoAnalysisOnCompaction("c1", "trusted_contact");
|
|
339
328
|
|
|
@@ -48,14 +48,12 @@ mock.module("../../config/assistant-feature-flags.js", () => ({
|
|
|
48
48
|
isAssistantFeatureFlagEnabled: () => true,
|
|
49
49
|
}));
|
|
50
50
|
|
|
51
|
-
// Stub trust resolver — never claim the actor is untrusted in tests.
|
|
52
|
-
mock.module("../../runtime/actor-trust-resolver.js", () => ({
|
|
53
|
-
isUntrustedTrustClass: () => false,
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
51
|
// Stub the conversation-source lookup so the recursion guards in the
|
|
57
52
|
// retrospective and auto-analysis paths fall through to the enqueue.
|
|
53
|
+
// `getConversation` returning null keeps `isLowYieldRetrospectiveSource`
|
|
54
|
+
// false, so the retrospective is enqueued rather than skipped.
|
|
58
55
|
mock.module("../conversation-crud.js", () => ({
|
|
56
|
+
getConversation: () => null,
|
|
59
57
|
getConversationSource: () => null,
|
|
60
58
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
61
59
|
}));
|
|
@@ -117,12 +115,10 @@ mock.module("../db-connection.js", () => ({
|
|
|
117
115
|
|
|
118
116
|
// Now load the real modules under test.
|
|
119
117
|
const { isMemoryEnabled } = await import("../jobs-store.js");
|
|
120
|
-
const { enqueueAutoAnalysisIfEnabled } =
|
|
121
|
-
"../auto-analysis-enqueue.js"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
"../memory-retrospective-enqueue.js"
|
|
125
|
-
);
|
|
118
|
+
const { enqueueAutoAnalysisIfEnabled } =
|
|
119
|
+
await import("../auto-analysis-enqueue.js");
|
|
120
|
+
const { enqueueMemoryRetrospectiveIfEnabled } =
|
|
121
|
+
await import("../memory-retrospective-enqueue.js");
|
|
126
122
|
|
|
127
123
|
beforeEach(() => {
|
|
128
124
|
dbInserts.length = 0;
|
|
@@ -12,6 +12,8 @@ mock.module("../../util/logger.js", () => ({
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
|
|
14
14
|
let sourceTag: string | null = null;
|
|
15
|
+
let convType = "standard";
|
|
16
|
+
let convSource = "user";
|
|
15
17
|
const upsertCalls: Array<{
|
|
16
18
|
payload: { conversationId: string };
|
|
17
19
|
runAfter: number;
|
|
@@ -19,6 +21,10 @@ const upsertCalls: Array<{
|
|
|
19
21
|
|
|
20
22
|
mock.module("../conversation-crud.js", () => ({
|
|
21
23
|
getConversationSource: (_id: string) => sourceTag,
|
|
24
|
+
getConversation: (_id: string) => ({
|
|
25
|
+
conversationType: convType,
|
|
26
|
+
source: convSource,
|
|
27
|
+
}),
|
|
22
28
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
23
29
|
}));
|
|
24
30
|
|
|
@@ -31,13 +37,6 @@ mock.module("../jobs-store.js", () => ({
|
|
|
31
37
|
},
|
|
32
38
|
}));
|
|
33
39
|
|
|
34
|
-
mock.module("../../runtime/actor-trust-resolver.js", () => ({
|
|
35
|
-
isUntrustedTrustClass: (trustClass: string | undefined) =>
|
|
36
|
-
trustClass === "trusted_contact" ||
|
|
37
|
-
trustClass === "unknown" ||
|
|
38
|
-
trustClass === undefined,
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
40
|
import {
|
|
42
41
|
enqueueMemoryRetrospectiveIfEnabled,
|
|
43
42
|
enqueueMemoryRetrospectiveOnCompaction,
|
|
@@ -47,6 +46,8 @@ import {
|
|
|
47
46
|
describe("enqueueMemoryRetrospectiveIfEnabled", () => {
|
|
48
47
|
beforeEach(() => {
|
|
49
48
|
sourceTag = null;
|
|
49
|
+
convType = "standard";
|
|
50
|
+
convSource = "user";
|
|
50
51
|
upsertCalls.length = 0;
|
|
51
52
|
});
|
|
52
53
|
|
|
@@ -85,6 +86,35 @@ describe("enqueueMemoryRetrospectiveIfEnabled", () => {
|
|
|
85
86
|
});
|
|
86
87
|
expect(upsertCalls).toHaveLength(0);
|
|
87
88
|
});
|
|
89
|
+
|
|
90
|
+
test("scheduled conversation — skips enqueue", () => {
|
|
91
|
+
convType = "scheduled";
|
|
92
|
+
enqueueMemoryRetrospectiveIfEnabled({
|
|
93
|
+
conversationId: "c1",
|
|
94
|
+
trigger: "interval",
|
|
95
|
+
});
|
|
96
|
+
expect(upsertCalls).toHaveLength(0);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("memory_v2_consolidation source — skips enqueue", () => {
|
|
100
|
+
convType = "background";
|
|
101
|
+
convSource = "memory_v2_consolidation";
|
|
102
|
+
enqueueMemoryRetrospectiveIfEnabled({
|
|
103
|
+
conversationId: "c1",
|
|
104
|
+
trigger: "interval",
|
|
105
|
+
});
|
|
106
|
+
expect(upsertCalls).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("heartbeat (background) source — still enqueues", () => {
|
|
110
|
+
convType = "background";
|
|
111
|
+
convSource = "heartbeat";
|
|
112
|
+
enqueueMemoryRetrospectiveIfEnabled({
|
|
113
|
+
conversationId: "c1",
|
|
114
|
+
trigger: "interval",
|
|
115
|
+
});
|
|
116
|
+
expect(upsertCalls).toHaveLength(1);
|
|
117
|
+
});
|
|
88
118
|
});
|
|
89
119
|
|
|
90
120
|
describe("isMemoryRetrospectiveConversation", () => {
|
|
@@ -685,6 +685,40 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
685
685
|
expect(wakeCalls[0]!.opts.toolGateMode).toBe("execution");
|
|
686
686
|
});
|
|
687
687
|
|
|
688
|
+
// -------------------------------------------------------------------------
|
|
689
|
+
// Source background-turn parity (isNonInteractive)
|
|
690
|
+
// -------------------------------------------------------------------------
|
|
691
|
+
|
|
692
|
+
test("background source → wake runs non-interactive (reproduces <background_turn>)", async () => {
|
|
693
|
+
conversationOverrides["src-conv-1"] = {
|
|
694
|
+
source: "heartbeat",
|
|
695
|
+
forkParentMessageId: null,
|
|
696
|
+
title: "Heartbeat",
|
|
697
|
+
conversationType: "background",
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
701
|
+
|
|
702
|
+
expect(outcome.kind).toBe("invoked");
|
|
703
|
+
expect(wakeCalls).toHaveLength(1);
|
|
704
|
+
expect(wakeCalls[0]!.opts.isNonInteractive).toBe(true);
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
test("standard source → wake stays interactive (no spurious <background_turn>)", async () => {
|
|
708
|
+
conversationOverrides["src-conv-1"] = {
|
|
709
|
+
source: "user",
|
|
710
|
+
forkParentMessageId: null,
|
|
711
|
+
title: "Source conversation",
|
|
712
|
+
conversationType: "standard",
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
716
|
+
|
|
717
|
+
expect(outcome.kind).toBe("invoked");
|
|
718
|
+
expect(wakeCalls).toHaveLength(1);
|
|
719
|
+
expect(wakeCalls[0]!.opts.isNonInteractive).toBe(false);
|
|
720
|
+
});
|
|
721
|
+
|
|
688
722
|
// -------------------------------------------------------------------------
|
|
689
723
|
// Source-derived persona override
|
|
690
724
|
// -------------------------------------------------------------------------
|
|
@@ -8,10 +8,10 @@ mock.module("../../util/logger.js", () => ({
|
|
|
8
8
|
}),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
// Mutable
|
|
12
|
-
let
|
|
13
|
-
mock.module("../../
|
|
14
|
-
|
|
11
|
+
// Mutable consent gate, flipped per-test.
|
|
12
|
+
let shareAnalytics = true;
|
|
13
|
+
mock.module("../../platform/consent-cache.js", () => ({
|
|
14
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
17
|
import { getDb } from "../db-connection.js";
|
|
@@ -30,7 +30,7 @@ function resetTable(): void {
|
|
|
30
30
|
|
|
31
31
|
describe("onboarding-events-store: recordActivationEvent", () => {
|
|
32
32
|
beforeEach(() => {
|
|
33
|
-
|
|
33
|
+
shareAnalytics = true;
|
|
34
34
|
resetTable();
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -66,8 +66,8 @@ describe("onboarding-events-store: recordActivationEvent", () => {
|
|
|
66
66
|
expect(rows[0]!.stepIndex).toBe(2);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
test("returns null and writes no row when
|
|
70
|
-
|
|
69
|
+
test("returns null and writes no row when share_analytics is disabled", () => {
|
|
70
|
+
shareAnalytics = false;
|
|
71
71
|
const event = recordActivationEvent({
|
|
72
72
|
stepName: "activation_moment_1_complete",
|
|
73
73
|
sessionId: "conv-3",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, asc, eq, gt, or } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getCachedShareAnalytics } from "../platform/consent-cache.js";
|
|
5
5
|
import { getDb } from "./db-connection.js";
|
|
6
6
|
import { authFallbackEvents } from "./schema.js";
|
|
7
7
|
|
|
@@ -36,7 +36,7 @@ export function recordAuthFallbackCounts(
|
|
|
36
36
|
windowEnd: number,
|
|
37
37
|
counts: AuthFallbackCount[],
|
|
38
38
|
): number {
|
|
39
|
-
if (!
|
|
39
|
+
if (!getCachedShareAnalytics()) return 0;
|
|
40
40
|
if (counts.length === 0) return 0;
|
|
41
41
|
const db = getDb();
|
|
42
42
|
const createdAt = Date.now();
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
2
2
|
import { getConfig } from "../config/loader.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type TrustClass,
|
|
6
|
-
} from "../runtime/actor-trust-resolver.js";
|
|
3
|
+
import { type TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
4
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
7
5
|
import { getLogger } from "../util/logger.js";
|
|
8
6
|
import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
|
|
9
7
|
import { isMemoryEnabled, upsertAutoAnalysisJob } from "./jobs-store.js";
|
|
@@ -118,7 +116,7 @@ export function enqueueAutoAnalysisOnCompaction(
|
|
|
118
116
|
conversationId: string,
|
|
119
117
|
trustClass: TrustClass | undefined,
|
|
120
118
|
): void {
|
|
121
|
-
if (
|
|
119
|
+
if (!resolveCapabilities(trustClass).canAccessMemory) {
|
|
122
120
|
return;
|
|
123
121
|
}
|
|
124
122
|
try {
|
|
@@ -74,7 +74,7 @@ export interface CanonicalGuardianRequest {
|
|
|
74
74
|
updatedAt: number;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
interface CanonicalGuardianDelivery {
|
|
77
|
+
export interface CanonicalGuardianDelivery {
|
|
78
78
|
id: string;
|
|
79
79
|
requestId: string;
|
|
80
80
|
destinationChannel: string;
|
|
@@ -674,6 +674,44 @@ export function listPendingCanonicalGuardianRequestsByDestinationChat(
|
|
|
674
674
|
return pendingRequests;
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
+
/**
|
|
678
|
+
* Find the pending canonical request whose guardian-facing delivery landed on a
|
|
679
|
+
* specific channel message (channel + chat + message id).
|
|
680
|
+
*
|
|
681
|
+
* This is the addressing key for emoji-reaction decisions: a reaction carries
|
|
682
|
+
* only the message it is attached to, so the delivered card's message id is how
|
|
683
|
+
* we recover which request the guardian acted on — even when several cards are
|
|
684
|
+
* pending in the same chat. Returns null when no delivery matches or the matched
|
|
685
|
+
* request is no longer pending.
|
|
686
|
+
*/
|
|
687
|
+
export function getPendingCanonicalRequestByDestinationMessage(
|
|
688
|
+
destinationChannel: string,
|
|
689
|
+
destinationChatId: string,
|
|
690
|
+
destinationMessageId: string,
|
|
691
|
+
): CanonicalGuardianRequest | null {
|
|
692
|
+
const db = getDb();
|
|
693
|
+
|
|
694
|
+
const delivery = db
|
|
695
|
+
.select()
|
|
696
|
+
.from(canonicalGuardianDeliveries)
|
|
697
|
+
.where(
|
|
698
|
+
and(
|
|
699
|
+
eq(canonicalGuardianDeliveries.destinationChannel, destinationChannel),
|
|
700
|
+
eq(canonicalGuardianDeliveries.destinationChatId, destinationChatId),
|
|
701
|
+
eq(
|
|
702
|
+
canonicalGuardianDeliveries.destinationMessageId,
|
|
703
|
+
destinationMessageId,
|
|
704
|
+
),
|
|
705
|
+
),
|
|
706
|
+
)
|
|
707
|
+
.get();
|
|
708
|
+
|
|
709
|
+
if (!delivery) return null;
|
|
710
|
+
|
|
711
|
+
const request = getCanonicalGuardianRequest(delivery.requestId);
|
|
712
|
+
return request && request.status === "pending" ? request : null;
|
|
713
|
+
}
|
|
714
|
+
|
|
677
715
|
// ---------------------------------------------------------------------------
|
|
678
716
|
// Conversation scope helpers
|
|
679
717
|
// ---------------------------------------------------------------------------
|
|
@@ -129,7 +129,7 @@ export const messageMetadataSchema = z
|
|
|
129
129
|
* and read gate (conversation history loading) to enforce trust-aware access.
|
|
130
130
|
*/
|
|
131
131
|
provenanceTrustClass: z
|
|
132
|
-
.enum(["guardian", "trusted_contact", "unknown"])
|
|
132
|
+
.enum(["guardian", "trusted_contact", "unverified_contact", "unknown"])
|
|
133
133
|
.optional(),
|
|
134
134
|
provenanceSourceChannel: channelIdSchema.optional(),
|
|
135
135
|
provenanceGuardianExternalUserId: z.string().optional(),
|
|
@@ -2575,12 +2575,17 @@ export function getConversationOriginInterface(
|
|
|
2575
2575
|
* in the given conversation, or `undefined` if none is found.
|
|
2576
2576
|
*
|
|
2577
2577
|
* Used by the pointer message trust resolver to detect conversations
|
|
2578
|
-
* whose audience is a guardian
|
|
2579
|
-
* conversations.
|
|
2578
|
+
* whose audience is a guardian, trusted_contact, or unverified_contact
|
|
2579
|
+
* outside desktop-origin conversations.
|
|
2580
2580
|
*/
|
|
2581
2581
|
export function getConversationRecentProvenanceTrustClass(
|
|
2582
2582
|
conversationId: string,
|
|
2583
|
-
):
|
|
2583
|
+
):
|
|
2584
|
+
| "guardian"
|
|
2585
|
+
| "trusted_contact"
|
|
2586
|
+
| "unverified_contact"
|
|
2587
|
+
| "unknown"
|
|
2588
|
+
| undefined {
|
|
2584
2589
|
const row = rawGet<{ metadata: string | null }>(
|
|
2585
2590
|
`SELECT metadata FROM messages
|
|
2586
2591
|
WHERE conversation_id = ? AND role = 'user' AND metadata IS NOT NULL
|