@vellumai/assistant 0.8.4 → 0.8.5
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 +2 -2
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/openapi.yaml +809 -11
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +34 -37
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
- package/src/__tests__/conversation-agent-loop.test.ts +163 -122
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +178 -15
- package/src/__tests__/conversation-lifecycle.test.ts +52 -11
- package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +77 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +46 -0
- package/src/__tests__/openai-responses-provider.test.ts +114 -12
- package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -2
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +145 -131
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +9 -62
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +6 -5
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/loop.ts +8 -0
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +176 -1
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +7 -6
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
- package/src/config/schemas/call-site-catalog.ts +21 -7
- package/src/config/schemas/llm.ts +12 -1
- package/src/config/schemas/memory-v2.ts +246 -0
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +52 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
- package/src/daemon/conversation-agent-loop.ts +126 -76
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +27 -22
- package/src/daemon/conversation-runtime-assembly.ts +10 -9
- package/src/daemon/conversation-tool-setup.ts +63 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +14 -29
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +11 -3
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +4 -4
- package/src/daemon/host-file-proxy.ts +4 -4
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +10 -10
- package/src/daemon/lifecycle.ts +23 -20
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-types/conversations.ts +6 -9
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +6 -14
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
- package/src/home/home-greeting.ts +0 -9
- package/src/home/suggested-prompts.ts +0 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +71 -70
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +10 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/graph/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +55 -22
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-enqueue.ts +8 -1
- package/src/memory/memory-retrospective-job.ts +5 -0
- package/src/memory/memory-v2-activation-log-store.ts +15 -6
- package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/schema/conversations.ts +1 -1
- package/src/memory/schema/infrastructure.ts +21 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection.test.ts +127 -98
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +171 -3
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection.ts +21 -15
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +171 -18
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/emit-signal.ts +29 -49
- package/src/permissions/prompter.ts +3 -3
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +2 -2
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +18 -11
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +39 -7
- package/src/prompts/system-prompt.ts +50 -185
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +230 -8
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +32 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +43 -0
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +6 -3
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +78 -21
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/agent-wake.ts +1 -0
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +36 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +0 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +142 -36
- package/src/runtime/routes/conversation-routes.ts +252 -410
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- package/src/runtime/routes/host-browser-routes.ts +10 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +215 -5
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +5 -4
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- package/src/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
- package/src/tools/browser/browser-execution.ts +16 -3
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
- package/src/tools/browser/cdp-client/factory.ts +100 -17
- package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
- package/src/tools/browser/cdp-client/types.ts +65 -0
- package/src/tools/browser/pinned-tabs.ts +96 -40
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- package/src/tools/document/document-tool.ts +59 -0
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -9
- package/src/tools/network/web-fetch.ts +3 -9
- package/src/tools/network/web-search.ts +25 -32
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +27 -98
- package/src/tools/ui-surface/definitions.ts +6 -22
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -2
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- package/src/daemon/query-complexity-router.ts +0 -75
- package/src/prompts/cache-boundary.ts +0 -8
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `db-maintenance.ts` (orchestration) and the underlying
|
|
3
|
+
* `db-async-query.ts` abstraction.
|
|
4
|
+
*
|
|
5
|
+
* The contract this PR locks in:
|
|
6
|
+
* 1. `runDbMaintenance` runs `VACUUM` through the async abstraction
|
|
7
|
+
* — when the `sqlite3` CLI is available, that means a subprocess
|
|
8
|
+
* and the daemon's main event loop keeps ticking. (The structural
|
|
9
|
+
* anti-block assertion lives in
|
|
10
|
+
* `db-async-query.test.ts`; here we focus on orchestration.)
|
|
11
|
+
* 2. The subprocess actually shrinks the on-disk page count when
|
|
12
|
+
* there's reclaimable space.
|
|
13
|
+
* 3. `maybeRunDbMaintenance` is genuinely async — callers can `await`
|
|
14
|
+
* it and observe completion.
|
|
15
|
+
* 4. The 24 h interval guard short-circuits a recent re-run.
|
|
16
|
+
*
|
|
17
|
+
* The per-file temp workspace is set up by `test-preload.ts`; tests just
|
|
18
|
+
* dynamic-import the DB modules so they resolve paths under that temp dir.
|
|
19
|
+
*/
|
|
20
|
+
import { Database } from "bun:sqlite";
|
|
21
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
22
|
+
|
|
23
|
+
const { getSqlite } = await import("../db-connection.js");
|
|
24
|
+
const { initializeDb } = await import("../db-init.js");
|
|
25
|
+
const { deleteMemoryCheckpoint, getMemoryCheckpoint } =
|
|
26
|
+
await import("../checkpoints.js");
|
|
27
|
+
const { maybeRunDbMaintenance } = await import("../db-maintenance.js");
|
|
28
|
+
const { getDbPath } = await import("../../util/platform.js");
|
|
29
|
+
|
|
30
|
+
initializeDb();
|
|
31
|
+
|
|
32
|
+
const MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
deleteMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/** Inflate the test DB with bloat that VACUUM can reclaim. */
|
|
39
|
+
function inflateAndDelete(byteTarget: number): void {
|
|
40
|
+
const sqlite = getSqlite();
|
|
41
|
+
sqlite.exec(
|
|
42
|
+
"CREATE TABLE IF NOT EXISTS bloat (id INTEGER PRIMARY KEY, payload BLOB)",
|
|
43
|
+
);
|
|
44
|
+
const pageSize = (
|
|
45
|
+
sqlite.query("PRAGMA page_size").get() as { page_size: number }
|
|
46
|
+
).page_size;
|
|
47
|
+
const rowsTarget = Math.max(1, Math.ceil(byteTarget / pageSize));
|
|
48
|
+
const payload = new Uint8Array(Math.max(1, pageSize - 64));
|
|
49
|
+
const insert = sqlite.prepare("INSERT INTO bloat (payload) VALUES (?)");
|
|
50
|
+
sqlite.exec("BEGIN");
|
|
51
|
+
for (let i = 0; i < rowsTarget; i++) {
|
|
52
|
+
insert.run(payload);
|
|
53
|
+
}
|
|
54
|
+
sqlite.exec("COMMIT");
|
|
55
|
+
sqlite.exec("DELETE FROM bloat");
|
|
56
|
+
sqlite.exec("DROP TABLE bloat");
|
|
57
|
+
// Force the WAL onto the main DB file so the bloat is visible on disk.
|
|
58
|
+
sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe("maybeRunDbMaintenance", () => {
|
|
62
|
+
test("returns a Promise that resolves", async () => {
|
|
63
|
+
const result = maybeRunDbMaintenance();
|
|
64
|
+
expect(result).toBeInstanceOf(Promise);
|
|
65
|
+
await result;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("respects the 24h interval and skips when last run was recent", async () => {
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
const recent = now - 60_000;
|
|
71
|
+
const { setMemoryCheckpoint } = await import("../checkpoints.js");
|
|
72
|
+
setMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY, String(recent));
|
|
73
|
+
|
|
74
|
+
await maybeRunDbMaintenance(now);
|
|
75
|
+
|
|
76
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(
|
|
77
|
+
String(recent),
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("stamps the checkpoint after a maintenance run", async () => {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
await maybeRunDbMaintenance(now);
|
|
84
|
+
|
|
85
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("VACUUM reclaims pages on a bloated DB", async () => {
|
|
89
|
+
const sqlite = getSqlite();
|
|
90
|
+
sqlite.exec("DROP TABLE IF EXISTS bloat");
|
|
91
|
+
sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
92
|
+
|
|
93
|
+
inflateAndDelete(8 * 1024 * 1024);
|
|
94
|
+
|
|
95
|
+
const dbPath = getDbPath();
|
|
96
|
+
// Read page_count from a fresh connection so we observe post-write
|
|
97
|
+
// ground truth without snapshot caching on the main test connection.
|
|
98
|
+
const readPageCount = (): number => {
|
|
99
|
+
const probe = new Database(dbPath, { readonly: true });
|
|
100
|
+
try {
|
|
101
|
+
return (
|
|
102
|
+
probe.query("PRAGMA page_count").get() as { page_count: number }
|
|
103
|
+
).page_count;
|
|
104
|
+
} finally {
|
|
105
|
+
probe.close();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const pagesBefore = readPageCount();
|
|
109
|
+
|
|
110
|
+
await maybeRunDbMaintenance();
|
|
111
|
+
|
|
112
|
+
const pagesAfter = readPageCount();
|
|
113
|
+
expect(pagesAfter).toBeLessThan(pagesBefore);
|
|
114
|
+
}, 60_000);
|
|
115
|
+
});
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Tests for the memory enqueue gate.
|
|
2
|
+
//
|
|
3
|
+
// Architecture under test:
|
|
4
|
+
// 1. `isMemoryEnabled()` (jobs-store.ts) reads `config.memory.enabled`
|
|
5
|
+
// and returns `true` unless explicitly `false`.
|
|
6
|
+
// 2. Each call site that enqueues a memory job gates on this helper
|
|
7
|
+
// before calling `enqueueMemoryJob` / `upsertDebouncedJob` etc.
|
|
8
|
+
// `enqueueMemoryJob` itself is *not* gated — it preserves its
|
|
9
|
+
// "always returns a real job id" contract, and non-memory jobs
|
|
10
|
+
// (`delete_qdrant_vectors`, `prune_*`) flow through unchanged.
|
|
11
|
+
//
|
|
12
|
+
// We verify (1) directly across the four config-shape variants, then
|
|
13
|
+
// smoke-test (2) at two central entry-point helpers:
|
|
14
|
+
// - `enqueueAutoAnalysisIfEnabled`
|
|
15
|
+
// - `enqueueMemoryRetrospectiveIfEnabled`
|
|
16
|
+
// Each call site re-checks `isMemoryEnabled()` itself, so we don't
|
|
17
|
+
// repeat 30+ identical scenarios — the helper test is the contract.
|
|
18
|
+
|
|
19
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
20
|
+
|
|
21
|
+
// Silence the logger.
|
|
22
|
+
mock.module("../../util/logger.js", () => ({
|
|
23
|
+
getLogger: () =>
|
|
24
|
+
new Proxy({} as Record<string, unknown>, {
|
|
25
|
+
get: () => () => {},
|
|
26
|
+
}),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// Mutable config shape, mutated per-test. `null` means "no `memory` key
|
|
30
|
+
// at all" — exercises the defensive `?.` chain in `isMemoryEnabled`.
|
|
31
|
+
type MemoryEnabledShape = boolean | null | undefined;
|
|
32
|
+
let memoryEnabled: MemoryEnabledShape = true;
|
|
33
|
+
let getConfigThrows = false;
|
|
34
|
+
mock.module("../../config/loader.js", () => ({
|
|
35
|
+
getConfig: () => {
|
|
36
|
+
if (getConfigThrows) throw new Error("config load failed");
|
|
37
|
+
if (memoryEnabled === null) return {};
|
|
38
|
+
return {
|
|
39
|
+
memory: { enabled: memoryEnabled, v2: { enabled: false } },
|
|
40
|
+
analysis: { idleTimeoutMs: 600_000, batchSize: 30 },
|
|
41
|
+
assistant: { featureFlags: { "auto-analyze": true } },
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// Stub feature flags so auto-analyze isn't gated by an unrelated flag.
|
|
47
|
+
mock.module("../../config/assistant-feature-flags.js", () => ({
|
|
48
|
+
isAssistantFeatureFlagEnabled: () => true,
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
// Stub trust resolver — never claim the actor is untrusted in tests.
|
|
52
|
+
mock.module("../../runtime/actor-trust-resolver.js", () => ({
|
|
53
|
+
isUntrustedTrustClass: () => false,
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
// Stub the conversation-source lookup so the recursion guards in the
|
|
57
|
+
// retrospective and auto-analysis paths fall through to the enqueue.
|
|
58
|
+
mock.module("../conversation-crud.js", () => ({
|
|
59
|
+
getConversationSource: () => null,
|
|
60
|
+
}));
|
|
61
|
+
mock.module("../auto-analysis-guard.js", () => ({
|
|
62
|
+
isAutoAnalysisConversation: () => false,
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
// Stub the qdrant breaker so `enqueueMemoryJob` doesn't trip on it.
|
|
66
|
+
mock.module("../qdrant-circuit-breaker.js", () => ({
|
|
67
|
+
isQdrantBreakerOpen: () => false,
|
|
68
|
+
shouldAllowQdrantProbe: () => true,
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
// Stub raw query helpers (used by jobs-store internally).
|
|
72
|
+
mock.module("../raw-query.js", () => ({
|
|
73
|
+
rawAll: () => [],
|
|
74
|
+
rawChanges: () => 0,
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
// Drizzle-shaped no-op db. Tracks inserts/updates so tests can observe
|
|
78
|
+
// whether an upsert/enqueue actually wrote anything.
|
|
79
|
+
const dbInserts: Array<{ table?: unknown; values: unknown }> = [];
|
|
80
|
+
const dbUpdates: Array<{ table?: unknown; set: unknown }> = [];
|
|
81
|
+
function makeStubDb() {
|
|
82
|
+
return {
|
|
83
|
+
insert: (table: unknown) => ({
|
|
84
|
+
values: (values: unknown) => {
|
|
85
|
+
dbInserts.push({ table, values });
|
|
86
|
+
return {
|
|
87
|
+
run: () => {},
|
|
88
|
+
onConflictDoUpdate: () => ({ run: () => {} }),
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
update: (table: unknown) => ({
|
|
93
|
+
set: (set: unknown) => {
|
|
94
|
+
dbUpdates.push({ table, set });
|
|
95
|
+
return {
|
|
96
|
+
where: () => ({ run: () => {} }),
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
}),
|
|
100
|
+
select: () => ({
|
|
101
|
+
from: () => ({
|
|
102
|
+
where: () => ({
|
|
103
|
+
orderBy: () => ({ get: () => null, all: () => [] }),
|
|
104
|
+
get: () => null,
|
|
105
|
+
all: () => [],
|
|
106
|
+
}),
|
|
107
|
+
}),
|
|
108
|
+
}),
|
|
109
|
+
transaction: (fn: (tx: unknown) => unknown) => fn(makeStubDb()),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const stubDb = makeStubDb();
|
|
113
|
+
mock.module("../db-connection.js", () => ({
|
|
114
|
+
getDb: () => stubDb,
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
// Now load the real modules under test.
|
|
118
|
+
const { isMemoryEnabled } = await import("../jobs-store.js");
|
|
119
|
+
const { enqueueAutoAnalysisIfEnabled } = await import(
|
|
120
|
+
"../auto-analysis-enqueue.js"
|
|
121
|
+
);
|
|
122
|
+
const { enqueueMemoryRetrospectiveIfEnabled } = await import(
|
|
123
|
+
"../memory-retrospective-enqueue.js"
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
dbInserts.length = 0;
|
|
128
|
+
dbUpdates.length = 0;
|
|
129
|
+
memoryEnabled = true;
|
|
130
|
+
getConfigThrows = false;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------
|
|
134
|
+
// isMemoryEnabled() contract
|
|
135
|
+
// ---------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
describe("isMemoryEnabled", () => {
|
|
138
|
+
test("returns true when memory.enabled is true", () => {
|
|
139
|
+
memoryEnabled = true;
|
|
140
|
+
expect(isMemoryEnabled()).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("returns true when memory.enabled is undefined (schema default)", () => {
|
|
144
|
+
memoryEnabled = undefined;
|
|
145
|
+
expect(isMemoryEnabled()).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("returns true when memory key is absent (partial config)", () => {
|
|
149
|
+
memoryEnabled = null;
|
|
150
|
+
expect(isMemoryEnabled()).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("returns false ONLY when memory.enabled is explicitly false", () => {
|
|
154
|
+
memoryEnabled = false;
|
|
155
|
+
expect(isMemoryEnabled()).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("returns true (defensive) when getConfig throws", () => {
|
|
159
|
+
// If config can't be read, we can't tell whether memory has been
|
|
160
|
+
// disabled, so default to "enabled". Callers that already have their
|
|
161
|
+
// own getConfig try/catch (e.g. enqueueAutoAnalysisIfEnabled) keep
|
|
162
|
+
// controlling the silent-failure semantic for the rest of their flow.
|
|
163
|
+
getConfigThrows = true;
|
|
164
|
+
expect(isMemoryEnabled()).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// ---------------------------------------------------------------------
|
|
169
|
+
// enqueueAutoAnalysisIfEnabled — representative entry-point helper
|
|
170
|
+
// ---------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
describe("enqueueAutoAnalysisIfEnabled (call-site gate)", () => {
|
|
173
|
+
test("does NOT enqueue when memory.enabled is false", () => {
|
|
174
|
+
memoryEnabled = false;
|
|
175
|
+
enqueueAutoAnalysisIfEnabled({
|
|
176
|
+
conversationId: "conv-1",
|
|
177
|
+
trigger: "batch",
|
|
178
|
+
});
|
|
179
|
+
expect(dbInserts.length).toBe(0);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("enqueues when memory.enabled is true", () => {
|
|
183
|
+
memoryEnabled = true;
|
|
184
|
+
enqueueAutoAnalysisIfEnabled({
|
|
185
|
+
conversationId: "conv-1",
|
|
186
|
+
trigger: "batch",
|
|
187
|
+
});
|
|
188
|
+
expect(dbInserts.length).toBeGreaterThan(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("enqueues when memory.enabled is undefined (schema default)", () => {
|
|
192
|
+
memoryEnabled = undefined;
|
|
193
|
+
enqueueAutoAnalysisIfEnabled({
|
|
194
|
+
conversationId: "conv-1",
|
|
195
|
+
trigger: "batch",
|
|
196
|
+
});
|
|
197
|
+
expect(dbInserts.length).toBeGreaterThan(0);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("enqueues when memory key is absent from config", () => {
|
|
201
|
+
memoryEnabled = null;
|
|
202
|
+
enqueueAutoAnalysisIfEnabled({
|
|
203
|
+
conversationId: "conv-1",
|
|
204
|
+
trigger: "batch",
|
|
205
|
+
});
|
|
206
|
+
expect(dbInserts.length).toBeGreaterThan(0);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// ---------------------------------------------------------------------
|
|
211
|
+
// enqueueMemoryRetrospectiveIfEnabled — representative entry-point
|
|
212
|
+
// ---------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
describe("enqueueMemoryRetrospectiveIfEnabled (call-site gate)", () => {
|
|
215
|
+
test("does NOT enqueue when memory.enabled is false", () => {
|
|
216
|
+
memoryEnabled = false;
|
|
217
|
+
enqueueMemoryRetrospectiveIfEnabled({
|
|
218
|
+
conversationId: "conv-1",
|
|
219
|
+
trigger: "interval",
|
|
220
|
+
});
|
|
221
|
+
expect(dbInserts.length).toBe(0);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("enqueues when memory.enabled is true", () => {
|
|
225
|
+
memoryEnabled = true;
|
|
226
|
+
enqueueMemoryRetrospectiveIfEnabled({
|
|
227
|
+
conversationId: "conv-1",
|
|
228
|
+
trigger: "interval",
|
|
229
|
+
});
|
|
230
|
+
expect(dbInserts.length).toBeGreaterThan(0);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("enqueues when memory.enabled is undefined (schema default)", () => {
|
|
234
|
+
memoryEnabled = undefined;
|
|
235
|
+
enqueueMemoryRetrospectiveIfEnabled({
|
|
236
|
+
conversationId: "conv-1",
|
|
237
|
+
trigger: "interval",
|
|
238
|
+
});
|
|
239
|
+
expect(dbInserts.length).toBeGreaterThan(0);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
EMBED_JOB_TYPES,
|
|
5
|
+
type MemoryJobType,
|
|
6
|
+
SLOW_LLM_JOB_TYPES,
|
|
7
|
+
} from "../jobs-store.js";
|
|
8
|
+
|
|
9
|
+
describe("memory v3 job types", () => {
|
|
10
|
+
test("the v3 job-type literals are members of MemoryJobType", () => {
|
|
11
|
+
// Compile-time assignability is enforced by `tsc --noEmit`; the runtime
|
|
12
|
+
// assertion keeps the literals visible to the test runner. These types are
|
|
13
|
+
// inert scaffolding until their handlers land in later PRs.
|
|
14
|
+
const v3JobTypes: MemoryJobType[] = [
|
|
15
|
+
"memory_v3_consolidate",
|
|
16
|
+
"memory_v3_index_maintenance",
|
|
17
|
+
"memory_v3_edge_learning",
|
|
18
|
+
];
|
|
19
|
+
expect(new Set(v3JobTypes).size).toBe(3);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
4
22
|
|
|
5
23
|
describe("memory job classes", () => {
|
|
6
24
|
test("EMBED_JOB_TYPES and SLOW_LLM_JOB_TYPES are disjoint", () => {
|
|
@@ -21,4 +39,13 @@ describe("memory job classes", () => {
|
|
|
21
39
|
const set = new Set(SLOW_LLM_JOB_TYPES);
|
|
22
40
|
expect(set.size).toBe(SLOW_LLM_JOB_TYPES.length);
|
|
23
41
|
});
|
|
42
|
+
|
|
43
|
+
test("memory_v3_consolidate is a slow LLM job (lane isolation)", () => {
|
|
44
|
+
// It hands off to a background agent for up to 15 min, like
|
|
45
|
+
// memory_v2_consolidate; it must not sit in the fast lane and starve
|
|
46
|
+
// short jobs. The mechanical v3 jobs intentionally stay fast.
|
|
47
|
+
expect(SLOW_LLM_JOB_TYPES).toContain("memory_v3_consolidate");
|
|
48
|
+
expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_index_maintenance");
|
|
49
|
+
expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_edge_learning");
|
|
50
|
+
});
|
|
24
51
|
});
|
|
@@ -315,6 +315,13 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
315
315
|
expect(bootstrapCalls[0]!.forkParentConversationId).toBe("src-conv-1");
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
+
test("legacy path: wake opts include suppressWakeSurface so the full retrospective prompt isn't rendered as a 'Conversation Woke' card body to clients", async () => {
|
|
319
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
320
|
+
|
|
321
|
+
expect(wakeCalls).toHaveLength(1);
|
|
322
|
+
expect(wakeCalls[0]!.opts.suppressWakeSurface).toBe(true);
|
|
323
|
+
});
|
|
324
|
+
|
|
318
325
|
test("no-new-messages early return: neither field changes, no wake, no bootstrap", async () => {
|
|
319
326
|
newMessages = [];
|
|
320
327
|
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "../runtime/actor-trust-resolver.js";
|
|
7
7
|
import { getLogger } from "../util/logger.js";
|
|
8
8
|
import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
|
|
9
|
-
import { upsertAutoAnalysisJob } from "./jobs-store.js";
|
|
9
|
+
import { isMemoryEnabled, upsertAutoAnalysisJob } from "./jobs-store.js";
|
|
10
10
|
|
|
11
11
|
const log = getLogger("auto-analysis-enqueue");
|
|
12
12
|
|
|
@@ -46,6 +46,10 @@ export function enqueueAutoAnalysisIfEnabled(args: {
|
|
|
46
46
|
}): void {
|
|
47
47
|
const { conversationId, trigger } = args;
|
|
48
48
|
|
|
49
|
+
if (!isMemoryEnabled()) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
let config;
|
|
50
54
|
try {
|
|
51
55
|
config = getConfig();
|
|
@@ -24,6 +24,7 @@ import type { ChannelId, InterfaceId } from "../channels/types.js";
|
|
|
24
24
|
import { parseChannelId, parseInterfaceId } from "../channels/types.js";
|
|
25
25
|
import { CHANNEL_IDS, isChannelId } from "../channels/types.js";
|
|
26
26
|
import { getConfig } from "../config/loader.js";
|
|
27
|
+
import { findDisplayTurnEndIndex } from "../conversations/message-consolidation.js";
|
|
27
28
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
28
29
|
import { UserError } from "../util/errors.js";
|
|
29
30
|
import { safeParseRecord } from "../util/json.js";
|
|
@@ -47,6 +48,7 @@ import {
|
|
|
47
48
|
} from "./conversation-disk-view.js";
|
|
48
49
|
import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
|
|
49
50
|
import { ensureGroupMigration } from "./conversation-group-migration.js";
|
|
51
|
+
import { runAsyncSqlite } from "./db-async-query.js";
|
|
50
52
|
import { getDb, getSqliteFrom } from "./db-connection.js";
|
|
51
53
|
import { forkGraphMemoryState } from "./graph/graph-memory-state-store.js";
|
|
52
54
|
import { indexMessageNow } from "./indexer.js";
|
|
@@ -190,7 +192,7 @@ export interface ConversationRow {
|
|
|
190
192
|
contextSummary: string | null;
|
|
191
193
|
contextCompactedMessageCount: number;
|
|
192
194
|
contextCompactedAt: number | null;
|
|
193
|
-
|
|
195
|
+
historyStrippedAt: number | null;
|
|
194
196
|
slackContextCompactionWatermarkTs: string | null;
|
|
195
197
|
slackContextCompactionWatermarkAt: number | null;
|
|
196
198
|
conversationType: string;
|
|
@@ -224,7 +226,7 @@ export const parseConversation = createRowMapper<
|
|
|
224
226
|
contextSummary: "contextSummary",
|
|
225
227
|
contextCompactedMessageCount: "contextCompactedMessageCount",
|
|
226
228
|
contextCompactedAt: "contextCompactedAt",
|
|
227
|
-
|
|
229
|
+
historyStrippedAt: "historyStrippedAt",
|
|
228
230
|
slackContextCompactionWatermarkTs: "slackContextCompactionWatermarkTs",
|
|
229
231
|
slackContextCompactionWatermarkAt: "slackContextCompactionWatermarkAt",
|
|
230
232
|
conversationType: "conversationType",
|
|
@@ -581,17 +583,30 @@ export function forkConversation(params: {
|
|
|
581
583
|
);
|
|
582
584
|
}
|
|
583
585
|
|
|
584
|
-
const
|
|
586
|
+
const initialBoundaryIndex =
|
|
585
587
|
throughMessageId == null
|
|
586
588
|
? sourceMessages.length - 1
|
|
587
589
|
: sourceMessages.findIndex((message) => message.id === throughMessageId);
|
|
588
590
|
|
|
589
|
-
if (throughMessageId != null &&
|
|
591
|
+
if (throughMessageId != null && initialBoundaryIndex === -1) {
|
|
590
592
|
throw new UserError(
|
|
591
593
|
`Message ${throughMessageId} does not belong to conversation ${conversationId}`,
|
|
592
594
|
);
|
|
593
595
|
}
|
|
594
596
|
|
|
597
|
+
// Extend the boundary to cover the full display turn the client
|
|
598
|
+
// addressed. The read-path collapses each assistant turn across
|
|
599
|
+
// multiple DB rows — consecutive assistant rows AND tool-result-only
|
|
600
|
+
// user rows between them — so "fork through message X" semantically
|
|
601
|
+
// means "fork through the entire display turn containing X" no matter
|
|
602
|
+
// which DB row in the cluster the client supplied. Single source of
|
|
603
|
+
// truth is `findDisplayTurnEndIndex`, shared with the read path so
|
|
604
|
+
// both stay in sync.
|
|
605
|
+
const copyBoundaryIndex = findDisplayTurnEndIndex(
|
|
606
|
+
sourceMessages,
|
|
607
|
+
initialBoundaryIndex,
|
|
608
|
+
);
|
|
609
|
+
|
|
595
610
|
const visibleWindowStartIndex = Math.max(
|
|
596
611
|
0,
|
|
597
612
|
Math.min(
|
|
@@ -607,15 +622,16 @@ export function forkConversation(params: {
|
|
|
607
622
|
? sourceMessages.slice(0, copyBoundaryIndex + 1)
|
|
608
623
|
: ([] as MessageRow[]);
|
|
609
624
|
|
|
610
|
-
// Inherit
|
|
611
|
-
//
|
|
612
|
-
//
|
|
613
|
-
|
|
625
|
+
// Inherit the history-strip marker only when the fork boundary is at-or-
|
|
626
|
+
// after the strip event. Pre-strip forks branch from history that pre-
|
|
627
|
+
// dates the strip, so the marker would be a no-op and is misleading to
|
|
628
|
+
// copy.
|
|
629
|
+
const sourceHistoryStrippedAt = sourceConversation.historyStrippedAt ?? null;
|
|
614
630
|
const boundaryMessageCreatedAt = messagesToCopy.at(-1)?.createdAt ?? null;
|
|
615
|
-
const
|
|
616
|
-
|
|
631
|
+
const inheritsHistoryStrippedAt =
|
|
632
|
+
sourceHistoryStrippedAt != null &&
|
|
617
633
|
boundaryMessageCreatedAt != null &&
|
|
618
|
-
boundaryMessageCreatedAt >=
|
|
634
|
+
boundaryMessageCreatedAt >= sourceHistoryStrippedAt;
|
|
619
635
|
const forkParentMessageId = messagesToCopy.at(-1)?.id ?? null;
|
|
620
636
|
const forkTitle =
|
|
621
637
|
params.title ?? `${sourceConversation.title ?? "Untitled"} (Fork)`;
|
|
@@ -662,7 +678,9 @@ export function forkConversation(params: {
|
|
|
662
678
|
slackContextCompactionWatermarkAt: preserveSourceCompactionState
|
|
663
679
|
? sourceConversation.slackContextCompactionWatermarkAt
|
|
664
680
|
: null,
|
|
665
|
-
|
|
681
|
+
historyStrippedAt: inheritsHistoryStrippedAt
|
|
682
|
+
? sourceHistoryStrippedAt
|
|
683
|
+
: null,
|
|
666
684
|
inferenceProfile: sourceConversation.inferenceProfile,
|
|
667
685
|
})
|
|
668
686
|
.where(eq(conversations.id, fc.id))
|
|
@@ -1451,14 +1469,14 @@ export function updateConversationContextWindow(
|
|
|
1451
1469
|
.run();
|
|
1452
1470
|
}
|
|
1453
1471
|
|
|
1454
|
-
export function
|
|
1472
|
+
export function setConversationHistoryStrippedAt(
|
|
1455
1473
|
id: string,
|
|
1456
|
-
|
|
1474
|
+
historyStrippedAt: number | null,
|
|
1457
1475
|
): void {
|
|
1458
1476
|
const db = getDb();
|
|
1459
1477
|
db.update(conversations)
|
|
1460
1478
|
.set({
|
|
1461
|
-
|
|
1479
|
+
historyStrippedAt,
|
|
1462
1480
|
updatedAt: Date.now(),
|
|
1463
1481
|
})
|
|
1464
1482
|
.where(eq(conversations.id, id))
|
|
@@ -1703,13 +1721,35 @@ export function setLastNotifiedInferenceProfile(
|
|
|
1703
1721
|
* Delete all conversations, messages, and related data (tool invocations,
|
|
1704
1722
|
* memory segments, etc.) from the daemon database.
|
|
1705
1723
|
* Returns { conversations, messages } counts.
|
|
1724
|
+
*
|
|
1725
|
+
* Each bulk DELETE is dispatched through {@link runAsyncSqlite}: when
|
|
1726
|
+
* the host has a `sqlite3` CLI it executes in a subprocess and the
|
|
1727
|
+
* daemon's main event loop stays responsive while large tables
|
|
1728
|
+
* (`llm_request_logs`, `tool_invocations`, etc.) are wiped. On hosts
|
|
1729
|
+
* without the CLI the abstraction falls back to in-process blocking
|
|
1730
|
+
* execution — the same behaviour the daemon had before.
|
|
1706
1731
|
*/
|
|
1707
|
-
export function clearAll(): {
|
|
1732
|
+
export async function clearAll(): Promise<{
|
|
1733
|
+
conversations: number;
|
|
1734
|
+
messages: number;
|
|
1735
|
+
}> {
|
|
1708
1736
|
const msgCount =
|
|
1709
1737
|
rawGet<{ c: number }>("SELECT COUNT(*) AS c FROM messages")?.c ?? 0;
|
|
1710
1738
|
const convCount =
|
|
1711
1739
|
rawGet<{ c: number }>("SELECT COUNT(*) AS c FROM conversations")?.c ?? 0;
|
|
1712
1740
|
|
|
1741
|
+
// Each DELETE goes through `runAsyncSqlite`. The original code threw
|
|
1742
|
+
// on rawExec failure; mirror that here by throwing when the async
|
|
1743
|
+
// result reports `ok: false`, so the route handler still returns 500.
|
|
1744
|
+
const runOrThrow = async (sql: string): Promise<void> => {
|
|
1745
|
+
const result = await runAsyncSqlite(sql);
|
|
1746
|
+
if (!result.ok) {
|
|
1747
|
+
throw new Error(
|
|
1748
|
+
`clearAll: \`${sql}\` failed (${result.backend}): ${result.error ?? "unknown"}`,
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
|
|
1713
1753
|
// Delete in dependency order. Cascades handle memory_segments and
|
|
1714
1754
|
// tool_invocations, but we explicitly clear non-cascading memory
|
|
1715
1755
|
// tables too.
|
|
@@ -1719,22 +1759,21 @@ export function clearAll(): { conversations: number; messages: number } {
|
|
|
1719
1759
|
// triggers so that the subsequent base-table DELETEs don't also fail
|
|
1720
1760
|
// (SQLite triggers are atomic with the triggering statement, so a
|
|
1721
1761
|
// corrupted FTS table would roll back every base-table DELETE).
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1762
|
+
await runOrThrow("DELETE FROM memory_segments");
|
|
1763
|
+
await runOrThrow("DELETE FROM memory_summaries");
|
|
1764
|
+
await runOrThrow("DELETE FROM memory_embeddings");
|
|
1765
|
+
await runOrThrow("DELETE FROM memory_jobs");
|
|
1766
|
+
await runOrThrow("DELETE FROM memory_checkpoints");
|
|
1767
|
+
await runOrThrow("DELETE FROM llm_request_logs");
|
|
1768
|
+
await runOrThrow("DELETE FROM llm_usage_events");
|
|
1769
|
+
await runOrThrow("DELETE FROM message_attachments");
|
|
1770
|
+
await runOrThrow("DELETE FROM attachments");
|
|
1771
|
+
await runOrThrow("DELETE FROM tool_invocations");
|
|
1732
1772
|
let messagesFtsCorrupted = false;
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
} catch (err) {
|
|
1773
|
+
const ftsResult = await runAsyncSqlite("DELETE FROM messages_fts");
|
|
1774
|
+
if (!ftsResult.ok) {
|
|
1736
1775
|
log.warn(
|
|
1737
|
-
{
|
|
1776
|
+
{ error: ftsResult.error, backend: ftsResult.backend },
|
|
1738
1777
|
"clearAll: failed to clear messages_fts — dropping triggers so base-table cleanup can proceed",
|
|
1739
1778
|
);
|
|
1740
1779
|
rawExec("DROP TRIGGER IF EXISTS messages_fts_ai");
|
|
@@ -1742,8 +1781,8 @@ export function clearAll(): { conversations: number; messages: number } {
|
|
|
1742
1781
|
rawExec("DROP TRIGGER IF EXISTS messages_fts_au");
|
|
1743
1782
|
messagesFtsCorrupted = true;
|
|
1744
1783
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1784
|
+
await runOrThrow("DELETE FROM messages");
|
|
1785
|
+
await runOrThrow("DELETE FROM conversations");
|
|
1747
1786
|
|
|
1748
1787
|
// Record audit event — lifecycle_events is NOT deleted by clearAll(),
|
|
1749
1788
|
// so this survives the wipe and provides a permanent trail.
|
|
@@ -1910,44 +1949,6 @@ export function updateMessageMetadata(
|
|
|
1910
1949
|
.run();
|
|
1911
1950
|
}
|
|
1912
1951
|
|
|
1913
|
-
/**
|
|
1914
|
-
* Bulk-remove the metadata fields that back the blocks stripped by
|
|
1915
|
-
* `stripInjectionsForCompaction` — currently `pkbSystemReminderBlock`
|
|
1916
|
-
* (`<system_reminder>`), `nowScratchpadBlock` (`<NOW.md …>`),
|
|
1917
|
-
* `pkbContextBlock` (`<knowledge_base>`), and `memoryV2StaticBlock`
|
|
1918
|
-
* (the static `<memory>\n…</memory>` block matched by the `<memory>\n`
|
|
1919
|
-
* prefix in `RUNTIME_INJECTION_PREFIXES`). Called from compaction-strip
|
|
1920
|
-
* sites so post-restart rehydration stays consistent with the in-memory
|
|
1921
|
-
* state produced by `stripInjectionsForCompaction` (which removes those
|
|
1922
|
-
* tags from live messages but cannot touch the DB). Fields backing
|
|
1923
|
-
* blocks that are intentionally NOT stripped (`turnContextBlock`,
|
|
1924
|
-
* `workspaceBlock`, `memoryInjectedBlock`) are preserved.
|
|
1925
|
-
*/
|
|
1926
|
-
export function clearStrippedInjectionMetadataForConversation(
|
|
1927
|
-
conversationId: string,
|
|
1928
|
-
): void {
|
|
1929
|
-
rawRun(
|
|
1930
|
-
`UPDATE messages
|
|
1931
|
-
SET metadata = json_remove(
|
|
1932
|
-
metadata,
|
|
1933
|
-
'$.pkbSystemReminderBlock',
|
|
1934
|
-
'$.nowScratchpadBlock',
|
|
1935
|
-
'$.pkbContextBlock',
|
|
1936
|
-
'$.memoryV2StaticBlock'
|
|
1937
|
-
)
|
|
1938
|
-
WHERE conversation_id = ?
|
|
1939
|
-
AND role = 'user'
|
|
1940
|
-
AND metadata IS NOT NULL
|
|
1941
|
-
AND (
|
|
1942
|
-
json_extract(metadata, '$.pkbSystemReminderBlock') IS NOT NULL
|
|
1943
|
-
OR json_extract(metadata, '$.nowScratchpadBlock') IS NOT NULL
|
|
1944
|
-
OR json_extract(metadata, '$.pkbContextBlock') IS NOT NULL
|
|
1945
|
-
OR json_extract(metadata, '$.memoryV2StaticBlock') IS NOT NULL
|
|
1946
|
-
)`,
|
|
1947
|
-
conversationId,
|
|
1948
|
-
);
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
1952
|
/**
|
|
1952
1953
|
* Atomically update both `content` and (shallow-merged) `metadata` for a
|
|
1953
1954
|
* message. Used by edit-propagation paths that need to update the message
|