@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
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
* re-install at the current pin — the underlying
|
|
17
17
|
* {@link ./install-from-github.installPlugin} performs that atomically, and the
|
|
18
18
|
* previously installed copy (including any local edits) is replaced wholesale.
|
|
19
|
-
* `ours`/`theirs` instead three-way merge the on-disk tree and the
|
|
20
|
-
* the re-materialized install commit so non-conflicting local edits
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
19
|
+
* `ours`/`theirs`/`assistant` instead three-way merge the on-disk tree and the
|
|
20
|
+
* pin against the re-materialized install commit so non-conflicting local edits
|
|
21
|
+
* survive; conflicting hunks resolve toward the local edit (`ours`) or the pin
|
|
22
|
+
* (`theirs`), while `assistant` writes git conflict markers into the file and
|
|
23
|
+
* reports the conflicted paths for the assistant to resolve.
|
|
24
24
|
*
|
|
25
25
|
* Designed for direct programmatic use with injected dependencies, mirroring
|
|
26
26
|
* the sibling plugin libraries. The CLI command `assistant plugins upgrade
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
readInstallMeta,
|
|
56
56
|
sanitizePluginName,
|
|
57
57
|
} from "./install-from-github.js";
|
|
58
|
-
import { mergePluginTree } from "./merge-plugin-tree.js";
|
|
58
|
+
import { type ConflictLabels, mergePluginTree } from "./merge-plugin-tree.js";
|
|
59
59
|
import { computeFingerprint, fingerprintsEqual } from "./plugin-fingerprint.js";
|
|
60
60
|
import { PluginNotInstalledError } from "./uninstall-plugin.js";
|
|
61
61
|
|
|
@@ -67,8 +67,8 @@ import { PluginNotInstalledError } from "./uninstall-plugin.js";
|
|
|
67
67
|
* wholesale. Matches the historical upgrade behavior.
|
|
68
68
|
* - `ours` — three-way merge; conflicting hunks resolve toward the local edit.
|
|
69
69
|
* - `theirs` — three-way merge; conflicting hunks resolve toward the pin.
|
|
70
|
-
* - `assistant` —
|
|
71
|
-
*
|
|
70
|
+
* - `assistant` — three-way merge that writes git conflict markers into
|
|
71
|
+
* conflicting files and reports them for the assistant to resolve.
|
|
72
72
|
*/
|
|
73
73
|
export type PluginUpgradeStrategy =
|
|
74
74
|
| "ours"
|
|
@@ -151,6 +151,19 @@ export interface PluginUpgradeResult {
|
|
|
151
151
|
readonly dryRun: boolean;
|
|
152
152
|
/** Conflict-resolution strategy the upgrade applied. */
|
|
153
153
|
readonly strategy: PluginUpgradeStrategy;
|
|
154
|
+
/**
|
|
155
|
+
* Paths (relative to {@link PluginUpgradeResult.target}) left for the
|
|
156
|
+
* assistant to resolve under the `assistant` strategy: text files carry git
|
|
157
|
+
* conflict markers, modify/delete divergences keep the surviving content.
|
|
158
|
+
* Always empty for other strategies.
|
|
159
|
+
*/
|
|
160
|
+
readonly conflicts: readonly string[];
|
|
161
|
+
/**
|
|
162
|
+
* Paths of binary files that conflicted under the `assistant` strategy; the
|
|
163
|
+
* local copy was kept since markers cannot be written into binary content.
|
|
164
|
+
* Always empty for other strategies.
|
|
165
|
+
*/
|
|
166
|
+
readonly binaryConflicts: readonly string[];
|
|
154
167
|
/**
|
|
155
168
|
* Whether the installed copy lacked resolvable provenance before the
|
|
156
169
|
* upgrade. Such installs are re-pinned to the current SHA, which also
|
|
@@ -170,16 +183,6 @@ export class PluginNotUpgradableError extends Error {
|
|
|
170
183
|
}
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
/** A requested merge strategy is recognized but not yet implemented. */
|
|
174
|
-
export class PluginUpgradeStrategyUnsupportedError extends Error {
|
|
175
|
-
constructor(readonly strategy: PluginUpgradeStrategy) {
|
|
176
|
-
super(
|
|
177
|
-
`Upgrade strategy "${strategy}" is not yet supported. Use one of: ours, theirs, overwrite.`,
|
|
178
|
-
);
|
|
179
|
-
this.name = "PluginUpgradeStrategyUnsupportedError";
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
186
|
/**
|
|
184
187
|
* A merge strategy (`ours`/`theirs`) was requested but the install-time
|
|
185
188
|
* baseline needed for a three-way merge cannot be reconstructed.
|
|
@@ -205,16 +208,16 @@ function pluginTarget(name: string, deps: UpgradePluginDeps): string {
|
|
|
205
208
|
* Move an installed plugin to the marketplace's current pin.
|
|
206
209
|
*
|
|
207
210
|
* The `strategy` controls how local edits are reconciled with the pin:
|
|
208
|
-
* `overwrite` (default) re-installs the pin wholesale; `ours`/`theirs
|
|
209
|
-
* three-way merge that carries non-conflicting local edits
|
|
210
|
-
* conflicting hunks toward the local edit or the
|
|
211
|
-
*
|
|
211
|
+
* `overwrite` (default) re-installs the pin wholesale; `ours`/`theirs`/
|
|
212
|
+
* `assistant` do a three-way merge that carries non-conflicting local edits
|
|
213
|
+
* forward, resolving conflicting hunks toward the local edit (`ours`) or the
|
|
214
|
+
* pin (`theirs`), or leaving git conflict markers in the file for the assistant
|
|
215
|
+
* to resolve (`assistant`).
|
|
212
216
|
*
|
|
213
217
|
* Throws {@link PluginNotInstalledError} when no copy is installed,
|
|
214
218
|
* {@link PluginNotUpgradableError} when the install has no marketplace entry to
|
|
215
|
-
* advance to, {@link
|
|
216
|
-
*
|
|
217
|
-
* but the install-time baseline cannot be reconstructed,
|
|
219
|
+
* advance to, {@link PluginMergeBaselineError} when a merge strategy is
|
|
220
|
+
* requested but the install-time baseline cannot be reconstructed,
|
|
218
221
|
* {@link PluginSourceUnavailableError} when the marketplace catalog is
|
|
219
222
|
* temporarily unreachable (a retryable outage, distinct from the permanent
|
|
220
223
|
* no-entry case), and propagates {@link installPlugin}'s errors (e.g. source
|
|
@@ -228,10 +231,6 @@ export async function upgradePlugin(
|
|
|
228
231
|
const dryRun = opts.dryRun ?? false;
|
|
229
232
|
const strategy = opts.strategy ?? DEFAULT_PLUGIN_UPGRADE_STRATEGY;
|
|
230
233
|
|
|
231
|
-
if (strategy === "assistant") {
|
|
232
|
-
throw new PluginUpgradeStrategyUnsupportedError(strategy);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
234
|
let inspection: PluginInspection;
|
|
236
235
|
try {
|
|
237
236
|
inspection = await inspectPlugin(
|
|
@@ -292,6 +291,8 @@ export async function upgradePlugin(
|
|
|
292
291
|
fileCount: null,
|
|
293
292
|
dryRun,
|
|
294
293
|
strategy,
|
|
294
|
+
conflicts: [],
|
|
295
|
+
binaryConflicts: [],
|
|
295
296
|
provenanceWasUnknown: false,
|
|
296
297
|
};
|
|
297
298
|
}
|
|
@@ -308,13 +309,20 @@ export async function upgradePlugin(
|
|
|
308
309
|
fileCount: null,
|
|
309
310
|
dryRun: true,
|
|
310
311
|
strategy,
|
|
312
|
+
conflicts: [],
|
|
313
|
+
binaryConflicts: [],
|
|
311
314
|
provenanceWasUnknown,
|
|
312
315
|
};
|
|
313
316
|
}
|
|
314
317
|
|
|
315
|
-
// `ours`/`theirs` carry local edits forward via a three-way
|
|
316
|
-
// default `overwrite` discards them and re-installs the pin
|
|
317
|
-
|
|
318
|
+
// `ours`/`theirs`/`assistant` carry local edits forward via a three-way
|
|
319
|
+
// merge; the default `overwrite` discards them and re-installs the pin
|
|
320
|
+
// wholesale.
|
|
321
|
+
if (
|
|
322
|
+
strategy === "ours" ||
|
|
323
|
+
strategy === "theirs" ||
|
|
324
|
+
strategy === "assistant"
|
|
325
|
+
) {
|
|
318
326
|
return mergeUpgrade(
|
|
319
327
|
{
|
|
320
328
|
name,
|
|
@@ -323,6 +331,8 @@ export async function upgradePlugin(
|
|
|
323
331
|
remote,
|
|
324
332
|
fromCommit,
|
|
325
333
|
fromTimestamp,
|
|
334
|
+
toCommit,
|
|
335
|
+
toTimestamp,
|
|
326
336
|
provenanceWasUnknown,
|
|
327
337
|
},
|
|
328
338
|
deps,
|
|
@@ -350,6 +360,8 @@ export async function upgradePlugin(
|
|
|
350
360
|
fileCount: result.fileCount,
|
|
351
361
|
dryRun: false,
|
|
352
362
|
strategy,
|
|
363
|
+
conflicts: [],
|
|
364
|
+
binaryConflicts: [],
|
|
353
365
|
provenanceWasUnknown,
|
|
354
366
|
};
|
|
355
367
|
}
|
|
@@ -359,22 +371,35 @@ export async function upgradePlugin(
|
|
|
359
371
|
* and the marketplace pin (`theirs`) against the re-materialized install commit
|
|
360
372
|
* (`base`), then atomically swapping the merged tree into place pinned at the
|
|
361
373
|
* new commit. Conflicting hunks resolve toward `ours` or `theirs` per the
|
|
362
|
-
* strategy
|
|
374
|
+
* strategy, or are left as git conflict markers under `assistant`;
|
|
375
|
+
* non-conflicting edits from both sides survive.
|
|
363
376
|
*/
|
|
364
377
|
async function mergeUpgrade(
|
|
365
378
|
ctx: {
|
|
366
379
|
readonly name: string;
|
|
367
|
-
readonly strategy: "ours" | "theirs";
|
|
380
|
+
readonly strategy: "ours" | "theirs" | "assistant";
|
|
368
381
|
readonly local: PluginLocalInfo;
|
|
369
382
|
readonly remote: PluginRemoteInfo;
|
|
370
383
|
readonly fromCommit: string | null;
|
|
371
384
|
readonly fromTimestamp: string | null;
|
|
385
|
+
readonly toCommit: string;
|
|
386
|
+
readonly toTimestamp: string | null;
|
|
372
387
|
readonly provenanceWasUnknown: boolean;
|
|
373
388
|
},
|
|
374
389
|
deps: UpgradePluginDeps,
|
|
375
390
|
): Promise<PluginUpgradeResult> {
|
|
376
391
|
const { name, strategy, local, remote } = ctx;
|
|
377
392
|
|
|
393
|
+
// Marker labels name each side by its commit so the assistant can tell the
|
|
394
|
+
// local edit from the incoming pin when resolving.
|
|
395
|
+
const shortSha = (sha: string | null): string =>
|
|
396
|
+
sha ? sha.slice(0, 7) : "unknown";
|
|
397
|
+
const conflictLabels: ConflictLabels = {
|
|
398
|
+
ours: `local edits (was ${shortSha(ctx.fromCommit)})`,
|
|
399
|
+
base: `install baseline ${shortSha(ctx.fromCommit)}`,
|
|
400
|
+
theirs: `upgrade pin ${shortSha(ctx.toCommit)}`,
|
|
401
|
+
};
|
|
402
|
+
|
|
378
403
|
const meta = readInstallMeta(local.target);
|
|
379
404
|
if (!meta || !meta.commit || !meta.fingerprint) {
|
|
380
405
|
throw new PluginMergeBaselineError(
|
|
@@ -461,12 +486,13 @@ async function mergeUpgrade(
|
|
|
461
486
|
);
|
|
462
487
|
}
|
|
463
488
|
|
|
464
|
-
const
|
|
489
|
+
const merge = await mergePluginTree({
|
|
465
490
|
baseDir,
|
|
466
491
|
oursDir: local.target,
|
|
467
492
|
theirsDir,
|
|
468
493
|
destDir: stagingDir,
|
|
469
494
|
strategy,
|
|
495
|
+
conflictLabels,
|
|
470
496
|
});
|
|
471
497
|
|
|
472
498
|
const toCommit = theirs.commit ?? remote.commit;
|
|
@@ -488,9 +514,11 @@ async function mergeUpgrade(
|
|
|
488
514
|
toCommit,
|
|
489
515
|
toTimestamp,
|
|
490
516
|
target: join(pluginsDir, name),
|
|
491
|
-
fileCount,
|
|
517
|
+
fileCount: merge.fileCount,
|
|
492
518
|
dryRun: false,
|
|
493
519
|
strategy,
|
|
520
|
+
conflicts: merge.conflicts,
|
|
521
|
+
binaryConflicts: merge.binaryConflicts,
|
|
494
522
|
provenanceWasUnknown: ctx.provenanceWasUnknown,
|
|
495
523
|
};
|
|
496
524
|
} catch (err) {
|
package/src/cli/program.ts
CHANGED
|
@@ -34,8 +34,7 @@ import { registerImageGenerationCommand } from "./commands/image-generation.js";
|
|
|
34
34
|
import { registerInferenceCommand } from "./commands/inference.js";
|
|
35
35
|
import { registerKeysCommand } from "./commands/keys.js";
|
|
36
36
|
import { registerMcpCommand } from "./commands/mcp.js";
|
|
37
|
-
import {
|
|
38
|
-
import { registerMemoryV3Command } from "./commands/memory-v3.js";
|
|
37
|
+
import { registerMemoryCommand } from "./commands/memory/index.js";
|
|
39
38
|
import { registerNotificationsCommand } from "./commands/notifications.js";
|
|
40
39
|
import { registerOAuthCommand } from "./commands/oauth/index.js";
|
|
41
40
|
import { registerPendingCommand } from "./commands/pending.js";
|
|
@@ -130,8 +129,7 @@ Examples:
|
|
|
130
129
|
registerInferenceCommand(program);
|
|
131
130
|
registerKeysCommand(program);
|
|
132
131
|
registerMcpCommand(program);
|
|
133
|
-
|
|
134
|
-
registerMemoryV3Command(program);
|
|
132
|
+
registerMemoryCommand(program);
|
|
135
133
|
registerNotificationsCommand(program);
|
|
136
134
|
registerOAuthCommand(program);
|
|
137
135
|
registerPendingCommand(program);
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import {
|
|
10
|
+
afterAll,
|
|
11
|
+
afterEach,
|
|
12
|
+
beforeEach,
|
|
13
|
+
describe,
|
|
14
|
+
expect,
|
|
15
|
+
mock,
|
|
16
|
+
test,
|
|
17
|
+
} from "bun:test";
|
|
18
|
+
|
|
19
|
+
const WORKSPACE_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
20
|
+
const CONFIG_PATH = join(WORKSPACE_DIR, "config.json");
|
|
21
|
+
|
|
22
|
+
function ensureTestDir(): void {
|
|
23
|
+
const dirs = [
|
|
24
|
+
WORKSPACE_DIR,
|
|
25
|
+
join(WORKSPACE_DIR, "data"),
|
|
26
|
+
join(WORKSPACE_DIR, "data", "memory"),
|
|
27
|
+
join(WORKSPACE_DIR, "data", "logs"),
|
|
28
|
+
];
|
|
29
|
+
for (const dir of dirs) {
|
|
30
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function makeLoggerStub(): Record<string, unknown> {
|
|
35
|
+
const stub: Record<string, unknown> = {};
|
|
36
|
+
for (const m of [
|
|
37
|
+
"info",
|
|
38
|
+
"warn",
|
|
39
|
+
"error",
|
|
40
|
+
"debug",
|
|
41
|
+
"trace",
|
|
42
|
+
"fatal",
|
|
43
|
+
"silent",
|
|
44
|
+
"child",
|
|
45
|
+
]) {
|
|
46
|
+
stub[m] = m === "child" ? () => makeLoggerStub() : () => {};
|
|
47
|
+
}
|
|
48
|
+
return stub;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
mock.module("../../util/logger.js", () => ({
|
|
52
|
+
getLogger: () => makeLoggerStub(),
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
afterAll(() => {
|
|
56
|
+
mock.restore();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
import { setOverridesForTesting } from "../../__tests__/feature-flag-test-helpers.js";
|
|
60
|
+
import { invalidateConfigCache } from "../loader.js";
|
|
61
|
+
import { LLMSchema } from "../schemas/llm.js";
|
|
62
|
+
import { seedInferenceProfiles } from "../seed-inference-profiles.js";
|
|
63
|
+
import { reconcileFlagGatedProfiles } from "../sync-gated-profiles.js";
|
|
64
|
+
|
|
65
|
+
type RawConfig = {
|
|
66
|
+
llm: {
|
|
67
|
+
profiles: Record<string, Record<string, unknown>>;
|
|
68
|
+
profileOrder: string[];
|
|
69
|
+
activeProfile?: string;
|
|
70
|
+
advisorProfile?: string;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function writeConfig(obj: unknown): void {
|
|
75
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readConfig(): RawConfig {
|
|
79
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function seedBalancedConfig(): void {
|
|
83
|
+
writeConfig({ llm: { default: { provider: "anthropic" } } });
|
|
84
|
+
invalidateConfigCache();
|
|
85
|
+
seedInferenceProfiles({});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe("reconcileFlagGatedProfiles", () => {
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
ensureTestDir();
|
|
91
|
+
if (existsSync(CONFIG_PATH)) rmSync(CONFIG_PATH, { force: true });
|
|
92
|
+
delete process.env.IS_PLATFORM;
|
|
93
|
+
setOverridesForTesting({});
|
|
94
|
+
invalidateConfigCache();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
afterEach(() => {
|
|
98
|
+
delete process.env.IS_PLATFORM;
|
|
99
|
+
setOverridesForTesting({});
|
|
100
|
+
invalidateConfigCache();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("flag on materializes the managed os-beta profile after balanced", () => {
|
|
104
|
+
process.env.IS_PLATFORM = "true";
|
|
105
|
+
seedBalancedConfig();
|
|
106
|
+
setOverridesForTesting({ "os-beta": true });
|
|
107
|
+
|
|
108
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
109
|
+
|
|
110
|
+
const raw = readConfig();
|
|
111
|
+
const osBeta = raw.llm.profiles["os-beta"]!;
|
|
112
|
+
expect(osBeta.model).toBe("accounts/fireworks/models/glm-5p2");
|
|
113
|
+
expect(osBeta.provider_connection).toBe("fireworks-managed");
|
|
114
|
+
expect(osBeta.source).toBe("managed");
|
|
115
|
+
expect(osBeta.label).toBe("OS Beta");
|
|
116
|
+
|
|
117
|
+
const order = raw.llm.profileOrder;
|
|
118
|
+
expect(order.indexOf("os-beta")).toBe(order.indexOf("balanced") + 1);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("flag on in BYOK mode seeds the os-beta profile disabled", () => {
|
|
122
|
+
seedBalancedConfig();
|
|
123
|
+
setOverridesForTesting({ "os-beta": true });
|
|
124
|
+
|
|
125
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
126
|
+
|
|
127
|
+
const osBeta = readConfig().llm.profiles["os-beta"]!;
|
|
128
|
+
expect(osBeta.status).toBe("disabled");
|
|
129
|
+
expect(osBeta.label).toBe("OS Beta (Managed)");
|
|
130
|
+
expect(osBeta.source).toBe("managed");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("flag on is idempotent across repeated runs", () => {
|
|
134
|
+
process.env.IS_PLATFORM = "true";
|
|
135
|
+
seedBalancedConfig();
|
|
136
|
+
setOverridesForTesting({ "os-beta": true });
|
|
137
|
+
|
|
138
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
139
|
+
invalidateConfigCache();
|
|
140
|
+
expect(reconcileFlagGatedProfiles()).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("flag on preserves user overrides on the managed profile", () => {
|
|
144
|
+
process.env.IS_PLATFORM = "true";
|
|
145
|
+
seedBalancedConfig();
|
|
146
|
+
setOverridesForTesting({ "os-beta": true });
|
|
147
|
+
reconcileFlagGatedProfiles();
|
|
148
|
+
|
|
149
|
+
const raw = readConfig();
|
|
150
|
+
raw.llm.profiles["os-beta"]!.label = "My OS Beta";
|
|
151
|
+
raw.llm.profiles["os-beta"]!.status = "disabled";
|
|
152
|
+
raw.llm.profiles["os-beta"]!.advisorEnabled = true;
|
|
153
|
+
writeConfig(raw);
|
|
154
|
+
invalidateConfigCache();
|
|
155
|
+
|
|
156
|
+
reconcileFlagGatedProfiles();
|
|
157
|
+
|
|
158
|
+
const after = readConfig().llm.profiles["os-beta"]!;
|
|
159
|
+
expect(after.label).toBe("My OS Beta");
|
|
160
|
+
expect(after.status).toBe("disabled");
|
|
161
|
+
expect(after.advisorEnabled).toBe(true);
|
|
162
|
+
expect(after.model).toBe("accounts/fireworks/models/glm-5p2");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("flag off removes a managed os-beta and applies fallbacks", () => {
|
|
166
|
+
process.env.IS_PLATFORM = "true";
|
|
167
|
+
seedBalancedConfig();
|
|
168
|
+
setOverridesForTesting({ "os-beta": true });
|
|
169
|
+
reconcileFlagGatedProfiles();
|
|
170
|
+
|
|
171
|
+
const raw = readConfig();
|
|
172
|
+
raw.llm.activeProfile = "os-beta";
|
|
173
|
+
raw.llm.advisorProfile = "os-beta";
|
|
174
|
+
writeConfig(raw);
|
|
175
|
+
invalidateConfigCache();
|
|
176
|
+
|
|
177
|
+
setOverridesForTesting({ "os-beta": false });
|
|
178
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
179
|
+
|
|
180
|
+
const after = readConfig();
|
|
181
|
+
expect(after.llm.profiles["os-beta"]).toBeUndefined();
|
|
182
|
+
expect(after.llm.profileOrder.includes("os-beta")).toBe(false);
|
|
183
|
+
expect(after.llm.activeProfile).toBe("balanced");
|
|
184
|
+
expect(after.llm.advisorProfile).toBe("quality-optimized");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("flag off with no os-beta present is a no-op", () => {
|
|
188
|
+
process.env.IS_PLATFORM = "true";
|
|
189
|
+
seedBalancedConfig();
|
|
190
|
+
setOverridesForTesting({ "os-beta": false });
|
|
191
|
+
|
|
192
|
+
expect(reconcileFlagGatedProfiles()).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("flag off scrubs os-beta from user mix arms, keeping >= 2-arm mixes", () => {
|
|
196
|
+
process.env.IS_PLATFORM = "true";
|
|
197
|
+
seedBalancedConfig();
|
|
198
|
+
setOverridesForTesting({ "os-beta": true });
|
|
199
|
+
reconcileFlagGatedProfiles();
|
|
200
|
+
|
|
201
|
+
const raw = readConfig();
|
|
202
|
+
raw.llm.profiles["my-mix"] = {
|
|
203
|
+
source: "user",
|
|
204
|
+
label: "My Mix",
|
|
205
|
+
mix: [
|
|
206
|
+
{ profile: "balanced", weight: 70 },
|
|
207
|
+
{ profile: "quality-optimized", weight: 20 },
|
|
208
|
+
{ profile: "os-beta", weight: 30 },
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
raw.llm.profileOrder.push("my-mix");
|
|
212
|
+
writeConfig(raw);
|
|
213
|
+
invalidateConfigCache();
|
|
214
|
+
|
|
215
|
+
setOverridesForTesting({ "os-beta": false });
|
|
216
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
217
|
+
|
|
218
|
+
const after = readConfig();
|
|
219
|
+
expect(after.llm.profiles["os-beta"]).toBeUndefined();
|
|
220
|
+
const mix = after.llm.profiles["my-mix"]!;
|
|
221
|
+
expect(mix.mix).toEqual([
|
|
222
|
+
{ profile: "balanced", weight: 70 },
|
|
223
|
+
{ profile: "quality-optimized", weight: 20 },
|
|
224
|
+
]);
|
|
225
|
+
expect(mix.source).toBe("user");
|
|
226
|
+
expect(mix.label).toBe("My Mix");
|
|
227
|
+
|
|
228
|
+
invalidateConfigCache();
|
|
229
|
+
expect(reconcileFlagGatedProfiles()).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("flag off removes a two-arm mix that loses os-beta and repoints refs", () => {
|
|
233
|
+
process.env.IS_PLATFORM = "true";
|
|
234
|
+
seedBalancedConfig();
|
|
235
|
+
setOverridesForTesting({ "os-beta": true });
|
|
236
|
+
reconcileFlagGatedProfiles();
|
|
237
|
+
|
|
238
|
+
const raw = readConfig();
|
|
239
|
+
raw.llm.profiles["experiment"] = {
|
|
240
|
+
source: "user",
|
|
241
|
+
label: "Experiment",
|
|
242
|
+
mix: [
|
|
243
|
+
{ profile: "balanced", weight: 50 },
|
|
244
|
+
{ profile: "os-beta", weight: 50 },
|
|
245
|
+
],
|
|
246
|
+
};
|
|
247
|
+
raw.llm.profileOrder.push("experiment");
|
|
248
|
+
raw.llm.activeProfile = "experiment";
|
|
249
|
+
raw.llm.advisorProfile = "os-beta";
|
|
250
|
+
(raw.llm as Record<string, unknown>).callSites = {
|
|
251
|
+
mainAgent: { profile: "experiment" },
|
|
252
|
+
};
|
|
253
|
+
writeConfig(raw);
|
|
254
|
+
invalidateConfigCache();
|
|
255
|
+
|
|
256
|
+
setOverridesForTesting({ "os-beta": false });
|
|
257
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
258
|
+
|
|
259
|
+
const after = readConfig();
|
|
260
|
+
expect(after.llm.profiles["os-beta"]).toBeUndefined();
|
|
261
|
+
expect(after.llm.profiles["experiment"]).toBeUndefined();
|
|
262
|
+
expect(after.llm.profileOrder.includes("os-beta")).toBe(false);
|
|
263
|
+
expect(after.llm.profileOrder.includes("experiment")).toBe(false);
|
|
264
|
+
expect(after.llm.activeProfile).toBe("balanced");
|
|
265
|
+
expect(after.llm.advisorProfile).toBe("quality-optimized");
|
|
266
|
+
expect(
|
|
267
|
+
(
|
|
268
|
+
after.llm as unknown as Record<
|
|
269
|
+
string,
|
|
270
|
+
Record<string, Record<string, unknown>>
|
|
271
|
+
>
|
|
272
|
+
).callSites.mainAgent.profile,
|
|
273
|
+
).toBeUndefined();
|
|
274
|
+
|
|
275
|
+
expect(LLMSchema.safeParse(after.llm).success).toBe(true);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("flag off shortens a three-arm mix to two surviving arms", () => {
|
|
279
|
+
process.env.IS_PLATFORM = "true";
|
|
280
|
+
seedBalancedConfig();
|
|
281
|
+
setOverridesForTesting({ "os-beta": true });
|
|
282
|
+
reconcileFlagGatedProfiles();
|
|
283
|
+
|
|
284
|
+
const raw = readConfig();
|
|
285
|
+
raw.llm.profiles["blend"] = {
|
|
286
|
+
source: "user",
|
|
287
|
+
label: "Blend",
|
|
288
|
+
mix: [
|
|
289
|
+
{ profile: "balanced", weight: 40 },
|
|
290
|
+
{ profile: "os-beta", weight: 30 },
|
|
291
|
+
{ profile: "quality-optimized", weight: 30 },
|
|
292
|
+
],
|
|
293
|
+
};
|
|
294
|
+
raw.llm.profileOrder.push("blend");
|
|
295
|
+
writeConfig(raw);
|
|
296
|
+
invalidateConfigCache();
|
|
297
|
+
|
|
298
|
+
setOverridesForTesting({ "os-beta": false });
|
|
299
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
300
|
+
|
|
301
|
+
const after = readConfig();
|
|
302
|
+
expect(after.llm.profiles["os-beta"]).toBeUndefined();
|
|
303
|
+
expect(after.llm.profiles["blend"]!.mix).toEqual([
|
|
304
|
+
{ profile: "balanced", weight: 40 },
|
|
305
|
+
{ profile: "quality-optimized", weight: 30 },
|
|
306
|
+
]);
|
|
307
|
+
|
|
308
|
+
expect(LLMSchema.safeParse(after.llm).success).toBe(true);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("flag off clears a call-site profile reference to os-beta", () => {
|
|
312
|
+
process.env.IS_PLATFORM = "true";
|
|
313
|
+
seedBalancedConfig();
|
|
314
|
+
setOverridesForTesting({ "os-beta": true });
|
|
315
|
+
reconcileFlagGatedProfiles();
|
|
316
|
+
|
|
317
|
+
const raw = readConfig();
|
|
318
|
+
(raw.llm as Record<string, unknown>).callSites = {
|
|
319
|
+
advisor: { profile: "os-beta", temperature: 0.3 },
|
|
320
|
+
};
|
|
321
|
+
writeConfig(raw);
|
|
322
|
+
invalidateConfigCache();
|
|
323
|
+
|
|
324
|
+
setOverridesForTesting({ "os-beta": false });
|
|
325
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
326
|
+
|
|
327
|
+
const after = readConfig();
|
|
328
|
+
const advisor = (
|
|
329
|
+
after.llm as unknown as Record<
|
|
330
|
+
string,
|
|
331
|
+
Record<string, Record<string, unknown>>
|
|
332
|
+
>
|
|
333
|
+
).callSites.advisor;
|
|
334
|
+
expect(advisor.profile).toBeUndefined();
|
|
335
|
+
expect(advisor.temperature).toBe(0.3);
|
|
336
|
+
|
|
337
|
+
expect(LLMSchema.safeParse(after.llm).success).toBe(true);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("a user-owned os-beta profile is never touched", () => {
|
|
341
|
+
process.env.IS_PLATFORM = "true";
|
|
342
|
+
writeConfig({
|
|
343
|
+
llm: {
|
|
344
|
+
default: { provider: "anthropic" },
|
|
345
|
+
profiles: {
|
|
346
|
+
"os-beta": {
|
|
347
|
+
source: "user",
|
|
348
|
+
label: "Mine",
|
|
349
|
+
provider: "anthropic",
|
|
350
|
+
model: "claude-x",
|
|
351
|
+
provider_connection: "anthropic-personal",
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
profileOrder: ["os-beta"],
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
invalidateConfigCache();
|
|
358
|
+
const before = readFileSync(CONFIG_PATH, "utf-8");
|
|
359
|
+
|
|
360
|
+
setOverridesForTesting({ "os-beta": true });
|
|
361
|
+
expect(reconcileFlagGatedProfiles()).toBe(false);
|
|
362
|
+
expect(readFileSync(CONFIG_PATH, "utf-8")).toBe(before);
|
|
363
|
+
|
|
364
|
+
setOverridesForTesting({ "os-beta": false });
|
|
365
|
+
expect(reconcileFlagGatedProfiles()).toBe(false);
|
|
366
|
+
expect(readFileSync(CONFIG_PATH, "utf-8")).toBe(before);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
@@ -103,7 +103,11 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
|
|
|
103
103
|
const entry = flag as Record<string, unknown>;
|
|
104
104
|
if (entry.scope !== "assistant" && entry.scope !== "both") continue;
|
|
105
105
|
if (typeof entry.key !== "string") continue;
|
|
106
|
-
if (
|
|
106
|
+
if (
|
|
107
|
+
typeof entry.defaultEnabled !== "boolean" &&
|
|
108
|
+
typeof entry.defaultEnabled !== "string"
|
|
109
|
+
)
|
|
110
|
+
continue;
|
|
107
111
|
|
|
108
112
|
result[entry.key as string] = {
|
|
109
113
|
defaultEnabled: entry.defaultEnabled,
|
|
@@ -178,6 +182,12 @@ const DEFAULT_INIT_RETRY_BACKOFFS_MS: readonly number[] = [
|
|
|
178
182
|
* tests preseed flag state via `setOverridesForTesting()` (in
|
|
179
183
|
* `__tests__/feature-flag-test-helpers.ts`) without the gateway IPC call
|
|
180
184
|
* clobbering their setup.
|
|
185
|
+
*
|
|
186
|
+
* Resolves `true` when the override cache is populated from the gateway and
|
|
187
|
+
* `false` when the cache is left unset (exhausted retries / unreachable
|
|
188
|
+
* gateway). Callers that mutate persisted state from flag values gate that
|
|
189
|
+
* work on a `true` result so a failed fetch never resolves a flag to its
|
|
190
|
+
* registry default and drops user state.
|
|
181
191
|
*/
|
|
182
192
|
export async function initFeatureFlagOverrides(options?: {
|
|
183
193
|
retryBackoffsMs?: readonly number[];
|
|
@@ -188,8 +198,8 @@ export async function initFeatureFlagOverrides(options?: {
|
|
|
188
198
|
* instead of blocking startup.
|
|
189
199
|
*/
|
|
190
200
|
callTimeoutMs?: number;
|
|
191
|
-
}): Promise<
|
|
192
|
-
if (isCachedFromGateway()) return;
|
|
201
|
+
}): Promise<boolean> {
|
|
202
|
+
if (isCachedFromGateway()) return true;
|
|
193
203
|
|
|
194
204
|
const backoffs = options?.retryBackoffsMs ?? DEFAULT_INIT_RETRY_BACKOFFS_MS;
|
|
195
205
|
const callTimeoutMs = options?.callTimeoutMs;
|
|
@@ -205,7 +215,7 @@ export async function initFeatureFlagOverrides(options?: {
|
|
|
205
215
|
// Re-check after the wait: a concurrent caller (e.g. a test using
|
|
206
216
|
// `setOverridesForTesting`) may have populated the cache while we
|
|
207
217
|
// were sleeping. Bail out so we don't clobber their setup.
|
|
208
|
-
if (isCachedFromGateway()) return;
|
|
218
|
+
if (isCachedFromGateway()) return true;
|
|
209
219
|
}
|
|
210
220
|
|
|
211
221
|
const gatewayOverrides = await fetchOverridesFromGateway(callTimeoutMs);
|
|
@@ -217,7 +227,7 @@ export async function initFeatureFlagOverrides(options?: {
|
|
|
217
227
|
"Feature flag overrides loaded from gateway after retry",
|
|
218
228
|
);
|
|
219
229
|
}
|
|
220
|
-
return;
|
|
230
|
+
return true;
|
|
221
231
|
}
|
|
222
232
|
}
|
|
223
233
|
|
|
@@ -230,6 +240,7 @@ export async function initFeatureFlagOverrides(options?: {
|
|
|
230
240
|
"Feature flag overrides empty after all retries; falling back to registry defaults and fail-closed undeclared flags",
|
|
231
241
|
);
|
|
232
242
|
}
|
|
243
|
+
return false;
|
|
233
244
|
}
|
|
234
245
|
|
|
235
246
|
/**
|
|
@@ -262,10 +273,14 @@ export function clearFeatureFlagOverridesCache(): void {
|
|
|
262
273
|
* retries (the gateway is known to be up because it just pushed an event).
|
|
263
274
|
* Called by the gateway flag listener when a `feature_flags_changed` event
|
|
264
275
|
* arrives.
|
|
276
|
+
*
|
|
277
|
+
* Resolves `true` when the cache was repopulated from the gateway, `false`
|
|
278
|
+
* when the fetch came back empty/failed — callers gate persisted-state
|
|
279
|
+
* reconciliation on a `true` result.
|
|
265
280
|
*/
|
|
266
|
-
export async function refreshOverridesFromGateway(): Promise<
|
|
281
|
+
export async function refreshOverridesFromGateway(): Promise<boolean> {
|
|
267
282
|
clearFeatureFlagOverridesCache();
|
|
268
|
-
|
|
283
|
+
return initFeatureFlagOverrides({ retryBackoffsMs: [] });
|
|
269
284
|
}
|
|
270
285
|
|
|
271
286
|
// ---------------------------------------------------------------------------
|