@vellumai/assistant 0.8.8 → 0.8.9-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +2 -2
- 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 +236 -6
- package/package.json +2 -2
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +185 -140
- 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 +1 -1
- 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-cascade.test.ts +5 -4
- 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 +132 -0
- package/src/__tests__/compaction-events.test.ts +5 -5
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +59 -103
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +94 -189
- package/src/__tests__/conversation-agent-loop.test.ts +249 -490
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +14 -14
- package/src/__tests__/conversation-provider-retry-repair.test.ts +18 -16
- package/src/__tests__/conversation-queue.test.ts +9 -9
- package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +6 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
- package/src/__tests__/conversation-speed-override.test.ts +10 -9
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
- package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- 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 +12 -2
- 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__/history-repair-hook.test.ts +1 -1
- 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__/memory-retrieval-hook.test.ts +73 -6
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +12 -23
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-types.test.ts +0 -70
- 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 +33 -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 +1 -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/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 +390 -240
- package/src/api/responses/conversation-message.ts +14 -1
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
- package/src/cli/lib/install-from-github.ts +464 -55
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +409 -0
- package/src/cli/lib/plugin-marketplace.ts +197 -0
- package/src/cli/lib/search-plugins.ts +195 -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 +6 -0
- 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 -381
- 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/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-tool-registry.ts +2 -0
- package/src/config/feature-flag-registry.json +14 -5
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/context/strip-injections.ts +8 -2
- package/src/context/window-manager.ts +27 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
- package/src/daemon/conversation-agent-loop.ts +167 -1005
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-process.ts +8 -136
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +293 -392
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +24 -8
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +383 -56
- package/src/daemon/external-plugins-bootstrap.ts +14 -19
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/server.ts +2 -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__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +2 -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/index.ts +1 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
- package/src/memory/v2/consolidation-job.ts +13 -4
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +6 -5
- package/src/plugin-api/types.ts +75 -0
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/manager-store.ts +44 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- 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 +135 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +103 -38
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +167 -56
- 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/unified-turn-context.ts +223 -0
- 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__/live-integration.test.ts +9 -6
- 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 +8 -5
- 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 +10 -5
- 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 +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
- 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/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
- 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 +5 -5
- 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 +7 -7
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -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/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/proactive-artifact/aux-message-injector.ts +1 -1
- package/src/proactive-artifact/job.test.ts +1 -1
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
- 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 +283 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +398 -73
- 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 +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- 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/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 +28 -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/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 +16 -6
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +336 -35
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- 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/filesystem/write.ts +34 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +25 -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/__tests__/plugin-external-api.test.ts +0 -68
- package/src/daemon/wake-target-adapter.ts +0 -253
- 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/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/plugins/external-api.ts +0 -104
- /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__/health.test.ts +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__/provider-blocks.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__/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}/health.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +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
|
@@ -110,7 +110,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
110
110
|
];
|
|
111
111
|
|
|
112
112
|
const result = await applyRuntimeInjections(runMessages, {
|
|
113
|
-
|
|
113
|
+
...makeTurnContext(),
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
// Exactly one <memory> source across the WHOLE assembled context.
|
|
@@ -149,7 +149,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
149
149
|
];
|
|
150
150
|
|
|
151
151
|
const result = await applyRuntimeInjections(runMessages, {
|
|
152
|
-
|
|
152
|
+
...makeTurnContext(),
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
// v2's block survives — the turn still ships memory.
|
|
@@ -177,7 +177,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
177
177
|
// captures the exact pre-flag assembly behavior: v2 prefix stays, v3
|
|
178
178
|
// splices after it.
|
|
179
179
|
const offResult = await applyRuntimeInjections(runMessages, {
|
|
180
|
-
|
|
180
|
+
...makeTurnContext(),
|
|
181
181
|
});
|
|
182
182
|
|
|
183
183
|
// The tail keeps v2's block AND gains v3's (the historical double-injection
|
|
@@ -206,7 +206,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
206
206
|
];
|
|
207
207
|
|
|
208
208
|
const result = await applyRuntimeInjections(runMessages, {
|
|
209
|
-
|
|
209
|
+
...makeTurnContext(),
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
expect(result.messages).toEqual(runMessages);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for handleListMessages clientMessageId projection.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that the persisted idempotency nonce is echoed back on the
|
|
5
|
+
* messages snapshot row so a client can correlate its optimistic row with
|
|
6
|
+
* the confirmed server row by identity instead of matching message text.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
mock.module("../util/logger.js", () => ({
|
|
12
|
+
getLogger: () =>
|
|
13
|
+
new Proxy({} as Record<string, unknown>, {
|
|
14
|
+
get: () => () => {},
|
|
15
|
+
}),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
mock.module("../config/loader.js", () => ({
|
|
19
|
+
getConfig: () => ({
|
|
20
|
+
ui: {},
|
|
21
|
+
model: "test",
|
|
22
|
+
provider: "test",
|
|
23
|
+
memory: { enabled: false },
|
|
24
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
import { addMessage, createConversation } from "../memory/conversation-crud.js";
|
|
29
|
+
import { getDb } from "../memory/db-connection.js";
|
|
30
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
31
|
+
import { handleListMessages } from "../runtime/routes/conversation-routes.js";
|
|
32
|
+
|
|
33
|
+
initializeDb();
|
|
34
|
+
|
|
35
|
+
function resetTables() {
|
|
36
|
+
const db = getDb();
|
|
37
|
+
db.run("DELETE FROM messages");
|
|
38
|
+
db.run("DELETE FROM conversations");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface MessagePayload {
|
|
42
|
+
role: string;
|
|
43
|
+
clientMessageId?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe("handleListMessages clientMessageId", () => {
|
|
47
|
+
beforeEach(resetTables);
|
|
48
|
+
|
|
49
|
+
test("echoes the persisted clientMessageId onto the user row", async () => {
|
|
50
|
+
// GIVEN a user message persisted with a client-generated idempotency nonce
|
|
51
|
+
const conv = createConversation();
|
|
52
|
+
await addMessage(
|
|
53
|
+
conv.id,
|
|
54
|
+
"user",
|
|
55
|
+
JSON.stringify([{ type: "text", text: "hello" }]),
|
|
56
|
+
{ clientMessageId: "nonce-abc", skipIndexing: true },
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// WHEN the messages snapshot is built
|
|
60
|
+
const response = handleListMessages({
|
|
61
|
+
queryParams: { conversationId: conv.id },
|
|
62
|
+
});
|
|
63
|
+
const body = response as { messages: MessagePayload[] };
|
|
64
|
+
|
|
65
|
+
// THEN the snapshot row carries the nonce back for id-based correlation
|
|
66
|
+
expect(body.messages).toHaveLength(1);
|
|
67
|
+
expect(body.messages[0].role).toBe("user");
|
|
68
|
+
expect(body.messages[0].clientMessageId).toBe("nonce-abc");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("omits clientMessageId when the row was persisted without one", async () => {
|
|
72
|
+
// GIVEN a user message persisted without a client nonce (e.g. a channel send)
|
|
73
|
+
const conv = createConversation();
|
|
74
|
+
await addMessage(
|
|
75
|
+
conv.id,
|
|
76
|
+
"user",
|
|
77
|
+
JSON.stringify([{ type: "text", text: "hello" }]),
|
|
78
|
+
{ skipIndexing: true },
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// WHEN the messages snapshot is built
|
|
82
|
+
const response = handleListMessages({
|
|
83
|
+
queryParams: { conversationId: conv.id },
|
|
84
|
+
});
|
|
85
|
+
const body = response as { messages: MessagePayload[] };
|
|
86
|
+
|
|
87
|
+
// THEN the field is absent rather than emitted as null/empty
|
|
88
|
+
expect(body.messages).toHaveLength(1);
|
|
89
|
+
expect(body.messages[0].clientMessageId).toBeUndefined();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -96,6 +96,44 @@ describe("handleListMessages metadata.hidden filtering", () => {
|
|
|
96
96
|
expect(llmRows[1].metadata).toContain('"hidden":true');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
test("UI serializer omits system rows but LLM-side getMessages includes them", async () => {
|
|
100
|
+
// GIVEN a conversation with a system row sandwiched between two
|
|
101
|
+
// renderable turns (e.g. a skill-authored context message)
|
|
102
|
+
const conv = createConversation();
|
|
103
|
+
await addMessage(
|
|
104
|
+
conv.id,
|
|
105
|
+
"user",
|
|
106
|
+
JSON.stringify([{ type: "text", text: "first visible" }]),
|
|
107
|
+
);
|
|
108
|
+
await addMessage(
|
|
109
|
+
conv.id,
|
|
110
|
+
"system",
|
|
111
|
+
JSON.stringify([{ type: "text", text: "system scaffolding" }]),
|
|
112
|
+
);
|
|
113
|
+
await addMessage(
|
|
114
|
+
conv.id,
|
|
115
|
+
"assistant",
|
|
116
|
+
JSON.stringify([{ type: "text", text: "second visible" }]),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// WHEN the UI history list is serialized
|
|
120
|
+
const response = handleListMessages({
|
|
121
|
+
queryParams: { conversationId: conv.id },
|
|
122
|
+
});
|
|
123
|
+
const body = response as { messages: MessagePayload[] };
|
|
124
|
+
|
|
125
|
+
// THEN only the user/assistant turns are returned, never the system row
|
|
126
|
+
expect(body.messages.map((m) => m.role)).toEqual(["user", "assistant"]);
|
|
127
|
+
expect(
|
|
128
|
+
body.messages.some((m) => plainText(m).includes("system scaffolding")),
|
|
129
|
+
).toBe(false);
|
|
130
|
+
|
|
131
|
+
// AND the LLM-side loader still includes the system row for agent context
|
|
132
|
+
const llmRows = getMessages(conv.id);
|
|
133
|
+
expect(llmRows).toHaveLength(3);
|
|
134
|
+
expect(llmRows[1].role).toBe("system");
|
|
135
|
+
});
|
|
136
|
+
|
|
99
137
|
test("messages without metadata or with hidden=false are returned", async () => {
|
|
100
138
|
const conv = createConversation();
|
|
101
139
|
await addMessage(
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for the default `user-prompt-submit-temp` hook (memory retrieval
|
|
2
|
+
* Tests for the default `user-prompt-submit-temp` hook (memory retrieval +
|
|
3
|
+
* runtime injection).
|
|
3
4
|
*
|
|
4
5
|
* Covers the retrieval behavior, the side effects the hook owns (injected-block
|
|
5
|
-
* metadata, recall log, `memory_recalled` event), trust gating,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* metadata, recall log, `memory_recalled` event), trust gating, the runtime
|
|
7
|
+
* injection the hook applies for every actor (and the assembled-block persist),
|
|
8
|
+
* error propagation, and abort-signal forwarding. Uses `mock.module` to stub
|
|
9
|
+
* the persistence helpers and `applyRuntimeInjections` so the test doesn't touch
|
|
10
|
+
* the developer's real `~/.vellum`, database, or live conversation state. The
|
|
11
|
+
* memory graph handle is a hand-rolled fake passed on the hook context — the
|
|
12
|
+
* hook only needs `prepareMemory`.
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
15
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -23,6 +26,17 @@ mock.module("../memory/memory-recall-log-store.js", () => ({
|
|
|
23
26
|
recordMemoryRecallLog: recordMemoryRecallLogMock,
|
|
24
27
|
}));
|
|
25
28
|
|
|
29
|
+
// Pass-through injection stub: returns the input messages unchanged with no
|
|
30
|
+
// assembled blocks, so existing array-identity assertions hold. Individual
|
|
31
|
+
// tests override it via `mockImplementationOnce` to exercise injected blocks.
|
|
32
|
+
const applyRuntimeInjectionsMock = mock(async (messages: unknown) => ({
|
|
33
|
+
messages,
|
|
34
|
+
blocks: {},
|
|
35
|
+
}));
|
|
36
|
+
mock.module("../daemon/conversation-runtime-assembly.js", () => ({
|
|
37
|
+
applyRuntimeInjections: applyRuntimeInjectionsMock,
|
|
38
|
+
}));
|
|
39
|
+
|
|
26
40
|
import type { AssistantConfig } from "../config/schema.js";
|
|
27
41
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
28
42
|
import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
@@ -117,6 +131,11 @@ function makeHookCtx(
|
|
|
117
131
|
} as unknown as MemoryRetrievalHookContext["logger"],
|
|
118
132
|
signal: new AbortController().signal,
|
|
119
133
|
latestMessages: [],
|
|
134
|
+
requestId: "req-test",
|
|
135
|
+
mode: "full",
|
|
136
|
+
isNonInteractive: false,
|
|
137
|
+
modelProfile: null,
|
|
138
|
+
actorContext: null,
|
|
120
139
|
...overrides,
|
|
121
140
|
};
|
|
122
141
|
}
|
|
@@ -124,6 +143,7 @@ function makeHookCtx(
|
|
|
124
143
|
beforeEach(() => {
|
|
125
144
|
updateMessageMetadataMock.mockReset();
|
|
126
145
|
recordMemoryRecallLogMock.mockReset();
|
|
146
|
+
applyRuntimeInjectionsMock.mockClear();
|
|
127
147
|
});
|
|
128
148
|
|
|
129
149
|
describe("user-prompt-submit-temp hook (memory retrieval)", () => {
|
|
@@ -177,6 +197,53 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
|
|
|
177
197
|
);
|
|
178
198
|
});
|
|
179
199
|
|
|
200
|
+
test("applies runtime injection on the retrieved history and persists the assembled blocks", async () => {
|
|
201
|
+
const retrieved: Message[] = [
|
|
202
|
+
{ role: "user", content: [{ type: "text", text: "retrieved" }] },
|
|
203
|
+
];
|
|
204
|
+
const injected: Message[] = [
|
|
205
|
+
{ role: "user", content: [{ type: "text", text: "injected" }] },
|
|
206
|
+
];
|
|
207
|
+
const { memory } = makeFakeGraphMemory({ messages: retrieved });
|
|
208
|
+
applyRuntimeInjectionsMock.mockImplementationOnce(async () => ({
|
|
209
|
+
messages: injected,
|
|
210
|
+
blocks: { unifiedTurnContext: "tc-block" },
|
|
211
|
+
}));
|
|
212
|
+
const ctx = makeHookCtx({ graphMemory: memory, userMessageId: "msg-77" });
|
|
213
|
+
|
|
214
|
+
await userPromptSubmitMemoryRetrieval(ctx);
|
|
215
|
+
|
|
216
|
+
// Injection runs on the retrieved history; its result becomes the working
|
|
217
|
+
// list the loop reads back.
|
|
218
|
+
expect(applyRuntimeInjectionsMock).toHaveBeenCalledTimes(1);
|
|
219
|
+
expect(applyRuntimeInjectionsMock.mock.calls[0]?.[0]).toBe(retrieved);
|
|
220
|
+
expect(ctx.latestMessages).toBe(injected);
|
|
221
|
+
// The assembled blocks are persisted onto the user message metadata.
|
|
222
|
+
expect(updateMessageMetadataMock).toHaveBeenCalledWith("msg-77", {
|
|
223
|
+
turnContextBlock: "tc-block",
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("applies runtime injection for untrusted actors despite skipping retrieval", async () => {
|
|
228
|
+
const { memory, prepareMemoryMock } = makeFakeGraphMemory();
|
|
229
|
+
const seeded: Message[] = [
|
|
230
|
+
{ role: "user", content: [{ type: "text", text: "seeded" }] },
|
|
231
|
+
];
|
|
232
|
+
const ctx = makeHookCtx({
|
|
233
|
+
graphMemory: memory,
|
|
234
|
+
isTrustedActor: false,
|
|
235
|
+
latestMessages: seeded,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
await userPromptSubmitMemoryRetrieval(ctx);
|
|
239
|
+
|
|
240
|
+
// The memory-graph step is gated on trust, but injection runs for everyone
|
|
241
|
+
// — on the seeded history, since no retrieval replaced it.
|
|
242
|
+
expect(prepareMemoryMock).not.toHaveBeenCalled();
|
|
243
|
+
expect(applyRuntimeInjectionsMock).toHaveBeenCalledTimes(1);
|
|
244
|
+
expect(applyRuntimeInjectionsMock.mock.calls[0]?.[0]).toBe(seeded);
|
|
245
|
+
});
|
|
246
|
+
|
|
180
247
|
test("skips graph retrieval and side effects for untrusted actors", async () => {
|
|
181
248
|
const { memory, prepareMemoryMock, recordPkbQueryVectorsMock } =
|
|
182
249
|
makeFakeGraphMemory();
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* - Skips (re)injection when the `<info>` block is already present in the
|
|
12
12
|
* turn's working messages (presence detection — the block persists in
|
|
13
13
|
* history between compactions).
|
|
14
|
+
* - Still injects when only a dynamic `<memory>` activation block is present
|
|
15
|
+
* (that wrapper must not be mistaken for the static block) — both on a
|
|
16
|
+
* normal turn and after compaction strips the prior `<info>` block.
|
|
14
17
|
*
|
|
15
18
|
* The injector sources its content itself via `readMemoryV2StaticContent()`
|
|
16
19
|
* behind the personal-memory trust gate, so each test seeds the workspace
|
|
@@ -35,7 +38,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
35
38
|
}));
|
|
36
39
|
|
|
37
40
|
const { defaultInjectors } =
|
|
38
|
-
await import("../plugins/defaults/injectors
|
|
41
|
+
await import("../plugins/defaults/memory-retrieval/injectors.js");
|
|
39
42
|
import type { Injector, TurnContext } from "../plugins/types.js";
|
|
40
43
|
import type { Message } from "../providers/types.js";
|
|
41
44
|
import { getWorkspacePromptPath } from "../util/platform.js";
|
|
@@ -54,7 +57,6 @@ function makeContext(overrides: Partial<TurnContext> = {}): TurnContext {
|
|
|
54
57
|
conversationId: "conv-test",
|
|
55
58
|
turnIndex: 0,
|
|
56
59
|
trust: { sourceChannel: "vellum", trustClass: "guardian" },
|
|
57
|
-
injectionInputs: {},
|
|
58
60
|
...overrides,
|
|
59
61
|
};
|
|
60
62
|
}
|
|
@@ -96,7 +98,7 @@ describe("memory-v2-static injector", () => {
|
|
|
96
98
|
|
|
97
99
|
test("returns null in minimal mode even with content", async () => {
|
|
98
100
|
seedEssentials("Alice prefers VS Code.");
|
|
99
|
-
const ctx = makeContext({
|
|
101
|
+
const ctx = makeContext({ mode: "minimal" });
|
|
100
102
|
expect(await memoryV2StaticInjector.produce(ctx)).toBeNull();
|
|
101
103
|
});
|
|
102
104
|
|
|
@@ -140,10 +142,12 @@ describe("memory-v2-static injector", () => {
|
|
|
140
142
|
expect(await memoryV2StaticInjector.produce(ctx, runMessages)).toBeNull();
|
|
141
143
|
});
|
|
142
144
|
|
|
143
|
-
test("
|
|
144
|
-
//
|
|
145
|
-
// `<memory>…</memory
|
|
146
|
-
//
|
|
145
|
+
test("injects the <info> block even when a dynamic <memory> activation block is present", async () => {
|
|
146
|
+
// Regression (#33612): the v2 *dynamic* activation block uses the
|
|
147
|
+
// `<memory>…</memory>` wrapper and `prepareMemory` prepends it to the tail
|
|
148
|
+
// user message every turn, before this injector runs. The static injector
|
|
149
|
+
// must NOT treat that as its own `<info>` block — doing so suppressed the
|
|
150
|
+
// static block on essentially every turn.
|
|
147
151
|
seedEssentials("Alice prefers VS Code.");
|
|
148
152
|
const ctx = makeContext();
|
|
149
153
|
const runMessages: Message[] = [
|
|
@@ -152,12 +156,86 @@ describe("memory-v2-static injector", () => {
|
|
|
152
156
|
content: [
|
|
153
157
|
{
|
|
154
158
|
type: "text",
|
|
155
|
-
text: "<memory>\n
|
|
159
|
+
text: "<memory>\n# memory/concepts/foo.md\nactivated page summary\n</memory>",
|
|
156
160
|
},
|
|
157
161
|
{ type: "text", text: "What next?" },
|
|
158
162
|
],
|
|
159
163
|
},
|
|
160
164
|
];
|
|
165
|
+
const block = await memoryV2StaticInjector.produce(ctx, runMessages);
|
|
166
|
+
expect(block).not.toBeNull();
|
|
167
|
+
expect(block!.id).toBe("memory-v2-static");
|
|
168
|
+
expect(block!.text).toBe(
|
|
169
|
+
"<info>\n## Essentials\n\nAlice prefers VS Code.\n</info>",
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("reinjects the <info> block after compaction strips it, alongside a fresh <memory> block", async () => {
|
|
174
|
+
// Post-compaction state: `stripInjectionsForCompaction` removed the prior
|
|
175
|
+
// `<info>` block, and the next turn's `prepareMemory` re-added a fresh
|
|
176
|
+
// dynamic `<memory>` block ahead of the chain. With no `<info>` present, the
|
|
177
|
+
// injector must reinject it rather than skip on the `<memory>` block.
|
|
178
|
+
seedEssentials("Alice prefers VS Code.");
|
|
179
|
+
const ctx = makeContext();
|
|
180
|
+
const postCompactMessages: Message[] = [
|
|
181
|
+
{
|
|
182
|
+
role: "user",
|
|
183
|
+
content: [
|
|
184
|
+
{ type: "text", text: "<memory>\nactivated page summary\n</memory>" },
|
|
185
|
+
{ type: "text", text: "Continue please." },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
];
|
|
189
|
+
const block = await memoryV2StaticInjector.produce(
|
|
190
|
+
ctx,
|
|
191
|
+
postCompactMessages,
|
|
192
|
+
);
|
|
193
|
+
expect(block).not.toBeNull();
|
|
194
|
+
expect(block!.id).toBe("memory-v2-static");
|
|
195
|
+
expect(block!.text).toContain("## Essentials");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("skips (re)injection when the <info> block is carried in a flattened history block", async () => {
|
|
199
|
+
// Regression: when a historical user message's separate injection content
|
|
200
|
+
// blocks get collapsed into one text block (observed after a daemon restart
|
|
201
|
+
// / plugin hot-reload rebuilt the rendered history mid-conversation — the
|
|
202
|
+
// blocks are joined with "\n"), the `<info>` wrapper is no longer its own
|
|
203
|
+
// block but a newline-delimited section inside a larger block. Presence
|
|
204
|
+
// detection must still recognize it; otherwise the injector re-injects a
|
|
205
|
+
// duplicate `<info>` onto the new tail (the double-injection bug).
|
|
206
|
+
seedEssentials("Alice prefers VS Code.");
|
|
207
|
+
const ctx = makeContext();
|
|
208
|
+
const flattenedFirstUser = [
|
|
209
|
+
"<workspace>\nRoot: /ws\nFiles: a.md\n</workspace>",
|
|
210
|
+
"<turn_context>\ncurrent_time: now\n</turn_context>",
|
|
211
|
+
"<memory>\nactivated page summary\n</memory>",
|
|
212
|
+
"<info>\n## Essentials\n\nAlice prefers VS Code.\n</info>",
|
|
213
|
+
"what should we do next?",
|
|
214
|
+
].join("\n");
|
|
215
|
+
const runMessages: Message[] = [
|
|
216
|
+
{ role: "user", content: [{ type: "text", text: flattenedFirstUser }] },
|
|
217
|
+
{ role: "assistant", content: [{ type: "text", text: "On it." }] },
|
|
218
|
+
{ role: "user", content: [{ type: "text", text: "great, continue" }] },
|
|
219
|
+
];
|
|
161
220
|
expect(await memoryV2StaticInjector.produce(ctx, runMessages)).toBeNull();
|
|
162
221
|
});
|
|
222
|
+
|
|
223
|
+
test("still injects when a user message opens an <info> tag without closing it", async () => {
|
|
224
|
+
// False-positive guard: the wrapper matcher requires BOTH the opening and
|
|
225
|
+
// closing tags, so user prose that merely opens an injection-like tag must
|
|
226
|
+
// not be mistaken for a carried block and suppress injection.
|
|
227
|
+
seedEssentials("Alice prefers VS Code.");
|
|
228
|
+
const ctx = makeContext();
|
|
229
|
+
const runMessages: Message[] = [
|
|
230
|
+
{
|
|
231
|
+
role: "user",
|
|
232
|
+
content: [
|
|
233
|
+
{ type: "text", text: "<info>\n is this how the tag works?" },
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
];
|
|
237
|
+
const block = await memoryV2StaticInjector.produce(ctx, runMessages);
|
|
238
|
+
expect(block).not.toBeNull();
|
|
239
|
+
expect(block!.id).toBe("memory-v2-static");
|
|
240
|
+
});
|
|
163
241
|
});
|
|
@@ -128,12 +128,19 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
128
128
|
return { content: "ok", isError: false };
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
-
const loop = new AgentLoop(
|
|
131
|
+
const loop = new AgentLoop({
|
|
132
|
+
provider: provider,
|
|
133
|
+
systemPrompt: "system",
|
|
134
|
+
conversationId: "test-conversation",
|
|
132
135
|
tools: dummyTools,
|
|
133
136
|
toolExecutor: toolExecutor,
|
|
134
137
|
});
|
|
135
138
|
const start = Date.now();
|
|
136
|
-
await loop.run(
|
|
139
|
+
await loop.run({
|
|
140
|
+
messages: [userMessage],
|
|
141
|
+
onEvent: () => {},
|
|
142
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
143
|
+
});
|
|
137
144
|
const elapsed = Date.now() - start;
|
|
138
145
|
|
|
139
146
|
// Parallel: ~50ms + overhead. Sequential would be ~250ms.
|
|
@@ -169,12 +176,19 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
169
176
|
return { content: "ok", isError: false };
|
|
170
177
|
};
|
|
171
178
|
|
|
172
|
-
const loop = new AgentLoop(
|
|
179
|
+
const loop = new AgentLoop({
|
|
180
|
+
provider: provider,
|
|
181
|
+
systemPrompt: "system",
|
|
182
|
+
conversationId: "test-conversation",
|
|
173
183
|
tools: dummyTools,
|
|
174
184
|
toolExecutor: toolExecutor,
|
|
175
185
|
});
|
|
176
186
|
const start = Date.now();
|
|
177
|
-
await loop.run(
|
|
187
|
+
await loop.run({
|
|
188
|
+
messages: [userMessage],
|
|
189
|
+
onEvent: () => {},
|
|
190
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
191
|
+
});
|
|
178
192
|
const elapsed = Date.now() - start;
|
|
179
193
|
|
|
180
194
|
// All 10 tools should have executed
|
|
@@ -222,13 +236,20 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
222
236
|
return { content: "ok", isError: false };
|
|
223
237
|
};
|
|
224
238
|
|
|
225
|
-
const loop = new AgentLoop(
|
|
239
|
+
const loop = new AgentLoop({
|
|
240
|
+
provider: provider,
|
|
241
|
+
systemPrompt: "system",
|
|
242
|
+
conversationId: "test-conversation",
|
|
226
243
|
tools: dummyTools,
|
|
227
244
|
toolExecutor: toolExecutor,
|
|
228
245
|
});
|
|
229
246
|
const events: AgentEvent[] = [];
|
|
230
247
|
const start = Date.now();
|
|
231
|
-
await loop.run(
|
|
248
|
+
await loop.run({
|
|
249
|
+
messages: [userMessage],
|
|
250
|
+
onEvent: collectEvents(events),
|
|
251
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
252
|
+
});
|
|
232
253
|
const elapsed = Date.now() - start;
|
|
233
254
|
|
|
234
255
|
// Parallel: ~2000ms (dominated by slow tool). Sequential: ~2400ms (2000 + 4*100).
|
|
@@ -289,12 +310,18 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
289
310
|
return { content: "should not return", isError: false };
|
|
290
311
|
};
|
|
291
312
|
|
|
292
|
-
const loop = new AgentLoop(
|
|
313
|
+
const loop = new AgentLoop({
|
|
314
|
+
provider: provider,
|
|
315
|
+
systemPrompt: "system",
|
|
316
|
+
conversationId: "test-conversation",
|
|
293
317
|
tools: dummyTools,
|
|
294
318
|
toolExecutor: toolExecutor,
|
|
295
319
|
});
|
|
296
320
|
const start = Date.now();
|
|
297
|
-
const { history } = await loop.run(
|
|
321
|
+
const { history } = await loop.run({
|
|
322
|
+
messages: [userMessage],
|
|
323
|
+
onEvent: () => {},
|
|
324
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
298
325
|
signal: controller.signal,
|
|
299
326
|
});
|
|
300
327
|
const elapsed = Date.now() - start;
|
|
@@ -34,22 +34,19 @@ describe("buildShimSource", () => {
|
|
|
34
34
|
test("emits a globalThis trampoline + one binding per export", () => {
|
|
35
35
|
const source = buildShimSource(
|
|
36
36
|
["foo", "bar"],
|
|
37
|
-
Symbol.for("vellum.plugin-api
|
|
37
|
+
Symbol.for("vellum.plugin-api"),
|
|
38
38
|
);
|
|
39
39
|
expect(source).toBe(
|
|
40
|
-
`const api = globalThis[Symbol.for("vellum.plugin-api
|
|
40
|
+
`const api = globalThis[Symbol.for("vellum.plugin-api")];\n` +
|
|
41
41
|
`export const foo = api.foo;\n` +
|
|
42
42
|
`export const bar = api.bar;\n`,
|
|
43
43
|
);
|
|
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"));
|
|
51
48
|
expect(source).toBe(
|
|
52
|
-
`const api = globalThis[Symbol.for("vellum.plugin-api
|
|
49
|
+
`const api = globalThis[Symbol.for("vellum.plugin-api")];\n`,
|
|
53
50
|
);
|
|
54
51
|
});
|
|
55
52
|
});
|
|
@@ -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,13 +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
45
|
import {
|
|
47
|
-
type PipelineMiddlewareMap,
|
|
48
46
|
type Plugin,
|
|
49
47
|
PluginExecutionError,
|
|
50
48
|
type PluginInitContext,
|
|
@@ -485,33 +483,24 @@ describe("plugin bootstrap", () => {
|
|
|
485
483
|
expect(initFired).toBe(false);
|
|
486
484
|
});
|
|
487
485
|
|
|
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);
|
|
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
|
+
|
|
500
492
|
const plugin = buildPlugin(
|
|
501
|
-
"gated-
|
|
502
|
-
{
|
|
503
|
-
|
|
504
|
-
},
|
|
505
|
-
{ requiresFlag: ["plugin-middleware-disabled"] },
|
|
493
|
+
"gated-registry",
|
|
494
|
+
{},
|
|
495
|
+
{ requiresFlag: ["plugin-registry-disabled"] },
|
|
506
496
|
);
|
|
507
497
|
registerPlugin(plugin);
|
|
508
498
|
|
|
509
499
|
await bootstrapPlugins();
|
|
510
500
|
|
|
511
|
-
// The
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
expect(getMiddlewaresFor("compaction")).not.toContain(gatedMiddleware);
|
|
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");
|
|
515
504
|
});
|
|
516
505
|
|
|
517
506
|
test("requiresFlag disabled: no shutdown hook entry installed for the skipped plugin", async () => {
|