@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
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
* Sites not exercised here (`aborted_via_error`) require deeper provider
|
|
14
14
|
* fakery and are best covered by integration tests.
|
|
15
15
|
*/
|
|
16
|
-
import { describe, expect,
|
|
16
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
17
17
|
|
|
18
18
|
import type {
|
|
19
19
|
AgentEvent,
|
|
20
|
+
AgentLoopRunOptions,
|
|
20
21
|
CheckpointDecision,
|
|
21
22
|
CheckpointInfo,
|
|
22
|
-
MidLoopCompaction,
|
|
23
23
|
} from "../agent/loop.js";
|
|
24
24
|
import { AgentLoop, isMaxTokensStopReason } from "../agent/loop.js";
|
|
25
|
-
import type {
|
|
26
|
-
import {
|
|
25
|
+
import type { TrustContext } from "../daemon/trust-context.js";
|
|
26
|
+
import type { PostCompactContext } from "../plugins/defaults/memory-retrieval/hooks/post-compact.js";
|
|
27
27
|
import type {
|
|
28
28
|
Message,
|
|
29
29
|
Provider,
|
|
@@ -32,6 +32,23 @@ import type {
|
|
|
32
32
|
ToolDefinition,
|
|
33
33
|
} from "../providers/types.js";
|
|
34
34
|
|
|
35
|
+
// The agent loop invokes the default post-compaction re-injection hook directly
|
|
36
|
+
// when it compacts in place. Stub it so these unit tests can drive the
|
|
37
|
+
// re-injection result without the daemon-level injector chain. Tests assign
|
|
38
|
+
// `postCompactImpl` to observe the call or force a failure; when unset
|
|
39
|
+
// the hook is a no-op that returns the history it was handed.
|
|
40
|
+
let postCompactImpl:
|
|
41
|
+
| ((input: PostCompactContext) => Promise<Message[]>)
|
|
42
|
+
| null = null;
|
|
43
|
+
mock.module(
|
|
44
|
+
"../plugins/defaults/memory-retrieval/hooks/post-compact.js",
|
|
45
|
+
() => ({
|
|
46
|
+
default: async (input: PostCompactContext) => ({
|
|
47
|
+
messages: postCompactImpl ? await postCompactImpl(input) : input.history,
|
|
48
|
+
}),
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
|
|
35
52
|
// ---------------------------------------------------------------------------
|
|
36
53
|
// Helpers (mirrored from agent-loop.test.ts so this file is self-contained)
|
|
37
54
|
// ---------------------------------------------------------------------------
|
|
@@ -101,38 +118,20 @@ const userMessage: Message = {
|
|
|
101
118
|
content: [{ type: "text", text: "Hello" }],
|
|
102
119
|
};
|
|
103
120
|
|
|
104
|
-
// A
|
|
105
|
-
// result, so the loop's
|
|
106
|
-
// orchestrator machinery.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
// A trust snapshot plus a context-window manager whose `maybeCompact` returns a
|
|
122
|
+
// canned result, so the loop's compaction call runs without the real
|
|
123
|
+
// orchestrator machinery. Both are supplied to the loop as dedicated run
|
|
124
|
+
// options.
|
|
125
|
+
function fakeCompaction(result: { compacted: boolean; exhausted: boolean }): {
|
|
126
|
+
trust: TrustContext;
|
|
127
|
+
contextWindowManager: AgentLoopRunOptions["contextWindowManager"];
|
|
128
|
+
} {
|
|
111
129
|
return {
|
|
112
|
-
requestId: "req-compact",
|
|
113
|
-
conversationId: "conv-compact",
|
|
114
|
-
turnIndex: 0,
|
|
115
130
|
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
116
131
|
contextWindowManager: {
|
|
117
132
|
maybeCompact: async () => result,
|
|
118
|
-
},
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// A turn context whose compaction call times out, exercising the loop's
|
|
123
|
-
// PluginTimeoutError handling.
|
|
124
|
-
function timeoutCompactionTurnContext(): TurnContext {
|
|
125
|
-
return {
|
|
126
|
-
requestId: "req-compact",
|
|
127
|
-
conversationId: "conv-compact",
|
|
128
|
-
turnIndex: 0,
|
|
129
|
-
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
130
|
-
contextWindowManager: {
|
|
131
|
-
maybeCompact: async () => {
|
|
132
|
-
throw new PluginTimeoutError("compaction", "default-compaction", 1);
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
} as unknown as TurnContext;
|
|
133
|
+
} as unknown as AgentLoopRunOptions["contextWindowManager"],
|
|
134
|
+
};
|
|
136
135
|
}
|
|
137
136
|
|
|
138
137
|
function lastExitEvent(
|
|
@@ -164,11 +163,19 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
164
163
|
|
|
165
164
|
test("emits exit event exactly once with 'no_tool_calls' on plain text response", async () => {
|
|
166
165
|
const { provider } = createMockProvider([textResponse("Hi there!")]);
|
|
167
|
-
const loop = new AgentLoop(
|
|
166
|
+
const loop = new AgentLoop({
|
|
167
|
+
provider: provider,
|
|
168
|
+
systemPrompt: "system prompt",
|
|
169
|
+
conversationId: "test-conversation",
|
|
170
|
+
});
|
|
168
171
|
|
|
169
172
|
const events: AgentEvent[] = [];
|
|
170
|
-
await loop.run(
|
|
171
|
-
|
|
173
|
+
await loop.run({
|
|
174
|
+
messages: [userMessage],
|
|
175
|
+
onEvent: (e) => {
|
|
176
|
+
events.push(e);
|
|
177
|
+
},
|
|
178
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
172
179
|
});
|
|
173
180
|
|
|
174
181
|
expect(countExitEvents(events)).toBe(1);
|
|
@@ -178,11 +185,19 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
178
185
|
|
|
179
186
|
test("agent_loop_exit is the last event emitted", async () => {
|
|
180
187
|
const { provider } = createMockProvider([textResponse("Hi there!")]);
|
|
181
|
-
const loop = new AgentLoop(
|
|
188
|
+
const loop = new AgentLoop({
|
|
189
|
+
provider: provider,
|
|
190
|
+
systemPrompt: "system prompt",
|
|
191
|
+
conversationId: "test-conversation",
|
|
192
|
+
});
|
|
182
193
|
|
|
183
194
|
const events: AgentEvent[] = [];
|
|
184
|
-
await loop.run(
|
|
185
|
-
|
|
195
|
+
await loop.run({
|
|
196
|
+
messages: [userMessage],
|
|
197
|
+
onEvent: (e) => {
|
|
198
|
+
events.push(e);
|
|
199
|
+
},
|
|
200
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
186
201
|
});
|
|
187
202
|
|
|
188
203
|
expect(events.length).toBeGreaterThan(0);
|
|
@@ -193,11 +208,19 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
193
208
|
const { provider } = createMockProvider([
|
|
194
209
|
maxTokensResponse("Partial answer"),
|
|
195
210
|
]);
|
|
196
|
-
const loop = new AgentLoop(
|
|
211
|
+
const loop = new AgentLoop({
|
|
212
|
+
provider: provider,
|
|
213
|
+
systemPrompt: "system prompt",
|
|
214
|
+
conversationId: "test-conversation",
|
|
215
|
+
});
|
|
197
216
|
|
|
198
217
|
const events: AgentEvent[] = [];
|
|
199
|
-
await loop.run(
|
|
200
|
-
|
|
218
|
+
await loop.run({
|
|
219
|
+
messages: [userMessage],
|
|
220
|
+
onEvent: (e) => {
|
|
221
|
+
events.push(e);
|
|
222
|
+
},
|
|
223
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
201
224
|
});
|
|
202
225
|
|
|
203
226
|
expect(events.map((e) => e.type)).toEqual([
|
|
@@ -228,13 +251,20 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
228
251
|
stopReason: "max_tokens",
|
|
229
252
|
},
|
|
230
253
|
]);
|
|
231
|
-
const loop = new AgentLoop(
|
|
254
|
+
const loop = new AgentLoop({
|
|
255
|
+
provider: provider,
|
|
256
|
+
systemPrompt: "system prompt",
|
|
257
|
+
conversationId: "test-conversation",
|
|
232
258
|
tools: dummyTools,
|
|
233
259
|
});
|
|
234
260
|
|
|
235
261
|
const events: AgentEvent[] = [];
|
|
236
|
-
const { history: result } = await loop.run(
|
|
237
|
-
|
|
262
|
+
const { history: result } = await loop.run({
|
|
263
|
+
messages: [userMessage],
|
|
264
|
+
onEvent: (e) => {
|
|
265
|
+
events.push(e);
|
|
266
|
+
},
|
|
267
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
238
268
|
});
|
|
239
269
|
|
|
240
270
|
expect(events.some((e) => e.type === "tool_use")).toBe(false);
|
|
@@ -246,19 +276,24 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
246
276
|
|
|
247
277
|
test("emits 'aborted_pre_call' when signal is already aborted at run start", async () => {
|
|
248
278
|
const { provider } = createMockProvider([textResponse("never sent")]);
|
|
249
|
-
const loop = new AgentLoop(
|
|
279
|
+
const loop = new AgentLoop({
|
|
280
|
+
provider: provider,
|
|
281
|
+
systemPrompt: "system prompt",
|
|
282
|
+
conversationId: "test-conversation",
|
|
283
|
+
});
|
|
250
284
|
|
|
251
285
|
const controller = new AbortController();
|
|
252
286
|
controller.abort();
|
|
253
287
|
|
|
254
288
|
const events: AgentEvent[] = [];
|
|
255
|
-
await loop.run(
|
|
256
|
-
[userMessage],
|
|
257
|
-
(e) => {
|
|
289
|
+
await loop.run({
|
|
290
|
+
messages: [userMessage],
|
|
291
|
+
onEvent: (e) => {
|
|
258
292
|
events.push(e);
|
|
259
293
|
},
|
|
260
|
-
{
|
|
261
|
-
|
|
294
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
295
|
+
signal: controller.signal,
|
|
296
|
+
});
|
|
262
297
|
|
|
263
298
|
expect(countExitEvents(events)).toBe(1);
|
|
264
299
|
expect(lastExitEvent(events)?.reason).toBe("aborted_pre_call");
|
|
@@ -273,14 +308,21 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
273
308
|
isError: false,
|
|
274
309
|
yieldToUser: true,
|
|
275
310
|
});
|
|
276
|
-
const loop = new AgentLoop(
|
|
311
|
+
const loop = new AgentLoop({
|
|
312
|
+
provider: provider,
|
|
313
|
+
systemPrompt: "system",
|
|
314
|
+
conversationId: "test-conversation",
|
|
277
315
|
tools: dummyTools,
|
|
278
316
|
toolExecutor: toolExecutor,
|
|
279
317
|
});
|
|
280
318
|
|
|
281
319
|
const events: AgentEvent[] = [];
|
|
282
|
-
await loop.run(
|
|
283
|
-
|
|
320
|
+
await loop.run({
|
|
321
|
+
messages: [userMessage],
|
|
322
|
+
onEvent: (e) => {
|
|
323
|
+
events.push(e);
|
|
324
|
+
},
|
|
325
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
284
326
|
});
|
|
285
327
|
|
|
286
328
|
expect(countExitEvents(events)).toBe(1);
|
|
@@ -293,7 +335,10 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
293
335
|
textResponse("never reached"),
|
|
294
336
|
]);
|
|
295
337
|
const toolExecutor = async () => ({ content: "ok", isError: false });
|
|
296
|
-
const loop = new AgentLoop(
|
|
338
|
+
const loop = new AgentLoop({
|
|
339
|
+
provider: provider,
|
|
340
|
+
systemPrompt: "system",
|
|
341
|
+
conversationId: "test-conversation",
|
|
297
342
|
tools: dummyTools,
|
|
298
343
|
toolExecutor: toolExecutor,
|
|
299
344
|
});
|
|
@@ -302,13 +347,14 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
302
347
|
"budget";
|
|
303
348
|
|
|
304
349
|
const events: AgentEvent[] = [];
|
|
305
|
-
await loop.run(
|
|
306
|
-
[userMessage],
|
|
307
|
-
(e) => {
|
|
350
|
+
await loop.run({
|
|
351
|
+
messages: [userMessage],
|
|
352
|
+
onEvent: (e) => {
|
|
308
353
|
events.push(e);
|
|
309
354
|
},
|
|
310
|
-
{
|
|
311
|
-
|
|
355
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
356
|
+
onCheckpoint,
|
|
357
|
+
});
|
|
312
358
|
|
|
313
359
|
expect(countExitEvents(events)).toBe(0);
|
|
314
360
|
});
|
|
@@ -321,7 +367,10 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
321
367
|
textResponse("never reached"),
|
|
322
368
|
]);
|
|
323
369
|
const toolExecutor = async () => ({ content: "ok", isError: false });
|
|
324
|
-
const loop = new AgentLoop(
|
|
370
|
+
const loop = new AgentLoop({
|
|
371
|
+
provider: provider,
|
|
372
|
+
systemPrompt: "system",
|
|
373
|
+
conversationId: "test-conversation",
|
|
325
374
|
tools: dummyTools,
|
|
326
375
|
toolExecutor: toolExecutor,
|
|
327
376
|
});
|
|
@@ -329,7 +378,10 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
329
378
|
// AND an effective context window so small that any real token estimate
|
|
330
379
|
// of the running history exceeds the mid-loop threshold.
|
|
331
380
|
// WHEN the loop checkpoints after the tool results land
|
|
332
|
-
const result = await loop.run(
|
|
381
|
+
const result = await loop.run({
|
|
382
|
+
messages: [userMessage],
|
|
383
|
+
onEvent: () => {},
|
|
384
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
333
385
|
resolveContextWindow: () => ({
|
|
334
386
|
maxInputTokens: 10,
|
|
335
387
|
overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
|
|
@@ -348,13 +400,19 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
348
400
|
textResponse("done"),
|
|
349
401
|
]);
|
|
350
402
|
const toolExecutor = async () => ({ content: "ok", isError: false });
|
|
351
|
-
const loop = new AgentLoop(
|
|
403
|
+
const loop = new AgentLoop({
|
|
404
|
+
provider: provider,
|
|
405
|
+
systemPrompt: "system",
|
|
406
|
+
conversationId: "test-conversation",
|
|
352
407
|
tools: dummyTools,
|
|
353
408
|
toolExecutor: toolExecutor,
|
|
354
409
|
});
|
|
355
410
|
|
|
356
411
|
// WHEN the loop runs to completion
|
|
357
|
-
const result = await loop.run(
|
|
412
|
+
const result = await loop.run({
|
|
413
|
+
messages: [userMessage],
|
|
414
|
+
onEvent: () => {},
|
|
415
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
358
416
|
resolveContextWindow: () => ({
|
|
359
417
|
maxInputTokens: 10,
|
|
360
418
|
overflowRecovery: { enabled: false, safetyMarginRatio: 0 },
|
|
@@ -373,38 +431,37 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
373
431
|
textResponse("done after compaction"),
|
|
374
432
|
]);
|
|
375
433
|
const toolExecutor = async () => ({ content: "ok", isError: false });
|
|
376
|
-
const loop = new AgentLoop(
|
|
434
|
+
const loop = new AgentLoop({
|
|
435
|
+
provider: provider,
|
|
436
|
+
systemPrompt: "system",
|
|
437
|
+
conversationId: "test-conversation",
|
|
377
438
|
tools: dummyTools,
|
|
378
439
|
toolExecutor: toolExecutor,
|
|
379
440
|
});
|
|
380
441
|
|
|
381
442
|
let reinjected = false;
|
|
382
443
|
const events: AgentEvent[] = [];
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return [userMessage];
|
|
387
|
-
},
|
|
444
|
+
postCompactImpl = async () => {
|
|
445
|
+
reinjected = true;
|
|
446
|
+
return [userMessage];
|
|
388
447
|
};
|
|
389
448
|
|
|
390
449
|
// WHEN the in-loop budget gate trips at the checkpoint
|
|
391
|
-
const result = await loop.run(
|
|
392
|
-
[userMessage],
|
|
393
|
-
(event) => {
|
|
450
|
+
const result = await loop.run({
|
|
451
|
+
messages: [userMessage],
|
|
452
|
+
onEvent: (event) => {
|
|
394
453
|
events.push(event);
|
|
395
454
|
},
|
|
396
|
-
{
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
},
|
|
407
|
-
);
|
|
455
|
+
resolveContextWindow: () => ({
|
|
456
|
+
maxInputTokens: 10,
|
|
457
|
+
overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
|
|
458
|
+
}),
|
|
459
|
+
compactInPlace: true,
|
|
460
|
+
...fakeCompaction({
|
|
461
|
+
compacted: true,
|
|
462
|
+
exhausted: false,
|
|
463
|
+
}),
|
|
464
|
+
});
|
|
408
465
|
|
|
409
466
|
// THEN the loop runs the compaction ceremony in place and continues to a
|
|
410
467
|
// clean exit instead of yielding for budget. The durable commit is
|
|
@@ -416,67 +473,36 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
416
473
|
expect(result.exitReason).not.toBe("budget");
|
|
417
474
|
});
|
|
418
475
|
|
|
419
|
-
test("yields 'budget' when inline compaction times out", async () => {
|
|
420
|
-
const { provider } = createMockProvider([
|
|
421
|
-
toolUseResponse("t1", "read_file", { path: "/a.txt" }),
|
|
422
|
-
textResponse("never reached"),
|
|
423
|
-
]);
|
|
424
|
-
const toolExecutor = async () => ({ content: "ok", isError: false });
|
|
425
|
-
const loop = new AgentLoop(provider, "system", {
|
|
426
|
-
tools: dummyTools,
|
|
427
|
-
toolExecutor: toolExecutor,
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
const compaction: MidLoopCompaction = {
|
|
431
|
-
postCompactionHook: async () => {
|
|
432
|
-
throw new Error("postCompactionHook must not run after a timeout");
|
|
433
|
-
},
|
|
434
|
-
};
|
|
435
|
-
const recordOutcomeSpy = spyOn(loop.compactionCircuit, "recordOutcome");
|
|
436
|
-
|
|
437
|
-
// WHEN the compaction pipeline throws a PluginTimeoutError
|
|
438
|
-
const result = await loop.run([userMessage], () => {}, {
|
|
439
|
-
resolveContextWindow: () => ({
|
|
440
|
-
maxInputTokens: 10,
|
|
441
|
-
overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
|
|
442
|
-
}),
|
|
443
|
-
compaction,
|
|
444
|
-
turnContext: timeoutCompactionTurnContext(),
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// THEN the loop records the timeout as a compaction failure against its
|
|
448
|
-
// own circuit breaker, and yields for budget so the orchestrator can
|
|
449
|
-
// escalate.
|
|
450
|
-
expect(recordOutcomeSpy).toHaveBeenCalledTimes(1);
|
|
451
|
-
expect(recordOutcomeSpy.mock.calls[0]?.[1]).toBe(true);
|
|
452
|
-
expect(result.exitReason).toBe("budget");
|
|
453
|
-
});
|
|
454
|
-
|
|
455
476
|
test("yields 'budget' when inline compaction reports exhausted", async () => {
|
|
456
477
|
const { provider } = createMockProvider([
|
|
457
478
|
toolUseResponse("t1", "read_file", { path: "/a.txt" }),
|
|
458
479
|
textResponse("never reached"),
|
|
459
480
|
]);
|
|
460
481
|
const toolExecutor = async () => ({ content: "ok", isError: false });
|
|
461
|
-
const loop = new AgentLoop(
|
|
482
|
+
const loop = new AgentLoop({
|
|
483
|
+
provider: provider,
|
|
484
|
+
systemPrompt: "system",
|
|
485
|
+
conversationId: "test-conversation",
|
|
462
486
|
tools: dummyTools,
|
|
463
487
|
toolExecutor: toolExecutor,
|
|
464
488
|
});
|
|
465
489
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
490
|
+
postCompactImpl = async () => {
|
|
491
|
+
throw new Error(
|
|
492
|
+
"post-compaction re-injection must not run when exhausted",
|
|
493
|
+
);
|
|
470
494
|
};
|
|
471
495
|
|
|
472
496
|
// WHEN compaction exhausts its retry budget
|
|
473
|
-
const result = await loop.run(
|
|
497
|
+
const result = await loop.run({
|
|
498
|
+
messages: [userMessage],
|
|
499
|
+
onEvent: () => {},
|
|
474
500
|
resolveContextWindow: () => ({
|
|
475
501
|
maxInputTokens: 10,
|
|
476
502
|
overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
|
|
477
503
|
}),
|
|
478
|
-
|
|
479
|
-
|
|
504
|
+
compactInPlace: true,
|
|
505
|
+
...fakeCompaction({
|
|
480
506
|
compacted: false,
|
|
481
507
|
exhausted: true,
|
|
482
508
|
}),
|
|
@@ -493,11 +519,19 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
493
519
|
throw new Error("provider exploded");
|
|
494
520
|
},
|
|
495
521
|
};
|
|
496
|
-
const loop = new AgentLoop(
|
|
522
|
+
const loop = new AgentLoop({
|
|
523
|
+
provider: provider,
|
|
524
|
+
systemPrompt: "system prompt",
|
|
525
|
+
conversationId: "test-conversation",
|
|
526
|
+
});
|
|
497
527
|
|
|
498
528
|
const events: AgentEvent[] = [];
|
|
499
|
-
await loop.run(
|
|
500
|
-
|
|
529
|
+
await loop.run({
|
|
530
|
+
messages: [userMessage],
|
|
531
|
+
onEvent: (e) => {
|
|
532
|
+
events.push(e);
|
|
533
|
+
},
|
|
534
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
501
535
|
});
|
|
502
536
|
|
|
503
537
|
expect(countExitEvents(events)).toBe(1);
|
|
@@ -516,14 +550,21 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
516
550
|
isError: false,
|
|
517
551
|
yieldToUser: true,
|
|
518
552
|
});
|
|
519
|
-
const loop = new AgentLoop(
|
|
553
|
+
const loop = new AgentLoop({
|
|
554
|
+
provider: provider,
|
|
555
|
+
systemPrompt: "system",
|
|
556
|
+
conversationId: "test-conversation",
|
|
520
557
|
tools: dummyTools,
|
|
521
558
|
toolExecutor: toolExecutor,
|
|
522
559
|
});
|
|
523
560
|
|
|
524
561
|
const events: AgentEvent[] = [];
|
|
525
|
-
await loop.run(
|
|
526
|
-
|
|
562
|
+
await loop.run({
|
|
563
|
+
messages: [userMessage],
|
|
564
|
+
onEvent: (e) => {
|
|
565
|
+
events.push(e);
|
|
566
|
+
},
|
|
567
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
527
568
|
});
|
|
528
569
|
|
|
529
570
|
expect(countExitEvents(events)).toBe(1);
|
|
@@ -540,19 +581,23 @@ describe("AgentLoop exit-reason instrumentation", () => {
|
|
|
540
581
|
controller.abort();
|
|
541
582
|
return { content: "ok", isError: false };
|
|
542
583
|
};
|
|
543
|
-
const loop = new AgentLoop(
|
|
584
|
+
const loop = new AgentLoop({
|
|
585
|
+
provider: provider,
|
|
586
|
+
systemPrompt: "system",
|
|
587
|
+
conversationId: "test-conversation",
|
|
544
588
|
tools: dummyTools,
|
|
545
589
|
toolExecutor: toolExecutor,
|
|
546
590
|
});
|
|
547
591
|
|
|
548
592
|
const events: AgentEvent[] = [];
|
|
549
|
-
await loop.run(
|
|
550
|
-
[userMessage],
|
|
551
|
-
(e) => {
|
|
593
|
+
await loop.run({
|
|
594
|
+
messages: [userMessage],
|
|
595
|
+
onEvent: (e) => {
|
|
552
596
|
events.push(e);
|
|
553
597
|
},
|
|
554
|
-
{
|
|
555
|
-
|
|
598
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
599
|
+
signal: controller.signal,
|
|
600
|
+
});
|
|
556
601
|
|
|
557
602
|
expect(countExitEvents(events)).toBe(1);
|
|
558
603
|
expect(lastExitEvent(events)?.reason).toBe("aborted_during_tools");
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Verifies that the
|
|
3
|
-
* `
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* long-TTL cache breakpoint on the most recent STABLE message instead
|
|
2
|
+
* Verifies that the `memory-v3-live` cache-anchor signal surfaces on every
|
|
3
|
+
* `SendMessageOptions.config` the loop emits. When the flag is on, the latest
|
|
4
|
+
* user message carries a volatile `<memory>` block, so the loop sets
|
|
5
|
+
* `providerConfig.mutableLatestUserMessage` to tell the provider to anchor its
|
|
6
|
+
* long-TTL cache breakpoint on the most recent STABLE message instead.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* from
|
|
10
|
-
* is
|
|
8
|
+
* The loop reads the flag directly where it assembles `providerConfig`, so the
|
|
9
|
+
* signal is sourced from the flag rather than a run option. When the flag is
|
|
10
|
+
* off the field is omitted (not `false`/`undefined`) so the wire stays
|
|
11
|
+
* byte-identical to today.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
14
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
14
15
|
|
|
15
16
|
import { makeMockLogger } from "./helpers/mock-logger.js";
|
|
16
17
|
|
|
@@ -19,6 +20,10 @@ mock.module("../util/logger.js", () => ({
|
|
|
19
20
|
}));
|
|
20
21
|
|
|
21
22
|
import { AgentLoop } from "../agent/loop.js";
|
|
23
|
+
import {
|
|
24
|
+
clearCachedOverrides,
|
|
25
|
+
setCachedOverrides,
|
|
26
|
+
} from "../config/feature-flag-cache.js";
|
|
22
27
|
import type {
|
|
23
28
|
Message,
|
|
24
29
|
Provider,
|
|
@@ -75,13 +80,20 @@ function makeRecordingProvider(responses: ProviderResponse[]): {
|
|
|
75
80
|
return { provider, configs: () => configs };
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
describe("AgentLoop.run — mutableLatestUserMessage
|
|
79
|
-
|
|
83
|
+
describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () => {
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
clearCachedOverrides();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("sets mutableLatestUserMessage on every LLM call when memory-v3-live is on (multi-turn)", async () => {
|
|
89
|
+
// GIVEN memory-v3-live is enabled
|
|
90
|
+
setCachedOverrides({ "memory-v3-live": true }, { fromGateway: true });
|
|
91
|
+
|
|
92
|
+
// AND a provider that records the config of each LLM call across a tool round-trip
|
|
80
93
|
const { provider, configs } = makeRecordingProvider([
|
|
81
94
|
toolUseResponse("t1", "echo", { value: "first" }),
|
|
82
95
|
textResponse("done"),
|
|
83
96
|
]);
|
|
84
|
-
|
|
85
97
|
const dummyTools: ToolDefinition[] = [
|
|
86
98
|
{
|
|
87
99
|
name: "echo",
|
|
@@ -92,50 +104,53 @@ describe("AgentLoop.run — mutableLatestUserMessage plumbing", () => {
|
|
|
92
104
|
},
|
|
93
105
|
},
|
|
94
106
|
];
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
const loop = new AgentLoop({
|
|
108
|
+
provider: provider,
|
|
109
|
+
systemPrompt: "system",
|
|
110
|
+
conversationId: "test-conversation",
|
|
99
111
|
config: { maxTokens: 1024 },
|
|
100
112
|
tools: dummyTools,
|
|
101
|
-
toolExecutor:
|
|
113
|
+
toolExecutor: async () => ({ content: "ok", isError: false }),
|
|
102
114
|
});
|
|
103
115
|
|
|
104
|
-
|
|
116
|
+
// WHEN the loop runs
|
|
117
|
+
await loop.run({
|
|
118
|
+
messages: [userMessage],
|
|
119
|
+
onEvent: () => {},
|
|
120
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
105
121
|
callSite: "mainAgent",
|
|
106
|
-
mutableLatestUserMessage: true,
|
|
107
122
|
});
|
|
108
123
|
|
|
109
|
-
//
|
|
124
|
+
// THEN every send (initial + tool round-trip) carries the cache-anchor signal
|
|
110
125
|
expect(configs()).toHaveLength(2);
|
|
111
126
|
for (const cfg of configs()) {
|
|
112
127
|
expect(cfg?.mutableLatestUserMessage).toBe(true);
|
|
113
128
|
}
|
|
114
129
|
});
|
|
115
130
|
|
|
116
|
-
test("omits mutableLatestUserMessage when
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
config: { maxTokens: 1024 },
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
await loop.run([userMessage], () => {});
|
|
131
|
+
test("omits mutableLatestUserMessage when memory-v3-live is off (flag-off byte-identity)", async () => {
|
|
132
|
+
// GIVEN memory-v3-live is off (no override; registry default is false)
|
|
133
|
+
clearCachedOverrides();
|
|
123
134
|
|
|
124
|
-
|
|
125
|
-
expect("mutableLatestUserMessage" in (configs()[0] ?? {})).toBe(false);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("omits mutableLatestUserMessage when explicitly false (flag-off byte-identity)", async () => {
|
|
135
|
+
// AND a provider that records the config of each LLM call
|
|
129
136
|
const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
|
|
130
|
-
const loop = new AgentLoop(
|
|
137
|
+
const loop = new AgentLoop({
|
|
138
|
+
provider: provider,
|
|
139
|
+
systemPrompt: "system",
|
|
140
|
+
conversationId: "test-conversation",
|
|
131
141
|
config: { maxTokens: 1024 },
|
|
132
142
|
});
|
|
133
143
|
|
|
134
|
-
|
|
144
|
+
// WHEN the loop runs
|
|
145
|
+
await loop.run({
|
|
146
|
+
messages: [userMessage],
|
|
147
|
+
onEvent: () => {},
|
|
148
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
135
149
|
callSite: "mainAgent",
|
|
136
|
-
mutableLatestUserMessage: false,
|
|
137
150
|
});
|
|
138
151
|
|
|
152
|
+
// THEN the field is omitted entirely, not carried as false/undefined
|
|
153
|
+
expect(configs()).toHaveLength(1);
|
|
139
154
|
expect("mutableLatestUserMessage" in (configs()[0] ?? {})).toBe(false);
|
|
140
155
|
});
|
|
141
156
|
});
|