@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
|
@@ -8,10 +8,10 @@ mock.module("../../util/logger.js", () => ({
|
|
|
8
8
|
}),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
// Mutable
|
|
12
|
-
let
|
|
13
|
-
mock.module("../../
|
|
14
|
-
|
|
11
|
+
// Mutable consent gate, flipped per-test.
|
|
12
|
+
let shareAnalytics = true;
|
|
13
|
+
mock.module("../../platform/consent-cache.js", () => ({
|
|
14
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
17
|
import { getDb } from "../db-connection.js";
|
|
@@ -30,7 +30,7 @@ function resetTable(): void {
|
|
|
30
30
|
|
|
31
31
|
describe("onboarding-events-store: recordActivationEvent", () => {
|
|
32
32
|
beforeEach(() => {
|
|
33
|
-
|
|
33
|
+
shareAnalytics = true;
|
|
34
34
|
resetTable();
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -66,8 +66,8 @@ describe("onboarding-events-store: recordActivationEvent", () => {
|
|
|
66
66
|
expect(rows[0]!.stepIndex).toBe(2);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
test("returns null and writes no row when
|
|
70
|
-
|
|
69
|
+
test("returns null and writes no row when share_analytics is disabled", () => {
|
|
70
|
+
shareAnalytics = false;
|
|
71
71
|
const event = recordActivationEvent({
|
|
72
72
|
stepName: "activation_moment_1_complete",
|
|
73
73
|
sessionId: "conv-3",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, asc, eq, gt, or } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getCachedShareAnalytics } from "../platform/consent-cache.js";
|
|
5
5
|
import { getDb } from "./db-connection.js";
|
|
6
6
|
import { authFallbackEvents } from "./schema.js";
|
|
7
7
|
|
|
@@ -36,7 +36,7 @@ export function recordAuthFallbackCounts(
|
|
|
36
36
|
windowEnd: number,
|
|
37
37
|
counts: AuthFallbackCount[],
|
|
38
38
|
): number {
|
|
39
|
-
if (!
|
|
39
|
+
if (!getCachedShareAnalytics()) return 0;
|
|
40
40
|
if (counts.length === 0) return 0;
|
|
41
41
|
const db = getDb();
|
|
42
42
|
const createdAt = Date.now();
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
2
2
|
import { getConfig } from "../config/loader.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type TrustClass,
|
|
6
|
-
} from "../runtime/actor-trust-resolver.js";
|
|
3
|
+
import { type TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
4
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
7
5
|
import { getLogger } from "../util/logger.js";
|
|
8
6
|
import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
|
|
9
7
|
import { isMemoryEnabled, upsertAutoAnalysisJob } from "./jobs-store.js";
|
|
@@ -118,7 +116,7 @@ export function enqueueAutoAnalysisOnCompaction(
|
|
|
118
116
|
conversationId: string,
|
|
119
117
|
trustClass: TrustClass | undefined,
|
|
120
118
|
): void {
|
|
121
|
-
if (
|
|
119
|
+
if (!resolveCapabilities(trustClass).canAccessMemory) {
|
|
122
120
|
return;
|
|
123
121
|
}
|
|
124
122
|
try {
|
|
@@ -7,8 +7,7 @@ import { conversations, messageBookmarks, messages } from "./schema.js";
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Wire-shape representation of a bookmark, joined with the bookmarked
|
|
10
|
-
* message and its parent conversation.
|
|
11
|
-
* `clients/shared/Network/BookmarkSummary.swift` — dates are emitted as
|
|
10
|
+
* message and its parent conversation. Dates are emitted as
|
|
12
11
|
* unix-millisecond integers, and the message preview is capped to keep
|
|
13
12
|
* the list payload bounded.
|
|
14
13
|
*/
|
|
@@ -74,7 +74,7 @@ export interface CanonicalGuardianRequest {
|
|
|
74
74
|
updatedAt: number;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
interface CanonicalGuardianDelivery {
|
|
77
|
+
export interface CanonicalGuardianDelivery {
|
|
78
78
|
id: string;
|
|
79
79
|
requestId: string;
|
|
80
80
|
destinationChannel: string;
|
|
@@ -674,6 +674,44 @@ export function listPendingCanonicalGuardianRequestsByDestinationChat(
|
|
|
674
674
|
return pendingRequests;
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
+
/**
|
|
678
|
+
* Find the pending canonical request whose guardian-facing delivery landed on a
|
|
679
|
+
* specific channel message (channel + chat + message id).
|
|
680
|
+
*
|
|
681
|
+
* This is the addressing key for emoji-reaction decisions: a reaction carries
|
|
682
|
+
* only the message it is attached to, so the delivered card's message id is how
|
|
683
|
+
* we recover which request the guardian acted on — even when several cards are
|
|
684
|
+
* pending in the same chat. Returns null when no delivery matches or the matched
|
|
685
|
+
* request is no longer pending.
|
|
686
|
+
*/
|
|
687
|
+
export function getPendingCanonicalRequestByDestinationMessage(
|
|
688
|
+
destinationChannel: string,
|
|
689
|
+
destinationChatId: string,
|
|
690
|
+
destinationMessageId: string,
|
|
691
|
+
): CanonicalGuardianRequest | null {
|
|
692
|
+
const db = getDb();
|
|
693
|
+
|
|
694
|
+
const delivery = db
|
|
695
|
+
.select()
|
|
696
|
+
.from(canonicalGuardianDeliveries)
|
|
697
|
+
.where(
|
|
698
|
+
and(
|
|
699
|
+
eq(canonicalGuardianDeliveries.destinationChannel, destinationChannel),
|
|
700
|
+
eq(canonicalGuardianDeliveries.destinationChatId, destinationChatId),
|
|
701
|
+
eq(
|
|
702
|
+
canonicalGuardianDeliveries.destinationMessageId,
|
|
703
|
+
destinationMessageId,
|
|
704
|
+
),
|
|
705
|
+
),
|
|
706
|
+
)
|
|
707
|
+
.get();
|
|
708
|
+
|
|
709
|
+
if (!delivery) return null;
|
|
710
|
+
|
|
711
|
+
const request = getCanonicalGuardianRequest(delivery.requestId);
|
|
712
|
+
return request && request.status === "pending" ? request : null;
|
|
713
|
+
}
|
|
714
|
+
|
|
677
715
|
// ---------------------------------------------------------------------------
|
|
678
716
|
// Conversation scope helpers
|
|
679
717
|
// ---------------------------------------------------------------------------
|
|
@@ -129,7 +129,7 @@ export const messageMetadataSchema = z
|
|
|
129
129
|
* and read gate (conversation history loading) to enforce trust-aware access.
|
|
130
130
|
*/
|
|
131
131
|
provenanceTrustClass: z
|
|
132
|
-
.enum(["guardian", "trusted_contact", "unknown"])
|
|
132
|
+
.enum(["guardian", "trusted_contact", "unverified_contact", "unknown"])
|
|
133
133
|
.optional(),
|
|
134
134
|
provenanceSourceChannel: channelIdSchema.optional(),
|
|
135
135
|
provenanceGuardianExternalUserId: z.string().optional(),
|
|
@@ -2575,12 +2575,17 @@ export function getConversationOriginInterface(
|
|
|
2575
2575
|
* in the given conversation, or `undefined` if none is found.
|
|
2576
2576
|
*
|
|
2577
2577
|
* Used by the pointer message trust resolver to detect conversations
|
|
2578
|
-
* whose audience is a guardian
|
|
2579
|
-
* conversations.
|
|
2578
|
+
* whose audience is a guardian, trusted_contact, or unverified_contact
|
|
2579
|
+
* outside desktop-origin conversations.
|
|
2580
2580
|
*/
|
|
2581
2581
|
export function getConversationRecentProvenanceTrustClass(
|
|
2582
2582
|
conversationId: string,
|
|
2583
|
-
):
|
|
2583
|
+
):
|
|
2584
|
+
| "guardian"
|
|
2585
|
+
| "trusted_contact"
|
|
2586
|
+
| "unverified_contact"
|
|
2587
|
+
| "unknown"
|
|
2588
|
+
| undefined {
|
|
2584
2589
|
const row = rawGet<{ metadata: string | null }>(
|
|
2585
2590
|
`SELECT metadata FROM messages
|
|
2586
2591
|
WHERE conversation_id = ? AND role = 'user' AND metadata IS NOT NULL
|
|
@@ -135,7 +135,16 @@ export function resolveConversationId(idOrKey: string): string | null {
|
|
|
135
135
|
*/
|
|
136
136
|
export function getOrCreateConversation(
|
|
137
137
|
conversationKey: string,
|
|
138
|
-
opts?: {
|
|
138
|
+
opts?: {
|
|
139
|
+
conversationType?: "standard";
|
|
140
|
+
/**
|
|
141
|
+
* Caller-supplied title for the conversation, used only when this call
|
|
142
|
+
* actually creates the row. Treated as a user-set title (`isAutoTitle = 0`)
|
|
143
|
+
* so the async LLM title generator's safe-overwrite check leaves it
|
|
144
|
+
* untouched. Ignored when the conversation already exists.
|
|
145
|
+
*/
|
|
146
|
+
title?: string;
|
|
147
|
+
},
|
|
139
148
|
): {
|
|
140
149
|
conversationId: string;
|
|
141
150
|
conversationType: string;
|
|
@@ -205,13 +214,19 @@ export function getOrCreateConversation(
|
|
|
205
214
|
|
|
206
215
|
const now = Date.now();
|
|
207
216
|
const conversationId = uuid();
|
|
208
|
-
const
|
|
217
|
+
const customTitle = opts?.title?.trim();
|
|
218
|
+
const title = customTitle || GENERATING_TITLE;
|
|
209
219
|
const memoryScopeId = "default";
|
|
210
220
|
|
|
211
221
|
tx.insert(conversations)
|
|
212
222
|
.values({
|
|
213
223
|
id: conversationId,
|
|
214
224
|
title,
|
|
225
|
+
// A caller-supplied title is user-set: mark it non-auto (0) so the
|
|
226
|
+
// async LLM title generator's `canReplaceTitle` check won't overwrite
|
|
227
|
+
// it. Without one, omit the column so it takes its default
|
|
228
|
+
// (AUTO_TITLE_LLM) and follows the auto-generated placeholder flow.
|
|
229
|
+
...(customTitle ? { isAutoTitle: 0 } : {}),
|
|
215
230
|
createdAt: now,
|
|
216
231
|
updatedAt: now,
|
|
217
232
|
totalInputTokens: 0,
|
|
@@ -166,6 +166,7 @@ export async function generateAndPersistConversationTitle(
|
|
|
166
166
|
const fallback = deriveFallbackTitle(context) ?? UNTITLED_FALLBACK;
|
|
167
167
|
updateConversationTitle(conversationId, fallback, AUTO_TITLE_DETERMINISTIC);
|
|
168
168
|
publishConversationTitleChanged(conversationId, fallback);
|
|
169
|
+
logRetryableFallback(params, "no_provider");
|
|
169
170
|
return { title: fallback, updated: true };
|
|
170
171
|
}
|
|
171
172
|
|
|
@@ -206,6 +207,7 @@ export async function generateAndPersistConversationTitle(
|
|
|
206
207
|
const fallback = deriveFallbackTitle(context) ?? UNTITLED_FALLBACK;
|
|
207
208
|
updateConversationTitle(conversationId, fallback, AUTO_TITLE_DETERMINISTIC);
|
|
208
209
|
publishConversationTitleChanged(conversationId, fallback);
|
|
210
|
+
logRetryableFallback(params, "empty_output");
|
|
209
211
|
return { title: fallback, updated: true };
|
|
210
212
|
}
|
|
211
213
|
|
|
@@ -230,7 +232,7 @@ export const titleMutex = new Mutex();
|
|
|
230
232
|
/**
|
|
231
233
|
* Fire-and-forget wrapper for title generation. Failures are logged
|
|
232
234
|
* but do not propagate. On failure, replaces loading placeholder with
|
|
233
|
-
* a
|
|
235
|
+
* a retryable fallback title so loading state is never permanent.
|
|
234
236
|
*
|
|
235
237
|
* Calls are serialized via {@link titleMutex} so burst conversation
|
|
236
238
|
* creation does not overwhelm the LLM provider.
|
|
@@ -244,16 +246,20 @@ export function queueGenerateConversationTitle(
|
|
|
244
246
|
})
|
|
245
247
|
.catch((err) => {
|
|
246
248
|
log.warn(
|
|
247
|
-
|
|
248
|
-
"
|
|
249
|
+
retryableFallbackLogFields(params, "generation_error", err),
|
|
250
|
+
"Conversation title generation used retryable fallback",
|
|
249
251
|
);
|
|
250
|
-
// Replace loading placeholder with
|
|
252
|
+
// Replace loading placeholder with a retryable fallback.
|
|
251
253
|
try {
|
|
252
254
|
const conversation = getConversation(params.conversationId);
|
|
253
255
|
if (conversation && conversation.title === GENERATING_TITLE) {
|
|
254
256
|
const fallback =
|
|
255
257
|
deriveFallbackTitle(params.context) ?? UNTITLED_FALLBACK;
|
|
256
|
-
updateConversationTitle(
|
|
258
|
+
updateConversationTitle(
|
|
259
|
+
params.conversationId,
|
|
260
|
+
fallback,
|
|
261
|
+
AUTO_TITLE_DETERMINISTIC,
|
|
262
|
+
);
|
|
257
263
|
publishConversationTitleChanged(params.conversationId, fallback);
|
|
258
264
|
}
|
|
259
265
|
} catch {
|
|
@@ -268,6 +274,11 @@ export interface RegenerateTitleParams {
|
|
|
268
274
|
conversationId: string;
|
|
269
275
|
provider?: Provider;
|
|
270
276
|
signal?: AbortSignal;
|
|
277
|
+
/**
|
|
278
|
+
* Limit regeneration to placeholder or deterministic titles. Used for retrying
|
|
279
|
+
* failed initial generation without racing against a successful initial title.
|
|
280
|
+
*/
|
|
281
|
+
onlyIfReplaceable?: boolean;
|
|
271
282
|
}
|
|
272
283
|
|
|
273
284
|
/**
|
|
@@ -278,12 +289,15 @@ export interface RegenerateTitleParams {
|
|
|
278
289
|
export async function regenerateConversationTitle(
|
|
279
290
|
params: RegenerateTitleParams,
|
|
280
291
|
): Promise<{ title: string; updated: boolean }> {
|
|
281
|
-
const { conversationId, signal } = params;
|
|
292
|
+
const { conversationId, onlyIfReplaceable, signal } = params;
|
|
282
293
|
|
|
283
294
|
const conversation = getConversation(conversationId);
|
|
284
295
|
if (!conversation || !conversation.isAutoTitle) {
|
|
285
296
|
return { title: conversation?.title ?? UNTITLED_FALLBACK, updated: false };
|
|
286
297
|
}
|
|
298
|
+
if (onlyIfReplaceable && !canReplaceTitle(conversation)) {
|
|
299
|
+
return { title: conversation.title ?? UNTITLED_FALLBACK, updated: false };
|
|
300
|
+
}
|
|
287
301
|
|
|
288
302
|
const provider =
|
|
289
303
|
params.provider ?? (await getConfiguredProvider("conversationTitle"));
|
|
@@ -317,7 +331,11 @@ export async function regenerateConversationTitle(
|
|
|
317
331
|
if (title) {
|
|
318
332
|
// Re-check isAutoTitle before persisting (race guard against manual rename)
|
|
319
333
|
const current = getConversation(conversationId);
|
|
320
|
-
if (
|
|
334
|
+
if (
|
|
335
|
+
!current ||
|
|
336
|
+
!current.isAutoTitle ||
|
|
337
|
+
(onlyIfReplaceable && !canReplaceTitle(current))
|
|
338
|
+
) {
|
|
321
339
|
return { title: current?.title ?? UNTITLED_FALLBACK, updated: false };
|
|
322
340
|
}
|
|
323
341
|
|
|
@@ -408,6 +426,45 @@ function buildTitlePrompt(
|
|
|
408
426
|
return parts.join("\n");
|
|
409
427
|
}
|
|
410
428
|
|
|
429
|
+
function titleGenerationLogFields(params: GenerateTitleParams) {
|
|
430
|
+
return {
|
|
431
|
+
conversationId: params.conversationId,
|
|
432
|
+
contextOrigin: params.context?.origin,
|
|
433
|
+
hasContext: Boolean(params.context),
|
|
434
|
+
userMessageLength: params.userMessage?.length ?? 0,
|
|
435
|
+
assistantResponseLength: params.assistantResponse?.length ?? 0,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
type TitleGenerationFallbackReason =
|
|
440
|
+
| "no_provider"
|
|
441
|
+
| "empty_output"
|
|
442
|
+
| "generation_error";
|
|
443
|
+
|
|
444
|
+
function retryableFallbackLogFields(
|
|
445
|
+
params: GenerateTitleParams,
|
|
446
|
+
reason: TitleGenerationFallbackReason,
|
|
447
|
+
err?: unknown,
|
|
448
|
+
): Record<string, unknown> {
|
|
449
|
+
const fields: Record<string, unknown> = {
|
|
450
|
+
...titleGenerationLogFields(params),
|
|
451
|
+
reason,
|
|
452
|
+
fallbackSource: params.context ? "context" : "untitled",
|
|
453
|
+
};
|
|
454
|
+
if (err) fields.err = err;
|
|
455
|
+
return fields;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function logRetryableFallback(
|
|
459
|
+
params: GenerateTitleParams,
|
|
460
|
+
reason: TitleGenerationFallbackReason,
|
|
461
|
+
): void {
|
|
462
|
+
log.warn(
|
|
463
|
+
retryableFallbackLogFields(params, reason),
|
|
464
|
+
"Conversation title generation used retryable fallback",
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
411
468
|
const META_FAILURE_TITLES = new Set([
|
|
412
469
|
"missing context",
|
|
413
470
|
"no context",
|
package/src/memory/db-init.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
createSkillLoadedEventsTable,
|
|
41
41
|
createTasksAndWorkItemsTables,
|
|
42
42
|
createWatchersAndLogsTables,
|
|
43
|
+
dropApprovalPromptTsTrackerTable,
|
|
43
44
|
migrate230AcpSessionHistory,
|
|
44
45
|
migrate231RepairMemoryGraphEventDates,
|
|
45
46
|
migrateA2ATasks,
|
|
@@ -68,6 +69,7 @@ import {
|
|
|
68
69
|
migrateChannelInboundDeliveryAttempts,
|
|
69
70
|
migrateChannelInteractionColumns,
|
|
70
71
|
migrateContactChannelsAccessFields,
|
|
72
|
+
migrateContactChannelsRenormalizeAddresses,
|
|
71
73
|
migrateContactChannelsTypeChatIdIndex,
|
|
72
74
|
migrateContactChannelsUniqueExtUser,
|
|
73
75
|
migrateContactsAssistantId,
|
|
@@ -101,6 +103,7 @@ import {
|
|
|
101
103
|
migrateDropConflicts,
|
|
102
104
|
migrateDropContactInteractionColumns,
|
|
103
105
|
migrateDropEntityTables,
|
|
106
|
+
migrateDropExternalUserId,
|
|
104
107
|
migrateDropLegacyMemberGuardianTables,
|
|
105
108
|
migrateDropLoopbackPortColumn,
|
|
106
109
|
migrateDropMemoryItemsTables,
|
|
@@ -194,7 +197,9 @@ import {
|
|
|
194
197
|
migrateRenameVerificationSessionIdColumn,
|
|
195
198
|
migrateRenameVerificationTable,
|
|
196
199
|
migrateRenameVoiceToPhone,
|
|
200
|
+
migrateRewriteBalancedEconomyProfilePins,
|
|
197
201
|
migrateScheduleCapabilities,
|
|
202
|
+
migrateScheduleDefaultNoReuseConversation,
|
|
198
203
|
migrateScheduleDescription,
|
|
199
204
|
migrateScheduleInferenceProfile,
|
|
200
205
|
migrateScheduleOneShotRouting,
|
|
@@ -222,6 +227,7 @@ import {
|
|
|
222
227
|
migrateUsageLlmCallCount,
|
|
223
228
|
migrateVoiceInviteColumns,
|
|
224
229
|
migrateVoiceInviteDisplayMetadata,
|
|
230
|
+
migrateWorkflowJournalLeafTokens,
|
|
225
231
|
migrateWorkflowRuns,
|
|
226
232
|
migrateWorkflowRunTrust,
|
|
227
233
|
recoverCrashedMigrations,
|
|
@@ -229,6 +235,7 @@ import {
|
|
|
229
235
|
runLateMigrations,
|
|
230
236
|
validateMigrationState,
|
|
231
237
|
} from "./migrations/index.js";
|
|
238
|
+
import { runMigrationSteps } from "./migrations/run-migrations.js";
|
|
232
239
|
|
|
233
240
|
// ---------------------------------------------------------------------------
|
|
234
241
|
// Test DB template — run migrations once, reuse across test files
|
|
@@ -518,29 +525,22 @@ export function initializeDb(): void {
|
|
|
518
525
|
migrateBackfillOriginChannelFromBindings,
|
|
519
526
|
migrateContactChannelsUniqueExtUser,
|
|
520
527
|
migrateScheduleCapabilities,
|
|
528
|
+
migrateContactChannelsRenormalizeAddresses,
|
|
529
|
+
migrateScheduleDefaultNoReuseConversation,
|
|
530
|
+
migrateWorkflowJournalLeafTokens,
|
|
531
|
+
migrateDropExternalUserId,
|
|
532
|
+
dropApprovalPromptTsTrackerTable,
|
|
533
|
+
migrateRewriteBalancedEconomyProfilePins,
|
|
521
534
|
];
|
|
522
535
|
|
|
523
536
|
// Run each migration step, catching and logging individual failures so one
|
|
524
537
|
// broken migration doesn't prevent independent later ones from succeeding.
|
|
525
|
-
const
|
|
526
|
-
for (const step of migrationSteps) {
|
|
527
|
-
try {
|
|
528
|
-
log.debug({ migration: step.name }, `Starting migration: ${step.name}`);
|
|
529
|
-
step(database);
|
|
530
|
-
log.debug({ migration: step.name }, `Migration succeeded: ${step.name}`);
|
|
531
|
-
} catch (err) {
|
|
532
|
-
failures.push(step.name);
|
|
533
|
-
log.error(
|
|
534
|
-
{ err, migration: step.name },
|
|
535
|
-
`Migration failed: ${step.name}`,
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
538
|
+
const { failed } = runMigrationSteps(database, migrationSteps);
|
|
539
539
|
|
|
540
|
-
if (
|
|
540
|
+
if (failed.length > 0) {
|
|
541
541
|
log.error(
|
|
542
|
-
{ failedMigrations:
|
|
543
|
-
`DB initialization completed with ${
|
|
542
|
+
{ failedMigrations: failed, count: failed.length },
|
|
543
|
+
`DB initialization completed with ${failed.length} failed migration(s)`,
|
|
544
544
|
);
|
|
545
545
|
}
|
|
546
546
|
|
|
@@ -7,6 +7,13 @@ import { PLATFORM_PROVIDER_META } from "../providers/platform-proxy/constants.js
|
|
|
7
7
|
import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
|
|
8
8
|
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
9
9
|
import { getLogger } from "../util/logger.js";
|
|
10
|
+
import {
|
|
11
|
+
EmbeddingBillingBlockError,
|
|
12
|
+
extractHttpStatus,
|
|
13
|
+
isEmbeddingBillingBreakerOpen,
|
|
14
|
+
recordBillingBlock,
|
|
15
|
+
recordBillingSuccess,
|
|
16
|
+
} from "./embedding-billing-breaker.js";
|
|
10
17
|
import { GeminiEmbeddingBackend } from "./embedding-gemini.js";
|
|
11
18
|
import { OllamaEmbeddingBackend } from "./embedding-ollama.js";
|
|
12
19
|
import { OpenAIEmbeddingBackend } from "./embedding-openai.js";
|
|
@@ -490,6 +497,20 @@ export async function getMemoryBackendStatus(config: AssistantConfig): Promise<{
|
|
|
490
497
|
};
|
|
491
498
|
}
|
|
492
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Thrown by {@link embedWithBackend} when no embedding backend is configured or
|
|
502
|
+
* available. This is a PROCESS-WIDE condition (backend selection is effectively
|
|
503
|
+
* cached for the run), so a caller embedding many items should treat the first
|
|
504
|
+
* occurrence as fatal to the whole batch rather than a per-item failure — see
|
|
505
|
+
* `backfillAllSections`, which aborts on it instead of churning through deletes.
|
|
506
|
+
*/
|
|
507
|
+
export class EmbeddingBackendUnavailableError extends Error {
|
|
508
|
+
constructor(message = "No memory embedding backend configured") {
|
|
509
|
+
super(message);
|
|
510
|
+
this.name = "EmbeddingBackendUnavailableError";
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
493
514
|
export async function embedWithBackend(
|
|
494
515
|
config: AssistantConfig,
|
|
495
516
|
inputs: EmbeddingInput[],
|
|
@@ -499,9 +520,15 @@ export async function embedWithBackend(
|
|
|
499
520
|
model: string;
|
|
500
521
|
vectors: number[][];
|
|
501
522
|
}> {
|
|
523
|
+
// Fail-fast when the billing breaker is open — avoids burning a network
|
|
524
|
+
// round-trip on every caller (embed lane jobs, activation recompute, etc.).
|
|
525
|
+
if (isEmbeddingBillingBreakerOpen()) {
|
|
526
|
+
throw new EmbeddingBillingBlockError();
|
|
527
|
+
}
|
|
528
|
+
|
|
502
529
|
const selection = await selectEmbeddingBackend(config);
|
|
503
530
|
if (!selection.backend) {
|
|
504
|
-
throw new
|
|
531
|
+
throw new EmbeddingBackendUnavailableError(
|
|
505
532
|
selection.reason ?? "No memory embedding backend configured",
|
|
506
533
|
);
|
|
507
534
|
}
|
|
@@ -612,6 +639,10 @@ export async function embedWithBackend(
|
|
|
612
639
|
);
|
|
613
640
|
}
|
|
614
641
|
|
|
642
|
+
// A successful backend call proves billing is active — close the
|
|
643
|
+
// breaker if it was in the probe window.
|
|
644
|
+
recordBillingSuccess();
|
|
645
|
+
|
|
615
646
|
if (isPrimary) {
|
|
616
647
|
const merged = [...cached] as number[][];
|
|
617
648
|
for (let i = 0; i < uncachedIndices.length; i++) {
|
|
@@ -626,6 +657,12 @@ export async function embedWithBackend(
|
|
|
626
657
|
return { provider: backend.provider, model: backend.model, vectors };
|
|
627
658
|
} catch (err) {
|
|
628
659
|
lastErr = err;
|
|
660
|
+
// If ANY backend in the chain returns 402, trip the billing breaker
|
|
661
|
+
// immediately — fallbacks will hit the same depleted balance.
|
|
662
|
+
if (extractHttpStatus(err) === 402) {
|
|
663
|
+
recordBillingBlock();
|
|
664
|
+
throw err;
|
|
665
|
+
}
|
|
629
666
|
if (backends.length > 1) {
|
|
630
667
|
log.warn(
|
|
631
668
|
{ err, provider: backend.provider },
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getLogger } from "../util/logger.js";
|
|
2
|
+
|
|
3
|
+
const log = getLogger("embedding-billing-breaker");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Lightweight circuit breaker for embedding billing blocks (HTTP 402).
|
|
7
|
+
*
|
|
8
|
+
* Unlike the Qdrant circuit breaker (which needs a failure threshold and
|
|
9
|
+
* half-open probe logic), billing exhaustion is deterministic — a single
|
|
10
|
+
* 402 means the org is depleted and every subsequent call will fail
|
|
11
|
+
* identically. The breaker opens immediately on the first 402 and stays
|
|
12
|
+
* open for COOLDOWN_MS, then allows one probe through. A successful
|
|
13
|
+
* probe (no 402) closes the breaker; a failed probe re-opens it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes
|
|
17
|
+
|
|
18
|
+
type BreakerState = "closed" | "open";
|
|
19
|
+
|
|
20
|
+
let breakerState: BreakerState = "closed";
|
|
21
|
+
let openedAt = 0;
|
|
22
|
+
|
|
23
|
+
export class EmbeddingBillingBlockError extends Error {
|
|
24
|
+
constructor() {
|
|
25
|
+
super("Embedding billing breaker open — org balance depleted");
|
|
26
|
+
this.name = "EmbeddingBillingBlockError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Trip the breaker after a 402 billing block. */
|
|
31
|
+
export function recordBillingBlock(): void {
|
|
32
|
+
if (breakerState === "closed") {
|
|
33
|
+
log.warn(
|
|
34
|
+
{ cooldownMs: COOLDOWN_MS },
|
|
35
|
+
"Embedding billing breaker opened — embedding jobs paused until probe succeeds",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
breakerState = "open";
|
|
39
|
+
openedAt = Date.now();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Clear the breaker after a successful embedding call during the probe
|
|
44
|
+
* window. Ignored when the breaker is closed or when the cooldown has not
|
|
45
|
+
* yet elapsed — this prevents a concurrent in-flight embed job (started
|
|
46
|
+
* before the breaker opened) from prematurely closing a freshly-tripped
|
|
47
|
+
* breaker.
|
|
48
|
+
*/
|
|
49
|
+
export function recordBillingSuccess(): void {
|
|
50
|
+
if (breakerState !== "open") return;
|
|
51
|
+
if (Date.now() - openedAt < COOLDOWN_MS) return;
|
|
52
|
+
log.info("Embedding billing breaker closed — billing probe succeeded");
|
|
53
|
+
breakerState = "closed";
|
|
54
|
+
openedAt = 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** True when the breaker is open and the cooldown has NOT yet elapsed. */
|
|
58
|
+
export function isEmbeddingBillingBreakerOpen(): boolean {
|
|
59
|
+
if (breakerState === "closed") return false;
|
|
60
|
+
if (Date.now() - openedAt >= COOLDOWN_MS) return false;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* True when the breaker is open but the cooldown has elapsed, meaning the
|
|
66
|
+
* next embed job should be allowed through as a probe to check whether
|
|
67
|
+
* the org has been re-funded.
|
|
68
|
+
*/
|
|
69
|
+
export function shouldAllowBillingProbe(): boolean {
|
|
70
|
+
return breakerState === "open" && Date.now() - openedAt >= COOLDOWN_MS;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Extract an HTTP status code from an error, if present. */
|
|
74
|
+
export function extractHttpStatus(err: unknown): number | undefined {
|
|
75
|
+
if (err == null || typeof err !== "object") return undefined;
|
|
76
|
+
|
|
77
|
+
// SDK-style errors (OpenAI, Anthropic) carry `.status` directly.
|
|
78
|
+
if ("status" in err) {
|
|
79
|
+
const s = (err as { status?: unknown }).status;
|
|
80
|
+
if (typeof s === "number") return s;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Gemini/Ollama backends embed the status in the message: "... (402): ..."
|
|
84
|
+
if (err instanceof Error) {
|
|
85
|
+
const match = err.message.match(/\((\d{3})\)/);
|
|
86
|
+
if (match) return parseInt(match[1], 10);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @internal Test-only: reset breaker state. */
|
|
93
|
+
export function _resetEmbeddingBillingBreaker(): void {
|
|
94
|
+
breakerState = "closed";
|
|
95
|
+
openedAt = 0;
|
|
96
|
+
}
|
package/src/memory/jobs-store.ts
CHANGED
|
@@ -5,6 +5,10 @@ import { getConfig } from "../config/loader.js";
|
|
|
5
5
|
import { getLogger } from "../util/logger.js";
|
|
6
6
|
import { truncate } from "../util/truncate.js";
|
|
7
7
|
import { getDb } from "./db-connection.js";
|
|
8
|
+
import {
|
|
9
|
+
isEmbeddingBillingBreakerOpen,
|
|
10
|
+
shouldAllowBillingProbe,
|
|
11
|
+
} from "./embedding-billing-breaker.js";
|
|
8
12
|
import {
|
|
9
13
|
isQdrantBreakerOpen,
|
|
10
14
|
shouldAllowQdrantProbe,
|
|
@@ -533,24 +537,32 @@ export function claimMemoryJobs(limits: LaneBudgets): MemoryJob[] {
|
|
|
533
537
|
.all()
|
|
534
538
|
: [];
|
|
535
539
|
|
|
536
|
-
// Embed lane: gated by the Qdrant circuit breaker
|
|
537
|
-
// skip embed jobs entirely — they
|
|
538
|
-
//
|
|
539
|
-
//
|
|
540
|
-
//
|
|
541
|
-
|
|
542
|
-
const
|
|
543
|
-
const
|
|
544
|
-
const
|
|
540
|
+
// Embed lane: gated by both the Qdrant circuit breaker and the billing
|
|
541
|
+
// breaker. When either breaker is open, skip embed jobs entirely — they
|
|
542
|
+
// would just be claimed → fail → deferred, wasting CPU cycles. Exception:
|
|
543
|
+
// if the cooldown has elapsed (breaker ready for probe), allow exactly
|
|
544
|
+
// 1 embed job through so the breaker can self-heal.
|
|
545
|
+
const qdrantBreakerOpen = isQdrantBreakerOpen();
|
|
546
|
+
const qdrantProbeAllowed = qdrantBreakerOpen && shouldAllowQdrantProbe();
|
|
547
|
+
const billingBreakerOpen = isEmbeddingBillingBreakerOpen();
|
|
548
|
+
const billingProbeAllowed = !billingBreakerOpen && shouldAllowBillingProbe();
|
|
549
|
+
const skipEmbedJobs =
|
|
550
|
+
(qdrantBreakerOpen && !qdrantProbeAllowed) ||
|
|
551
|
+
(billingBreakerOpen && !billingProbeAllowed);
|
|
552
|
+
const probeAllowed = qdrantProbeAllowed || billingProbeAllowed;
|
|
545
553
|
const embedLimit = probeAllowed ? Math.min(1, limits.embed) : limits.embed;
|
|
546
554
|
|
|
547
555
|
if (skipEmbedJobs && limits.embed > 0) {
|
|
548
|
-
|
|
556
|
+
if (billingBreakerOpen) {
|
|
557
|
+
log.debug(
|
|
558
|
+
"Skipping embed job claims — embedding billing breaker is open",
|
|
559
|
+
);
|
|
560
|
+
} else {
|
|
561
|
+
log.debug("Skipping embed job claims — Qdrant circuit breaker is open");
|
|
562
|
+
}
|
|
549
563
|
}
|
|
550
564
|
if (probeAllowed && limits.embed > 0) {
|
|
551
|
-
log.debug(
|
|
552
|
-
"Allowing 1 embed probe job — Qdrant circuit breaker cooldown elapsed",
|
|
553
|
-
);
|
|
565
|
+
log.debug("Allowing 1 embed probe job — breaker cooldown elapsed");
|
|
554
566
|
}
|
|
555
567
|
|
|
556
568
|
const embedCandidates =
|