@vellumai/assistant 0.9.0 → 0.10.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +49 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
WorkflowRunCapError,
|
|
23
23
|
WorkflowRunManager,
|
|
24
24
|
type WorkflowRunManagerDeps,
|
|
25
|
-
WorkflowsDisabledError,
|
|
26
25
|
} from "./run-manager.js";
|
|
27
26
|
|
|
28
27
|
const TRUST: TrustContext = {
|
|
@@ -144,7 +143,6 @@ interface EngineResult {
|
|
|
144
143
|
* deterministically), an in-memory journal, and spies for broadcast + wake.
|
|
145
144
|
*/
|
|
146
145
|
function makeHarness(opts?: {
|
|
147
|
-
flagEnabled?: boolean;
|
|
148
146
|
maxConcurrentRuns?: number;
|
|
149
147
|
/** Custom engine impl; defaults to the deferred-resolver fake. */
|
|
150
148
|
engine?: WorkflowRunManagerDeps["executeWorkflow"];
|
|
@@ -190,7 +188,6 @@ function makeHarness(opts?: {
|
|
|
190
188
|
}) as unknown as WorkflowRunManagerDeps["leafRunner"],
|
|
191
189
|
journal: fake.journal,
|
|
192
190
|
getConfig: () => makeConfig(opts?.maxConcurrentRuns),
|
|
193
|
-
isFlagEnabled: () => opts?.flagEnabled ?? true,
|
|
194
191
|
wake: (async (wakeOpts) => {
|
|
195
192
|
wakes.push({
|
|
196
193
|
conversationId: wakeOpts.conversationId,
|
|
@@ -227,22 +224,6 @@ function makeHarness(opts?: {
|
|
|
227
224
|
};
|
|
228
225
|
}
|
|
229
226
|
|
|
230
|
-
describe("WorkflowRunManager.start — flag gate", () => {
|
|
231
|
-
test("flag off → start throws and the engine is never invoked", () => {
|
|
232
|
-
const h = makeHarness({ flagEnabled: false });
|
|
233
|
-
expect(() =>
|
|
234
|
-
h.manager.start({
|
|
235
|
-
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
236
|
-
args: {},
|
|
237
|
-
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
238
|
-
trustContext: TRUST,
|
|
239
|
-
}),
|
|
240
|
-
).toThrow(WorkflowsDisabledError);
|
|
241
|
-
expect(h.executeCalls).toHaveLength(0);
|
|
242
|
-
expect(h.fake.rows.size).toBe(0);
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
|
|
246
227
|
describe("WorkflowRunManager.start — concurrent-run cap", () => {
|
|
247
228
|
test("the (N+1)th concurrent start is rejected", () => {
|
|
248
229
|
const h = makeHarness({ maxConcurrentRuns: 2 });
|
|
@@ -311,6 +292,7 @@ describe("WorkflowRunManager.abort", () => {
|
|
|
311
292
|
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
312
293
|
args: {},
|
|
313
294
|
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
295
|
+
conversationId: "conv-1",
|
|
314
296
|
trustContext: TRUST,
|
|
315
297
|
});
|
|
316
298
|
|
|
@@ -337,6 +319,7 @@ describe("WorkflowRunManager — progress events", () => {
|
|
|
337
319
|
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
338
320
|
args: {},
|
|
339
321
|
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
322
|
+
conversationId: "conv-1",
|
|
340
323
|
label: "My Flow",
|
|
341
324
|
trustContext: TRUST,
|
|
342
325
|
});
|
|
@@ -349,14 +332,164 @@ describe("WorkflowRunManager — progress events", () => {
|
|
|
349
332
|
expect(progress).toHaveLength(2);
|
|
350
333
|
expect(progress[0]).toMatchObject({
|
|
351
334
|
type: "workflow_progress",
|
|
335
|
+
conversationId: "conv-1",
|
|
352
336
|
label: "My Flow",
|
|
353
337
|
phase: "Gathering",
|
|
354
338
|
});
|
|
355
339
|
expect(progress[1]).toMatchObject({
|
|
356
340
|
type: "workflow_progress",
|
|
341
|
+
conversationId: "conv-1",
|
|
357
342
|
message: "step done",
|
|
358
343
|
});
|
|
359
344
|
});
|
|
345
|
+
|
|
346
|
+
test("a run with no conversation broadcasts progress unscoped (no conversationId)", () => {
|
|
347
|
+
const h = makeHarness();
|
|
348
|
+
h.manager.start({
|
|
349
|
+
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
350
|
+
args: {},
|
|
351
|
+
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
352
|
+
label: "My Flow",
|
|
353
|
+
trustContext: TRUST,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const onProgress = h.executeCalls[0]!.onProgress!;
|
|
357
|
+
onProgress({ type: "phase", title: "Gathering" });
|
|
358
|
+
onProgress({ type: "log", message: "step done" });
|
|
359
|
+
|
|
360
|
+
// `workflow_progress` is a pre-existing event; it still broadcasts for a
|
|
361
|
+
// conversationless run, just without a `conversationId` (unscoped).
|
|
362
|
+
const progress = h.broadcasts.filter((b) => b.type === "workflow_progress");
|
|
363
|
+
expect(progress).toHaveLength(2);
|
|
364
|
+
expect(progress[0]).toMatchObject({
|
|
365
|
+
type: "workflow_progress",
|
|
366
|
+
label: "My Flow",
|
|
367
|
+
phase: "Gathering",
|
|
368
|
+
});
|
|
369
|
+
expect(progress[0]).not.toHaveProperty("conversationId");
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test("onLeaf start/finish are republished as scoped leaf events", () => {
|
|
373
|
+
const fakeEngine: WorkflowRunManagerDeps["executeWorkflow"] = (options) => {
|
|
374
|
+
options.onLeaf?.({
|
|
375
|
+
type: "leaf_started",
|
|
376
|
+
seq: 1,
|
|
377
|
+
label: "Research",
|
|
378
|
+
phase: "Gather",
|
|
379
|
+
promptSummary: "look it up",
|
|
380
|
+
});
|
|
381
|
+
options.onLeaf?.({
|
|
382
|
+
type: "leaf_finished",
|
|
383
|
+
seq: 1,
|
|
384
|
+
status: "completed",
|
|
385
|
+
label: "Research",
|
|
386
|
+
inputTokens: 30,
|
|
387
|
+
outputTokens: 12,
|
|
388
|
+
resultSummary: "found it",
|
|
389
|
+
});
|
|
390
|
+
return new Promise(() => {}) as ReturnType<
|
|
391
|
+
WorkflowRunManagerDeps["executeWorkflow"]
|
|
392
|
+
>;
|
|
393
|
+
};
|
|
394
|
+
const h = makeHarness({ engine: fakeEngine });
|
|
395
|
+
|
|
396
|
+
const { runId } = h.manager.start({
|
|
397
|
+
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
398
|
+
args: {},
|
|
399
|
+
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
400
|
+
conversationId: "conv-1",
|
|
401
|
+
trustContext: TRUST,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const started = h.broadcasts.find(
|
|
405
|
+
(b) => b.type === "workflow_leaf_started",
|
|
406
|
+
);
|
|
407
|
+
expect(started).toMatchObject({
|
|
408
|
+
type: "workflow_leaf_started",
|
|
409
|
+
runId,
|
|
410
|
+
conversationId: "conv-1",
|
|
411
|
+
seq: 1,
|
|
412
|
+
label: "Research",
|
|
413
|
+
phase: "Gather",
|
|
414
|
+
promptSummary: "look it up",
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
const finished = h.broadcasts.find(
|
|
418
|
+
(b) => b.type === "workflow_leaf_finished",
|
|
419
|
+
);
|
|
420
|
+
expect(finished).toMatchObject({
|
|
421
|
+
type: "workflow_leaf_finished",
|
|
422
|
+
runId,
|
|
423
|
+
conversationId: "conv-1",
|
|
424
|
+
seq: 1,
|
|
425
|
+
status: "completed",
|
|
426
|
+
label: "Research",
|
|
427
|
+
inputTokens: 30,
|
|
428
|
+
outputTokens: 12,
|
|
429
|
+
resultSummary: "found it",
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test("a run with no conversation gets no onLeaf callback", () => {
|
|
434
|
+
const h = makeHarness();
|
|
435
|
+
h.manager.start({
|
|
436
|
+
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
437
|
+
args: {},
|
|
438
|
+
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
439
|
+
trustContext: TRUST,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
expect(h.executeCalls[0]!.onLeaf).toBeUndefined();
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
describe("WorkflowRunManager.start — workflow_started", () => {
|
|
447
|
+
test("start with a conversation broadcasts workflow_started carrying toolUseId", () => {
|
|
448
|
+
const h = makeHarness();
|
|
449
|
+
const { runId } = h.manager.start({
|
|
450
|
+
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
451
|
+
args: {},
|
|
452
|
+
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
453
|
+
conversationId: "conv-1",
|
|
454
|
+
toolUseId: "toolu-abc",
|
|
455
|
+
label: "My Flow",
|
|
456
|
+
trustContext: TRUST,
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const started = h.broadcasts.find((b) => b.type === "workflow_started");
|
|
460
|
+
expect(started).toMatchObject({
|
|
461
|
+
type: "workflow_started",
|
|
462
|
+
runId,
|
|
463
|
+
conversationId: "conv-1",
|
|
464
|
+
toolUseId: "toolu-abc",
|
|
465
|
+
label: "My Flow",
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
test("start without a conversation broadcasts no workflow_started", () => {
|
|
470
|
+
const h = makeHarness();
|
|
471
|
+
h.manager.start({
|
|
472
|
+
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
473
|
+
args: {},
|
|
474
|
+
manifest: { tools: [], hostFunctions: [], persona: false },
|
|
475
|
+
trustContext: TRUST,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
expect(h.broadcasts.some((b) => b.type === "workflow_started")).toBe(false);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test("resume never broadcasts workflow_started", () => {
|
|
482
|
+
const h = makeHarness({});
|
|
483
|
+
seedRun(h, {
|
|
484
|
+
id: "run-x",
|
|
485
|
+
status: "interrupted",
|
|
486
|
+
conversationId: "conv-1",
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
h.manager.resume("run-x");
|
|
490
|
+
|
|
491
|
+
expect(h.broadcasts.some((b) => b.type === "workflow_started")).toBe(false);
|
|
492
|
+
});
|
|
360
493
|
});
|
|
361
494
|
|
|
362
495
|
describe("WorkflowRunManager — completion", () => {
|
|
@@ -383,6 +516,7 @@ describe("WorkflowRunManager — completion", () => {
|
|
|
383
516
|
expect(completed).toMatchObject({
|
|
384
517
|
type: "workflow_completed",
|
|
385
518
|
runId,
|
|
519
|
+
conversationId: "conv-1",
|
|
386
520
|
status: "completed",
|
|
387
521
|
agentsSpawned: 4,
|
|
388
522
|
inputTokens: 100,
|
|
@@ -437,7 +571,7 @@ describe("WorkflowRunManager — completion", () => {
|
|
|
437
571
|
expect(h.manager.status(runId)?.result).toBe(big);
|
|
438
572
|
});
|
|
439
573
|
|
|
440
|
-
test("no conversationId →
|
|
574
|
+
test("no conversationId → pre-existing events broadcast unscoped; UI events and wake are gated", async () => {
|
|
441
575
|
const h = makeHarness();
|
|
442
576
|
h.manager.start({
|
|
443
577
|
scriptSource: "export const meta = { name: 'x', description: 'y' }",
|
|
@@ -454,8 +588,23 @@ describe("WorkflowRunManager — completion", () => {
|
|
|
454
588
|
outputTokens: 1,
|
|
455
589
|
});
|
|
456
590
|
|
|
457
|
-
|
|
458
|
-
|
|
591
|
+
// The pre-existing terminal event still broadcasts (unscoped) so raw SSE
|
|
592
|
+
// listeners and conversationless scheduled runs are still surfaced.
|
|
593
|
+
const completed = h.broadcasts.find((b) => b.type === "workflow_completed");
|
|
594
|
+
expect(completed).toMatchObject({
|
|
595
|
+
type: "workflow_completed",
|
|
596
|
+
status: "completed",
|
|
597
|
+
});
|
|
598
|
+
expect(completed).not.toHaveProperty("conversationId");
|
|
599
|
+
|
|
600
|
+
// The conversation-only signals stay gated: no `workflow_started`, no leaf
|
|
601
|
+
// events, and no completion wake without an originating conversation.
|
|
602
|
+
expect(h.broadcasts.some((b) => b.type === "workflow_started")).toBe(false);
|
|
603
|
+
expect(h.broadcasts.some((b) => b.type === "workflow_leaf_started")).toBe(
|
|
604
|
+
false,
|
|
605
|
+
);
|
|
606
|
+
expect(h.broadcasts.some((b) => b.type === "workflow_leaf_finished")).toBe(
|
|
607
|
+
false,
|
|
459
608
|
);
|
|
460
609
|
expect(h.wakes).toHaveLength(0);
|
|
461
610
|
});
|
|
@@ -702,10 +851,4 @@ describe("WorkflowRunManager.resume", () => {
|
|
|
702
851
|
|
|
703
852
|
expect(() => h.manager.resume("run-x")).toThrow(WorkflowRunCapError);
|
|
704
853
|
});
|
|
705
|
-
|
|
706
|
-
test("resume is rejected when the flag is off", () => {
|
|
707
|
-
const h = makeHarness({ flagEnabled: false });
|
|
708
|
-
seedRun(h, { id: "run-x", status: "interrupted" });
|
|
709
|
-
expect(() => h.manager.resume("run-x")).toThrow(WorkflowsDisabledError);
|
|
710
|
-
});
|
|
711
854
|
});
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* and routes (later PRs) drive. It owns everything the raw {@link executeWorkflow}
|
|
6
6
|
* engine deliberately does NOT:
|
|
7
7
|
*
|
|
8
|
-
* - **Feature-flag gate.** `start` hard-fails with {@link WorkflowsDisabledError}
|
|
9
|
-
* when the `workflows` flag is off, BEFORE any engine code path is reachable.
|
|
10
8
|
* - **Concurrent-run cap.** At most `config.workflows.maxConcurrentRuns` runs
|
|
11
9
|
* may be in flight; the (N+1)th `start` throws {@link WorkflowRunCapError}.
|
|
12
10
|
* - **Async launch.** `start` resolves capabilities, creates the journal run
|
|
@@ -28,7 +26,6 @@
|
|
|
28
26
|
|
|
29
27
|
import { createHash, randomUUID } from "node:crypto";
|
|
30
28
|
|
|
31
|
-
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
32
29
|
import { getConfig } from "../config/loader.js";
|
|
33
30
|
import type { AssistantConfig } from "../config/schema.js";
|
|
34
31
|
import {
|
|
@@ -58,15 +55,6 @@ const log = getLogger("workflow-run-manager");
|
|
|
58
55
|
/** Source tag for the completion wake (shows up in the wake's structured log). */
|
|
59
56
|
const WORKFLOW_WAKE_SOURCE = "workflow_completed";
|
|
60
57
|
|
|
61
|
-
/** Thrown by `start` when the `workflows` feature flag is disabled. */
|
|
62
|
-
export class WorkflowsDisabledError extends Error {
|
|
63
|
-
readonly code = "workflows_disabled" as const;
|
|
64
|
-
constructor() {
|
|
65
|
-
super("Workflows are not enabled.");
|
|
66
|
-
this.name = "WorkflowsDisabledError";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
58
|
/** Thrown by `start` when the concurrent-run cap is already reached. */
|
|
71
59
|
export class WorkflowRunCapError extends Error {
|
|
72
60
|
readonly code = "workflow_run_cap_exceeded" as const;
|
|
@@ -126,6 +114,11 @@ interface StartWorkflowCommon {
|
|
|
126
114
|
* scheduled run with no chat surface) — the summary is then events-only.
|
|
127
115
|
*/
|
|
128
116
|
conversationId?: string;
|
|
117
|
+
/**
|
|
118
|
+
* The `skill_execute` tool-use block id that launched this run; forwarded on
|
|
119
|
+
* `workflow_started` so the client anchors the inline card to the spawn call.
|
|
120
|
+
*/
|
|
121
|
+
toolUseId?: string;
|
|
129
122
|
/** Human-readable label for display; defaults to the run id. */
|
|
130
123
|
label?: string;
|
|
131
124
|
/** Trust/auth context forwarded to every leaf. */
|
|
@@ -141,7 +134,6 @@ export interface WorkflowRunManagerDeps {
|
|
|
141
134
|
leafRunner: typeof runLeaf;
|
|
142
135
|
journal: typeof journalStore;
|
|
143
136
|
getConfig: () => AssistantConfig;
|
|
144
|
-
isFlagEnabled: (config: AssistantConfig) => boolean;
|
|
145
137
|
wake: typeof wakeAgentForOpportunity;
|
|
146
138
|
broadcast: typeof broadcastMessage;
|
|
147
139
|
newRunId: () => string;
|
|
@@ -155,8 +147,6 @@ function defaultDeps(): WorkflowRunManagerDeps {
|
|
|
155
147
|
leafRunner: runLeaf,
|
|
156
148
|
journal: journalStore,
|
|
157
149
|
getConfig,
|
|
158
|
-
isFlagEnabled: (config) =>
|
|
159
|
-
isAssistantFeatureFlagEnabled("workflows", config),
|
|
160
150
|
wake: wakeAgentForOpportunity,
|
|
161
151
|
broadcast: broadcastMessage,
|
|
162
152
|
newRunId: () => randomUUID(),
|
|
@@ -178,18 +168,14 @@ export class WorkflowRunManager {
|
|
|
178
168
|
}
|
|
179
169
|
|
|
180
170
|
/**
|
|
181
|
-
* Launch a workflow run. Gates on the
|
|
182
|
-
*
|
|
171
|
+
* Launch a workflow run. Gates on the concurrent-run cap (which throws before
|
|
172
|
+
* any engine code is reachable), resolves capabilities,
|
|
183
173
|
* creates the journal run row, and kicks off {@link executeWorkflow}
|
|
184
174
|
* asynchronously. Returns the `runId` immediately — completion is surfaced via
|
|
185
175
|
* events and a conversation wake.
|
|
186
176
|
*/
|
|
187
177
|
start(opts: StartWorkflowOptions): { runId: string } {
|
|
188
178
|
const config = this.deps.getConfig();
|
|
189
|
-
if (!this.deps.isFlagEnabled(config)) {
|
|
190
|
-
throw new WorkflowsDisabledError();
|
|
191
|
-
}
|
|
192
|
-
|
|
193
179
|
const limit = config.workflows.maxConcurrentRuns;
|
|
194
180
|
if (this.inflight.size >= limit) {
|
|
195
181
|
throw new WorkflowRunCapError(limit);
|
|
@@ -234,6 +220,19 @@ export class WorkflowRunManager {
|
|
|
234
220
|
const controller = new AbortController();
|
|
235
221
|
this.inflight.set(runId, controller);
|
|
236
222
|
|
|
223
|
+
// Announce the launch only when there's a conversation to scope it to (the
|
|
224
|
+
// same gate the in-flight/terminal broadcasts use). `resume` never emits
|
|
225
|
+
// this — the run already announced itself on its original `start`.
|
|
226
|
+
if (opts.conversationId) {
|
|
227
|
+
this.deps.broadcast({
|
|
228
|
+
type: "workflow_started",
|
|
229
|
+
runId,
|
|
230
|
+
conversationId: opts.conversationId,
|
|
231
|
+
...(opts.toolUseId ? { toolUseId: opts.toolUseId } : {}),
|
|
232
|
+
label,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
237
236
|
// Fire-and-forget: the engine owns its own try/catch and always finishes
|
|
238
237
|
// the run row. We never await it — `start` must return synchronously.
|
|
239
238
|
void this.runToCompletion(
|
|
@@ -282,9 +281,6 @@ export class WorkflowRunManager {
|
|
|
282
281
|
*/
|
|
283
282
|
resume(runId: string): { runId: string } {
|
|
284
283
|
const config = this.deps.getConfig();
|
|
285
|
-
if (!this.deps.isFlagEnabled(config)) {
|
|
286
|
-
throw new WorkflowsDisabledError();
|
|
287
|
-
}
|
|
288
284
|
|
|
289
285
|
if (this.inflight.has(runId)) {
|
|
290
286
|
throw new WorkflowResumeNotPossibleError(runId, "in_flight");
|
|
@@ -398,6 +394,14 @@ export class WorkflowRunManager {
|
|
|
398
394
|
ctx: RunContext,
|
|
399
395
|
): Promise<void> {
|
|
400
396
|
const config = this.deps.getConfig();
|
|
397
|
+
// `workflow_progress` / `workflow_completed` always broadcast: a run with no
|
|
398
|
+
// originating conversation emits them unscoped (no `conversationId`) for raw
|
|
399
|
+
// SSE listeners and the DB record, preserving the pre-scoping contract that
|
|
400
|
+
// surfaces conversationless scheduled runs. The conversation-only signals —
|
|
401
|
+
// `workflow_started`, the `onLeaf` leaf events, and the completion wake —
|
|
402
|
+
// stay gated on `conversationId` since they exist solely to drive the inline
|
|
403
|
+
// workflow card in that conversation.
|
|
404
|
+
const conversationId = ctx.conversationId;
|
|
401
405
|
try {
|
|
402
406
|
const result = await this.deps.executeWorkflow({
|
|
403
407
|
runId,
|
|
@@ -415,6 +419,7 @@ export class WorkflowRunManager {
|
|
|
415
419
|
this.deps.broadcast({
|
|
416
420
|
type: "workflow_progress",
|
|
417
421
|
runId,
|
|
422
|
+
...(conversationId ? { conversationId } : {}),
|
|
418
423
|
label,
|
|
419
424
|
agentsSpawned,
|
|
420
425
|
...(event.type === "phase"
|
|
@@ -422,6 +427,41 @@ export class WorkflowRunManager {
|
|
|
422
427
|
: { message: event.message }),
|
|
423
428
|
});
|
|
424
429
|
},
|
|
430
|
+
...(conversationId
|
|
431
|
+
? {
|
|
432
|
+
onLeaf: (event) => {
|
|
433
|
+
if (event.type === "leaf_started") {
|
|
434
|
+
this.deps.broadcast({
|
|
435
|
+
type: "workflow_leaf_started",
|
|
436
|
+
runId,
|
|
437
|
+
conversationId,
|
|
438
|
+
seq: event.seq,
|
|
439
|
+
...(event.label !== undefined
|
|
440
|
+
? { label: event.label }
|
|
441
|
+
: {}),
|
|
442
|
+
...(event.phase !== undefined
|
|
443
|
+
? { phase: event.phase }
|
|
444
|
+
: {}),
|
|
445
|
+
promptSummary: event.promptSummary,
|
|
446
|
+
});
|
|
447
|
+
} else {
|
|
448
|
+
this.deps.broadcast({
|
|
449
|
+
type: "workflow_leaf_finished",
|
|
450
|
+
runId,
|
|
451
|
+
conversationId,
|
|
452
|
+
seq: event.seq,
|
|
453
|
+
status: event.status,
|
|
454
|
+
...(event.label !== undefined
|
|
455
|
+
? { label: event.label }
|
|
456
|
+
: {}),
|
|
457
|
+
inputTokens: event.inputTokens,
|
|
458
|
+
outputTokens: event.outputTokens,
|
|
459
|
+
resultSummary: event.resultSummary,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
}
|
|
464
|
+
: {}),
|
|
425
465
|
});
|
|
426
466
|
|
|
427
467
|
const summary = buildCompletionSummary(
|
|
@@ -434,6 +474,7 @@ export class WorkflowRunManager {
|
|
|
434
474
|
this.deps.broadcast({
|
|
435
475
|
type: "workflow_completed",
|
|
436
476
|
runId,
|
|
477
|
+
...(conversationId ? { conversationId } : {}),
|
|
437
478
|
status: result.status,
|
|
438
479
|
agentsSpawned: result.agentsSpawned,
|
|
439
480
|
inputTokens: result.inputTokens,
|
|
@@ -495,6 +536,7 @@ interface RunContext {
|
|
|
495
536
|
const VALID_TRUST_CLASSES: ReadonlySet<TrustContext["trustClass"]> = new Set([
|
|
496
537
|
"guardian",
|
|
497
538
|
"trusted_contact",
|
|
539
|
+
"unverified_contact",
|
|
498
540
|
"unknown",
|
|
499
541
|
]);
|
|
500
542
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { MigrationRunContext, WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Switch brand-new assistants onto memory-v3 (the live injected memory source)
|
|
8
|
+
* at creation by persisting `memory.v3.live = true`.
|
|
9
|
+
*
|
|
10
|
+
* The schema default is `false`, so existing assistants keep running v2 on
|
|
11
|
+
* upgrade — this migration writes the value only for freshly-created
|
|
12
|
+
* workspaces. The value gates v3 via `isMemoryV3Live`; existing assistants are
|
|
13
|
+
* switched on deliberately, never automatically. Covers every surface (local,
|
|
14
|
+
* Docker, managed) uniformly because all run workspace migrations on first boot.
|
|
15
|
+
*/
|
|
16
|
+
export const enableMemoryV3LiveForNewWorkspacesMigration: WorkspaceMigration = {
|
|
17
|
+
id: "105-enable-memory-v3-live-for-new-workspaces",
|
|
18
|
+
description:
|
|
19
|
+
"Persist memory.v3.live=true for brand-new workspaces so new assistants run memory-v3 from creation",
|
|
20
|
+
|
|
21
|
+
run(workspaceDir: string, ctx?: MigrationRunContext): void {
|
|
22
|
+
// Only switch new assistants on. Existing workspaces fall through to the
|
|
23
|
+
// schema default (false) and keep running v2 until enabled explicitly.
|
|
24
|
+
// Without a context (older callers) treat the workspace as existing.
|
|
25
|
+
if (!ctx?.isNewWorkspace) return;
|
|
26
|
+
|
|
27
|
+
const configPath = join(workspaceDir, "config.json");
|
|
28
|
+
|
|
29
|
+
// A fresh workspace may have no config.json yet (schema defaults are
|
|
30
|
+
// applied in memory at load); create one so the live default persists.
|
|
31
|
+
let config: Record<string, unknown> = {};
|
|
32
|
+
if (existsSync(configPath)) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
35
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
36
|
+
config = raw as Record<string, unknown>;
|
|
37
|
+
} catch {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (config.memory === undefined) config.memory = {};
|
|
43
|
+
const memory = config.memory;
|
|
44
|
+
if (!memory || typeof memory !== "object" || Array.isArray(memory)) return;
|
|
45
|
+
const memoryConfig = memory as Record<string, unknown>;
|
|
46
|
+
|
|
47
|
+
if (memoryConfig.v3 === undefined) memoryConfig.v3 = {};
|
|
48
|
+
const v3 = memoryConfig.v3;
|
|
49
|
+
if (!v3 || typeof v3 !== "object" || Array.isArray(v3)) return;
|
|
50
|
+
const v3Config = v3 as Record<string, unknown>;
|
|
51
|
+
|
|
52
|
+
// Respect an explicit value already present (idempotent re-runs, or a
|
|
53
|
+
// hatch-time override that set it deliberately).
|
|
54
|
+
if ("live" in v3Config) return;
|
|
55
|
+
|
|
56
|
+
v3Config.live = true;
|
|
57
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
down(_workspaceDir: string): void {
|
|
61
|
+
// Forward-only: cannot distinguish a user's explicit choice from this seed.
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Drops the `collectUsageData` config key (gating is governed by the platform
|
|
8
|
+
* `share_analytics` consent). An explicit local opt-out (`collectUsageData:
|
|
9
|
+
* false`) is preserved as `legacyTelemetryOptOut: true` so telemetry stays off
|
|
10
|
+
* regardless of platform consent, which defaults to opt-in and cannot be
|
|
11
|
+
* written by the daemon. A `true`/non-false value carries no opt-out intent, so
|
|
12
|
+
* the key is removed without setting the marker. The schema strips unknown keys
|
|
13
|
+
* on the next save, so a stale value is otherwise harmless.
|
|
14
|
+
*/
|
|
15
|
+
export const dropCollectUsageDataMigration: WorkspaceMigration = {
|
|
16
|
+
id: "106-drop-collect-usage-data",
|
|
17
|
+
description:
|
|
18
|
+
"Drop collectUsageData; preserve an explicit opt-out as legacyTelemetryOptOut (telemetry is gated by platform share_analytics consent)",
|
|
19
|
+
run(workspaceDir: string): void {
|
|
20
|
+
const configPath = join(workspaceDir, "config.json");
|
|
21
|
+
if (!existsSync(configPath)) return;
|
|
22
|
+
|
|
23
|
+
let config: Record<string, unknown>;
|
|
24
|
+
try {
|
|
25
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
26
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
27
|
+
config = raw as Record<string, unknown>;
|
|
28
|
+
} catch {
|
|
29
|
+
return; // Malformed config — skip
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!("collectUsageData" in config)) return;
|
|
33
|
+
|
|
34
|
+
// An explicit opt-out is preserved as a fail-closed marker; any non-false
|
|
35
|
+
// value carries no opt-out intent.
|
|
36
|
+
if (config.collectUsageData === false) {
|
|
37
|
+
config.legacyTelemetryOptOut = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
delete config.collectUsageData;
|
|
41
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
42
|
+
},
|
|
43
|
+
down(_workspaceDir: string): void {
|
|
44
|
+
// No-op: the forward migration only removes a key and sets a fail-closed
|
|
45
|
+
// marker, so there is nothing to restore.
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Drops the `sendDiagnostics` config key (crash reporting is governed by the
|
|
8
|
+
* platform `share_diagnostics` consent). An explicit local opt-out
|
|
9
|
+
* (`sendDiagnostics: false`) is preserved as `legacyDiagnosticsOptOut: true` so
|
|
10
|
+
* Sentry stays off regardless of platform consent, which defaults to opt-in and
|
|
11
|
+
* cannot be written by the daemon. A `true`/non-false value carries no opt-out
|
|
12
|
+
* intent, so the key is removed without setting the marker. The schema strips
|
|
13
|
+
* unknown keys on the next save, so a stale value is otherwise harmless.
|
|
14
|
+
*/
|
|
15
|
+
export const dropSendDiagnosticsMigration: WorkspaceMigration = {
|
|
16
|
+
id: "107-drop-send-diagnostics",
|
|
17
|
+
description:
|
|
18
|
+
"Drop sendDiagnostics; preserve an explicit opt-out as legacyDiagnosticsOptOut (crash reporting is gated by platform share_diagnostics consent)",
|
|
19
|
+
run(workspaceDir: string): void {
|
|
20
|
+
const configPath = join(workspaceDir, "config.json");
|
|
21
|
+
if (!existsSync(configPath)) return;
|
|
22
|
+
|
|
23
|
+
let config: Record<string, unknown>;
|
|
24
|
+
try {
|
|
25
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
26
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
27
|
+
config = raw as Record<string, unknown>;
|
|
28
|
+
} catch {
|
|
29
|
+
return; // Malformed config — skip
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!("sendDiagnostics" in config)) return;
|
|
33
|
+
|
|
34
|
+
// An explicit opt-out is preserved as a fail-closed marker; any non-false
|
|
35
|
+
// value carries no opt-out intent.
|
|
36
|
+
if (config.sendDiagnostics === false) {
|
|
37
|
+
config.legacyDiagnosticsOptOut = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
delete config.sendDiagnostics;
|
|
41
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
42
|
+
},
|
|
43
|
+
down(_workspaceDir: string): void {
|
|
44
|
+
// No-op: the forward migration only removes a key and sets a fail-closed
|
|
45
|
+
// marker, so there is nothing to restore.
|
|
46
|
+
},
|
|
47
|
+
};
|