@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
|
@@ -17,7 +17,9 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* IPC params for `addMessage()`. `role` is constrained to the
|
|
20
|
-
*
|
|
20
|
+
* renderable conversation roles (`user`/`assistant`): the messages store
|
|
21
|
+
* is UI-facing (`ConversationMessage`), so agent-context `system` rows are
|
|
22
|
+
* not accepted here. `metadata` is a free-form record (validated
|
|
21
23
|
* downstream by `messageMetadataSchema` with a warn-and-store fallback).
|
|
22
24
|
* `skipIndexing` and `clientMessageId` mirror `AddMessageOptions`.
|
|
23
25
|
*
|
|
@@ -28,7 +30,7 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
|
|
|
28
30
|
*/
|
|
29
31
|
const MemoryAddMessageParams = z.object({
|
|
30
32
|
conversationId: z.string().min(1),
|
|
31
|
-
role: z.enum(["user", "assistant"
|
|
33
|
+
role: z.enum(["user", "assistant"]),
|
|
32
34
|
content: z.string(),
|
|
33
35
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
34
36
|
skipIndexing: z.boolean().optional(),
|
|
@@ -151,6 +151,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
151
151
|
|
|
152
152
|
test("enqueues consolidate when v2 is on and no checkpoint exists", () => {
|
|
153
153
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
154
|
+
writeBuffer(15);
|
|
154
155
|
|
|
155
156
|
maybeEnqueueGraphMaintenanceJobs(config, Date.now());
|
|
156
157
|
|
|
@@ -177,6 +178,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
177
178
|
|
|
178
179
|
test("enqueues consolidate again once the interval elapses", () => {
|
|
179
180
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
181
|
+
writeBuffer(15);
|
|
180
182
|
|
|
181
183
|
const now = Date.now();
|
|
182
184
|
// Stamp checkpoint to >1h ago.
|
|
@@ -193,6 +195,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
193
195
|
|
|
194
196
|
test("respects a custom consolidation_interval_hours value", () => {
|
|
195
197
|
const config = buildConfig({ v2Enabled: true, intervalHours: 6 });
|
|
198
|
+
writeBuffer(15);
|
|
196
199
|
|
|
197
200
|
const now = Date.now();
|
|
198
201
|
// 4h elapsed — under the configured 6h interval.
|
|
@@ -216,6 +219,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
216
219
|
|
|
217
220
|
test("v1 maintenance entries are suppressed when v2 is active", () => {
|
|
218
221
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
222
|
+
writeBuffer(15);
|
|
219
223
|
|
|
220
224
|
// No checkpoints set — every entry would be due if it were scheduled.
|
|
221
225
|
deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
|
|
@@ -426,3 +430,86 @@ describe("maybeEnqueueGraphMaintenanceJobs — buffer-size trigger", () => {
|
|
|
426
430
|
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
427
431
|
});
|
|
428
432
|
});
|
|
433
|
+
|
|
434
|
+
describe("maybeEnqueueGraphMaintenanceJobs — min buffer lines noop", () => {
|
|
435
|
+
test("skips scheduled consolidation when buffer is under 10 lines", () => {
|
|
436
|
+
// GIVEN v2 consolidation is enabled and the interval has elapsed
|
|
437
|
+
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
438
|
+
const now = Date.now();
|
|
439
|
+
setMemoryCheckpoint(
|
|
440
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
441
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
442
|
+
);
|
|
443
|
+
// AND the buffer has fewer than 10 lines
|
|
444
|
+
writeBuffer(5);
|
|
445
|
+
|
|
446
|
+
// WHEN the schedule runs
|
|
447
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
448
|
+
|
|
449
|
+
// THEN no consolidation job is enqueued
|
|
450
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
451
|
+
// AND the checkpoint advances so the skip doesn't re-fire next tick
|
|
452
|
+
expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
test("allows scheduled consolidation when buffer has exactly 10 lines", () => {
|
|
456
|
+
// GIVEN v2 consolidation is enabled and the interval has elapsed
|
|
457
|
+
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
458
|
+
const now = Date.now();
|
|
459
|
+
setMemoryCheckpoint(
|
|
460
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
461
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
462
|
+
);
|
|
463
|
+
// AND the buffer has exactly 10 lines
|
|
464
|
+
writeBuffer(10);
|
|
465
|
+
|
|
466
|
+
// WHEN the schedule runs
|
|
467
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
468
|
+
|
|
469
|
+
// THEN consolidation is enqueued
|
|
470
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test("skips scheduled consolidation when buffer file is missing", () => {
|
|
474
|
+
// GIVEN v2 consolidation is enabled and the interval has elapsed
|
|
475
|
+
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
476
|
+
const now = Date.now();
|
|
477
|
+
setMemoryCheckpoint(
|
|
478
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
479
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
480
|
+
);
|
|
481
|
+
// AND no buffer file exists (0 lines)
|
|
482
|
+
removeBuffer();
|
|
483
|
+
|
|
484
|
+
// WHEN the schedule runs
|
|
485
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
486
|
+
|
|
487
|
+
// THEN no consolidation job is enqueued
|
|
488
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
489
|
+
// AND the checkpoint advances
|
|
490
|
+
expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
test("size-based trigger is independent of the min-lines noop", () => {
|
|
494
|
+
// GIVEN the time-based interval has elapsed but the buffer has fewer
|
|
495
|
+
// than 10 lines, while exceeding the configured maxBufferLines threshold
|
|
496
|
+
const config = buildConfig({
|
|
497
|
+
v2Enabled: true,
|
|
498
|
+
intervalHours: 1,
|
|
499
|
+
maxBufferLines: 5,
|
|
500
|
+
});
|
|
501
|
+
const now = Date.now();
|
|
502
|
+
setMemoryCheckpoint(
|
|
503
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
504
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
505
|
+
);
|
|
506
|
+
writeBuffer(8);
|
|
507
|
+
|
|
508
|
+
// WHEN the schedule runs
|
|
509
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
510
|
+
|
|
511
|
+
// THEN consolidation is enqueued via the size trigger despite the
|
|
512
|
+
// time-based schedule being nooped (8 < 10 min lines, but 8 >= 5 max)
|
|
513
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
@@ -277,6 +277,7 @@ export interface MessageRow {
|
|
|
277
277
|
content: string;
|
|
278
278
|
createdAt: number;
|
|
279
279
|
metadata: string | null;
|
|
280
|
+
clientMessageId: string | null;
|
|
280
281
|
}
|
|
281
282
|
|
|
282
283
|
const parseMessage = createRowMapper<typeof messages.$inferSelect, MessageRow>({
|
|
@@ -286,6 +287,7 @@ const parseMessage = createRowMapper<typeof messages.$inferSelect, MessageRow>({
|
|
|
286
287
|
content: "content",
|
|
287
288
|
createdAt: "createdAt",
|
|
288
289
|
metadata: "metadata",
|
|
290
|
+
clientMessageId: "clientMessageId",
|
|
289
291
|
});
|
|
290
292
|
|
|
291
293
|
export type ConversationCreateType = "standard" | "background" | "scheduled";
|
|
@@ -1903,21 +1905,30 @@ export function listActiveInferenceProfileSessions(
|
|
|
1903
1905
|
}
|
|
1904
1906
|
|
|
1905
1907
|
/**
|
|
1906
|
-
*
|
|
1907
|
-
*
|
|
1908
|
-
*
|
|
1909
|
-
*
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1908
|
+
* The conversation fields needed to resolve a per-turn inference-profile
|
|
1909
|
+
* override. Satisfied by both the DB {@link ConversationRow} and the live
|
|
1910
|
+
* in-memory `Conversation`, so callers can derive the override from whichever
|
|
1911
|
+
* representation they already hold without a redundant row fetch.
|
|
1912
|
+
*/
|
|
1913
|
+
export interface OverrideProfileFields {
|
|
1914
|
+
conversationType?: string | null;
|
|
1915
|
+
inferenceProfile?: string | null;
|
|
1916
|
+
inferenceProfileExpiresAt?: number | null;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
/**
|
|
1920
|
+
* Resolve the per-turn inference-profile override from a conversation's
|
|
1921
|
+
* fields. Returns the `inferenceProfile` for interactive conversations,
|
|
1922
|
+
* `undefined` for automation threads (subagent fan-out, scheduled tasks,
|
|
1923
|
+
* update bulletins) so they run on the workspace defaults rather than
|
|
1924
|
+
* inheriting an interactive override.
|
|
1914
1925
|
*/
|
|
1915
|
-
export function
|
|
1916
|
-
|
|
1926
|
+
export function resolveOverrideProfile(
|
|
1927
|
+
fields: OverrideProfileFields | null,
|
|
1917
1928
|
): string | undefined {
|
|
1918
1929
|
if (
|
|
1919
|
-
|
|
1920
|
-
|
|
1930
|
+
fields?.conversationType === "background" ||
|
|
1931
|
+
fields?.conversationType === "scheduled"
|
|
1921
1932
|
) {
|
|
1922
1933
|
return undefined;
|
|
1923
1934
|
}
|
|
@@ -1931,24 +1942,23 @@ export function getConversationOverrideProfileFromRow(
|
|
|
1931
1942
|
// Without this, a session at the exact-expiry millisecond would be served
|
|
1932
1943
|
// for one extra turn here while being cleared by the reaper.
|
|
1933
1944
|
if (
|
|
1934
|
-
|
|
1935
|
-
|
|
1945
|
+
fields?.inferenceProfileExpiresAt != null &&
|
|
1946
|
+
fields.inferenceProfileExpiresAt <= Date.now()
|
|
1936
1947
|
) {
|
|
1937
1948
|
return undefined;
|
|
1938
1949
|
}
|
|
1939
|
-
return
|
|
1950
|
+
return fields?.inferenceProfile ?? undefined;
|
|
1940
1951
|
}
|
|
1941
1952
|
|
|
1942
1953
|
/**
|
|
1943
1954
|
* Resolve the per-turn inference-profile override by conversation id.
|
|
1944
|
-
* Convenience wrapper
|
|
1945
|
-
*
|
|
1946
|
-
* already have the row in hand.
|
|
1955
|
+
* Convenience wrapper for standalone callers (e.g. subagent spawn,
|
|
1956
|
+
* opportunity-wake) that don't already have the conversation in hand.
|
|
1947
1957
|
*/
|
|
1948
1958
|
export function getConversationOverrideProfile(
|
|
1949
1959
|
conversationId: string,
|
|
1950
1960
|
): string | undefined {
|
|
1951
|
-
return
|
|
1961
|
+
return resolveOverrideProfile(getConversation(conversationId));
|
|
1952
1962
|
}
|
|
1953
1963
|
|
|
1954
1964
|
export function setLastNotifiedInferenceProfile(
|
|
@@ -15,6 +15,7 @@ import { memoryCheckpoints } from "./schema.js";
|
|
|
15
15
|
export const CK_ITEM_COUNT = "conversation_starters:item_count_at_last_gen";
|
|
16
16
|
export const CK_BATCH = "conversation_starters:generation_batch";
|
|
17
17
|
export const CK_LAST_GEN_AT = "conversation_starters:last_gen_at";
|
|
18
|
+
export const CK_LAST_ATTEMPT_AT = "conversation_starters:last_attempt_at";
|
|
18
19
|
|
|
19
20
|
export function checkpointKey(base: string, scopeId: string): string {
|
|
20
21
|
return `${base}:${scopeId}`;
|
package/src/memory/db-init.ts
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
migrate230AcpSessionHistory,
|
|
42
42
|
migrate231RepairMemoryGraphEventDates,
|
|
43
43
|
migrateA2ATasks,
|
|
44
|
+
migrateAcpSessionHistoryCwd,
|
|
44
45
|
migrateActivationState,
|
|
45
46
|
migrateActivationStateFkCascade,
|
|
46
47
|
migrateAddConversationInferenceProfile,
|
|
@@ -478,6 +479,7 @@ export function initializeDb(): void {
|
|
|
478
479
|
migrateScheduleSourceConversation,
|
|
479
480
|
migrateMessagesRoleCreatedAtIndex,
|
|
480
481
|
createAuthFallbackEventsTable,
|
|
482
|
+
migrateAcpSessionHistoryCwd,
|
|
481
483
|
];
|
|
482
484
|
|
|
483
485
|
// Run each migration step, catching and logging individual failures so one
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
checkpointKey,
|
|
23
23
|
CK_BATCH,
|
|
24
24
|
CK_ITEM_COUNT,
|
|
25
|
+
CK_LAST_ATTEMPT_AT,
|
|
25
26
|
CK_LAST_GEN_AT,
|
|
26
27
|
countActiveMemoryNodes,
|
|
27
28
|
getCheckpointValue,
|
|
@@ -336,6 +337,7 @@ Bad → Good (incomplete phrase → complete):
|
|
|
336
337
|
config: {
|
|
337
338
|
callSite: "conversationStarters" as const,
|
|
338
339
|
max_tokens: 2048,
|
|
340
|
+
temperature: 0.7,
|
|
339
341
|
tool_choice: {
|
|
340
342
|
type: "tool" as const,
|
|
341
343
|
name: "store_conversation_starters",
|
|
@@ -395,18 +397,27 @@ export async function generateConversationStartersJob(
|
|
|
395
397
|
const db = getDb();
|
|
396
398
|
const now = Date.now();
|
|
397
399
|
|
|
400
|
+
// Record attempt time so the route handler's cooldown prevents rapid retries
|
|
401
|
+
// even when generation produces 0 valid starters.
|
|
402
|
+
upsertCheckpoint(
|
|
403
|
+
checkpointKey(CK_LAST_ATTEMPT_AT, scopeId),
|
|
404
|
+
String(now),
|
|
405
|
+
now,
|
|
406
|
+
);
|
|
407
|
+
|
|
398
408
|
const starters = await generateStarters(scopeId);
|
|
399
409
|
if (starters.length === 0) {
|
|
400
410
|
log.info({ scopeId }, "No conversation starters generated");
|
|
401
411
|
|
|
402
|
-
// Sync
|
|
412
|
+
// Sync the item count checkpoint so `checkpointAhead` clears, but do NOT
|
|
413
|
+
// update `CK_LAST_GEN_AT` — the stale TTL should trigger a retry on the
|
|
414
|
+
// next GET rather than blocking retries for the full TTL window.
|
|
403
415
|
const totalActive = countActiveMemoryNodes(scopeId);
|
|
404
416
|
upsertCheckpoint(
|
|
405
417
|
checkpointKey(CK_ITEM_COUNT, scopeId),
|
|
406
418
|
String(totalActive),
|
|
407
419
|
now,
|
|
408
420
|
);
|
|
409
|
-
upsertCheckpoint(checkpointKey(CK_LAST_GEN_AT, scopeId), String(now), now);
|
|
410
421
|
return;
|
|
411
422
|
}
|
|
412
423
|
|
|
@@ -143,9 +143,8 @@ afterAll(() => {
|
|
|
143
143
|
// `getWorkspaceDir()` resolves to the tmpdir.
|
|
144
144
|
const { DEFAULT_CONFIG } = await import("../../../config/defaults.js");
|
|
145
145
|
const { getDb } = await import("../../db-connection.js");
|
|
146
|
-
const { resetDbForTesting } =
|
|
147
|
-
"../../../__tests__/db-test-helpers.js"
|
|
148
|
-
);
|
|
146
|
+
const { resetDbForTesting } =
|
|
147
|
+
await import("../../../__tests__/db-test-helpers.js");
|
|
149
148
|
const { initializeDb } = await import("../../db-init.js");
|
|
150
149
|
const { memoryEmbeddings, memoryJobs } = await import("../../schema.js");
|
|
151
150
|
const { claimMemoryJobs } = await import("../../jobs-store.js");
|
|
@@ -187,12 +186,14 @@ function makeJob(payload: Record<string, unknown>): MemoryJob {
|
|
|
187
186
|
|
|
188
187
|
beforeEach(() => {
|
|
189
188
|
resetDbForTesting();
|
|
189
|
+
// The first run pays the full cold-start migration chain; bump the hook
|
|
190
|
+
// timeout above bun's 5s default so the leading test doesn't flake in CI.
|
|
190
191
|
initializeDb();
|
|
191
192
|
embedWithBackendCalls.length = 0;
|
|
192
193
|
upsertCalls.length = 0;
|
|
193
194
|
deleteCalls.length = 0;
|
|
194
195
|
_resetQdrantBreaker();
|
|
195
|
-
});
|
|
196
|
+
}, 30_000);
|
|
196
197
|
|
|
197
198
|
afterEach(() => {
|
|
198
199
|
// Clean up any pages written between tests so each scenario starts fresh.
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
diskPressureBackgroundSkipLogFields,
|
|
9
9
|
shouldLogDiskPressureBackgroundSkip,
|
|
10
10
|
} from "../daemon/disk-pressure-background-gate.js";
|
|
11
|
+
import { maintainJob as memoryV3MaintainJob } from "../plugins/defaults/memory-v3-shadow/maintain-job.js";
|
|
11
12
|
import { getLogger } from "../util/logger.js";
|
|
12
13
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
13
14
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
|
|
@@ -87,7 +88,6 @@ import {
|
|
|
87
88
|
memoryV2ConsolidateJob,
|
|
88
89
|
} from "./v2/consolidation-job.js";
|
|
89
90
|
import { memoryV2SweepJob } from "./v2/sweep-job.js";
|
|
90
|
-
import { maintainJob as memoryV3MaintainJob } from "./v3/maintain-job.js";
|
|
91
91
|
|
|
92
92
|
const log = getLogger("memory-jobs-worker");
|
|
93
93
|
|
|
@@ -95,6 +95,16 @@ const AUTOMATIC_CONSOLIDATION_JOB_PAYLOAD = {
|
|
|
95
95
|
trigger: MEMORY_V2_CONSOLIDATION_JOB_TRIGGERS.automatic,
|
|
96
96
|
} as const;
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Minimum buffer entries required for a scheduled consolidation run. The
|
|
100
|
+
* time-based schedule noops when `memory/buffer.md` has fewer non-empty lines
|
|
101
|
+
* than this threshold — the LLM cost of a full consolidation pass outweighs
|
|
102
|
+
* the benefit when the buffer is nearly empty. Mirrors the heartbeat
|
|
103
|
+
* max-consecutive-runs skip pattern. Manual "Run now" and the size-based
|
|
104
|
+
* trigger are not affected.
|
|
105
|
+
*/
|
|
106
|
+
export const MIN_BUFFER_LINES_FOR_CONSOLIDATION = 10;
|
|
107
|
+
|
|
98
108
|
/**
|
|
99
109
|
* V1 job types that read or write the v1 Qdrant collection via
|
|
100
110
|
* `getQdrantClient()`. When `memory.v2.enabled` is true, the v1 client is
|
|
@@ -815,6 +825,20 @@ export function maybeEnqueueGraphMaintenanceJobs(
|
|
|
815
825
|
for (const { key, intervalMs, jobType } of schedule) {
|
|
816
826
|
const lastRun = parseInt(getMemoryCheckpoint(key) ?? "0", 10);
|
|
817
827
|
if (nowMs - lastRun >= intervalMs) {
|
|
828
|
+
// Noop scheduled consolidation when the buffer has too few entries to
|
|
829
|
+
// justify an LLM run — mirrors the heartbeat max-consecutive-runs skip.
|
|
830
|
+
// The checkpoint advances so the next check fires after the regular
|
|
831
|
+
// interval. Manual "Run now" is unaffected (routes layer, not schedule).
|
|
832
|
+
if (jobType === consolidateEntry.jobType) {
|
|
833
|
+
const bufferPath = join(getWorkspaceDir(), "memory", "buffer.md");
|
|
834
|
+
if (countBufferLines(bufferPath) < MIN_BUFFER_LINES_FOR_CONSOLIDATION) {
|
|
835
|
+
log.debug(
|
|
836
|
+
"Scheduled consolidation skipped: buffer under minimum line threshold",
|
|
837
|
+
);
|
|
838
|
+
setMemoryCheckpoint(key, String(nowMs));
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
818
842
|
const payload =
|
|
819
843
|
jobType === consolidateEntry.jobType
|
|
820
844
|
? AUTOMATIC_CONSOLIDATION_JOB_PAYLOAD
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
|
+
|
|
5
|
+
const CHECKPOINT_KEY = "migration_acp_session_history_cwd_v1";
|
|
6
|
+
|
|
7
|
+
const TABLE = "acp_session_history";
|
|
8
|
+
const COLUMN = "cwd";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Add a nullable `cwd TEXT` column to `acp_session_history`.
|
|
12
|
+
*
|
|
13
|
+
* Records the working directory the ACP agent process was spawned with so
|
|
14
|
+
* that a persisted session can later be resumed (`session/load` /
|
|
15
|
+
* `session/resume` both require the original cwd to reconstruct params).
|
|
16
|
+
*
|
|
17
|
+
* `NULL` for rows persisted before this migration ran — legacy sessions
|
|
18
|
+
* simply are not resumable.
|
|
19
|
+
*
|
|
20
|
+
* Idempotent — the PRAGMA guard makes re-running a no-op once the column
|
|
21
|
+
* exists.
|
|
22
|
+
*/
|
|
23
|
+
export function migrateAcpSessionHistoryCwd(database: DrizzleDb): void {
|
|
24
|
+
withCrashRecovery(database, CHECKPOINT_KEY, () => {
|
|
25
|
+
const raw = getSqliteFrom(database);
|
|
26
|
+
|
|
27
|
+
const columns = raw.query(`PRAGMA table_info(${TABLE})`).all() as Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
}>;
|
|
30
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
31
|
+
|
|
32
|
+
if (!columnNames.has(COLUMN)) {
|
|
33
|
+
raw.exec(`ALTER TABLE ${TABLE} ADD COLUMN ${COLUMN} TEXT`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -259,6 +259,7 @@ export { migrateScheduleScriptTimeout } from "./269-schedule-script-timeout.js";
|
|
|
259
259
|
export { migrateMessagesRoleCreatedAtIndex } from "./270-messages-role-created-at-index.js";
|
|
260
260
|
export { migrateScheduleSourceConversation } from "./270-schedule-source-conversation.js";
|
|
261
261
|
export { createAuthFallbackEventsTable } from "./271-create-auth-fallback-events.js";
|
|
262
|
+
export { migrateAcpSessionHistoryCwd } from "./272-acp-session-history-cwd.js";
|
|
262
263
|
export {
|
|
263
264
|
MIGRATION_REGISTRY,
|
|
264
265
|
type MigrationRegistryEntry,
|
package/src/memory/schema/acp.ts
CHANGED
|
@@ -20,6 +20,10 @@ export const acpSessionHistory = sqliteTable(
|
|
|
20
20
|
stopReason: text("stop_reason"),
|
|
21
21
|
error: text("error"),
|
|
22
22
|
eventLogJson: text("event_log_json").notNull().default("[]"),
|
|
23
|
+
// Working directory the agent process was spawned with. Required to
|
|
24
|
+
// resume a persisted session; null for rows written before migration
|
|
25
|
+
// 272 (those sessions are not resumable).
|
|
26
|
+
cwd: text("cwd"),
|
|
23
27
|
},
|
|
24
28
|
(table) => [
|
|
25
29
|
index("idx_acp_session_history_started_at").on(table.startedAt),
|
|
@@ -121,7 +121,7 @@ mock.module("../../../config/assistant-feature-flags.js", () => ({
|
|
|
121
121
|
|
|
122
122
|
let renderedHealth = "";
|
|
123
123
|
let computeThrows = false;
|
|
124
|
-
mock.module("
|
|
124
|
+
mock.module("../../../plugins/defaults/memory-v3-shadow/health.js", () => ({
|
|
125
125
|
computeV3Health: () => {
|
|
126
126
|
if (computeThrows) throw new Error("simulated health compute failure");
|
|
127
127
|
return {};
|
|
@@ -129,12 +129,12 @@ mock.module("../../v3/health.js", () => ({
|
|
|
129
129
|
renderV3Health: () => renderedHealth,
|
|
130
130
|
}));
|
|
131
131
|
|
|
132
|
-
mock.module("
|
|
132
|
+
mock.module("../../../plugins/defaults/memory-v3-shadow/tree.js", () => ({
|
|
133
133
|
loadLeafTree: async () => ({ leaves: new Map(), byPage: new Map() }),
|
|
134
134
|
resolveDataDir: () => "/tmp/v3-data-stub",
|
|
135
135
|
}));
|
|
136
136
|
|
|
137
|
-
mock.module("
|
|
137
|
+
mock.module("../../../plugins/defaults/memory-v3-shadow/core.js", () => ({
|
|
138
138
|
loadCore: async () => new Set<string>(),
|
|
139
139
|
}));
|
|
140
140
|
|
|
@@ -63,6 +63,19 @@ import { dirname, join } from "node:path";
|
|
|
63
63
|
|
|
64
64
|
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
65
65
|
import type { AssistantConfig } from "../../config/types.js";
|
|
66
|
+
import { loadCore } from "../../plugins/defaults/memory-v3-shadow/core.js";
|
|
67
|
+
import {
|
|
68
|
+
computeV3Health,
|
|
69
|
+
renderV3Health,
|
|
70
|
+
} from "../../plugins/defaults/memory-v3-shadow/health.js";
|
|
71
|
+
import {
|
|
72
|
+
loadLeafTree,
|
|
73
|
+
resolveDataDir,
|
|
74
|
+
} from "../../plugins/defaults/memory-v3-shadow/tree.js";
|
|
75
|
+
import type {
|
|
76
|
+
LeafPath,
|
|
77
|
+
Slug,
|
|
78
|
+
} from "../../plugins/defaults/memory-v3-shadow/types.js";
|
|
66
79
|
import { runBackgroundJob } from "../../runtime/background-job-runner.js";
|
|
67
80
|
import { getLogger } from "../../util/logger.js";
|
|
68
81
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
@@ -74,10 +87,6 @@ import {
|
|
|
74
87
|
type MemoryJobType,
|
|
75
88
|
} from "../jobs-store.js";
|
|
76
89
|
import { getPageIndex } from "../v2/page-index.js";
|
|
77
|
-
import { loadCore } from "../v3/core.js";
|
|
78
|
-
import { computeV3Health, renderV3Health } from "../v3/health.js";
|
|
79
|
-
import { loadLeafTree, resolveDataDir } from "../v3/tree.js";
|
|
80
|
-
import type { LeafPath, Slug } from "../v3/types.js";
|
|
81
90
|
import { MEMORY_V2_CONSOLIDATION_SOURCE } from "./constants.js";
|
|
82
91
|
import { resolveConsolidationPrompt } from "./prompts/consolidation.js";
|
|
83
92
|
|
|
@@ -20,10 +20,14 @@ export const HOOKS = {
|
|
|
20
20
|
SHUTDOWN: "shutdown",
|
|
21
21
|
/** Fires once per user turn, immediately before the agent loop receives `runMessages`. */
|
|
22
22
|
USER_PROMPT_SUBMIT: "user-prompt-submit",
|
|
23
|
+
/** Fires immediately before each provider call. A hook may edit the outbound request (e.g. the system prompt) and opt the turn into deferred output streaming. */
|
|
24
|
+
PRE_MODEL_CALL: "pre-model-call",
|
|
23
25
|
/** Fires once per tool result, after the tool returns and before the result is sent to the provider. */
|
|
24
26
|
POST_TOOL_USE: "post-tool-use",
|
|
25
27
|
/** Fires when the model yields a response with no tool calls — the run's stop boundary. Decides whether to stop or continue with a follow-up turn. */
|
|
26
28
|
STOP: "stop",
|
|
29
|
+
/** Fires for each finalized assistant message, before it is persisted/streamed-final. A hook may transform the message content. */
|
|
30
|
+
ASSISTANT_MESSAGE: "assistant-message",
|
|
27
31
|
} as const;
|
|
28
32
|
|
|
29
33
|
/** Union of every hook name declared in {@link HOOKS}. */
|
package/src/plugin-api/index.ts
CHANGED
|
@@ -24,31 +24,32 @@
|
|
|
24
24
|
* - {@link PluginShutdownContext} — passed to `shutdown` hook at teardown
|
|
25
25
|
* - {@link UserPromptSubmitContext} — passed to `user-prompt-submit` hook,
|
|
26
26
|
* fired immediately before the agent loop receives a user's prompt
|
|
27
|
+
* - {@link PreModelCallContext} — passed to `pre-model-call` hook, fired
|
|
28
|
+
* before each provider call to edit the request / defer output streaming
|
|
27
29
|
* - {@link PostToolUseContext} — passed to `post-tool-use` hook, fired once
|
|
28
30
|
* per tool result before it joins the provider-bound history
|
|
29
31
|
* - {@link StopContext} — passed to `stop` hook, fired when the model yields
|
|
30
32
|
* a response with no tool calls
|
|
33
|
+
* - {@link AssistantMessageContext} — passed to `assistant-message` hook,
|
|
34
|
+
* fired for each finalized assistant message to transform its content
|
|
31
35
|
* - {@link PluginHookFn} — signature every lifecycle hook implements
|
|
32
36
|
* - {@link PluginLogger} — pino-compatible logger shape on the contexts
|
|
33
37
|
* - {@link ToolDefinition} — author-facing tool spec (default-export shape
|
|
34
38
|
* for both plugin tool files and workspace tool files)
|
|
35
39
|
* - {@link ToolContext} — passed to a plugin tool's `execute` method
|
|
36
40
|
* - {@link ToolExecutionResult} — return shape of a plugin tool's `execute`
|
|
37
|
-
*
|
|
38
|
-
* Pipeline-argument types (`MemoryArgs`, `CompactionArgs`, etc.) currently
|
|
39
|
-
* live in `assistant/src/plugins/types.ts` and have not yet migrated into
|
|
40
|
-
* this package. A follow-up PR will move them into this surface as the
|
|
41
|
-
* per-pipeline schemas stabilize.
|
|
42
41
|
*/
|
|
43
42
|
|
|
44
43
|
export type { HookName } from "./constants.js";
|
|
45
44
|
export { HOOKS } from "./constants.js";
|
|
46
45
|
export type {
|
|
46
|
+
AssistantMessageContext,
|
|
47
47
|
PluginHookFn,
|
|
48
48
|
PluginInitContext,
|
|
49
49
|
PluginLogger,
|
|
50
50
|
PluginShutdownContext,
|
|
51
51
|
PostToolUseContext,
|
|
52
|
+
PreModelCallContext,
|
|
52
53
|
StopContext,
|
|
53
54
|
StopDecision,
|
|
54
55
|
ToolContext,
|
package/src/plugin-api/types.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* removing is breaking and gated on a major bump.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
7
8
|
import type {
|
|
8
9
|
ContentBlock,
|
|
9
10
|
Message,
|
|
@@ -51,8 +52,10 @@ export interface PluginLogger {
|
|
|
51
52
|
* - `init` — {@link PluginInitContext}
|
|
52
53
|
* - `shutdown` — {@link PluginShutdownContext}
|
|
53
54
|
* - `user-prompt-submit` — {@link UserPromptSubmitContext}
|
|
55
|
+
* - `pre-model-call` — {@link PreModelCallContext}
|
|
54
56
|
* - `post-tool-use` — {@link PostToolUseContext}
|
|
55
57
|
* - `stop` — {@link StopContext}
|
|
58
|
+
* - `assistant-message` — {@link AssistantMessageContext}
|
|
56
59
|
*/
|
|
57
60
|
export type PluginHookFn<TCtx = unknown> = (ctx: TCtx) => Promise<TCtx | void>;
|
|
58
61
|
|
|
@@ -288,3 +291,75 @@ export interface StopContext {
|
|
|
288
291
|
*/
|
|
289
292
|
readonly logger: PluginLogger;
|
|
290
293
|
}
|
|
294
|
+
|
|
295
|
+
// ─── Pre-model-call hook context ─────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Context passed to the `pre-model-call` hook. Fires immediately before each
|
|
299
|
+
* provider call — once per model call within a turn, including tool-result
|
|
300
|
+
* follow-up calls. Because it runs for every provider call (background, subagent,
|
|
301
|
+
* and compaction work can share a conversation), hooks MUST self-gate on
|
|
302
|
+
* {@link callSite} / {@link conversationId} before acting.
|
|
303
|
+
*
|
|
304
|
+
* A hook may edit the outbound request by replacing {@link systemPrompt}, and may
|
|
305
|
+
* opt this turn into deferred output streaming via {@link deferAssistantOutput}.
|
|
306
|
+
* Mutate the context in place or return a new one; throwing is contained by the
|
|
307
|
+
* loop (the call proceeds with the original request).
|
|
308
|
+
*/
|
|
309
|
+
export interface PreModelCallContext {
|
|
310
|
+
/** Conversation ID the call belongs to. */
|
|
311
|
+
readonly conversationId: string;
|
|
312
|
+
/**
|
|
313
|
+
* The call site this provider call serves — `"mainAgent"` for the user-facing
|
|
314
|
+
* reply, or a background/utility site. Omitted by call sites that don't tag one.
|
|
315
|
+
*/
|
|
316
|
+
readonly callSite?: LLMCallSite;
|
|
317
|
+
/**
|
|
318
|
+
* The system prompt about to be sent. A hook may replace it (e.g. strip or
|
|
319
|
+
* append a section); the loop sends the resulting value.
|
|
320
|
+
*/
|
|
321
|
+
systemPrompt: string | undefined;
|
|
322
|
+
/**
|
|
323
|
+
* Seeded `false`. When a hook sets it `true`, the loop suppresses this turn's
|
|
324
|
+
* live assistant `text_delta` stream; an `assistant-message` hook is then
|
|
325
|
+
* expected to produce the text the client sees (emitted once, after the reply
|
|
326
|
+
* is finalized). Lets a plugin replace streamed output wholesale — e.g.
|
|
327
|
+
* redaction that needs the full message — instead of leaking the raw stream.
|
|
328
|
+
*/
|
|
329
|
+
deferAssistantOutput: boolean;
|
|
330
|
+
/** Logger scoped to the current turn (tag structured fields with `{ plugin }`). */
|
|
331
|
+
readonly logger: PluginLogger;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ─── Assistant-message hook context ──────────────────────────────────────────
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Context passed to the `assistant-message` hook. Fires for each finalized
|
|
338
|
+
* assistant message — once per model call, at the message-complete boundary —
|
|
339
|
+
* before the message is persisted and (if deferred) streamed-final. Unlike
|
|
340
|
+
* {@link StopContext}'s read-only `responseContent` (which exists for the stop
|
|
341
|
+
* decision), {@link content} is mutable: the loop adopts the hook's result as the
|
|
342
|
+
* persisted and streamed message.
|
|
343
|
+
*
|
|
344
|
+
* Fires on tool-bearing turns too (a reply can carry both text and `tool_use`),
|
|
345
|
+
* so a hook should transform only the blocks it owns and leave others — notably
|
|
346
|
+
* `tool_use` — intact. Runs for every finalized message regardless of call site;
|
|
347
|
+
* hooks MUST self-gate on {@link callSite} / {@link conversationId}. Mutate in
|
|
348
|
+
* place or return a new context; throwing is contained by the loop (the original
|
|
349
|
+
* content is kept).
|
|
350
|
+
*/
|
|
351
|
+
export interface AssistantMessageContext {
|
|
352
|
+
/** Conversation ID the message belongs to. */
|
|
353
|
+
readonly conversationId: string;
|
|
354
|
+
/** The call site this message serves — `"mainAgent"` for the user-facing reply. */
|
|
355
|
+
readonly callSite?: LLMCallSite;
|
|
356
|
+
/**
|
|
357
|
+
* The finalized message content. Mutable — transform the text blocks and leave
|
|
358
|
+
* `tool_use` (and other non-text blocks) intact.
|
|
359
|
+
*/
|
|
360
|
+
content: ContentBlock[];
|
|
361
|
+
/** Provider-reported stop reason for the turn, when reported. */
|
|
362
|
+
readonly stopReason: string | null | undefined;
|
|
363
|
+
/** Logger scoped to the current turn (tag structured fields with `{ plugin }`). */
|
|
364
|
+
readonly logger: PluginLogger;
|
|
365
|
+
}
|