@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
|
@@ -13,7 +13,7 @@ export interface TrustContext {
|
|
|
13
13
|
/** Channel through which the inbound message arrived. */
|
|
14
14
|
sourceChannel: ChannelId;
|
|
15
15
|
/** Trust classification -- see {@link TrustClass} for semantics. */
|
|
16
|
-
trustClass:
|
|
16
|
+
trustClass: TrustClass;
|
|
17
17
|
/** Chat/conversation ID for delivering guardian notifications. */
|
|
18
18
|
guardianChatId?: string;
|
|
19
19
|
/** Canonical external user ID of the guardian for this (assistant, channel) binding. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { getConfig } from "../config/loader.js";
|
|
2
1
|
import {
|
|
3
2
|
recordToolInvocation,
|
|
4
3
|
type ToolInvocationRecord,
|
|
5
4
|
} from "../memory/tool-usage-store.js";
|
|
5
|
+
import { getCachedShareAnalytics } from "../platform/consent-cache.js";
|
|
6
6
|
import { redactJsonStringLeaves } from "../security/redact-json.js";
|
|
7
7
|
import { redactSecrets } from "../security/secret-scanner.js";
|
|
8
8
|
import {
|
|
@@ -168,7 +168,7 @@ function telemetryColumns(
|
|
|
168
168
|
rawInput: string,
|
|
169
169
|
resultBytes: number,
|
|
170
170
|
): TelemetryColumns {
|
|
171
|
-
if (!
|
|
171
|
+
if (!getCachedShareAnalytics()) return NULL_TELEMETRY_COLUMNS;
|
|
172
172
|
return {
|
|
173
173
|
argBytes: event.inputBytes ?? Buffer.byteLength(rawInput, "utf8"),
|
|
174
174
|
resultBytes,
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { type FeedItem } from "../api/responses/home.js";
|
|
4
|
+
import {
|
|
5
|
+
classifyConversationSource,
|
|
6
|
+
enrichFeedItemsWithSource,
|
|
7
|
+
type FeedSourceEnrichmentDeps,
|
|
8
|
+
} from "./feed-source-enrichment.js";
|
|
9
|
+
|
|
10
|
+
function makeItem(partial: Partial<FeedItem> & Pick<FeedItem, "id">): FeedItem {
|
|
11
|
+
return {
|
|
12
|
+
type: "notification",
|
|
13
|
+
priority: 50,
|
|
14
|
+
summary: "summary",
|
|
15
|
+
timestamp: "2026-06-18T00:00:00.000Z",
|
|
16
|
+
createdAt: "2026-06-18T00:00:00.000Z",
|
|
17
|
+
status: "new",
|
|
18
|
+
...partial,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("classifyConversationSource", () => {
|
|
23
|
+
it("maps each known producer source to its coarse type", () => {
|
|
24
|
+
expect(classifyConversationSource("heartbeat")).toBe("heartbeat");
|
|
25
|
+
expect(classifyConversationSource("memory-retrospective")).toBe(
|
|
26
|
+
"memory_consolidation",
|
|
27
|
+
);
|
|
28
|
+
expect(classifyConversationSource("memory-retrospective-fork")).toBe(
|
|
29
|
+
"memory_consolidation",
|
|
30
|
+
);
|
|
31
|
+
expect(classifyConversationSource("schedule")).toBe("schedule");
|
|
32
|
+
expect(classifyConversationSource("auto-analysis")).toBe("auto_analysis");
|
|
33
|
+
expect(classifyConversationSource("user")).toBe("user");
|
|
34
|
+
expect(classifyConversationSource("home-feed")).toBe("user");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("falls through to 'other' for unknown / paired-delivery / empty sources", () => {
|
|
38
|
+
expect(classifyConversationSource("notification")).toBe("other");
|
|
39
|
+
expect(classifyConversationSource("something-else")).toBe("other");
|
|
40
|
+
expect(classifyConversationSource(null)).toBe("other");
|
|
41
|
+
expect(classifyConversationSource(undefined)).toBe("other");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("enrichFeedItemsWithSource", () => {
|
|
46
|
+
const deps = (
|
|
47
|
+
conv: Record<string, { source: string; scheduleJobId: string | null }>,
|
|
48
|
+
schedules: Record<string, string> = {},
|
|
49
|
+
): FeedSourceEnrichmentDeps => ({
|
|
50
|
+
getConversationRow: (id) => conv[id] ?? null,
|
|
51
|
+
getScheduleName: (id) => schedules[id] ?? null,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("classifies a heartbeat item with a static label", () => {
|
|
55
|
+
const [item] = enrichFeedItemsWithSource(
|
|
56
|
+
[makeItem({ id: "n1", conversationId: "c1" })],
|
|
57
|
+
deps({ c1: { source: "heartbeat", scheduleJobId: null } }),
|
|
58
|
+
);
|
|
59
|
+
expect(item.sourceType).toBe("heartbeat");
|
|
60
|
+
expect(item.sourceKey).toBe("heartbeat");
|
|
61
|
+
expect(item.sourceLabel).toBe("Heartbeat");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("classifies a memory-consolidation item", () => {
|
|
65
|
+
const [item] = enrichFeedItemsWithSource(
|
|
66
|
+
[makeItem({ id: "n2", conversationId: "c2" })],
|
|
67
|
+
deps({ c2: { source: "memory-retrospective", scheduleJobId: null } }),
|
|
68
|
+
);
|
|
69
|
+
expect(item.sourceType).toBe("memory_consolidation");
|
|
70
|
+
expect(item.sourceKey).toBe("memory_consolidation");
|
|
71
|
+
expect(item.sourceLabel).toBe("Memory consolidation");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("gives distinct keys and names to two different schedules", () => {
|
|
75
|
+
const [a, b] = enrichFeedItemsWithSource(
|
|
76
|
+
[
|
|
77
|
+
makeItem({ id: "n3", conversationId: "cA" }),
|
|
78
|
+
makeItem({ id: "n4", conversationId: "cB" }),
|
|
79
|
+
],
|
|
80
|
+
deps(
|
|
81
|
+
{
|
|
82
|
+
cA: { source: "schedule", scheduleJobId: "sched-A" },
|
|
83
|
+
cB: { source: "schedule", scheduleJobId: "sched-B" },
|
|
84
|
+
},
|
|
85
|
+
{ "sched-A": "Morning digest", "sched-B": "Evening recap" },
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
expect(a.sourceType).toBe("schedule");
|
|
89
|
+
expect(a.sourceKey).toBe("schedule:sched-A");
|
|
90
|
+
expect(a.sourceLabel).toBe("Morning digest");
|
|
91
|
+
// scheduleId recovered from the conversation row is surfaced in metadata.
|
|
92
|
+
expect(a.metadata?.scheduleId).toBe("sched-A");
|
|
93
|
+
|
|
94
|
+
expect(b.sourceKey).toBe("schedule:sched-B");
|
|
95
|
+
expect(b.sourceLabel).toBe("Evening recap");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("prefers an explicit metadata.scheduleId over the conversation row", () => {
|
|
99
|
+
const [item] = enrichFeedItemsWithSource(
|
|
100
|
+
[
|
|
101
|
+
makeItem({
|
|
102
|
+
id: "n5",
|
|
103
|
+
conversationId: "cX",
|
|
104
|
+
metadata: { scheduleId: "from-payload" },
|
|
105
|
+
}),
|
|
106
|
+
],
|
|
107
|
+
deps(
|
|
108
|
+
{ cX: { source: "schedule", scheduleJobId: "from-row" } },
|
|
109
|
+
{ "from-payload": "Payload schedule" },
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
expect(item.sourceKey).toBe("schedule:from-payload");
|
|
113
|
+
expect(item.sourceLabel).toBe("Payload schedule");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("labels a schedule with no resolvable name as 'Scheduled'", () => {
|
|
117
|
+
const [item] = enrichFeedItemsWithSource(
|
|
118
|
+
[makeItem({ id: "n6", conversationId: "cY" })],
|
|
119
|
+
deps({ cY: { source: "schedule", scheduleJobId: "gone" } }),
|
|
120
|
+
);
|
|
121
|
+
expect(item.sourceKey).toBe("schedule:gone");
|
|
122
|
+
expect(item.sourceLabel).toBe("Scheduled");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("classifies items with no source conversation as 'other'", () => {
|
|
126
|
+
const [item] = enrichFeedItemsWithSource(
|
|
127
|
+
[makeItem({ id: "n7" })],
|
|
128
|
+
deps({}),
|
|
129
|
+
);
|
|
130
|
+
expect(item.sourceType).toBe("other");
|
|
131
|
+
expect(item.sourceKey).toBe("other");
|
|
132
|
+
expect(item.sourceLabel).toBe("Other");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("resolves each distinct conversation at most once", () => {
|
|
136
|
+
const calls: string[] = [];
|
|
137
|
+
enrichFeedItemsWithSource(
|
|
138
|
+
[
|
|
139
|
+
makeItem({ id: "n8", conversationId: "dup" }),
|
|
140
|
+
makeItem({ id: "n9", conversationId: "dup" }),
|
|
141
|
+
],
|
|
142
|
+
{
|
|
143
|
+
getConversationRow: (id) => {
|
|
144
|
+
calls.push(id);
|
|
145
|
+
return { source: "heartbeat", scheduleJobId: null };
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
expect(calls).toEqual(["dup"]);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-time enrichment of home-feed items with their source-conversation
|
|
3
|
+
* classification.
|
|
4
|
+
*
|
|
5
|
+
* The activity feed surfaces notifications produced by many background
|
|
6
|
+
* flows — the periodic heartbeat, memory-consolidation passes, each
|
|
7
|
+
* recurring schedule, auto-analysis runs, etc. Clients let the user filter
|
|
8
|
+
* the feed by that producer, so every item is tagged with:
|
|
9
|
+
*
|
|
10
|
+
* - `sourceType` — coarse producer category.
|
|
11
|
+
* - `sourceKey` — stable filter id (`schedule:<id>` per schedule,
|
|
12
|
+
* otherwise the `sourceType`).
|
|
13
|
+
* - `sourceLabel` — human-readable display (a schedule's name, or a
|
|
14
|
+
* static label such as "Heartbeat").
|
|
15
|
+
*
|
|
16
|
+
* The classification is derived from the source conversation's `source`
|
|
17
|
+
* column (and, for schedules, its name) rather than persisted onto the
|
|
18
|
+
* feed item. Resolving at read time keeps labels fresh across schedule
|
|
19
|
+
* renames and retroactively classifies items written before this feature
|
|
20
|
+
* existed, with no migration.
|
|
21
|
+
*
|
|
22
|
+
* The lookups are injectable so the enrichment can be unit-tested without
|
|
23
|
+
* a live database; production callers use the defaults.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
type FeedItem,
|
|
28
|
+
type FeedItemSourceType,
|
|
29
|
+
} from "../api/responses/home.js";
|
|
30
|
+
import { AUTO_ANALYSIS_SOURCE } from "../memory/auto-analysis-constants.js";
|
|
31
|
+
import { getConversation } from "../memory/conversation-crud.js";
|
|
32
|
+
import {
|
|
33
|
+
MEMORY_RETROSPECTIVE_FORK_SOURCE,
|
|
34
|
+
MEMORY_RETROSPECTIVE_SOURCE,
|
|
35
|
+
} from "../memory/memory-retrospective-constants.js";
|
|
36
|
+
import { getSchedule } from "../schedule/schedule-store.js";
|
|
37
|
+
|
|
38
|
+
/** Minimal source-conversation shape the enrichment needs. */
|
|
39
|
+
interface ConversationSourceRow {
|
|
40
|
+
source: string;
|
|
41
|
+
scheduleJobId: string | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface FeedSourceEnrichmentDeps {
|
|
45
|
+
/** Resolve the source columns for a conversation, or null when missing. */
|
|
46
|
+
getConversationRow?: (id: string) => ConversationSourceRow | null;
|
|
47
|
+
/** Resolve a schedule's display name, or null when missing. */
|
|
48
|
+
getScheduleName?: (id: string) => string | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Map a conversation's `source` column to a coarse feed source type.
|
|
53
|
+
* Mechanical mapping over the known producer sources; anything else
|
|
54
|
+
* (including the broadcaster's paired `"notification"` delivery
|
|
55
|
+
* conversations) falls through to `"other"`.
|
|
56
|
+
*/
|
|
57
|
+
export function classifyConversationSource(
|
|
58
|
+
source: string | null | undefined,
|
|
59
|
+
): FeedItemSourceType {
|
|
60
|
+
switch (source) {
|
|
61
|
+
case "heartbeat":
|
|
62
|
+
return "heartbeat";
|
|
63
|
+
case MEMORY_RETROSPECTIVE_SOURCE:
|
|
64
|
+
case MEMORY_RETROSPECTIVE_FORK_SOURCE:
|
|
65
|
+
return "memory_consolidation";
|
|
66
|
+
case "schedule":
|
|
67
|
+
return "schedule";
|
|
68
|
+
case AUTO_ANALYSIS_SOURCE:
|
|
69
|
+
return "auto_analysis";
|
|
70
|
+
case "user":
|
|
71
|
+
case "home-feed":
|
|
72
|
+
return "user";
|
|
73
|
+
default:
|
|
74
|
+
return "other";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Static display labels for non-schedule source types. */
|
|
79
|
+
const STATIC_SOURCE_LABELS: Record<
|
|
80
|
+
Exclude<FeedItemSourceType, "schedule">,
|
|
81
|
+
string
|
|
82
|
+
> = {
|
|
83
|
+
heartbeat: "Heartbeat",
|
|
84
|
+
memory_consolidation: "Memory consolidation",
|
|
85
|
+
auto_analysis: "Auto-analysis",
|
|
86
|
+
user: "Conversation",
|
|
87
|
+
other: "Other",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function defaultGetConversationRow(id: string): ConversationSourceRow | null {
|
|
91
|
+
try {
|
|
92
|
+
const row = getConversation(id);
|
|
93
|
+
return row
|
|
94
|
+
? { source: row.source, scheduleJobId: row.scheduleJobId }
|
|
95
|
+
: null;
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function defaultGetScheduleName(id: string): string | null {
|
|
102
|
+
try {
|
|
103
|
+
return getSchedule(id)?.name ?? null;
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Return a copy of each feed item enriched with `sourceType`, `sourceKey`,
|
|
111
|
+
* and `sourceLabel`. Conversation and schedule lookups are memoized across
|
|
112
|
+
* the batch so each distinct source is resolved at most once.
|
|
113
|
+
*/
|
|
114
|
+
export function enrichFeedItemsWithSource(
|
|
115
|
+
items: FeedItem[],
|
|
116
|
+
deps: FeedSourceEnrichmentDeps = {},
|
|
117
|
+
): FeedItem[] {
|
|
118
|
+
if (items.length === 0) return items;
|
|
119
|
+
|
|
120
|
+
const getConversationRow =
|
|
121
|
+
deps.getConversationRow ?? defaultGetConversationRow;
|
|
122
|
+
const getScheduleName = deps.getScheduleName ?? defaultGetScheduleName;
|
|
123
|
+
|
|
124
|
+
const convCache = new Map<string, ConversationSourceRow | null>();
|
|
125
|
+
const resolveConv = (id: string): ConversationSourceRow | null => {
|
|
126
|
+
if (!convCache.has(id)) convCache.set(id, getConversationRow(id));
|
|
127
|
+
return convCache.get(id) ?? null;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const scheduleNameCache = new Map<string, string | null>();
|
|
131
|
+
const resolveScheduleName = (id: string): string | null => {
|
|
132
|
+
if (!scheduleNameCache.has(id)) {
|
|
133
|
+
scheduleNameCache.set(id, getScheduleName(id));
|
|
134
|
+
}
|
|
135
|
+
return scheduleNameCache.get(id) ?? null;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return items.map((item) => {
|
|
139
|
+
const row = item.conversationId ? resolveConv(item.conversationId) : null;
|
|
140
|
+
const sourceType = classifyConversationSource(row?.source);
|
|
141
|
+
|
|
142
|
+
const metadataScheduleId =
|
|
143
|
+
typeof item.metadata?.scheduleId === "string"
|
|
144
|
+
? item.metadata.scheduleId
|
|
145
|
+
: undefined;
|
|
146
|
+
const scheduleId = metadataScheduleId ?? row?.scheduleJobId ?? undefined;
|
|
147
|
+
|
|
148
|
+
let sourceKey: string;
|
|
149
|
+
let sourceLabel: string;
|
|
150
|
+
if (sourceType === "schedule" && scheduleId) {
|
|
151
|
+
sourceKey = `schedule:${scheduleId}`;
|
|
152
|
+
sourceLabel = resolveScheduleName(scheduleId) ?? "Scheduled";
|
|
153
|
+
} else if (sourceType === "schedule") {
|
|
154
|
+
sourceKey = "schedule";
|
|
155
|
+
sourceLabel = "Scheduled";
|
|
156
|
+
} else {
|
|
157
|
+
sourceKey = sourceType;
|
|
158
|
+
sourceLabel = STATIC_SOURCE_LABELS[sourceType];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Surface the scheduleId in metadata when it was recovered from the
|
|
162
|
+
// source conversation so clients have a single place to read it.
|
|
163
|
+
const metadata =
|
|
164
|
+
scheduleId !== undefined && metadataScheduleId === undefined
|
|
165
|
+
? { ...(item.metadata ?? {}), scheduleId }
|
|
166
|
+
: item.metadata;
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
...item,
|
|
170
|
+
sourceType,
|
|
171
|
+
sourceKey,
|
|
172
|
+
sourceLabel,
|
|
173
|
+
...(metadata ? { metadata } : {}),
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
* relationship with the user: which tier they're at, what facts the
|
|
6
6
|
* assistant has learned about them, and which capabilities are unlocked.
|
|
7
7
|
*
|
|
8
|
-
* The TypeScript types here are the source of truth
|
|
9
|
-
*
|
|
10
|
-
* `clients/shared/Models/RelationshipState.swift` — any change here must
|
|
11
|
-
* be mirrored there (and the contract test guards the default list).
|
|
8
|
+
* The TypeScript types here are the source of truth; the contract test
|
|
9
|
+
* guards the default list.
|
|
12
10
|
*/
|
|
13
11
|
|
|
14
12
|
export const RELATIONSHIP_STATE_VERSION = 1 as const;
|
package/src/instrument.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getPlatformUserId,
|
|
8
8
|
getSentryDsn,
|
|
9
9
|
} from "./config/env.js";
|
|
10
|
+
import { getCachedShareDiagnostics } from "./platform/consent-cache.js";
|
|
10
11
|
import { APP_VERSION, COMMIT_SHA } from "./version.js";
|
|
11
12
|
|
|
12
13
|
/** Patterns that match sensitive data in Sentry event values. */
|
|
@@ -41,9 +42,11 @@ function redactObject(obj: unknown): unknown {
|
|
|
41
42
|
/**
|
|
42
43
|
* Call after dotenv has loaded so SENTRY_DSN_ASSISTANT is available.
|
|
43
44
|
* Initializes Sentry when the DSN is set; no-ops when empty/unset so
|
|
44
|
-
* local dev builds don't send crash reports.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
45
|
+
* local dev builds don't send crash reports. Events are dropped by
|
|
46
|
+
* beforeSend until the platform share_diagnostics consent confirms opt-in
|
|
47
|
+
* (re-read from the consent cache per event, so a revocation takes effect
|
|
48
|
+
* within one refresh cycle); VELLUM_DEV=1 and legacyDiagnosticsOptOut
|
|
49
|
+
* hard-disable via closeSentry() after config loads.
|
|
47
50
|
*/
|
|
48
51
|
export function initSentry(): void {
|
|
49
52
|
const dsn = getSentryDsn();
|
|
@@ -54,6 +57,14 @@ export function initSentry(): void {
|
|
|
54
57
|
dist: COMMIT_SHA,
|
|
55
58
|
environment: process.env.VELLUM_ENVIRONMENT ?? "production",
|
|
56
59
|
sendDefaultPii: false,
|
|
60
|
+
// Fail-closed: suppress client-report pings (discarded-event counts) so an
|
|
61
|
+
// assistant with unconfirmed share_diagnostics consent stays network-silent.
|
|
62
|
+
sendClientReports: false,
|
|
63
|
+
// Drop the default ProcessSession integration: it starts a release-health
|
|
64
|
+
// session on init and flushes a session envelope on process exit (a network
|
|
65
|
+
// request not covered by the beforeSend event gate), violating fail-closed.
|
|
66
|
+
integrations: (defaults) =>
|
|
67
|
+
defaults.filter((i) => i.name !== "ProcessSession"),
|
|
57
68
|
serverName: hostname(),
|
|
58
69
|
initialScope: {
|
|
59
70
|
tags: {
|
|
@@ -77,6 +88,7 @@ export function initSentry(): void {
|
|
|
77
88
|
},
|
|
78
89
|
},
|
|
79
90
|
beforeSend(event) {
|
|
91
|
+
if (!getCachedShareDiagnostics()) return null;
|
|
80
92
|
if (event.exception?.values) {
|
|
81
93
|
event.exception.values = event.exception.values.map((ex) => ({
|
|
82
94
|
...ex,
|
|
@@ -101,9 +113,9 @@ export function initSentry(): void {
|
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
/**
|
|
104
|
-
* Stop capturing future Sentry events. Called after config loads
|
|
105
|
-
*
|
|
106
|
-
*
|
|
116
|
+
* Stop capturing future Sentry events. Called after config loads for the
|
|
117
|
+
* legacyDiagnosticsOptOut local opt-out (or VELLUM_DEV=1) to hard-disable
|
|
118
|
+
* crash reporting independent of the share_diagnostics consent gate.
|
|
107
119
|
*/
|
|
108
120
|
export async function closeSentry(): Promise<void> {
|
|
109
121
|
await Sentry.close();
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for how `AssistantIpcServer.sendResult` handles a binary or
|
|
3
|
+
* streaming handler result over the IPC transport.
|
|
4
|
+
*
|
|
5
|
+
* A `RouteResponse` (or a bare `Uint8Array` / `ReadableStream` / `Blob`)
|
|
6
|
+
* wraps raw bytes that can't be carried as a JSON `result` field. Rather than
|
|
7
|
+
* silently serialize the body into garbage, the server reports a structured
|
|
8
|
+
* `BINARY_UNSUPPORTED_OVER_IPC` error; the gateway IPC proxy uses that signal
|
|
9
|
+
* to fall back to the HTTP proxy, which streams binary responses correctly.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, expect, test } from "bun:test";
|
|
13
|
+
|
|
14
|
+
import { RouteResponse } from "../../runtime/routes/types.js";
|
|
15
|
+
import { AssistantIpcServer } from "../assistant-server.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `sendResult` is private; access it through an interface cast so the test
|
|
19
|
+
* exercises the real production path without a test-only API on the class.
|
|
20
|
+
*/
|
|
21
|
+
type PrivateApi = {
|
|
22
|
+
sendResult(
|
|
23
|
+
socket: unknown,
|
|
24
|
+
reader: unknown,
|
|
25
|
+
requestId: string,
|
|
26
|
+
value: unknown,
|
|
27
|
+
): void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Drive `sendResult` with a capturing socket in legacy (newline-delimited
|
|
32
|
+
* JSON) mode — the same wire shape the gateway's IPC client speaks — and
|
|
33
|
+
* return the parsed response envelope.
|
|
34
|
+
*/
|
|
35
|
+
function captureSendResult(value: unknown): Record<string, unknown> {
|
|
36
|
+
const server = new AssistantIpcServer() as unknown as PrivateApi;
|
|
37
|
+
const writes: string[] = [];
|
|
38
|
+
const socket = {
|
|
39
|
+
destroyed: false,
|
|
40
|
+
write: (chunk: string) => {
|
|
41
|
+
writes.push(chunk);
|
|
42
|
+
return true;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const reader = { isLegacy: true };
|
|
46
|
+
|
|
47
|
+
server.sendResult(socket, reader, "req-1", value);
|
|
48
|
+
|
|
49
|
+
expect(writes).toHaveLength(1);
|
|
50
|
+
return JSON.parse(writes[0]) as Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("AssistantIpcServer.sendResult binary handling", () => {
|
|
54
|
+
test("a binary RouteResponse is reported as BINARY_UNSUPPORTED_OVER_IPC", () => {
|
|
55
|
+
const env = captureSendResult(
|
|
56
|
+
new RouteResponse(new Uint8Array([137, 80, 78, 71]), {
|
|
57
|
+
"content-type": "image/png",
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(env.id).toBe("req-1");
|
|
62
|
+
expect(env.statusCode).toBe(421);
|
|
63
|
+
expect(env.errorCode).toBe("BINARY_UNSUPPORTED_OVER_IPC");
|
|
64
|
+
// The binary body must never be JSON-serialized into a `result` field.
|
|
65
|
+
expect("result" in env).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("a bare Uint8Array result is also reported as unsupported", () => {
|
|
69
|
+
const env = captureSendResult(new Uint8Array([1, 2, 3]));
|
|
70
|
+
|
|
71
|
+
expect(env.errorCode).toBe("BINARY_UNSUPPORTED_OVER_IPC");
|
|
72
|
+
expect("result" in env).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("a plain JSON result still serializes into `result`", () => {
|
|
76
|
+
const env = captureSendResult({ ok: true, count: 2 });
|
|
77
|
+
|
|
78
|
+
expect(env.result).toEqual({ ok: true, count: 2 });
|
|
79
|
+
expect(env.errorCode).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -166,4 +166,24 @@ describe("assistant clients list over IPC — same-user filter", () => {
|
|
|
166
166
|
const ids = parsed.clients.map((c) => c.clientId).sort();
|
|
167
167
|
expect(ids).toEqual(["client-other", "client-self"]);
|
|
168
168
|
});
|
|
169
|
+
|
|
170
|
+
test("--json output carries the degraded flag (false for fresh clients)", async () => {
|
|
171
|
+
registerClient({
|
|
172
|
+
clientId: "client-self-1",
|
|
173
|
+
actorPrincipalId: "guardian-local",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await startServer();
|
|
177
|
+
|
|
178
|
+
const { stdout } = await runAssistantCommandFull(
|
|
179
|
+
"clients",
|
|
180
|
+
"list",
|
|
181
|
+
"--json",
|
|
182
|
+
);
|
|
183
|
+
const parsed = JSON.parse(stdout.trim()) as {
|
|
184
|
+
clients: Array<{ clientId: string; degraded?: boolean }>;
|
|
185
|
+
};
|
|
186
|
+
expect(parsed.clients).toHaveLength(1);
|
|
187
|
+
expect(parsed.clients[0].degraded).toBe(false);
|
|
188
|
+
});
|
|
169
189
|
});
|
|
@@ -31,10 +31,7 @@
|
|
|
31
31
|
import { existsSync, unlinkSync } from "node:fs";
|
|
32
32
|
import { createServer, type Server, type Socket } from "node:net";
|
|
33
33
|
|
|
34
|
-
import {
|
|
35
|
-
ensureSocketDir,
|
|
36
|
-
SocketWatchdog,
|
|
37
|
-
} from "@vellumai/ipc-server-utils";
|
|
34
|
+
import { ensureSocketDir, SocketWatchdog } from "@vellumai/ipc-server-utils";
|
|
38
35
|
|
|
39
36
|
import { findLocalGuardianPrincipalId } from "../runtime/local-actor-identity.js";
|
|
40
37
|
import { RouteError } from "../runtime/routes/errors.js";
|
|
@@ -43,6 +40,7 @@ import type {
|
|
|
43
40
|
RouteDefinition,
|
|
44
41
|
RouteHandlerArgs,
|
|
45
42
|
} from "../runtime/routes/types.js";
|
|
43
|
+
import { RouteResponse } from "../runtime/routes/types.js";
|
|
46
44
|
import { getLogger } from "../util/logger.js";
|
|
47
45
|
import {
|
|
48
46
|
type IpcEnvelope,
|
|
@@ -134,6 +132,26 @@ function isIpcBinaryResponse(value: unknown): value is IpcBinaryResponse {
|
|
|
134
132
|
);
|
|
135
133
|
}
|
|
136
134
|
|
|
135
|
+
/**
|
|
136
|
+
* A handler result whose body is raw bytes or a stream rather than a JSON
|
|
137
|
+
* value — `RouteResponse` (e.g. `workspace/file/content` returns a
|
|
138
|
+
* `Bun.file`), or a bare `Uint8Array` / `ReadableStream` / `Blob`.
|
|
139
|
+
*
|
|
140
|
+
* These cannot be carried as a JSON `result` field, so over the IPC
|
|
141
|
+
* transport they are reported as a structured error rather than silently
|
|
142
|
+
* JSON-serialized into garbage. Distinct from the `IpcBinaryResponse` /
|
|
143
|
+
* `IpcStreamingResponse` wrappers, which are explicit binary envelopes the
|
|
144
|
+
* framing protocol does transmit.
|
|
145
|
+
*/
|
|
146
|
+
function isNonJsonIpcResult(value: unknown): boolean {
|
|
147
|
+
return (
|
|
148
|
+
value instanceof RouteResponse ||
|
|
149
|
+
value instanceof Uint8Array ||
|
|
150
|
+
value instanceof ReadableStream ||
|
|
151
|
+
value instanceof Blob
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
137
155
|
// ---------------------------------------------------------------------------
|
|
138
156
|
// Server
|
|
139
157
|
// ---------------------------------------------------------------------------
|
|
@@ -401,6 +419,8 @@ export class AssistantIpcServer {
|
|
|
401
419
|
* Route a handler result to the appropriate send path:
|
|
402
420
|
* - IpcStreamingResponse → chunked binary frames
|
|
403
421
|
* - IpcBinaryResponse → single binary frame with content-length
|
|
422
|
+
* - A raw binary/stream result (RouteResponse, Uint8Array, ...) → structured
|
|
423
|
+
* BINARY_UNSUPPORTED_OVER_IPC error (the transport can't carry it as JSON)
|
|
404
424
|
* - Everything else → JSON response
|
|
405
425
|
*/
|
|
406
426
|
private sendResult(
|
|
@@ -420,6 +440,19 @@ export class AssistantIpcServer {
|
|
|
420
440
|
},
|
|
421
441
|
};
|
|
422
442
|
this.sendResponse(socket, reader, envelope, value.binary);
|
|
443
|
+
} else if (isNonJsonIpcResult(value)) {
|
|
444
|
+
// A binary/streaming handler result (e.g. a file-content RouteResponse
|
|
445
|
+
// wrapping a Bun.file) cannot be carried as a JSON `result`. Report a
|
|
446
|
+
// structured error instead of silently serializing it into garbage; the
|
|
447
|
+
// gateway IPC proxy treats this code as a signal to retry over HTTP,
|
|
448
|
+
// which streams binary correctly.
|
|
449
|
+
this.sendResponse(socket, reader, {
|
|
450
|
+
id: requestId,
|
|
451
|
+
error:
|
|
452
|
+
"Binary/streaming responses are not supported over the IPC transport; use HTTP",
|
|
453
|
+
statusCode: 421,
|
|
454
|
+
errorCode: "BINARY_UNSUPPORTED_OVER_IPC",
|
|
455
|
+
});
|
|
423
456
|
} else {
|
|
424
457
|
this.sendResponse(socket, reader, { id: requestId, result: value });
|
|
425
458
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { connect, type Socket } from "node:net";
|
|
2
2
|
|
|
3
3
|
import { refreshOverridesFromGateway } from "../config/assistant-feature-flags.js";
|
|
4
|
+
import { reconcileFlagGatedProfiles } from "../config/sync-gated-profiles.js";
|
|
4
5
|
import { SYNC_TAGS } from "../daemon/message-types/sync.js";
|
|
6
|
+
import { publishConfigChanged } from "../runtime/sync/resource-sync-events.js";
|
|
5
7
|
import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
|
|
6
8
|
import { syncFlagGatedTools } from "../tools/registry.js";
|
|
7
9
|
import { getLogger } from "../util/logger.js";
|
|
@@ -19,11 +21,25 @@ const log = getLogger("gateway-flag-listener");
|
|
|
19
21
|
* they live in a flag-gated bundled skill surfaced via `skill_load` against the
|
|
20
22
|
* live flag cache, so they need no registry sync here. `syncFlagGatedTools` is
|
|
21
23
|
* idempotent and enable-only, so re-running it on every refresh is safe; it also
|
|
22
|
-
* never throws (it logs internally).
|
|
24
|
+
* never throws (it logs internally). After tools sync, `reconcileFlagGatedProfiles`
|
|
25
|
+
* adds or removes the flag-gated managed profile (OS Beta); when it reports a
|
|
26
|
+
* change a `config_changed` broadcast refreshes the profile picker on clients.
|
|
27
|
+
* The profile reconcile runs only when the refresh confirmed flags loaded from
|
|
28
|
+
* the gateway — a transient IPC failure leaves the cache unset and resolves
|
|
29
|
+
* `os-beta` to its registry default `false`, which would remove the user's
|
|
30
|
+
* profile and reset their selection. Tool sync tolerates the default and stays
|
|
31
|
+
* unconditional.
|
|
23
32
|
*/
|
|
24
33
|
function refreshFlagsAndSyncTools(context: string): void {
|
|
25
34
|
refreshOverridesFromGateway()
|
|
26
|
-
.then(() =>
|
|
35
|
+
.then(async (loaded) => {
|
|
36
|
+
await syncFlagGatedTools();
|
|
37
|
+
if (loaded && reconcileFlagGatedProfiles()) {
|
|
38
|
+
// Reuse the config-changed broadcast clients already consume so the
|
|
39
|
+
// profile picker reflects the added/removed managed profile.
|
|
40
|
+
publishConfigChanged();
|
|
41
|
+
}
|
|
42
|
+
})
|
|
27
43
|
.catch((err) => {
|
|
28
44
|
log.warn(
|
|
29
45
|
{ err },
|