@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +1 -1
- package/Dockerfile +8 -0
- package/docs/activation-funnel-telemetry.md +13 -7
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/openapi.yaml +530 -107
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +8 -0
- package/src/__tests__/activation-early-marking.test.ts +6 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
- package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
- package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
- package/src/__tests__/app-bundler.test.ts +4 -12
- package/src/__tests__/app-executors.test.ts +2 -49
- package/src/__tests__/app-routes-csp.test.ts +114 -146
- package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
- package/src/__tests__/build-persisted-content.test.ts +96 -0
- package/src/__tests__/bundle-scanner.test.ts +27 -1
- package/src/__tests__/call-controller.test.ts +291 -0
- package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
- package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
- package/src/__tests__/config-loader-backfill.test.ts +9 -4
- package/src/__tests__/config-schema-cmd.test.ts +10 -11
- package/src/__tests__/config-schema.test.ts +190 -257
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
- package/src/__tests__/conversation-agent-loop.test.ts +22 -9
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
- package/src/__tests__/conversation-process-callsite.test.ts +12 -19
- package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
- package/src/__tests__/conversation-summarize-route.test.ts +36 -44
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
- package/src/__tests__/conversation-usage.test.ts +4 -12
- package/src/__tests__/credential-routes.test.ts +250 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/db-migration-rollback.test.ts +22 -0
- package/src/__tests__/empty-response-hook.test.ts +186 -1
- package/src/__tests__/external-plugin-loader.test.ts +11 -5
- package/src/__tests__/heartbeat-service.test.ts +0 -28
- package/src/__tests__/host-shell-tool.test.ts +2 -0
- package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
- package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -16
- package/src/__tests__/list-all-apps.test.ts +0 -4
- package/src/__tests__/llm-context-resolution.test.ts +32 -56
- package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
- package/src/__tests__/llm-resolver.test.ts +342 -602
- package/src/__tests__/llm-schema.test.ts +79 -37
- package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
- package/src/__tests__/media-stream-output.test.ts +259 -3
- package/src/__tests__/media-stream-server-integration.test.ts +22 -1
- package/src/__tests__/media-stream-stt-session.test.ts +47 -0
- package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
- package/src/__tests__/memory-recall-log-store.test.ts +47 -13
- package/src/__tests__/mock-gateway-ipc.ts +46 -1
- package/src/__tests__/mtime-cache.test.ts +61 -0
- package/src/__tests__/navigate-settings-tab.test.ts +2 -0
- package/src/__tests__/normalize-onboarding.test.ts +33 -0
- package/src/__tests__/onboarding-persona-write.test.ts +26 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
- package/src/__tests__/provider-connections-backfill.test.ts +138 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
- package/src/__tests__/provider-registry-ollama.test.ts +8 -18
- package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
- package/src/__tests__/provider-usage-tracking.test.ts +9 -19
- package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
- package/src/__tests__/published-app-updater.test.ts +22 -16
- package/src/__tests__/registry.test.ts +5 -16
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
- package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
- package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
- package/src/__tests__/reveal-success-registry.test.ts +123 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
- package/src/__tests__/secret-fixtures.ts +9 -0
- package/src/__tests__/server-history-render.test.ts +28 -0
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/slack-share-routes.test.ts +0 -1
- package/src/__tests__/stt-stream-session.test.ts +6 -5
- package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
- package/src/__tests__/subagent-disposal.test.ts +2 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +37 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
- package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
- package/src/__tests__/telemetry-routes.test.ts +99 -19
- package/src/__tests__/tool-audit.test.ts +34 -4
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-profiler.test.ts +72 -1
- package/src/__tests__/tool-result-spool.test.ts +49 -4
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
- package/src/__tests__/ui-channel-variants.test.ts +108 -0
- package/src/__tests__/ui-shape-teaching.test.ts +255 -0
- package/src/__tests__/usage-attribution.test.ts +18 -41
- package/src/__tests__/user-plugin-loader.test.ts +4 -4
- package/src/__tests__/voice-config-update.test.ts +46 -0
- package/src/__tests__/voice-session-bridge.test.ts +216 -84
- package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
- package/src/agent/loop.ts +4 -3
- package/src/api/events/open-conversation.test.ts +64 -0
- package/src/api/events/open-conversation.ts +33 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +25 -35
- package/src/bundler/app-bundler.ts +34 -48
- package/src/bundler/app-compiler.ts +39 -4
- package/src/bundler/bundle-scanner.ts +13 -0
- package/src/bundler/manifest.ts +1 -1
- package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
- package/src/calls/call-constants.ts +5 -0
- package/src/calls/call-controller.ts +100 -32
- package/src/calls/call-transport.ts +9 -0
- package/src/calls/media-stream-output.ts +107 -4
- package/src/calls/media-stream-server.ts +29 -9
- package/src/calls/media-stream-stt-session.ts +7 -1
- package/src/calls/media-turn-detector.ts +11 -1
- package/src/calls/voice-session-bridge.ts +84 -63
- package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
- package/src/cli/commands/__tests__/notifications.test.ts +24 -3
- package/src/cli/commands/config.help.ts +6 -6
- package/src/cli/commands/credentials.help.ts +13 -0
- package/src/cli/commands/credentials.ts +6 -1
- package/src/cli/commands/email.help.ts +7 -0
- package/src/cli/commands/email.ts +35 -1
- package/src/cli/commands/inference-providers.ts +168 -107
- package/src/cli/commands/inference.help.ts +118 -46
- package/src/cli/commands/memory/index.help.ts +23 -0
- package/src/cli/commands/memory/nodes.ts +146 -0
- package/src/cli/commands/notifications.help.ts +10 -10
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
- package/src/cli/commands/oauth/connect.test.ts +126 -0
- package/src/cli/commands/oauth/connect.ts +29 -6
- package/src/cli/commands/oauth/index.help.ts +7 -1
- package/src/cli/commands/oauth/status.test.ts +69 -3
- package/src/cli/commands/oauth/status.ts +50 -12
- package/src/cli/commands/plugins.help.ts +13 -2
- package/src/cli/commands/plugins.ts +61 -7
- package/src/cli/commands/telemetry.help.ts +13 -0
- package/src/cli/commands/telemetry.ts +45 -5
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
- package/src/cli/lib/bundled-marketplace.json +93 -36
- package/src/cli/lib/inspect-plugin.ts +5 -1
- package/src/cli/lib/install-from-github.ts +68 -4
- package/src/cli/lib/plugin-catalog-local.ts +6 -2
- package/src/cli/lib/plugin-fingerprint.ts +3 -3
- package/src/cli/lib/upgrade-plugin.ts +236 -35
- package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
- package/src/config/__tests__/profile-materialization.test.ts +1 -88
- package/src/config/bundled-skills/AGENTS.md +3 -30
- package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
- package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
- package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/call-site-defaults.ts +3 -3
- package/src/config/feature-flag-registry.json +24 -39
- package/src/config/llm-resolver.ts +88 -475
- package/src/config/profile-materialization.ts +11 -11
- package/src/config/schema.ts +93 -127
- package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
- package/src/config/schemas/__tests__/stt.test.ts +31 -3
- package/src/config/schemas/live-voice.ts +10 -4
- package/src/config/schemas/llm.ts +52 -15
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +8 -0
- package/src/config/schemas/memory-v2.ts +2 -2
- package/src/config/schemas/memory-v3.ts +1 -1
- package/src/config/schemas/services.ts +6 -3
- package/src/config/schemas/stt.ts +38 -21
- package/src/config/schemas/tts.ts +13 -18
- package/src/config/skills.ts +31 -21
- package/src/context/compactor.ts +44 -7
- package/src/context/post-turn-tool-result-truncation.ts +4 -2
- package/src/context/tool-result-spool.ts +26 -5
- package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
- package/src/conversations/message-consolidation.ts +22 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
- package/src/daemon/app-source-watcher.ts +17 -23
- package/src/daemon/chat-credential-redaction.ts +1365 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
- package/src/daemon/conversation-agent-loop.ts +71 -31
- package/src/daemon/conversation-error.ts +36 -14
- package/src/daemon/conversation-process.ts +22 -0
- package/src/daemon/conversation-store.ts +35 -0
- package/src/daemon/conversation-surfaces.ts +25 -25
- package/src/daemon/conversation-tool-setup.ts +33 -7
- package/src/daemon/conversation.ts +55 -5
- package/src/daemon/handlers/shared.ts +33 -2
- package/src/daemon/lifecycle.ts +4 -4
- package/src/daemon/message-types/conversations.ts +6 -17
- package/src/daemon/providers-setup.ts +8 -0
- package/src/daemon/tool-setup-types.ts +7 -1
- package/src/daemon/wake-conversation-ops.ts +10 -1
- package/src/hooks/types.ts +9 -0
- package/src/ipc/__tests__/email-ipc.test.ts +90 -0
- package/src/ipc/gateway-client.test.ts +59 -0
- package/src/ipc/gateway-client.ts +70 -27
- package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
- package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
- package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
- package/src/live-voice/__tests__/protocol.test.ts +122 -0
- package/src/live-voice/live-voice-session.ts +443 -40
- package/src/live-voice/protocol.ts +143 -1
- package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
- package/src/monitoring/plugin-source-watch.ts +3 -62
- package/src/notifications/README.md +1 -1
- package/src/permissions/checker.ts +8 -4
- package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
- package/src/persistence/conversation-crud.ts +104 -2
- package/src/persistence/db-init.ts +14 -3
- package/src/persistence/job-handlers/cleanup.ts +15 -7
- package/src/persistence/llm-request-log-store.ts +88 -52
- package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
- package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
- package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
- package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
- package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
- package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
- package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
- package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
- package/src/persistence/migrations/helpers/relocation.ts +44 -1
- package/src/persistence/migrations/run-migrations.ts +25 -1
- package/src/persistence/schema/infrastructure.ts +9 -0
- package/src/persistence/schema/memory-core.ts +3 -0
- package/src/persistence/schema/memory-injection.ts +2 -0
- package/src/persistence/steps.ts +36 -0
- package/src/platform/client.test.ts +1 -44
- package/src/platform/client.ts +10 -20
- package/src/platform/consent-cache.test.ts +89 -23
- package/src/platform/consent-cache.ts +71 -33
- package/src/plugin-api/constants.ts +12 -0
- package/src/plugin-api/conversation-turn.ts +37 -14
- package/src/plugin-api/index.ts +11 -1
- package/src/plugin-api/resolve-credential.ts +75 -0
- package/src/plugin-api/vision-support.test.ts +8 -19
- package/src/plugin-api/vision-support.ts +26 -17
- package/src/plugins/collect-source-versions.ts +77 -0
- package/src/plugins/defaults/compaction/compact.ts +6 -0
- package/src/plugins/defaults/compaction/window-manager.ts +9 -0
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
- package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
- package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
- package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
- package/src/plugins/defaults/index.ts +8 -1
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
- package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
- package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
- package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
- package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
- package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
- package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
- package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
- package/src/plugins/defaults/memory/graph/store.ts +114 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
- package/src/plugins/defaults/memory/graph/tools.ts +47 -13
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
- package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
- package/src/plugins/defaults/memory/memory-db.ts +3 -2
- package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
- package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
- package/src/plugins/defaults/memory/path-containment.ts +21 -0
- package/src/plugins/defaults/memory/prompt-override.ts +51 -13
- package/src/plugins/defaults/memory/tools.test.ts +34 -0
- package/src/plugins/defaults/memory/tools.ts +12 -2
- package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
- package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
- package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
- package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
- package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
- package/src/plugins/defaults/memory/v2/migration.ts +46 -16
- package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
- package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
- package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
- package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
- package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
- package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
- package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
- package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
- package/src/plugins/defaults/memory/v3/prune.ts +17 -11
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
- package/src/plugins/external-plugin-loader.ts +15 -15
- package/src/plugins/mtime-cache.ts +47 -0
- package/src/plugins/pipeline.ts +9 -1
- package/src/plugins/plugin-execution-context.ts +44 -0
- package/src/plugins/plugin-tree-walk.ts +31 -24
- package/src/plugins/source-fingerprint.ts +3 -4
- package/src/prompts/normalize-onboarding.ts +12 -0
- package/src/prompts/persona-resolver.ts +8 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
- package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
- package/src/providers/__tests__/retry-callsite.test.ts +235 -215
- package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
- package/src/providers/atlascloud/client.ts +10 -49
- package/src/providers/baseten/client.ts +43 -0
- package/src/providers/call-site-routing.ts +26 -13
- package/src/providers/connection-resolution.ts +9 -11
- package/src/providers/fetch-provider-catalog.ts +4 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
- package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
- package/src/providers/inference/adapter-factory.ts +27 -6
- package/src/providers/inference/auth.ts +29 -1
- package/src/providers/inference/backfill.ts +61 -10
- package/src/providers/inference/connection-availability.ts +3 -2
- package/src/providers/inference/resolve-auth.ts +9 -1
- package/src/providers/model-catalog.ts +37 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
- package/src/providers/openai/api-key-validation.ts +70 -0
- package/src/providers/retry.ts +2 -0
- package/src/providers/types.ts +11 -4
- package/src/providers/vellum-model-routing.test.ts +26 -0
- package/src/providers/vellum-model-routing.ts +30 -0
- package/src/providers/voice-error-copy.ts +47 -0
- package/src/runtime/agent-wake.ts +40 -24
- package/src/runtime/for-chat-mint-registry.ts +118 -0
- package/src/runtime/reveal-nonce.ts +49 -0
- package/src/runtime/reveal-success-registry.ts +306 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
- package/src/runtime/routes/app-management-routes.ts +152 -45
- package/src/runtime/routes/app-routes.ts +16 -77
- package/src/runtime/routes/canned-message-complete.ts +16 -16
- package/src/runtime/routes/conversation-management-routes.ts +17 -14
- package/src/runtime/routes/conversation-query-routes.ts +54 -6
- package/src/runtime/routes/conversation-routes.ts +48 -2
- package/src/runtime/routes/credential-routes.ts +116 -3
- package/src/runtime/routes/email-routes.ts +17 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
- package/src/runtime/routes/inference-send-routes.ts +1 -1
- package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
- package/src/runtime/routes/plugins-routes.ts +37 -6
- package/src/runtime/routes/publish-routes.ts +15 -18
- package/src/runtime/routes/schedule-worker-routes.ts +9 -0
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/telemetry-routes.ts +149 -45
- package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
- package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
- package/src/schedule/schedule-store.ts +12 -1
- package/src/schedule/schedule-timezone.ts +63 -0
- package/src/schedule/scheduler.ts +100 -6
- package/src/schedule/worker-control.ts +19 -0
- package/src/security/auth-fallback-events-store.ts +7 -6
- package/src/security/secret-scanner.ts +26 -1
- package/src/services/published-app-updater.ts +6 -11
- package/src/stt/stt-stream-session.ts +36 -16
- package/src/subagent/manager.ts +34 -8
- package/src/telemetry/AGENTS.md +30 -1
- package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
- package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
- package/src/telemetry/config-setting-snapshot.ts +44 -10
- package/src/telemetry/telemetry-event-sources.test.ts +124 -30
- package/src/telemetry/telemetry-event-sources.ts +137 -83
- package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
- package/src/telemetry/telemetry-events-outbox.ts +60 -9
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire-validation.ts +39 -2
- package/src/telemetry/telemetry-wire.generated.ts +8 -0
- package/src/telemetry/tool-audit.ts +15 -9
- package/src/telemetry/tool-executed-events-store.test.ts +1 -1
- package/src/telemetry/turn-events-store.ts +20 -0
- package/src/telemetry/types.ts +45 -12
- package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
- package/src/telemetry/usage-telemetry-reporter.ts +117 -18
- package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
- package/src/telemetry/watchdog-direct-emit.ts +11 -6
- package/src/tools/apps/executors.ts +11 -36
- package/src/tools/executor.ts +14 -1
- package/src/tools/host-terminal/host-shell.ts +6 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
- package/src/tools/network/web-fetch.ts +6 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/subagent/spawn.ts +7 -11
- package/src/tools/terminal/shell.ts +5 -2
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/tool-profiler.ts +37 -6
- package/src/tools/ui-surface/channel-variants.ts +101 -0
- package/src/tools/ui-surface/definitions.ts +23 -67
- package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
- package/src/tts/__tests__/provider-adapters.test.ts +18 -0
- package/src/tts/provider-catalog.ts +2 -4
- package/src/tts/providers/deepgram-provider.ts +10 -1
- package/src/types/onboarding-context.ts +8 -0
- package/src/usage/attribution.ts +18 -112
- package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
- package/src/util/provider-error-patterns.ts +7 -1
- package/src/util/worker-process.ts +105 -0
- package/src/watcher/__tests__/telemetry.test.ts +17 -4
- package/src/watcher/telemetry.ts +5 -5
- package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/provider-commit-message-generator.ts +6 -4
- package/src/__tests__/app-open-proxy.test.ts +0 -67
- package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
- package/src/onboarding/onboarding-research-events-store.ts +0 -104
- package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
- package/src/tools/apps/definitions.ts +0 -73
- package/src/tools/apps/open-proxy.ts +0 -43
|
@@ -5,21 +5,12 @@
|
|
|
5
5
|
* tool use."
|
|
6
6
|
*
|
|
7
7
|
* This is the root cause of repeated failures in memory graph operations
|
|
8
|
-
* (extraction, narrative, pattern-scan, consolidation) when the
|
|
9
|
-
*
|
|
8
|
+
* (extraction, narrative, pattern-scan, consolidation) when the resolved LLM
|
|
9
|
+
* config has `thinking.enabled: true` and the call site uses forced
|
|
10
10
|
* `tool_choice` without explicitly disabling thinking.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
16
|
-
|
|
17
|
-
// Legacy-shaped fixtures (llm.default-centric resolution): pinned to the
|
|
18
|
-
// flag-off cascade. Override-or-default (flag-on) semantics are pinned by
|
|
19
|
-
// llm-resolver-override-or-default.test.ts and its companion suites.
|
|
20
|
-
beforeAll(() => {
|
|
21
|
-
setOverridesForTesting({ "override-or-default-resolution": false });
|
|
22
|
-
});
|
|
13
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
23
14
|
|
|
24
15
|
import { RetryProvider } from "../providers/retry.js";
|
|
25
16
|
import type {
|
|
@@ -72,13 +63,13 @@ const userMessage: Message = {
|
|
|
72
63
|
describe("retry normalization: thinking + forced tool_choice", () => {
|
|
73
64
|
test("strips thinking when tool_choice forces a specific tool (type: 'tool')", async () => {
|
|
74
65
|
setLlmConfig({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
66
|
+
callSites: {
|
|
67
|
+
memoryExtraction: {
|
|
68
|
+
provider: "anthropic",
|
|
69
|
+
model: "claude-opus-4-7",
|
|
70
|
+
thinking: { enabled: true, streamThinking: true },
|
|
71
|
+
},
|
|
79
72
|
},
|
|
80
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
81
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
82
73
|
});
|
|
83
74
|
const { provider, lastConfig } = makePipeline("anthropic");
|
|
84
75
|
await provider.sendMessage([userMessage], {
|
|
@@ -98,13 +89,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
98
89
|
|
|
99
90
|
test("strips thinking when tool_choice type is 'any'", async () => {
|
|
100
91
|
setLlmConfig({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
callSites: {
|
|
93
|
+
memoryExtraction: {
|
|
94
|
+
provider: "anthropic",
|
|
95
|
+
model: "claude-opus-4-7",
|
|
96
|
+
thinking: { enabled: true },
|
|
97
|
+
},
|
|
105
98
|
},
|
|
106
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
107
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
108
99
|
});
|
|
109
100
|
const { provider, lastConfig } = makePipeline("anthropic");
|
|
110
101
|
await provider.sendMessage([userMessage], {
|
|
@@ -118,13 +109,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
118
109
|
|
|
119
110
|
test("preserves thinking when tool_choice type is 'auto'", async () => {
|
|
120
111
|
setLlmConfig({
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
112
|
+
callSites: {
|
|
113
|
+
memoryExtraction: {
|
|
114
|
+
provider: "anthropic",
|
|
115
|
+
model: "claude-opus-4-7",
|
|
116
|
+
thinking: { enabled: true },
|
|
117
|
+
},
|
|
125
118
|
},
|
|
126
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
127
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
128
119
|
});
|
|
129
120
|
const { provider, lastConfig } = makePipeline("anthropic");
|
|
130
121
|
await provider.sendMessage([userMessage], {
|
|
@@ -138,13 +129,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
138
129
|
|
|
139
130
|
test("preserves thinking when no tool_choice is set", async () => {
|
|
140
131
|
setLlmConfig({
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
132
|
+
callSites: {
|
|
133
|
+
memoryExtraction: {
|
|
134
|
+
provider: "anthropic",
|
|
135
|
+
model: "claude-opus-4-7",
|
|
136
|
+
thinking: { enabled: true },
|
|
137
|
+
},
|
|
145
138
|
},
|
|
146
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
147
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
148
139
|
});
|
|
149
140
|
const { provider, lastConfig } = makePipeline("anthropic");
|
|
150
141
|
await provider.sendMessage([userMessage], {
|
|
@@ -184,13 +175,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
184
175
|
|
|
185
176
|
test("preserves resolved thinking: disabled with forced tool_choice", async () => {
|
|
186
177
|
setLlmConfig({
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
178
|
+
callSites: {
|
|
179
|
+
memoryExtraction: {
|
|
180
|
+
provider: "anthropic",
|
|
181
|
+
model: "claude-opus-4-7",
|
|
182
|
+
thinking: { enabled: false },
|
|
183
|
+
},
|
|
191
184
|
},
|
|
192
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
193
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
194
185
|
});
|
|
195
186
|
const { provider, lastConfig } = makePipeline("anthropic");
|
|
196
187
|
await provider.sendMessage([userMessage], {
|
|
@@ -204,13 +195,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
204
195
|
|
|
205
196
|
test("strips thinking for openrouter with anthropic model and forced tool_choice", async () => {
|
|
206
197
|
setLlmConfig({
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
198
|
+
callSites: {
|
|
199
|
+
memoryExtraction: {
|
|
200
|
+
provider: "openrouter",
|
|
201
|
+
model: "anthropic/claude-opus-4.7",
|
|
202
|
+
thinking: { enabled: true },
|
|
203
|
+
},
|
|
211
204
|
},
|
|
212
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
213
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
214
205
|
});
|
|
215
206
|
const { provider, lastConfig } = makePipeline("openrouter");
|
|
216
207
|
await provider.sendMessage([userMessage], {
|
|
@@ -224,13 +215,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
224
215
|
|
|
225
216
|
test("preserves thinking for openrouter with non-anthropic model and forced tool_choice", async () => {
|
|
226
217
|
setLlmConfig({
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
218
|
+
callSites: {
|
|
219
|
+
memoryExtraction: {
|
|
220
|
+
provider: "openrouter",
|
|
221
|
+
model: "x-ai/grok-3-mini",
|
|
222
|
+
thinking: { enabled: true },
|
|
223
|
+
},
|
|
231
224
|
},
|
|
232
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
233
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
234
225
|
});
|
|
235
226
|
const { provider, lastConfig } = makePipeline("openrouter");
|
|
236
227
|
await provider.sendMessage([userMessage], {
|
|
@@ -246,13 +237,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
246
237
|
|
|
247
238
|
test("strips thinking for vercel-ai-gateway with anthropic model and forced tool_choice", async () => {
|
|
248
239
|
setLlmConfig({
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
240
|
+
callSites: {
|
|
241
|
+
memoryExtraction: {
|
|
242
|
+
provider: "vercel-ai-gateway",
|
|
243
|
+
model: "anthropic/claude-opus-4.6",
|
|
244
|
+
thinking: { enabled: true },
|
|
245
|
+
},
|
|
253
246
|
},
|
|
254
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
255
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
256
247
|
});
|
|
257
248
|
const { provider, lastConfig } = makePipeline("vercel-ai-gateway");
|
|
258
249
|
await provider.sendMessage([userMessage], {
|
|
@@ -266,13 +257,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
|
|
|
266
257
|
|
|
267
258
|
test("preserves thinking for vercel-ai-gateway with non-anthropic model and forced tool_choice", async () => {
|
|
268
259
|
setLlmConfig({
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
260
|
+
callSites: {
|
|
261
|
+
memoryExtraction: {
|
|
262
|
+
provider: "vercel-ai-gateway",
|
|
263
|
+
model: "xai/grok-4.3",
|
|
264
|
+
thinking: { enabled: true },
|
|
265
|
+
},
|
|
273
266
|
},
|
|
274
|
-
// Disable the catalog default so resolution lands on llm.default.
|
|
275
|
-
profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
|
|
276
267
|
});
|
|
277
268
|
const { provider, lastConfig } = makePipeline("vercel-ai-gateway");
|
|
278
269
|
await provider.sendMessage([userMessage], {
|
|
@@ -5,16 +5,7 @@
|
|
|
5
5
|
* the unknown field on the wire.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
11
|
-
|
|
12
|
-
// Legacy-shaped fixtures (llm.default-centric resolution): pinned to the
|
|
13
|
-
// flag-off cascade. Override-or-default (flag-on) semantics are pinned by
|
|
14
|
-
// llm-resolver-override-or-default.test.ts and its companion suites.
|
|
15
|
-
beforeAll(() => {
|
|
16
|
-
setOverridesForTesting({ "override-or-default-resolution": false });
|
|
17
|
-
});
|
|
8
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
18
9
|
|
|
19
10
|
import { RetryProvider } from "../providers/retry.js";
|
|
20
11
|
import type {
|
|
@@ -67,10 +58,12 @@ const userMessage: Message = {
|
|
|
67
58
|
describe("retry normalization for verbosity", () => {
|
|
68
59
|
test("forwards verbosity on the outbound config for openai", async () => {
|
|
69
60
|
setLlmConfig({
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
61
|
+
callSites: {
|
|
62
|
+
mainAgent: {
|
|
63
|
+
provider: "openai",
|
|
64
|
+
model: "gpt-5.5",
|
|
65
|
+
verbosity: "high",
|
|
66
|
+
},
|
|
74
67
|
},
|
|
75
68
|
});
|
|
76
69
|
const { provider, lastConfig } = makePipeline("openai");
|
|
@@ -96,13 +89,17 @@ describe("retry normalization for verbosity", () => {
|
|
|
96
89
|
expect(lastConfig()?.verbosity).toBe(undefined);
|
|
97
90
|
});
|
|
98
91
|
|
|
99
|
-
test("call-site override replaces
|
|
92
|
+
test("call-site override replaces the winning profile's verbosity", async () => {
|
|
100
93
|
setLlmConfig({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
94
|
+
profiles: {
|
|
95
|
+
"openai-profile": {
|
|
96
|
+
source: "user",
|
|
97
|
+
provider: "openai",
|
|
98
|
+
model: "gpt-5.5",
|
|
99
|
+
verbosity: "low",
|
|
100
|
+
},
|
|
105
101
|
},
|
|
102
|
+
activeProfile: "openai-profile",
|
|
106
103
|
callSites: {
|
|
107
104
|
mainAgent: { verbosity: "high" },
|
|
108
105
|
},
|
|
@@ -116,11 +113,15 @@ describe("retry normalization for verbosity", () => {
|
|
|
116
113
|
|
|
117
114
|
test("per-call explicit verbosity overrides resolved call-site value", async () => {
|
|
118
115
|
setLlmConfig({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
profiles: {
|
|
117
|
+
"openai-profile": {
|
|
118
|
+
source: "user",
|
|
119
|
+
provider: "openai",
|
|
120
|
+
model: "gpt-5.5",
|
|
121
|
+
verbosity: "low",
|
|
122
|
+
},
|
|
123
123
|
},
|
|
124
|
+
activeProfile: "openai-profile",
|
|
124
125
|
callSites: {
|
|
125
126
|
mainAgent: { verbosity: "medium" },
|
|
126
127
|
},
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the reveal-success registry's retention behavior (LUM-2768).
|
|
3
|
+
*
|
|
4
|
+
* The registry is the ground truth the chat-credential persist seams use
|
|
5
|
+
* to prove a reveal ran; losing a record before its tool window closes
|
|
6
|
+
* silently drops the candidate and the printed value persists raw. Recent
|
|
7
|
+
* records must therefore survive the count cap — only the age bound may
|
|
8
|
+
* remove them while a window could still be active.
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
afterEach,
|
|
12
|
+
beforeEach,
|
|
13
|
+
describe,
|
|
14
|
+
expect,
|
|
15
|
+
setSystemTime,
|
|
16
|
+
test,
|
|
17
|
+
} from "bun:test";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
_recordCountForTest,
|
|
21
|
+
_resetRevealSuccessRegistryForTest,
|
|
22
|
+
closeRevealProofWindow,
|
|
23
|
+
currentRevealSuccessWatermark,
|
|
24
|
+
openRevealProofWindow,
|
|
25
|
+
recordRevealSuccess,
|
|
26
|
+
revealedValueSince,
|
|
27
|
+
} from "../runtime/reveal-success-registry.js";
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
_resetRevealSuccessRegistryForTest();
|
|
31
|
+
// Recording is gated on an open proof window (a staged tool reveal);
|
|
32
|
+
// these tests exercise retention semantics, so arm one for the file.
|
|
33
|
+
openRevealProofWindow();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
setSystemTime();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const TEST_NONCE = "nonce-registry-test";
|
|
41
|
+
|
|
42
|
+
describe("reveal success registry retention", () => {
|
|
43
|
+
test("recent records survive the count cap while a tool window is active", () => {
|
|
44
|
+
// A single command with hundreds of reveal invocations records every
|
|
45
|
+
// success BEFORE the tool result triggers the proof check. A hard
|
|
46
|
+
// count cap would evict the earliest ones mid-window, so their values
|
|
47
|
+
// could never become candidates.
|
|
48
|
+
const watermark = currentRevealSuccessWatermark();
|
|
49
|
+
for (let i = 0; i < 300; i++) {
|
|
50
|
+
recordRevealSuccess(`service-${i}`, "api_key", `value-${i}`, TEST_NONCE);
|
|
51
|
+
}
|
|
52
|
+
expect(
|
|
53
|
+
revealedValueSince(watermark, "service-0", "api_key", TEST_NONCE),
|
|
54
|
+
).toBe("value-0");
|
|
55
|
+
expect(
|
|
56
|
+
revealedValueSince(watermark, "service-299", "api_key", TEST_NONCE),
|
|
57
|
+
).toBe("value-299");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("watermark gating still excludes earlier successes", () => {
|
|
61
|
+
recordRevealSuccess("openai", "api_key", "before-watermark", TEST_NONCE);
|
|
62
|
+
const watermark = currentRevealSuccessWatermark();
|
|
63
|
+
expect(
|
|
64
|
+
revealedValueSince(watermark, "openai", "api_key", TEST_NONCE),
|
|
65
|
+
).toBeUndefined();
|
|
66
|
+
recordRevealSuccess("openai", "api_key", "after-watermark", TEST_NONCE);
|
|
67
|
+
expect(revealedValueSince(watermark, "openai", "api_key", TEST_NONCE)).toBe(
|
|
68
|
+
"after-watermark",
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("records nothing while no proof window is open", () => {
|
|
73
|
+
// A user's own local CLI reveal outside any assistant tool turn has no
|
|
74
|
+
// pending proof to satisfy — retaining its plaintext for the age bound
|
|
75
|
+
// would expand every CLI reveal's exposure for nothing.
|
|
76
|
+
_resetRevealSuccessRegistryForTest();
|
|
77
|
+
recordRevealSuccess("svc", "f", "hunter2-no-window", TEST_NONCE);
|
|
78
|
+
expect(_recordCountForTest()).toBe(0);
|
|
79
|
+
expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBeUndefined();
|
|
80
|
+
|
|
81
|
+
// With a window open the same reveal records; closing the last window
|
|
82
|
+
// both stops further recording and drops the retained plaintext.
|
|
83
|
+
const token = openRevealProofWindow();
|
|
84
|
+
recordRevealSuccess("svc", "f", "hunter2-windowed", TEST_NONCE);
|
|
85
|
+
expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBe(
|
|
86
|
+
"hunter2-windowed",
|
|
87
|
+
);
|
|
88
|
+
closeRevealProofWindow(token);
|
|
89
|
+
recordRevealSuccess("svc", "f", "hunter2-after-close", TEST_NONCE);
|
|
90
|
+
expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBeUndefined();
|
|
91
|
+
expect(_recordCountForTest()).toBe(0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("closing the last proof window drops retained plaintext immediately", () => {
|
|
95
|
+
// Proof consumption reads happen BEFORE a window closes, so once the
|
|
96
|
+
// last window is gone nothing can ever read the records again — they
|
|
97
|
+
// must not sit in memory until the age bound.
|
|
98
|
+
_resetRevealSuccessRegistryForTest();
|
|
99
|
+
const first = openRevealProofWindow();
|
|
100
|
+
const second = openRevealProofWindow();
|
|
101
|
+
recordRevealSuccess("svc", "f", "hunter2-windowed", TEST_NONCE);
|
|
102
|
+
closeRevealProofWindow(first);
|
|
103
|
+
// A concurrent staging (second window) may still consume the record.
|
|
104
|
+
expect(_recordCountForTest()).toBe(1);
|
|
105
|
+
closeRevealProofWindow(second);
|
|
106
|
+
expect(_recordCountForTest()).toBe(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("an expired record is EVICTED on read, not merely filtered", () => {
|
|
110
|
+
// Records hold credential plaintext. Filtering expired records out of
|
|
111
|
+
// read results while retaining them in memory would keep a lone
|
|
112
|
+
// reveal's secret alive indefinitely — eviction must happen on any
|
|
113
|
+
// registry activity (a write, a read, or the idle timer).
|
|
114
|
+
setSystemTime(new Date("2026-01-01T00:00:00Z"));
|
|
115
|
+
recordRevealSuccess("svc", "f", "hunter2-expiring", TEST_NONCE);
|
|
116
|
+
expect(_recordCountForTest()).toBe(1);
|
|
117
|
+
|
|
118
|
+
// Past the 6-hour age bound with NO further writes.
|
|
119
|
+
setSystemTime(new Date("2026-01-01T07:00:00Z"));
|
|
120
|
+
expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBeUndefined();
|
|
121
|
+
expect(_recordCountForTest()).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createConversation,
|
|
5
|
+
ensureConversationExists,
|
|
6
|
+
getConversation,
|
|
7
|
+
} from "../persistence/conversation-crud.js";
|
|
8
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
9
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
10
|
+
|
|
11
|
+
await initializeDb();
|
|
12
|
+
|
|
13
|
+
// Capture the list-invalidation calls `runConversationTurn` fires when it
|
|
14
|
+
// creates a brand-new conversation row, without pulling in the real sync
|
|
15
|
+
// publisher (which reaches for live SSE subscribers).
|
|
16
|
+
const listChangedCalls: Array<{ kind: string; conversationId: string }> = [];
|
|
17
|
+
mock.module("../runtime/sync/resource-sync-events.js", () => ({
|
|
18
|
+
publishConversationListAndMetadataChanged: (
|
|
19
|
+
kind: string,
|
|
20
|
+
conversationId: string,
|
|
21
|
+
) => {
|
|
22
|
+
listChangedCalls.push({ kind, conversationId });
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
// Stub the heavy machinery: the in-memory conversation build (provider wiring,
|
|
27
|
+
// system prompt, history hydration) and the SSE event fan-out. The agent turn
|
|
28
|
+
// itself is a no-op — this test only asserts that the `conversations` row is
|
|
29
|
+
// persisted before the turn runs. The real `getOrCreateConversation` now
|
|
30
|
+
// creates the DB row before hydrating, so the mock mirrors that by calling
|
|
31
|
+
// `ensureConversationExists`. The persistence module is intentionally NOT
|
|
32
|
+
// mocked so the real `ensureConversationExists` runs against the real DB.
|
|
33
|
+
let lastProcessMessageConversationId: string | undefined;
|
|
34
|
+
mock.module("../daemon/conversation-store.js", () => ({
|
|
35
|
+
getOrCreateConversation: async (
|
|
36
|
+
conversationId: string,
|
|
37
|
+
options?: { conversationType?: string },
|
|
38
|
+
) => {
|
|
39
|
+
if (!getConversation(conversationId)) {
|
|
40
|
+
if (options?.conversationType) {
|
|
41
|
+
createConversation({
|
|
42
|
+
id: conversationId,
|
|
43
|
+
conversationType: options.conversationType as "standard" | "background",
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
ensureConversationExists(conversationId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
abortController: undefined,
|
|
51
|
+
isProcessing: () => false,
|
|
52
|
+
async processMessage() {
|
|
53
|
+
// The row must already exist by the time the turn persists its user
|
|
54
|
+
// message — record the id so the FK precondition can be asserted.
|
|
55
|
+
lastProcessMessageConversationId = conversationId;
|
|
56
|
+
return "user-message-id";
|
|
57
|
+
},
|
|
58
|
+
enqueueMessage: () => ({ rejected: false }),
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
64
|
+
broadcastMessage: () => {},
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
mock.module("../providers/media-resolve.js", () => ({
|
|
68
|
+
resolveMediaSourceData: () => null,
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
// Import under test AFTER the mocks are registered so its dynamic imports
|
|
72
|
+
// resolve to the stubs above.
|
|
73
|
+
const { runConversationTurn } =
|
|
74
|
+
await import("../plugin-api/conversation-turn.js");
|
|
75
|
+
|
|
76
|
+
describe("runConversationTurn persistence", () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
const db = getDb();
|
|
79
|
+
db.run("DELETE FROM messages");
|
|
80
|
+
db.run("DELETE FROM conversations");
|
|
81
|
+
listChangedCalls.length = 0;
|
|
82
|
+
lastProcessMessageConversationId = undefined;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("persists a conversations row for a freshly-minted conversation", async () => {
|
|
86
|
+
const result = await runConversationTurn({
|
|
87
|
+
content: [{ type: "text", text: "hello" }],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// The row exists on disk — not just as an in-memory Conversation object —
|
|
91
|
+
// so the user-message persist inside the turn has its FK target.
|
|
92
|
+
const row = getConversation(result.conversationId);
|
|
93
|
+
expect(row?.id).toBe(result.conversationId);
|
|
94
|
+
expect(lastProcessMessageConversationId).toBe(result.conversationId);
|
|
95
|
+
|
|
96
|
+
// Siblings/sidebars are told about the new conversation, mirroring the
|
|
97
|
+
// send-message route.
|
|
98
|
+
expect(listChangedCalls).toEqual([
|
|
99
|
+
{ kind: "created", conversationId: result.conversationId },
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("adopts a caller-supplied conversation id verbatim when no row exists", async () => {
|
|
104
|
+
const conversationId = "0f9c1e2a-3b4d-5e6f-7a8b-9c0d1e2f3a4b";
|
|
105
|
+
|
|
106
|
+
const result = await runConversationTurn({
|
|
107
|
+
conversationId,
|
|
108
|
+
content: [{ type: "text", text: "hello" }],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(result.conversationId).toBe(conversationId);
|
|
112
|
+
expect(getConversation(conversationId)?.id).toBe(conversationId);
|
|
113
|
+
expect(listChangedCalls).toEqual([{ kind: "created", conversationId }]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("is a no-op for an already-persisted conversation row", async () => {
|
|
117
|
+
const existing = createConversation({ title: "already here" });
|
|
118
|
+
listChangedCalls.length = 0;
|
|
119
|
+
|
|
120
|
+
const result = await runConversationTurn({
|
|
121
|
+
conversationId: existing.id,
|
|
122
|
+
content: [{ type: "text", text: "follow up" }],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(result.conversationId).toBe(existing.id);
|
|
126
|
+
// Row is untouched and no duplicate "created" invalidation fires.
|
|
127
|
+
expect(getConversation(existing.id)?.title).toBe("already here");
|
|
128
|
+
expect(listChangedCalls).toEqual([]);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -18,3 +18,12 @@ export const SYNTHETIC_OPENAI_PROJECT_KEY =
|
|
|
18
18
|
/** The marker `redactSecrets()` substitutes for the key above. */
|
|
19
19
|
export const OPENAI_PROJECT_KEY_REDACTION_MARKER =
|
|
20
20
|
'<redacted type="OpenAI Project Key" />';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* An opaque/manual credential value with NO scanner-recognizable shape — no
|
|
24
|
+
* known prefix, not high-entropy enough for the AWS-secret heuristic, no
|
|
25
|
+
* private-key header. The scanner leaves it untouched, so only the
|
|
26
|
+
* reveal-candidate exact-match machinery can redact it. Used to prove the
|
|
27
|
+
* candidate fallback catches values the pattern scanner cannot classify.
|
|
28
|
+
*/
|
|
29
|
+
export const SYNTHETIC_OPAQUE_CREDENTIAL = "hunter2-manual-token-value";
|
|
@@ -15,6 +15,34 @@ import { getDb } from "../persistence/db-connection.js";
|
|
|
15
15
|
import { initializeDb } from "../persistence/db-init.js";
|
|
16
16
|
await initializeDb();
|
|
17
17
|
|
|
18
|
+
describe("renderHistoryContent sentinel forgery guard", () => {
|
|
19
|
+
const FORGED = "\u3014redacted:GitHub Token:github-app:pem\u3015";
|
|
20
|
+
|
|
21
|
+
test("neutralizes sentinel-shaped text in blocks without the redaction rider (pre-feature rows)", () => {
|
|
22
|
+
const output = renderHistoryContent([
|
|
23
|
+
{ type: "text", text: `quoted: ${FORGED}` },
|
|
24
|
+
]);
|
|
25
|
+
// The word joiner breaks the chip regex; text is otherwise unchanged.
|
|
26
|
+
expect(output.textSegments[0]).toBe(
|
|
27
|
+
`quoted: \u3014\u2060redacted:GitHub Token:github-app:pem\u3015`,
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("passes sentinels through verbatim when the block carries the redaction rider", () => {
|
|
32
|
+
const output = renderHistoryContent([
|
|
33
|
+
{ type: "text", text: `key: ${FORGED}`, _redactionVersion: 2 },
|
|
34
|
+
]);
|
|
35
|
+
expect(output.textSegments[0]).toBe(`key: ${FORGED}`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("rider-less text without sentinels is untouched", () => {
|
|
39
|
+
const output = renderHistoryContent([
|
|
40
|
+
{ type: "text", text: "ordinary history text" },
|
|
41
|
+
]);
|
|
42
|
+
expect(output.textSegments[0]).toBe("ordinary history text");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
18
46
|
describe("renderHistoryContent", () => {
|
|
19
47
|
test("renders text-only content unchanged", () => {
|
|
20
48
|
const output = renderHistoryContent([
|
|
@@ -340,15 +340,20 @@ describe("plugin-resident skills", () => {
|
|
|
340
340
|
expect(skill).toBeUndefined();
|
|
341
341
|
});
|
|
342
342
|
|
|
343
|
-
test("
|
|
344
|
-
//
|
|
345
|
-
//
|
|
343
|
+
test("surfaces plugin skills when package.json name differs from the directory", () => {
|
|
344
|
+
// A plugin is installed under its slug (marketplace name or GitHub path
|
|
345
|
+
// leaf), which routinely differs from its authored `package.json` name —
|
|
346
|
+
// e.g. cognee installs to `cognee`/`vellum-assistant` while its package is
|
|
347
|
+
// named `cognee-memory`. The skill must still surface, attributed to the
|
|
348
|
+
// install directory (the identity every other surface uses).
|
|
346
349
|
writePluginSkill("caveman", "caveman", "Caveman", "Terse mode", "body", {
|
|
347
350
|
packageName: "caveman-installer",
|
|
348
351
|
});
|
|
349
352
|
|
|
350
353
|
const skill = loadSkillCatalog().find((s) => s.id === "caveman");
|
|
351
|
-
expect(skill).
|
|
354
|
+
expect(skill).toBeDefined();
|
|
355
|
+
expect(skill!.source).toBe("plugin");
|
|
356
|
+
expect(skill!.owner).toEqual({ kind: "plugin", id: "caveman" });
|
|
352
357
|
});
|
|
353
358
|
|
|
354
359
|
test("warns when a plugin directory is missing package.json", () => {
|
|
@@ -321,11 +321,12 @@ describe("SttStreamSession", () => {
|
|
|
321
321
|
const frames = ws.frames;
|
|
322
322
|
expect(frames[0].type).toBe("error");
|
|
323
323
|
expect(frames[0].category).toBe("provider-error");
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
324
|
+
// Friendly copy names the provider; the raw upstream error stays in
|
|
325
|
+
// logs and must not leak to the client.
|
|
326
|
+
expect(frames[0].message as string).toContain("Deepgram");
|
|
327
|
+
expect(frames[0].message as string).not.toContain(
|
|
328
|
+
"Mock provider connection refused",
|
|
329
|
+
);
|
|
329
330
|
expect(frames[1].type).toBe("closed");
|
|
330
331
|
expect(session.isClosed).toBe(true);
|
|
331
332
|
expect(ws.closed).toBe(true);
|