@vellumai/assistant 0.8.8 → 0.8.9-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +6 -6
- package/bun.lock +2 -2
- package/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 +235 -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 +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +1 -1
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +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__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +1 -1
- 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 +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +142 -218
- package/src/__tests__/conversation-agent-loop.test.ts +297 -586
- 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-history-web-search.test.ts +1 -1
- 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 +70 -65
- 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 +131 -26
- 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__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +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 +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +225 -9
- package/src/acp/resolve-agent.ts +122 -17
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +414 -248
- 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 +85 -33
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -2
- package/src/config/feature-flag-registry.json +15 -6
- package/src/config/schemas/call-site-catalog.ts +0 -14
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +0 -2
- package/src/context/compactor.ts +22 -4
- 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 +175 -1055
- 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 +404 -57
- package/src/daemon/disk-pressure-policy.ts +0 -1
- 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 -7
- 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/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
- package/src/plugins/defaults/compaction/manager-store.ts +57 -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 +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
- 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/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 +315 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/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 +337 -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 +8 -2
- package/src/tools/acp/spawn.test.ts +176 -195
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/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/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- /package/src/{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
|
@@ -39,6 +39,20 @@ const conversationRuntimeAssemblyRealSnapshot = {
|
|
|
39
39
|
|
|
40
40
|
// ── Module mocks (must precede imports of the module under test) ─────
|
|
41
41
|
|
|
42
|
+
// The real AgentLoop resolves the per-conversation ContextWindowManager from
|
|
43
|
+
// the compaction store keyed by conversationId. These overflow tests build
|
|
44
|
+
// fake conversations whose manager is a canned stub, so register each stub in a
|
|
45
|
+
// map the mocked store reads from.
|
|
46
|
+
const fakeContextWindowManagers = new Map<string, unknown>();
|
|
47
|
+
mock.module("../plugins/defaults/compaction/manager-store.js", () => ({
|
|
48
|
+
createContextWindowManager: () => undefined,
|
|
49
|
+
getContextWindowManager: (conversationId: string) =>
|
|
50
|
+
fakeContextWindowManagers.get(conversationId),
|
|
51
|
+
disposeContextWindowManager: (conversationId: string) => {
|
|
52
|
+
fakeContextWindowManagers.delete(conversationId);
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
|
|
42
56
|
mock.module("../util/logger.js", () => ({
|
|
43
57
|
getLogger: () =>
|
|
44
58
|
new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
|
|
@@ -100,7 +114,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
100
114
|
// Can be a number (constant), a no-arg function, or a function that
|
|
101
115
|
// receives the messages array for dynamic behavior based on content.
|
|
102
116
|
// Both the calibrated entry point (`estimatePromptTokens`, which backs the
|
|
103
|
-
//
|
|
117
|
+
// loop's budget gate and the convergence path) and the raw entry point
|
|
104
118
|
// (`estimatePromptTokensRaw`, used by the pre-send calibration capture) are
|
|
105
119
|
// stubbed so either call site can drive the test.
|
|
106
120
|
let mockEstimateTokens: number | ((msgs?: Message[]) => number) = 1000;
|
|
@@ -113,7 +127,7 @@ mock.module("../context/token-estimator.js", () => ({
|
|
|
113
127
|
typeof mockEstimateTokens === "function"
|
|
114
128
|
? mockEstimateTokens(msgs)
|
|
115
129
|
: mockEstimateTokens,
|
|
116
|
-
// The
|
|
130
|
+
// The loop's budget gate calls this calibrated wrapper directly, so it
|
|
117
131
|
// must honor `mockEstimateTokens` too — otherwise the real implementation
|
|
118
132
|
// (which sums tool tokens onto the real calibrated estimate) ignores the
|
|
119
133
|
// per-test value and the overflow scenarios below never trigger.
|
|
@@ -137,35 +151,38 @@ mock.module("../context/token-estimator.js", () => ({
|
|
|
137
151
|
let mockReducerStepFn:
|
|
138
152
|
| ((msgs: Message[], cfg: unknown, state: unknown) => unknown)
|
|
139
153
|
| null = null;
|
|
140
|
-
mock.module(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
154
|
+
mock.module(
|
|
155
|
+
"../plugins/defaults/compaction/context-overflow-reducer.js",
|
|
156
|
+
() => ({
|
|
157
|
+
createInitialReducerState: () => ({
|
|
158
|
+
appliedTiers: [],
|
|
159
|
+
injectionMode: "full" as const,
|
|
160
|
+
exhausted: false,
|
|
161
|
+
}),
|
|
162
|
+
reduceContextOverflow: async (
|
|
163
|
+
msgs: Message[],
|
|
164
|
+
cfg: unknown,
|
|
165
|
+
state: unknown,
|
|
166
|
+
) => {
|
|
167
|
+
if (mockReducerStepFn) return mockReducerStepFn(msgs, cfg, state);
|
|
168
|
+
return {
|
|
169
|
+
messages: msgs,
|
|
170
|
+
tier: "forced_compaction",
|
|
171
|
+
state: {
|
|
172
|
+
appliedTiers: [
|
|
173
|
+
"forced_compaction",
|
|
174
|
+
"tool_result_truncation",
|
|
175
|
+
"media_stubbing",
|
|
176
|
+
"injection_downgrade",
|
|
177
|
+
],
|
|
178
|
+
injectionMode: "full",
|
|
179
|
+
exhausted: true,
|
|
180
|
+
},
|
|
181
|
+
estimatedTokens: 1000,
|
|
182
|
+
};
|
|
183
|
+
},
|
|
145
184
|
}),
|
|
146
|
-
|
|
147
|
-
msgs: Message[],
|
|
148
|
-
cfg: unknown,
|
|
149
|
-
state: unknown,
|
|
150
|
-
) => {
|
|
151
|
-
if (mockReducerStepFn) return mockReducerStepFn(msgs, cfg, state);
|
|
152
|
-
return {
|
|
153
|
-
messages: msgs,
|
|
154
|
-
tier: "forced_compaction",
|
|
155
|
-
state: {
|
|
156
|
-
appliedTiers: [
|
|
157
|
-
"forced_compaction",
|
|
158
|
-
"tool_result_truncation",
|
|
159
|
-
"media_stubbing",
|
|
160
|
-
"injection_downgrade",
|
|
161
|
-
],
|
|
162
|
-
injectionMode: "full",
|
|
163
|
-
exhausted: true,
|
|
164
|
-
},
|
|
165
|
-
estimatedTokens: 1000,
|
|
166
|
-
};
|
|
167
|
-
},
|
|
168
|
-
}));
|
|
185
|
+
);
|
|
169
186
|
|
|
170
187
|
// Policy: default to fail_gracefully
|
|
171
188
|
let mockOverflowAction: string = "fail_gracefully";
|
|
@@ -202,7 +219,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
202
219
|
updateMessageMetadata: () => {},
|
|
203
220
|
setLastNotifiedInferenceProfile: () => {},
|
|
204
221
|
getLastUserTimestampBefore: () => 0,
|
|
205
|
-
|
|
222
|
+
resolveOverrideProfile: () => undefined,
|
|
206
223
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
207
224
|
}));
|
|
208
225
|
|
|
@@ -422,10 +439,8 @@ mock.module("../memory/archive-store.js", () => ({
|
|
|
422
439
|
// ── Imports (after mocks) ────────────────────────────────────────────
|
|
423
440
|
|
|
424
441
|
import { AgentLoop } from "../agent/loop.js";
|
|
425
|
-
import {
|
|
426
|
-
|
|
427
|
-
runAgentLoopImpl,
|
|
428
|
-
} from "../daemon/conversation-agent-loop.js";
|
|
442
|
+
import type { Conversation } from "../daemon/conversation.js";
|
|
443
|
+
import { runAgentLoopImpl } from "../daemon/conversation-agent-loop.js";
|
|
429
444
|
import {
|
|
430
445
|
createMockProvider,
|
|
431
446
|
type ScriptedResponse,
|
|
@@ -436,13 +451,13 @@ import {
|
|
|
436
451
|
// ── Test helpers ─────────────────────────────────────────────────────
|
|
437
452
|
|
|
438
453
|
function makeCtx(
|
|
439
|
-
overrides?: Partial<
|
|
454
|
+
overrides?: Partial<Conversation> & {
|
|
440
455
|
providerResponses?: ScriptedResponse[];
|
|
441
456
|
loopProvider?: Provider;
|
|
442
457
|
loopTools?: ToolDefinition[];
|
|
443
458
|
toolExecutor?: LoopToolExecutor;
|
|
444
459
|
},
|
|
445
|
-
):
|
|
460
|
+
): Conversation {
|
|
446
461
|
const {
|
|
447
462
|
providerResponses,
|
|
448
463
|
loopProvider,
|
|
@@ -465,13 +480,15 @@ function makeCtx(
|
|
|
465
480
|
providerResponses ?? [textResponse("response")],
|
|
466
481
|
loopProviderName,
|
|
467
482
|
).provider;
|
|
468
|
-
const agentLoop = new AgentLoop(
|
|
483
|
+
const agentLoop = new AgentLoop({
|
|
484
|
+
provider: provider,
|
|
485
|
+
systemPrompt: "system prompt",
|
|
469
486
|
conversationId,
|
|
470
487
|
tools: loopTools ?? [],
|
|
471
488
|
toolExecutor,
|
|
472
489
|
});
|
|
473
490
|
|
|
474
|
-
|
|
491
|
+
const ctx = {
|
|
475
492
|
conversationId: "test-conv",
|
|
476
493
|
messages: [
|
|
477
494
|
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
|
@@ -495,13 +512,14 @@ function makeCtx(
|
|
|
495
512
|
usage: { inputTokens: 0, outputTokens: 0 },
|
|
496
513
|
stopReason: "end_turn",
|
|
497
514
|
}),
|
|
498
|
-
} as unknown as
|
|
515
|
+
} as unknown as Conversation["provider"],
|
|
499
516
|
systemPrompt: "system prompt",
|
|
500
517
|
|
|
501
518
|
contextWindowManager: {
|
|
519
|
+
updateConfig: () => {},
|
|
502
520
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
503
521
|
maybeCompact: async () => ({ compacted: false }),
|
|
504
|
-
} as unknown as
|
|
522
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
505
523
|
contextCompactedMessageCount: 0,
|
|
506
524
|
contextCompactedAt: null,
|
|
507
525
|
|
|
@@ -515,8 +533,6 @@ function makeCtx(
|
|
|
515
533
|
currentTurnSurfaces: [],
|
|
516
534
|
|
|
517
535
|
workingDir: "/tmp",
|
|
518
|
-
workspaceTopLevelContext: null,
|
|
519
|
-
workspaceTopLevelDirty: false,
|
|
520
536
|
channelCapabilities: undefined,
|
|
521
537
|
commandIntent: undefined,
|
|
522
538
|
trustContext: undefined,
|
|
@@ -526,15 +542,15 @@ function makeCtx(
|
|
|
526
542
|
preactivatedSkillIds: undefined,
|
|
527
543
|
skillProjectionState: new Map(),
|
|
528
544
|
skillProjectionCache:
|
|
529
|
-
new Map() as unknown as
|
|
545
|
+
new Map() as unknown as Conversation["skillProjectionCache"],
|
|
530
546
|
|
|
531
547
|
traceEmitter: {
|
|
532
548
|
emit: () => {},
|
|
533
|
-
} as unknown as
|
|
549
|
+
} as unknown as Conversation["traceEmitter"],
|
|
534
550
|
profiler: {
|
|
535
551
|
startRequest: () => {},
|
|
536
552
|
emitSummary: () => {},
|
|
537
|
-
} as unknown as
|
|
553
|
+
} as unknown as Conversation["profiler"],
|
|
538
554
|
usageStats: {
|
|
539
555
|
totalInputTokens: 0,
|
|
540
556
|
totalOutputTokens: 0,
|
|
@@ -547,13 +563,12 @@ function makeCtx(
|
|
|
547
563
|
lastAttachmentWarnings: [],
|
|
548
564
|
|
|
549
565
|
hasNoClient: false,
|
|
550
|
-
prompter: {} as unknown as
|
|
551
|
-
queue: {} as unknown as
|
|
566
|
+
prompter: {} as unknown as Conversation["prompter"],
|
|
567
|
+
queue: {} as unknown as Conversation["queue"],
|
|
552
568
|
|
|
553
569
|
getWorkspaceGitService: () => ({ ensureInitialized: async () => {} }),
|
|
554
570
|
commitTurnChanges: async () => {},
|
|
555
571
|
|
|
556
|
-
refreshWorkspaceTopLevelContextIfNeeded: () => {},
|
|
557
572
|
markWorkspaceTopLevelDirty: () => {},
|
|
558
573
|
emitActivityState: () => {},
|
|
559
574
|
getQueueDepth: () => 0,
|
|
@@ -580,10 +595,12 @@ function makeCtx(
|
|
|
580
595
|
}),
|
|
581
596
|
retrackCachedNodes: () => {},
|
|
582
597
|
recordPkbQueryVectors: () => {},
|
|
583
|
-
} as unknown as
|
|
598
|
+
} as unknown as Conversation["graphMemory"],
|
|
584
599
|
|
|
585
600
|
...ctxOverrides,
|
|
586
|
-
} as
|
|
601
|
+
} as unknown as Conversation;
|
|
602
|
+
fakeContextWindowManagers.set(conversationId, ctx.contextWindowManager);
|
|
603
|
+
return ctx;
|
|
587
604
|
}
|
|
588
605
|
|
|
589
606
|
/**
|
|
@@ -651,10 +668,9 @@ beforeEach(() => {
|
|
|
651
668
|
recordUsageMock.mockClear();
|
|
652
669
|
setAgentLoopExitReasonOnLatestLogMock.mockClear();
|
|
653
670
|
addMessageMock.mockClear();
|
|
654
|
-
// Reset the plugin registry and re-register every default so the
|
|
655
|
-
//
|
|
656
|
-
//
|
|
657
|
-
// (`reduceContextOverflow`, `syncMessageToDisk`, …) these tests install.
|
|
671
|
+
// Reset the plugin registry and re-register every default so the compaction
|
|
672
|
+
// pipeline dispatches to the default middleware, which in turn hits the
|
|
673
|
+
// mocked collaborators (`syncMessageToDisk`, …) these tests install.
|
|
658
674
|
resetPluginRegistryAndRegisterDefaults();
|
|
659
675
|
});
|
|
660
676
|
|
|
@@ -768,9 +784,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
768
784
|
isError: false,
|
|
769
785
|
}),
|
|
770
786
|
contextWindowManager: {
|
|
787
|
+
updateConfig: () => {},
|
|
771
788
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
772
789
|
maybeCompact: async () => ({ compacted: false }),
|
|
773
|
-
} as unknown as
|
|
790
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
774
791
|
});
|
|
775
792
|
|
|
776
793
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
@@ -797,9 +814,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
797
814
|
const events: ServerMessage[] = [];
|
|
798
815
|
let reducerCalled = false;
|
|
799
816
|
|
|
800
|
-
// GIVEN the estimator reports 185k
|
|
801
|
-
//
|
|
802
|
-
//
|
|
817
|
+
// GIVEN the estimator reports 185k and the context manager's compaction
|
|
818
|
+
// is a no-op, so the first call proceeds to the provider without any
|
|
819
|
+
// up-front reduction.
|
|
803
820
|
mockEstimateTokens = 185_000;
|
|
804
821
|
|
|
805
822
|
// AND the post-run convergence reducer successfully compacts
|
|
@@ -842,9 +859,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
842
859
|
const ctx = makeCtx({
|
|
843
860
|
loopProvider: provider,
|
|
844
861
|
contextWindowManager: {
|
|
862
|
+
updateConfig: () => {},
|
|
845
863
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
846
864
|
maybeCompact: async () => ({ compacted: false }),
|
|
847
|
-
} as unknown as
|
|
865
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
848
866
|
});
|
|
849
867
|
|
|
850
868
|
// WHEN the turn runs
|
|
@@ -918,9 +936,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
918
936
|
const ctx = makeCtx({
|
|
919
937
|
loopProvider: provider,
|
|
920
938
|
contextWindowManager: {
|
|
939
|
+
updateConfig: () => {},
|
|
921
940
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
922
941
|
maybeCompact: async () => ({ compacted: false }),
|
|
923
|
-
} as unknown as
|
|
942
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
924
943
|
});
|
|
925
944
|
|
|
926
945
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
@@ -1005,9 +1024,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1005
1024
|
loopProvider: provider,
|
|
1006
1025
|
messages: longHistory,
|
|
1007
1026
|
contextWindowManager: {
|
|
1027
|
+
updateConfig: () => {},
|
|
1008
1028
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1009
1029
|
maybeCompact: async () => ({ compacted: false }),
|
|
1010
|
-
} as unknown as
|
|
1030
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1011
1031
|
});
|
|
1012
1032
|
|
|
1013
1033
|
await runAgentLoopImpl(ctx, "analyze this", "msg-1", (msg) =>
|
|
@@ -1087,6 +1107,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1087
1107
|
isError: false,
|
|
1088
1108
|
}),
|
|
1089
1109
|
contextWindowManager: {
|
|
1110
|
+
updateConfig: () => {},
|
|
1090
1111
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1091
1112
|
maybeCompact: async (
|
|
1092
1113
|
_msgs: Message[],
|
|
@@ -1118,7 +1139,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1118
1139
|
}
|
|
1119
1140
|
return { compacted: false };
|
|
1120
1141
|
},
|
|
1121
|
-
} as unknown as
|
|
1142
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1122
1143
|
});
|
|
1123
1144
|
|
|
1124
1145
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
@@ -1187,6 +1208,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1187
1208
|
isError: false,
|
|
1188
1209
|
}),
|
|
1189
1210
|
contextWindowManager: {
|
|
1211
|
+
updateConfig: () => {},
|
|
1190
1212
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1191
1213
|
maybeCompact: async () => {
|
|
1192
1214
|
compactionCalled = true;
|
|
@@ -1211,7 +1233,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1211
1233
|
summaryModel: "mock-model",
|
|
1212
1234
|
};
|
|
1213
1235
|
},
|
|
1214
|
-
} as unknown as
|
|
1236
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1215
1237
|
});
|
|
1216
1238
|
|
|
1217
1239
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
@@ -1293,6 +1315,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1293
1315
|
isError: false,
|
|
1294
1316
|
}),
|
|
1295
1317
|
contextWindowManager: {
|
|
1318
|
+
updateConfig: () => {},
|
|
1296
1319
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1297
1320
|
maybeCompact: async () => {
|
|
1298
1321
|
compactionCalled = true;
|
|
@@ -1317,7 +1340,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1317
1340
|
summaryModel: "mock-model",
|
|
1318
1341
|
};
|
|
1319
1342
|
},
|
|
1320
|
-
} as unknown as
|
|
1343
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1321
1344
|
});
|
|
1322
1345
|
|
|
1323
1346
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => {
|
|
@@ -1360,14 +1383,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1360
1383
|
|
|
1361
1384
|
// Budget = 200_000 * 0.95 = 190_000
|
|
1362
1385
|
// Mid-loop threshold = 190_000 * 0.85 = 161_500
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
// Preflight: below budget
|
|
1367
|
-
if (estimateCallCount === 1) return 100_000;
|
|
1368
|
-
// Every checkpoint call: above threshold — always triggers yield
|
|
1369
|
-
return 170_000;
|
|
1370
|
-
};
|
|
1386
|
+
// Every estimate is above the threshold, so the first-call gate compacts
|
|
1387
|
+
// before the first provider call and every checkpoint trips the yield.
|
|
1388
|
+
mockEstimateTokens = 170_000;
|
|
1371
1389
|
|
|
1372
1390
|
// The convergence reducer reduces tokens enough for the rerun to recover.
|
|
1373
1391
|
let convergenceReducerCalled = false;
|
|
@@ -1410,6 +1428,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1410
1428
|
],
|
|
1411
1429
|
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
1412
1430
|
contextWindowManager: {
|
|
1431
|
+
updateConfig: () => {},
|
|
1413
1432
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1414
1433
|
maybeCompact: async () => {
|
|
1415
1434
|
compactionCallCount++;
|
|
@@ -1441,7 +1460,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1441
1460
|
summaryModel: "mock-model",
|
|
1442
1461
|
};
|
|
1443
1462
|
},
|
|
1444
|
-
} as unknown as
|
|
1463
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1445
1464
|
});
|
|
1446
1465
|
|
|
1447
1466
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
@@ -1480,16 +1499,11 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1480
1499
|
|
|
1481
1500
|
// Budget = 200_000 * 0.95 = 190_000
|
|
1482
1501
|
// Mid-loop threshold = 190_000 * 0.85 = 161_500
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
// Every checkpoint estimate: above threshold — always trips the
|
|
1489
|
-
// yield. Simulates a long turn where each tool call's result
|
|
1490
|
-
// inflates the context past 85% even after a successful compaction.
|
|
1491
|
-
return 170_000;
|
|
1492
|
-
};
|
|
1502
|
+
// Every estimate is above the threshold: the first-call gate compacts
|
|
1503
|
+
// before the first provider call, and each subsequent checkpoint trips
|
|
1504
|
+
// the yield even after a successful compaction (each tool result inflates
|
|
1505
|
+
// the context back past 85%).
|
|
1506
|
+
mockEstimateTokens = 170_000;
|
|
1493
1507
|
|
|
1494
1508
|
// A single tool round reaches one checkpoint; the in-loop budget gate
|
|
1495
1509
|
// trips there and compaction runs in place. The loop continues the run
|
|
@@ -1518,6 +1532,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1518
1532
|
],
|
|
1519
1533
|
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
1520
1534
|
contextWindowManager: {
|
|
1535
|
+
updateConfig: () => {},
|
|
1521
1536
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1522
1537
|
maybeCompact: async () => {
|
|
1523
1538
|
compactionCallCount++;
|
|
@@ -1542,7 +1557,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1542
1557
|
summaryModel: "mock-model",
|
|
1543
1558
|
};
|
|
1544
1559
|
},
|
|
1545
|
-
} as unknown as
|
|
1560
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1546
1561
|
});
|
|
1547
1562
|
|
|
1548
1563
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
@@ -1633,6 +1648,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1633
1648
|
],
|
|
1634
1649
|
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
1635
1650
|
contextWindowManager: {
|
|
1651
|
+
updateConfig: () => {},
|
|
1636
1652
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1637
1653
|
// Under the new architecture (Compaction Re-homing Arc, Bullet 1)
|
|
1638
1654
|
// the retry budget lives inside `ContextWindowManager._maybeCompact`,
|
|
@@ -1660,7 +1676,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1660
1676
|
summaryModel: "mock-model",
|
|
1661
1677
|
exhausted: true,
|
|
1662
1678
|
}),
|
|
1663
|
-
} as unknown as
|
|
1679
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1664
1680
|
});
|
|
1665
1681
|
|
|
1666
1682
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
@@ -1680,124 +1696,6 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1680
1696
|
);
|
|
1681
1697
|
});
|
|
1682
1698
|
|
|
1683
|
-
// ── Test 8 ────────────────────────────────────────────────────────
|
|
1684
|
-
// BUG: The preflight overflow reducer's budget check uses
|
|
1685
|
-
// step.estimatedTokens (computed on bare ctx.messages) without
|
|
1686
|
-
// accounting for tokens added by applyRuntimeInjections(). This
|
|
1687
|
-
// causes the reducer to stop early when the bare estimate is under
|
|
1688
|
-
// budget, even though post-injection tokens exceed it — leading to
|
|
1689
|
-
// a wasted provider round-trip that gets rejected.
|
|
1690
|
-
//
|
|
1691
|
-
// After fix: the budget check re-estimates on runMessages (with
|
|
1692
|
-
// injections) so the reducer continues to the next tier.
|
|
1693
|
-
test("preflight reducer continues when post-injection tokens exceed budget", async () => {
|
|
1694
|
-
const events: ServerMessage[] = [];
|
|
1695
|
-
|
|
1696
|
-
// Injections add an extra message, bumping the token count.
|
|
1697
|
-
const injectionMessage: Message = {
|
|
1698
|
-
role: "user" as const,
|
|
1699
|
-
content: [
|
|
1700
|
-
{
|
|
1701
|
-
type: "text" as const,
|
|
1702
|
-
text: "injected context " + "x".repeat(500),
|
|
1703
|
-
},
|
|
1704
|
-
],
|
|
1705
|
-
};
|
|
1706
|
-
mockApplyRuntimeInjections = (msgs) => [...msgs, injectionMessage];
|
|
1707
|
-
|
|
1708
|
-
// Budget = 200_000 * 0.95 = 190_000
|
|
1709
|
-
// The estimator returns different values based on whether the
|
|
1710
|
-
// injection message is present:
|
|
1711
|
-
// - bare history (no injection msg) → 195_000 (triggers preflight)
|
|
1712
|
-
// - after tier 1 bare → 185_000 (under budget, would stop early without fix)
|
|
1713
|
-
// - after tier 1 with injection → 195_000 (still over budget)
|
|
1714
|
-
// - after tier 2 bare → 170_000
|
|
1715
|
-
// - after tier 2 with injection → 175_000 (under budget, reducer stops)
|
|
1716
|
-
let reducerCallCount = 0;
|
|
1717
|
-
mockEstimateTokens = (msgs?: Message[]) => {
|
|
1718
|
-
const hasInjection = msgs?.some(
|
|
1719
|
-
(m) =>
|
|
1720
|
-
m.role === "user" &&
|
|
1721
|
-
Array.isArray(m.content) &&
|
|
1722
|
-
m.content.some(
|
|
1723
|
-
(b: { type: string; text?: string }) =>
|
|
1724
|
-
b.type === "text" &&
|
|
1725
|
-
typeof b.text === "string" &&
|
|
1726
|
-
b.text.startsWith("injected context"),
|
|
1727
|
-
),
|
|
1728
|
-
);
|
|
1729
|
-
if (reducerCallCount === 0) {
|
|
1730
|
-
// Before any reduction: preflight check on runMessages (with injection)
|
|
1731
|
-
return 195_000;
|
|
1732
|
-
}
|
|
1733
|
-
if (reducerCallCount === 1) {
|
|
1734
|
-
// After tier 1
|
|
1735
|
-
return hasInjection ? 195_000 : 185_000;
|
|
1736
|
-
}
|
|
1737
|
-
// After tier 2
|
|
1738
|
-
return hasInjection ? 175_000 : 170_000;
|
|
1739
|
-
};
|
|
1740
|
-
|
|
1741
|
-
mockReducerStepFn = (msgs: Message[]) => {
|
|
1742
|
-
reducerCallCount++;
|
|
1743
|
-
const tier =
|
|
1744
|
-
reducerCallCount === 1 ? "forced_compaction" : "tool_result_truncation";
|
|
1745
|
-
return {
|
|
1746
|
-
messages: msgs,
|
|
1747
|
-
tier,
|
|
1748
|
-
state: {
|
|
1749
|
-
appliedTiers:
|
|
1750
|
-
reducerCallCount === 1
|
|
1751
|
-
? ["forced_compaction"]
|
|
1752
|
-
: ["forced_compaction", "tool_result_truncation"],
|
|
1753
|
-
injectionMode: "full" as const,
|
|
1754
|
-
exhausted: reducerCallCount >= 2,
|
|
1755
|
-
},
|
|
1756
|
-
// Bare-history estimate (what the reducer sees on ctx.messages)
|
|
1757
|
-
estimatedTokens: reducerCallCount === 1 ? 185_000 : 170_000,
|
|
1758
|
-
compactionResult: {
|
|
1759
|
-
compacted: true,
|
|
1760
|
-
messages: msgs,
|
|
1761
|
-
compactedPersistedMessages: 5,
|
|
1762
|
-
summaryText: "Summary",
|
|
1763
|
-
previousEstimatedInputTokens: 195_000,
|
|
1764
|
-
estimatedInputTokens: reducerCallCount === 1 ? 185_000 : 170_000,
|
|
1765
|
-
maxInputTokens: 200_000,
|
|
1766
|
-
thresholdTokens: 160_000,
|
|
1767
|
-
compactedMessages: 10,
|
|
1768
|
-
summaryCalls: 1,
|
|
1769
|
-
summaryInputTokens: 500,
|
|
1770
|
-
summaryOutputTokens: 200,
|
|
1771
|
-
summaryModel: "mock-model",
|
|
1772
|
-
},
|
|
1773
|
-
};
|
|
1774
|
-
};
|
|
1775
|
-
|
|
1776
|
-
// The preflight overflow reducer runs in the orchestrator before the loop,
|
|
1777
|
-
// so a single successful provider turn is enough to drive the path.
|
|
1778
|
-
const ctx = makeCtx({
|
|
1779
|
-
providerResponses: [textResponse("done")],
|
|
1780
|
-
contextWindowManager: {
|
|
1781
|
-
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1782
|
-
maybeCompact: async () => ({ compacted: false }),
|
|
1783
|
-
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
1784
|
-
});
|
|
1785
|
-
|
|
1786
|
-
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1787
|
-
|
|
1788
|
-
// The reducer must be called twice — the first tier's bare estimate
|
|
1789
|
-
// (185k) is under budget (190k), but post-injection tokens (195k)
|
|
1790
|
-
// still exceed it. Without the fix, the reducer would stop after
|
|
1791
|
-
// tier 1 and the provider call would likely fail.
|
|
1792
|
-
expect(reducerCallCount).toBe(2);
|
|
1793
|
-
|
|
1794
|
-
// Should succeed without errors
|
|
1795
|
-
const conversationError = events.find(
|
|
1796
|
-
(e) => e.type === "conversation_error",
|
|
1797
|
-
);
|
|
1798
|
-
expect(conversationError).toBeUndefined();
|
|
1799
|
-
});
|
|
1800
|
-
|
|
1801
1699
|
// ── Test 9 ────────────────────────────────────────────────────────
|
|
1802
1700
|
// When the `auto_compress_latest_turn` rerun (the last layer of the
|
|
1803
1701
|
// overflow-recovery ladder) still yields at the mid-loop checkpoint,
|
|
@@ -1854,14 +1752,15 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1854
1752
|
toolUseResponse("tu-1", "bash", { command: "ls" }),
|
|
1855
1753
|
]);
|
|
1856
1754
|
|
|
1857
|
-
// `maybeCompact` is invoked through three distinct call
|
|
1858
|
-
//
|
|
1859
|
-
//
|
|
1860
|
-
//
|
|
1861
|
-
//
|
|
1862
|
-
//
|
|
1863
|
-
//
|
|
1864
|
-
//
|
|
1755
|
+
// Every forced `maybeCompact` is invoked through three distinct call
|
|
1756
|
+
// sites, all owned by the loop's pre-call budget gate / recovery ladder:
|
|
1757
|
+
// 1. First-call (turn-start) gate (`force: true`) — the loop now owns
|
|
1758
|
+
// the turn-start compaction. It succeeds, but the mocked estimate
|
|
1759
|
+
// stays above the mid-loop threshold, so the turn proceeds and the
|
|
1760
|
+
// mid-loop gate still trips on the next iteration.
|
|
1761
|
+
// 2. Mid-loop after the first tool turn (`force: true`) — must signal
|
|
1762
|
+
// `exhausted: true` so the daemon escalates to the convergence
|
|
1763
|
+
// reducer instead of looping forever.
|
|
1865
1764
|
// 3. auto_compress_latest_turn emergency compaction (`force: true`,
|
|
1866
1765
|
// `minKeepRecentUserTurns: 0`) — succeeds and drops tokens below
|
|
1867
1766
|
// threshold; the subsequent rerun yields again and is classified
|
|
@@ -1881,22 +1780,47 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1881
1780
|
],
|
|
1882
1781
|
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
1883
1782
|
contextWindowManager: {
|
|
1783
|
+
updateConfig: () => {},
|
|
1884
1784
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1885
1785
|
maybeCompact: async (
|
|
1886
1786
|
_msgs: Message[],
|
|
1887
1787
|
_signal: AbortSignal,
|
|
1888
1788
|
opts?: { force?: boolean },
|
|
1889
1789
|
) => {
|
|
1890
|
-
//
|
|
1891
|
-
// no-op
|
|
1790
|
+
// Only forced compactions drive this test; any non-forced probe is
|
|
1791
|
+
// a no-op.
|
|
1892
1792
|
if (!opts?.force) {
|
|
1893
1793
|
return { compacted: false };
|
|
1894
1794
|
}
|
|
1895
1795
|
forcedMaybeCompactCallCount++;
|
|
1896
1796
|
if (forcedMaybeCompactCallCount === 1) {
|
|
1897
|
-
//
|
|
1898
|
-
//
|
|
1899
|
-
//
|
|
1797
|
+
// First-call (turn-start) gate: succeeds, but the mocked estimate
|
|
1798
|
+
// stays above the threshold so the turn proceeds to the call and
|
|
1799
|
+
// the mid-loop gate still trips next iteration.
|
|
1800
|
+
return {
|
|
1801
|
+
compacted: true,
|
|
1802
|
+
messages: [
|
|
1803
|
+
{
|
|
1804
|
+
role: "user" as const,
|
|
1805
|
+
content: [{ type: "text", text: "turn-start compacted" }],
|
|
1806
|
+
},
|
|
1807
|
+
] as Message[],
|
|
1808
|
+
compactedPersistedMessages: 5,
|
|
1809
|
+
summaryText: "Turn-start summary",
|
|
1810
|
+
previousEstimatedInputTokens: 170_000,
|
|
1811
|
+
estimatedInputTokens: 165_000,
|
|
1812
|
+
maxInputTokens: 200_000,
|
|
1813
|
+
thresholdTokens: 160_000,
|
|
1814
|
+
compactedMessages: 10,
|
|
1815
|
+
summaryCalls: 1,
|
|
1816
|
+
summaryInputTokens: 500,
|
|
1817
|
+
summaryOutputTokens: 200,
|
|
1818
|
+
summaryModel: "mock-model",
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
if (forcedMaybeCompactCallCount === 2) {
|
|
1822
|
+
// Mid-loop call — the manager owns its own retry budget; signal
|
|
1823
|
+
// exhaustion to escalate to convergence.
|
|
1900
1824
|
return {
|
|
1901
1825
|
compacted: true,
|
|
1902
1826
|
messages: [
|
|
@@ -1941,7 +1865,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1941
1865
|
summaryModel: "mock-model",
|
|
1942
1866
|
};
|
|
1943
1867
|
},
|
|
1944
|
-
} as unknown as
|
|
1868
|
+
} as unknown as Conversation["contextWindowManager"],
|
|
1945
1869
|
});
|
|
1946
1870
|
|
|
1947
1871
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|