@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.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/bun.lock +0 -20
- package/docs/workspace-tools.md +33 -42
- package/eslint-rules/cli-no-daemon-internals.js +0 -6
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
- package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
- package/node_modules/@vellumai/service-contracts/package.json +0 -1
- package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
- package/openapi.yaml +0 -155
- package/package.json +1 -4
- package/scripts/test.sh +15 -36
- package/src/__tests__/actor-token-service.test.ts +14 -36
- package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
- package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
- package/src/__tests__/annotate-risk-options.test.ts +0 -2
- package/src/__tests__/approval-cascade.test.ts +0 -2
- package/src/__tests__/assistant-attachments.test.ts +0 -42
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -2
- package/src/__tests__/build-persisted-content.test.ts +0 -2
- package/src/__tests__/call-controller.test.ts +0 -19
- package/src/__tests__/channel-guardian.test.ts +58 -94
- package/src/__tests__/channel-reply-delivery.test.ts +0 -2
- package/src/__tests__/compaction-events.test.ts +0 -2
- package/src/__tests__/compaction.benchmark.test.ts +0 -2
- package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
- package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
- package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
- package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
- package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
- package/src/__tests__/config-loader-backfill.test.ts +10 -123
- package/src/__tests__/config-schema.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
- package/src/__tests__/contacts-relay-reads.test.ts +15 -13
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +0 -134
- package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
- package/src/__tests__/conversation-history-web-search.test.ts +0 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
- package/src/__tests__/conversation-pairing.test.ts +0 -2
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
- package/src/__tests__/conversation-process-callsite.test.ts +0 -2
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
- package/src/__tests__/conversation-queue.test.ts +0 -91
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
- package/src/__tests__/conversation-slash-queue.test.ts +0 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
- package/src/__tests__/conversation-speed-override.test.ts +0 -2
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
- package/src/__tests__/conversation-title-service.test.ts +0 -2
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-migration-rollback.test.ts +171 -205
- package/src/__tests__/db-test-helpers.ts +4 -5
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
- package/src/__tests__/disk-pressure-guard.test.ts +0 -41
- package/src/__tests__/dm-persistence.test.ts +0 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
- package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
- package/src/__tests__/exploration-drift-hook.test.ts +2 -3
- package/src/__tests__/filing-service.test.ts +0 -2
- package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
- package/src/__tests__/guardian-dispatch.test.ts +1 -95
- package/src/__tests__/guardian-outbound-http.test.ts +0 -13
- package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
- package/src/__tests__/heartbeat-service.test.ts +0 -2
- package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
- package/src/__tests__/host-app-control-routes.test.ts +30 -24
- package/src/__tests__/host-bash-routes.test.ts +41 -31
- package/src/__tests__/host-browser-routes.test.ts +32 -26
- package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
- package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
- package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
- package/src/__tests__/http-user-message-parity.test.ts +8 -290
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
- package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/invite-redemption-service.test.ts +0 -198
- package/src/__tests__/llm-context-normalization.test.ts +0 -105
- package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
- package/src/__tests__/llm-usage-store.test.ts +0 -25
- package/src/__tests__/mcp-health-check.test.ts +1 -2
- package/src/__tests__/media-stream-server-integration.test.ts +0 -127
- package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
- package/src/__tests__/messaging-send-tool.test.ts +0 -2
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +5 -146
- package/src/__tests__/native-web-search.test.ts +0 -2
- package/src/__tests__/non-member-access-request.test.ts +17 -189
- package/src/__tests__/notification-broadcaster.test.ts +0 -4
- package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
- package/src/__tests__/notification-deep-link.test.ts +0 -6
- package/src/__tests__/notification-guardian-path.test.ts +0 -19
- package/src/__tests__/openai-provider.test.ts +12 -22
- package/src/__tests__/openai-responses-provider.test.ts +2 -12
- package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
- package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
- package/src/__tests__/plugin-bootstrap.test.ts +73 -3
- package/src/__tests__/plugin-route-contribution.test.ts +17 -4
- package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
- package/src/__tests__/plugin-types.test.ts +2 -0
- package/src/__tests__/process-message-background-slack.test.ts +0 -2
- package/src/__tests__/process-message-display-content.test.ts +0 -2
- package/src/__tests__/provider-error-scenarios.test.ts +4 -5
- package/src/__tests__/provider-usage-tracking.test.ts +0 -39
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
- package/src/__tests__/registry.test.ts +1 -4
- package/src/__tests__/relay-server.test.ts +25 -694
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -14
- package/src/__tests__/send-endpoint-busy.test.ts +8 -30
- package/src/__tests__/skills.test.ts +0 -44
- package/src/__tests__/slack-inbound-verification.test.ts +2 -47
- package/src/__tests__/stt-hints.test.ts +13 -44
- package/src/__tests__/subagent-detail.test.ts +0 -27
- package/src/__tests__/subagent-disposal.test.ts +0 -65
- package/src/__tests__/subagent-notify-parent.test.ts +0 -2
- package/src/__tests__/subagent-role-registry.test.ts +2 -7
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +0 -2
- package/src/__tests__/suggestion-routes.test.ts +0 -2
- package/src/__tests__/title-generate-hook.test.ts +0 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
- package/src/__tests__/tool-executor.test.ts +11 -16
- package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
- package/src/__tests__/tool-start-timestamp.test.ts +0 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +0 -96
- package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +0 -33
- package/src/__tests__/web-search-backend-failure.test.ts +0 -2
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
- package/src/__tests__/workspace-tool-loader.test.ts +2 -195
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +0 -56
- package/src/api/index.ts +1 -19
- package/src/api/responses/llm-request-log-entry.ts +0 -29
- package/src/api/responses/subagent-detail.ts +0 -17
- package/src/api/surfaces.ts +3 -39
- package/src/approvals/guardian-request-resolvers.ts +11 -1
- package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +8 -10
- package/src/calls/inbound-trust-reader.ts +1 -17
- package/src/calls/media-stream-server.ts +0 -21
- package/src/calls/relay-server.ts +50 -167
- package/src/calls/relay-setup-router.ts +7 -37
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +12 -9
- package/src/calls/twilio-routes.ts +4 -14
- package/src/channels/types.ts +20 -10
- package/src/cli/commands/__tests__/cache.test.ts +1 -8
- package/src/cli/commands/cache.ts +181 -194
- package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
- package/src/cli/commands/db/status.ts +1 -37
- package/src/cli/commands/mcp.ts +218 -252
- package/src/cli/commands/memory/index.ts +0 -2
- package/src/cli/commands/plugins.ts +3 -75
- package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
- package/src/cli/lib/list-installed-plugins.ts +1 -179
- package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
- package/src/config/bundled-skills/subagent/SKILL.md +1 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +5 -38
- package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
- package/src/config/schemas/memory-lifecycle.ts +0 -12
- package/src/config/schemas/memory-v3.ts +0 -7
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/timeouts.ts +0 -8
- package/src/config/seed-inference-profiles.ts +11 -21
- package/src/config/skills.ts +5 -27
- package/src/config/sync-gated-profiles.ts +13 -12
- package/src/contacts/contacts-write.ts +0 -3
- package/src/daemon/assistant-attachments.ts +4 -27
- package/src/daemon/conversation-agent-loop.ts +0 -28
- package/src/daemon/conversation-process.ts +16 -35
- package/src/daemon/conversation-surfaces.ts +38 -111
- package/src/daemon/conversation-tool-setup.ts +16 -50
- package/src/daemon/conversation.ts +1 -13
- package/src/daemon/disk-pressure-guard.ts +2 -12
- package/src/daemon/event-loop-watchdog.ts +1 -28
- package/src/daemon/external-plugins-bootstrap.ts +34 -4
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
- package/src/daemon/handlers/config-a2a.ts +14 -6
- package/src/daemon/handlers/config-channels.ts +22 -78
- package/src/daemon/handlers/conversations.ts +0 -77
- package/src/daemon/lifecycle.ts +0 -4
- package/src/daemon/mcp-reload-service.ts +0 -10
- package/src/daemon/memory-v2-startup.test.ts +0 -72
- package/src/daemon/memory-v2-startup.ts +19 -87
- package/src/daemon/message-types/conversations.ts +0 -2
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +4 -0
- package/src/daemon/shutdown-handlers.ts +0 -20
- package/src/daemon/tool-setup-types.ts +0 -9
- package/src/daemon/workspace-tools-watcher.ts +328 -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 +0 -1
- package/src/mcp/client.ts +1 -15
- package/src/mcp/mcp-auth-orchestrator.ts +1 -6
- package/src/mcp/mcp-oauth-provider.ts +8 -19
- package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
- package/src/memory/conversation-crud.ts +0 -38
- package/src/memory/db-connection.ts +3 -22
- package/src/memory/db-init.ts +502 -36
- package/src/memory/db-singleton.ts +4 -6
- package/src/memory/jobs-worker.ts +0 -58
- package/src/memory/llm-request-log-store.ts +1 -26
- package/src/memory/llm-usage-store.ts +20 -48
- package/src/memory/memory-retrospective-job.ts +8 -9
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/registry.ts +573 -0
- package/src/memory/migrations/run-migrations.ts +6 -90
- package/src/memory/migrations/validate-migration-state.ts +66 -101
- package/src/memory/schema/conversations.ts +0 -9
- package/src/memory/schema/infrastructure.ts +0 -20
- package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
- package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
- package/src/memory/v2/cli-command-store.ts +38 -75
- package/src/memory/v2/prompts/consolidation.ts +82 -13
- package/src/memory/v2/prompts/router.ts +93 -21
- package/src/memory/v2/skill-store.ts +31 -68
- package/src/notifications/__tests__/broadcaster.test.ts +8 -16
- package/src/notifications/__tests__/decision-engine.test.ts +9 -78
- package/src/notifications/broadcaster.ts +1 -8
- package/src/notifications/decision-engine.ts +7 -15
- package/src/notifications/destination-resolver.ts +24 -68
- package/src/notifications/emit-signal.ts +14 -39
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +4 -7
- package/src/plugin-api/index.ts +6 -6
- package/src/plugin-api/types.ts +5 -3
- package/src/plugin-api/vision-support.test.ts +4 -28
- package/src/plugin-api/vision-support.ts +31 -66
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
- package/src/plugins/defaults/advisor/consult.ts +6 -110
- package/src/plugins/defaults/advisor/steering.ts +2 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
- package/src/plugins/mtime-cache.ts +291 -76
- package/src/plugins/pipeline.ts +13 -111
- package/src/plugins/types.ts +2 -0
- package/src/providers/anthropic/client.ts +0 -5
- package/src/providers/call-site-routing.ts +0 -4
- package/src/providers/model-catalog.ts +0 -16
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/chat-completions-provider.ts +83 -37
- package/src/providers/openai/responses-provider.ts +46 -50
- package/src/providers/openrouter/client.ts +0 -5
- package/src/providers/provider-send-message.ts +0 -4
- package/src/providers/ratelimit.ts +0 -4
- package/src/providers/retry.ts +0 -4
- package/src/providers/types.ts +0 -9
- package/src/providers/usage-tracking.ts +0 -4
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
- package/src/runtime/access-request-helper.ts +39 -19
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +2 -9
- package/src/runtime/auth/require-bound-guardian.ts +11 -21
- package/src/runtime/channel-verification-service.ts +31 -56
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +7 -66
- package/src/runtime/invite-redemption-service.ts +187 -198
- package/src/runtime/local-actor-identity.ts +11 -76
- package/src/runtime/pending-interactions.ts +1 -11
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +32 -8
- package/src/runtime/routes/conversation-cli-routes.ts +5 -4
- package/src/runtime/routes/conversation-list-routes.ts +7 -4
- package/src/runtime/routes/conversation-query-routes.ts +0 -72
- package/src/runtime/routes/conversation-routes.ts +85 -84
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +1 -3
- package/src/runtime/routes/guardian-action-routes.ts +5 -4
- package/src/runtime/routes/host-app-control-routes.ts +4 -5
- package/src/runtime/routes/host-bash-routes.ts +4 -5
- package/src/runtime/routes/host-browser-routes.ts +11 -9
- package/src/runtime/routes/host-cu-routes.ts +4 -5
- package/src/runtime/routes/host-file-routes.ts +4 -5
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +2 -3
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
- package/src/runtime/routes/index.ts +0 -2
- package/src/runtime/routes/llm-context-normalization.ts +0 -83
- package/src/runtime/routes/mcp-auth-routes.ts +19 -171
- package/src/runtime/routes/migration-rollback-routes.ts +3 -4
- package/src/runtime/routes/migration-routes.ts +1 -4
- package/src/runtime/routes/subagents-routes.ts +0 -5
- package/src/runtime/routes/surface-action-routes.ts +56 -42
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
- package/src/runtime/services/conversation-serializer.ts +9 -7
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +9 -85
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +0 -16
- package/src/subagent/manager.ts +0 -9
- package/src/subagent/types.ts +3 -3
- package/src/telemetry/types.ts +1 -34
- package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
- package/src/telemetry/usage-telemetry-reporter.ts +3 -87
- package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
- package/src/tools/ask-question/ask-question-tool.ts +0 -13
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +0 -18
- package/src/tools/shared/filesystem/path-policy.ts +5 -12
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +2 -9
- package/src/tools/tool-manifest.ts +0 -3
- package/src/tools/types.ts +2 -17
- package/src/tools/workspace-tools/loader.ts +244 -348
- package/src/util/errors.ts +1 -26
- package/src/util/platform.ts +0 -5
- package/src/workflows/library.test.ts +0 -140
- package/src/workflows/library.ts +28 -82
- package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
- package/src/workspace/migrations/registry.ts +0 -2
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
- package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
- package/src/__tests__/code-search-tool.test.ts +0 -585
- package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
- package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
- package/src/__tests__/plugin-pipeline.test.ts +0 -96
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
- package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
- package/src/agent/loop-exclusive-tool.test.ts +0 -150
- package/src/api/constants/sse-replay.ts +0 -41
- package/src/api/events/conversation-notice.ts +0 -26
- package/src/approvals/guardian-channel-delivery.ts +0 -30
- package/src/approvals/guardian-expiry-notifier.ts +0 -148
- package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
- package/src/cli/commands/memory/worker.ts +0 -175
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
- package/src/config/prune-seeded-callsite-defaults.ts +0 -110
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
- package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
- package/src/contacts/guardian-delivery-reader.ts +0 -223
- package/src/contacts/member-write-relay.ts +0 -189
- package/src/daemon/conversation-notices.ts +0 -60
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
- package/src/hooks/hook-loader.ts +0 -341
- package/src/mcp/mcp-header-store.ts +0 -134
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
- package/src/memory/__tests__/prompt-override.test.ts +0 -192
- package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
- package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
- package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
- package/src/memory/prompt-override.ts +0 -129
- package/src/memory/steps.ts +0 -573
- package/src/memory/watchdog-events-store.ts +0 -87
- package/src/memory/worker-control.ts +0 -118
- package/src/memory/worker-process.ts +0 -72
- package/src/notifications/__tests__/connected-channels.test.ts +0 -114
- package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
- package/src/onboarding/checkin-event.test.ts +0 -222
- package/src/onboarding/checkin-event.ts +0 -321
- package/src/onboarding/schedule-checkin.ts +0 -190
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
- package/src/plugins/defaults/advisor/context-pack.ts +0 -288
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
- package/src/plugins/surface-import.ts +0 -121
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
- package/src/providers/openai/api-error-normalization.ts +0 -270
- package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
- package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
- package/src/runtime/anchored-guardian.test.ts +0 -156
- package/src/runtime/anchored-guardian.ts +0 -135
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
- package/src/runtime/local-principal-trust.ts +0 -52
- package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
- package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
- package/src/tools/filesystem/search.ts +0 -543
- package/src/util/telemetry-db-path.ts +0 -24
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
|
@@ -12,11 +12,8 @@ import type {
|
|
|
12
12
|
|
|
13
13
|
// ─── Mocks ──────────────────────────────────────────────────────────────────
|
|
14
14
|
|
|
15
|
-
// Control doesSupportVision from the test
|
|
16
|
-
// user-prompt-submit path (ModelProfileInfo) and by model id for the
|
|
17
|
-
// post-tool-use path (bare string).
|
|
15
|
+
// Control doesSupportVision per-profile from the test.
|
|
18
16
|
let visionProfiles: Set<string>;
|
|
19
|
-
let visionModels: Set<string>;
|
|
20
17
|
let mockProfiles: ModelProfileInfo[];
|
|
21
18
|
let sendMessageResponse = {
|
|
22
19
|
content: [{ type: "text", text: "A red chart showing Q3 revenue." }],
|
|
@@ -33,10 +30,8 @@ const fakeProvider = {
|
|
|
33
30
|
// Mock @vellumai/plugin-api — only the runtime handles the plugin imports.
|
|
34
31
|
// `extractAllText` stays real (imported from the relative path, not plugin-api).
|
|
35
32
|
mock.module("@vellumai/plugin-api", () => ({
|
|
36
|
-
doesSupportVision: (
|
|
37
|
-
|
|
38
|
-
? visionModels.has(arg)
|
|
39
|
-
: visionProfiles.has(arg.key),
|
|
33
|
+
doesSupportVision: (profile: ModelProfileInfo) =>
|
|
34
|
+
visionProfiles.has(profile.key),
|
|
40
35
|
getModelProfiles: () => mockProfiles,
|
|
41
36
|
getConfiguredProvider: async () => (providerResolves ? fakeProvider : null),
|
|
42
37
|
}));
|
|
@@ -141,9 +136,6 @@ function makeToolCtx(
|
|
|
141
136
|
|
|
142
137
|
beforeEach(() => {
|
|
143
138
|
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>();
|
|
147
139
|
mockProfiles = [
|
|
148
140
|
profile("text-only", { label: "Text Only", isActive: true }),
|
|
149
141
|
profile("vision-profile", { label: "Vision" }),
|
|
@@ -263,10 +255,7 @@ describe("image-fallback user-prompt-submit hook", () => {
|
|
|
263
255
|
};
|
|
264
256
|
// Override the mock to track calls.
|
|
265
257
|
mock.module("@vellumai/plugin-api", () => ({
|
|
266
|
-
doesSupportVision: (
|
|
267
|
-
typeof arg === "string"
|
|
268
|
-
? visionModels.has(arg)
|
|
269
|
-
: visionProfiles.has(arg.key),
|
|
258
|
+
doesSupportVision: (p: ModelProfileInfo) => visionProfiles.has(p.key),
|
|
270
259
|
getModelProfiles: () => mockProfiles,
|
|
271
260
|
getConfiguredProvider: async () => trackingProvider,
|
|
272
261
|
}));
|
|
@@ -284,10 +273,7 @@ describe("image-fallback user-prompt-submit hook", () => {
|
|
|
284
273
|
|
|
285
274
|
// Restore the original mock for other tests.
|
|
286
275
|
mock.module("@vellumai/plugin-api", () => ({
|
|
287
|
-
doesSupportVision: (
|
|
288
|
-
typeof arg === "string"
|
|
289
|
-
? visionModels.has(arg)
|
|
290
|
-
: visionProfiles.has(arg.key),
|
|
276
|
+
doesSupportVision: (p: ModelProfileInfo) => visionProfiles.has(p.key),
|
|
291
277
|
getModelProfiles: () => mockProfiles,
|
|
292
278
|
getConfiguredProvider: async () =>
|
|
293
279
|
providerResolves ? fakeProvider : null,
|
|
@@ -360,10 +346,9 @@ describe("image-fallback post-tool-use hook", () => {
|
|
|
360
346
|
);
|
|
361
347
|
});
|
|
362
348
|
|
|
363
|
-
test("is a no-op when the model
|
|
364
|
-
|
|
349
|
+
test("is a no-op when the active model supports vision", async () => {
|
|
350
|
+
visionProfiles = new Set(["text-only"]); // active profile supports vision
|
|
365
351
|
const ctx = makeToolCtx({
|
|
366
|
-
model: "vision-model",
|
|
367
352
|
toolResponse: toolResult([imageBlock("shot1")]),
|
|
368
353
|
});
|
|
369
354
|
await postToolUse(ctx);
|
|
@@ -413,29 +398,4 @@ describe("image-fallback post-tool-use hook", () => {
|
|
|
413
398
|
const text = (ctx.toolResponse.contentBlocks![0] as { text: string }).text;
|
|
414
399
|
expect(text).not.toContain("saved to");
|
|
415
400
|
});
|
|
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
|
-
});
|
|
441
401
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Default `post-tool-use` hook: when the
|
|
2
|
+
* Default `post-tool-use` hook: when the active 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,14 +9,15 @@
|
|
|
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
|
+
* The active model is resolved from the workspace's active profile — the
|
|
13
|
+
* post-tool-use context carries the running model, and the active profile is
|
|
14
|
+
* what the loop is executing this turn. If that profile supports vision, the
|
|
15
|
+
* hook is a no-op and the image reaches the model untouched.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
19
|
doesSupportVision,
|
|
20
|
+
getModelProfiles,
|
|
20
21
|
type PluginHookFn,
|
|
21
22
|
type PostToolUseContext,
|
|
22
23
|
} from "@vellumai/plugin-api";
|
|
@@ -25,13 +26,13 @@ import { captionImageBlocks } from "../src/caption-blocks.js";
|
|
|
25
26
|
import { findVisionProfile } from "../src/vision-caption.js";
|
|
26
27
|
|
|
27
28
|
const postToolUse: PluginHookFn<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.
|
|
30
29
|
const blocks = ctx.toolResponse.contentBlocks;
|
|
31
|
-
if (blocks == null ||
|
|
30
|
+
if (blocks == null || blocks.length === 0) return;
|
|
32
31
|
|
|
33
|
-
// If the model
|
|
34
|
-
|
|
32
|
+
// If the active model already supports vision, leave the image in place.
|
|
33
|
+
const activeProfile = getModelProfiles().find((p) => p.isActive);
|
|
34
|
+
if (activeProfile == null) return;
|
|
35
|
+
if (doesSupportVision(activeProfile)) return;
|
|
35
36
|
|
|
36
37
|
// Find a vision-capable profile for captioning.
|
|
37
38
|
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 active 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,14 +7,13 @@
|
|
|
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
|
-
*
|
|
13
|
-
* images, the hook is a no-op.
|
|
10
|
+
* 1. Resolves the active profile from `modelProfileKey` (or the workspace's
|
|
11
|
+
* active profile when the key is `null`) and checks `doesSupportVision`.
|
|
12
|
+
* If the model already handles images, the hook is a no-op.
|
|
14
13
|
* 2. Finds a vision-capable profile for captioning via `findVisionProfile`.
|
|
15
14
|
* If none exists, images are replaced with a fail-open placeholder so the
|
|
16
15
|
* model at least knows an image was present.
|
|
17
|
-
* 3. Replaces each
|
|
16
|
+
* 3. Replaces each `ImageContent` block with a `[Image …]` text caption via
|
|
18
17
|
* {@link captionImageBlocks} (which also persists the original and caches
|
|
19
18
|
* captions across turns).
|
|
20
19
|
*
|
|
@@ -23,19 +22,28 @@
|
|
|
23
22
|
*/
|
|
24
23
|
|
|
25
24
|
import {
|
|
25
|
+
doesSupportVision,
|
|
26
|
+
getModelProfiles,
|
|
26
27
|
type PluginHookFn,
|
|
27
28
|
type UserPromptSubmitContext,
|
|
28
29
|
} from "@vellumai/plugin-api";
|
|
29
30
|
|
|
30
|
-
import {
|
|
31
|
-
captionImageBlocks,
|
|
32
|
-
needsImageFallback,
|
|
33
|
-
} from "../src/caption-blocks.js";
|
|
31
|
+
import { captionImageBlocks } from "../src/caption-blocks.js";
|
|
34
32
|
import { findVisionProfile } from "../src/vision-caption.js";
|
|
35
33
|
|
|
36
34
|
const userPromptSubmit: PluginHookFn<UserPromptSubmitContext> = async (ctx) => {
|
|
37
|
-
//
|
|
38
|
-
|
|
35
|
+
// Resolve the active profile from modelProfileKey, falling back to the
|
|
36
|
+
// workspace's active profile when the key is null (profile unchanged since
|
|
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;
|
|
39
47
|
|
|
40
48
|
// Find a vision-capable profile for captioning.
|
|
41
49
|
const visionProfileKey = findVisionProfile();
|
|
@@ -1,62 +1,31 @@
|
|
|
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 active
|
|
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
|
-
*
|
|
10
|
-
* known location, caption it via a vision-capable profile, and swap in a
|
|
11
|
-
* `[Image …]` text block.
|
|
7
|
+
* screenshot). This module holds the per-block substitution they share —
|
|
8
|
+
* persist the original image to a known location, caption it via a
|
|
9
|
+
* vision-capable profile, and swap in a `[Image …]` text block.
|
|
12
10
|
*
|
|
13
|
-
* The
|
|
14
|
-
* image
|
|
15
|
-
*
|
|
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.
|
|
11
|
+
* The caption text states up front that the active model can't view images and
|
|
12
|
+
* the image was auto-described to text, so the model treats the block as a
|
|
13
|
+
* derived description rather than a verbatim transcript.
|
|
21
14
|
*
|
|
22
15
|
* Fail-open is the dominant error mode: a captioning failure leaves a
|
|
23
16
|
* placeholder text block rather than the raw image (which a text-only provider
|
|
24
17
|
* would reject) or nothing (which would lose information).
|
|
25
18
|
*/
|
|
26
19
|
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
type ImageContent,
|
|
32
|
-
type PluginLogger,
|
|
20
|
+
import type {
|
|
21
|
+
ContentBlock,
|
|
22
|
+
ImageContent,
|
|
23
|
+
PluginLogger,
|
|
33
24
|
} from "@vellumai/plugin-api";
|
|
34
25
|
|
|
35
26
|
import { persistImage } from "./image-persist.js";
|
|
36
27
|
import { captionImage } from "./vision-caption.js";
|
|
37
28
|
|
|
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
|
-
|
|
60
29
|
/**
|
|
61
30
|
* Replace every `image` block in `blocks` (in place) with a text caption so a
|
|
62
31
|
* text-only model can still reason about the image's content. Returns the
|
|
@@ -67,17 +67,13 @@ 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 a failed turn. */
|
|
71
|
+
let turnResults = new Map<number, OrchestrateResult | null>();
|
|
72
72
|
const observeTurnSpy = mock(
|
|
73
73
|
async (
|
|
74
74
|
_conversationId: string,
|
|
75
75
|
turnIndex: number,
|
|
76
|
-
): Promise<OrchestrateResult | null> =>
|
|
77
|
-
const value = turnResults.get(turnIndex) ?? null;
|
|
78
|
-
if (value instanceof Error) throw value;
|
|
79
|
-
return value;
|
|
80
|
-
},
|
|
76
|
+
): Promise<OrchestrateResult | null> => turnResults.get(turnIndex) ?? null,
|
|
81
77
|
);
|
|
82
78
|
|
|
83
79
|
const logCalls: Array<{ data: unknown; msg: string }> = [];
|
|
@@ -203,9 +199,6 @@ const {
|
|
|
203
199
|
} = await import("../ever-injected-store.js");
|
|
204
200
|
const { V3_CARDS_INJECTION_HEADER } = await import("../render-injection.js");
|
|
205
201
|
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");
|
|
209
202
|
|
|
210
203
|
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
211
204
|
|
|
@@ -320,7 +313,6 @@ beforeEach(async () => {
|
|
|
320
313
|
logCalls.length = 0;
|
|
321
314
|
testDb = makeDb();
|
|
322
315
|
resetMemoryV3InjectorStateForTests();
|
|
323
|
-
resetConversationNoticesForTests();
|
|
324
316
|
});
|
|
325
317
|
|
|
326
318
|
afterAll(async () => {
|
|
@@ -343,28 +335,6 @@ describe("memoryV3Injector — frozen net-new cards", () => {
|
|
|
343
335
|
expect(getActiveSlugs("conv-1")).toEqual(new Set());
|
|
344
336
|
});
|
|
345
337
|
|
|
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
|
-
|
|
368
338
|
test("turn 1 renders cards; turn 2 re-selecting the same pages renders ZERO new cards", async () => {
|
|
369
339
|
liveEnabled = true;
|
|
370
340
|
turnResults.set(0, result(["page-a", "page-b"]));
|
|
@@ -26,7 +26,6 @@
|
|
|
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";
|
|
30
29
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
31
30
|
|
|
32
31
|
import type {
|
|
@@ -36,7 +35,6 @@ import type {
|
|
|
36
35
|
SendMessageOptions,
|
|
37
36
|
ToolUseContent,
|
|
38
37
|
} from "../../../../providers/types.js";
|
|
39
|
-
import { ProviderError } from "../../../../util/errors.js";
|
|
40
38
|
import type { MemoryRoutingTurn } from "../types.js";
|
|
41
39
|
|
|
42
40
|
// ---------------------------------------------------------------------------
|
|
@@ -45,11 +43,6 @@ import type { MemoryRoutingTurn } from "../types.js";
|
|
|
45
43
|
// ---------------------------------------------------------------------------
|
|
46
44
|
|
|
47
45
|
let providerStub: Provider | null = null;
|
|
48
|
-
const registryReal = {
|
|
49
|
-
...(createRequire(import.meta.url)(
|
|
50
|
-
"../../../../providers/registry.js",
|
|
51
|
-
) as Record<string, unknown>),
|
|
52
|
-
};
|
|
53
46
|
|
|
54
47
|
interface ProviderCall {
|
|
55
48
|
messages: Message[];
|
|
@@ -64,12 +57,6 @@ mock.module("../../../../providers/provider-send-message.js", () => ({
|
|
|
64
57
|
response.content.find((b): b is ToolUseContent => b.type === "tool_use"),
|
|
65
58
|
}));
|
|
66
59
|
|
|
67
|
-
mock.module("../../../../providers/registry.js", () => ({
|
|
68
|
-
...registryReal,
|
|
69
|
-
getProviderRoutingSource: (providerName: string) =>
|
|
70
|
-
providerName === "managed" ? "managed-proxy" : "user-key",
|
|
71
|
-
}));
|
|
72
|
-
|
|
73
60
|
mock.module("../../../../util/logger.js", () => ({
|
|
74
61
|
getLogger: () => ({
|
|
75
62
|
warn: (...args: unknown[]) => warnCalls.push({ args }),
|
|
@@ -266,9 +253,10 @@ describe("selectPool — id mapping", () => {
|
|
|
266
253
|
});
|
|
267
254
|
|
|
268
255
|
// ---------------------------------------------------------------------------
|
|
269
|
-
// selectPool — infrastructure failures THROW
|
|
270
|
-
// an empty pool (covered above) still return
|
|
271
|
-
//
|
|
256
|
+
// selectPool — infrastructure failures THROW (no silent degradation). A
|
|
257
|
+
// deliberate empty selection and an empty pool (covered above) still return
|
|
258
|
+
// normally; only a genuine infra failure throws so the LIVE injector can
|
|
259
|
+
// hard-fail the turn instead of shipping it with no memory.
|
|
272
260
|
// ---------------------------------------------------------------------------
|
|
273
261
|
|
|
274
262
|
describe("selectPool — infrastructure failures throw", () => {
|
|
@@ -383,38 +371,6 @@ describe("selectPool — infrastructure failures throw", () => {
|
|
|
383
371
|
]);
|
|
384
372
|
});
|
|
385
373
|
|
|
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
|
-
|
|
418
374
|
test("provider throw redacts sensitive message details in diagnostics", async () => {
|
|
419
375
|
const providerSecret = ["sk-proj-", "a".repeat(40)].join("");
|
|
420
376
|
const message = `provider rejected Authorization: Bearer ${providerSecret}`;
|
|
@@ -808,7 +808,7 @@ describe("memory-v3 shadow plugin", () => {
|
|
|
808
808
|
});
|
|
809
809
|
});
|
|
810
810
|
|
|
811
|
-
describe("memory-v3 infrastructure-failure handling", () => {
|
|
811
|
+
describe("memory-v3 infrastructure-failure handling (hard-fail vs swallow)", () => {
|
|
812
812
|
const throwInfra = () =>
|
|
813
813
|
orchestrateSpy.mockImplementationOnce(async () => {
|
|
814
814
|
throw new MemoryV3RetrievalUnavailableError(
|
|
@@ -816,12 +816,14 @@ describe("memory-v3 infrastructure-failure handling", () => {
|
|
|
816
816
|
);
|
|
817
817
|
});
|
|
818
818
|
|
|
819
|
-
test("LIVE injector
|
|
819
|
+
test("LIVE injector HARD-FAILS the turn on an infra failure (no silent memory loss)", async () => {
|
|
820
820
|
liveEnabled = true;
|
|
821
821
|
shadowEnabled = false;
|
|
822
822
|
throwInfra();
|
|
823
823
|
|
|
824
|
-
expect(
|
|
824
|
+
await expect(produce("conv-infra-live", 0)).rejects.toThrow(
|
|
825
|
+
MemoryV3RetrievalUnavailableError,
|
|
826
|
+
);
|
|
825
827
|
});
|
|
826
828
|
|
|
827
829
|
test("SHADOW injector swallows an infra failure (v2 fallback) — no throw, no block", async () => {
|
|
@@ -830,7 +832,7 @@ describe("memory-v3 infrastructure-failure handling", () => {
|
|
|
830
832
|
throwInfra();
|
|
831
833
|
|
|
832
834
|
// Shadow mode: v2 retrieval still ran this turn, so the v3 injector returns
|
|
833
|
-
// null.
|
|
835
|
+
// null rather than failing the turn.
|
|
834
836
|
expect(await produce("conv-infra-shadow", 0)).toBeNull();
|
|
835
837
|
});
|
|
836
838
|
|
|
@@ -851,6 +853,8 @@ describe("memory-v3 infrastructure-failure handling", () => {
|
|
|
851
853
|
throw new Error("some unexpected non-infra bug");
|
|
852
854
|
});
|
|
853
855
|
|
|
856
|
+
// Only INFRA failures hard-fail; any other error stays non-fatal so a bug
|
|
857
|
+
// in one lane can't take every turn down.
|
|
854
858
|
expect(await produce("conv-nonfatal-live", 0)).toBeNull();
|
|
855
859
|
});
|
|
856
860
|
});
|
|
@@ -63,10 +63,6 @@
|
|
|
63
63
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
64
64
|
import { getConfig } from "../../../config/loader.js";
|
|
65
65
|
import { isMemoryV3Live } from "../../../config/memory-v3-gate.js";
|
|
66
|
-
import {
|
|
67
|
-
type PendingConversationNotice,
|
|
68
|
-
queueConversationNotice,
|
|
69
|
-
} from "../../../daemon/conversation-notices.js";
|
|
70
66
|
import { isPersonalMemoryAllowed } from "../../../daemon/trust-context.js";
|
|
71
67
|
import {
|
|
72
68
|
wrapMemoryBlock,
|
|
@@ -124,26 +120,6 @@ function lruSet<V>(map: Map<string, V>, key: string, value: V): void {
|
|
|
124
120
|
map.set(key, value);
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
function queueMemoryV3ConversationNotice(
|
|
128
|
-
err: MemoryV3RetrievalUnavailableError,
|
|
129
|
-
ctx: TurnContext,
|
|
130
|
-
live: boolean,
|
|
131
|
-
): void {
|
|
132
|
-
if (!live) return;
|
|
133
|
-
const notice: PendingConversationNotice = err.conversationNotice ?? {
|
|
134
|
-
source: "memory_v3",
|
|
135
|
-
code: "UNKNOWN",
|
|
136
|
-
userMessage:
|
|
137
|
-
"Memory is temporarily unavailable, so this response may not use your saved memories. You can retry in a moment.",
|
|
138
|
-
errorCategory: "memory_v3_degraded",
|
|
139
|
-
};
|
|
140
|
-
queueConversationNotice(
|
|
141
|
-
ctx.conversationId,
|
|
142
|
-
`memory_v3:${ctx.turnIndex}:${notice.errorCategory ?? notice.code}`,
|
|
143
|
-
notice,
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
123
|
// ─── shared per-turn orchestration memo ─────────────────────────────────────
|
|
148
124
|
|
|
149
125
|
interface ObservedTurn {
|
|
@@ -269,16 +245,16 @@ export const memoryV3Injector: Injector = {
|
|
|
269
245
|
try {
|
|
270
246
|
observed = await observeTurnOnce(ctx.conversationId, ctx.turnIndex);
|
|
271
247
|
} catch (err) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
248
|
+
// A memory-v3 INFRASTRUCTURE failure (the selector lost its provider —
|
|
249
|
+
// e.g. a transient CES credential blip). Under `memory-v3-live` the
|
|
250
|
+
// user-prompt-submit hook already skipped v2 retrieval, so swallowing
|
|
251
|
+
// here would ship the turn with NO memory at all — exactly the silent
|
|
252
|
+
// degradation we want to eliminate. Hard-fail the turn instead (a clean,
|
|
253
|
+
// retryable error). In shadow mode v2 still ran this turn, so fall back
|
|
254
|
+
// to it (return null). Non-infra errors are already swallowed inside
|
|
255
|
+
// observeTurn; anything else reaching here stays non-fatal.
|
|
256
|
+
if (live && err instanceof MemoryV3RetrievalUnavailableError) {
|
|
257
|
+
throw err;
|
|
282
258
|
}
|
|
283
259
|
return null;
|
|
284
260
|
}
|
|
@@ -429,16 +405,12 @@ export const memoryV3SpotlightInjector: Injector = {
|
|
|
429
405
|
placement: "after-memory-prefix",
|
|
430
406
|
};
|
|
431
407
|
} catch (err) {
|
|
408
|
+
// Live-only injector: an infra failure must hard-fail the turn. The cards
|
|
409
|
+
// injector (ordered ahead of this one) normally throws first, so this
|
|
410
|
+
// path is defensive — it keeps the behavior correct if the cards injector
|
|
411
|
+
// is ever disabled or reordered.
|
|
432
412
|
if (err instanceof MemoryV3RetrievalUnavailableError) {
|
|
433
|
-
|
|
434
|
-
log.error(
|
|
435
|
-
{
|
|
436
|
-
err: err.message,
|
|
437
|
-
conversationId: ctx.conversationId,
|
|
438
|
-
},
|
|
439
|
-
"memory-v3 spotlight selection failed; skipping spotlight",
|
|
440
|
-
);
|
|
441
|
-
return null;
|
|
413
|
+
throw err;
|
|
442
414
|
}
|
|
443
415
|
log.warn(
|
|
444
416
|
{
|
|
@@ -119,10 +119,6 @@ export interface OrchestrateDeps {
|
|
|
119
119
|
/** Hard cap on total learned-lane surfaced articles; `0` disables the pass
|
|
120
120
|
* (canonical value: `memory.v3.learnedEdges.cap`). */
|
|
121
121
|
learnedCap?: number;
|
|
122
|
-
/** The selector's system prompt. Omitted → the selector's bundled default.
|
|
123
|
-
* The live caller resolves `memory.v3.selectorPromptPath` (workspace-relative
|
|
124
|
-
* file override) via `resolveSelectorPrompt` and threads the result here. */
|
|
125
|
-
selectorPrompt?: string;
|
|
126
122
|
}
|
|
127
123
|
|
|
128
124
|
/** A finder-lane candidate: the slug, the descriptor that justified it, and
|
|
@@ -382,13 +378,8 @@ export async function orchestrate(
|
|
|
382
378
|
|
|
383
379
|
// Step 3: a SINGLE forced-tool select over the cache-ordered pool. The
|
|
384
380
|
// selections come back slug-deduped (pinned flags ORed) — `selectPool`'s
|
|
385
|
-
// contract.
|
|
386
|
-
|
|
387
|
-
const selections = await selectPool(
|
|
388
|
-
{ stable, finder: finderTail },
|
|
389
|
-
turn,
|
|
390
|
-
deps.selectorPrompt,
|
|
391
|
-
);
|
|
381
|
+
// contract.
|
|
382
|
+
const selections = await selectPool({ stable, finder: finderTail }, turn);
|
|
392
383
|
|
|
393
384
|
return {
|
|
394
385
|
selections,
|