@vellumai/assistant 0.8.6 → 0.8.7
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/AGENTS.md +4 -4
- package/Dockerfile +1 -0
- package/bun.lock +11 -2
- package/docker-entrypoint.sh +8 -6
- package/docs/plugins.md +63 -28
- package/examples/plugins/echo/register.ts +4 -7
- package/knip.json +1 -0
- package/node_modules/@vellumai/environments/bun.lock +24 -0
- package/node_modules/@vellumai/environments/package.json +18 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
- package/node_modules/@vellumai/environments/src/index.ts +11 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
- package/node_modules/@vellumai/environments/src/types.ts +70 -0
- package/node_modules/@vellumai/environments/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +6 -2
- package/openapi.yaml +3735 -353
- package/package.json +7 -3
- package/scripts/generate-openapi.ts +20 -13
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +240 -39
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
- package/src/__tests__/agent-loop-thinking.test.ts +17 -12
- package/src/__tests__/agent-loop.test.ts +207 -341
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +4 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
- package/src/__tests__/anthropic-provider.test.ts +201 -55
- package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
- package/src/__tests__/app-control-flow.test.ts +5 -0
- package/src/__tests__/approval-cascade.test.ts +4 -11
- package/src/__tests__/approval-routes-http.test.ts +4 -2
- package/src/__tests__/assistant-event.test.ts +15 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +7 -37
- package/src/__tests__/avatar-generator.test.ts +12 -42
- package/src/__tests__/avatar-identity-sync.test.ts +28 -3
- package/src/__tests__/background-shell-bash.test.ts +3 -7
- package/src/__tests__/btw-routes.test.ts +7 -12
- package/src/__tests__/call-pointer-messages.test.ts +5 -3
- package/src/__tests__/call-site-routing-provider.test.ts +22 -40
- package/src/__tests__/catalog-files.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +48 -20
- package/src/__tests__/channel-approvals.test.ts +3 -1
- package/src/__tests__/channel-invite-transport.test.ts +1 -5
- package/src/__tests__/channel-readiness-routes.test.ts +0 -4
- package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
- package/src/__tests__/channel-retry-sweep.test.ts +71 -79
- package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
- package/src/__tests__/clawhub-files.test.ts +1 -0
- package/src/__tests__/compaction-events.test.ts +5 -17
- package/src/__tests__/compaction-pipeline.test.ts +1 -1
- package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
- package/src/__tests__/compaction-trail-store.test.ts +1 -79
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/computer-use-tools.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +34 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +14 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +12 -27
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +430 -90
- package/src/__tests__/conversation-agent-loop.test.ts +581 -62
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clear-safety.test.ts +20 -10
- package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-disk-view.test.ts +10 -17
- package/src/__tests__/conversation-fork-crud.test.ts +86 -172
- package/src/__tests__/conversation-fork-route.test.ts +16 -14
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +34 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -0
- package/src/__tests__/conversation-process-callsite.test.ts +27 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +53 -44
- package/src/__tests__/conversation-queue.test.ts +270 -164
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -2
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +20 -22
- package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
- package/src/__tests__/conversation-slash-queue.test.ts +37 -31
- package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
- package/src/__tests__/conversation-speed-override.test.ts +8 -22
- package/src/__tests__/conversation-stream-state.test.ts +484 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +6 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
- package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +3 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +48 -22
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +27 -7
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/cross-provider-web-search.test.ts +6 -2
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
- package/src/__tests__/disk-pressure-guard.test.ts +66 -0
- package/src/__tests__/disk-pressure-routes.test.ts +9 -2
- package/src/__tests__/dm-persistence.test.ts +7 -2
- package/src/__tests__/dynamic-page-surface.test.ts +68 -0
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-pipeline.test.ts +127 -5
- package/src/__tests__/filing-service.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +55 -14
- package/src/__tests__/gemini-inline-media.test.ts +78 -0
- package/src/__tests__/gemini-provider.test.ts +351 -28
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +9 -7
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -1
- package/src/__tests__/history-repair-hook.test.ts +161 -0
- package/src/__tests__/history-repair-observability.test.ts +1 -1
- package/src/__tests__/history-repair.test.ts +2 -1
- package/src/__tests__/host-app-control-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-file-edit-tool.test.ts +4 -2
- package/src/__tests__/host-file-proxy.test.ts +31 -0
- package/src/__tests__/host-file-read-tool.test.ts +4 -2
- package/src/__tests__/host-file-write-tool.test.ts +9 -3
- package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
- package/src/__tests__/host-shell-tool.test.ts +9 -4
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +35 -14
- package/src/__tests__/inbound-slack-persistence.test.ts +7 -2
- package/src/__tests__/injector-background-turn.test.ts +1 -1
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-disk-pressure.test.ts +1 -1
- package/src/__tests__/injector-document-comments.test.ts +1 -1
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
- package/src/__tests__/injector-v3-suppression.test.ts +220 -0
- package/src/__tests__/list-messages-attachments.test.ts +7 -8
- package/src/__tests__/list-messages-hidden-metadata.test.ts +17 -15
- package/src/__tests__/list-messages-page-latest.test.ts +0 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
- package/src/__tests__/llm-call-pipeline.test.ts +21 -15
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +23 -47
- package/src/__tests__/llm-usage-store.test.ts +45 -0
- package/src/__tests__/log-export-routes.test.ts +59 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-health-check.test.ts +18 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
- package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +8 -4
- package/src/__tests__/migration-export-http.test.ts +12 -12
- package/src/__tests__/migration-import-commit-http.test.ts +8 -8
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +14 -20
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/oauth-commands-routes.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +10 -0
- package/src/__tests__/openai-provider.test.ts +66 -70
- package/src/__tests__/openai-responses-provider.test.ts +21 -77
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
- package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
- package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
- package/src/__tests__/persistence-pipeline.test.ts +15 -26
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -1
- package/src/__tests__/pipeline-runner.test.ts +2 -3
- package/src/__tests__/plugin-bootstrap.test.ts +51 -25
- package/src/__tests__/plugin-route-contribution.test.ts +6 -16
- package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
- package/src/__tests__/plugin-types.test.ts +7 -14
- package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
- package/src/__tests__/process-message-background-slack.test.ts +17 -16
- package/src/__tests__/process-message-display-content.test.ts +30 -42
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
- package/src/__tests__/provider-error-scenarios.test.ts +7 -6
- package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
- package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
- package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
- package/src/__tests__/provider-usage-tracking.test.ts +0 -6
- package/src/__tests__/ratelimit.test.ts +9 -4
- package/src/__tests__/relay-server.test.ts +20 -13
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
- package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
- package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
- package/src/__tests__/schedule-routes.test.ts +80 -10
- package/src/__tests__/schedule-store.test.ts +67 -0
- package/src/__tests__/schedule-tools.test.ts +125 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
- package/src/__tests__/secret-response-routing.test.ts +13 -11
- package/src/__tests__/send-endpoint-busy.test.ts +2 -1
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +11 -11
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/starter-task-flow.test.ts +6 -6
- package/src/__tests__/strip-memory-injections.test.ts +102 -14
- package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +54 -0
- package/src/__tests__/terminal-tools.test.ts +3 -24
- package/src/__tests__/thread-backfill.test.ts +4 -9
- package/src/__tests__/title-generate-pipeline.test.ts +1 -1
- package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
- package/src/__tests__/tool-error-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
- package/src/__tests__/tool-preview-lifecycle.test.ts +13 -11
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
- package/src/__tests__/tool-result-truncation.test.ts +3 -1
- package/src/__tests__/tools-audio-read.test.ts +113 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
- package/src/__tests__/turn-events-store.test.ts +11 -7
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
- package/src/__tests__/voice-session-bridge.test.ts +13 -7
- package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
- package/src/acp/prepare-agent-env.ts +52 -11
- package/src/agent/compaction-circuit.ts +140 -0
- package/src/agent/loop.ts +409 -85
- package/src/api/README.md +19 -17
- package/src/api/constants/tool-execution.ts +21 -0
- package/src/api/events/assistant-activity-state.ts +75 -0
- package/src/api/events/assistant-outbound-attachment.ts +25 -27
- package/src/api/events/assistant-text-delta.ts +6 -8
- package/src/api/events/assistant-turn-start.ts +5 -7
- package/src/api/events/avatar-updated.ts +24 -0
- package/src/api/events/compaction-circuit-closed.ts +26 -0
- package/src/api/events/compaction-circuit-open.ts +28 -0
- package/src/api/events/confirmation-request.ts +114 -0
- package/src/api/events/contact-request.ts +33 -0
- package/src/api/events/conversation-error.ts +77 -0
- package/src/api/events/conversation-list-invalidated.ts +38 -0
- package/src/api/events/conversation-title-updated.ts +24 -0
- package/src/api/events/disk-pressure-status-changed.ts +61 -0
- package/src/api/events/document-comment-created.ts +24 -28
- package/src/api/events/document-comment-deleted.ts +6 -8
- package/src/api/events/document-comment-reopened.ts +6 -8
- package/src/api/events/document-comment-resolved.ts +8 -10
- package/src/api/events/document-editor-update.ts +27 -0
- package/src/api/events/error.ts +32 -0
- package/src/api/events/generation-cancelled.ts +4 -6
- package/src/api/events/generation-handoff.ts +13 -15
- package/src/api/events/home-feed-updated.ts +26 -0
- package/src/api/events/identity-changed.ts +32 -0
- package/src/api/events/interaction-resolved.ts +50 -0
- package/src/api/events/message-complete.ts +10 -12
- package/src/api/events/message-dequeued.ts +21 -0
- package/src/api/events/message-queued-deleted.ts +23 -0
- package/src/api/events/message-queued.ts +22 -0
- package/src/api/events/message-request-complete.ts +29 -0
- package/src/api/events/navigate-settings.ts +20 -0
- package/src/api/events/notification-intent.ts +33 -0
- package/src/api/events/open-url.ts +6 -8
- package/src/api/events/question-request.ts +67 -0
- package/src/api/events/relationship-state-updated.ts +4 -6
- package/src/api/events/secret-request.ts +42 -0
- package/src/api/events/subagent-event.ts +79 -0
- package/src/api/events/subagent-spawned.ts +40 -0
- package/src/api/events/subagent-status-changed.ts +65 -0
- package/src/api/events/sync-changed.ts +29 -0
- package/src/api/events/tool-result.ts +129 -0
- package/src/api/events/tool-use-start.ts +8 -10
- package/src/api/events/turn-profile-auto-routed.ts +28 -0
- package/src/api/events/ui-surface-complete.ts +30 -0
- package/src/api/events/ui-surface-dismiss.ts +22 -0
- package/src/api/events/ui-surface-show.ts +67 -0
- package/src/api/events/ui-surface-update.ts +26 -0
- package/src/api/events/usage-update.ts +34 -0
- package/src/api/events/user-message-echo.ts +35 -0
- package/src/api/index.ts +354 -0
- package/src/api/requests/dictation.ts +45 -0
- package/src/api/responses/disk-pressure-status.ts +26 -0
- package/src/api/responses/home.ts +217 -0
- package/src/api/responses/llm-context-response.ts +2 -0
- package/src/api/responses/memory-v3-selection-log.ts +50 -0
- package/src/api/responses/subagent-detail.ts +48 -0
- package/src/approvals/guardian-decision-primitive.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +6 -9
- package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
- package/src/avatar/__tests__/avatar-store.test.ts +193 -0
- package/src/avatar/avatar-manifest.ts +195 -0
- package/src/avatar/avatar-store.ts +113 -0
- package/src/avatar/traits-png-sync.ts +8 -2
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +4 -1
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +13 -27
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/conversations.ts +15 -1
- package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
- package/src/cli/commands/db/__tests__/status.test.ts +253 -0
- package/src/cli/commands/db/format.ts +48 -0
- package/src/cli/commands/db/index.ts +29 -0
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
- package/src/cli/commands/db/repair-step-integrity.ts +146 -0
- package/src/cli/commands/db/repair-steps.ts +164 -0
- package/src/cli/commands/db/repair.ts +141 -0
- package/src/cli/commands/db/status.ts +366 -0
- package/src/cli/commands/memory-v3.ts +159 -445
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/program.ts +4 -5
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/assistant-feature-flags.ts +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
- package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/call-site-defaults.ts +2 -7
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -226
- package/src/config/schemas/call-site-catalog.ts +8 -15
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +0 -253
- package/src/config/schemas/memory-v3.ts +39 -0
- package/src/config/schemas/memory.ts +6 -1
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/context/compactor.ts +54 -31
- package/src/context/token-estimator.ts +19 -0
- package/src/context/tool-result-truncation.ts +1 -43
- package/src/context/window-manager.ts +138 -20
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
- package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
- package/src/daemon/approval-generators.ts +4 -4
- package/src/daemon/config-watcher.ts +7 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +225 -88
- package/src/daemon/conversation-agent-loop.ts +284 -584
- package/src/daemon/conversation-error.ts +7 -7
- package/src/daemon/conversation-history.ts +22 -6
- package/src/daemon/conversation-launch.ts +4 -8
- package/src/daemon/conversation-lifecycle.ts +10 -38
- package/src/daemon/conversation-messaging.ts +1 -3
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +100 -79
- package/src/daemon/conversation-runtime-assembly.ts +47 -21
- package/src/daemon/conversation-store.ts +6 -5
- package/src/daemon/conversation-surfaces.ts +55 -69
- package/src/daemon/conversation-tool-setup.ts +3 -0
- package/src/daemon/conversation.ts +91 -126
- package/src/daemon/daemon-skill-host.ts +2 -6
- package/src/daemon/disk-pressure-guard.ts +35 -29
- package/src/daemon/external-plugins-bootstrap.ts +46 -24
- package/src/daemon/first-greeting.ts +26 -4
- package/src/daemon/guardian-action-generators.ts +2 -2
- package/src/daemon/handlers/conversations.ts +6 -22
- package/src/daemon/handlers/shared.ts +4 -0
- package/src/daemon/handlers/skills.ts +15 -14
- package/src/daemon/host-app-control-proxy.ts +54 -1
- package/src/daemon/host-cu-proxy.ts +46 -22
- package/src/daemon/host-file-proxy.ts +25 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -6
- package/src/daemon/lifecycle.ts +28 -55
- package/src/daemon/message-protocol.ts +2 -3
- package/src/daemon/message-provenance.ts +49 -0
- package/src/daemon/message-types/contacts.ts +3 -20
- package/src/daemon/message-types/conversations.ts +13 -111
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +4 -17
- package/src/daemon/message-types/integrations.ts +2 -6
- package/src/daemon/message-types/messages.ts +28 -343
- package/src/daemon/message-types/notifications.ts +2 -32
- package/src/daemon/message-types/settings.ts +3 -8
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/sync.ts +12 -25
- package/src/daemon/message-types/workspace.ts +3 -11
- package/src/daemon/process-message.ts +49 -46
- package/src/daemon/server.ts +12 -0
- package/src/daemon/tool-side-effects.ts +10 -7
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +11 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -1
- package/src/heartbeat/heartbeat-run-store.ts +31 -0
- package/src/heartbeat/heartbeat-service.ts +16 -0
- package/src/home/feature-gate.ts +22 -0
- package/src/home/feed-types.ts +36 -221
- package/src/ipc/__tests__/email-ipc.test.ts +0 -9
- package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
- package/src/ipc/routes/route-adapter.ts +45 -6
- package/src/ipc/skill-routes/__tests__/memory.test.ts +18 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
- package/src/ipc/skill-routes/memory.ts +26 -13
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +13 -61
- package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
- package/src/memory/__tests__/conversation-queries.test.ts +192 -8
- package/src/memory/__tests__/db-maintenance.test.ts +128 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
- package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
- package/src/memory/context-search/agent-runner.ts +2 -4
- package/src/memory/conversation-crud.ts +39 -8
- package/src/memory/conversation-queries.ts +78 -22
- package/src/memory/db-init.ts +8 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/graph/consolidation.ts +8 -11
- package/src/memory/graph/conversation-graph-memory.ts +41 -8
- package/src/memory/graph/extraction.ts +6 -9
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.ts +20 -26
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/job-handlers/conversation-starters.ts +32 -32
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs-store.ts +3 -1
- package/src/memory/jobs-worker.ts +51 -39
- package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
- package/src/memory/llm-request-log-source-local.ts +0 -11
- package/src/memory/llm-request-log-source.ts +9 -25
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +10 -0
- package/src/memory/memory-marker.ts +17 -0
- package/src/memory/memory-retrospective-job.ts +6 -2
- package/src/memory/memory-v2-activation-log-store.ts +1 -83
- package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
- package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
- package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
- package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
- package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/schema/infrastructure.ts +11 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
- package/src/memory/v2/__tests__/migration.test.ts +11 -3
- package/src/memory/v2/__tests__/page-index.test.ts +37 -1
- package/src/memory/v2/__tests__/router.test.ts +14 -4
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +89 -9
- package/src/memory/v2/migration.ts +5 -3
- package/src/memory/v2/page-index.ts +11 -0
- package/src/memory/v2/router.ts +8 -11
- package/src/memory/v2/sweep-job.ts +8 -11
- package/src/memory/v2/types.ts +1 -0
- package/src/memory/v3/__tests__/assign.test.ts +242 -0
- package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
- package/src/memory/v3/__tests__/core.test.ts +39 -0
- package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
- package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
- package/src/memory/v3/__tests__/health.test.ts +203 -0
- package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
- package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
- package/src/memory/v3/__tests__/needle.test.ts +107 -0
- package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
- package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
- package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
- package/src/memory/v3/__tests__/router.test.ts +260 -0
- package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
- package/src/memory/v3/__tests__/selector.test.ts +404 -0
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
- package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
- package/src/memory/v3/__tests__/tree.test.ts +192 -0
- package/src/memory/v3/__tests__/types.test.ts +54 -0
- package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
- package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
- package/src/memory/v3/assign.ts +268 -0
- package/src/memory/v3/capabilities.ts +124 -0
- package/src/memory/v3/core.ts +26 -0
- package/src/memory/v3/data/README.md +84 -0
- package/src/memory/v3/data/assignments.json +5 -0
- package/src/memory/v3/data/core.json +1 -0
- package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
- package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
- package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +314 -0
- package/src/memory/v3/needle.ts +115 -0
- package/src/memory/v3/orchestrate.ts +114 -0
- package/src/memory/v3/page-content.ts +34 -0
- package/src/memory/v3/provider-blocks.ts +16 -0
- package/src/memory/v3/reconcile.ts +523 -0
- package/src/memory/v3/render-injection.ts +32 -0
- package/src/memory/v3/router.ts +184 -0
- package/src/memory/v3/selection-log-store.ts +84 -0
- package/src/memory/v3/selector.ts +211 -0
- package/src/memory/v3/shadow-plugin.ts +379 -0
- package/src/memory/v3/snapshot.ts +209 -0
- package/src/memory/v3/tree.ts +174 -0
- package/src/memory/v3/types.ts +46 -60
- package/src/memory/v3/working-set.ts +88 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
- package/src/messaging/providers/slack/render-transcript.ts +2 -2
- package/src/messaging/style-analyzer.ts +8 -11
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/decision-engine.ts +10 -13
- package/src/notifications/preference-extractor.ts +11 -14
- package/src/permissions/prompter.ts +42 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/plugin-api/index.ts +2 -0
- package/src/plugin-api/types.ts +25 -3
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
- package/src/plugins/defaults/circuit-breaker/package.json +15 -0
- package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
- package/src/plugins/defaults/compaction/package.json +15 -0
- package/src/plugins/defaults/compaction/register.ts +35 -0
- package/src/plugins/defaults/compaction/terminal.ts +73 -0
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
- package/src/plugins/defaults/empty-response/package.json +15 -0
- package/src/plugins/defaults/empty-response/register.ts +28 -0
- package/src/plugins/defaults/empty-response/terminal.ts +106 -0
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/history-repair/package.json +15 -0
- package/src/plugins/defaults/history-repair/register.ts +24 -0
- package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
- package/src/plugins/defaults/index.ts +29 -40
- package/src/plugins/defaults/injectors/package.json +15 -0
- package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +14 -38
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
- package/src/plugins/defaults/llm-call/package.json +15 -0
- package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
- package/src/plugins/defaults/memory-retrieval/package.json +15 -0
- package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
- package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
- package/src/plugins/defaults/overflow-reduce/package.json +15 -0
- package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
- package/src/plugins/defaults/persistence/package.json +15 -0
- package/src/plugins/defaults/persistence/register.ts +38 -0
- package/src/plugins/defaults/persistence/terminal.ts +83 -0
- package/src/plugins/defaults/title-generate/package.json +15 -0
- package/src/plugins/defaults/title-generate/register.ts +35 -0
- package/src/plugins/defaults/title-generate/terminal.ts +31 -0
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
- package/src/plugins/defaults/token-estimate/package.json +15 -0
- package/src/plugins/defaults/token-estimate/register.ts +34 -0
- package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
- package/src/plugins/defaults/tool-error/package.json +15 -0
- package/src/plugins/defaults/tool-error/register.ts +35 -0
- package/src/plugins/defaults/tool-error/terminal.ts +47 -0
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
- package/src/plugins/defaults/tool-execute/package.json +15 -0
- package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
- package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
- package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/pipeline.ts +0 -12
- package/src/plugins/types.ts +51 -90
- package/src/plugins/user-loader.ts +4 -3
- package/src/proactive-artifact/aux-message-injector.ts +0 -1
- package/src/proactive-artifact/job.test.ts +20 -8
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
- package/src/prompts/templates/BOOTSTRAP.md +5 -1
- package/src/prompts/templates/system-sections.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -25
- package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
- package/src/providers/anthropic/client.ts +24 -5
- package/src/providers/call-site-routing.ts +1 -9
- package/src/providers/gemini/client.ts +152 -34
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +0 -2
- package/src/providers/openai/chat-completions-provider.ts +1 -4
- package/src/providers/openai/responses-provider.ts +1 -4
- package/src/providers/openrouter/client.ts +1 -6
- package/src/providers/provider-send-message.ts +6 -6
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/retry.ts +0 -5
- package/src/providers/types.ts +11 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +131 -26
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
- package/src/runtime/agent-wake.ts +93 -18
- package/src/runtime/assistant-event-hub.ts +2 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
- package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
- package/src/runtime/auth/route-policy.ts +42 -1079
- package/src/runtime/background-job-runner.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -1
- package/src/runtime/channel-approvals.ts +3 -14
- package/src/runtime/channel-invite-transport.ts +5 -6
- package/src/runtime/channel-readiness-service.ts +2 -5
- package/src/runtime/channel-retry-sweep.ts +12 -16
- package/src/runtime/conversation-stream-state.ts +294 -0
- package/src/runtime/http-router.ts +19 -22
- package/src/runtime/http-types.ts +12 -6
- package/src/runtime/invite-instruction-generator.ts +3 -3
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
- package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
- package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
- package/src/runtime/routes/acp-routes.test.ts +97 -75
- package/src/runtime/routes/acp-routes.ts +29 -6
- package/src/runtime/routes/app-management-routes.ts +97 -24
- package/src/runtime/routes/app-routes.ts +25 -5
- package/src/runtime/routes/approval-routes.ts +16 -4
- package/src/runtime/routes/attachment-routes.ts +25 -1
- package/src/runtime/routes/audio-routes.ts +1 -0
- package/src/runtime/routes/audit-routes.ts +5 -0
- package/src/runtime/routes/auth-routes.ts +5 -0
- package/src/runtime/routes/avatar-routes.ts +238 -59
- package/src/runtime/routes/background-tool-routes.ts +9 -0
- package/src/runtime/routes/background-wake-routes.ts +13 -3
- package/src/runtime/routes/backup-routes.ts +45 -0
- package/src/runtime/routes/bookmark-routes.ts +13 -0
- package/src/runtime/routes/brain-graph-routes.ts +9 -0
- package/src/runtime/routes/browser-routes.ts +5 -0
- package/src/runtime/routes/browser-tabs-routes.ts +5 -0
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/cache-routes.ts +13 -0
- package/src/runtime/routes/call-routes.ts +21 -10
- package/src/runtime/routes/channel-availability-routes.ts +5 -1
- package/src/runtime/routes/channel-readiness-routes.ts +37 -4
- package/src/runtime/routes/channel-route-definitions.ts +21 -0
- package/src/runtime/routes/channel-verification-routes.ts +21 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
- package/src/runtime/routes/client-routes.ts +9 -0
- package/src/runtime/routes/consolidation-routes.ts +13 -5
- package/src/runtime/routes/contact-prompt-routes.ts +9 -0
- package/src/runtime/routes/contact-routes.ts +90 -23
- package/src/runtime/routes/content-source-routes.ts +5 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
- package/src/runtime/routes/conversation-attention-routes.ts +5 -0
- package/src/runtime/routes/conversation-cli-routes.ts +54 -7
- package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
- package/src/runtime/routes/conversation-list-routes.ts +81 -12
- package/src/runtime/routes/conversation-management-routes.ts +57 -14
- package/src/runtime/routes/conversation-query-routes.ts +88 -41
- package/src/runtime/routes/conversation-routes.ts +74 -19
- package/src/runtime/routes/conversation-starter-routes.ts +22 -13
- package/src/runtime/routes/conversations-import-routes.ts +6 -1
- package/src/runtime/routes/credential-prompt-routes.ts +5 -0
- package/src/runtime/routes/credential-routes.ts +25 -6
- package/src/runtime/routes/debug-bash-routes.ts +5 -0
- package/src/runtime/routes/debug-routes.ts +11 -2
- package/src/runtime/routes/defer-routes.ts +13 -0
- package/src/runtime/routes/diagnostics-routes.ts +37 -46
- package/src/runtime/routes/disk-pressure-routes.ts +17 -31
- package/src/runtime/routes/document-comments-routes.ts +46 -27
- package/src/runtime/routes/documents-routes.ts +21 -10
- package/src/runtime/routes/domain-routes.ts +61 -28
- package/src/runtime/routes/email-routes.ts +33 -0
- package/src/runtime/routes/events-routes.ts +114 -9
- package/src/runtime/routes/filing-routes.ts +9 -4
- package/src/runtime/routes/gateway-log-routes.ts +5 -0
- package/src/runtime/routes/global-search-routes.ts +53 -50
- package/src/runtime/routes/group-routes.ts +21 -5
- package/src/runtime/routes/guardian-action-routes.ts +9 -0
- package/src/runtime/routes/guardian-approval-interception.ts +0 -31
- package/src/runtime/routes/heartbeat-routes.ts +25 -9
- package/src/runtime/routes/home-feed-routes.ts +23 -19
- package/src/runtime/routes/home-state-routes.ts +8 -40
- package/src/runtime/routes/host-app-control-routes.ts +5 -0
- package/src/runtime/routes/host-bash-routes.ts +5 -0
- package/src/runtime/routes/host-browser-routes.ts +13 -0
- package/src/runtime/routes/host-cu-routes.ts +5 -0
- package/src/runtime/routes/host-file-routes.ts +26 -6
- package/src/runtime/routes/host-transfer-routes.ts +13 -2
- package/src/runtime/routes/http-adapter.ts +1 -2
- package/src/runtime/routes/identity-intro-cache.ts +17 -6
- package/src/runtime/routes/identity-routes.ts +12 -2
- package/src/runtime/routes/image-generation-routes.ts +5 -0
- package/src/runtime/routes/inbound-message-handler.ts +15 -11
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
- package/src/runtime/routes/inference-send-routes.ts +11 -11
- package/src/runtime/routes/integrations/a2a.ts +30 -7
- package/src/runtime/routes/integrations/slack/channel.ts +19 -3
- package/src/runtime/routes/integrations/slack/share.ts +9 -2
- package/src/runtime/routes/integrations/telegram.ts +28 -9
- package/src/runtime/routes/integrations/twilio.ts +35 -7
- package/src/runtime/routes/integrations/vercel.ts +3 -3
- package/src/runtime/routes/internal-oauth-routes.ts +5 -0
- package/src/runtime/routes/internal-twilio-routes.ts +13 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
- package/src/runtime/routes/log-export-routes.ts +28 -10
- package/src/runtime/routes/mcp-auth-routes.ts +25 -0
- package/src/runtime/routes/memory-item-routes.ts +21 -10
- package/src/runtime/routes/memory-v2-routes.ts +90 -36
- package/src/runtime/routes/memory-v3-routes.ts +273 -407
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +29 -0
- package/src/runtime/routes/notification-routes.ts +17 -1
- package/src/runtime/routes/oauth-apps.ts +33 -11
- package/src/runtime/routes/oauth-commands-routes.ts +37 -14
- package/src/runtime/routes/oauth-connect-routes.ts +9 -0
- package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
- package/src/runtime/routes/oauth-providers.ts +35 -10
- package/src/runtime/routes/platform-routes.ts +21 -0
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
- package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +0 -1
- package/src/runtime/routes/playground/inject-failures.ts +13 -8
- package/src/runtime/routes/playground/reset-circuit.ts +14 -9
- package/src/runtime/routes/playground/seed-conversation.ts +1 -1
- package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
- package/src/runtime/routes/playground/state.ts +4 -3
- package/src/runtime/routes/plugins-routes.ts +22 -19
- package/src/runtime/routes/profiler-routes.ts +17 -4
- package/src/runtime/routes/ps-routes.ts +5 -0
- package/src/runtime/routes/publish-routes.ts +13 -3
- package/src/runtime/routes/question-routes.ts +5 -0
- package/src/runtime/routes/recording-routes.ts +25 -12
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/sanity-routes.ts +9 -2
- package/src/runtime/routes/schedule-routes.ts +137 -47
- package/src/runtime/routes/secret-routes.ts +17 -4
- package/src/runtime/routes/sequence-routes.ts +33 -0
- package/src/runtime/routes/settings-routes.ts +65 -19
- package/src/runtime/routes/skills-routes.ts +133 -69
- package/src/runtime/routes/slack-channel-routes.ts +5 -0
- package/src/runtime/routes/stt-routes.ts +13 -6
- package/src/runtime/routes/subagents-routes.ts +24 -18
- package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
- package/src/runtime/routes/surface-action-routes.ts +9 -0
- package/src/runtime/routes/surface-content-routes.ts +10 -2
- package/src/runtime/routes/task-routes.ts +37 -0
- package/src/runtime/routes/telemetry-routes.ts +9 -0
- package/src/runtime/routes/trace-event-routes.ts +42 -1
- package/src/runtime/routes/trust-rules-routes.ts +5 -0
- package/src/runtime/routes/tts-routes.ts +13 -6
- package/src/runtime/routes/types.ts +17 -8
- package/src/runtime/routes/ui-request-routes.ts +5 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
- package/src/runtime/routes/usage-routes.ts +71 -3
- package/src/runtime/routes/user-routes-cli.ts +9 -0
- package/src/runtime/routes/user-routes.ts +5 -1
- package/src/runtime/routes/wake-conversation-routes.ts +5 -0
- package/src/runtime/routes/watcher-routes.ts +21 -0
- package/src/runtime/routes/webhook-routes.ts +9 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
- package/src/runtime/routes/work-items-routes.ts +47 -19
- package/src/runtime/routes/workspace-commit-routes.ts +5 -0
- package/src/runtime/routes/workspace-routes.test.ts +42 -0
- package/src/runtime/routes/workspace-routes.ts +120 -9
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -4
- package/src/runtime/services/analyze-conversation.ts +3 -6
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/sync/resource-sync-events.ts +16 -2
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +8 -0
- package/src/schedule/scheduler.ts +3 -1
- package/src/signals/user-message.ts +5 -8
- package/src/skills/catalog-files.ts +4 -1
- package/src/skills/clawhub-files.ts +2 -0
- package/src/skills/skillssh-files.ts +2 -0
- package/src/subagent/manager.ts +3 -6
- package/src/telemetry/types.ts +26 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
- package/src/telemetry/usage-telemetry-reporter.ts +31 -0
- package/src/tools/acp/spawn.test.ts +88 -38
- package/src/tools/apps/definitions.ts +8 -4
- package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
- package/src/tools/ask-question/ask-question-tool.ts +85 -90
- package/src/tools/computer-use/definitions.ts +28 -24
- package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
- package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
- package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
- package/src/tools/credentials/vault.ts +112 -111
- package/src/tools/execution-target.ts +1 -1
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/filesystem/edit.ts +45 -42
- package/src/tools/filesystem/list.ts +33 -30
- package/src/tools/filesystem/read.ts +54 -35
- package/src/tools/filesystem/write.ts +34 -31
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/web-fetch.ts +49 -46
- package/src/tools/network/web-search.ts +16 -13
- package/src/tools/registry.ts +39 -16
- package/src/tools/schedule/create.ts +11 -0
- package/src/tools/schedule/update.ts +16 -0
- package/src/tools/shared/filesystem/audio-read.ts +122 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/execute.ts +34 -31
- package/src/tools/skills/load.ts +29 -23
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/system/avatar-generator.ts +13 -22
- package/src/tools/system/request-permission.ts +30 -27
- package/src/tools/terminal/shell.ts +190 -61
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +74 -23
- package/src/tools/ui-surface/definitions.ts +69 -9
- package/src/usage/types.ts +10 -0
- package/src/util/errors.ts +2 -2
- package/src/util/map-limit.ts +27 -0
- package/src/util/platform.ts +15 -12
- package/src/work-items/work-item-runner.ts +7 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
- package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
- package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
- package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
- package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
- package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/provider-commit-message-generator.ts +15 -17
- package/tsconfig.json +4 -1
- package/src/__tests__/history-repair-pipeline.test.ts +0 -396
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
- package/src/cli/commands/memory-v3-render.ts +0 -491
- package/src/daemon/message-types/disk-pressure.ts +0 -9
- package/src/email/feature-gate.ts +0 -23
- package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
- package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -706
- package/src/memory/v3/__tests__/filter.test.ts +0 -560
- package/src/memory/v3/__tests__/gate.test.ts +0 -637
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
- package/src/memory/v3/__tests__/loop.test.ts +0 -775
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -489
- package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -508
- package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
- package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
- package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
- package/src/memory/v3/__tests__/validate.test.ts +0 -277
- package/src/memory/v3/auto-edges.ts +0 -223
- package/src/memory/v3/coactivation-store.ts +0 -124
- package/src/memory/v3/consolidation-job.ts +0 -323
- package/src/memory/v3/coretrieval-seed.ts +0 -240
- package/src/memory/v3/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -286
- package/src/memory/v3/filter.ts +0 -286
- package/src/memory/v3/gate.ts +0 -349
- package/src/memory/v3/index-composition.ts +0 -126
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -430
- package/src/memory/v3/maintenance.ts +0 -144
- package/src/memory/v3/prompt-context.ts +0 -33
- package/src/memory/v3/prompts/consolidation.ts +0 -458
- package/src/memory/v3/prompts/system-prompts.ts +0 -196
- package/src/memory/v3/retriever.ts +0 -33
- package/src/memory/v3/scouts.ts +0 -431
- package/src/memory/v3/shadow-diff.ts +0 -287
- package/src/memory/v3/shadow-middleware.ts +0 -347
- package/src/memory/v3/traversal.ts +0 -211
- package/src/memory/v3/tree-index.ts +0 -237
- package/src/memory/v3/tree-store.ts +0 -394
- package/src/memory/v3/tree-walk.ts +0 -356
- package/src/memory/v3/validate.ts +0 -323
- package/src/plugins/defaults/circuit-breaker.ts +0 -141
- package/src/plugins/defaults/compaction.ts +0 -141
- package/src/plugins/defaults/empty-response.ts +0 -124
- package/src/plugins/defaults/history-repair.ts +0 -83
- package/src/plugins/defaults/persistence.ts +0 -146
- package/src/plugins/defaults/title-generate.ts +0 -90
- package/src/plugins/defaults/token-estimate.ts +0 -101
- package/src/plugins/defaults/tool-error.ts +0 -119
- package/src/plugins/defaults/tool-result-truncate.ts +0 -84
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
|
@@ -25,16 +25,37 @@ const { initializeDb } = await import("../db-init.js");
|
|
|
25
25
|
const { deleteMemoryCheckpoint, getMemoryCheckpoint } =
|
|
26
26
|
await import("../checkpoints.js");
|
|
27
27
|
const { maybeRunDbMaintenance } = await import("../db-maintenance.js");
|
|
28
|
+
const { getLastUserMessageTimestamp } = await import("../conversation-crud.js");
|
|
28
29
|
const { getDbPath } = await import("../../util/platform.js");
|
|
29
30
|
|
|
30
31
|
initializeDb();
|
|
31
32
|
|
|
32
33
|
const MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
|
|
34
|
+
const QUIET_PERIOD_MS = 3 * 60 * 60 * 1000;
|
|
33
35
|
|
|
34
36
|
beforeEach(() => {
|
|
35
37
|
deleteMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY);
|
|
38
|
+
const sqlite = getSqlite();
|
|
39
|
+
sqlite.exec("DELETE FROM messages");
|
|
40
|
+
sqlite.exec("DELETE FROM conversations");
|
|
36
41
|
});
|
|
37
42
|
|
|
43
|
+
/** Insert a message row directly, bypassing indexing/job side effects. */
|
|
44
|
+
function insertMessage(role: "user" | "assistant", createdAt: number): void {
|
|
45
|
+
const sqlite = getSqlite();
|
|
46
|
+
const convId = `conv-${createdAt}-${role}`;
|
|
47
|
+
sqlite
|
|
48
|
+
.prepare(
|
|
49
|
+
"INSERT OR IGNORE INTO conversations (id, created_at, updated_at) VALUES (?, ?, ?)",
|
|
50
|
+
)
|
|
51
|
+
.run(convId, createdAt, createdAt);
|
|
52
|
+
sqlite
|
|
53
|
+
.prepare(
|
|
54
|
+
"INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)",
|
|
55
|
+
)
|
|
56
|
+
.run(`msg-${createdAt}-${role}`, convId, role, "[]", createdAt);
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
/** Inflate the test DB with bloat that VACUUM can reclaim. */
|
|
39
60
|
function inflateAndDelete(byteTarget: number): void {
|
|
40
61
|
const sqlite = getSqlite();
|
|
@@ -112,4 +133,111 @@ describe("maybeRunDbMaintenance", () => {
|
|
|
112
133
|
const pagesAfter = readPageCount();
|
|
113
134
|
expect(pagesAfter).toBeLessThan(pagesBefore);
|
|
114
135
|
}, 60_000);
|
|
136
|
+
|
|
137
|
+
test("defers maintenance while the last user message is within the quiet period", async () => {
|
|
138
|
+
/** VACUUM must not fire while the user is active, so a recent user
|
|
139
|
+
* message keeps maintenance deferred. */
|
|
140
|
+
// GIVEN the user sent a message one minute ago (well within the quiet period)
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
insertMessage("user", now - 60_000);
|
|
143
|
+
|
|
144
|
+
// WHEN maintenance is considered
|
|
145
|
+
await maybeRunDbMaintenance(now);
|
|
146
|
+
|
|
147
|
+
// THEN it is deferred — the checkpoint is never stamped, so a later idle
|
|
148
|
+
// tick will retry
|
|
149
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBeNull();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("runs maintenance once the quiet period has elapsed since the last user message", async () => {
|
|
153
|
+
/** After the user has been quiet for longer than the quiet period,
|
|
154
|
+
* maintenance is allowed to run. */
|
|
155
|
+
// GIVEN the last user message is older than the quiet period
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
insertMessage("user", now - (QUIET_PERIOD_MS + 60_000));
|
|
158
|
+
|
|
159
|
+
// WHEN maintenance is considered
|
|
160
|
+
await maybeRunDbMaintenance(now);
|
|
161
|
+
|
|
162
|
+
// THEN it runs and stamps the checkpoint
|
|
163
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("ignores quiet period when no user message exists", async () => {
|
|
167
|
+
/** A fresh install with no user messages must not be blocked from ever
|
|
168
|
+
* running maintenance. */
|
|
169
|
+
// GIVEN no user messages exist
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
|
|
172
|
+
// WHEN maintenance is considered
|
|
173
|
+
await maybeRunDbMaintenance(now);
|
|
174
|
+
|
|
175
|
+
// THEN it runs and stamps the checkpoint
|
|
176
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("a recent assistant message does not keep maintenance deferred", async () => {
|
|
180
|
+
/** The gate keys off user activity only — background assistant writes
|
|
181
|
+
* must not postpone maintenance indefinitely. */
|
|
182
|
+
// GIVEN the user has been quiet past the quiet period
|
|
183
|
+
const now = Date.now();
|
|
184
|
+
insertMessage("user", now - (QUIET_PERIOD_MS + 60_000));
|
|
185
|
+
// AND the assistant wrote a message recently
|
|
186
|
+
insertMessage("assistant", now - 60_000);
|
|
187
|
+
|
|
188
|
+
// WHEN maintenance is considered
|
|
189
|
+
await maybeRunDbMaintenance(now);
|
|
190
|
+
|
|
191
|
+
// THEN it still runs, since only user activity gates it
|
|
192
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("getLastUserMessageTimestamp", () => {
|
|
197
|
+
test("returns 0 when no user message exists", () => {
|
|
198
|
+
/** With only non-user rows present, there is no user activity to report. */
|
|
199
|
+
// GIVEN only an assistant message exists
|
|
200
|
+
insertMessage("assistant", Date.now());
|
|
201
|
+
|
|
202
|
+
// WHEN the last user message timestamp is read
|
|
203
|
+
const result = getLastUserMessageTimestamp();
|
|
204
|
+
|
|
205
|
+
// THEN it reports 0 (no user activity)
|
|
206
|
+
expect(result).toBe(0);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("returns the most recent user message by timestamp, ignoring assistant rows", () => {
|
|
210
|
+
/** The lookup reports the newest user-message timestamp and must skip
|
|
211
|
+
* non-user rows even when they are more recent. */
|
|
212
|
+
// GIVEN two user messages and a newer assistant message
|
|
213
|
+
const base = Date.now();
|
|
214
|
+
insertMessage("user", base - 10_000);
|
|
215
|
+
insertMessage("user", base - 5_000);
|
|
216
|
+
insertMessage("assistant", base);
|
|
217
|
+
|
|
218
|
+
// WHEN the last user message timestamp is read
|
|
219
|
+
const result = getLastUserMessageTimestamp();
|
|
220
|
+
|
|
221
|
+
// THEN it returns the most recent user message, not the assistant row
|
|
222
|
+
expect(result).toBe(base - 5_000);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("reports the newest user timestamp even when an older turn was inserted later", () => {
|
|
226
|
+
/** `forkConversation` copies a parent's user turns into the fork with
|
|
227
|
+
* their original (older) `created_at` but fresh row ids, so insertion
|
|
228
|
+
* order can place an old turn last. The lookup must key off `created_at`
|
|
229
|
+
* so a fork can't make recent activity look stale and prematurely
|
|
230
|
+
* un-gate maintenance. */
|
|
231
|
+
// GIVEN a recent user message
|
|
232
|
+
const base = Date.now();
|
|
233
|
+
insertMessage("user", base - 5_000);
|
|
234
|
+
// AND a later insert of an older user turn (as a fork copy would produce)
|
|
235
|
+
insertMessage("user", base - 60 * 60 * 1000);
|
|
236
|
+
|
|
237
|
+
// WHEN the last user message timestamp is read
|
|
238
|
+
const result = getLastUserMessageTimestamp();
|
|
239
|
+
|
|
240
|
+
// THEN it reports the genuinely most recent turn, not the last-inserted one
|
|
241
|
+
expect(result).toBe(base - 5_000);
|
|
242
|
+
});
|
|
115
243
|
});
|
|
@@ -40,11 +40,12 @@ describe("memory job classes", () => {
|
|
|
40
40
|
expect(set.size).toBe(SLOW_LLM_JOB_TYPES.length);
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
test("
|
|
44
|
-
// It
|
|
43
|
+
test("memory_v3_maintain is a slow LLM job (lane isolation)", () => {
|
|
44
|
+
// It runs an LLM classify-union pass over unassigned pages, like
|
|
45
45
|
// memory_v2_consolidate; it must not sit in the fast lane and starve
|
|
46
|
-
// short jobs. The
|
|
47
|
-
expect(SLOW_LLM_JOB_TYPES).toContain("
|
|
46
|
+
// short jobs. The retired v3 job literals intentionally stay out.
|
|
47
|
+
expect(SLOW_LLM_JOB_TYPES).toContain("memory_v3_maintain");
|
|
48
|
+
expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_consolidate");
|
|
48
49
|
expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_index_maintenance");
|
|
49
50
|
expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_edge_learning");
|
|
50
51
|
});
|
|
@@ -57,7 +57,7 @@ let addMessageCalls: Array<{
|
|
|
57
57
|
conversationId: string;
|
|
58
58
|
role: string;
|
|
59
59
|
content: string;
|
|
60
|
-
|
|
60
|
+
options: unknown;
|
|
61
61
|
}> = [];
|
|
62
62
|
|
|
63
63
|
// Per-conversation overrides for getConversation. Lets fork-path tests stage
|
|
@@ -137,9 +137,9 @@ mock.module("../conversation-crud.js", () => ({
|
|
|
137
137
|
conversationId: string,
|
|
138
138
|
role: string,
|
|
139
139
|
content: string,
|
|
140
|
-
|
|
140
|
+
options: unknown,
|
|
141
141
|
) => {
|
|
142
|
-
addMessageCalls.push({ conversationId, role, content,
|
|
142
|
+
addMessageCalls.push({ conversationId, role, content, options });
|
|
143
143
|
},
|
|
144
144
|
deleteConversation: (id: string) => {
|
|
145
145
|
deletedConversationIds.push(id);
|
|
@@ -316,10 +316,11 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
316
316
|
expect(bootstrapCalls[0]!.forkParentConversationId).toBe("src-conv-1");
|
|
317
317
|
});
|
|
318
318
|
|
|
319
|
-
test("legacy path: wake
|
|
319
|
+
test("legacy path: wake is scoped to memory saves and suppresses the internal wake surface", async () => {
|
|
320
320
|
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
321
321
|
|
|
322
322
|
expect(wakeCalls).toHaveLength(1);
|
|
323
|
+
expect(wakeCalls[0]!.opts.allowedTools).toEqual(["remember"]);
|
|
323
324
|
expect(wakeCalls[0]!.opts.suppressWakeSurface).toBe(true);
|
|
324
325
|
});
|
|
325
326
|
|
|
@@ -519,7 +520,9 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
519
520
|
expect(addMessageCalls).toHaveLength(1);
|
|
520
521
|
expect(addMessageCalls[0]!.conversationId).toBe("fork-conv-1");
|
|
521
522
|
expect(addMessageCalls[0]!.role).toBe("user");
|
|
522
|
-
expect(
|
|
523
|
+
expect(
|
|
524
|
+
(addMessageCalls[0]!.options as Record<string, unknown>).metadata,
|
|
525
|
+
).toEqual({
|
|
523
526
|
kind: "memory_retrospective_instruction",
|
|
524
527
|
hidden: true,
|
|
525
528
|
});
|
|
@@ -535,7 +538,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
535
538
|
expect(forkCalls[0]!.groupId).toBe("system:background");
|
|
536
539
|
});
|
|
537
540
|
|
|
538
|
-
test("fork path: wake
|
|
541
|
+
test("fork path: wake is scoped to memory saves and suppresses the internal wake surface", async () => {
|
|
539
542
|
forkFlagEnabled = true;
|
|
540
543
|
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
541
544
|
|
|
@@ -543,6 +546,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
543
546
|
expect(wakeCalls).toHaveLength(1);
|
|
544
547
|
expect(wakeCalls[0]!.conversationId).toBe("fork-conv-1");
|
|
545
548
|
const opts = wakeCalls[0]!.opts;
|
|
549
|
+
expect(opts.allowedTools).toEqual(["remember"]);
|
|
546
550
|
expect(opts.suppressWakeSurface).toBe(true);
|
|
547
551
|
// Sanity: the other fork-specific opts the handler relies on are still set.
|
|
548
552
|
expect(opts.skipHintInjection).toBe(true);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
7
|
+
import { migrateAddMemoryV3Selections } from "../migrations/268-add-memory-v3-selections.js";
|
|
8
|
+
import * as schema from "../schema.js";
|
|
9
|
+
|
|
10
|
+
interface ColumnRow {
|
|
11
|
+
name: string;
|
|
12
|
+
type: string;
|
|
13
|
+
notnull: number;
|
|
14
|
+
dflt_value: string | null;
|
|
15
|
+
pk: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface IndexRow {
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createTestDb() {
|
|
23
|
+
const sqlite = new Database(":memory:");
|
|
24
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
25
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
26
|
+
return drizzle(sqlite, { schema });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("memory_v3_selections migration", () => {
|
|
30
|
+
test("creates table with expected columns", () => {
|
|
31
|
+
const db = createTestDb();
|
|
32
|
+
const raw = getSqliteFrom(db);
|
|
33
|
+
|
|
34
|
+
migrateAddMemoryV3Selections(db);
|
|
35
|
+
|
|
36
|
+
const columns = raw
|
|
37
|
+
.query(`PRAGMA table_info(memory_v3_selections)`)
|
|
38
|
+
.all() as ColumnRow[];
|
|
39
|
+
const byName = new Map(columns.map((c) => [c.name, c]));
|
|
40
|
+
|
|
41
|
+
expect(byName.get("conversation_id")?.type).toBe("TEXT");
|
|
42
|
+
expect(byName.get("conversation_id")?.notnull).toBe(1);
|
|
43
|
+
expect(byName.get("conversation_id")?.pk).toBe(1);
|
|
44
|
+
|
|
45
|
+
expect(byName.get("turn")?.type).toBe("INTEGER");
|
|
46
|
+
expect(byName.get("turn")?.notnull).toBe(1);
|
|
47
|
+
expect(byName.get("turn")?.pk).toBe(2);
|
|
48
|
+
|
|
49
|
+
expect(byName.get("slug")?.type).toBe("TEXT");
|
|
50
|
+
expect(byName.get("slug")?.notnull).toBe(1);
|
|
51
|
+
expect(byName.get("slug")?.pk).toBe(3);
|
|
52
|
+
|
|
53
|
+
expect(byName.get("source")?.type).toBe("TEXT");
|
|
54
|
+
expect(byName.get("source")?.notnull).toBe(1);
|
|
55
|
+
|
|
56
|
+
expect(byName.get("pinned")?.type).toBe("INTEGER");
|
|
57
|
+
expect(byName.get("pinned")?.notnull).toBe(1);
|
|
58
|
+
expect(byName.get("pinned")?.dflt_value).toBe("0");
|
|
59
|
+
|
|
60
|
+
expect(byName.get("created_at")?.type).toBe("INTEGER");
|
|
61
|
+
expect(byName.get("created_at")?.notnull).toBe(1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("creates the conversation-turn index", () => {
|
|
65
|
+
const db = createTestDb();
|
|
66
|
+
const raw = getSqliteFrom(db);
|
|
67
|
+
|
|
68
|
+
migrateAddMemoryV3Selections(db);
|
|
69
|
+
|
|
70
|
+
const indexes = raw
|
|
71
|
+
.query(`PRAGMA index_list(memory_v3_selections)`)
|
|
72
|
+
.all() as IndexRow[];
|
|
73
|
+
const indexNames = new Set(indexes.map((i) => i.name));
|
|
74
|
+
|
|
75
|
+
expect(indexNames.has("idx_memory_v3_selections_conv")).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("is idempotent — re-running does not throw and preserves rows", () => {
|
|
79
|
+
const db = createTestDb();
|
|
80
|
+
const raw = getSqliteFrom(db);
|
|
81
|
+
|
|
82
|
+
migrateAddMemoryV3Selections(db);
|
|
83
|
+
|
|
84
|
+
raw
|
|
85
|
+
.query(
|
|
86
|
+
/*sql*/ `
|
|
87
|
+
INSERT INTO memory_v3_selections (
|
|
88
|
+
conversation_id, turn, slug, source, pinned, created_at
|
|
89
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
90
|
+
`,
|
|
91
|
+
)
|
|
92
|
+
.run("conv-abc", 3, "page-slug", "lane-a", 1, 1000);
|
|
93
|
+
|
|
94
|
+
expect(() => migrateAddMemoryV3Selections(db)).not.toThrow();
|
|
95
|
+
|
|
96
|
+
const row = raw
|
|
97
|
+
.query(
|
|
98
|
+
`SELECT slug FROM memory_v3_selections WHERE conversation_id = 'conv-abc' AND turn = 3`,
|
|
99
|
+
)
|
|
100
|
+
.get() as { slug: string } | null;
|
|
101
|
+
expect(row?.slug).toBe("page-slug");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -290,9 +290,8 @@ export async function runAgenticRecall(
|
|
|
290
290
|
try {
|
|
291
291
|
response = await provider.sendMessage(
|
|
292
292
|
[userTextMessage(promptBundle.prompt)],
|
|
293
|
-
[...RECALL_AGENT_TOOL_DEFINITIONS],
|
|
294
|
-
undefined,
|
|
295
293
|
{
|
|
294
|
+
tools: [...RECALL_AGENT_TOOL_DEFINITIONS],
|
|
296
295
|
// `thinking: disabled` is required because we set `temperature: 0`
|
|
297
296
|
// explicitly. Anthropic 400s on `temperature` ≠ 1 whenever thinking
|
|
298
297
|
// is enabled or in adaptive mode; without this, profiles that
|
|
@@ -591,9 +590,8 @@ async function tryFinalFinishRecall(options: {
|
|
|
591
590
|
try {
|
|
592
591
|
response = await options.provider.sendMessage(
|
|
593
592
|
[userTextMessage(promptBundle.prompt)],
|
|
594
|
-
[FINISH_RECALL_TOOL_DEFINITION],
|
|
595
|
-
undefined,
|
|
596
593
|
{
|
|
594
|
+
tools: [FINISH_RECALL_TOOL_DEFINITION],
|
|
597
595
|
// `thinking: disabled` required for the same reason as the agent
|
|
598
596
|
// round above — Anthropic 400s on `temperature` ≠ 1 whenever
|
|
599
597
|
// thinking is enabled or in adaptive mode.
|
|
@@ -1195,23 +1195,29 @@ export function wipeConversation(id: string): WipeConversationResult {
|
|
|
1195
1195
|
};
|
|
1196
1196
|
}
|
|
1197
1197
|
|
|
1198
|
+
/** Options for {@link addMessage}. Only `skipIndexing` and `clientMessageId`
|
|
1199
|
+
* have defaults; `metadata` is genuinely optional. */
|
|
1200
|
+
export interface AddMessageOptions {
|
|
1201
|
+
metadata?: Record<string, unknown>;
|
|
1202
|
+
skipIndexing?: boolean;
|
|
1203
|
+
/** Client-generated nonce for idempotent inserts. When provided,
|
|
1204
|
+
* duplicate inserts for the same `(conversationId, clientMessageId)`
|
|
1205
|
+
* pair are silently skipped. */
|
|
1206
|
+
clientMessageId?: string;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1198
1209
|
/**
|
|
1199
1210
|
* Persist a message and run post-insert side effects (memory indexing,
|
|
1200
1211
|
* attention projection). Delegates the core insert + retry logic to
|
|
1201
1212
|
* {@link insertMessageCore}.
|
|
1202
|
-
*
|
|
1203
|
-
* @param clientMessageId Optional client-generated nonce. When
|
|
1204
|
-
* provided, duplicate inserts for the same `(conversationId,
|
|
1205
|
-
* clientMessageId)` pair are silently skipped (idempotent).
|
|
1206
1213
|
*/
|
|
1207
1214
|
export async function addMessage(
|
|
1208
1215
|
conversationId: string,
|
|
1209
1216
|
role: MessageRole,
|
|
1210
1217
|
content: string,
|
|
1211
|
-
|
|
1212
|
-
opts?: { skipIndexing?: boolean },
|
|
1213
|
-
clientMessageId?: string,
|
|
1218
|
+
options?: AddMessageOptions,
|
|
1214
1219
|
) {
|
|
1220
|
+
const { metadata, skipIndexing, clientMessageId } = options ?? {};
|
|
1215
1221
|
const inserted = await insertMessageCore({
|
|
1216
1222
|
conversationId,
|
|
1217
1223
|
role,
|
|
@@ -1226,7 +1232,7 @@ export async function addMessage(
|
|
|
1226
1232
|
|
|
1227
1233
|
const message = inserted;
|
|
1228
1234
|
|
|
1229
|
-
if (!
|
|
1235
|
+
if (!skipIndexing) {
|
|
1230
1236
|
try {
|
|
1231
1237
|
const config = getConfig();
|
|
1232
1238
|
const parsed = metadata
|
|
@@ -1613,6 +1619,31 @@ export function getLastUserTimestampBefore(
|
|
|
1613
1619
|
return row?.createdAt ?? 0;
|
|
1614
1620
|
}
|
|
1615
1621
|
|
|
1622
|
+
/**
|
|
1623
|
+
* Most recent user-message timestamp (epoch ms) across all conversations, or
|
|
1624
|
+
* `0` when no user message exists.
|
|
1625
|
+
*
|
|
1626
|
+
* Ordered by `created_at` rather than insertion order, because `forkConversation`
|
|
1627
|
+
* copies a parent's messages into the fork while preserving their original
|
|
1628
|
+
* `created_at`. Those copies receive fresh row ids, so an insertion-order scan
|
|
1629
|
+
* could surface an old forked turn as if it were the latest activity and let
|
|
1630
|
+
* maintenance run while the user is in fact active. The `(role, created_at)`
|
|
1631
|
+
* index (`idx_messages_role_created_at`) makes this an indexed seek to the
|
|
1632
|
+
* newest `role = "user"` row rather than a scan of the whole (potentially
|
|
1633
|
+
* multi-GB) table.
|
|
1634
|
+
*/
|
|
1635
|
+
export function getLastUserMessageTimestamp(): number {
|
|
1636
|
+
const db = getDb();
|
|
1637
|
+
const row = db
|
|
1638
|
+
.select({ createdAt: messages.createdAt })
|
|
1639
|
+
.from(messages)
|
|
1640
|
+
.where(eq(messages.role, "user"))
|
|
1641
|
+
.orderBy(desc(messages.createdAt))
|
|
1642
|
+
.limit(1)
|
|
1643
|
+
.get();
|
|
1644
|
+
return row?.createdAt ?? 0;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1616
1647
|
/** Fetch a single message by ID, optionally scoped to a specific conversation. */
|
|
1617
1648
|
export function getMessageById(
|
|
1618
1649
|
messageId: string,
|
|
@@ -11,6 +11,7 @@ import type { ConversationRow } from "./conversation-crud.js";
|
|
|
11
11
|
import { parseConversation } from "./conversation-crud.js";
|
|
12
12
|
import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
|
|
13
13
|
import { ensureGroupMigration } from "./conversation-group-migration.js";
|
|
14
|
+
import type { ConversationType } from "./conversation-types.js";
|
|
14
15
|
import { getDb } from "./db-connection.js";
|
|
15
16
|
import { rawAll } from "./raw-query.js";
|
|
16
17
|
import { conversations, messages } from "./schema.js";
|
|
@@ -48,30 +49,78 @@ export function buildFtsMatchQuery(
|
|
|
48
49
|
return unique.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* How {@link listConversations} (and friends) treats archived rows.
|
|
54
|
+
*
|
|
55
|
+
* - `"active"` — exclude rows with a non-null `archivedAt`. The default
|
|
56
|
+
* for sidebar lists, restore, CLI pickers, and anything user-facing.
|
|
57
|
+
* - `"archived"` — return ONLY archived rows. Powers the Archive page
|
|
58
|
+
* so it does not have to pull the entire conversation history and
|
|
59
|
+
* filter client-side.
|
|
60
|
+
* - `"all"` — include both. Reserved for migrations and back-compat
|
|
61
|
+
* call sites that genuinely want everything in one query.
|
|
62
|
+
*/
|
|
63
|
+
export type ArchiveStatusFilter = "active" | "archived" | "all";
|
|
64
|
+
|
|
65
|
+
function archiveStatusClause(status: ArchiveStatusFilter) {
|
|
66
|
+
switch (status) {
|
|
67
|
+
case "active":
|
|
68
|
+
return sql`${conversations.archivedAt} IS NULL`;
|
|
69
|
+
case "archived":
|
|
70
|
+
return sql`${conversations.archivedAt} IS NOT NULL`;
|
|
71
|
+
case "all":
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* SQL predicate selecting which bucket {@link listConversations} and
|
|
78
|
+
* {@link countConversations} return, keyed by the canonical
|
|
79
|
+
* {@link ConversationType}:
|
|
80
|
+
*
|
|
81
|
+
* - `"standard"` — the primary sidebar list: standard conversations only,
|
|
82
|
+
* excluding background, scheduled, and private rows. `"private"` is excluded
|
|
83
|
+
* defensively because in-place snapshot restore swaps the SQLite file without
|
|
84
|
+
* running migrations in-process, so legacy private rows can briefly exist
|
|
85
|
+
* before migration cleanup deletes them.
|
|
86
|
+
* - `"background"` — the background **umbrella**: background *and* scheduled
|
|
87
|
+
* rows together. The back-compat bucket for the single
|
|
88
|
+
* `conversationType=background` fetch that older clients (e.g. the macOS app,
|
|
89
|
+
* which ships out of lockstep with the daemon) rely on to populate both the
|
|
90
|
+
* Background and Scheduled sidebar sections from one request.
|
|
91
|
+
* - `"scheduled"` — scheduled rows only, so the Scheduled section can load
|
|
92
|
+
* independently of the broader background backlog without over-fetching it.
|
|
93
|
+
*
|
|
94
|
+
* `group_id` is matched alongside `conversationType` so conversations routed to
|
|
95
|
+
* `system:background` / `system:scheduled` (heartbeat, reminders, schedule-job
|
|
96
|
+
* runs) but created with conversationType `"standard"` still land in the
|
|
97
|
+
* correct bucket. Subagent runs are excluded from the background/scheduled
|
|
98
|
+
* buckets so the sidebar never surfaces them.
|
|
99
|
+
*/
|
|
100
|
+
function conversationTypeClause(type: ConversationType) {
|
|
101
|
+
const notSubagent = sql`(${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`;
|
|
102
|
+
switch (type) {
|
|
103
|
+
case "standard":
|
|
104
|
+
return sql`${conversations.conversationType} NOT IN ('background', 'scheduled', 'private') AND COALESCE(group_id, 'system:all') NOT IN ('system:background', 'system:scheduled')`;
|
|
105
|
+
case "background":
|
|
106
|
+
return sql`(${conversations.conversationType} IN ('background', 'scheduled') OR group_id IN ('system:background', 'system:scheduled')) AND ${notSubagent}`;
|
|
107
|
+
case "scheduled":
|
|
108
|
+
return sql`(${conversations.conversationType} = 'scheduled' OR group_id = 'system:scheduled') AND ${notSubagent}`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
51
112
|
export function listConversations(
|
|
52
113
|
limit?: number,
|
|
53
|
-
|
|
114
|
+
conversationType: ConversationType = "standard",
|
|
54
115
|
offset = 0,
|
|
55
|
-
|
|
116
|
+
archiveStatus: ArchiveStatusFilter = "active",
|
|
56
117
|
): ConversationRow[] {
|
|
57
118
|
ensureDisplayOrderMigration();
|
|
58
119
|
ensureGroupMigration();
|
|
59
120
|
const db = getDb();
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// lists until the next migration pass deletes them.
|
|
64
|
-
//
|
|
65
|
-
// group_id is checked alongside conversationType so that conversations
|
|
66
|
-
// routed to system:background (e.g. heartbeat) via conversationMetadata
|
|
67
|
-
// but created with conversationType "standard" (vellum channel strategy)
|
|
68
|
-
// appear in the correct bucket.
|
|
69
|
-
const typeCond = backgroundOnly
|
|
70
|
-
? sql`(${conversations.conversationType} IN ('background', 'scheduled') OR group_id IN ('system:background', 'system:scheduled')) AND (${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`
|
|
71
|
-
: sql`${conversations.conversationType} NOT IN ('background', 'scheduled', 'private') AND COALESCE(group_id, 'system:all') NOT IN ('system:background', 'system:scheduled')`;
|
|
72
|
-
const where = includeArchived
|
|
73
|
-
? typeCond
|
|
74
|
-
: sql`${typeCond} AND ${conversations.archivedAt} IS NULL`;
|
|
121
|
+
const typeCond = conversationTypeClause(conversationType);
|
|
122
|
+
const archiveCond = archiveStatusClause(archiveStatus);
|
|
123
|
+
const where = archiveCond ? sql`${typeCond} AND ${archiveCond}` : typeCond;
|
|
75
124
|
const query = db
|
|
76
125
|
.select()
|
|
77
126
|
.from(conversations)
|
|
@@ -172,10 +221,13 @@ export function getMessageRoleStatsByConversation(
|
|
|
172
221
|
);
|
|
173
222
|
}
|
|
174
223
|
|
|
175
|
-
export function listPinnedConversations(
|
|
224
|
+
export function listPinnedConversations(
|
|
225
|
+
archiveStatus: ArchiveStatusFilter = "active",
|
|
226
|
+
): ConversationRow[] {
|
|
176
227
|
ensureDisplayOrderMigration();
|
|
177
228
|
ensureGroupMigration();
|
|
178
229
|
const db = getDb();
|
|
230
|
+
const archiveCond = archiveStatusClause(archiveStatus);
|
|
179
231
|
const query = db
|
|
180
232
|
.select()
|
|
181
233
|
.from(conversations)
|
|
@@ -183,6 +235,7 @@ export function listPinnedConversations(): ConversationRow[] {
|
|
|
183
235
|
and(
|
|
184
236
|
sql`${conversations.conversationType} NOT IN ('background', 'scheduled', 'private')`,
|
|
185
237
|
sql`is_pinned = 1`,
|
|
238
|
+
...(archiveCond ? [archiveCond] : []),
|
|
186
239
|
),
|
|
187
240
|
)
|
|
188
241
|
.orderBy(
|
|
@@ -247,12 +300,15 @@ export function listConversationsByTitlePrefix(
|
|
|
247
300
|
}));
|
|
248
301
|
}
|
|
249
302
|
|
|
250
|
-
export function countConversations(
|
|
303
|
+
export function countConversations(
|
|
304
|
+
conversationType: ConversationType = "standard",
|
|
305
|
+
archiveStatus: ArchiveStatusFilter = "active",
|
|
306
|
+
): number {
|
|
251
307
|
ensureGroupMigration();
|
|
252
308
|
const db = getDb();
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
309
|
+
const typeCond = conversationTypeClause(conversationType);
|
|
310
|
+
const archiveCond = archiveStatusClause(archiveStatus);
|
|
311
|
+
const where = archiveCond ? sql`${typeCond} AND ${archiveCond}` : typeCond;
|
|
256
312
|
const [{ total }] = db
|
|
257
313
|
.select({ total: count() })
|
|
258
314
|
.from(conversations)
|
package/src/memory/db-init.ts
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
migrateActivationState,
|
|
44
44
|
migrateActivationStateFkCascade,
|
|
45
45
|
migrateAddConversationInferenceProfile,
|
|
46
|
+
migrateAddMemoryV3Selections,
|
|
46
47
|
migrateAddSourceTypeColumns,
|
|
47
48
|
migrateAssistantContactMetadata,
|
|
48
49
|
migrateBackfillAudioAttachmentMimeTypes,
|
|
@@ -125,6 +126,7 @@ import {
|
|
|
125
126
|
migrateLlmRequestLogsCreatedAtIndex,
|
|
126
127
|
migrateLlmUsageAddRawUsage,
|
|
127
128
|
migrateLlmUsageAttribution,
|
|
129
|
+
migrateLlmUsageEventsAddAssistantVersion,
|
|
128
130
|
migrateMemoryGraphImageRefs,
|
|
129
131
|
migrateMemoryItemSupersession,
|
|
130
132
|
migrateMemoryRecallLogsQueryContext,
|
|
@@ -137,6 +139,7 @@ import {
|
|
|
137
139
|
migrateMessagesClientMessageId,
|
|
138
140
|
migrateMessagesConversationCreatedAtIndex,
|
|
139
141
|
migrateMessagesFtsBackfill,
|
|
142
|
+
migrateMessagesRoleCreatedAtIndex,
|
|
140
143
|
migrateNormalizePhoneIdentities,
|
|
141
144
|
migrateNormalizeSlackExternalContent,
|
|
142
145
|
migrateNormalizeUserFileByPrincipal,
|
|
@@ -184,6 +187,7 @@ import {
|
|
|
184
187
|
migrateScheduleRetryPolicy,
|
|
185
188
|
migrateScheduleReuseConversation,
|
|
186
189
|
migrateScheduleScriptColumn,
|
|
190
|
+
migrateScheduleScriptTimeout,
|
|
187
191
|
migrateScheduleWakeConversationId,
|
|
188
192
|
migrateSchemaIndexesAndColumns,
|
|
189
193
|
migrateScrubCorruptedImageAttachments,
|
|
@@ -466,6 +470,10 @@ export function initializeDb(): void {
|
|
|
466
470
|
migrateLlmRequestLogCallSite,
|
|
467
471
|
migrateDropProviderConnectionStatus,
|
|
468
472
|
migrateMessagesClientMessageId,
|
|
473
|
+
migrateLlmUsageEventsAddAssistantVersion,
|
|
474
|
+
migrateAddMemoryV3Selections,
|
|
475
|
+
migrateScheduleScriptTimeout,
|
|
476
|
+
migrateMessagesRoleCreatedAtIndex,
|
|
469
477
|
];
|
|
470
478
|
|
|
471
479
|
// Run each migration step, catching and logging individual failures so one
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { statSync } from "node:fs";
|
|
2
2
|
|
|
3
|
+
import { getConfig } from "../config/loader.js";
|
|
3
4
|
import { getLogger } from "../util/logger.js";
|
|
4
5
|
import { getDbPath } from "../util/platform.js";
|
|
5
6
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
|
|
7
|
+
import { getLastUserMessageTimestamp } from "./conversation-crud.js";
|
|
6
8
|
import { runAsyncSqlite } from "./db-async-query.js";
|
|
7
9
|
import { getSqlite } from "./db-connection.js";
|
|
8
10
|
|
|
9
11
|
const log = getLogger("db-maintenance");
|
|
10
12
|
|
|
11
13
|
const DB_MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
|
|
12
|
-
const DB_MAINTENANCE_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
13
14
|
|
|
14
15
|
interface DbStats {
|
|
15
16
|
pageSize: number;
|
|
@@ -101,11 +102,26 @@ async function runDbMaintenance(): Promise<void> {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
export async function maybeRunDbMaintenance(nowMs = Date.now()): Promise<void> {
|
|
105
|
+
const { intervalMs, quietPeriodMs } = getConfig().memory.maintenance;
|
|
106
|
+
|
|
104
107
|
const lastRun = parseInt(
|
|
105
108
|
getMemoryCheckpoint(DB_MAINTENANCE_CHECKPOINT_KEY) ?? "0",
|
|
106
109
|
10,
|
|
107
110
|
);
|
|
108
|
-
if (nowMs - lastRun <
|
|
111
|
+
if (nowMs - lastRun < intervalMs) return;
|
|
112
|
+
|
|
113
|
+
// VACUUM holds an exclusive lock on the database for its full duration —
|
|
114
|
+
// minutes on a multi-GB DB — during which every other write fails with
|
|
115
|
+
// SQLITE_BUSY ("database is locked"). Defer maintenance until the user has
|
|
116
|
+
// been quiet for `quietPeriodMs` so that lock never lands mid-conversation.
|
|
117
|
+
// The checkpoint below is only written once maintenance actually runs, so a
|
|
118
|
+
// deferred run is simply retried on a later (still-idle) worker tick.
|
|
119
|
+
if (quietPeriodMs > 0) {
|
|
120
|
+
const lastUserMessageAt = getLastUserMessageTimestamp();
|
|
121
|
+
if (lastUserMessageAt > 0 && nowMs - lastUserMessageAt < quietPeriodMs) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
109
125
|
|
|
110
126
|
try {
|
|
111
127
|
await runDbMaintenance();
|