@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
|
@@ -9,7 +9,7 @@ import { and, eq, inArray, sql } from "drizzle-orm";
|
|
|
9
9
|
|
|
10
10
|
import { getLogger } from "../util/logger.js";
|
|
11
11
|
import { getDb } from "./db-connection.js";
|
|
12
|
-
import { enqueueMemoryJob } from "./jobs-store.js";
|
|
12
|
+
import { enqueueMemoryJob, isMemoryEnabled } from "./jobs-store.js";
|
|
13
13
|
import { rawGet } from "./raw-query.js";
|
|
14
14
|
import { memoryCheckpoints, memoryJobs } from "./schema.js";
|
|
15
15
|
|
|
@@ -20,6 +20,8 @@ const log = getLogger("conversation-starters-cadence");
|
|
|
20
20
|
* generating a fresh batch of conversation starters.
|
|
21
21
|
*/
|
|
22
22
|
export function maybeEnqueueConversationStartersJob(scopeId: string): void {
|
|
23
|
+
if (!isMemoryEnabled()) return;
|
|
24
|
+
|
|
23
25
|
const db = getDb();
|
|
24
26
|
|
|
25
27
|
// Count total active memory items
|
|
@@ -331,10 +331,10 @@ function buildTitlePrompt(
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
if (userMessage) {
|
|
334
|
-
parts.push(`User: ${userMessage}`);
|
|
334
|
+
parts.push(`User: ${stripThinkingTags(userMessage)}`);
|
|
335
335
|
}
|
|
336
336
|
if (assistantResponse) {
|
|
337
|
-
parts.push(`Assistant: ${assistantResponse}`);
|
|
337
|
+
parts.push(`Assistant: ${stripThinkingTags(assistantResponse)}`);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
return parts.join("\n");
|
|
@@ -378,12 +378,28 @@ function truncateTitle(title: string): string {
|
|
|
378
378
|
function normalizeTitle(raw: string): string {
|
|
379
379
|
let title = raw.trim().replace(/^["']|["']$/g, "");
|
|
380
380
|
title = stripMarkdown(title);
|
|
381
|
+
title = stripThinkingTags(title);
|
|
381
382
|
if (META_FAILURE_TITLES.has(title.toLowerCase())) {
|
|
382
383
|
return "";
|
|
383
384
|
}
|
|
384
385
|
return truncateTitle(title);
|
|
385
386
|
}
|
|
386
387
|
|
|
388
|
+
/** Strip thinking tags so they don't bleed into generated titles. */
|
|
389
|
+
function stripThinkingTags(text: string): string {
|
|
390
|
+
return text
|
|
391
|
+
.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "")
|
|
392
|
+
.replace(/<thought>[\s\S]*?<\/thought>/gi, "")
|
|
393
|
+
.replace(/<think>[\s\S]*?<\/think>/gi, "")
|
|
394
|
+
.replace(/<thought>/gi, "")
|
|
395
|
+
.replace(/<\/thought>/gi, "")
|
|
396
|
+
.replace(/<thinking>/gi, "")
|
|
397
|
+
.replace(/<\/thinking>/gi, "")
|
|
398
|
+
.replace(/<think>/gi, "")
|
|
399
|
+
.replace(/<\/think>/gi, "")
|
|
400
|
+
.replace(/<:[^>]*>/gi, "");
|
|
401
|
+
}
|
|
402
|
+
|
|
387
403
|
/** Strip common markdown formatting so titles render as plain text. */
|
|
388
404
|
function stripMarkdown(text: string): string {
|
|
389
405
|
return text
|
|
@@ -458,7 +474,7 @@ function buildRegenerationPrompt(recentMessages: MessageRow[]): string {
|
|
|
458
474
|
const text = extractTextForTitle(msg.content);
|
|
459
475
|
if (!text) continue;
|
|
460
476
|
const role = msg.role === "user" ? "User" : "Assistant";
|
|
461
|
-
parts.push(`${role}: ${text}`);
|
|
477
|
+
parts.push(`${role}: ${stripThinkingTags(text)}`);
|
|
462
478
|
}
|
|
463
479
|
|
|
464
480
|
return parts.join("\n");
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run a SQL statement asynchronously, without blocking the daemon's
|
|
3
|
+
* main event loop.
|
|
4
|
+
*
|
|
5
|
+
* `bun:sqlite` is synchronous, so any long-running statement on the
|
|
6
|
+
* shared in-process connection stalls the event loop for the full
|
|
7
|
+
* duration of the statement. For multi-minute operations like `VACUUM`
|
|
8
|
+
* on a multi-GB database, this stalls every other piece of I/O —
|
|
9
|
+
* including the healthz handler — and on platform that has been
|
|
10
|
+
* observed to fail liveness probes and crashloop the pod.
|
|
11
|
+
*
|
|
12
|
+
* Backend selection:
|
|
13
|
+
* 1. **`sqlite3` CLI subprocess (preferred).** Spawn a child process
|
|
14
|
+
* that opens its own SQLite connection, runs the statement, and
|
|
15
|
+
* exits. The daemon's event loop is free for the full duration.
|
|
16
|
+
* SQLite's own file-locking arbitrates between the subprocess and
|
|
17
|
+
* the still-running in-process connection.
|
|
18
|
+
* 2. **In-process `bun:sqlite` (fallback).** Synchronous, blocking.
|
|
19
|
+
* Only fires when no `sqlite3` binary is on the host. This is the
|
|
20
|
+
* same behavior the daemon had before this abstraction existed,
|
|
21
|
+
* and is acceptable on desktop where no liveness probe is going to
|
|
22
|
+
* kill the process for a long stall.
|
|
23
|
+
*
|
|
24
|
+
* Use this for statements known to be slow (`VACUUM`, `ANALYZE`,
|
|
25
|
+
* `PRAGMA optimize`, large bulk `DELETE`/`UPDATE`). Fast queries (a few
|
|
26
|
+
* ms) should keep using the in-process drizzle / `bun:sqlite` handle
|
|
27
|
+
* directly — the subprocess overhead would dominate.
|
|
28
|
+
*/
|
|
29
|
+
import { getLogger } from "../util/logger.js";
|
|
30
|
+
import { getDbPath } from "../util/platform.js";
|
|
31
|
+
import { findSqlite3 } from "../util/sqlite3-runtime.js";
|
|
32
|
+
import { getSqlite } from "./db-connection.js";
|
|
33
|
+
|
|
34
|
+
const log = getLogger("db-async-query");
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Default wall-clock cap for an async statement. A real `VACUUM` on a
|
|
38
|
+
* multi-GB database can legitimately take many minutes; the cap is
|
|
39
|
+
* here to bound a runaway subprocess (e.g. one stuck on a stale file
|
|
40
|
+
* lock). Override per call via `runAsyncSqlite(sql, { timeoutMs })`.
|
|
41
|
+
*/
|
|
42
|
+
const DEFAULT_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
|
|
43
|
+
|
|
44
|
+
export type AsyncSqliteBackend = "sqlite3-cli" | "in-process-blocking";
|
|
45
|
+
|
|
46
|
+
export interface AsyncSqliteResult {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
backend: AsyncSqliteBackend;
|
|
49
|
+
error: string | null;
|
|
50
|
+
elapsedMs: number;
|
|
51
|
+
stdout?: string;
|
|
52
|
+
stderr?: string;
|
|
53
|
+
timedOut?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface RunAsyncSqliteOptions {
|
|
57
|
+
/** Override the default 1 h subprocess timeout. */
|
|
58
|
+
timeoutMs?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Force a specific backend. Test-only; production callers should let
|
|
61
|
+
* the runtime pick.
|
|
62
|
+
*/
|
|
63
|
+
forceBackend?: AsyncSqliteBackend;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let warnedAboutFallback = false;
|
|
67
|
+
|
|
68
|
+
export async function runAsyncSqlite(
|
|
69
|
+
sql: string,
|
|
70
|
+
options: RunAsyncSqliteOptions = {},
|
|
71
|
+
): Promise<AsyncSqliteResult> {
|
|
72
|
+
const forced = options.forceBackend;
|
|
73
|
+
const sqlite3Path =
|
|
74
|
+
forced === "in-process-blocking" ? undefined : findSqlite3();
|
|
75
|
+
|
|
76
|
+
if (sqlite3Path && forced !== "in-process-blocking") {
|
|
77
|
+
return runViaCli(sqlite3Path, sql, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!warnedAboutFallback) {
|
|
81
|
+
warnedAboutFallback = true;
|
|
82
|
+
log.warn(
|
|
83
|
+
"No sqlite3 CLI found on host — falling back to in-process blocking " +
|
|
84
|
+
"execution for slow SQLite statements. Install sqlite3 to keep the " +
|
|
85
|
+
"event loop responsive during VACUUM and other long operations.",
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return runInProcessBlocking(sql);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** For tests: reset the once-only fallback warning. */
|
|
92
|
+
export function _resetFallbackWarning(): void {
|
|
93
|
+
warnedAboutFallback = false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function runViaCli(
|
|
97
|
+
sqlite3Path: string,
|
|
98
|
+
sql: string,
|
|
99
|
+
timeoutMs: number,
|
|
100
|
+
): Promise<AsyncSqliteResult> {
|
|
101
|
+
const startMs = Date.now();
|
|
102
|
+
const dbPath = getDbPath();
|
|
103
|
+
|
|
104
|
+
log.info(
|
|
105
|
+
{ sqlite3Path, dbPath, timeoutMs, sqlPreview: sql.slice(0, 80) },
|
|
106
|
+
"Running async SQL via sqlite3 CLI subprocess",
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Pass the SQL via stdin rather than -cmd so newlines and quoting are
|
|
110
|
+
// never an issue regardless of the statement complexity.
|
|
111
|
+
const proc = Bun.spawn({
|
|
112
|
+
cmd: [sqlite3Path, dbPath],
|
|
113
|
+
stdin: "pipe",
|
|
114
|
+
stdout: "pipe",
|
|
115
|
+
stderr: "pipe",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Write the SQL and close stdin so sqlite3 sees EOF and exits.
|
|
119
|
+
proc.stdin.write(sql + "\n");
|
|
120
|
+
await proc.stdin.end();
|
|
121
|
+
|
|
122
|
+
// Begin draining the streams immediately so the subprocess never
|
|
123
|
+
// blocks on a full pipe buffer.
|
|
124
|
+
const stdoutPromise = new Response(proc.stdout).text();
|
|
125
|
+
const stderrPromise = new Response(proc.stderr).text();
|
|
126
|
+
|
|
127
|
+
let timedOut = false;
|
|
128
|
+
const timer = setTimeout(() => {
|
|
129
|
+
timedOut = true;
|
|
130
|
+
proc.kill("SIGKILL");
|
|
131
|
+
}, timeoutMs);
|
|
132
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
133
|
+
|
|
134
|
+
const exitCode = await proc.exited;
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
|
|
137
|
+
const stdout = await stdoutPromise;
|
|
138
|
+
const stderr = await stderrPromise;
|
|
139
|
+
const elapsedMs = Date.now() - startMs;
|
|
140
|
+
|
|
141
|
+
if (timedOut) {
|
|
142
|
+
log.error(
|
|
143
|
+
{ timeoutMs, elapsedMs, stderr: stderr.slice(0, 2000) },
|
|
144
|
+
"Async SQL subprocess timed out — killed",
|
|
145
|
+
);
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
backend: "sqlite3-cli",
|
|
149
|
+
error: `sqlite3 subprocess timed out after ${timeoutMs}ms`,
|
|
150
|
+
elapsedMs,
|
|
151
|
+
stdout,
|
|
152
|
+
stderr,
|
|
153
|
+
timedOut: true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (exitCode !== 0) {
|
|
157
|
+
log.error(
|
|
158
|
+
{ exitCode, elapsedMs, stderr: stderr.slice(0, 2000) },
|
|
159
|
+
"Async SQL subprocess failed",
|
|
160
|
+
);
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
backend: "sqlite3-cli",
|
|
164
|
+
error: `sqlite3 exited with code ${exitCode}: ${stderr.slice(0, 500)}`,
|
|
165
|
+
elapsedMs,
|
|
166
|
+
stdout,
|
|
167
|
+
stderr,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
ok: true,
|
|
172
|
+
backend: "sqlite3-cli",
|
|
173
|
+
error: null,
|
|
174
|
+
elapsedMs,
|
|
175
|
+
stdout,
|
|
176
|
+
stderr,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function runInProcessBlocking(sql: string): Promise<AsyncSqliteResult> {
|
|
181
|
+
const startMs = Date.now();
|
|
182
|
+
try {
|
|
183
|
+
const sqlite = getSqlite();
|
|
184
|
+
sqlite.exec(sql);
|
|
185
|
+
// Synthesize `stdout` to match what the CLI backend would emit
|
|
186
|
+
// when the caller chained `SELECT changes();` at the end of their
|
|
187
|
+
// SQL. `bun:sqlite`'s `exec()` discards SELECT results, so without
|
|
188
|
+
// this synthesis, callers that read `stdout` to get the row count
|
|
189
|
+
// (the prune jobs in cleanup.ts, for one) would see `undefined`
|
|
190
|
+
// and treat the run as "no rows deleted" — silently dropping the
|
|
191
|
+
// re-enqueue gate on every fallback host. Captured atomically with
|
|
192
|
+
// exec (same synchronous slice — no other code can run between
|
|
193
|
+
// these two lines), so the count is accurate for the SQL we just
|
|
194
|
+
// ran. Harmless for callers that don't read stdout.
|
|
195
|
+
const changes = (
|
|
196
|
+
sqlite.query("SELECT changes() AS c").get() as { c: number }
|
|
197
|
+
).c;
|
|
198
|
+
return {
|
|
199
|
+
ok: true,
|
|
200
|
+
backend: "in-process-blocking",
|
|
201
|
+
error: null,
|
|
202
|
+
elapsedMs: Date.now() - startMs,
|
|
203
|
+
stdout: `${changes}\n`,
|
|
204
|
+
};
|
|
205
|
+
} catch (err) {
|
|
206
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
207
|
+
return {
|
|
208
|
+
ok: false,
|
|
209
|
+
backend: "in-process-blocking",
|
|
210
|
+
error: message,
|
|
211
|
+
elapsedMs: Date.now() - startMs,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
package/src/memory/db-init.ts
CHANGED
|
@@ -118,9 +118,11 @@ import {
|
|
|
118
118
|
migrateInviteCodeHashColumn,
|
|
119
119
|
migrateInviteContactId,
|
|
120
120
|
migrateLlmRequestLogAgentLoopExitReason,
|
|
121
|
+
migrateLlmRequestLogCallSite,
|
|
121
122
|
migrateLlmRequestLogMessageId,
|
|
122
123
|
migrateLlmRequestLogProvider,
|
|
123
124
|
migrateLlmRequestLogsCreatedAtIndex,
|
|
125
|
+
migrateLlmUsageAddRawUsage,
|
|
124
126
|
migrateLlmUsageAttribution,
|
|
125
127
|
migrateMemoryGraphImageRefs,
|
|
126
128
|
migrateMemoryItemSupersession,
|
|
@@ -128,6 +130,8 @@ import {
|
|
|
128
130
|
migrateMemoryRetrospectiveState,
|
|
129
131
|
migrateMemoryV2ActivationLogs,
|
|
130
132
|
migrateMemoryV2InjectionEvents,
|
|
133
|
+
migrateMemoryV3AutoEdges,
|
|
134
|
+
migrateMemoryV3Coactivation,
|
|
131
135
|
migrateMessageBookmarks,
|
|
132
136
|
migrateMessagesConversationCreatedAtIndex,
|
|
133
137
|
migrateMessagesFtsBackfill,
|
|
@@ -155,6 +159,7 @@ import {
|
|
|
155
159
|
migrateProviderConnectionStatusLabel,
|
|
156
160
|
migrateReminderRoutingIntent,
|
|
157
161
|
migrateRemindersToSchedules,
|
|
162
|
+
migrateRenameCleanedAt,
|
|
158
163
|
migrateRenameConversationTypeColumn,
|
|
159
164
|
migrateRenameCreatedBySessionIdColumns,
|
|
160
165
|
migrateRenameFollowupsThreadIdColumn,
|
|
@@ -452,6 +457,11 @@ export function initializeDb(): void {
|
|
|
452
457
|
migrateStripBaseUrlNonOpenaiCompatible,
|
|
453
458
|
migrateOnboardingEventsPriorAssistants,
|
|
454
459
|
migrateConversationCleanedAt,
|
|
460
|
+
migrateRenameCleanedAt,
|
|
461
|
+
migrateLlmUsageAddRawUsage,
|
|
462
|
+
migrateMemoryV3Coactivation,
|
|
463
|
+
migrateMemoryV3AutoEdges,
|
|
464
|
+
migrateLlmRequestLogCallSite,
|
|
455
465
|
];
|
|
456
466
|
|
|
457
467
|
// Run each migration step, catching and logging individual failures so one
|
|
@@ -3,6 +3,7 @@ import { statSync } from "node:fs";
|
|
|
3
3
|
import { getLogger } from "../util/logger.js";
|
|
4
4
|
import { getDbPath } from "../util/platform.js";
|
|
5
5
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
|
|
6
|
+
import { runAsyncSqlite } from "./db-async-query.js";
|
|
6
7
|
import { getSqlite } from "./db-connection.js";
|
|
7
8
|
|
|
8
9
|
const log = getLogger("db-maintenance");
|
|
@@ -11,7 +12,7 @@ const DB_MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
|
|
|
11
12
|
const DB_MAINTENANCE_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
12
13
|
|
|
13
14
|
interface DbStats {
|
|
14
|
-
|
|
15
|
+
pageSize: number;
|
|
15
16
|
pageCount: number;
|
|
16
17
|
freelistCount: number;
|
|
17
18
|
fileSizeBytes: number | null;
|
|
@@ -19,7 +20,7 @@ interface DbStats {
|
|
|
19
20
|
|
|
20
21
|
function getDbStats(): DbStats {
|
|
21
22
|
const sqlite = getSqlite();
|
|
22
|
-
const
|
|
23
|
+
const pageSize = (
|
|
23
24
|
sqlite.query("PRAGMA page_size").get() as { page_size: number }
|
|
24
25
|
).page_size;
|
|
25
26
|
const pageCount = (
|
|
@@ -34,10 +35,10 @@ function getDbStats(): DbStats {
|
|
|
34
35
|
} catch {
|
|
35
36
|
/* non-fatal */
|
|
36
37
|
}
|
|
37
|
-
return {
|
|
38
|
+
return { pageSize, pageCount, freelistCount, fileSizeBytes };
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
function runDbMaintenance(): void {
|
|
41
|
+
async function runDbMaintenance(): Promise<void> {
|
|
41
42
|
const before = getDbStats();
|
|
42
43
|
const freelistPct =
|
|
43
44
|
before.pageCount > 0
|
|
@@ -54,22 +55,24 @@ function runDbMaintenance(): void {
|
|
|
54
55
|
"Starting database maintenance",
|
|
55
56
|
);
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
// VACUUM is the long-running one — minutes on a multi-GB DB. PRAGMA
|
|
59
|
+
// optimize is fast but routed through the same async path for
|
|
60
|
+
// consistency and to keep both off the main thread when the CLI
|
|
61
|
+
// backend is available.
|
|
62
|
+
const vacuumResult = await runAsyncSqlite("VACUUM");
|
|
63
|
+
if (!vacuumResult.ok) {
|
|
64
|
+
log.warn(
|
|
65
|
+
{ error: vacuumResult.error, backend: vacuumResult.backend },
|
|
66
|
+
"VACUUM failed (non-fatal)",
|
|
67
|
+
);
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const optimizeResult = await runAsyncSqlite("PRAGMA optimize");
|
|
71
|
+
if (!optimizeResult.ok) {
|
|
72
|
+
log.warn(
|
|
73
|
+
{ error: optimizeResult.error, backend: optimizeResult.backend },
|
|
74
|
+
"PRAGMA optimize failed (non-fatal)",
|
|
75
|
+
);
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
const after = getDbStats();
|
|
@@ -81,17 +84,23 @@ function runDbMaintenance(): void {
|
|
|
81
84
|
|
|
82
85
|
log.info(
|
|
83
86
|
{
|
|
87
|
+
backend: vacuumResult.backend,
|
|
88
|
+
vacuumOk: vacuumResult.ok,
|
|
89
|
+
optimizeOk: optimizeResult.ok,
|
|
90
|
+
vacuumElapsedMs: vacuumResult.elapsedMs,
|
|
91
|
+
optimizeElapsedMs: optimizeResult.elapsedMs,
|
|
84
92
|
beforePageCount: before.pageCount,
|
|
85
93
|
afterPageCount: after.pageCount,
|
|
86
94
|
reclaimedPages,
|
|
87
|
-
|
|
95
|
+
beforeFileSizeBytes: before.fileSizeBytes,
|
|
88
96
|
afterFileSizeBytes: after.fileSizeBytes,
|
|
97
|
+
reclaimedBytes,
|
|
89
98
|
},
|
|
90
99
|
"Database maintenance complete",
|
|
91
100
|
);
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
export function maybeRunDbMaintenance(nowMs = Date.now()): void {
|
|
103
|
+
export async function maybeRunDbMaintenance(nowMs = Date.now()): Promise<void> {
|
|
95
104
|
const lastRun = parseInt(
|
|
96
105
|
getMemoryCheckpoint(DB_MAINTENANCE_CHECKPOINT_KEY) ?? "0",
|
|
97
106
|
10,
|
|
@@ -99,7 +108,7 @@ export function maybeRunDbMaintenance(nowMs = Date.now()): void {
|
|
|
99
108
|
if (nowMs - lastRun < DB_MAINTENANCE_INTERVAL_MS) return;
|
|
100
109
|
|
|
101
110
|
try {
|
|
102
|
-
runDbMaintenance();
|
|
111
|
+
await runDbMaintenance();
|
|
103
112
|
} catch (err) {
|
|
104
113
|
log.error({ err }, "Database maintenance failed unexpectedly");
|
|
105
114
|
}
|
|
@@ -20,7 +20,11 @@ import { getLogger } from "../../util/logger.js";
|
|
|
20
20
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
21
21
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "../checkpoints.js";
|
|
22
22
|
import { getDb } from "../db-connection.js";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
enqueueMemoryJob,
|
|
25
|
+
hasActiveJobOfType,
|
|
26
|
+
isMemoryEnabled,
|
|
27
|
+
} from "../jobs-store.js";
|
|
24
28
|
import { initQdrantClient, resolveQdrantUrl } from "../qdrant-client.js";
|
|
25
29
|
import { rawAll, rawGet, rawRun } from "../raw-query.js";
|
|
26
30
|
import { conversations, memoryGraphNodes, memorySegments } from "../schema.js";
|
|
@@ -342,6 +346,8 @@ export function maybeEnqueueGraphBootstrap(): void {
|
|
|
342
346
|
// Don't enqueue if already in progress
|
|
343
347
|
if (hasActiveJobOfType("graph_bootstrap")) return;
|
|
344
348
|
|
|
349
|
+
if (!isMemoryEnabled()) return;
|
|
350
|
+
|
|
345
351
|
log.info(
|
|
346
352
|
{ segmentCount, hasJournalFiles },
|
|
347
353
|
"Graph empty with historical data — enqueueing bootstrap",
|
|
@@ -392,6 +398,7 @@ const KIND_TO_PREFIX: Record<string, string> = {
|
|
|
392
398
|
*/
|
|
393
399
|
export function migrateToolCreatedItems(): void {
|
|
394
400
|
if (getMemoryCheckpoint(MIGRATE_ITEMS_CHECKPOINT)) return;
|
|
401
|
+
if (!isMemoryEnabled()) return;
|
|
395
402
|
|
|
396
403
|
const kinds = Object.keys(KIND_TO_PREFIX);
|
|
397
404
|
const placeholders = kinds.map(() => "?").join(", ");
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "../../skills/skill-memory.js";
|
|
24
24
|
import { getLogger } from "../../util/logger.js";
|
|
25
25
|
import { getDb } from "../db-connection.js";
|
|
26
|
-
import { enqueueMemoryJob } from "../jobs-store.js";
|
|
26
|
+
import { enqueueMemoryJob, isMemoryEnabled } from "../jobs-store.js";
|
|
27
27
|
import { memoryGraphNodes } from "../schema.js";
|
|
28
28
|
import { createNode } from "./store.js";
|
|
29
29
|
|
|
@@ -268,7 +268,9 @@ function upsertCapabilityNode(sourceKey: string, content: string): void {
|
|
|
268
268
|
})
|
|
269
269
|
.where(eq(memoryGraphNodes.id, existing.id))
|
|
270
270
|
.run();
|
|
271
|
-
|
|
271
|
+
if (isMemoryEnabled()) {
|
|
272
|
+
enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
|
|
273
|
+
}
|
|
272
274
|
return;
|
|
273
275
|
}
|
|
274
276
|
|
|
@@ -301,7 +303,9 @@ function upsertCapabilityNode(sourceKey: string, content: string): void {
|
|
|
301
303
|
scopeId: "default",
|
|
302
304
|
});
|
|
303
305
|
|
|
304
|
-
|
|
306
|
+
if (isMemoryEnabled()) {
|
|
307
|
+
enqueueMemoryJob("embed_graph_node", { nodeId: node.id });
|
|
308
|
+
}
|
|
305
309
|
log.info({ sourceKey, nodeId: node.id }, "Created capability graph node");
|
|
306
310
|
}
|
|
307
311
|
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
type InjectMemoryV2Mode,
|
|
36
36
|
} from "../v2/injection.js";
|
|
37
37
|
import { loadNowText } from "../v2/now-text.js";
|
|
38
|
+
import type { RouterTurnPair } from "../v2/router.js";
|
|
38
39
|
import {
|
|
39
40
|
loadGraphMemoryState,
|
|
40
41
|
saveGraphMemoryState,
|
|
@@ -440,8 +441,11 @@ export class ConversationGraphMemory {
|
|
|
440
441
|
messages,
|
|
441
442
|
config,
|
|
442
443
|
"context-load",
|
|
444
|
+
// Context-load runs before the messages array necessarily contains
|
|
445
|
+
// the just-arrived user turn (post-compaction restore, turn 1
|
|
446
|
+
// sketch), so override with the resolved user text rather than
|
|
447
|
+
// walking back through `messages`.
|
|
443
448
|
rawUserText ?? userQuery ?? "",
|
|
444
|
-
"",
|
|
445
449
|
signal,
|
|
446
450
|
);
|
|
447
451
|
|
|
@@ -605,8 +609,7 @@ export class ConversationGraphMemory {
|
|
|
605
609
|
messages,
|
|
606
610
|
config,
|
|
607
611
|
"per-turn",
|
|
608
|
-
|
|
609
|
-
assistantLast,
|
|
612
|
+
null,
|
|
610
613
|
signal,
|
|
611
614
|
);
|
|
612
615
|
if (v2.routed) {
|
|
@@ -762,8 +765,14 @@ export class ConversationGraphMemory {
|
|
|
762
765
|
messages: Message[],
|
|
763
766
|
config: AssistantConfig,
|
|
764
767
|
mode: InjectMemoryV2Mode,
|
|
765
|
-
|
|
766
|
-
|
|
768
|
+
/**
|
|
769
|
+
* Override for the just-arrived user message text. Used by
|
|
770
|
+
* `runContextLoad` where the conversation history may not yet contain
|
|
771
|
+
* the user message that triggered the load (e.g. turn 1 / post-
|
|
772
|
+
* compaction restoration). When provided, the extracted pairs array
|
|
773
|
+
* is replaced with `[{ assistantMessage: "", userMessage: override }]`.
|
|
774
|
+
*/
|
|
775
|
+
userMessageOverride: string | null,
|
|
767
776
|
signal: AbortSignal,
|
|
768
777
|
): Promise<{
|
|
769
778
|
routed: boolean;
|
|
@@ -776,13 +785,17 @@ export class ConversationGraphMemory {
|
|
|
776
785
|
|
|
777
786
|
const nowText = await loadNowText(getWorkspaceDir());
|
|
778
787
|
const currentTurn = this.tracker.getTurn();
|
|
788
|
+
const historicalPairs = config.memory.v2.router.historical_pairs;
|
|
789
|
+
const recentTurnPairs =
|
|
790
|
+
userMessageOverride !== null
|
|
791
|
+
? [{ assistantMessage: "", userMessage: userMessageOverride }]
|
|
792
|
+
: extractRecentTurnPairs(messages, historicalPairs);
|
|
779
793
|
|
|
780
794
|
const result = await injectMemoryV2Block({
|
|
781
795
|
database: getDb(),
|
|
782
796
|
conversationId: this.conversationId,
|
|
783
797
|
currentTurn,
|
|
784
|
-
|
|
785
|
-
assistantMessage,
|
|
798
|
+
recentTurnPairs,
|
|
786
799
|
nowText,
|
|
787
800
|
messageId: `${this.conversationId}:turn:${currentTurn}`,
|
|
788
801
|
mode,
|
|
@@ -808,16 +821,22 @@ export class ConversationGraphMemory {
|
|
|
808
821
|
|
|
809
822
|
/**
|
|
810
823
|
* Count the leading content blocks on a user message that were added by
|
|
811
|
-
* `injectMemoryBlock
|
|
812
|
-
* (opening `<memory_image>` text + image +
|
|
813
|
-
* followed by a `<memory>…</memory>` text
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
824
|
+
* `injectMemoryBlock` or the `memory-v2-static` injector. Memory-injected
|
|
825
|
+
* images use a 3-block pattern (opening `<memory_image>` text + image +
|
|
826
|
+
* closing `</memory_image>` text), followed by a `<memory>…</memory>` text
|
|
827
|
+
* block (legacy `<memory __injected>` is also accepted). The static
|
|
828
|
+
* memory-v2 block uses `<info>…</info>` and is also counted here so that
|
|
829
|
+
* `after-memory-prefix` splices for subsequent injectors (e.g. `now-md`)
|
|
830
|
+
* land after both the dynamic and static blocks.
|
|
831
|
+
*
|
|
832
|
+
* The bare `<memory>` and `<info>` forms are matched only when the block
|
|
833
|
+
* also ends with the corresponding closing tag, so user-authored content
|
|
834
|
+
* that happens to begin with `<memory>` or `<info>` (for example, a
|
|
835
|
+
* message discussing the XML-like markup) is not mistaken for an injected
|
|
836
|
+
* prefix and stripped on re-injection. A legacy 2-block image pattern (no
|
|
837
|
+
* closing tag) is also accepted for backward compatibility. The injection
|
|
838
|
+
* prefix is always contiguous at the start, so we stop at the first
|
|
839
|
+
* non-memory block.
|
|
821
840
|
*/
|
|
822
841
|
export function countMemoryPrefixBlocks(content: ContentBlock[]): number {
|
|
823
842
|
let firstNonMemory = 0;
|
|
@@ -829,6 +848,8 @@ export function countMemoryPrefixBlocks(content: ContentBlock[]): number {
|
|
|
829
848
|
block.type === "text" &&
|
|
830
849
|
((block.text.startsWith("<memory>\n") &&
|
|
831
850
|
block.text.endsWith("\n</memory>")) ||
|
|
851
|
+
(block.text.startsWith("<info>\n") &&
|
|
852
|
+
block.text.endsWith("\n</info>")) ||
|
|
832
853
|
block.text.startsWith("<memory __injected>\n"))
|
|
833
854
|
) {
|
|
834
855
|
firstNonMemory++;
|
|
@@ -991,3 +1012,65 @@ function readRawUserText(message: Message | undefined): string | null {
|
|
|
991
1012
|
if (texts.length === 0) return null;
|
|
992
1013
|
return texts.join(" ");
|
|
993
1014
|
}
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Walk back through the conversation history and collect the most recent
|
|
1018
|
+
* `K` `(assistant, user)` turn pairs for the router prompt. Each pair
|
|
1019
|
+
* represents the assistant's reply followed by the user message that
|
|
1020
|
+
* came after — the last pair's `userMessage` is the just-arrived turn
|
|
1021
|
+
* that triggered this call.
|
|
1022
|
+
*
|
|
1023
|
+
* Behavior at K=1 is bit-identical to the pre-knob signature: one pair
|
|
1024
|
+
* with the prior assistant reply + the just-arrived user message.
|
|
1025
|
+
*
|
|
1026
|
+
* Edge cases:
|
|
1027
|
+
* - If history has fewer than K full pairs available (e.g. early in a
|
|
1028
|
+
* conversation), returns however many pairs were found, oldest first.
|
|
1029
|
+
* The oldest pair may have `assistantMessage: ""` when there is a
|
|
1030
|
+
* user message with no preceding assistant reply — `runRouterBatch`
|
|
1031
|
+
* skips the `[assistant]:` line in that case.
|
|
1032
|
+
* - Non-text content (tool_use, tool_result, images) is collapsed by
|
|
1033
|
+
* joining all text blocks within a single message with spaces. This
|
|
1034
|
+
* matches the v1-style extraction the router has used since K=1.
|
|
1035
|
+
*/
|
|
1036
|
+
function extractRecentTurnPairs(
|
|
1037
|
+
messages: Message[],
|
|
1038
|
+
k: number,
|
|
1039
|
+
): RouterTurnPair[] {
|
|
1040
|
+
const messageText = (msg: Message): string =>
|
|
1041
|
+
msg.content
|
|
1042
|
+
.filter(
|
|
1043
|
+
(b): b is Extract<typeof b, { type: "text" }> => b.type === "text",
|
|
1044
|
+
)
|
|
1045
|
+
.map((b) => b.text)
|
|
1046
|
+
.join(" ");
|
|
1047
|
+
|
|
1048
|
+
const pairs: RouterTurnPair[] = [];
|
|
1049
|
+
let pendingUser: string | null = null;
|
|
1050
|
+
for (let i = messages.length - 1; i >= 0 && pairs.length < k; i--) {
|
|
1051
|
+
const msg = messages[i];
|
|
1052
|
+
if (msg.role === "user" && pendingUser === null) {
|
|
1053
|
+
pendingUser = messageText(msg);
|
|
1054
|
+
} else if (msg.role === "assistant" && pendingUser !== null) {
|
|
1055
|
+
pairs.unshift({
|
|
1056
|
+
assistantMessage: messageText(msg),
|
|
1057
|
+
userMessage: pendingUser,
|
|
1058
|
+
});
|
|
1059
|
+
pendingUser = null;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
// Conversation start: a user message with no preceding assistant reply
|
|
1063
|
+
// still belongs in the prompt as the just-arrived turn. Emit it with an
|
|
1064
|
+
// empty `assistantMessage` so `runRouterBatch` renders only `[user]:`.
|
|
1065
|
+
if (pendingUser !== null && pairs.length < k) {
|
|
1066
|
+
pairs.unshift({ assistantMessage: "", userMessage: pendingUser });
|
|
1067
|
+
}
|
|
1068
|
+
// Defensive fallback: the router contract requires a non-empty array.
|
|
1069
|
+
// This only fires when `messages` has no user-text content at all
|
|
1070
|
+
// (currently impossible since the agent loop always appends a user
|
|
1071
|
+
// turn before invoking the v2 path, but cheap to keep correct).
|
|
1072
|
+
if (pairs.length === 0) {
|
|
1073
|
+
pairs.push({ assistantMessage: "", userMessage: "" });
|
|
1074
|
+
}
|
|
1075
|
+
return pairs;
|
|
1076
|
+
}
|