@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62
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 +6 -6
- package/bun.lock +2 -2
- package/docs/activation-funnel-telemetry.md +310 -0
- package/examples/plugins/echo/README.md +61 -60
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +273 -121
- package/package.json +2 -2
- package/scripts/sync-llm-catalog.ts +2 -0
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +23 -8
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +6 -5
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/history-repair-hook.test.ts +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +5 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/web-fetch.test.ts +45 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +403 -251
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/responses/conversation-message.ts +72 -13
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -29
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -3
- package/src/config/feature-flag-registry.json +10 -56
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/loader.ts +5 -22
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/memory/__tests__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -0
- package/src/memory/embedding-backend.ts +5 -22
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +9 -2
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/external-api.ts +12 -2
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +57 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +57 -7
- package/src/providers/types.ts +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/acp-routes.test.ts +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +30 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- package/src/util/map-limit.ts +0 -27
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { preModelCallSanitize } from "../agent/loop.js";
|
|
4
|
+
import type { Message } from "../providers/types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `preModelCallSanitize` is the loop's single pre-send transform: it converts
|
|
8
|
+
* historical `web_search_tool_result` blocks to text alongside the media and
|
|
9
|
+
* AX-tree strips, so every provider call — first call, post-compaction, and
|
|
10
|
+
* recovery reruns — is sanitized in one place. These tests guard that the
|
|
11
|
+
* helper actually performs the web-search conversion and is idempotent.
|
|
12
|
+
*/
|
|
13
|
+
describe("preModelCallSanitize", () => {
|
|
14
|
+
test("passes through history with nothing to sanitize", () => {
|
|
15
|
+
// GIVEN a plain conversation with no media, AX trees, or web-search blocks
|
|
16
|
+
const messages: Message[] = [
|
|
17
|
+
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
|
18
|
+
{ role: "assistant", content: [{ type: "text", text: "Hi" }] },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// WHEN the loop sanitizes the outbound history
|
|
22
|
+
const result = preModelCallSanitize(messages);
|
|
23
|
+
|
|
24
|
+
// THEN the history is returned unchanged
|
|
25
|
+
expect(result).toEqual(messages);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("converts historical web_search_tool_result blocks to text summaries", () => {
|
|
29
|
+
// GIVEN an assistant turn whose web_search_tool_result carries an opaque
|
|
30
|
+
// encrypted_content token that would be rejected if replayed
|
|
31
|
+
const messages: Message[] = [
|
|
32
|
+
{ role: "user", content: [{ type: "text", text: "Search cats" }] },
|
|
33
|
+
{
|
|
34
|
+
role: "assistant",
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: "server_tool_use",
|
|
38
|
+
id: "stu_1",
|
|
39
|
+
name: "web_search",
|
|
40
|
+
input: { query: "cats" },
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: "web_search_tool_result",
|
|
44
|
+
tool_use_id: "stu_1",
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: "web_search_result",
|
|
48
|
+
url: "https://cats.com",
|
|
49
|
+
title: "Cats!",
|
|
50
|
+
encrypted_content: "expired_token_1",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// WHEN the loop sanitizes the outbound history
|
|
59
|
+
const result = preModelCallSanitize(messages);
|
|
60
|
+
|
|
61
|
+
// THEN the opaque block is replaced with a plaintext title+URL summary and
|
|
62
|
+
// the paired server_tool_use is dropped, so no expired token is replayed
|
|
63
|
+
const assistantMsg = result[1];
|
|
64
|
+
expect(assistantMsg.content.map((b) => b.type)).toEqual(["text"]);
|
|
65
|
+
const summary = assistantMsg.content[0];
|
|
66
|
+
expect(summary.type).toBe("text");
|
|
67
|
+
if (summary.type === "text") {
|
|
68
|
+
expect(summary.text).toContain("Cats!");
|
|
69
|
+
expect(summary.text).toContain("https://cats.com");
|
|
70
|
+
expect(summary.text).not.toContain("expired_token_1");
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("is idempotent — re-sanitizing already-sanitized history is a no-op", () => {
|
|
75
|
+
// GIVEN history that has already been sanitized once
|
|
76
|
+
const messages: Message[] = [
|
|
77
|
+
{
|
|
78
|
+
role: "assistant",
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: "server_tool_use",
|
|
82
|
+
id: "stu_A",
|
|
83
|
+
name: "web_search",
|
|
84
|
+
input: { query: "alpha" },
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "web_search_tool_result",
|
|
88
|
+
tool_use_id: "stu_A",
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "web_search_result",
|
|
92
|
+
url: "https://a.example",
|
|
93
|
+
title: "A",
|
|
94
|
+
encrypted_content: "tok_A",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
const once = preModelCallSanitize(messages);
|
|
102
|
+
|
|
103
|
+
// WHEN it is sanitized a second time (every outbound call re-runs the helper)
|
|
104
|
+
const twice = preModelCallSanitize(once);
|
|
105
|
+
|
|
106
|
+
// THEN the second pass changes nothing
|
|
107
|
+
expect(twice).toEqual(once);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that the auto-redeploy path deploys the app's *effective* HTML
|
|
3
|
+
* (dist/index.html for multifile apps) rather than the empty `htmlDefinition`.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
7
|
+
|
|
8
|
+
import type { AppDefinition } from "../memory/app-store.js";
|
|
9
|
+
|
|
10
|
+
// ── Mocks ───────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
let mockApp: AppDefinition | null = null;
|
|
13
|
+
let mockIsMultifile = false;
|
|
14
|
+
let mockEffectiveHtml = "";
|
|
15
|
+
// A directory that does not exist on disk, so the multifile dist-existence
|
|
16
|
+
// guard reads `false` from the real fs without mocking node:fs (which would
|
|
17
|
+
// leak across test files).
|
|
18
|
+
let mockAppDir = "/tmp/__vellum_test_nonexistent__/app-1";
|
|
19
|
+
|
|
20
|
+
mock.module("../memory/app-store.js", () => ({
|
|
21
|
+
getApp: () => mockApp,
|
|
22
|
+
getAppDirPath: () => mockAppDir,
|
|
23
|
+
isMultifileApp: () => mockIsMultifile,
|
|
24
|
+
resolveEffectiveAppHtml: () => mockEffectiveHtml,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
let mockPublishedPage: {
|
|
28
|
+
id: string;
|
|
29
|
+
projectSlug?: string;
|
|
30
|
+
htmlHash: string;
|
|
31
|
+
} | null = null;
|
|
32
|
+
const updatePublishedPageSpy = mock(() => {});
|
|
33
|
+
|
|
34
|
+
mock.module("../memory/published-pages-store.js", () => ({
|
|
35
|
+
getActivePublishedPageByAppId: () => mockPublishedPage,
|
|
36
|
+
updatePublishedPage: updatePublishedPageSpy,
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
const deploySpy = mock(async (_args: { html: string; name: string }) => ({
|
|
40
|
+
deploymentId: "dep-1",
|
|
41
|
+
url: "https://example.vercel.app",
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
mock.module("../services/vercel-deploy.js", () => ({
|
|
45
|
+
deployHtmlToVercel: deploySpy,
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
// credentialBroker.serverUse invokes execute(token) and reports success.
|
|
49
|
+
mock.module("../tools/credentials/broker.js", () => ({
|
|
50
|
+
credentialBroker: {
|
|
51
|
+
serverUse: async ({
|
|
52
|
+
execute,
|
|
53
|
+
}: {
|
|
54
|
+
execute: (token: string) => Promise<unknown>;
|
|
55
|
+
}) => {
|
|
56
|
+
await execute("test-token");
|
|
57
|
+
return { success: true };
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
const { updatePublishedAppDeployment } =
|
|
63
|
+
await import("../services/published-app-updater.js");
|
|
64
|
+
|
|
65
|
+
function makeApp(overrides: Partial<AppDefinition> = {}): AppDefinition {
|
|
66
|
+
return {
|
|
67
|
+
id: "app-1",
|
|
68
|
+
name: "App",
|
|
69
|
+
schemaJson: "{}",
|
|
70
|
+
htmlDefinition: "",
|
|
71
|
+
createdAt: 1,
|
|
72
|
+
updatedAt: 2,
|
|
73
|
+
...overrides,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Tests ───────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
describe("updatePublishedAppDeployment", () => {
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
mockApp = makeApp();
|
|
82
|
+
mockIsMultifile = false;
|
|
83
|
+
mockEffectiveHtml = "";
|
|
84
|
+
mockPublishedPage = { id: "pp-1", projectSlug: "slug", htmlHash: "old" };
|
|
85
|
+
mockAppDir = "/tmp/__vellum_test_nonexistent__/app-1";
|
|
86
|
+
deploySpy.mockClear();
|
|
87
|
+
updatePublishedPageSpy.mockClear();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("deploys the resolved effective HTML, not the empty htmlDefinition", async () => {
|
|
91
|
+
// htmlDefinition is "" (as it is for every multifile app); the real
|
|
92
|
+
// content comes from resolveEffectiveAppHtml. isMultifile=false here keeps
|
|
93
|
+
// the dist guard out of the way so the assertion targets the html source.
|
|
94
|
+
mockApp = makeApp({ htmlDefinition: "" });
|
|
95
|
+
mockEffectiveHtml = "<html><body>real app</body></html>";
|
|
96
|
+
|
|
97
|
+
await updatePublishedAppDeployment("app-1");
|
|
98
|
+
|
|
99
|
+
expect(deploySpy).toHaveBeenCalledTimes(1);
|
|
100
|
+
expect(deploySpy.mock.calls[0][0].html).toBe(
|
|
101
|
+
"<html><body>real app</body></html>",
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("skips deploy when a multifile app has no compiled output", async () => {
|
|
106
|
+
mockIsMultifile = true;
|
|
107
|
+
mockApp = makeApp({ formatVersion: 2 });
|
|
108
|
+
mockEffectiveHtml = "<p>App compilation failed.</p>";
|
|
109
|
+
// mockAppDir does not exist → dist/index.html is absent.
|
|
110
|
+
|
|
111
|
+
await updatePublishedAppDeployment("app-1");
|
|
112
|
+
|
|
113
|
+
expect(deploySpy).not.toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("skips deploy when content hash is unchanged", async () => {
|
|
117
|
+
const html = "<html>same</html>";
|
|
118
|
+
mockEffectiveHtml = html;
|
|
119
|
+
const { createHash } = await import("node:crypto");
|
|
120
|
+
mockPublishedPage = {
|
|
121
|
+
id: "pp-1",
|
|
122
|
+
htmlHash: createHash("sha256").update(html).digest("hex"),
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
await updatePublishedAppDeployment("app-1");
|
|
126
|
+
|
|
127
|
+
expect(deploySpy).not.toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("skips deploy when the app has no active published page", async () => {
|
|
131
|
+
mockPublishedPage = null;
|
|
132
|
+
mockEffectiveHtml = "<html>x</html>";
|
|
133
|
+
|
|
134
|
+
await updatePublishedAppDeployment("app-1");
|
|
135
|
+
|
|
136
|
+
expect(deploySpy).not.toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -27,7 +27,7 @@ mock.module("../config/env.js", () => ({
|
|
|
27
27
|
}));
|
|
28
28
|
|
|
29
29
|
const _conversationMocks = new Map<string, unknown>();
|
|
30
|
-
mock.module("../daemon/conversation-
|
|
30
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
31
31
|
findConversation: (id: string) => _conversationMocks.get(id),
|
|
32
32
|
}));
|
|
33
33
|
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifies that `RetryProvider.normalizeSendMessageOptions` drops a disabled
|
|
3
|
+
* thinking config for adaptive-thinking-only models (Claude Fable), preventing
|
|
4
|
+
* an Anthropic 400: Fable always reasons with always-on adaptive thinking and
|
|
5
|
+
* rejects `thinking: { type: "disabled" }`.
|
|
6
|
+
*
|
|
7
|
+
* Effort and other parameters are unaffected, and models that genuinely support
|
|
8
|
+
* disabling thinking (e.g. Opus) keep their disabled config.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
mock.module("../util/logger.js", () => ({
|
|
14
|
+
getLogger: () =>
|
|
15
|
+
new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
let mockLlmConfig: Record<string, unknown> = {};
|
|
19
|
+
|
|
20
|
+
mock.module("../config/loader.js", () => ({
|
|
21
|
+
getConfig: () => ({ llm: mockLlmConfig }),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
import { LLMSchema } from "../config/schemas/llm.js";
|
|
25
|
+
import { RetryProvider } from "../providers/retry.js";
|
|
26
|
+
import type {
|
|
27
|
+
Message,
|
|
28
|
+
Provider,
|
|
29
|
+
ProviderResponse,
|
|
30
|
+
SendMessageOptions,
|
|
31
|
+
} from "../providers/types.js";
|
|
32
|
+
|
|
33
|
+
function setLlmConfig(raw: unknown): void {
|
|
34
|
+
mockLlmConfig = LLMSchema.parse(raw) as Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
mockLlmConfig = LLMSchema.parse({}) as Record<string, unknown>;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function makePipeline(providerName: string): {
|
|
42
|
+
provider: Provider;
|
|
43
|
+
lastConfig: () => Record<string, unknown> | undefined;
|
|
44
|
+
} {
|
|
45
|
+
let captured: Record<string, unknown> | undefined;
|
|
46
|
+
const inner: Provider = {
|
|
47
|
+
name: providerName,
|
|
48
|
+
async sendMessage(
|
|
49
|
+
_messages: Message[],
|
|
50
|
+
options?: SendMessageOptions,
|
|
51
|
+
): Promise<ProviderResponse> {
|
|
52
|
+
captured = options?.config as Record<string, unknown> | undefined;
|
|
53
|
+
return {
|
|
54
|
+
content: [],
|
|
55
|
+
model: "test",
|
|
56
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
57
|
+
stopReason: "stop",
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
provider: new RetryProvider(inner),
|
|
63
|
+
lastConfig: () => captured,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const userMessage: Message = {
|
|
68
|
+
role: "user",
|
|
69
|
+
content: [{ type: "text", text: "hi" }],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
describe("retry normalization: adaptive-only thinking models", () => {
|
|
73
|
+
test("drops resolved thinking: disabled for Claude Fable", async () => {
|
|
74
|
+
// GIVEN a profile that disables thinking for an adaptive-only Fable model
|
|
75
|
+
setLlmConfig({
|
|
76
|
+
default: {
|
|
77
|
+
provider: "anthropic",
|
|
78
|
+
model: "claude-fable-5",
|
|
79
|
+
thinking: { enabled: false },
|
|
80
|
+
effort: "high",
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const { provider, lastConfig } = makePipeline("anthropic");
|
|
84
|
+
|
|
85
|
+
// WHEN a request resolves through the call-site config
|
|
86
|
+
await provider.sendMessage([userMessage], {
|
|
87
|
+
config: { callSite: "memoryExtraction" },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// THEN the disabled thinking config is dropped so Fable falls back to its
|
|
91
|
+
// always-on adaptive thinking instead of 400-ing
|
|
92
|
+
expect(lastConfig()?.thinking).toBeUndefined();
|
|
93
|
+
// AND effort is still forwarded
|
|
94
|
+
expect(lastConfig()?.effort).toBe("high");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("drops explicit thinking: disabled from pass-through callers for Fable", async () => {
|
|
98
|
+
// GIVEN a pass-through caller supplying the wire-shape disabled config
|
|
99
|
+
const { provider, lastConfig } = makePipeline("anthropic");
|
|
100
|
+
|
|
101
|
+
// WHEN sending against a Fable model
|
|
102
|
+
await provider.sendMessage([userMessage], {
|
|
103
|
+
config: {
|
|
104
|
+
model: "claude-fable-5",
|
|
105
|
+
thinking: { type: "disabled" },
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// THEN the disabled thinking config is dropped
|
|
110
|
+
expect(lastConfig()?.thinking).toBeUndefined();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("drops disabled thinking for OpenRouter-proxied Fable", async () => {
|
|
114
|
+
// GIVEN a profile disabling thinking on the OpenRouter-proxied Fable id
|
|
115
|
+
setLlmConfig({
|
|
116
|
+
default: {
|
|
117
|
+
provider: "openrouter",
|
|
118
|
+
model: "anthropic/claude-fable-5",
|
|
119
|
+
thinking: { enabled: false },
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const { provider, lastConfig } = makePipeline("openrouter");
|
|
123
|
+
|
|
124
|
+
// WHEN a request resolves through the call-site config
|
|
125
|
+
await provider.sendMessage([userMessage], {
|
|
126
|
+
config: { callSite: "memoryExtraction" },
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// THEN the disabled thinking config is dropped
|
|
130
|
+
expect(lastConfig()?.thinking).toBeUndefined();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("preserves adaptive thinking for Fable", async () => {
|
|
134
|
+
// GIVEN a profile that enables thinking for a Fable model
|
|
135
|
+
setLlmConfig({
|
|
136
|
+
default: {
|
|
137
|
+
provider: "anthropic",
|
|
138
|
+
model: "claude-fable-5",
|
|
139
|
+
thinking: { enabled: true },
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
const { provider, lastConfig } = makePipeline("anthropic");
|
|
143
|
+
|
|
144
|
+
// WHEN a request resolves through the call-site config
|
|
145
|
+
await provider.sendMessage([userMessage], {
|
|
146
|
+
config: { callSite: "memoryExtraction" },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// THEN adaptive thinking is preserved (only disabled is dropped)
|
|
150
|
+
expect(lastConfig()?.thinking).toEqual({ type: "adaptive" });
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("preserves disabled thinking for models that support disabling it", async () => {
|
|
154
|
+
// GIVEN a profile disabling thinking for a non-adaptive-only model
|
|
155
|
+
setLlmConfig({
|
|
156
|
+
default: {
|
|
157
|
+
provider: "anthropic",
|
|
158
|
+
model: "claude-opus-4-7",
|
|
159
|
+
thinking: { enabled: false },
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
const { provider, lastConfig } = makePipeline("anthropic");
|
|
163
|
+
|
|
164
|
+
// WHEN a request resolves through the call-site config
|
|
165
|
+
await provider.sendMessage([userMessage], {
|
|
166
|
+
config: { callSite: "memoryExtraction" },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// THEN the disabled thinking config is preserved for Opus
|
|
170
|
+
expect(lastConfig()?.thinking).toEqual({ type: "disabled" });
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("drops non-1 temperature for Fable when thinking is disabled", async () => {
|
|
174
|
+
// GIVEN a Fable profile that disables thinking and sets a non-1 temperature
|
|
175
|
+
setLlmConfig({
|
|
176
|
+
default: {
|
|
177
|
+
provider: "anthropic",
|
|
178
|
+
model: "claude-fable-5",
|
|
179
|
+
thinking: { enabled: false },
|
|
180
|
+
temperature: 0.7,
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
const { provider, lastConfig } = makePipeline("anthropic");
|
|
184
|
+
|
|
185
|
+
// WHEN a request resolves through the call-site config
|
|
186
|
+
await provider.sendMessage([userMessage], {
|
|
187
|
+
config: { callSite: "memoryExtraction" },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// THEN the disabled thinking is dropped (Fable falls back to adaptive) AND
|
|
191
|
+
// the non-1 temperature is dropped, since adaptive mode requires
|
|
192
|
+
// temperature: 1 — leaving it would 400 the request
|
|
193
|
+
expect(lastConfig()?.thinking).toBeUndefined();
|
|
194
|
+
expect(lastConfig()?.temperature).toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("drops non-1 temperature for Fable with no explicit thinking config", async () => {
|
|
198
|
+
// GIVEN a pass-through caller that sets a non-1 temperature and no thinking
|
|
199
|
+
const { provider, lastConfig } = makePipeline("anthropic");
|
|
200
|
+
|
|
201
|
+
// WHEN sending against a Fable model
|
|
202
|
+
await provider.sendMessage([userMessage], {
|
|
203
|
+
config: {
|
|
204
|
+
model: "claude-fable-5",
|
|
205
|
+
temperature: 0.5,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// THEN the temperature is dropped: Fable is always in adaptive mode, so the
|
|
210
|
+
// temperature: 1 constraint applies even without an explicit thinking config
|
|
211
|
+
expect(lastConfig()?.temperature).toBeUndefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("drops non-1 temperature for OpenRouter-proxied Fable", async () => {
|
|
215
|
+
// GIVEN a pass-through caller on the OpenRouter-proxied Fable id
|
|
216
|
+
const { provider, lastConfig } = makePipeline("openrouter");
|
|
217
|
+
|
|
218
|
+
// WHEN sending with a non-1 temperature and no explicit thinking config
|
|
219
|
+
await provider.sendMessage([userMessage], {
|
|
220
|
+
config: {
|
|
221
|
+
model: "anthropic/claude-fable-5",
|
|
222
|
+
temperature: 0.2,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// THEN the temperature is dropped for the Anthropic-fronted Fable model
|
|
227
|
+
expect(lastConfig()?.temperature).toBeUndefined();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("preserves temperature: 1 for Fable", async () => {
|
|
231
|
+
// GIVEN a Fable profile with the only adaptive-mode-valid temperature
|
|
232
|
+
const { provider, lastConfig } = makePipeline("anthropic");
|
|
233
|
+
|
|
234
|
+
// WHEN sending against a Fable model
|
|
235
|
+
await provider.sendMessage([userMessage], {
|
|
236
|
+
config: {
|
|
237
|
+
model: "claude-fable-5",
|
|
238
|
+
temperature: 1,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// THEN temperature: 1 is preserved (it is valid in adaptive mode)
|
|
243
|
+
expect(lastConfig()?.temperature).toBe(1);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -91,11 +91,14 @@ mock.module("../config/loader.js", () => ({
|
|
|
91
91
|
let _conversationFactory: (() => Conversation) | undefined;
|
|
92
92
|
let _approvalGenerator: unknown;
|
|
93
93
|
|
|
94
|
-
mock.module("../daemon/conversation-
|
|
94
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
95
95
|
findConversation: () => {
|
|
96
96
|
if (!_conversationFactory) return undefined;
|
|
97
97
|
return _conversationFactory();
|
|
98
98
|
},
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
mock.module("../daemon/conversation-store.js", () => ({
|
|
99
102
|
getOrCreateConversation: async (..._args: unknown[]) => {
|
|
100
103
|
if (!_conversationFactory)
|
|
101
104
|
throw new Error("_conversationFactory not set in test");
|
|
@@ -808,6 +808,45 @@ describe("renderHistoryContent contentBlocks", () => {
|
|
|
808
808
|
);
|
|
809
809
|
});
|
|
810
810
|
|
|
811
|
+
test("surfaces persisted thinking timing onto the thinking block", () => {
|
|
812
|
+
// GIVEN a thinking block the daemon stamped with internal `_startedAt` /
|
|
813
|
+
// `_completedAt` timing at message_complete
|
|
814
|
+
const output = renderHistoryContent([
|
|
815
|
+
{
|
|
816
|
+
type: "thinking",
|
|
817
|
+
thinking: "reasoning",
|
|
818
|
+
signature: "sig",
|
|
819
|
+
_startedAt: 1000,
|
|
820
|
+
_completedAt: 1500,
|
|
821
|
+
},
|
|
822
|
+
]);
|
|
823
|
+
|
|
824
|
+
// THEN the rendered block exposes them as the wire schema's
|
|
825
|
+
// startedAt/completedAt so the client can show the duration + hover state
|
|
826
|
+
expect(output.contentBlocks).toEqual([
|
|
827
|
+
{
|
|
828
|
+
type: "thinking",
|
|
829
|
+
thinking: "reasoning",
|
|
830
|
+
startedAt: 1000,
|
|
831
|
+
completedAt: 1500,
|
|
832
|
+
},
|
|
833
|
+
]);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
test("omits thinking timing when the block carries none", () => {
|
|
837
|
+
// GIVEN a thinking block with no persisted timing (older daemon / thinking
|
|
838
|
+
// streaming disabled)
|
|
839
|
+
const output = renderHistoryContent([
|
|
840
|
+
{ type: "thinking", thinking: "reasoning", signature: "sig" },
|
|
841
|
+
]);
|
|
842
|
+
|
|
843
|
+
// THEN the rendered block has no startedAt/completedAt and the client hides
|
|
844
|
+
// the duration, exactly as a tool call with no timing
|
|
845
|
+
expect(output.contentBlocks).toEqual([
|
|
846
|
+
{ type: "thinking", thinking: "reasoning" },
|
|
847
|
+
]);
|
|
848
|
+
});
|
|
849
|
+
|
|
811
850
|
const pdfBlock = {
|
|
812
851
|
type: "file",
|
|
813
852
|
source: {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* parses it via the real frontmatter parser, and verifies that `skillFlagKey()`
|
|
6
6
|
* returns the correct key and `resolveSkillStates()` correctly gates the skill.
|
|
7
7
|
*/
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
8
10
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
9
11
|
|
|
10
12
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
@@ -225,3 +227,32 @@ describe("frontmatter feature-flag integration", () => {
|
|
|
225
227
|
expect(resolvedOff.length).toBe(0);
|
|
226
228
|
});
|
|
227
229
|
});
|
|
230
|
+
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Bundled ACP skill: discoverability when ACP is disabled
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
describe("bundled acp skill discoverability", () => {
|
|
236
|
+
test("acp skill resolves with no frontmatter flag gate", () => {
|
|
237
|
+
// The ACP skill carries its own first-time-setup instructions and is
|
|
238
|
+
// always discoverable: it has no frontmatter feature-flag gate.
|
|
239
|
+
const skillMdPath = fileURLToPath(
|
|
240
|
+
new URL("../config/bundled-skills/acp/SKILL.md", import.meta.url),
|
|
241
|
+
);
|
|
242
|
+
const skillMd = readFileSync(skillMdPath, "utf8");
|
|
243
|
+
|
|
244
|
+
const skill = buildSkillSummary("acp", skillMd);
|
|
245
|
+
expect(skill).not.toBeNull();
|
|
246
|
+
expect(skill!.featureFlag).toBeUndefined();
|
|
247
|
+
expect(skillFlagKey(skill!)).toBeUndefined();
|
|
248
|
+
|
|
249
|
+
const config = makeConfig({
|
|
250
|
+
acp: { maxConcurrentSessions: 4, agents: {} },
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const resolved = resolveSkillStates([skill!], config);
|
|
254
|
+
expect(resolved.length).toBe(1);
|
|
255
|
+
expect(resolved[0].summary.id).toBe("acp");
|
|
256
|
+
expect(resolved[0].state).toBe("enabled");
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -14,7 +14,7 @@ import type { Message } from "../providers/types.js";
|
|
|
14
14
|
/**
|
|
15
15
|
* Minimal reproduction of repairPendingToolUseBlocks logic for direct
|
|
16
16
|
* unit testing. The real implementation lives in conversation-process.ts.
|
|
17
|
-
* This mirrors it exactly to avoid needing the full
|
|
17
|
+
* This mirrors it exactly to avoid needing the full Conversation.
|
|
18
18
|
*/
|
|
19
19
|
function repairPendingToolUseBlocks(messages: Message[]): Message[] {
|
|
20
20
|
if (messages.length === 0) return messages;
|
|
@@ -174,7 +174,7 @@ import { LLMSchema } from "../config/schemas/llm.js";
|
|
|
174
174
|
import {
|
|
175
175
|
clearConversations,
|
|
176
176
|
setConversation,
|
|
177
|
-
} from "../daemon/conversation-
|
|
177
|
+
} from "../daemon/conversation-registry.js";
|
|
178
178
|
import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
|
|
179
179
|
import { SubagentManager } from "../subagent/manager.js";
|
|
180
180
|
|
|
@@ -18,6 +18,7 @@ function msg(role: string, content: unknown[]): MessageRow {
|
|
|
18
18
|
content: JSON.stringify(content),
|
|
19
19
|
createdAt: Date.now(),
|
|
20
20
|
metadata: null,
|
|
21
|
+
clientMessageId: null,
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -26,10 +27,19 @@ describe("parseSubagentMessages", () => {
|
|
|
26
27
|
const messages = [
|
|
27
28
|
msg("user", [{ type: "text", text: "Do something" }]),
|
|
28
29
|
msg("assistant", [
|
|
29
|
-
{
|
|
30
|
+
{
|
|
31
|
+
type: "tool_use",
|
|
32
|
+
id: "t1",
|
|
33
|
+
name: "web_search",
|
|
34
|
+
input: { query: "test" },
|
|
35
|
+
},
|
|
30
36
|
]),
|
|
31
37
|
msg("user", [
|
|
32
|
-
{
|
|
38
|
+
{
|
|
39
|
+
type: "tool_result",
|
|
40
|
+
tool_use_id: "t1",
|
|
41
|
+
content: "Search results here",
|
|
42
|
+
},
|
|
33
43
|
]),
|
|
34
44
|
];
|
|
35
45
|
|
|
@@ -44,7 +54,12 @@ describe("parseSubagentMessages", () => {
|
|
|
44
54
|
const messages = [
|
|
45
55
|
msg("user", [{ type: "text", text: "Do something" }]),
|
|
46
56
|
msg("assistant", [
|
|
47
|
-
{
|
|
57
|
+
{
|
|
58
|
+
type: "tool_use",
|
|
59
|
+
id: "t2",
|
|
60
|
+
name: "file_read",
|
|
61
|
+
input: { file_path: "/tmp/test.txt" },
|
|
62
|
+
},
|
|
48
63
|
]),
|
|
49
64
|
msg("user", [
|
|
50
65
|
{
|
|
@@ -69,11 +84,14 @@ describe("parseSubagentMessages", () => {
|
|
|
69
84
|
const messages = [
|
|
70
85
|
msg("user", [{ type: "text", text: "Do something" }]),
|
|
71
86
|
msg("assistant", [
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
{
|
|
88
|
+
type: "tool_use",
|
|
89
|
+
id: "t3",
|
|
90
|
+
name: "bash",
|
|
91
|
+
input: { command: "echo hi" },
|
|
92
|
+
},
|
|
76
93
|
]),
|
|
94
|
+
msg("user", [{ type: "tool_result", tool_use_id: "t3", content: null }]),
|
|
77
95
|
];
|
|
78
96
|
|
|
79
97
|
const result = parseSubagentMessages("sub-1", messages);
|