@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- 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/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.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__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -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__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -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__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- 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__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -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-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- 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 +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- 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/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -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-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- 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/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.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/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -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 +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- 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/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- 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 +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- 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-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- 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__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- 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/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- 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/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /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__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +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}/core.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}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for the image-too-large persistence path (JARVIS-1037).
|
|
3
|
+
*
|
|
4
|
+
* An image the provider can never accept — over the per-side pixel cap or the
|
|
5
|
+
* per-image byte cap, and not shrinkable on this host — must be durably swapped
|
|
6
|
+
* for a text note in its stored message. If it stays in the stored content,
|
|
7
|
+
* every later turn rehydrates it from the DB and the model reports seeing both
|
|
8
|
+
* the rejected image and any smaller re-upload. `persistUnsendableImageDowngrades`
|
|
9
|
+
* makes the swap durable so the rejected upload cannot resurface.
|
|
10
|
+
*
|
|
11
|
+
* Uses the real SQLite DB wired up via `test-preload.ts` (per-file temp
|
|
12
|
+
* workspace).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
import { persistUnsendableImageDowngrades } from "../daemon/persist-unsendable-image.js";
|
|
18
|
+
import {
|
|
19
|
+
addMessage,
|
|
20
|
+
createConversation,
|
|
21
|
+
getMessages,
|
|
22
|
+
} from "../memory/conversation-crud.js";
|
|
23
|
+
import { getDb } from "../memory/db-connection.js";
|
|
24
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
25
|
+
import type { ContentBlock } from "../providers/types.js";
|
|
26
|
+
|
|
27
|
+
initializeDb();
|
|
28
|
+
|
|
29
|
+
function resetTables(): void {
|
|
30
|
+
const db = getDb();
|
|
31
|
+
db.run("DELETE FROM message_attachments");
|
|
32
|
+
db.run("DELETE FROM attachments");
|
|
33
|
+
db.run("DELETE FROM memory_segments");
|
|
34
|
+
db.run("DELETE FROM memory_embeddings");
|
|
35
|
+
db.run("DELETE FROM messages");
|
|
36
|
+
db.run("DELETE FROM conversations");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build a minimal PNG whose IHDR declares the given dimensions. Only the
|
|
41
|
+
* 8-byte signature and the width/height fields (read by `parseImageDimensions`)
|
|
42
|
+
* need to be correct; the rest is padding. `optimizeImageForTransport` cannot
|
|
43
|
+
* downscale this off macOS (no `sips`), so it stays a no-op — exactly the
|
|
44
|
+
* host condition that produces an unsendable stored image.
|
|
45
|
+
*/
|
|
46
|
+
function makePngBase64(width: number, height: number, padBytes = 0): string {
|
|
47
|
+
const header = Buffer.from(
|
|
48
|
+
Uint8Array.from([
|
|
49
|
+
0x89,
|
|
50
|
+
0x50,
|
|
51
|
+
0x4e,
|
|
52
|
+
0x47,
|
|
53
|
+
0x0d,
|
|
54
|
+
0x0a,
|
|
55
|
+
0x1a,
|
|
56
|
+
0x0a, // PNG signature
|
|
57
|
+
0x00,
|
|
58
|
+
0x00,
|
|
59
|
+
0x00,
|
|
60
|
+
0x0d, // IHDR length (13)
|
|
61
|
+
0x49,
|
|
62
|
+
0x48,
|
|
63
|
+
0x44,
|
|
64
|
+
0x52, // "IHDR"
|
|
65
|
+
(width >>> 24) & 0xff,
|
|
66
|
+
(width >>> 16) & 0xff,
|
|
67
|
+
(width >>> 8) & 0xff,
|
|
68
|
+
width & 0xff,
|
|
69
|
+
(height >>> 24) & 0xff,
|
|
70
|
+
(height >>> 16) & 0xff,
|
|
71
|
+
(height >>> 8) & 0xff,
|
|
72
|
+
height & 0xff,
|
|
73
|
+
0x08,
|
|
74
|
+
0x06,
|
|
75
|
+
0x00,
|
|
76
|
+
0x00,
|
|
77
|
+
0x00, // bit depth / color type / etc.
|
|
78
|
+
]),
|
|
79
|
+
).toString("base64");
|
|
80
|
+
return padBytes > 0 ? header + "A".repeat(padBytes) : header;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function imageBlock(data: string): ContentBlock {
|
|
84
|
+
return {
|
|
85
|
+
type: "image",
|
|
86
|
+
source: { type: "base64", media_type: "image/png", data },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function storedContent(conversationId: string): ContentBlock[][] {
|
|
91
|
+
return getMessages(conversationId).map(
|
|
92
|
+
(row) => JSON.parse(row.content) as ContentBlock[],
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const PROVIDER_MAX_IMAGE_DIMENSION = 8000;
|
|
97
|
+
|
|
98
|
+
describe("persistUnsendableImageDowngrades", () => {
|
|
99
|
+
beforeEach(() => {
|
|
100
|
+
resetTables();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/** A stored image past the provider pixel cap is swapped for a text note. */
|
|
104
|
+
test("replaces an oversized image block with a text note", async () => {
|
|
105
|
+
// GIVEN a message holding text plus an image past the pixel cap
|
|
106
|
+
const conv = createConversation();
|
|
107
|
+
const oversized = makePngBase64(PROVIDER_MAX_IMAGE_DIMENSION + 1000, 6000);
|
|
108
|
+
await addMessage(
|
|
109
|
+
conv.id,
|
|
110
|
+
"user",
|
|
111
|
+
JSON.stringify([
|
|
112
|
+
{ type: "text", text: "look at this" },
|
|
113
|
+
imageBlock(oversized),
|
|
114
|
+
]),
|
|
115
|
+
{ skipIndexing: true },
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// WHEN the downgrade is persisted
|
|
119
|
+
const rewritten = persistUnsendableImageDowngrades(conv.id);
|
|
120
|
+
|
|
121
|
+
// THEN one message is rewritten with no image block left
|
|
122
|
+
expect(rewritten).toBe(1);
|
|
123
|
+
const [content] = storedContent(conv.id);
|
|
124
|
+
expect(content.some((b) => b.type === "image")).toBe(false);
|
|
125
|
+
// AND the original text is preserved alongside the substituted note
|
|
126
|
+
expect(content.filter((b) => b.type === "text")).toHaveLength(2);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
/** The JARVIS-1037 scenario: the rejected original must not resurface next
|
|
130
|
+
* to a valid re-upload. */
|
|
131
|
+
test("re-uploaded smaller image survives while the rejected original is removed", async () => {
|
|
132
|
+
// GIVEN turn 1 contains an oversized upload that was rejected
|
|
133
|
+
const conv = createConversation();
|
|
134
|
+
await addMessage(
|
|
135
|
+
conv.id,
|
|
136
|
+
"user",
|
|
137
|
+
JSON.stringify([imageBlock(makePngBase64(12000, 9000))]),
|
|
138
|
+
{ skipIndexing: true },
|
|
139
|
+
);
|
|
140
|
+
// AND turn 2 contains a properly sized re-upload
|
|
141
|
+
await addMessage(
|
|
142
|
+
conv.id,
|
|
143
|
+
"user",
|
|
144
|
+
JSON.stringify([imageBlock(makePngBase64(800, 600))]),
|
|
145
|
+
{ skipIndexing: true },
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// WHEN the downgrade is persisted
|
|
149
|
+
const rewritten = persistUnsendableImageDowngrades(conv.id);
|
|
150
|
+
|
|
151
|
+
// THEN only the rejected original is removed
|
|
152
|
+
expect(rewritten).toBe(1);
|
|
153
|
+
const [first, second] = storedContent(conv.id);
|
|
154
|
+
expect(first.some((b) => b.type === "image")).toBe(false);
|
|
155
|
+
// AND the valid re-upload is left intact
|
|
156
|
+
expect(second.some((b) => b.type === "image")).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/** Sendable images are never disturbed by the recovery path. */
|
|
160
|
+
test("leaves a normally-sized image untouched", async () => {
|
|
161
|
+
// GIVEN a message with an image well within provider limits
|
|
162
|
+
const conv = createConversation();
|
|
163
|
+
await addMessage(
|
|
164
|
+
conv.id,
|
|
165
|
+
"user",
|
|
166
|
+
JSON.stringify([imageBlock(makePngBase64(1024, 768))]),
|
|
167
|
+
{ skipIndexing: true },
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// WHEN the downgrade is persisted
|
|
171
|
+
const rewritten = persistUnsendableImageDowngrades(conv.id);
|
|
172
|
+
|
|
173
|
+
// THEN nothing is rewritten and the image remains
|
|
174
|
+
expect(rewritten).toBe(0);
|
|
175
|
+
const [content] = storedContent(conv.id);
|
|
176
|
+
expect(content.some((b) => b.type === "image")).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
/** The byte-size cap is enforced independently of pixel dimensions. */
|
|
180
|
+
test("removes an image whose payload exceeds the per-image byte cap", async () => {
|
|
181
|
+
// GIVEN an image within the pixel cap but with a payload over 5 MB
|
|
182
|
+
const conv = createConversation();
|
|
183
|
+
const huge = makePngBase64(1000, 1000, 6 * 1024 * 1024);
|
|
184
|
+
await addMessage(conv.id, "user", JSON.stringify([imageBlock(huge)]), {
|
|
185
|
+
skipIndexing: true,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// WHEN the downgrade is persisted
|
|
189
|
+
const rewritten = persistUnsendableImageDowngrades(conv.id);
|
|
190
|
+
|
|
191
|
+
// THEN the oversized-payload image is removed
|
|
192
|
+
expect(rewritten).toBe(1);
|
|
193
|
+
const [content] = storedContent(conv.id);
|
|
194
|
+
expect(content.some((b) => b.type === "image")).toBe(false);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
/** Re-running after a rewrite is a safe no-op (no image blocks remain). */
|
|
198
|
+
test("is idempotent — a second run rewrites nothing", async () => {
|
|
199
|
+
// GIVEN a conversation whose oversized image has already been downgraded
|
|
200
|
+
const conv = createConversation();
|
|
201
|
+
await addMessage(
|
|
202
|
+
conv.id,
|
|
203
|
+
"user",
|
|
204
|
+
JSON.stringify([imageBlock(makePngBase64(10000, 10000))]),
|
|
205
|
+
{ skipIndexing: true },
|
|
206
|
+
);
|
|
207
|
+
expect(persistUnsendableImageDowngrades(conv.id)).toBe(1);
|
|
208
|
+
|
|
209
|
+
// WHEN the downgrade runs a second time
|
|
210
|
+
const secondRun = persistUnsendableImageDowngrades(conv.id);
|
|
211
|
+
|
|
212
|
+
// THEN nothing further is rewritten
|
|
213
|
+
expect(secondRun).toBe(0);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
|
6
6
|
import {
|
|
7
7
|
getPkbAutoInjectList,
|
|
8
8
|
readAutoinjectList,
|
|
9
|
-
} from "../
|
|
9
|
+
} from "../memory/pkb/autoinject.js";
|
|
10
10
|
|
|
11
11
|
const PKB_DEFAULT_FILES = [
|
|
12
12
|
"INDEX.md",
|
|
@@ -80,10 +80,7 @@ describe("readAutoinjectList", () => {
|
|
|
80
80
|
"INDEX.md\ncustom-topic.md\n",
|
|
81
81
|
"utf-8",
|
|
82
82
|
);
|
|
83
|
-
expect(readAutoinjectList(pkbDir)).toEqual([
|
|
84
|
-
"INDEX.md",
|
|
85
|
-
"custom-topic.md",
|
|
86
|
-
]);
|
|
83
|
+
expect(readAutoinjectList(pkbDir)).toEqual(["INDEX.md", "custom-topic.md"]);
|
|
87
84
|
});
|
|
88
85
|
|
|
89
86
|
test("strips blank lines and whitespace", () => {
|
|
@@ -44,10 +44,7 @@ describe("buildShimSource", () => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
test("handles an empty export list (today's types-only surface)", () => {
|
|
47
|
-
const source = buildShimSource(
|
|
48
|
-
[],
|
|
49
|
-
Symbol.for("vellum.plugin-api.v1"),
|
|
50
|
-
);
|
|
47
|
+
const source = buildShimSource([], Symbol.for("vellum.plugin-api.v1"));
|
|
51
48
|
expect(source).toBe(
|
|
52
49
|
`const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n`,
|
|
53
50
|
);
|
|
@@ -109,7 +106,7 @@ describe("ensurePluginApiShim", () => {
|
|
|
109
106
|
const pluginDir = join(workspaceDir, "plugins", "fake-plugin");
|
|
110
107
|
await mkdir(pluginDir, { recursive: true });
|
|
111
108
|
await writeFile(
|
|
112
|
-
join(pluginDir, "
|
|
109
|
+
join(pluginDir, "probe.js"),
|
|
113
110
|
`import * as api from "@vellumai/plugin-api";\nexport { api };\n`,
|
|
114
111
|
);
|
|
115
112
|
|
|
@@ -118,7 +115,7 @@ describe("ensurePluginApiShim", () => {
|
|
|
118
115
|
// → plugin-api namespace. If any link in that chain is broken, this
|
|
119
116
|
// import throws.
|
|
120
117
|
const mod: { api: Record<string, unknown> } = await import(
|
|
121
|
-
join(pluginDir, "
|
|
118
|
+
join(pluginDir, "probe.js")
|
|
122
119
|
);
|
|
123
120
|
expect(mod.api).toBeDefined();
|
|
124
121
|
});
|
|
@@ -38,14 +38,11 @@ import { RiskLevel } from "../permissions/types.js";
|
|
|
38
38
|
import { registerDefaultPlugins } from "../plugins/defaults/index.js";
|
|
39
39
|
import {
|
|
40
40
|
closeRegistration,
|
|
41
|
-
getInjectors,
|
|
42
|
-
getMiddlewaresFor,
|
|
43
41
|
getRegisteredPlugins,
|
|
44
42
|
registerPlugin,
|
|
45
43
|
resetPluginRegistryForTests,
|
|
46
44
|
} from "../plugins/registry.js";
|
|
47
45
|
import {
|
|
48
|
-
type PipelineMiddlewareMap,
|
|
49
46
|
type Plugin,
|
|
50
47
|
PluginExecutionError,
|
|
51
48
|
type PluginInitContext,
|
|
@@ -342,8 +339,8 @@ describe("plugin bootstrap", () => {
|
|
|
342
339
|
// THEN the defaults are still registered, ahead of the user plugin, so the
|
|
343
340
|
// middleware onion order is unchanged
|
|
344
341
|
const names = getRegisteredPlugins().map((p) => p.manifest.name);
|
|
345
|
-
expect(names).toContain("default-
|
|
346
|
-
expect(names.indexOf("default-
|
|
342
|
+
expect(names).toContain("default-compaction");
|
|
343
|
+
expect(names.indexOf("default-compaction")).toBeLessThan(
|
|
347
344
|
names.indexOf("user-after-defaults"),
|
|
348
345
|
);
|
|
349
346
|
});
|
|
@@ -486,47 +483,24 @@ describe("plugin bootstrap", () => {
|
|
|
486
483
|
expect(initFired).toBe(false);
|
|
487
484
|
});
|
|
488
485
|
|
|
489
|
-
test("requiresFlag disabled:
|
|
490
|
-
// Regression:
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// depended on.
|
|
496
|
-
setOverridesForTesting({ "plugin-middleware-disabled": false });
|
|
497
|
-
|
|
498
|
-
const gatedMiddleware: PipelineMiddlewareMap["llmCall"] = async (
|
|
499
|
-
args,
|
|
500
|
-
next,
|
|
501
|
-
) => next(args);
|
|
486
|
+
test("requiresFlag disabled: the skipped plugin is removed from the registry", async () => {
|
|
487
|
+
// Regression: a flag-gated skip must call `unregisterPlugin()` so the
|
|
488
|
+
// gated-off plugin does not linger in `registeredPlugins` with its
|
|
489
|
+
// `init()` never having fired to set up the state it depends on.
|
|
490
|
+
setOverridesForTesting({ "plugin-registry-disabled": false });
|
|
491
|
+
|
|
502
492
|
const plugin = buildPlugin(
|
|
503
|
-
"gated-
|
|
504
|
-
{
|
|
505
|
-
|
|
506
|
-
injectors: [
|
|
507
|
-
{
|
|
508
|
-
name: "gated-middleware-injector",
|
|
509
|
-
order: 100,
|
|
510
|
-
async produce() {
|
|
511
|
-
return null;
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
},
|
|
516
|
-
{ requiresFlag: ["plugin-middleware-disabled"] },
|
|
493
|
+
"gated-registry",
|
|
494
|
+
{},
|
|
495
|
+
{ requiresFlag: ["plugin-registry-disabled"] },
|
|
517
496
|
);
|
|
518
497
|
registerPlugin(plugin);
|
|
519
498
|
|
|
520
499
|
await bootstrapPlugins();
|
|
521
500
|
|
|
522
|
-
//
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// asserting empty lists.
|
|
526
|
-
expect(getMiddlewaresFor("llmCall")).not.toContain(gatedMiddleware);
|
|
527
|
-
expect(
|
|
528
|
-
getInjectors().some((i) => i.name === "gated-middleware-injector"),
|
|
529
|
-
).toBe(false);
|
|
501
|
+
// The gated-off plugin must not survive in the registry snapshot.
|
|
502
|
+
const names = getRegisteredPlugins().map((p) => p.manifest.name);
|
|
503
|
+
expect(names).not.toContain("gated-registry");
|
|
530
504
|
});
|
|
531
505
|
|
|
532
506
|
test("requiresFlag disabled: no shutdown hook entry installed for the skipped plugin", async () => {
|
|
@@ -2,28 +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
|
-
getInjectors,
|
|
14
|
-
getMiddlewaresFor,
|
|
15
13
|
getRegisteredPlugins,
|
|
16
14
|
registerPlugin,
|
|
17
15
|
resetPluginRegistryForTests,
|
|
18
16
|
} from "../plugins/registry.js";
|
|
19
|
-
import {
|
|
20
|
-
type CompactionArgs,
|
|
21
|
-
type CompactionResult,
|
|
22
|
-
type Injector,
|
|
23
|
-
type Middleware,
|
|
24
|
-
type Plugin,
|
|
25
|
-
PluginExecutionError,
|
|
26
|
-
} from "../plugins/types.js";
|
|
17
|
+
import { type Plugin, PluginExecutionError } from "../plugins/types.js";
|
|
27
18
|
|
|
28
19
|
/** Build a minimal, valid plugin with the given name and optional extras. */
|
|
29
20
|
function buildPlugin(
|
|
@@ -149,70 +140,6 @@ describe("plugin registry", () => {
|
|
|
149
140
|
expect(() => registerPlugin(bad)).toThrow(/manifest\.version is required/);
|
|
150
141
|
});
|
|
151
142
|
|
|
152
|
-
test("getInjectors returns injectors sorted by order ascending", () => {
|
|
153
|
-
const high: Injector = {
|
|
154
|
-
name: "high-order",
|
|
155
|
-
order: 20,
|
|
156
|
-
async produce() {
|
|
157
|
-
return null;
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
const low: Injector = {
|
|
161
|
-
name: "low-order",
|
|
162
|
-
order: 10,
|
|
163
|
-
async produce() {
|
|
164
|
-
return null;
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// Register the higher-order plugin first so registration order alone
|
|
169
|
-
// would produce the wrong sequence — the test proves sort-by-order wins.
|
|
170
|
-
registerPlugin(buildPlugin("high", { injectors: [high] }));
|
|
171
|
-
registerPlugin(buildPlugin("low", { injectors: [low] }));
|
|
172
|
-
|
|
173
|
-
const injectors = getInjectors();
|
|
174
|
-
expect(injectors.map((i) => i.name)).toEqual(["low-order", "high-order"]);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test("getMiddlewaresFor returns middleware in registration order", () => {
|
|
178
|
-
const firstMw: Middleware<CompactionArgs, CompactionResult> = async (
|
|
179
|
-
args,
|
|
180
|
-
next,
|
|
181
|
-
) => next(args);
|
|
182
|
-
const secondMw: Middleware<CompactionArgs, CompactionResult> = async (
|
|
183
|
-
args,
|
|
184
|
-
next,
|
|
185
|
-
) => next(args);
|
|
186
|
-
|
|
187
|
-
registerPlugin(
|
|
188
|
-
buildPlugin("plugin-first", { middleware: { compaction: firstMw } }),
|
|
189
|
-
);
|
|
190
|
-
registerPlugin(
|
|
191
|
-
buildPlugin("plugin-second", { middleware: { compaction: secondMw } }),
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
const middlewares = getMiddlewaresFor("compaction");
|
|
195
|
-
expect(middlewares).toHaveLength(2);
|
|
196
|
-
// Identity comparison proves the middleware instances come back in
|
|
197
|
-
// registration order — outer→inner composition semantics belong to the
|
|
198
|
-
// pipeline runner (PR 12), not the registry.
|
|
199
|
-
expect(middlewares[0]).toBe(firstMw);
|
|
200
|
-
expect(middlewares[1]).toBe(secondMw);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test("getMiddlewaresFor skips plugins without a middleware for the pipeline", () => {
|
|
204
|
-
const mw: Middleware<CompactionArgs, CompactionResult> = async (
|
|
205
|
-
args,
|
|
206
|
-
next,
|
|
207
|
-
) => next(args);
|
|
208
|
-
registerPlugin(buildPlugin("bare"));
|
|
209
|
-
registerPlugin(buildPlugin("has-mw", { middleware: { compaction: mw } }));
|
|
210
|
-
|
|
211
|
-
const middlewares = getMiddlewaresFor("compaction");
|
|
212
|
-
expect(middlewares).toHaveLength(1);
|
|
213
|
-
expect(middlewares[0]).toBe(mw);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
143
|
test("getRegisteredPlugins reflects registration order", () => {
|
|
217
144
|
registerPlugin(buildPlugin("one"));
|
|
218
145
|
registerPlugin(buildPlugin("two"));
|
|
@@ -12,40 +12,11 @@ import { describe, expect, test } from "bun:test";
|
|
|
12
12
|
|
|
13
13
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
14
14
|
import { RiskLevel } from "../permissions/types.js";
|
|
15
|
-
import type {
|
|
16
|
-
ToolResultTruncateArgs,
|
|
17
|
-
ToolResultTruncateResult,
|
|
18
|
-
} from "../plugins/defaults/tool-result-truncate/types.js";
|
|
19
15
|
import {
|
|
20
|
-
type CircuitBreakerArgs,
|
|
21
|
-
type CircuitBreakerResult,
|
|
22
|
-
type CompactionArgs,
|
|
23
|
-
type CompactionResult,
|
|
24
|
-
type EmptyResponseArgs,
|
|
25
|
-
type EmptyResponseResult,
|
|
26
|
-
type EstimateArgs,
|
|
27
|
-
type EstimateResult,
|
|
28
|
-
type Injector,
|
|
29
|
-
type LLMCallArgs,
|
|
30
|
-
type LLMCallResult,
|
|
31
|
-
type MemoryArgs,
|
|
32
|
-
type MemoryResult,
|
|
33
|
-
type Middleware,
|
|
34
|
-
type OverflowReduceArgs,
|
|
35
|
-
type OverflowReduceResult,
|
|
36
|
-
type PersistArgs,
|
|
37
|
-
type PersistResult,
|
|
38
16
|
type Plugin,
|
|
39
17
|
PluginExecutionError,
|
|
40
18
|
type PluginInitContext,
|
|
41
19
|
type PluginManifest,
|
|
42
|
-
PluginTimeoutError,
|
|
43
|
-
type TitleArgs,
|
|
44
|
-
type TitleResult,
|
|
45
|
-
type ToolErrorArgs,
|
|
46
|
-
type ToolErrorDecision,
|
|
47
|
-
type ToolExecuteArgs,
|
|
48
|
-
type ToolExecuteResult,
|
|
49
20
|
type TurnContext,
|
|
50
21
|
} from "../plugins/types.js";
|
|
51
22
|
import type { Tool } from "../tools/types.js";
|
|
@@ -59,7 +30,6 @@ const sampleTurnContext: TurnContext = {
|
|
|
59
30
|
requestId: "req-abc",
|
|
60
31
|
conversationId: "conv-xyz",
|
|
61
32
|
turnIndex: 0,
|
|
62
|
-
pluginName: "sample-plugin",
|
|
63
33
|
trust: sampleTrust,
|
|
64
34
|
};
|
|
65
35
|
|
|
@@ -73,134 +43,6 @@ describe("plugin core types", () => {
|
|
|
73
43
|
config: { parse: (input: unknown) => input },
|
|
74
44
|
};
|
|
75
45
|
|
|
76
|
-
// Generic passthrough — typed per slot below because per-pipeline
|
|
77
|
-
// arg/result types have diverged from the early `{input: unknown}` /
|
|
78
|
-
// `{output: unknown}` placeholders as individual pipeline wrap-up PRs
|
|
79
|
-
// land.
|
|
80
|
-
const passthrough: Middleware<
|
|
81
|
-
{ input: unknown },
|
|
82
|
-
{ output: unknown }
|
|
83
|
-
> = async (args, next, _ctx) => next(args);
|
|
84
|
-
// `llmCall` has concrete arg/result types (upgraded in PR 15).
|
|
85
|
-
const llmCallPassthrough: Middleware<LLMCallArgs, LLMCallResult> = async (
|
|
86
|
-
args,
|
|
87
|
-
next,
|
|
88
|
-
_ctx,
|
|
89
|
-
) => next(args);
|
|
90
|
-
|
|
91
|
-
// `toolExecute` has concrete arg/result types (refined in PR 16).
|
|
92
|
-
const toolExecutePassthrough: Middleware<
|
|
93
|
-
ToolExecuteArgs,
|
|
94
|
-
ToolExecuteResult
|
|
95
|
-
> = async (args, next, _ctx) => next(args);
|
|
96
|
-
|
|
97
|
-
// `toolResultTruncate` has a concrete args/result shape (PR 17) so we
|
|
98
|
-
// need a dedicated passthrough for that slot.
|
|
99
|
-
const truncatePassthrough: Middleware<
|
|
100
|
-
ToolResultTruncateArgs,
|
|
101
|
-
ToolResultTruncateResult
|
|
102
|
-
> = async (args, _next, _ctx) => ({
|
|
103
|
-
content: args.content,
|
|
104
|
-
truncated: false,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// The `emptyResponse` slot has concrete args/result types; use a
|
|
108
|
-
// dedicated passthrough so the `satisfies Plugin` check stays honest.
|
|
109
|
-
const emptyResponsePassthrough: Middleware<
|
|
110
|
-
EmptyResponseArgs,
|
|
111
|
-
EmptyResponseResult
|
|
112
|
-
> = async (args, next, _ctx) => next(args);
|
|
113
|
-
|
|
114
|
-
// The `toolError` slot has concrete args/result types (PR 19); use a
|
|
115
|
-
// dedicated passthrough so the shape-only test keeps compiling as types
|
|
116
|
-
// get tightened.
|
|
117
|
-
const toolErrorPassthrough: Middleware<
|
|
118
|
-
ToolErrorArgs,
|
|
119
|
-
ToolErrorDecision
|
|
120
|
-
> = async (args, next, _ctx) => next(args);
|
|
121
|
-
|
|
122
|
-
// `memoryRetrieval` has a concrete typed signature (MemoryArgs →
|
|
123
|
-
// MemoryResult) introduced in PR 20, so it can't use the generic
|
|
124
|
-
// `{ input }` passthrough above.
|
|
125
|
-
const memoryPassthrough: Middleware<MemoryArgs, MemoryResult> = async (
|
|
126
|
-
args,
|
|
127
|
-
next,
|
|
128
|
-
_ctx,
|
|
129
|
-
) => next(args);
|
|
130
|
-
|
|
131
|
-
// `tokenEstimate` has a concrete arg/result shape (refined in the
|
|
132
|
-
// tokenEstimate-pipeline PR), so its middleware can't share the generic
|
|
133
|
-
// `{ input, output }` passthrough. A slot-specific passthrough keeps the
|
|
134
|
-
// shape-only assertion honest across type-refinement PRs.
|
|
135
|
-
const tokenEstimatePassthrough: Middleware<
|
|
136
|
-
EstimateArgs,
|
|
137
|
-
EstimateResult
|
|
138
|
-
> = async (args, next, _ctx) => next(args);
|
|
139
|
-
|
|
140
|
-
// `overflowReduce` has a concrete arg/result shape (PR 23). Uses a
|
|
141
|
-
// dedicated passthrough that returns a structurally-correct result so
|
|
142
|
-
// `satisfies Plugin` keeps verifying the signature.
|
|
143
|
-
const overflowReducePassthrough: Middleware<
|
|
144
|
-
OverflowReduceArgs,
|
|
145
|
-
OverflowReduceResult
|
|
146
|
-
> = async (args, _next, _ctx) => ({
|
|
147
|
-
messages: args.messages,
|
|
148
|
-
runMessages: args.runMessages,
|
|
149
|
-
injectionMode: "full",
|
|
150
|
-
reducerState: {
|
|
151
|
-
appliedTiers: [],
|
|
152
|
-
injectionMode: "full",
|
|
153
|
-
exhausted: true,
|
|
154
|
-
},
|
|
155
|
-
reducerCompacted: false,
|
|
156
|
-
attempts: 0,
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Slot-specific passthrough for the `compaction` pipeline — PR 25
|
|
160
|
-
// narrowed its args/result away from the generic `{ input: unknown }`
|
|
161
|
-
// placeholder, so the generic `passthrough` no longer satisfies its
|
|
162
|
-
// middleware signature. This dedicated middleware keeps the shape-only
|
|
163
|
-
// assertion for the slot without forcing every other slot to narrow.
|
|
164
|
-
const compactionPassthrough: Middleware<
|
|
165
|
-
CompactionArgs,
|
|
166
|
-
CompactionResult
|
|
167
|
-
> = async (args, next, _ctx) => next(args);
|
|
168
|
-
|
|
169
|
-
// `circuitBreaker` carries a concrete arg shape (pipeline wrapping
|
|
170
|
-
// landed ahead of the other slots) so it needs its own passthrough
|
|
171
|
-
// rather than reusing the generic placeholder.
|
|
172
|
-
const circuitPassthrough: Middleware<
|
|
173
|
-
CircuitBreakerArgs,
|
|
174
|
-
CircuitBreakerResult
|
|
175
|
-
> = async (args, next, _ctx) => next(args);
|
|
176
|
-
|
|
177
|
-
// `persistence` has concrete discriminated-union arg/result types
|
|
178
|
-
// (upgraded from the initial `{ input }/{ output }` placeholder in PR 27)
|
|
179
|
-
// so it gets its own passthrough rather than sharing the generic one
|
|
180
|
-
// above.
|
|
181
|
-
const persistPassthrough: Middleware<PersistArgs, PersistResult> = async (
|
|
182
|
-
args,
|
|
183
|
-
next,
|
|
184
|
-
_ctx,
|
|
185
|
-
) => next(args);
|
|
186
|
-
|
|
187
|
-
// The `titleGenerate` slot now has concrete arg/result types (PR 28)
|
|
188
|
-
// rather than the placeholder `{ input/output: unknown }` shape, so it
|
|
189
|
-
// needs its own passthrough implementation.
|
|
190
|
-
const titlePassthrough: Middleware<TitleArgs, TitleResult> = async (
|
|
191
|
-
args,
|
|
192
|
-
next,
|
|
193
|
-
_ctx,
|
|
194
|
-
) => next(args);
|
|
195
|
-
|
|
196
|
-
const injector: Injector = {
|
|
197
|
-
name: "sample-injector",
|
|
198
|
-
order: 10,
|
|
199
|
-
async produce(_ctx) {
|
|
200
|
-
return { id: "sample-block", text: "hello", meta: { kind: "demo" } };
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
|
|
204
46
|
const sampleTool: Tool = {
|
|
205
47
|
name: "sample-tool",
|
|
206
48
|
description: "Sample plugin tool",
|
|
@@ -244,45 +86,10 @@ describe("plugin core types", () => {
|
|
|
244
86
|
body: "## Sample\n\nPlugin-provided skill body.",
|
|
245
87
|
},
|
|
246
88
|
],
|
|
247
|
-
injectors: [injector],
|
|
248
|
-
middleware: {
|
|
249
|
-
turn: passthrough,
|
|
250
|
-
llmCall: llmCallPassthrough,
|
|
251
|
-
toolExecute: toolExecutePassthrough,
|
|
252
|
-
memoryRetrieval: memoryPassthrough,
|
|
253
|
-
tokenEstimate: tokenEstimatePassthrough,
|
|
254
|
-
compaction: compactionPassthrough,
|
|
255
|
-
overflowReduce: overflowReducePassthrough,
|
|
256
|
-
persistence: persistPassthrough,
|
|
257
|
-
titleGenerate: titlePassthrough,
|
|
258
|
-
toolResultTruncate: truncatePassthrough,
|
|
259
|
-
emptyResponse: emptyResponsePassthrough,
|
|
260
|
-
toolError: toolErrorPassthrough,
|
|
261
|
-
circuitBreaker: circuitPassthrough,
|
|
262
|
-
},
|
|
263
89
|
} satisfies Plugin;
|
|
264
90
|
|
|
265
91
|
// Minimal runtime check so the test body is non-empty.
|
|
266
92
|
expect(plugin.manifest.name).toBe("sample-plugin");
|
|
267
|
-
expect(plugin.middleware.turn).toBe(passthrough);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
test("PluginTimeoutError carries pipeline, plugin, and elapsed fields", () => {
|
|
271
|
-
const err = new PluginTimeoutError("compaction", "sample-plugin", 30000);
|
|
272
|
-
expect(err).toBeInstanceOf(Error);
|
|
273
|
-
expect(err.name).toBe("PluginTimeoutError");
|
|
274
|
-
expect(err.pipeline).toBe("compaction");
|
|
275
|
-
expect(err.pluginName).toBe("sample-plugin");
|
|
276
|
-
expect(err.elapsedMs).toBe(30000);
|
|
277
|
-
expect(err.message).toContain("compaction");
|
|
278
|
-
expect(err.message).toContain("30000");
|
|
279
|
-
expect(err.message).toContain("sample-plugin");
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
test("PluginTimeoutError omits plugin suffix when unknown", () => {
|
|
283
|
-
const err = new PluginTimeoutError("llmCall", undefined, 1234);
|
|
284
|
-
expect(err.pluginName).toBeUndefined();
|
|
285
|
-
expect(err.message).not.toContain("offending plugin");
|
|
286
93
|
});
|
|
287
94
|
|
|
288
95
|
test("PluginExecutionError carries the plugin name and message", () => {
|