@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae
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 +263 -121
- package/package.json +2 -2
- 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__/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__/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-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__/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-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__/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__/send-endpoint-busy.test.ts +4 -1
- 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__/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/responses/conversation-message.ts +22 -2
- 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 -16
- package/src/config/llm-resolver.ts +39 -7
- 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 +18 -36
- 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/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/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 +34 -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 +22 -0
- 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
|
@@ -38,17 +38,11 @@ import { RiskLevel } from "../permissions/types.js";
|
|
|
38
38
|
import { registerDefaultPlugins } from "../plugins/defaults/index.js";
|
|
39
39
|
import {
|
|
40
40
|
closeRegistration,
|
|
41
|
-
getMiddlewaresFor,
|
|
42
41
|
getRegisteredPlugins,
|
|
43
42
|
registerPlugin,
|
|
44
43
|
resetPluginRegistryForTests,
|
|
45
44
|
} from "../plugins/registry.js";
|
|
46
|
-
import {
|
|
47
|
-
type PipelineMiddlewareMap,
|
|
48
|
-
type Plugin,
|
|
49
|
-
PluginExecutionError,
|
|
50
|
-
type PluginInitContext,
|
|
51
|
-
} from "../plugins/types.js";
|
|
45
|
+
import { type Plugin, type PluginInitContext } from "../plugins/types.js";
|
|
52
46
|
import { APP_VERSION } from "../version.js";
|
|
53
47
|
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
54
48
|
|
|
@@ -176,9 +170,9 @@ describe("plugin bootstrap", () => {
|
|
|
176
170
|
expect(received?.credentials).toEqual({ "some-key": "super-secret-value" });
|
|
177
171
|
});
|
|
178
172
|
|
|
179
|
-
test("credential resolution:
|
|
173
|
+
test("credential resolution: a plugin whose required credential is missing is skipped, not fatal", async () => {
|
|
174
|
+
// GIVEN a plugin that requires a credential the store cannot resolve
|
|
180
175
|
getSecureKeyAsyncMock.mockImplementation(async () => undefined);
|
|
181
|
-
|
|
182
176
|
registerPlugin(
|
|
183
177
|
buildPlugin(
|
|
184
178
|
"missing-cred",
|
|
@@ -187,16 +181,14 @@ describe("plugin bootstrap", () => {
|
|
|
187
181
|
),
|
|
188
182
|
);
|
|
189
183
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
expect(msg).toContain("missing-cred");
|
|
199
|
-
expect(msg).toContain("absent-key");
|
|
184
|
+
// WHEN bootstrap runs
|
|
185
|
+
// THEN it completes without throwing — the unresolvable credential is
|
|
186
|
+
// contained to that plugin (same per-plugin isolation as an init throw)
|
|
187
|
+
await bootstrapPlugins();
|
|
188
|
+
|
|
189
|
+
// AND the plugin is dropped from the registry
|
|
190
|
+
const names = getRegisteredPlugins().map((p) => p.manifest.name);
|
|
191
|
+
expect(names).not.toContain("missing-cred");
|
|
200
192
|
});
|
|
201
193
|
|
|
202
194
|
test("version mismatch: external plugin loader rejects when peerDependency unsatisfied", async () => {
|
|
@@ -210,7 +202,8 @@ describe("plugin bootstrap", () => {
|
|
|
210
202
|
expect(() => registerPlugin(plugin)).not.toThrow();
|
|
211
203
|
});
|
|
212
204
|
|
|
213
|
-
test("plugin init throw: bootstrap
|
|
205
|
+
test("plugin init throw: bootstrap contains the failure and does not throw", async () => {
|
|
206
|
+
// GIVEN a plugin whose init throws
|
|
214
207
|
registerPlugin(
|
|
215
208
|
buildPlugin("broken", {
|
|
216
209
|
async init() {
|
|
@@ -219,65 +212,91 @@ describe("plugin bootstrap", () => {
|
|
|
219
212
|
}),
|
|
220
213
|
);
|
|
221
214
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
expect(
|
|
231
|
-
expect(msg).toContain("kaboom");
|
|
215
|
+
// WHEN bootstrap runs
|
|
216
|
+
// THEN it completes without throwing — a single plugin's init failure is
|
|
217
|
+
// contained to that plugin rather than aborting the whole plugin layer
|
|
218
|
+
await bootstrapPlugins();
|
|
219
|
+
|
|
220
|
+
// AND the failing plugin is dropped from the registry so its hooks never
|
|
221
|
+
// participate in the turn lifecycle
|
|
222
|
+
const names = getRegisteredPlugins().map((p) => p.manifest.name);
|
|
223
|
+
expect(names).not.toContain("broken");
|
|
232
224
|
});
|
|
233
225
|
|
|
234
|
-
test("
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// the shutdown hook is only registered once the entire loop completes.
|
|
240
|
-
const callOrder: string[] = [];
|
|
226
|
+
test("mid-list init failure: surrounding plugins still initialize and survive", async () => {
|
|
227
|
+
// GIVEN two healthy plugins surrounding one whose init throws, registered
|
|
228
|
+
// in order
|
|
229
|
+
const initialized: string[] = [];
|
|
230
|
+
const shutDownDuringBootstrap: string[] = [];
|
|
241
231
|
registerPlugin(
|
|
242
|
-
buildPlugin("
|
|
243
|
-
async init() {
|
|
232
|
+
buildPlugin("before", {
|
|
233
|
+
async init() {
|
|
234
|
+
initialized.push("before");
|
|
235
|
+
},
|
|
244
236
|
async onShutdown() {
|
|
245
|
-
|
|
237
|
+
shutDownDuringBootstrap.push("before");
|
|
246
238
|
},
|
|
247
239
|
}),
|
|
248
240
|
);
|
|
249
241
|
registerPlugin(
|
|
250
|
-
buildPlugin("
|
|
251
|
-
async init() {
|
|
252
|
-
|
|
253
|
-
callOrder.push("survivor-b");
|
|
242
|
+
buildPlugin("failing", {
|
|
243
|
+
async init() {
|
|
244
|
+
throw new Error("mid-bootstrap failure");
|
|
254
245
|
},
|
|
255
246
|
}),
|
|
256
247
|
);
|
|
257
248
|
registerPlugin(
|
|
258
|
-
buildPlugin("
|
|
249
|
+
buildPlugin("after", {
|
|
259
250
|
async init() {
|
|
260
|
-
|
|
251
|
+
initialized.push("after");
|
|
261
252
|
},
|
|
262
253
|
async onShutdown() {
|
|
263
|
-
|
|
264
|
-
// added to the active list that teardown walks.
|
|
265
|
-
callOrder.push("failing");
|
|
254
|
+
shutDownDuringBootstrap.push("after");
|
|
266
255
|
},
|
|
267
256
|
}),
|
|
268
257
|
);
|
|
269
258
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
//
|
|
280
|
-
|
|
259
|
+
// WHEN bootstrap runs
|
|
260
|
+
await bootstrapPlugins();
|
|
261
|
+
|
|
262
|
+
// THEN both healthy plugins initialized — the one registered after the
|
|
263
|
+
// failure is not skipped
|
|
264
|
+
expect(initialized).toEqual(["before", "after"]);
|
|
265
|
+
// AND neither healthy plugin was torn down by the failure (their
|
|
266
|
+
// onShutdown only runs at real shutdown, not during bootstrap)
|
|
267
|
+
expect(shutDownDuringBootstrap).toEqual([]);
|
|
268
|
+
// AND the failing plugin is dropped while both survivors remain registered
|
|
269
|
+
const names = getRegisteredPlugins().map((p) => p.manifest.name);
|
|
270
|
+
expect(names).toContain("before");
|
|
271
|
+
expect(names).toContain("after");
|
|
272
|
+
expect(names).not.toContain("failing");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("user-plugin init failure leaves the first-party defaults registered", async () => {
|
|
276
|
+
// GIVEN the first-party defaults are registered ahead of user plugins, as
|
|
277
|
+
// at daemon startup
|
|
278
|
+
registerDefaultPlugins();
|
|
279
|
+
// AND a user plugin registered after them whose init throws
|
|
280
|
+
registerPlugin(
|
|
281
|
+
buildPlugin("breaking-user-plugin", {
|
|
282
|
+
async init() {
|
|
283
|
+
throw new Error("user plugin boom");
|
|
284
|
+
},
|
|
285
|
+
}),
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// WHEN bootstrap runs
|
|
289
|
+
await bootstrapPlugins();
|
|
290
|
+
|
|
291
|
+
// THEN the failing user plugin is dropped from the registry
|
|
292
|
+
const names = getRegisteredPlugins().map((p) => p.manifest.name);
|
|
293
|
+
expect(names).not.toContain("breaking-user-plugin");
|
|
294
|
+
// AND every first-party default survived, so core turn behavior (memory
|
|
295
|
+
// retrieval, history repair, title generation) keeps running in degraded
|
|
296
|
+
// mode instead of being torn down with the failing plugin
|
|
297
|
+
expect(names).toContain("default-memory-retrieval");
|
|
298
|
+
expect(names).toContain("default-history-repair");
|
|
299
|
+
expect(names).toContain("default-title-generate");
|
|
281
300
|
});
|
|
282
301
|
|
|
283
302
|
test("shutdown order: onShutdown fires in reverse registration order", async () => {
|
|
@@ -485,33 +504,24 @@ describe("plugin bootstrap", () => {
|
|
|
485
504
|
expect(initFired).toBe(false);
|
|
486
505
|
});
|
|
487
506
|
|
|
488
|
-
test("requiresFlag disabled: plugin
|
|
489
|
-
// Regression:
|
|
490
|
-
//
|
|
491
|
-
// `
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
setOverridesForTesting({ "plugin-middleware-disabled": false });
|
|
495
|
-
|
|
496
|
-
const gatedMiddleware: PipelineMiddlewareMap["compaction"] = async (
|
|
497
|
-
args,
|
|
498
|
-
next,
|
|
499
|
-
) => next(args);
|
|
507
|
+
test("requiresFlag disabled: the skipped plugin is removed from the registry", async () => {
|
|
508
|
+
// Regression: a flag-gated skip must call `unregisterPlugin()` so the
|
|
509
|
+
// gated-off plugin does not linger in `registeredPlugins` with its
|
|
510
|
+
// `init()` never having fired to set up the state it depends on.
|
|
511
|
+
setOverridesForTesting({ "plugin-registry-disabled": false });
|
|
512
|
+
|
|
500
513
|
const plugin = buildPlugin(
|
|
501
|
-
"gated-
|
|
502
|
-
{
|
|
503
|
-
|
|
504
|
-
},
|
|
505
|
-
{ requiresFlag: ["plugin-middleware-disabled"] },
|
|
514
|
+
"gated-registry",
|
|
515
|
+
{},
|
|
516
|
+
{ requiresFlag: ["plugin-registry-disabled"] },
|
|
506
517
|
);
|
|
507
518
|
registerPlugin(plugin);
|
|
508
519
|
|
|
509
520
|
await bootstrapPlugins();
|
|
510
521
|
|
|
511
|
-
// The
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
expect(getMiddlewaresFor("compaction")).not.toContain(gatedMiddleware);
|
|
522
|
+
// The gated-off plugin must not survive in the registry snapshot.
|
|
523
|
+
const names = getRegisteredPlugins().map((p) => p.manifest.name);
|
|
524
|
+
expect(names).not.toContain("gated-registry");
|
|
515
525
|
});
|
|
516
526
|
|
|
517
527
|
test("requiresFlag disabled: no shutdown hook entry installed for the skipped plugin", async () => {
|
|
@@ -2,26 +2,19 @@
|
|
|
2
2
|
* Tests for the plugin registry (PR 13).
|
|
3
3
|
*
|
|
4
4
|
* Covers successful registration, required-field and duplicate-name
|
|
5
|
-
* validation, capability-version negotiation error messaging,
|
|
6
|
-
*
|
|
5
|
+
* validation, capability-version negotiation error messaging, and
|
|
6
|
+
* registration order.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
closeRegistration,
|
|
13
|
-
getMiddlewaresFor,
|
|
14
13
|
getRegisteredPlugins,
|
|
15
14
|
registerPlugin,
|
|
16
15
|
resetPluginRegistryForTests,
|
|
17
16
|
} from "../plugins/registry.js";
|
|
18
|
-
import {
|
|
19
|
-
type CompactionArgs,
|
|
20
|
-
type CompactionResult,
|
|
21
|
-
type Middleware,
|
|
22
|
-
type Plugin,
|
|
23
|
-
PluginExecutionError,
|
|
24
|
-
} from "../plugins/types.js";
|
|
17
|
+
import { type Plugin, PluginExecutionError } from "../plugins/types.js";
|
|
25
18
|
|
|
26
19
|
/** Build a minimal, valid plugin with the given name and optional extras. */
|
|
27
20
|
function buildPlugin(
|
|
@@ -147,45 +140,6 @@ describe("plugin registry", () => {
|
|
|
147
140
|
expect(() => registerPlugin(bad)).toThrow(/manifest\.version is required/);
|
|
148
141
|
});
|
|
149
142
|
|
|
150
|
-
test("getMiddlewaresFor returns middleware in registration order", () => {
|
|
151
|
-
const firstMw: Middleware<CompactionArgs, CompactionResult> = async (
|
|
152
|
-
args,
|
|
153
|
-
next,
|
|
154
|
-
) => next(args);
|
|
155
|
-
const secondMw: Middleware<CompactionArgs, CompactionResult> = async (
|
|
156
|
-
args,
|
|
157
|
-
next,
|
|
158
|
-
) => next(args);
|
|
159
|
-
|
|
160
|
-
registerPlugin(
|
|
161
|
-
buildPlugin("plugin-first", { middleware: { compaction: firstMw } }),
|
|
162
|
-
);
|
|
163
|
-
registerPlugin(
|
|
164
|
-
buildPlugin("plugin-second", { middleware: { compaction: secondMw } }),
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
const middlewares = getMiddlewaresFor("compaction");
|
|
168
|
-
expect(middlewares).toHaveLength(2);
|
|
169
|
-
// Identity comparison proves the middleware instances come back in
|
|
170
|
-
// registration order — outer→inner composition semantics belong to the
|
|
171
|
-
// pipeline runner (PR 12), not the registry.
|
|
172
|
-
expect(middlewares[0]).toBe(firstMw);
|
|
173
|
-
expect(middlewares[1]).toBe(secondMw);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("getMiddlewaresFor skips plugins without a middleware for the pipeline", () => {
|
|
177
|
-
const mw: Middleware<CompactionArgs, CompactionResult> = async (
|
|
178
|
-
args,
|
|
179
|
-
next,
|
|
180
|
-
) => next(args);
|
|
181
|
-
registerPlugin(buildPlugin("bare"));
|
|
182
|
-
registerPlugin(buildPlugin("has-mw", { middleware: { compaction: mw } }));
|
|
183
|
-
|
|
184
|
-
const middlewares = getMiddlewaresFor("compaction");
|
|
185
|
-
expect(middlewares).toHaveLength(1);
|
|
186
|
-
expect(middlewares[0]).toBe(mw);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
143
|
test("getRegisteredPlugins reflects registration order", () => {
|
|
190
144
|
registerPlugin(buildPlugin("one"));
|
|
191
145
|
registerPlugin(buildPlugin("two"));
|
|
@@ -202,7 +202,8 @@ describe("plugin skill contributions", () => {
|
|
|
202
202
|
expect(getPluginContributedSkillSummaries()).toEqual([]);
|
|
203
203
|
});
|
|
204
204
|
|
|
205
|
-
test("duplicate skill id across plugins
|
|
205
|
+
test("duplicate skill id across plugins drops the colliding plugin, keeping the first", async () => {
|
|
206
|
+
// GIVEN two plugins that both declare the same skill id
|
|
206
207
|
const shared: PluginSkillRegistration = {
|
|
207
208
|
id: "contested-id",
|
|
208
209
|
name: "contested",
|
|
@@ -215,22 +216,22 @@ describe("plugin skill contributions", () => {
|
|
|
215
216
|
description: "Second",
|
|
216
217
|
body: "from second plugin",
|
|
217
218
|
};
|
|
218
|
-
|
|
219
|
+
// AND the first registered plugin claims the id; the second collides on it
|
|
219
220
|
registerPlugin(buildSkillPlugin("first-plugin", [shared]));
|
|
220
221
|
registerPlugin(buildSkillPlugin("second-plugin", [duplicate]));
|
|
221
222
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
expect(
|
|
233
|
-
expect(
|
|
223
|
+
// WHEN bootstrap runs
|
|
224
|
+
// THEN it does not throw — the collision is contained to the colliding
|
|
225
|
+
// plugin rather than aborting the whole plugin layer
|
|
226
|
+
await bootstrapPlugins();
|
|
227
|
+
|
|
228
|
+
// AND the first plugin keeps the contested skill, while the second
|
|
229
|
+
// plugin's colliding contribution is rolled back and dropped
|
|
230
|
+
expect(
|
|
231
|
+
getPluginContributedSkillDefinition("contested-id")?.description,
|
|
232
|
+
).toBe("First");
|
|
233
|
+
expect(getPluginSkillRefCount("first-plugin")).toBe(1);
|
|
234
|
+
expect(getPluginSkillRefCount("second-plugin")).toBe(0);
|
|
234
235
|
});
|
|
235
236
|
|
|
236
237
|
test("intra-batch duplicate skill id in one plugin is rejected at registration", () => {
|
|
@@ -218,9 +218,7 @@ describe("plugin tool contributions", () => {
|
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
test("tools are only registered after init() succeeds", async () => {
|
|
221
|
-
//
|
|
222
|
-
// aborts with a PluginExecutionError, and nothing from this plugin
|
|
223
|
-
// should leak into the tool registry.
|
|
221
|
+
// GIVEN a plugin that declares a tool but throws during init()
|
|
224
222
|
const plugin = buildPlugin("delta-broken", {
|
|
225
223
|
async init() {
|
|
226
224
|
throw new Error("boom");
|
|
@@ -229,7 +227,12 @@ describe("plugin tool contributions", () => {
|
|
|
229
227
|
});
|
|
230
228
|
registerPlugin(plugin);
|
|
231
229
|
|
|
232
|
-
|
|
230
|
+
// WHEN bootstrap runs
|
|
231
|
+
// THEN it does not throw — the init failure is contained to this plugin
|
|
232
|
+
await bootstrapPlugins();
|
|
233
|
+
|
|
234
|
+
// AND the failing plugin's tool is rolled back, never leaking into the
|
|
235
|
+
// registry
|
|
233
236
|
expect(getTool("delta-tool")).toBeUndefined();
|
|
234
237
|
});
|
|
235
238
|
});
|
|
@@ -13,18 +13,10 @@ import { describe, expect, test } from "bun:test";
|
|
|
13
13
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
14
14
|
import { RiskLevel } from "../permissions/types.js";
|
|
15
15
|
import {
|
|
16
|
-
type CircuitBreakerArgs,
|
|
17
|
-
type CircuitBreakerResult,
|
|
18
|
-
type CompactionArgs,
|
|
19
|
-
type CompactionResult,
|
|
20
|
-
type Middleware,
|
|
21
|
-
type OverflowReduceArgs,
|
|
22
|
-
type OverflowReduceResult,
|
|
23
16
|
type Plugin,
|
|
24
17
|
PluginExecutionError,
|
|
25
18
|
type PluginInitContext,
|
|
26
19
|
type PluginManifest,
|
|
27
|
-
PluginTimeoutError,
|
|
28
20
|
type TurnContext,
|
|
29
21
|
} from "../plugins/types.js";
|
|
30
22
|
import type { Tool } from "../tools/types.js";
|
|
@@ -38,7 +30,6 @@ const sampleTurnContext: TurnContext = {
|
|
|
38
30
|
requestId: "req-abc",
|
|
39
31
|
conversationId: "conv-xyz",
|
|
40
32
|
turnIndex: 0,
|
|
41
|
-
pluginName: "sample-plugin",
|
|
42
33
|
trust: sampleTrust,
|
|
43
34
|
};
|
|
44
35
|
|
|
@@ -52,43 +43,6 @@ describe("plugin core types", () => {
|
|
|
52
43
|
config: { parse: (input: unknown) => input },
|
|
53
44
|
};
|
|
54
45
|
|
|
55
|
-
// `overflowReduce` has a concrete arg/result shape (PR 23). Uses a
|
|
56
|
-
// dedicated passthrough that returns a structurally-correct result so
|
|
57
|
-
// `satisfies Plugin` keeps verifying the signature.
|
|
58
|
-
const overflowReducePassthrough: Middleware<
|
|
59
|
-
OverflowReduceArgs,
|
|
60
|
-
OverflowReduceResult
|
|
61
|
-
> = async (args, _next, _ctx) => ({
|
|
62
|
-
messages: args.messages,
|
|
63
|
-
runMessages: args.runMessages,
|
|
64
|
-
injectionMode: "full",
|
|
65
|
-
reducerState: {
|
|
66
|
-
appliedTiers: [],
|
|
67
|
-
injectionMode: "full",
|
|
68
|
-
exhausted: true,
|
|
69
|
-
},
|
|
70
|
-
reducerCompacted: false,
|
|
71
|
-
attempts: 0,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Slot-specific passthrough for the `compaction` pipeline — PR 25
|
|
75
|
-
// narrowed its args/result away from the generic `{ input: unknown }`
|
|
76
|
-
// placeholder, so the generic `passthrough` no longer satisfies its
|
|
77
|
-
// middleware signature. This dedicated middleware keeps the shape-only
|
|
78
|
-
// assertion for the slot without forcing every other slot to narrow.
|
|
79
|
-
const compactionPassthrough: Middleware<
|
|
80
|
-
CompactionArgs,
|
|
81
|
-
CompactionResult
|
|
82
|
-
> = async (args, next, _ctx) => next(args);
|
|
83
|
-
|
|
84
|
-
// `circuitBreaker` carries a concrete arg shape (pipeline wrapping
|
|
85
|
-
// landed ahead of the other slots) so it needs its own passthrough
|
|
86
|
-
// rather than reusing the generic placeholder.
|
|
87
|
-
const circuitPassthrough: Middleware<
|
|
88
|
-
CircuitBreakerArgs,
|
|
89
|
-
CircuitBreakerResult
|
|
90
|
-
> = async (args, next, _ctx) => next(args);
|
|
91
|
-
|
|
92
46
|
const sampleTool: Tool = {
|
|
93
47
|
name: "sample-tool",
|
|
94
48
|
description: "Sample plugin tool",
|
|
@@ -132,34 +86,10 @@ describe("plugin core types", () => {
|
|
|
132
86
|
body: "## Sample\n\nPlugin-provided skill body.",
|
|
133
87
|
},
|
|
134
88
|
],
|
|
135
|
-
middleware: {
|
|
136
|
-
compaction: compactionPassthrough,
|
|
137
|
-
overflowReduce: overflowReducePassthrough,
|
|
138
|
-
circuitBreaker: circuitPassthrough,
|
|
139
|
-
},
|
|
140
89
|
} satisfies Plugin;
|
|
141
90
|
|
|
142
91
|
// Minimal runtime check so the test body is non-empty.
|
|
143
92
|
expect(plugin.manifest.name).toBe("sample-plugin");
|
|
144
|
-
expect(plugin.middleware.compaction).toBe(compactionPassthrough);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("PluginTimeoutError carries pipeline, plugin, and elapsed fields", () => {
|
|
148
|
-
const err = new PluginTimeoutError("compaction", "sample-plugin", 30000);
|
|
149
|
-
expect(err).toBeInstanceOf(Error);
|
|
150
|
-
expect(err.name).toBe("PluginTimeoutError");
|
|
151
|
-
expect(err.pipeline).toBe("compaction");
|
|
152
|
-
expect(err.pluginName).toBe("sample-plugin");
|
|
153
|
-
expect(err.elapsedMs).toBe(30000);
|
|
154
|
-
expect(err.message).toContain("compaction");
|
|
155
|
-
expect(err.message).toContain("30000");
|
|
156
|
-
expect(err.message).toContain("sample-plugin");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("PluginTimeoutError omits plugin suffix when unknown", () => {
|
|
160
|
-
const err = new PluginTimeoutError("circuitBreaker", undefined, 1234);
|
|
161
|
-
expect(err.pluginName).toBeUndefined();
|
|
162
|
-
expect(err.message).not.toContain("offending plugin");
|
|
163
93
|
});
|
|
164
94
|
|
|
165
95
|
test("PluginExecutionError carries the plugin name and message", () => {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
TARGET_CHARS,
|
|
13
13
|
THRESHOLD_CHARS,
|
|
14
14
|
TOOL_RESULT_DIR,
|
|
15
|
+
TRUNCATION_EXEMPT_TOOLS,
|
|
15
16
|
TRUNCATION_MARKER,
|
|
16
17
|
} from "../context/post-turn-tool-result-truncation.js";
|
|
17
18
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
@@ -151,6 +152,74 @@ describe("postTurnTruncateToolResults", () => {
|
|
|
151
152
|
expect(stub).toContain(filePath);
|
|
152
153
|
});
|
|
153
154
|
|
|
155
|
+
test("skill_load result above threshold is NOT truncated (durable instructions exempt)", () => {
|
|
156
|
+
// Regression for JARVIS-1000: a hosted assistant lost its app-builder skill
|
|
157
|
+
// workflow when the large skill_load result was middle-truncated between the
|
|
158
|
+
// turn that loaded the skill and the turn that used it, then fell back to a
|
|
159
|
+
// local-dev (vite/localhost) build path.
|
|
160
|
+
const toolUseId = "tool_skill_load";
|
|
161
|
+
const skillBody = "S".repeat(THRESHOLD_CHARS + 5_000);
|
|
162
|
+
const messages: Message[] = [
|
|
163
|
+
{
|
|
164
|
+
role: "assistant",
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "tool_use" as const,
|
|
168
|
+
id: toolUseId,
|
|
169
|
+
name: "skill_load",
|
|
170
|
+
input: { skill: "app-builder" },
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
{ role: "user", content: [makeToolResult(skillBody, toolUseId)] },
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const { messages: result, truncatedCount } =
|
|
178
|
+
postTurnTruncateToolResults(messages, { conversationDir: convDir });
|
|
179
|
+
|
|
180
|
+
expect(truncatedCount).toBe(0);
|
|
181
|
+
expect(result).toBe(messages); // same reference — no copy
|
|
182
|
+
expect(existsSync(join(convDir, TOOL_RESULT_DIR))).toBe(false);
|
|
183
|
+
|
|
184
|
+
const block = result[1].content[0] as {
|
|
185
|
+
type: "tool_result";
|
|
186
|
+
content: string;
|
|
187
|
+
};
|
|
188
|
+
expect(block.content).toBe(skillBody);
|
|
189
|
+
expect(TRUNCATION_EXEMPT_TOOLS.has("skill_load")).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("non-exempt tool result above threshold is still truncated when paired with a tool_use", () => {
|
|
193
|
+
// Control for the exemption: same shape as the skill_load case, but a tool
|
|
194
|
+
// that is NOT exempt must still be truncated.
|
|
195
|
+
const toolUseId = "tool_bash_1";
|
|
196
|
+
const longContent = "B".repeat(THRESHOLD_CHARS + 5_000);
|
|
197
|
+
const messages: Message[] = [
|
|
198
|
+
{
|
|
199
|
+
role: "assistant",
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: "tool_use" as const,
|
|
203
|
+
id: toolUseId,
|
|
204
|
+
name: "bash",
|
|
205
|
+
input: { command: "cat big.log" },
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
{ role: "user", content: [makeToolResult(longContent, toolUseId)] },
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
const { messages: result, truncatedCount } =
|
|
213
|
+
postTurnTruncateToolResults(messages, { conversationDir: convDir });
|
|
214
|
+
|
|
215
|
+
expect(truncatedCount).toBe(1);
|
|
216
|
+
const block = result[1].content[0] as {
|
|
217
|
+
type: "tool_result";
|
|
218
|
+
content: string;
|
|
219
|
+
};
|
|
220
|
+
expect(block.content).toContain(TRUNCATION_MARKER);
|
|
221
|
+
});
|
|
222
|
+
|
|
154
223
|
test("file path is deterministic for the same toolUseId", () => {
|
|
155
224
|
const id = "tool_use_deterministic";
|
|
156
225
|
const path1 = getToolResultFilePath("/some/dir", id);
|