@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- package/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +42 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +134 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +16 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
|
-
|
|
13
|
+
HookFunction,
|
|
14
14
|
UserPromptSubmitContext,
|
|
15
15
|
} from "@vellumai/plugin-api";
|
|
16
16
|
|
|
17
17
|
import { repairHistory } from "../terminal.js";
|
|
18
18
|
|
|
19
|
-
const userPromptSubmit:
|
|
19
|
+
const userPromptSubmit: HookFunction<UserPromptSubmitContext> = async (ctx) => {
|
|
20
20
|
const { messages, stats } = repairHistory(ctx.latestMessages);
|
|
21
21
|
ctx.latestMessages = messages;
|
|
22
22
|
if (
|
|
@@ -12,8 +12,11 @@ import type {
|
|
|
12
12
|
|
|
13
13
|
// ─── Mocks ──────────────────────────────────────────────────────────────────
|
|
14
14
|
|
|
15
|
-
// Control doesSupportVision
|
|
15
|
+
// Control doesSupportVision from the test: by profile key for the
|
|
16
|
+
// user-prompt-submit path (ModelProfileInfo) and by model id for the
|
|
17
|
+
// post-tool-use path (bare string).
|
|
16
18
|
let visionProfiles: Set<string>;
|
|
19
|
+
let visionModels: Set<string>;
|
|
17
20
|
let mockProfiles: ModelProfileInfo[];
|
|
18
21
|
let sendMessageResponse = {
|
|
19
22
|
content: [{ type: "text", text: "A red chart showing Q3 revenue." }],
|
|
@@ -30,8 +33,10 @@ const fakeProvider = {
|
|
|
30
33
|
// Mock @vellumai/plugin-api — only the runtime handles the plugin imports.
|
|
31
34
|
// `extractAllText` stays real (imported from the relative path, not plugin-api).
|
|
32
35
|
mock.module("@vellumai/plugin-api", () => ({
|
|
33
|
-
doesSupportVision: (
|
|
34
|
-
|
|
36
|
+
doesSupportVision: (arg: ModelProfileInfo | string) =>
|
|
37
|
+
typeof arg === "string"
|
|
38
|
+
? visionModels.has(arg)
|
|
39
|
+
: visionProfiles.has(arg.key),
|
|
35
40
|
getModelProfiles: () => mockProfiles,
|
|
36
41
|
getConfiguredProvider: async () => (providerResolves ? fakeProvider : null),
|
|
37
42
|
}));
|
|
@@ -136,6 +141,9 @@ function makeToolCtx(
|
|
|
136
141
|
|
|
137
142
|
beforeEach(() => {
|
|
138
143
|
visionProfiles = new Set<string>(["vision-profile"]);
|
|
144
|
+
// "text-only-model" (the default post-tool-use ctx.model) is absent, so it
|
|
145
|
+
// reads as text-only; a vision model id is added per-test.
|
|
146
|
+
visionModels = new Set<string>();
|
|
139
147
|
mockProfiles = [
|
|
140
148
|
profile("text-only", { label: "Text Only", isActive: true }),
|
|
141
149
|
profile("vision-profile", { label: "Vision" }),
|
|
@@ -255,7 +263,10 @@ describe("image-fallback user-prompt-submit hook", () => {
|
|
|
255
263
|
};
|
|
256
264
|
// Override the mock to track calls.
|
|
257
265
|
mock.module("@vellumai/plugin-api", () => ({
|
|
258
|
-
doesSupportVision: (
|
|
266
|
+
doesSupportVision: (arg: ModelProfileInfo | string) =>
|
|
267
|
+
typeof arg === "string"
|
|
268
|
+
? visionModels.has(arg)
|
|
269
|
+
: visionProfiles.has(arg.key),
|
|
259
270
|
getModelProfiles: () => mockProfiles,
|
|
260
271
|
getConfiguredProvider: async () => trackingProvider,
|
|
261
272
|
}));
|
|
@@ -273,7 +284,10 @@ describe("image-fallback user-prompt-submit hook", () => {
|
|
|
273
284
|
|
|
274
285
|
// Restore the original mock for other tests.
|
|
275
286
|
mock.module("@vellumai/plugin-api", () => ({
|
|
276
|
-
doesSupportVision: (
|
|
287
|
+
doesSupportVision: (arg: ModelProfileInfo | string) =>
|
|
288
|
+
typeof arg === "string"
|
|
289
|
+
? visionModels.has(arg)
|
|
290
|
+
: visionProfiles.has(arg.key),
|
|
277
291
|
getModelProfiles: () => mockProfiles,
|
|
278
292
|
getConfiguredProvider: async () =>
|
|
279
293
|
providerResolves ? fakeProvider : null,
|
|
@@ -346,9 +360,10 @@ describe("image-fallback post-tool-use hook", () => {
|
|
|
346
360
|
);
|
|
347
361
|
});
|
|
348
362
|
|
|
349
|
-
test("is a no-op when the
|
|
350
|
-
|
|
363
|
+
test("is a no-op when the model that ran supports vision", async () => {
|
|
364
|
+
visionModels = new Set(["vision-model"]);
|
|
351
365
|
const ctx = makeToolCtx({
|
|
366
|
+
model: "vision-model",
|
|
352
367
|
toolResponse: toolResult([imageBlock("shot1")]),
|
|
353
368
|
});
|
|
354
369
|
await postToolUse(ctx);
|
|
@@ -398,4 +413,29 @@ describe("image-fallback post-tool-use hook", () => {
|
|
|
398
413
|
const text = (ctx.toolResponse.contentBlocks![0] as { text: string }).text;
|
|
399
414
|
expect(text).not.toContain("saved to");
|
|
400
415
|
});
|
|
416
|
+
|
|
417
|
+
test("is a no-op when contentBlocks carry no image", async () => {
|
|
418
|
+
const textBlock = { type: "text" as const, text: "just text" };
|
|
419
|
+
const ctx = makeToolCtx({ toolResponse: toolResult([textBlock]) });
|
|
420
|
+
await postToolUse(ctx);
|
|
421
|
+
expect(ctx.toolResponse.contentBlocks![0]).toEqual(textBlock);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("gates on ctx.model, not the workspace active profile", async () => {
|
|
425
|
+
// The active profile is vision-capable, but the model that actually ran
|
|
426
|
+
// (ctx.model) is text-only — the model that ran must win, so the image is
|
|
427
|
+
// captioned.
|
|
428
|
+
mockProfiles = [
|
|
429
|
+
profile("vision-active", { isActive: true }),
|
|
430
|
+
profile("vision-profile", {}),
|
|
431
|
+
];
|
|
432
|
+
visionProfiles = new Set(["vision-active", "vision-profile"]);
|
|
433
|
+
visionModels = new Set<string>(); // "text-only-model" is text-only
|
|
434
|
+
const ctx = makeToolCtx({
|
|
435
|
+
model: "text-only-model",
|
|
436
|
+
toolResponse: toolResult([imageBlock("shot1")]),
|
|
437
|
+
});
|
|
438
|
+
await postToolUse(ctx);
|
|
439
|
+
expect(ctx.toolResponse.contentBlocks![0].type).toBe("text");
|
|
440
|
+
});
|
|
401
441
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Default `post-tool-use` hook: when the
|
|
2
|
+
* Default `post-tool-use` hook: when the turn's model is text-only, captions
|
|
3
3
|
* the image blocks a tool returns (e.g. a `browser_screenshot`) and
|
|
4
4
|
* substitutes the caption as a text block so the result stays sendable to a
|
|
5
5
|
* provider that would otherwise reject the raw image.
|
|
@@ -9,30 +9,29 @@
|
|
|
9
9
|
* rather than the top-level message content the `user-prompt-submit` hook
|
|
10
10
|
* handles. Both share {@link captionImageBlocks}.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* Capability is read straight off `ctx.model` — the provider-reported model id
|
|
13
|
+
* for the turn that issued this tool call — so the decision tracks the model
|
|
14
|
+
* that actually ran, including a text-only override. The substitution is in
|
|
15
|
+
* place, so the persisted/displayed tool result carries the caption too.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
19
|
doesSupportVision,
|
|
20
|
-
|
|
21
|
-
type PluginHookFn,
|
|
20
|
+
type HookFunction,
|
|
22
21
|
type PostToolUseContext,
|
|
23
22
|
} from "@vellumai/plugin-api";
|
|
24
23
|
|
|
25
24
|
import { captionImageBlocks } from "../src/caption-blocks.js";
|
|
26
25
|
import { findVisionProfile } from "../src/vision-caption.js";
|
|
27
26
|
|
|
28
|
-
const postToolUse:
|
|
27
|
+
const postToolUse: HookFunction<PostToolUseContext> = async (ctx) => {
|
|
28
|
+
// Cheapest gate first: bail unless the tool actually returned an image,
|
|
29
|
+
// before touching the model catalog or resolving a vision profile.
|
|
29
30
|
const blocks = ctx.toolResponse.contentBlocks;
|
|
30
|
-
if (blocks == null || blocks.
|
|
31
|
+
if (blocks == null || !blocks.some((b) => b.type === "image")) return;
|
|
31
32
|
|
|
32
|
-
// If the
|
|
33
|
-
|
|
34
|
-
if (activeProfile == null) return;
|
|
35
|
-
if (doesSupportVision(activeProfile)) return;
|
|
33
|
+
// If the model that ran already supports vision, leave the image in place.
|
|
34
|
+
if (doesSupportVision(ctx.model)) return;
|
|
36
35
|
|
|
37
36
|
// Find a vision-capable profile for captioning.
|
|
38
37
|
const visionProfileKey = findVisionProfile();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Default `user-prompt-submit` hook: when the
|
|
2
|
+
* Default `user-prompt-submit` hook: when the turn's model is text-only,
|
|
3
3
|
* captions image blocks via a vision-capable profile and substitutes the
|
|
4
4
|
* caption as a text block so the model can still reason about the image's
|
|
5
5
|
* content.
|
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* The hook runs once per user turn, after the assistant assembles
|
|
8
8
|
* `latestMessages` and before they flow into `agentLoop.run()`. It:
|
|
9
9
|
*
|
|
10
|
-
* 1.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 1. Checks whether the turn's model needs image→text fallback via
|
|
11
|
+
* {@link needsImageFallback} (resolving the turn's `modelProfileKey`, or the
|
|
12
|
+
* workspace's active profile when the key is `null`). If the model handles
|
|
13
|
+
* images, the hook is a no-op.
|
|
13
14
|
* 2. Finds a vision-capable profile for captioning via `findVisionProfile`.
|
|
14
15
|
* If none exists, images are replaced with a fail-open placeholder so the
|
|
15
16
|
* model at least knows an image was present.
|
|
16
|
-
* 3. Replaces each
|
|
17
|
+
* 3. Replaces each image block with a `[Image …]` text caption via
|
|
17
18
|
* {@link captionImageBlocks} (which also persists the original and caches
|
|
18
19
|
* captions across turns).
|
|
19
20
|
*
|
|
@@ -22,28 +23,19 @@
|
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
import {
|
|
25
|
-
|
|
26
|
-
getModelProfiles,
|
|
27
|
-
type PluginHookFn,
|
|
26
|
+
type HookFunction,
|
|
28
27
|
type UserPromptSubmitContext,
|
|
29
28
|
} from "@vellumai/plugin-api";
|
|
30
29
|
|
|
31
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
captionImageBlocks,
|
|
32
|
+
needsImageFallback,
|
|
33
|
+
} from "../src/caption-blocks.js";
|
|
32
34
|
import { findVisionProfile } from "../src/vision-caption.js";
|
|
33
35
|
|
|
34
|
-
const userPromptSubmit:
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
// the last notified turn).
|
|
38
|
-
const profiles = getModelProfiles();
|
|
39
|
-
const activeProfile =
|
|
40
|
-
ctx.modelProfileKey != null
|
|
41
|
-
? profiles.find((p) => p.key === ctx.modelProfileKey)
|
|
42
|
-
: profiles.find((p) => p.isActive);
|
|
43
|
-
if (activeProfile == null) return;
|
|
44
|
-
|
|
45
|
-
// If the active model already supports vision, nothing to do.
|
|
46
|
-
if (doesSupportVision(activeProfile)) return;
|
|
36
|
+
const userPromptSubmit: HookFunction<UserPromptSubmitContext> = async (ctx) => {
|
|
37
|
+
// If the turn's model already supports vision, nothing to do.
|
|
38
|
+
if (!needsImageFallback(ctx.modelProfileKey)) return;
|
|
47
39
|
|
|
48
40
|
// Find a vision-capable profile for captioning.
|
|
49
41
|
const visionProfileKey = findVisionProfile();
|
|
@@ -1,31 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared image→text substitution for the image-fallback plugin's hooks.
|
|
3
3
|
*
|
|
4
|
-
* Two hooks replace `image` content blocks with a text caption when the
|
|
4
|
+
* Two hooks replace `image` content blocks with a text caption when the turn's
|
|
5
5
|
* model can't process images: `user-prompt-submit` handles user-attached
|
|
6
6
|
* images, and `post-tool-use` handles images a tool returns (e.g. a browser
|
|
7
|
-
* screenshot). This module holds
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* screenshot). This module holds what they share — deciding whether a profile
|
|
8
|
+
* needs the fallback ({@link needsImageFallback}) and the per-block
|
|
9
|
+
* substitution ({@link captionImageBlocks}): persist the original image to a
|
|
10
|
+
* known location, caption it via a vision-capable profile, and swap in a
|
|
11
|
+
* `[Image …]` text block.
|
|
10
12
|
*
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* The substitution mutates the blocks in place, so the caption replaces the
|
|
14
|
+
* image everywhere the block is referenced (the provider-bound history and the
|
|
15
|
+
* persisted/displayed copy alike) — a text-only turn does not keep the raw
|
|
16
|
+
* image around.
|
|
17
|
+
*
|
|
18
|
+
* The caption text states up front that the model can't view images and the
|
|
19
|
+
* image was auto-described to text, so the model treats the block as a derived
|
|
20
|
+
* description rather than a verbatim transcript.
|
|
14
21
|
*
|
|
15
22
|
* Fail-open is the dominant error mode: a captioning failure leaves a
|
|
16
23
|
* placeholder text block rather than the raw image (which a text-only provider
|
|
17
24
|
* would reject) or nothing (which would lose information).
|
|
18
25
|
*/
|
|
19
26
|
|
|
20
|
-
import
|
|
21
|
-
ContentBlock,
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
import {
|
|
28
|
+
type ContentBlock,
|
|
29
|
+
doesSupportVision,
|
|
30
|
+
getModelProfiles,
|
|
31
|
+
type ImageContent,
|
|
32
|
+
type PluginLogger,
|
|
24
33
|
} from "@vellumai/plugin-api";
|
|
25
34
|
|
|
26
35
|
import { persistImage } from "./image-persist.js";
|
|
27
36
|
import { captionImage } from "./vision-caption.js";
|
|
28
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Whether the profile a turn runs needs image→text fallback (i.e. it can't
|
|
40
|
+
* process images itself).
|
|
41
|
+
*
|
|
42
|
+
* Used by `user-prompt-submit`, whose context carries the profile key rather
|
|
43
|
+
* than the resolved model id: prefer the turn's `modelProfileKey` — which
|
|
44
|
+
* carries a text-only override even when the workspace's active profile is
|
|
45
|
+
* vision-capable — and fall back to the active profile only when the key is
|
|
46
|
+
* `null` (profile unchanged since the last notified turn). Returns `false` when
|
|
47
|
+
* no profile resolves or the resolved model already supports vision, in which
|
|
48
|
+
* case the image reaches the model untouched.
|
|
49
|
+
*/
|
|
50
|
+
export function needsImageFallback(modelProfileKey: string | null): boolean {
|
|
51
|
+
const profiles = getModelProfiles();
|
|
52
|
+
const activeProfile =
|
|
53
|
+
modelProfileKey != null
|
|
54
|
+
? profiles.find((p) => p.key === modelProfileKey)
|
|
55
|
+
: profiles.find((p) => p.isActive);
|
|
56
|
+
if (activeProfile == null) return false;
|
|
57
|
+
return !doesSupportVision(activeProfile);
|
|
58
|
+
}
|
|
59
|
+
|
|
29
60
|
/**
|
|
30
61
|
* Replace every `image` block in `blocks` (in place) with a text caption so a
|
|
31
62
|
* text-only model can still reason about the image's content. Returns the
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* empty-response hook; only a provider rejection is this hook's to act on.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import type {
|
|
27
|
+
import type { HookFunction, PostModelCallContext } from "@vellumai/plugin-api";
|
|
28
28
|
|
|
29
29
|
import { isImageDimensionsTooLargeError } from "../detect.js";
|
|
30
30
|
import {
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
recoverOversizedImages,
|
|
37
37
|
} from "../recover.js";
|
|
38
38
|
|
|
39
|
-
const postModelCall:
|
|
39
|
+
const postModelCall: HookFunction<PostModelCallContext> = async (ctx) => {
|
|
40
40
|
if (
|
|
41
41
|
ctx.error &&
|
|
42
42
|
isImageDimensionsTooLargeError(ctx.error.message) &&
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
* retry the loop's per-run backstop refused).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type {
|
|
14
|
+
import type { HookFunction, StopContext } from "@vellumai/plugin-api";
|
|
15
15
|
|
|
16
16
|
import { clearImageRecoveryAttempted } from "../image-recovery-state-store.js";
|
|
17
17
|
|
|
18
|
-
const stop:
|
|
18
|
+
const stop: HookFunction<StopContext> = async (ctx) => {
|
|
19
19
|
clearImageRecoveryAttempted(ctx.conversationId);
|
|
20
20
|
};
|
|
21
21
|
|
|
@@ -24,15 +24,8 @@
|
|
|
24
24
|
* {@link registerDefaultPlugins} at call time.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { finalizeTool } from "../../tools/tool-defaults.js";
|
|
28
27
|
import { registerPlugin, resetPluginRegistryForTests } from "../registry.js";
|
|
29
28
|
import { type Plugin, PluginExecutionError } from "../types.js";
|
|
30
|
-
import { resetAdvisorStateForTests } from "./advisor/advisor-state-store.js";
|
|
31
|
-
import advisorPostModelCall from "./advisor/hooks/post-model-call.js";
|
|
32
|
-
import advisorPreModelCall from "./advisor/hooks/pre-model-call.js";
|
|
33
|
-
import advisorUserPromptSubmit from "./advisor/hooks/user-prompt-submit.js";
|
|
34
|
-
import advisorPkg from "./advisor/package.json" with { type: "json" };
|
|
35
|
-
import advisorTool from "./advisor/tools/advisor.js";
|
|
36
29
|
import compactionPkg from "./compaction/package.json" with { type: "json" };
|
|
37
30
|
import emptyResponsePostModelCall from "./empty-response/hooks/post-model-call.js";
|
|
38
31
|
import emptyResponseStop from "./empty-response/hooks/stop.js";
|
|
@@ -329,30 +322,6 @@ export const defaultToolResultTruncatePlugin: Plugin = {
|
|
|
329
322
|
},
|
|
330
323
|
};
|
|
331
324
|
|
|
332
|
-
/**
|
|
333
|
-
* `advisor` — adds the model-visible `advisor` tool: a no-argument tool the
|
|
334
|
-
* model calls to consult a stronger inference profile (the `inference` call
|
|
335
|
-
* site with a `quality-optimized` override) on the full transcript, routed
|
|
336
|
-
* through the assistant's own inference. Three hooks feed it: `user-prompt-submit`
|
|
337
|
-
* seeds the capture, `pre-model-call` records the executor's system prompt and
|
|
338
|
-
* injects the steering that nudges the model to consult, and `post-model-call`
|
|
339
|
-
* snapshots the transcript the tool reads. `finalizeTool` fills the tool's
|
|
340
|
-
* defaults so it satisfies `Tool`, and `bootstrapPlugins` registers it into the
|
|
341
|
-
* catalog.
|
|
342
|
-
*/
|
|
343
|
-
export const defaultAdvisorPlugin: Plugin = {
|
|
344
|
-
manifest: {
|
|
345
|
-
name: advisorPkg.name,
|
|
346
|
-
version: advisorPkg.version,
|
|
347
|
-
},
|
|
348
|
-
hooks: {
|
|
349
|
-
"user-prompt-submit": advisorUserPromptSubmit,
|
|
350
|
-
"pre-model-call": advisorPreModelCall,
|
|
351
|
-
"post-model-call": advisorPostModelCall,
|
|
352
|
-
},
|
|
353
|
-
tools: [finalizeTool(advisorTool, "advisor")],
|
|
354
|
-
};
|
|
355
|
-
|
|
356
325
|
/**
|
|
357
326
|
* Full set of first-party default plugins. Used by
|
|
358
327
|
* {@link registerDefaultPlugins} to drive the registration loop; the array
|
|
@@ -375,9 +344,6 @@ function getAllDefaultPlugins(): readonly Plugin[] {
|
|
|
375
344
|
defaultCompactionPlugin,
|
|
376
345
|
defaultTitleGeneratePlugin,
|
|
377
346
|
memoryV3ShadowPlugin,
|
|
378
|
-
// Registered last so its capture hooks observe the fully-processed turn
|
|
379
|
-
// (memory injections, history repair) that the executor actually sees.
|
|
380
|
-
defaultAdvisorPlugin,
|
|
381
347
|
];
|
|
382
348
|
}
|
|
383
349
|
|
|
@@ -424,7 +390,6 @@ export function resetPluginRegistryAndRegisterDefaults(): void {
|
|
|
424
390
|
resetExplorationDriftStateForTests();
|
|
425
391
|
resetTaskProgressNudgeStateForTests();
|
|
426
392
|
resetSurfaceCompletionNudgeStoreForTests();
|
|
427
|
-
resetAdvisorStateForTests();
|
|
428
393
|
resetCaptionCacheForTests();
|
|
429
394
|
registerDefaultPlugins();
|
|
430
395
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* reject.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import type {
|
|
33
|
+
import type { HookFunction, PostModelCallContext } from "@vellumai/plugin-api";
|
|
34
34
|
|
|
35
35
|
import { isMaxTokensStopReason } from "../../../../agent/loop.js";
|
|
36
36
|
import {
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
export const MAX_TOKENS_CONTINUE_NUDGE_TEXT =
|
|
46
46
|
"<system_notice>Your previous response was cut off because it reached the maximum output length. Continue exactly where you stopped — do not repeat content you already sent and do not start over.</system_notice>";
|
|
47
47
|
|
|
48
|
-
const postModelCall:
|
|
48
|
+
const postModelCall: HookFunction<PostModelCallContext> = async (ctx) => {
|
|
49
49
|
if (ctx.error) return;
|
|
50
50
|
if (!isMaxTokensStopReason(ctx.stopReason)) return;
|
|
51
51
|
if (ctx.callSite !== "mainAgent") return;
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
* budget, no matter how the turn ended.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type {
|
|
12
|
+
import type { HookFunction, StopContext } from "@vellumai/plugin-api";
|
|
13
13
|
|
|
14
14
|
import { clearMaxTokensContinueBudget } from "../continue-state-store.js";
|
|
15
15
|
|
|
16
|
-
const stop:
|
|
16
|
+
const stop: HookFunction<StopContext> = async (ctx) => {
|
|
17
17
|
clearMaxTokensContinueBudget(ctx.conversationId);
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* {@link PostCompactContext}.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
import type {
|
|
41
|
+
import type { HookFunction, PostCompactContext } from "@vellumai/plugin-api";
|
|
42
42
|
|
|
43
43
|
import { getConfig } from "../../../../config/loader.js";
|
|
44
44
|
import { findConversationOrSubagent } from "../../../../daemon/conversation-registry.js";
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
import { getLiveGraphMemory } from "../../../../memory/graph/conversation-graph-memory.js";
|
|
54
54
|
import { stripTailInjectionsForReinjection } from "../tail-reinjection-strip.js";
|
|
55
55
|
|
|
56
|
-
const postCompact:
|
|
56
|
+
const postCompact: HookFunction<PostCompactContext> = async (ctx) => {
|
|
57
57
|
const {
|
|
58
58
|
history,
|
|
59
59
|
requestId,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import type {
|
|
31
|
-
|
|
31
|
+
HookFunction,
|
|
32
32
|
UserPromptSubmitContext,
|
|
33
33
|
} from "@vellumai/plugin-api";
|
|
34
34
|
|
|
@@ -264,7 +264,7 @@ function persistInjectionBlocks(
|
|
|
264
264
|
* slower turn. Cancellation still works via `ctx.signal`, which is threaded
|
|
265
265
|
* into `prepareMemory`.
|
|
266
266
|
*/
|
|
267
|
-
const userPromptSubmitMemoryRetrieval:
|
|
267
|
+
const userPromptSubmitMemoryRetrieval: HookFunction<
|
|
268
268
|
UserPromptSubmitContext
|
|
269
269
|
> = async (ctx) => {
|
|
270
270
|
// The conversation-scoped retrieval state — graph handle, abort signal, and
|
|
@@ -67,13 +67,17 @@ let pruneConfig: {
|
|
|
67
67
|
maxResidentBytes: number;
|
|
68
68
|
targetResidentBytes: number;
|
|
69
69
|
} | null = null;
|
|
70
|
-
/** Canned orchestrate result per turnIndex; `null` simulates
|
|
71
|
-
let turnResults = new Map<number, OrchestrateResult | null>();
|
|
70
|
+
/** Canned orchestrate result per turnIndex; `null` simulates an ordinary miss. */
|
|
71
|
+
let turnResults = new Map<number, OrchestrateResult | null | Error>();
|
|
72
72
|
const observeTurnSpy = mock(
|
|
73
73
|
async (
|
|
74
74
|
_conversationId: string,
|
|
75
75
|
turnIndex: number,
|
|
76
|
-
): Promise<OrchestrateResult | null> =>
|
|
76
|
+
): Promise<OrchestrateResult | null> => {
|
|
77
|
+
const value = turnResults.get(turnIndex) ?? null;
|
|
78
|
+
if (value instanceof Error) throw value;
|
|
79
|
+
return value;
|
|
80
|
+
},
|
|
77
81
|
);
|
|
78
82
|
|
|
79
83
|
const logCalls: Array<{ data: unknown; msg: string }> = [];
|
|
@@ -199,6 +203,9 @@ const {
|
|
|
199
203
|
} = await import("../ever-injected-store.js");
|
|
200
204
|
const { V3_CARDS_INJECTION_HEADER } = await import("../render-injection.js");
|
|
201
205
|
const { flushPruneValveForTests } = await import("../prune.js");
|
|
206
|
+
const { drainConversationNotices, resetConversationNoticesForTests } =
|
|
207
|
+
await import("../../../../daemon/conversation-notices.js");
|
|
208
|
+
const { MemoryV3RetrievalUnavailableError } = await import("../pool-select.js");
|
|
202
209
|
|
|
203
210
|
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
204
211
|
|
|
@@ -313,6 +320,7 @@ beforeEach(async () => {
|
|
|
313
320
|
logCalls.length = 0;
|
|
314
321
|
testDb = makeDb();
|
|
315
322
|
resetMemoryV3InjectorStateForTests();
|
|
323
|
+
resetConversationNoticesForTests();
|
|
316
324
|
});
|
|
317
325
|
|
|
318
326
|
afterAll(async () => {
|
|
@@ -335,6 +343,28 @@ describe("memoryV3Injector — frozen net-new cards", () => {
|
|
|
335
343
|
expect(getActiveSlugs("conv-1")).toEqual(new Set());
|
|
336
344
|
});
|
|
337
345
|
|
|
346
|
+
test("live retrieval failure queues a degraded-memory notice", async () => {
|
|
347
|
+
liveEnabled = true;
|
|
348
|
+
turnResults.set(
|
|
349
|
+
0,
|
|
350
|
+
new MemoryV3RetrievalUnavailableError("selector unavailable"),
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
await expect(produceCardsWithoutCommit("conv-1", 0)).resolves.toBeNull();
|
|
354
|
+
|
|
355
|
+
expect(drainConversationNotices("conv-1")).toEqual([
|
|
356
|
+
{
|
|
357
|
+
type: "conversation_notice",
|
|
358
|
+
conversationId: "conv-1",
|
|
359
|
+
source: "memory_v3",
|
|
360
|
+
code: "UNKNOWN",
|
|
361
|
+
userMessage:
|
|
362
|
+
"Memory is temporarily unavailable, so this response may not use your saved memories. You can retry in a moment.",
|
|
363
|
+
errorCategory: "memory_v3_degraded",
|
|
364
|
+
},
|
|
365
|
+
]);
|
|
366
|
+
});
|
|
367
|
+
|
|
338
368
|
test("turn 1 renders cards; turn 2 re-selecting the same pages renders ZERO new cards", async () => {
|
|
339
369
|
liveEnabled = true;
|
|
340
370
|
turnResults.set(0, result(["page-a", "page-b"]));
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
* The provider is stubbed so no network calls fire; mirrors selector.test.ts.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
+
import { createRequire } from "node:module";
|
|
29
30
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
30
31
|
|
|
31
32
|
import type {
|
|
@@ -35,6 +36,7 @@ import type {
|
|
|
35
36
|
SendMessageOptions,
|
|
36
37
|
ToolUseContent,
|
|
37
38
|
} from "../../../../providers/types.js";
|
|
39
|
+
import { ProviderError } from "../../../../util/errors.js";
|
|
38
40
|
import type { MemoryRoutingTurn } from "../types.js";
|
|
39
41
|
|
|
40
42
|
// ---------------------------------------------------------------------------
|
|
@@ -43,6 +45,11 @@ import type { MemoryRoutingTurn } from "../types.js";
|
|
|
43
45
|
// ---------------------------------------------------------------------------
|
|
44
46
|
|
|
45
47
|
let providerStub: Provider | null = null;
|
|
48
|
+
const registryReal = {
|
|
49
|
+
...(createRequire(import.meta.url)(
|
|
50
|
+
"../../../../providers/registry.js",
|
|
51
|
+
) as Record<string, unknown>),
|
|
52
|
+
};
|
|
46
53
|
|
|
47
54
|
interface ProviderCall {
|
|
48
55
|
messages: Message[];
|
|
@@ -57,6 +64,12 @@ mock.module("../../../../providers/provider-send-message.js", () => ({
|
|
|
57
64
|
response.content.find((b): b is ToolUseContent => b.type === "tool_use"),
|
|
58
65
|
}));
|
|
59
66
|
|
|
67
|
+
mock.module("../../../../providers/registry.js", () => ({
|
|
68
|
+
...registryReal,
|
|
69
|
+
getProviderRoutingSource: (providerName: string) =>
|
|
70
|
+
providerName === "managed" ? "managed-proxy" : "user-key",
|
|
71
|
+
}));
|
|
72
|
+
|
|
60
73
|
mock.module("../../../../util/logger.js", () => ({
|
|
61
74
|
getLogger: () => ({
|
|
62
75
|
warn: (...args: unknown[]) => warnCalls.push({ args }),
|
|
@@ -253,10 +266,9 @@ describe("selectPool — id mapping", () => {
|
|
|
253
266
|
});
|
|
254
267
|
|
|
255
268
|
// ---------------------------------------------------------------------------
|
|
256
|
-
// selectPool — infrastructure failures THROW
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
// hard-fail the turn instead of shipping it with no memory.
|
|
269
|
+
// selectPool — infrastructure failures THROW. A deliberate empty selection and
|
|
270
|
+
// an empty pool (covered above) still return normally; only a genuine infra
|
|
271
|
+
// failure throws so callers can log it distinctly from an empty selection.
|
|
260
272
|
// ---------------------------------------------------------------------------
|
|
261
273
|
|
|
262
274
|
describe("selectPool — infrastructure failures throw", () => {
|
|
@@ -371,6 +383,38 @@ describe("selectPool — infrastructure failures throw", () => {
|
|
|
371
383
|
]);
|
|
372
384
|
});
|
|
373
385
|
|
|
386
|
+
test("managed provider 402 attaches a non-terminal credits notice", async () => {
|
|
387
|
+
providerStub = {
|
|
388
|
+
name: "managed",
|
|
389
|
+
sendMessage: async (messages, options) => {
|
|
390
|
+
providerCalls.push({ messages, options });
|
|
391
|
+
throw new ProviderError(
|
|
392
|
+
"Together AI API error (402): 402 status code (no body)",
|
|
393
|
+
"managed",
|
|
394
|
+
402,
|
|
395
|
+
);
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
let caught: unknown;
|
|
399
|
+
try {
|
|
400
|
+
await selectPool(makePool(), makeTurn("x"));
|
|
401
|
+
} catch (err) {
|
|
402
|
+
caught = err;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
expect(caught).toBeInstanceOf(MemoryV3RetrievalUnavailableError);
|
|
406
|
+
const notice = (
|
|
407
|
+
caught as InstanceType<typeof MemoryV3RetrievalUnavailableError>
|
|
408
|
+
).conversationNotice;
|
|
409
|
+
expect(notice).toEqual({
|
|
410
|
+
source: "memory_v3",
|
|
411
|
+
code: "PROVIDER_BILLING",
|
|
412
|
+
userMessage:
|
|
413
|
+
"You've run out of credits. Add funds to continue using the assistant.",
|
|
414
|
+
errorCategory: "credits_exhausted",
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
374
418
|
test("provider throw redacts sensitive message details in diagnostics", async () => {
|
|
375
419
|
const providerSecret = ["sk-proj-", "a".repeat(40)].join("");
|
|
376
420
|
const message = `provider rejected Authorization: Bearer ${providerSecret}`;
|