@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae
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/docs/activation-funnel-telemetry.md +310 -0
- 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 +263 -121
- package/package.json +2 -2
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- 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 +23 -8
- 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 +6 -5
- 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 +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- 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 +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- 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 +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -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 +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/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__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -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 +31 -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 +5 -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/__tests__/web-fetch.test.ts +45 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
- 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/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- 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 +403 -251
- package/src/api/responses/conversation-message.ts +22 -2
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -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 +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- 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 -3
- package/src/config/feature-flag-registry.json +10 -16
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -36
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -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__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -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/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +9 -2
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- 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 +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- 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/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -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/external-api.ts +12 -2
- 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/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +22 -0
- package/src/providers/types.ts +6 -0
- 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 +466 -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__/stt-routes.test.ts +112 -0
- 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 +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- 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/channel-verification-routes.ts +14 -5
- 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 +30 -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/inbound-stages/acl-enforcement.ts +33 -34
- 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 +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- 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/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -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/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- 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/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- 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/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/util/map-limit.ts +0 -27
- /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__/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}/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}/llm-retry.ts +0 -0
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory v3 — `memory_v3_maintain` job handler.
|
|
3
|
-
*
|
|
4
|
-
* A flag-gated, best-effort self-maintenance pass over the v3 topic tree and
|
|
5
|
-
* its page→leaf assignments. It runs three independent stages, in order:
|
|
6
|
-
*
|
|
7
|
-
* 1. **Classify-union** — `assignPages` over the delta: every page that is
|
|
8
|
-
* still unassigned (empty `leaves:`) plus every page edited since the last
|
|
9
|
-
* successful pass (the high-water mark below), UNIONing freshly classified
|
|
10
|
-
* leaves into each page (never dropping existing picks). Re-touching
|
|
11
|
-
* already-assigned-but-recently-edited pages lets the classifier enrich the
|
|
12
|
-
* assistant's own in-consolidation labels with any leaves it missed.
|
|
13
|
-
* 2. **Prune** — drop any frontmatter `leaves:` entry that points at a leaf
|
|
14
|
-
* path no longer present in the tree (dangling references left behind when
|
|
15
|
-
* a leaf is renamed or removed). Read + rewrite only the affected pages.
|
|
16
|
-
* 3. **Needle rebuild** — `invalidateLanes()` so the next turn rebuilds the
|
|
17
|
-
* leaf tree and BM25 needle from the freshly-updated assignments.
|
|
18
|
-
*
|
|
19
|
-
* Best-effort by construction: each stage is wrapped so a failure in one is
|
|
20
|
-
* logged and recorded in the outcome but does NOT abort the others. The job is
|
|
21
|
-
* a no-op (returns a disabled outcome) unless `memory-v3-shadow` OR
|
|
22
|
-
* `memory-v3-live` is enabled — the same flags that gate the v3 plugin.
|
|
23
|
-
*
|
|
24
|
-
* Dependency-injectable: `deps` lets tests substitute the tree loader,
|
|
25
|
-
* `assignPages`, and `invalidateLanes` without process-global module mocks.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
29
|
-
import type { AssistantConfig } from "../../config/types.js";
|
|
30
|
-
import { getLogger } from "../../util/logger.js";
|
|
31
|
-
import { getWorkspaceDir } from "../../util/platform.js";
|
|
32
|
-
import { getMemoryCheckpoint, setMemoryCheckpoint } from "../checkpoints.js";
|
|
33
|
-
import type { MemoryJob } from "../jobs-store.js";
|
|
34
|
-
import { getPageIndex } from "../v2/page-index.js";
|
|
35
|
-
import { listPages, readPage, writePage } from "../v2/page-store.js";
|
|
36
|
-
import { assignPages as realAssignPages } from "./assign.js";
|
|
37
|
-
import { invalidateLanes as realInvalidateLanes } from "./shadow-plugin.js";
|
|
38
|
-
import { loadLeafTree as realLoadLeafTree, resolveDataDir } from "./tree.js";
|
|
39
|
-
import type { LeafPath, LeafTree, Slug } from "./types.js";
|
|
40
|
-
|
|
41
|
-
const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
42
|
-
const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Durable checkpoint holding the epoch-ms high-water mark of the last successful
|
|
46
|
-
* classify-union pass. Pages whose mtime is past this mark are re-classified
|
|
47
|
-
* (enrichment); the mark is advanced only after a pass completes, captured after
|
|
48
|
-
* its writes so the pass's own frontmatter rewrites do not re-trigger it.
|
|
49
|
-
* Distinct from `memory_v3_maintain_last_run` (the enqueue-cadence checkpoint in
|
|
50
|
-
* `jobs-worker.ts`), which advances on every backstop enqueue rather than on an
|
|
51
|
-
* actual maintenance run.
|
|
52
|
-
*/
|
|
53
|
-
const MAINTAIN_ENRICH_HIGH_WATER_KEY =
|
|
54
|
-
"memory_v3_maintain:enriched_through_ms" as const;
|
|
55
|
-
|
|
56
|
-
const log = getLogger("memory-v3-maintain");
|
|
57
|
-
|
|
58
|
-
/** Injectable collaborators; defaults wire the real implementations. */
|
|
59
|
-
export interface MaintainJobDeps {
|
|
60
|
-
/** Load the v3 leaf tree (page→leaf membership resolved from frontmatter). */
|
|
61
|
-
loadTree: () => Promise<LeafTree>;
|
|
62
|
-
/** Classify-union pass over pages. */
|
|
63
|
-
assignPages: typeof realAssignPages;
|
|
64
|
-
/**
|
|
65
|
-
* The slugs to classify this pass: unassigned pages plus pages edited since
|
|
66
|
-
* the last successful pass. See {@link computeClassifyTargets}.
|
|
67
|
-
*/
|
|
68
|
-
selectClassifyTargets: () => Promise<Slug[]>;
|
|
69
|
-
/**
|
|
70
|
-
* Persist the enrichment high-water mark after a successful classify pass. The
|
|
71
|
-
* value is captured after the pass's writes (see the key docstring).
|
|
72
|
-
*/
|
|
73
|
-
commitClassifyHighWater: (highWaterMs: number) => void;
|
|
74
|
-
/** Drop the memoized v3 lanes so the next turn rebuilds tree + needle. */
|
|
75
|
-
invalidateLanes: () => void;
|
|
76
|
-
/** Workspace root; pages live under `<workspaceDir>/memory/concepts/`. */
|
|
77
|
-
workspaceDir: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Per-stage outcome surfaced in the structured log line. */
|
|
81
|
-
export interface MaintainOutcome {
|
|
82
|
-
/** True when both v3 flags were off and the job no-opped. */
|
|
83
|
-
disabled: boolean;
|
|
84
|
-
/** Pages newly assigned by the classify-union stage. */
|
|
85
|
-
assigned: number;
|
|
86
|
-
/** Pages whose dangling refs were pruned. */
|
|
87
|
-
pruned: number;
|
|
88
|
-
/** Dangling leaf references dropped across all pruned pages. */
|
|
89
|
-
prunedRefs: number;
|
|
90
|
-
/** Whether the needle/tree lanes were invalidated. */
|
|
91
|
-
invalidated: boolean;
|
|
92
|
-
/** Stages that threw (and were contained). */
|
|
93
|
-
failures: string[];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Load the v3 leaf tree the same way the live plugin does: resolve the data
|
|
98
|
-
* dir, build the page→leaf membership map from the page index frontmatter, and
|
|
99
|
-
* hand both to `loadLeafTree`.
|
|
100
|
-
*/
|
|
101
|
-
async function loadTreeFromWorkspace(workspaceDir: string): Promise<LeafTree> {
|
|
102
|
-
const pageIndex = await getPageIndex(workspaceDir);
|
|
103
|
-
const pageLeaves = new Map<Slug, LeafPath[]>();
|
|
104
|
-
for (const entry of pageIndex.entries) {
|
|
105
|
-
pageLeaves.set(entry.slug, entry.leaves);
|
|
106
|
-
}
|
|
107
|
-
return realLoadLeafTree(resolveDataDir(), pageLeaves);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Page-index projection the classify-target selector reads. */
|
|
111
|
-
export interface ClassifyCandidate {
|
|
112
|
-
slug: Slug;
|
|
113
|
-
/** File mtime in epoch ms; 0 for synthetic skill/CLI rows (excluded). */
|
|
114
|
-
modifiedAt: number;
|
|
115
|
-
/** Leaf assignments from frontmatter; `[]` when unassigned. */
|
|
116
|
-
leaves: LeafPath[];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Pick the pages the classify-union stage should run this pass:
|
|
121
|
-
*
|
|
122
|
-
* - **Unassigned** pages (empty `leaves:`) — always classified so a page is
|
|
123
|
-
* never left permanently unrouted (the pre-enrichment behavior).
|
|
124
|
-
* - **Recently edited** pages (mtime past `prevHighWaterMs`) — even when
|
|
125
|
-
* already assigned, so the classifier can enrich the assistant's own
|
|
126
|
-
* in-consolidation labels with leaves it missed. `assignPages` UNIONs, so a
|
|
127
|
-
* page's existing picks are never dropped — only added to.
|
|
128
|
-
*
|
|
129
|
-
* Synthetic skill/CLI rows (`modifiedAt === 0`) are excluded: they are
|
|
130
|
-
* capability entries, not topic-tree pages. On the first run (`prevHighWaterMs`
|
|
131
|
-
* null) the recency arm is disabled, so a fresh or freshly-backfilled install
|
|
132
|
-
* does a single unassigned-only pass instead of re-classifying the whole corpus.
|
|
133
|
-
*/
|
|
134
|
-
export function computeClassifyTargets(
|
|
135
|
-
pages: ReadonlyArray<ClassifyCandidate>,
|
|
136
|
-
prevHighWaterMs: number | null,
|
|
137
|
-
): Slug[] {
|
|
138
|
-
const targets: Slug[] = [];
|
|
139
|
-
for (const page of pages) {
|
|
140
|
-
if (page.modifiedAt <= 0) continue; // synthetic capability row
|
|
141
|
-
const unassigned = page.leaves.length === 0;
|
|
142
|
-
const recentlyEdited =
|
|
143
|
-
prevHighWaterMs !== null && page.modifiedAt > prevHighWaterMs;
|
|
144
|
-
if (unassigned || recentlyEdited) targets.push(page.slug);
|
|
145
|
-
}
|
|
146
|
-
return targets;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Read the persisted high-water mark, treating missing/garbage as first-run. */
|
|
150
|
-
function readClassifyHighWater(): number | null {
|
|
151
|
-
const raw = getMemoryCheckpoint(MAINTAIN_ENRICH_HIGH_WATER_KEY);
|
|
152
|
-
if (raw === null) return null;
|
|
153
|
-
const parsed = Number.parseInt(raw, 10);
|
|
154
|
-
return Number.isNaN(parsed) ? null : parsed;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** Default target selector: page index → {@link computeClassifyTargets}. */
|
|
158
|
-
async function selectClassifyTargetsFromWorkspace(
|
|
159
|
-
workspaceDir: string,
|
|
160
|
-
): Promise<Slug[]> {
|
|
161
|
-
const index = await getPageIndex(workspaceDir);
|
|
162
|
-
return computeClassifyTargets(index.entries, readClassifyHighWater());
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function commitClassifyHighWater(highWaterMs: number): void {
|
|
166
|
-
setMemoryCheckpoint(MAINTAIN_ENRICH_HIGH_WATER_KEY, String(highWaterMs));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function defaultDeps(): MaintainJobDeps {
|
|
170
|
-
const workspaceDir = getWorkspaceDir();
|
|
171
|
-
return {
|
|
172
|
-
loadTree: () => loadTreeFromWorkspace(workspaceDir),
|
|
173
|
-
assignPages: realAssignPages,
|
|
174
|
-
selectClassifyTargets: () =>
|
|
175
|
-
selectClassifyTargetsFromWorkspace(workspaceDir),
|
|
176
|
-
commitClassifyHighWater,
|
|
177
|
-
invalidateLanes: realInvalidateLanes,
|
|
178
|
-
workspaceDir,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Prune dangling `leaves:` references: for every page, drop frontmatter leaf
|
|
184
|
-
* paths that are not present in `tree`. Read + rewrite only pages that actually
|
|
185
|
-
* change. Returns the number of pages rewritten and total refs dropped.
|
|
186
|
-
*/
|
|
187
|
-
async function prunePages(
|
|
188
|
-
tree: LeafTree,
|
|
189
|
-
workspaceDir: string,
|
|
190
|
-
): Promise<{ pruned: number; prunedRefs: number }> {
|
|
191
|
-
const validLeaves = tree.leaves;
|
|
192
|
-
const slugs = await listPages(workspaceDir);
|
|
193
|
-
let pruned = 0;
|
|
194
|
-
let prunedRefs = 0;
|
|
195
|
-
for (const slug of slugs) {
|
|
196
|
-
const page = await readPage(workspaceDir, slug);
|
|
197
|
-
const leaves = page?.frontmatter.leaves;
|
|
198
|
-
if (!page || !leaves || leaves.length === 0) continue;
|
|
199
|
-
const kept = leaves.filter((leaf) => validLeaves.has(leaf));
|
|
200
|
-
if (kept.length === leaves.length) continue;
|
|
201
|
-
prunedRefs += leaves.length - kept.length;
|
|
202
|
-
pruned += 1;
|
|
203
|
-
await writePage(workspaceDir, {
|
|
204
|
-
...page,
|
|
205
|
-
frontmatter: { ...page.frontmatter, leaves: kept },
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
return { pruned, prunedRefs };
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Run the v3 self-maintenance pass. Each stage is independently contained: a
|
|
213
|
-
* thrown error is logged and recorded in `failures` without aborting the rest.
|
|
214
|
-
* No-ops when both v3 flags are off.
|
|
215
|
-
*/
|
|
216
|
-
export async function maintainJob(
|
|
217
|
-
_job: MemoryJob,
|
|
218
|
-
config: AssistantConfig,
|
|
219
|
-
deps: MaintainJobDeps = defaultDeps(),
|
|
220
|
-
): Promise<MaintainOutcome> {
|
|
221
|
-
const outcome: MaintainOutcome = {
|
|
222
|
-
disabled: false,
|
|
223
|
-
assigned: 0,
|
|
224
|
-
pruned: 0,
|
|
225
|
-
prunedRefs: 0,
|
|
226
|
-
invalidated: false,
|
|
227
|
-
failures: [],
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const enabled =
|
|
231
|
-
isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config) ||
|
|
232
|
-
isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config);
|
|
233
|
-
if (!enabled) {
|
|
234
|
-
outcome.disabled = true;
|
|
235
|
-
return outcome;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// The tree is shared input for both the assign and prune stages. If it can't
|
|
239
|
-
// load, those two stages are skipped (recorded as a failure), but we still
|
|
240
|
-
// invalidate the lanes so a transient load error doesn't wedge the next turn.
|
|
241
|
-
let tree: LeafTree | null = null;
|
|
242
|
-
try {
|
|
243
|
-
tree = await deps.loadTree();
|
|
244
|
-
} catch (err) {
|
|
245
|
-
outcome.failures.push("load_tree");
|
|
246
|
-
log.warn(
|
|
247
|
-
{ err: err instanceof Error ? err.message : String(err) },
|
|
248
|
-
"memory-v3 maintain: tree load failed (non-fatal)",
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (tree) {
|
|
253
|
-
// Stage 1: classify-union over the delta (unassigned ∪ recently-edited).
|
|
254
|
-
try {
|
|
255
|
-
const targets = await deps.selectClassifyTargets();
|
|
256
|
-
const results = await deps.assignPages({
|
|
257
|
-
tree,
|
|
258
|
-
workspaceDir: deps.workspaceDir,
|
|
259
|
-
slugs: targets,
|
|
260
|
-
});
|
|
261
|
-
outcome.assigned = results.filter(
|
|
262
|
-
(r) => !r.failed && r.after.length > r.before.length,
|
|
263
|
-
).length;
|
|
264
|
-
// Advance the high-water mark only on success, captured AFTER the writes
|
|
265
|
-
// above so pages this pass just rewrote (mtime now bumped) sit at-or-below
|
|
266
|
-
// the mark and do not re-trigger themselves next pass.
|
|
267
|
-
deps.commitClassifyHighWater(Date.now());
|
|
268
|
-
} catch (err) {
|
|
269
|
-
outcome.failures.push("assign");
|
|
270
|
-
log.warn(
|
|
271
|
-
{ err: err instanceof Error ? err.message : String(err) },
|
|
272
|
-
"memory-v3 maintain: classify-union failed (non-fatal)",
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Stage 2: prune dangling refs.
|
|
277
|
-
try {
|
|
278
|
-
const { pruned, prunedRefs } = await prunePages(tree, deps.workspaceDir);
|
|
279
|
-
outcome.pruned = pruned;
|
|
280
|
-
outcome.prunedRefs = prunedRefs;
|
|
281
|
-
} catch (err) {
|
|
282
|
-
outcome.failures.push("prune");
|
|
283
|
-
log.warn(
|
|
284
|
-
{ err: err instanceof Error ? err.message : String(err) },
|
|
285
|
-
"memory-v3 maintain: prune failed (non-fatal)",
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Stage 3: rebuild tree + needle on the next turn.
|
|
291
|
-
try {
|
|
292
|
-
deps.invalidateLanes();
|
|
293
|
-
outcome.invalidated = true;
|
|
294
|
-
} catch (err) {
|
|
295
|
-
outcome.failures.push("invalidate");
|
|
296
|
-
log.warn(
|
|
297
|
-
{ err: err instanceof Error ? err.message : String(err) },
|
|
298
|
-
"memory-v3 maintain: lane invalidation failed (non-fatal)",
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
log.info(
|
|
303
|
-
{
|
|
304
|
-
assigned: outcome.assigned,
|
|
305
|
-
pruned: outcome.pruned,
|
|
306
|
-
prunedRefs: outcome.prunedRefs,
|
|
307
|
-
invalidated: outcome.invalidated,
|
|
308
|
-
failures: outcome.failures,
|
|
309
|
-
},
|
|
310
|
-
"memory-v3 maintain pass complete",
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
return outcome;
|
|
314
|
-
}
|
package/src/memory/v3/needle.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import type { LeafTree, Slug } from "./types.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Sparse "needle" arm for memory-v3 routing: a lexical BM25 search over page
|
|
5
|
-
* titles, slug tokens, summaries, and the labels of the leaves each page
|
|
6
|
-
* belongs to. It augments topical (tree) routing by surfacing pages that share
|
|
7
|
-
* a literal term with the query even when topical routing would miss them.
|
|
8
|
-
*
|
|
9
|
-
* Implementation notes:
|
|
10
|
-
* - Hand-rolled Okapi BM25 (no dependency). The corpus is bounded (one doc per
|
|
11
|
-
* page), so a plain inverted index is plenty.
|
|
12
|
-
* - The index is built once and held in memory. The orchestrator/plugin
|
|
13
|
-
* lazy-inits it. Rebuilding on consolidation is a documented fast-follow and
|
|
14
|
-
* intentionally out of scope here.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/** Okapi BM25 term-frequency saturation parameter. */
|
|
18
|
-
export const k1 = 1.5;
|
|
19
|
-
/** Okapi BM25 length-normalization parameter. */
|
|
20
|
-
export const b = 0.75;
|
|
21
|
-
|
|
22
|
-
export interface NeedleIndex {
|
|
23
|
-
/** Returns up to `k` slugs ranked by BM25 score (descending, ties by slug). */
|
|
24
|
-
query(text: string, k: number): Slug[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Lowercase, split on non-alphanumeric, drop empties. */
|
|
28
|
-
function tokenize(text: string): string[] {
|
|
29
|
-
return text
|
|
30
|
-
.toLowerCase()
|
|
31
|
-
.split(/[^a-z0-9]+/)
|
|
32
|
-
.filter((token) => token.length > 0);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface Posting {
|
|
36
|
-
doc: number;
|
|
37
|
-
tf: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export async function buildNeedleIndex(
|
|
41
|
-
tree: LeafTree,
|
|
42
|
-
pageSummary: (slug: Slug) => Promise<string>,
|
|
43
|
-
): Promise<NeedleIndex> {
|
|
44
|
-
const slugs: Slug[] = [...tree.byPage.keys()];
|
|
45
|
-
|
|
46
|
-
// term -> postings (doc index + term frequency in that doc)
|
|
47
|
-
const postings = new Map<string, Posting[]>();
|
|
48
|
-
const docLengths: number[] = new Array(slugs.length).fill(0);
|
|
49
|
-
let totalLength = 0;
|
|
50
|
-
|
|
51
|
-
for (let doc = 0; doc < slugs.length; doc++) {
|
|
52
|
-
const slug = slugs[doc]!;
|
|
53
|
-
|
|
54
|
-
// Title = last path segment of the slug; also index the slug tokens.
|
|
55
|
-
const segments = slug.split(/[/.]/);
|
|
56
|
-
const title = segments[segments.length - 1] ?? "";
|
|
57
|
-
|
|
58
|
-
const parts: string[] = [title, slug, await pageSummary(slug)];
|
|
59
|
-
|
|
60
|
-
for (const leafPath of tree.byPage.get(slug) ?? []) {
|
|
61
|
-
const leaf = tree.leaves.get(leafPath);
|
|
62
|
-
if (leaf) parts.push(leaf.description);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const tokens = tokenize(parts.join(" "));
|
|
66
|
-
docLengths[doc] = tokens.length;
|
|
67
|
-
totalLength += tokens.length;
|
|
68
|
-
|
|
69
|
-
const termFreqs = new Map<string, number>();
|
|
70
|
-
for (const token of tokens) {
|
|
71
|
-
termFreqs.set(token, (termFreqs.get(token) ?? 0) + 1);
|
|
72
|
-
}
|
|
73
|
-
for (const [term, tf] of termFreqs) {
|
|
74
|
-
let list = postings.get(term);
|
|
75
|
-
if (!list) {
|
|
76
|
-
list = [];
|
|
77
|
-
postings.set(term, list);
|
|
78
|
-
}
|
|
79
|
-
list.push({ doc, tf });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const docCount = slugs.length;
|
|
84
|
-
const avgDocLength = docCount > 0 ? totalLength / docCount : 0;
|
|
85
|
-
|
|
86
|
-
function query(text: string, k: number): Slug[] {
|
|
87
|
-
if (k <= 0 || docCount === 0) return [];
|
|
88
|
-
|
|
89
|
-
const queryTerms = new Set(tokenize(text));
|
|
90
|
-
const scores = new Map<number, number>();
|
|
91
|
-
|
|
92
|
-
// O(query-terms x matching-docs): walk only the postings of query terms.
|
|
93
|
-
for (const term of queryTerms) {
|
|
94
|
-
const list = postings.get(term);
|
|
95
|
-
if (!list) continue;
|
|
96
|
-
|
|
97
|
-
const idf = Math.log(
|
|
98
|
-
1 + (docCount - list.length + 0.5) / (list.length + 0.5),
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
for (const { doc, tf } of list) {
|
|
102
|
-
const norm = tf * (k1 + 1);
|
|
103
|
-
const denom = tf + k1 * (1 - b + b * (docLengths[doc]! / avgDocLength));
|
|
104
|
-
scores.set(doc, (scores.get(doc) ?? 0) + idf * (norm / denom));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return [...scores.entries()]
|
|
109
|
-
.sort((a, c) => c[1] - a[1] || slugs[a[0]]!.localeCompare(slugs[c[0]]!))
|
|
110
|
-
.slice(0, k)
|
|
111
|
-
.map(([doc]) => slugs[doc]!);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return { query };
|
|
115
|
-
}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory v3 — orchestrator composing the four routing lanes into one turn.
|
|
3
|
-
*
|
|
4
|
-
* Each turn runs:
|
|
5
|
-
* 1. L1 topical routing (`routeL1`) and the lexical BM25 needle
|
|
6
|
-
* (`NeedleIndex.query`) IN PARALLEL. Routing is the slow LLM arm; the
|
|
7
|
-
* needle is a synchronous in-memory lookup, so we wrap it in a resolved
|
|
8
|
-
* promise and let them settle together.
|
|
9
|
-
* 2. Open set = unique union of routed leaves ∪ always-on core leaves ∪ the
|
|
10
|
-
* leaves that own each needle hit. Every lane only ever ADDS leaves — the
|
|
11
|
-
* union is recall-safe by construction.
|
|
12
|
-
* 3. Bounded per-leaf L2 selection (`selectAcrossLeaves`) over the open set,
|
|
13
|
-
* then dedup by slug (a page assigned to multiple opened leaves comes back
|
|
14
|
-
* once per leaf) ORing the pinned flag so a page pinned anywhere stays
|
|
15
|
-
* pinned.
|
|
16
|
-
* 4. Age the carry-forward working set to this turn (evict core slugs, stale
|
|
17
|
-
* non-pinned entries, then the cap) and snapshot it — the pages carried in
|
|
18
|
-
* from EARLIER turns.
|
|
19
|
-
* 5. Final injection = unique union of this turn's selected slugs and that
|
|
20
|
-
* carried-forward set, so pages selected on earlier turns carry forward
|
|
21
|
-
* even when this turn does not re-select them.
|
|
22
|
-
* 6. Record this turn's selections into the working set for LATER turns. This
|
|
23
|
-
* runs AFTER the snapshot so the cap is spent on genuinely carried pages,
|
|
24
|
-
* not on this turn's selections (which are injected directly) — otherwise a
|
|
25
|
-
* turn selecting more pages than the cap would evict the entire carry.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import type { NeedleIndex } from "./needle.js";
|
|
29
|
-
import { routeL1 } from "./router.js";
|
|
30
|
-
import type { SelectedPage } from "./selector.js";
|
|
31
|
-
import { selectAcrossLeaves } from "./selector.js";
|
|
32
|
-
import { coreSlugs, leavesOf } from "./tree.js";
|
|
33
|
-
import type { LeafPath, LeafTree, Slug, TurnContext } from "./types.js";
|
|
34
|
-
import { WorkingSet } from "./working-set.js";
|
|
35
|
-
|
|
36
|
-
/** Default number of needle hits to fold into the open set when unspecified. */
|
|
37
|
-
export const DEFAULT_NEEDLE_K = 10;
|
|
38
|
-
|
|
39
|
-
export interface OrchestrateDeps {
|
|
40
|
-
tree: LeafTree;
|
|
41
|
-
core: Set<LeafPath>;
|
|
42
|
-
needle: NeedleIndex;
|
|
43
|
-
workingSet: WorkingSet;
|
|
44
|
-
pageSummary: (slug: Slug) => Promise<string>;
|
|
45
|
-
/** Number of BM25 needle hits to fold in. Defaults to {@link DEFAULT_NEEDLE_K}. */
|
|
46
|
-
needleK?: number;
|
|
47
|
-
/** Bounded fan-out for per-leaf L2 selection. */
|
|
48
|
-
l2Concurrency?: number;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface OrchestrateResult {
|
|
52
|
-
/** The unique open set: routed ∪ core ∪ needle-owning leaves. */
|
|
53
|
-
openedLeaves: LeafPath[];
|
|
54
|
-
/** This turn's L2 selections, deduped by slug (pinned flags ORed). */
|
|
55
|
-
currentSelections: SelectedPage[];
|
|
56
|
-
/** The carried-forward set: selections from EARLIER turns, aged to this turn
|
|
57
|
-
* (snapshotted before this turn's selections are recorded). */
|
|
58
|
-
workingSetUnion: Set<Slug>;
|
|
59
|
-
/** Slugs to inject: this turn's selections ∪ the carried-forward working set. */
|
|
60
|
-
finalInjection: Slug[];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** Stable-order de-duplication preserving first occurrence. */
|
|
64
|
-
function unique<T>(items: Iterable<T>): T[] {
|
|
65
|
-
return [...new Set(items)];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export async function orchestrate(
|
|
69
|
-
turn: TurnContext,
|
|
70
|
-
deps: OrchestrateDeps,
|
|
71
|
-
): Promise<OrchestrateResult> {
|
|
72
|
-
// Step 1: routing (LLM) and needle (sync BM25) run in parallel.
|
|
73
|
-
const needleK = deps.needleK ?? DEFAULT_NEEDLE_K;
|
|
74
|
-
const [routed, needled] = await Promise.all([
|
|
75
|
-
routeL1(turn, deps.tree),
|
|
76
|
-
Promise.resolve(deps.needle.query(turn.currentMessage, needleK)),
|
|
77
|
-
]);
|
|
78
|
-
|
|
79
|
-
// Step 2: open set = routed ∪ core ∪ leaves owning each needle hit.
|
|
80
|
-
const openedLeaves = unique<LeafPath>([
|
|
81
|
-
...routed,
|
|
82
|
-
...deps.core,
|
|
83
|
-
...needled.flatMap((slug) => leavesOf(deps.tree, slug)),
|
|
84
|
-
]);
|
|
85
|
-
|
|
86
|
-
// Step 3: per-leaf L2 selection, then dedup by slug ORing pinned flags.
|
|
87
|
-
const selected = await selectAcrossLeaves(
|
|
88
|
-
openedLeaves,
|
|
89
|
-
turn,
|
|
90
|
-
deps.tree,
|
|
91
|
-
deps.pageSummary,
|
|
92
|
-
deps.l2Concurrency,
|
|
93
|
-
);
|
|
94
|
-
const bySlug = new Map<Slug, SelectedPage>();
|
|
95
|
-
for (const page of selected) {
|
|
96
|
-
const existing = bySlug.get(page.slug);
|
|
97
|
-
bySlug.set(page.slug, {
|
|
98
|
-
slug: page.slug,
|
|
99
|
-
pinned: (existing?.pinned ?? false) || page.pinned,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
const currentSelections = [...bySlug.values()];
|
|
103
|
-
|
|
104
|
-
// Step 4: age the carry-forward set to this turn (drop core slugs, stale
|
|
105
|
-
// non-pinned entries, then the cap) and snapshot it. This is the set carried
|
|
106
|
-
// in from EARLIER turns; recording this turn happens afterward (step 6) so the
|
|
107
|
-
// cap is spent on genuinely carried pages, not on this turn's selections
|
|
108
|
-
// (which are injected directly anyway).
|
|
109
|
-
deps.workingSet.evict(turn.turnNumber, coreSlugs(deps.tree, deps.core));
|
|
110
|
-
const workingSetUnion = deps.workingSet.union();
|
|
111
|
-
|
|
112
|
-
// Step 5: final injection = this turn's selections ∪ the carried-forward set,
|
|
113
|
-
// so pages selected on earlier turns carry forward even when this turn does
|
|
114
|
-
// not re-select them.
|
|
115
|
-
const finalInjection = unique<Slug>([
|
|
116
|
-
...currentSelections.map((s) => s.slug),
|
|
117
|
-
...workingSetUnion,
|
|
118
|
-
]);
|
|
119
|
-
|
|
120
|
-
// Step 6: record this turn's selections so they carry forward to LATER turns.
|
|
121
|
-
for (const sel of currentSelections) {
|
|
122
|
-
deps.workingSet.recordSelection(sel.slug, turn.turnNumber, sel.pinned);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return { openedLeaves, currentSelections, workingSetUnion, finalInjection };
|
|
126
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { getWorkspaceDir } from "../../util/platform.js";
|
|
2
|
-
import { readPage, renderPageContent } from "../v2/page-store.js";
|
|
3
|
-
import { renderCapabilityContent } from "./capabilities.js";
|
|
4
|
-
import type { Slug } from "./types.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Render a selected page's full content for the v3 `<memory>` block. Mirrors
|
|
8
|
-
* the v2 dynamic-memory layout (`# memory/concepts/<slug>.md\n<frontmatter+body>`)
|
|
9
|
-
* so the working-set block reads like v2's. A missing page (or any read
|
|
10
|
-
* failure) degrades to "" — `renderMemoryBlock` still emits a line for the
|
|
11
|
-
* slug, and a blank section is preferable to throwing into the turn.
|
|
12
|
-
*
|
|
13
|
-
* Synthetic capability slugs (skills, `assistant` CLI commands) have no on-disk
|
|
14
|
-
* page; they resolve through {@link renderCapabilityContent} instead of
|
|
15
|
-
* `readPage`. A non-null result (including "") means the slug was a capability
|
|
16
|
-
* slug and was handled here.
|
|
17
|
-
*
|
|
18
|
-
* Shared by the live injector (`shadow-plugin.ts`) and the inspector
|
|
19
|
-
* selection-log store (`selection-log-store.ts`) so the inspector's rendered
|
|
20
|
-
* block is byte-identical to what live injection produces.
|
|
21
|
-
*/
|
|
22
|
-
export async function renderV3PageContent(slug: Slug): Promise<string> {
|
|
23
|
-
const capability = renderCapabilityContent(slug);
|
|
24
|
-
if (capability !== null) return capability;
|
|
25
|
-
try {
|
|
26
|
-
const page = await readPage(getWorkspaceDir(), slug);
|
|
27
|
-
if (!page) return "";
|
|
28
|
-
const content = renderPageContent(page).trim();
|
|
29
|
-
if (content.length === 0) return "";
|
|
30
|
-
return `# memory/concepts/${slug}.md\n${content}`;
|
|
31
|
-
} catch {
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ContentBlock } from "../../providers/types.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Text content block carrying a `cache_control` breakpoint with a 1-hour TTL.
|
|
5
|
-
* Shared by the v3 router (the static leaf-tree block) and selector (each
|
|
6
|
-
* leaf's static `<pages>` block): these prefixes are stable across turns — the
|
|
7
|
-
* leaf tree is byte-identical every turn, and a leaf's pages block changes only
|
|
8
|
-
* when its pages/summaries do — while v3 turns are frequently more than the
|
|
9
|
-
* default 5-minute cache window apart. A 1h TTL keeps the prefix warm across
|
|
10
|
-
* those gaps so it is read from cache rather than re-created every turn; the
|
|
11
|
-
* volatile current-message block is rendered after this one and left un-cached.
|
|
12
|
-
* Haiku does not support the extended-cache-ttl beta, so the Anthropic provider
|
|
13
|
-
* strips this `ttl` for Haiku models.
|
|
14
|
-
*
|
|
15
|
-
* Our internal `TextContent` type omits `cache_control` (only the Anthropic
|
|
16
|
-
* provider transforms it onto the wire), so we reach through a `Record` cast to
|
|
17
|
-
* keep the core types provider-agnostic.
|
|
18
|
-
*/
|
|
19
|
-
export function cachedTextBlock(text: string): ContentBlock {
|
|
20
|
-
const block: ContentBlock = { type: "text", text };
|
|
21
|
-
(block as unknown as Record<string, unknown>).cache_control = {
|
|
22
|
-
type: "ephemeral",
|
|
23
|
-
ttl: "1h",
|
|
24
|
-
};
|
|
25
|
-
return block;
|
|
26
|
-
}
|