@vellumai/assistant 0.8.3 → 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/docker-entrypoint.sh +0 -1
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +1492 -100
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +302 -33
- package/src/__tests__/approval-cascade.test.ts +1 -1
- 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 +4 -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-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +2 -1
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -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__/config-watcher.test.ts +1 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
- package/src/__tests__/conversation-agent-loop.test.ts +188 -129
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- 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 +324 -0
- package/src/__tests__/conversation-lifecycle.test.ts +53 -12
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- 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-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -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 +7 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- 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__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- 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 +161 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/log-export-routes.test.ts +99 -2
- 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__/message-queue-steer.test.ts +114 -0
- 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 +151 -0
- package/src/__tests__/openai-responses-provider.test.ts +118 -16
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -5
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- 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__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- 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__/server-history-render.test.ts +83 -4
- 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__/steer-tool-repair.test.ts +249 -0
- 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 +161 -124
- package/src/__tests__/terminal-tools.test.ts +12 -2
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- 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 +89 -53
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- 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__/twilio-routes.test.ts +1 -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__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +94 -10
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- 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/attachments.ts +1 -0
- package/src/agent/loop.ts +65 -20
- 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/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- 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 +10 -12
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference-providers.ts +147 -1
- 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 +483 -0
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/program.ts +2 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.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/SKILL.md +8 -0
- 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-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +24 -12
- package/src/config/call-site-defaults.ts +20 -0
- package/src/config/feature-flag-registry.json +115 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
- package/src/config/schemas/call-site-catalog.ts +35 -0
- package/src/config/schemas/llm.ts +14 -0
- package/src/config/schemas/memory-v2.ts +294 -1
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +60 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
- package/src/daemon/conversation-agent-loop.ts +307 -88
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +149 -118
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +145 -84
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +70 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +54 -32
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +2 -0
- package/src/daemon/handlers/conversations.ts +90 -3
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +5 -5
- package/src/daemon/host-file-proxy.ts +5 -5
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +11 -11
- package/src/daemon/lifecycle.ts +40 -23
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +14 -9
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +66 -7
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- 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/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +85 -0
- package/src/home/suggested-prompts.ts +168 -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__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +191 -100
- 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 +26 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- 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 +68 -15
- 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-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +11 -3
- package/src/memory/memory-retrospective-job.ts +413 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +41 -14
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- 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 +34 -0
- package/src/memory/migrations/registry.ts +58 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +22 -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-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +158 -112
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +660 -4
- package/src/memory/v2/consolidation-job.ts +14 -0
- 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-events.ts +101 -0
- package/src/memory/v2/injection.ts +42 -25
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +369 -62
- 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/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +38 -50
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +8 -5
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +6 -3
- 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 +100 -20
- 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__/system-prompt.test.ts +46 -2
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +69 -19
- package/src/prompts/system-prompt.ts +118 -216
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +10 -2
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +281 -9
- 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 +159 -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/fireworks/client.ts +20 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +18 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +91 -1
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +63 -23
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +86 -23
- package/src/providers/openrouter/client.ts +5 -1
- 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/types.ts +25 -0
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +152 -56
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +43 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -6
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +50 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/approval-routes.ts +4 -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/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +147 -2
- 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 +196 -31
- package/src/runtime/routes/conversation-routes.ts +472 -425
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- 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/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +17 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +20 -4
- 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/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +427 -0
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/sanity-routes.ts +159 -0
- 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 +188 -0
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/services/conversation-serializer.ts +30 -4
- 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/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- 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 +150 -0
- package/src/tools/browser/browser-execution.ts +106 -0
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
- package/src/tools/browser/cdp-client/factory.ts +171 -4
- package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
- package/src/tools/browser/cdp-client/types.ts +101 -0
- package/src/tools/browser/pinned-tabs.ts +146 -0
- 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-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +187 -2
- 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/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +216 -73
- package/src/tools/network/web-search.ts +216 -98
- 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/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +31 -98
- package/src/tools/ui-surface/definitions.ts +9 -23
- package/src/types/onboarding-context.ts +4 -0
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -7
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +4 -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/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/prompts/cache-boundary.ts +0 -8
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -6,11 +6,22 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Two sources of prompts:
|
|
8
8
|
* - **Deterministic** — derived from missing OAuth connections.
|
|
9
|
+
* Computed inline (read-only, safe for GET).
|
|
9
10
|
* - **Assistant-generated** — contextual suggestions from the LLM
|
|
10
|
-
*
|
|
11
|
+
* based on what's relevant to the user. Read from an in-memory
|
|
12
|
+
* cache in the GET path; generation runs in the background via
|
|
13
|
+
* `refreshAssistantSuggestedPrompts`.
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
|
-
import {
|
|
16
|
+
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
17
|
+
import { getConfig } from "../config/loader.js";
|
|
18
|
+
import { listProviders } from "../oauth/oauth-store.js";
|
|
19
|
+
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
20
|
+
import { getConfiguredProvider } from "../providers/provider-send-message.js";
|
|
21
|
+
import { buildAssistantEvent } from "../runtime/assistant-event.js";
|
|
22
|
+
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
23
|
+
import { runBtwSidechain } from "../runtime/btw-sidechain.js";
|
|
24
|
+
import { isOAuthProviderConnected } from "../schedule/integration-status.js";
|
|
14
25
|
import { getLogger } from "../util/logger.js";
|
|
15
26
|
import type { SuggestedPrompt } from "./feed-types.js";
|
|
16
27
|
|
|
@@ -72,27 +83,88 @@ const CONNECT_PROMPT_META: Record<
|
|
|
72
83
|
},
|
|
73
84
|
};
|
|
74
85
|
|
|
86
|
+
const LLM_SUGGESTIONS_TIMEOUT_MS = 5_000;
|
|
87
|
+
const LLM_CACHE_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// In-memory cache for LLM-generated suggestions
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
let cachedLLMPrompts: SuggestedPrompt[] = [];
|
|
94
|
+
let cachedLLMPromptsAt = 0;
|
|
95
|
+
|
|
75
96
|
/**
|
|
76
|
-
* Produce
|
|
77
|
-
*
|
|
78
|
-
*
|
|
97
|
+
* Produce suggested prompts from both deterministic and cached LLM sources.
|
|
98
|
+
* Deterministic prompts always come first; cached LLM-generated prompts are
|
|
99
|
+
* appended when available. No LLM calls happen in this path — safe for GET.
|
|
79
100
|
*/
|
|
80
101
|
export async function getSuggestedPrompts(): Promise<SuggestedPrompt[]> {
|
|
81
102
|
const prompts: SuggestedPrompt[] = [];
|
|
82
103
|
|
|
104
|
+
let deterministicPrompts: SuggestedPrompt[] = [];
|
|
83
105
|
try {
|
|
84
|
-
|
|
106
|
+
deterministicPrompts = await getDeterministicPrompts();
|
|
85
107
|
prompts.push(...deterministicPrompts);
|
|
86
108
|
} catch (err) {
|
|
87
109
|
log.warn({ err }, "Failed to compute deterministic suggested prompts");
|
|
88
110
|
}
|
|
89
111
|
|
|
90
|
-
|
|
91
|
-
|
|
112
|
+
if (Date.now() - cachedLLMPromptsAt < LLM_CACHE_TTL_MS) {
|
|
113
|
+
prompts.push(...cachedLLMPrompts);
|
|
114
|
+
}
|
|
92
115
|
|
|
93
116
|
return prompts;
|
|
94
117
|
}
|
|
95
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Drops the in-memory LLM suggestion cache so the next call to
|
|
121
|
+
* `getSuggestedPrompts()` returns only the fresh deterministic list (and
|
|
122
|
+
* a follow-up background refresh repopulates the LLM half).
|
|
123
|
+
*
|
|
124
|
+
* Called from OAuth connect/disconnect paths so a freshly-connected
|
|
125
|
+
* provider stops surfacing as a "Connect X" pill within one reload — the
|
|
126
|
+
* 30-minute TTL would otherwise pin a stale suggestion until the next
|
|
127
|
+
* periodic refresh.
|
|
128
|
+
*/
|
|
129
|
+
export function invalidateAssistantSuggestedPromptsCache(): void {
|
|
130
|
+
cachedLLMPrompts = [];
|
|
131
|
+
cachedLLMPromptsAt = 0;
|
|
132
|
+
assistantEventHub
|
|
133
|
+
.publish(
|
|
134
|
+
buildAssistantEvent({
|
|
135
|
+
type: "home_feed_updated",
|
|
136
|
+
updatedAt: new Date().toISOString(),
|
|
137
|
+
newItemCount: 0,
|
|
138
|
+
}),
|
|
139
|
+
)
|
|
140
|
+
.catch((err) => {
|
|
141
|
+
log.warn(
|
|
142
|
+
{ err },
|
|
143
|
+
"Failed to publish home_feed_updated after prompt cache invalidation",
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generate LLM-based suggestion prompts and write them to the in-memory
|
|
150
|
+
* cache. No-ops when the cache is still fresh. Intended for background
|
|
151
|
+
* invocation (daemon startup / periodic refresh), not the GET path.
|
|
152
|
+
*/
|
|
153
|
+
export async function refreshAssistantSuggestedPrompts(): Promise<void> {
|
|
154
|
+
if (Date.now() - cachedLLMPromptsAt < LLM_CACHE_TTL_MS) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const deterministicPrompts = await getDeterministicPrompts();
|
|
160
|
+
const llmPrompts = await generateAssistantPrompts(deterministicPrompts);
|
|
161
|
+
cachedLLMPrompts = llmPrompts;
|
|
162
|
+
cachedLLMPromptsAt = Date.now();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
log.warn({ err }, "Failed to refresh assistant suggested prompts");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
96
168
|
/**
|
|
97
169
|
* Check which well-known OAuth providers are not connected and return
|
|
98
170
|
* a "Connect X" prompt for each. For connected providers that have
|
|
@@ -107,7 +179,7 @@ async function getDeterministicPrompts(): Promise<SuggestedPrompt[]> {
|
|
|
107
179
|
const meta = CONNECT_PROMPT_META[provider.provider];
|
|
108
180
|
if (!meta) continue;
|
|
109
181
|
|
|
110
|
-
const connected = await
|
|
182
|
+
const connected = await isOAuthProviderConnected(provider.provider);
|
|
111
183
|
|
|
112
184
|
if (!connected) {
|
|
113
185
|
prompts.push({
|
|
@@ -135,3 +207,90 @@ async function getDeterministicPrompts(): Promise<SuggestedPrompt[]> {
|
|
|
135
207
|
|
|
136
208
|
return prompts;
|
|
137
209
|
}
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// LLM-generated suggestions
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
interface LLMSuggestion {
|
|
216
|
+
label: string;
|
|
217
|
+
prompt: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Ask the LLM to generate contextual conversation-starter suggestions
|
|
222
|
+
* based on the assistant's persona and the user's connected services.
|
|
223
|
+
* Returns an empty array on failure so deterministic prompts still show.
|
|
224
|
+
*/
|
|
225
|
+
async function generateAssistantPrompts(
|
|
226
|
+
deterministicPrompts: SuggestedPrompt[],
|
|
227
|
+
): Promise<SuggestedPrompt[]> {
|
|
228
|
+
const config = getConfig();
|
|
229
|
+
const resolved = resolveCallSiteConfig("homeSuggestedPrompts", config.llm);
|
|
230
|
+
|
|
231
|
+
const provider = await getConfiguredProvider("homeSuggestedPrompts");
|
|
232
|
+
if (!provider) {
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const systemPrompt = buildSystemPrompt({
|
|
237
|
+
excludeBootstrap: true,
|
|
238
|
+
excludeCustomPrefix: true,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const existingLabels = deterministicPrompts.map((p) => p.label).join(", ");
|
|
242
|
+
const contextNote = existingLabels
|
|
243
|
+
? `The user already has these suggestions: ${existingLabels}. Do NOT duplicate them.`
|
|
244
|
+
: "";
|
|
245
|
+
|
|
246
|
+
const result = await runBtwSidechain({
|
|
247
|
+
content:
|
|
248
|
+
"Suggest 2-3 short, actionable conversation starters for the home page. " +
|
|
249
|
+
"Each should be something specific and helpful you can do for the user right now. " +
|
|
250
|
+
`${contextNote} ` +
|
|
251
|
+
'Return ONLY a JSON array of objects with "label" (max 5 words) and "prompt" (the full message to send). ' +
|
|
252
|
+
"No markdown fences, no explanation.",
|
|
253
|
+
provider,
|
|
254
|
+
systemPrompt,
|
|
255
|
+
messages: [],
|
|
256
|
+
tools: [],
|
|
257
|
+
callSite: "homeSuggestedPrompts",
|
|
258
|
+
maxTokens: resolved.maxTokens,
|
|
259
|
+
timeoutMs: LLM_SUGGESTIONS_TIMEOUT_MS,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const text = result.text.trim();
|
|
263
|
+
if (!text) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const parsed = parseLLMSuggestions(text);
|
|
268
|
+
return parsed.map((s, i) => ({
|
|
269
|
+
id: `assistant-${i}-${s.label.toLowerCase().replace(/\s+/g, "-")}`,
|
|
270
|
+
label: s.label,
|
|
271
|
+
prompt: s.prompt,
|
|
272
|
+
source: "assistant" as const,
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function parseLLMSuggestions(text: string): LLMSuggestion[] {
|
|
277
|
+
try {
|
|
278
|
+
const cleaned = text
|
|
279
|
+
.replace(/^```(?:json)?\n?/m, "")
|
|
280
|
+
.replace(/\n?```$/m, "");
|
|
281
|
+
const parsed = JSON.parse(cleaned);
|
|
282
|
+
if (!Array.isArray(parsed)) {
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
return parsed.filter(
|
|
286
|
+
(item): item is LLMSuggestion =>
|
|
287
|
+
typeof item === "object" &&
|
|
288
|
+
item !== null &&
|
|
289
|
+
typeof item.label === "string" &&
|
|
290
|
+
typeof item.prompt === "string",
|
|
291
|
+
);
|
|
292
|
+
} catch {
|
|
293
|
+
log.warn("Failed to parse LLM suggestions response");
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { connect, type Socket } from "node:net";
|
|
2
|
+
|
|
3
|
+
import { refreshOverridesFromGateway } from "../config/assistant-feature-flags.js";
|
|
4
|
+
import { SYNC_TAGS } from "../daemon/message-types/sync.js";
|
|
5
|
+
import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
|
|
6
|
+
import { getLogger } from "../util/logger.js";
|
|
7
|
+
import { resolveIpcSocketPath } from "./socket-path.js";
|
|
8
|
+
|
|
9
|
+
const log = getLogger("gateway-flag-listener");
|
|
10
|
+
|
|
11
|
+
const MAX_BACKOFF_MS = 30_000;
|
|
12
|
+
const INITIAL_BACKOFF_MS = 1_000;
|
|
13
|
+
|
|
14
|
+
let socket: Socket | null = null;
|
|
15
|
+
let stopped = false;
|
|
16
|
+
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
17
|
+
let currentBackoffMs = INITIAL_BACKOFF_MS;
|
|
18
|
+
|
|
19
|
+
function getSocketPath(): string {
|
|
20
|
+
return resolveIpcSocketPath("gateway").path;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function handleData(chunk: Buffer): void {
|
|
24
|
+
const lines = chunk.toString().split("\n");
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
if (!trimmed) continue;
|
|
28
|
+
try {
|
|
29
|
+
const msg = JSON.parse(trimmed) as { event?: string };
|
|
30
|
+
if (msg.event === "feature_flags_changed") {
|
|
31
|
+
log.info("Received feature_flags_changed event — refreshing overrides");
|
|
32
|
+
refreshOverridesFromGateway().catch((err) => {
|
|
33
|
+
log.warn({ err }, "Failed to refresh feature flag overrides");
|
|
34
|
+
});
|
|
35
|
+
// Fan out to every connected web client so React Query caches
|
|
36
|
+
// for `/v1/feature-flags/client-flag-values/` and
|
|
37
|
+
// `/v1/assistants/:id/feature-flags` invalidate immediately
|
|
38
|
+
// instead of waiting on a 5s polling tick.
|
|
39
|
+
publishSyncInvalidation([
|
|
40
|
+
SYNC_TAGS.featureFlagsClient,
|
|
41
|
+
SYNC_TAGS.featureFlagsAssistant,
|
|
42
|
+
]).catch((err) => {
|
|
43
|
+
log.warn({ err }, "Failed to broadcast feature-flags sync_changed");
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// Ignore non-JSON lines (e.g. IPC responses on a shared socket)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function scheduleReconnect(): void {
|
|
53
|
+
if (stopped) return;
|
|
54
|
+
reconnectTimer = setTimeout(() => {
|
|
55
|
+
reconnectTimer = null;
|
|
56
|
+
connectToGateway();
|
|
57
|
+
}, currentBackoffMs);
|
|
58
|
+
currentBackoffMs = Math.min(currentBackoffMs * 2, MAX_BACKOFF_MS);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function connectToGateway(): void {
|
|
62
|
+
if (stopped) return;
|
|
63
|
+
|
|
64
|
+
const socketPath = getSocketPath();
|
|
65
|
+
const conn = connect(socketPath);
|
|
66
|
+
|
|
67
|
+
conn.on("connect", () => {
|
|
68
|
+
if (stopped) {
|
|
69
|
+
conn.destroy();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
log.info("Connected to gateway IPC for flag events");
|
|
73
|
+
currentBackoffMs = INITIAL_BACKOFF_MS;
|
|
74
|
+
socket = conn;
|
|
75
|
+
refreshOverridesFromGateway().catch((err) => {
|
|
76
|
+
log.warn({ err }, "Failed to refresh feature flag overrides on reconnect");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
let buffer = "";
|
|
81
|
+
conn.on("data", (chunk) => {
|
|
82
|
+
buffer += chunk.toString();
|
|
83
|
+
let newlineIdx: number;
|
|
84
|
+
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
85
|
+
const line = buffer.slice(0, newlineIdx);
|
|
86
|
+
buffer = buffer.slice(newlineIdx + 1);
|
|
87
|
+
if (line.trim()) {
|
|
88
|
+
handleData(Buffer.from(line));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
conn.on("close", () => {
|
|
94
|
+
socket = null;
|
|
95
|
+
if (!stopped) {
|
|
96
|
+
log.debug("Gateway IPC connection closed — reconnecting");
|
|
97
|
+
scheduleReconnect();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
conn.on("error", (err) => {
|
|
102
|
+
log.debug({ err }, "Gateway IPC connection error");
|
|
103
|
+
conn.destroy();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function startGatewayFlagListener(): void {
|
|
108
|
+
stopped = false;
|
|
109
|
+
currentBackoffMs = INITIAL_BACKOFF_MS;
|
|
110
|
+
connectToGateway();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function stopGatewayFlagListener(): void {
|
|
114
|
+
stopped = true;
|
|
115
|
+
if (reconnectTimer) {
|
|
116
|
+
clearTimeout(reconnectTimer);
|
|
117
|
+
reconnectTimer = null;
|
|
118
|
+
}
|
|
119
|
+
if (socket) {
|
|
120
|
+
socket.destroy();
|
|
121
|
+
socket = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -22,12 +22,9 @@ import { z } from "zod";
|
|
|
22
22
|
import type { MeetHostSupervisor } from "../../daemon/meet-host-supervisor.js";
|
|
23
23
|
import { registerShutdownHook } from "../../daemon/shutdown-registry.js";
|
|
24
24
|
import { registerSkillRoute } from "../../runtime/skill-route-registry.js";
|
|
25
|
+
import { resolveExecutionTarget } from "../../tools/execution-target.js";
|
|
25
26
|
import { registerSkillTools } from "../../tools/registry.js";
|
|
26
|
-
import type {
|
|
27
|
-
ExecutionTarget,
|
|
28
|
-
Tool,
|
|
29
|
-
ToolDefinition,
|
|
30
|
-
} from "../../tools/types.js";
|
|
27
|
+
import type { ExecutionTarget, Tool } from "../../tools/types.js";
|
|
31
28
|
import { RiskLevel } from "../../tools/types.js";
|
|
32
29
|
import { getLogger } from "../../util/logger.js";
|
|
33
30
|
import type { SkillIpcRoute } from "../skill-ipc-types.js";
|
|
@@ -182,25 +179,24 @@ export function __getActiveSessionCountForTesting(): number {
|
|
|
182
179
|
* exercised end-to-end.
|
|
183
180
|
*/
|
|
184
181
|
function buildProxyTool(manifest: ToolManifest): Tool {
|
|
185
|
-
const definition: ToolDefinition = {
|
|
186
|
-
name: manifest.name,
|
|
187
|
-
description: manifest.description,
|
|
188
|
-
input_schema: manifest.input_schema as object,
|
|
189
|
-
};
|
|
190
182
|
// RiskLevel is a string enum whose values are "low" | "medium" | "high",
|
|
191
183
|
// matching the schema above exactly — the cast is a no-op at runtime.
|
|
192
184
|
return {
|
|
193
185
|
name: manifest.name,
|
|
194
186
|
description: manifest.description,
|
|
187
|
+
input_schema: manifest.input_schema as object,
|
|
195
188
|
category: manifest.category,
|
|
196
189
|
defaultRiskLevel: manifest.defaultRiskLevel as RiskLevel,
|
|
197
190
|
executionMode: manifest.executionMode ?? "proxy",
|
|
198
|
-
executionTarget:
|
|
191
|
+
executionTarget: resolveExecutionTarget({
|
|
192
|
+
name: manifest.name,
|
|
193
|
+
executionTarget: manifest.executionTarget as ExecutionTarget | undefined,
|
|
194
|
+
executionMode: manifest.executionMode ?? "proxy",
|
|
195
|
+
}),
|
|
199
196
|
origin: "skill",
|
|
200
197
|
ownerSkillId: manifest.ownerSkillId,
|
|
201
198
|
ownerSkillBundled: manifest.ownerSkillBundled,
|
|
202
199
|
ownerSkillVersionHash: manifest.ownerSkillVersionHash,
|
|
203
|
-
getDefinition: () => definition,
|
|
204
200
|
execute: async () => {
|
|
205
201
|
// Only reached when no supervisor is attached (tests/boot race);
|
|
206
202
|
// the supervisor short-circuit above replaces this with the
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `db-async-query.ts` — the runAsyncSqlite abstraction.
|
|
3
|
+
*
|
|
4
|
+
* The contract this PR locks in:
|
|
5
|
+
* 1. **The main event loop keeps ticking while a long SQLite
|
|
6
|
+
* statement is in flight via the sqlite3 CLI backend.** This is
|
|
7
|
+
* the structural anti-block assertion — a recursive `setImmediate`
|
|
8
|
+
* probe counts event-loop iterations during the operation; if
|
|
9
|
+
* anyone moves the slow path back onto the main thread,
|
|
10
|
+
* `bun:sqlite` is synchronous and tick counting collapses to ~0;
|
|
11
|
+
* this test fails loudly.
|
|
12
|
+
* 2. The CLI backend reports `backend: "sqlite3-cli"` and `ok: true`
|
|
13
|
+
* on success.
|
|
14
|
+
* 3. The in-process fallback backend executes the statement
|
|
15
|
+
* synchronously and reports `backend: "in-process-blocking"`.
|
|
16
|
+
* 4. Errors from sqlite3 surface as `ok: false` with the stderr
|
|
17
|
+
* preserved in `error`.
|
|
18
|
+
*/
|
|
19
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
20
|
+
|
|
21
|
+
const { getSqlite } = await import("../db-connection.js");
|
|
22
|
+
const { initializeDb } = await import("../db-init.js");
|
|
23
|
+
const { runAsyncSqlite, _resetFallbackWarning } =
|
|
24
|
+
await import("../db-async-query.js");
|
|
25
|
+
const { findSqlite3 } = await import("../../util/sqlite3-runtime.js");
|
|
26
|
+
|
|
27
|
+
initializeDb();
|
|
28
|
+
|
|
29
|
+
const sqlite3Available = findSqlite3() !== undefined;
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
_resetFallbackWarning();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
function inflateAndDelete(byteTarget: number): void {
|
|
36
|
+
const sqlite = getSqlite();
|
|
37
|
+
sqlite.exec(
|
|
38
|
+
"CREATE TABLE IF NOT EXISTS async_bloat (id INTEGER PRIMARY KEY, payload BLOB)",
|
|
39
|
+
);
|
|
40
|
+
const pageSize = (
|
|
41
|
+
sqlite.query("PRAGMA page_size").get() as { page_size: number }
|
|
42
|
+
).page_size;
|
|
43
|
+
const rowsTarget = Math.max(1, Math.ceil(byteTarget / pageSize));
|
|
44
|
+
const payload = new Uint8Array(Math.max(1, pageSize - 64));
|
|
45
|
+
const insert = sqlite.prepare("INSERT INTO async_bloat (payload) VALUES (?)");
|
|
46
|
+
sqlite.exec("BEGIN");
|
|
47
|
+
for (let i = 0; i < rowsTarget; i++) {
|
|
48
|
+
insert.run(payload);
|
|
49
|
+
}
|
|
50
|
+
sqlite.exec("COMMIT");
|
|
51
|
+
sqlite.exec("DELETE FROM async_bloat");
|
|
52
|
+
sqlite.exec("DROP TABLE async_bloat");
|
|
53
|
+
sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
describe("runAsyncSqlite", () => {
|
|
57
|
+
test("returns ok=true for a trivial statement", async () => {
|
|
58
|
+
const result = await runAsyncSqlite("SELECT 1");
|
|
59
|
+
expect(result.ok).toBe(true);
|
|
60
|
+
expect(result.error).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("in-process fallback reports the right backend", async () => {
|
|
64
|
+
const result = await runAsyncSqlite("SELECT 1", {
|
|
65
|
+
forceBackend: "in-process-blocking",
|
|
66
|
+
});
|
|
67
|
+
expect(result.ok).toBe(true);
|
|
68
|
+
expect(result.backend).toBe("in-process-blocking");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("in-process fallback emits changes() count on stdout after a DELETE", async () => {
|
|
72
|
+
// Regression for Codex P1 on #31894: callers (e.g. prune jobs) rely
|
|
73
|
+
// on reading the row count off `result.stdout`. The CLI backend
|
|
74
|
+
// populates this naturally when the SQL ends with `SELECT changes();`,
|
|
75
|
+
// but `bun:sqlite`'s `exec()` discards SELECT results — so the
|
|
76
|
+
// in-process backend has to synthesize the same line, or the
|
|
77
|
+
// re-enqueue gate in pruneOld*Job silently never fires on hosts
|
|
78
|
+
// without the sqlite3 CLI.
|
|
79
|
+
const sqlite = getSqlite();
|
|
80
|
+
sqlite.exec(
|
|
81
|
+
"CREATE TABLE IF NOT EXISTS async_changes_probe (id INTEGER PRIMARY KEY)",
|
|
82
|
+
);
|
|
83
|
+
sqlite.exec("DELETE FROM async_changes_probe");
|
|
84
|
+
sqlite.exec(
|
|
85
|
+
"INSERT INTO async_changes_probe (id) VALUES (1),(2),(3),(4),(5)",
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const result = await runAsyncSqlite(
|
|
89
|
+
"DELETE FROM async_changes_probe WHERE id <= 3; SELECT changes();",
|
|
90
|
+
{ forceBackend: "in-process-blocking" },
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(result.ok).toBe(true);
|
|
94
|
+
expect(result.backend).toBe("in-process-blocking");
|
|
95
|
+
// The synthesized stdout matches what the CLI backend would emit:
|
|
96
|
+
// a bare integer on its own line. The exact format keeps the
|
|
97
|
+
// parser in cleanup.ts backend-agnostic.
|
|
98
|
+
expect(result.stdout).toBe("3\n");
|
|
99
|
+
|
|
100
|
+
sqlite.exec("DROP TABLE async_changes_probe");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test.if(sqlite3Available)(
|
|
104
|
+
"sqlite3 CLI backend reports the right backend on success",
|
|
105
|
+
async () => {
|
|
106
|
+
const result = await runAsyncSqlite("SELECT 1");
|
|
107
|
+
expect(result.ok).toBe(true);
|
|
108
|
+
expect(result.backend).toBe("sqlite3-cli");
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
test.if(sqlite3Available)(
|
|
113
|
+
"surfaces sqlite3 errors as ok=false with the message preserved",
|
|
114
|
+
async () => {
|
|
115
|
+
// Intentional SQL syntax error.
|
|
116
|
+
const result = await runAsyncSqlite("THIS IS NOT VALID SQL");
|
|
117
|
+
expect(result.ok).toBe(false);
|
|
118
|
+
expect(result.backend).toBe("sqlite3-cli");
|
|
119
|
+
expect(result.error).toBeTruthy();
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
test.if(sqlite3Available)(
|
|
124
|
+
"VACUUM via sqlite3 CLI keeps the event loop ticking (anti-block)",
|
|
125
|
+
async () => {
|
|
126
|
+
// Inflate the DB so VACUUM has measurable work to do. Without
|
|
127
|
+
// this the subprocess finishes in single-digit ms and the
|
|
128
|
+
// probe has no opportunity to record meaningful ticks.
|
|
129
|
+
inflateAndDelete(8 * 1024 * 1024);
|
|
130
|
+
|
|
131
|
+
// Probe the event loop with recursive setImmediate. This fires
|
|
132
|
+
// on every event-loop iteration with no minimum delay, so on a
|
|
133
|
+
// healthy unblocked loop it produces tens of thousands of ticks
|
|
134
|
+
// per second (vs. setInterval(1) which is capped by the host's
|
|
135
|
+
// timer resolution — observed at ~32 ms on GitHub Actions
|
|
136
|
+
// runners). If anyone moves VACUUM back onto the main thread,
|
|
137
|
+
// `bun:sqlite` is sync and tick count collapses to ~0; the
|
|
138
|
+
// assertion below fails loudly. The signal is intentionally
|
|
139
|
+
// binary: "many" vs "none".
|
|
140
|
+
let tickCount = 0;
|
|
141
|
+
let probing = true;
|
|
142
|
+
const tick = (): void => {
|
|
143
|
+
if (!probing) return;
|
|
144
|
+
tickCount += 1;
|
|
145
|
+
setImmediate(tick);
|
|
146
|
+
};
|
|
147
|
+
setImmediate(tick);
|
|
148
|
+
|
|
149
|
+
let result;
|
|
150
|
+
try {
|
|
151
|
+
result = await runAsyncSqlite("VACUUM");
|
|
152
|
+
} finally {
|
|
153
|
+
probing = false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
expect(result.ok).toBe(true);
|
|
157
|
+
expect(result.backend).toBe("sqlite3-cli");
|
|
158
|
+
|
|
159
|
+
// Any positive tick count proves the event loop wasn't blocked.
|
|
160
|
+
// A sync in-process VACUUM would collapse this to 0.
|
|
161
|
+
expect(tickCount).toBeGreaterThanOrEqual(1);
|
|
162
|
+
},
|
|
163
|
+
60_000,
|
|
164
|
+
);
|
|
165
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `db-maintenance.ts` (orchestration) and the underlying
|
|
3
|
+
* `db-async-query.ts` abstraction.
|
|
4
|
+
*
|
|
5
|
+
* The contract this PR locks in:
|
|
6
|
+
* 1. `runDbMaintenance` runs `VACUUM` through the async abstraction
|
|
7
|
+
* — when the `sqlite3` CLI is available, that means a subprocess
|
|
8
|
+
* and the daemon's main event loop keeps ticking. (The structural
|
|
9
|
+
* anti-block assertion lives in
|
|
10
|
+
* `db-async-query.test.ts`; here we focus on orchestration.)
|
|
11
|
+
* 2. The subprocess actually shrinks the on-disk page count when
|
|
12
|
+
* there's reclaimable space.
|
|
13
|
+
* 3. `maybeRunDbMaintenance` is genuinely async — callers can `await`
|
|
14
|
+
* it and observe completion.
|
|
15
|
+
* 4. The 24 h interval guard short-circuits a recent re-run.
|
|
16
|
+
*
|
|
17
|
+
* The per-file temp workspace is set up by `test-preload.ts`; tests just
|
|
18
|
+
* dynamic-import the DB modules so they resolve paths under that temp dir.
|
|
19
|
+
*/
|
|
20
|
+
import { Database } from "bun:sqlite";
|
|
21
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
22
|
+
|
|
23
|
+
const { getSqlite } = await import("../db-connection.js");
|
|
24
|
+
const { initializeDb } = await import("../db-init.js");
|
|
25
|
+
const { deleteMemoryCheckpoint, getMemoryCheckpoint } =
|
|
26
|
+
await import("../checkpoints.js");
|
|
27
|
+
const { maybeRunDbMaintenance } = await import("../db-maintenance.js");
|
|
28
|
+
const { getDbPath } = await import("../../util/platform.js");
|
|
29
|
+
|
|
30
|
+
initializeDb();
|
|
31
|
+
|
|
32
|
+
const MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
deleteMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/** Inflate the test DB with bloat that VACUUM can reclaim. */
|
|
39
|
+
function inflateAndDelete(byteTarget: number): void {
|
|
40
|
+
const sqlite = getSqlite();
|
|
41
|
+
sqlite.exec(
|
|
42
|
+
"CREATE TABLE IF NOT EXISTS bloat (id INTEGER PRIMARY KEY, payload BLOB)",
|
|
43
|
+
);
|
|
44
|
+
const pageSize = (
|
|
45
|
+
sqlite.query("PRAGMA page_size").get() as { page_size: number }
|
|
46
|
+
).page_size;
|
|
47
|
+
const rowsTarget = Math.max(1, Math.ceil(byteTarget / pageSize));
|
|
48
|
+
const payload = new Uint8Array(Math.max(1, pageSize - 64));
|
|
49
|
+
const insert = sqlite.prepare("INSERT INTO bloat (payload) VALUES (?)");
|
|
50
|
+
sqlite.exec("BEGIN");
|
|
51
|
+
for (let i = 0; i < rowsTarget; i++) {
|
|
52
|
+
insert.run(payload);
|
|
53
|
+
}
|
|
54
|
+
sqlite.exec("COMMIT");
|
|
55
|
+
sqlite.exec("DELETE FROM bloat");
|
|
56
|
+
sqlite.exec("DROP TABLE bloat");
|
|
57
|
+
// Force the WAL onto the main DB file so the bloat is visible on disk.
|
|
58
|
+
sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe("maybeRunDbMaintenance", () => {
|
|
62
|
+
test("returns a Promise that resolves", async () => {
|
|
63
|
+
const result = maybeRunDbMaintenance();
|
|
64
|
+
expect(result).toBeInstanceOf(Promise);
|
|
65
|
+
await result;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("respects the 24h interval and skips when last run was recent", async () => {
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
const recent = now - 60_000;
|
|
71
|
+
const { setMemoryCheckpoint } = await import("../checkpoints.js");
|
|
72
|
+
setMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY, String(recent));
|
|
73
|
+
|
|
74
|
+
await maybeRunDbMaintenance(now);
|
|
75
|
+
|
|
76
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(
|
|
77
|
+
String(recent),
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("stamps the checkpoint after a maintenance run", async () => {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
await maybeRunDbMaintenance(now);
|
|
84
|
+
|
|
85
|
+
expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("VACUUM reclaims pages on a bloated DB", async () => {
|
|
89
|
+
const sqlite = getSqlite();
|
|
90
|
+
sqlite.exec("DROP TABLE IF EXISTS bloat");
|
|
91
|
+
sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
92
|
+
|
|
93
|
+
inflateAndDelete(8 * 1024 * 1024);
|
|
94
|
+
|
|
95
|
+
const dbPath = getDbPath();
|
|
96
|
+
// Read page_count from a fresh connection so we observe post-write
|
|
97
|
+
// ground truth without snapshot caching on the main test connection.
|
|
98
|
+
const readPageCount = (): number => {
|
|
99
|
+
const probe = new Database(dbPath, { readonly: true });
|
|
100
|
+
try {
|
|
101
|
+
return (
|
|
102
|
+
probe.query("PRAGMA page_count").get() as { page_count: number }
|
|
103
|
+
).page_count;
|
|
104
|
+
} finally {
|
|
105
|
+
probe.close();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const pagesBefore = readPageCount();
|
|
109
|
+
|
|
110
|
+
await maybeRunDbMaintenance();
|
|
111
|
+
|
|
112
|
+
const pagesAfter = readPageCount();
|
|
113
|
+
expect(pagesAfter).toBeLessThan(pagesBefore);
|
|
114
|
+
}, 60_000);
|
|
115
|
+
});
|