@vellumai/assistant 0.8.1 → 0.8.3
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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- 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-security-invariants.test.ts +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -5,11 +5,26 @@
|
|
|
5
5
|
* background jobs (e.g. proactive artifact generation) can persist documents
|
|
6
6
|
* without going through the HTTP layer.
|
|
7
7
|
*/
|
|
8
|
-
import { rawGet, rawRun } from "../memory/raw-query.js";
|
|
8
|
+
import { rawAll, rawGet, rawRun } from "../memory/raw-query.js";
|
|
9
9
|
import { getLogger } from "../util/logger.js";
|
|
10
10
|
|
|
11
11
|
const log = getLogger("document-store");
|
|
12
12
|
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Shared types
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
/** A document record with camelCase field names, mapped from the SQLite row. */
|
|
18
|
+
export interface DocumentRecord {
|
|
19
|
+
surfaceId: string;
|
|
20
|
+
conversationId: string;
|
|
21
|
+
title: string;
|
|
22
|
+
content: string;
|
|
23
|
+
wordCount: number;
|
|
24
|
+
createdAt: number;
|
|
25
|
+
updatedAt: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
// ---------------------------------------------------------------------------
|
|
14
29
|
// Junction table helper
|
|
15
30
|
// ---------------------------------------------------------------------------
|
|
@@ -27,6 +42,204 @@ export function addDocumentConversation(
|
|
|
27
42
|
);
|
|
28
43
|
}
|
|
29
44
|
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Shared query helpers
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
interface DocumentRow {
|
|
50
|
+
surface_id: string;
|
|
51
|
+
conversation_id: string;
|
|
52
|
+
title: string;
|
|
53
|
+
content: string;
|
|
54
|
+
word_count: number;
|
|
55
|
+
created_at: number;
|
|
56
|
+
updated_at: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type DocumentListRow = Omit<DocumentRow, "content">;
|
|
60
|
+
|
|
61
|
+
function escapeSqlLikePattern(value: string): string {
|
|
62
|
+
return value.replace(/[\\%_]/g, "\\$&");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function mapRowToRecord(row: DocumentRow): DocumentRecord {
|
|
66
|
+
return {
|
|
67
|
+
surfaceId: row.surface_id,
|
|
68
|
+
conversationId: row.conversation_id,
|
|
69
|
+
title: row.title,
|
|
70
|
+
content: row.content,
|
|
71
|
+
wordCount: row.word_count,
|
|
72
|
+
createdAt: row.created_at,
|
|
73
|
+
updatedAt: row.updated_at,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Look up a single document by surface ID. Returns `null` when not found. */
|
|
78
|
+
export function getDocumentById(surfaceId: string): DocumentRecord | null {
|
|
79
|
+
try {
|
|
80
|
+
const row = rawGet<DocumentRow>(
|
|
81
|
+
/*sql*/ `SELECT surface_id, conversation_id, title, content, word_count, created_at, updated_at
|
|
82
|
+
FROM documents
|
|
83
|
+
WHERE surface_id = ?`,
|
|
84
|
+
surfaceId,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (!row) {
|
|
88
|
+
log.info({ surfaceId }, "Document not found");
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
log.info({ surfaceId }, "Loaded document");
|
|
93
|
+
return mapRowToRecord(row);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
log.error({ err: error, surfaceId }, "Load error");
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Return true when a document is associated with a conversation. */
|
|
101
|
+
export function isDocumentAssociatedWithConversation(
|
|
102
|
+
surfaceId: string,
|
|
103
|
+
conversationId: string,
|
|
104
|
+
): boolean {
|
|
105
|
+
try {
|
|
106
|
+
const row = rawGet<{ found: number }>(
|
|
107
|
+
/*sql*/ `
|
|
108
|
+
SELECT 1 AS found
|
|
109
|
+
FROM document_conversations
|
|
110
|
+
WHERE surface_id = ? AND conversation_id = ?
|
|
111
|
+
LIMIT 1
|
|
112
|
+
`,
|
|
113
|
+
surfaceId,
|
|
114
|
+
conversationId,
|
|
115
|
+
);
|
|
116
|
+
return row != null;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
log.error(
|
|
119
|
+
{ err: error, surfaceId, conversationId },
|
|
120
|
+
"Document association check error",
|
|
121
|
+
);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* List documents for a given conversation (via the junction table).
|
|
128
|
+
* Returns an empty array when the conversation has no documents or on error.
|
|
129
|
+
*/
|
|
130
|
+
export function getDocumentsForConversation(
|
|
131
|
+
conversationId: string,
|
|
132
|
+
): Omit<DocumentRecord, "content">[] {
|
|
133
|
+
try {
|
|
134
|
+
const rows = rawAll<DocumentListRow>(
|
|
135
|
+
/*sql*/ `
|
|
136
|
+
SELECT d.surface_id, dc.conversation_id AS conversation_id,
|
|
137
|
+
d.title, d.word_count, d.created_at, d.updated_at
|
|
138
|
+
FROM documents d
|
|
139
|
+
INNER JOIN document_conversations dc ON d.surface_id = dc.surface_id
|
|
140
|
+
WHERE dc.conversation_id = ?
|
|
141
|
+
ORDER BY d.updated_at DESC
|
|
142
|
+
`,
|
|
143
|
+
conversationId,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
log.info(
|
|
147
|
+
{ conversationId, count: rows.length },
|
|
148
|
+
"Listed documents for conversation",
|
|
149
|
+
);
|
|
150
|
+
return rows.map((row) => ({
|
|
151
|
+
surfaceId: row.surface_id,
|
|
152
|
+
conversationId: row.conversation_id,
|
|
153
|
+
title: row.title,
|
|
154
|
+
wordCount: row.word_count,
|
|
155
|
+
createdAt: row.created_at,
|
|
156
|
+
updatedAt: row.updated_at,
|
|
157
|
+
}));
|
|
158
|
+
} catch (error) {
|
|
159
|
+
log.error({ err: error, conversationId }, "List error");
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Search documents by title substring (case-insensitive).
|
|
166
|
+
* When `conversationId` is supplied, only documents associated with that
|
|
167
|
+
* conversation are returned.
|
|
168
|
+
* Returns documents ordered by most recently updated.
|
|
169
|
+
*/
|
|
170
|
+
export function searchDocumentsByTitle(
|
|
171
|
+
query: string,
|
|
172
|
+
options: { conversationId?: string } = {},
|
|
173
|
+
): Omit<DocumentRecord, "content">[] {
|
|
174
|
+
try {
|
|
175
|
+
const pattern = `%${escapeSqlLikePattern(query)}%`;
|
|
176
|
+
const rows = options.conversationId
|
|
177
|
+
? rawAll<DocumentListRow>(
|
|
178
|
+
/*sql*/ `
|
|
179
|
+
SELECT d.surface_id, dc.conversation_id AS conversation_id,
|
|
180
|
+
d.title, d.word_count, d.created_at, d.updated_at
|
|
181
|
+
FROM documents d
|
|
182
|
+
INNER JOIN document_conversations dc ON d.surface_id = dc.surface_id
|
|
183
|
+
WHERE dc.conversation_id = ?
|
|
184
|
+
AND d.title COLLATE NOCASE LIKE ? ESCAPE '\\'
|
|
185
|
+
ORDER BY d.updated_at DESC
|
|
186
|
+
LIMIT 20
|
|
187
|
+
`,
|
|
188
|
+
options.conversationId,
|
|
189
|
+
pattern,
|
|
190
|
+
)
|
|
191
|
+
: rawAll<DocumentListRow>(
|
|
192
|
+
/*sql*/ `
|
|
193
|
+
SELECT surface_id, conversation_id, title, word_count, created_at, updated_at
|
|
194
|
+
FROM documents
|
|
195
|
+
WHERE title COLLATE NOCASE LIKE ? ESCAPE '\\'
|
|
196
|
+
ORDER BY updated_at DESC
|
|
197
|
+
LIMIT 20
|
|
198
|
+
`,
|
|
199
|
+
pattern,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
log.info(
|
|
203
|
+
{ query, conversationId: options.conversationId, count: rows.length },
|
|
204
|
+
"Searched documents by title",
|
|
205
|
+
);
|
|
206
|
+
return rows.map((row) => ({
|
|
207
|
+
surfaceId: row.surface_id,
|
|
208
|
+
conversationId: row.conversation_id,
|
|
209
|
+
title: row.title,
|
|
210
|
+
wordCount: row.word_count,
|
|
211
|
+
createdAt: row.created_at,
|
|
212
|
+
updatedAt: row.updated_at,
|
|
213
|
+
}));
|
|
214
|
+
} catch (error) {
|
|
215
|
+
log.error({ err: error, query }, "Search error");
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Delete a document and its conversation associations.
|
|
222
|
+
* Returns `true` if the document existed and was deleted, `false` otherwise.
|
|
223
|
+
*/
|
|
224
|
+
export function deleteDocument(surfaceId: string): boolean {
|
|
225
|
+
try {
|
|
226
|
+
const changes = rawRun(
|
|
227
|
+
/*sql*/ `DELETE FROM documents WHERE surface_id = ?`,
|
|
228
|
+
surfaceId,
|
|
229
|
+
);
|
|
230
|
+
rawRun(
|
|
231
|
+
/*sql*/ `DELETE FROM document_conversations WHERE surface_id = ?`,
|
|
232
|
+
surfaceId,
|
|
233
|
+
);
|
|
234
|
+
const existed = changes > 0;
|
|
235
|
+
log.info({ surfaceId, existed }, "Deleted document");
|
|
236
|
+
return existed;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
log.error({ err: error, surfaceId }, "Delete error");
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
30
243
|
// ---------------------------------------------------------------------------
|
|
31
244
|
// Document persistence
|
|
32
245
|
// ---------------------------------------------------------------------------
|
|
@@ -89,7 +302,7 @@ export function updateDocumentContent(
|
|
|
89
302
|
surfaceId: string,
|
|
90
303
|
markdown: string,
|
|
91
304
|
mode: string,
|
|
92
|
-
):
|
|
305
|
+
): { success: true } | { success: false; error: string } {
|
|
93
306
|
try {
|
|
94
307
|
const existing = rawGet<{ content: string }>(
|
|
95
308
|
/*sql*/ `SELECT content FROM documents WHERE surface_id = ?`,
|
|
@@ -97,7 +310,7 @@ export function updateDocumentContent(
|
|
|
97
310
|
);
|
|
98
311
|
if (!existing) {
|
|
99
312
|
log.info({ surfaceId }, "No persisted document to update");
|
|
100
|
-
return;
|
|
313
|
+
return { success: false, error: "Document not found" };
|
|
101
314
|
}
|
|
102
315
|
const sep = mode === "append" && existing.content.length > 0 ? "\n\n" : "";
|
|
103
316
|
const newContent =
|
|
@@ -113,7 +326,12 @@ export function updateDocumentContent(
|
|
|
113
326
|
surfaceId,
|
|
114
327
|
);
|
|
115
328
|
log.info({ surfaceId, mode }, "Updated document content");
|
|
329
|
+
return { success: true };
|
|
116
330
|
} catch (error) {
|
|
117
331
|
log.error({ err: error, surfaceId }, "Document content update error");
|
|
332
|
+
return {
|
|
333
|
+
success: false,
|
|
334
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
335
|
+
};
|
|
118
336
|
}
|
|
119
337
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded handle for `@vellumai/plugin-api`.
|
|
3
|
+
*
|
|
4
|
+
* Standard `import * as pluginApi from "../plugin-api/index.ts"` lets Bun's
|
|
5
|
+
* normal bundler walk the plugin-api module graph at build time — relative
|
|
6
|
+
* imports inside plugin-api (`./types.js`, future runtime siblings) are
|
|
7
|
+
* inlined naturally, with no separate build step. In `bun --compile`, this
|
|
8
|
+
* means plugin-api ships as part of the assistant binary's regular code
|
|
9
|
+
* graph; in JIT/Docker, it loads from source.
|
|
10
|
+
*
|
|
11
|
+
* The loaded namespace is then installed on `globalThis` under a versioned
|
|
12
|
+
* symbol. The boot-time shim writer (`ensurePluginApiShim`) enumerates
|
|
13
|
+
* {@link PLUGIN_API_EXPORTS} and generates a tiny ESM module at
|
|
14
|
+
* `<workspaceDir>/node_modules/@vellumai/plugin-api/index.js` that
|
|
15
|
+
* re-binds each runtime export from `globalThis`. User plugins that
|
|
16
|
+
* `import { ... } from "@vellumai/plugin-api"` walk up to that shim and
|
|
17
|
+
* pick up the bindings.
|
|
18
|
+
*
|
|
19
|
+
* Type-only exports erase before this module loads, so `Object.keys`
|
|
20
|
+
* sees only runtime exports. That's correct — types are a dev-time
|
|
21
|
+
* concern, resolved against plugin-api source via tsconfig path-mapping
|
|
22
|
+
* (or, post-PR-5, against a generated `.d.ts` next to the runtime
|
|
23
|
+
* shim).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import * as pluginApi from "../plugin-api/index.js";
|
|
27
|
+
|
|
28
|
+
/** Symbol key under which the plugin-api namespace is published on globalThis. */
|
|
29
|
+
export const PLUGIN_API_REGISTRY_KEY = Symbol.for("vellum.plugin-api.v1");
|
|
30
|
+
|
|
31
|
+
// Install on globalThis once at module-load time. The shim writer reads
|
|
32
|
+
// `PLUGIN_API_EXPORTS` to know which bindings to re-export; the shim's
|
|
33
|
+
// generated body then reads `globalThis[PLUGIN_API_REGISTRY_KEY]` to grab
|
|
34
|
+
// the live module namespace.
|
|
35
|
+
(globalThis as Record<symbol, unknown>)[PLUGIN_API_REGISTRY_KEY] = pluginApi;
|
|
36
|
+
|
|
37
|
+
/** Names of the runtime exports the workspace shim should re-bind. */
|
|
38
|
+
export const PLUGIN_API_EXPORTS: readonly string[] = Object.freeze(
|
|
39
|
+
Object.keys(pluginApi),
|
|
40
|
+
);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
type TestMessage = {
|
|
6
|
+
id: string;
|
|
7
|
+
conversationId: string;
|
|
8
|
+
role: string;
|
|
9
|
+
content: string;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
metadata: string | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const parentMessages: TestMessage[] = [
|
|
15
|
+
{
|
|
16
|
+
id: "msg-parent-1",
|
|
17
|
+
conversationId: "parent-conv",
|
|
18
|
+
role: "user",
|
|
19
|
+
content: JSON.stringify([{ type: "text", text: "go research foo" }]),
|
|
20
|
+
createdAt: 1_700_000_000_000,
|
|
21
|
+
metadata: null,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "msg-parent-2",
|
|
25
|
+
conversationId: "parent-conv",
|
|
26
|
+
role: "assistant",
|
|
27
|
+
content: JSON.stringify([{ type: "text", text: "spawning subagent" }]),
|
|
28
|
+
createdAt: 1_700_000_001_000,
|
|
29
|
+
metadata: JSON.stringify({
|
|
30
|
+
subagentNotification: {
|
|
31
|
+
subagentId: "sa-1",
|
|
32
|
+
label: "research-foo",
|
|
33
|
+
status: "completed",
|
|
34
|
+
conversationId: "child-conv-1",
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const childMessages: TestMessage[] = [
|
|
41
|
+
{
|
|
42
|
+
id: "msg-child-1",
|
|
43
|
+
conversationId: "child-conv-1",
|
|
44
|
+
role: "user",
|
|
45
|
+
content: JSON.stringify([
|
|
46
|
+
{ type: "text", text: "Objective: research foo and report back." },
|
|
47
|
+
]),
|
|
48
|
+
createdAt: 1_700_000_002_000,
|
|
49
|
+
metadata: null,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "msg-child-2",
|
|
53
|
+
conversationId: "child-conv-1",
|
|
54
|
+
role: "assistant",
|
|
55
|
+
content: JSON.stringify([
|
|
56
|
+
{ type: "text", text: "I found that foo is a bar." },
|
|
57
|
+
]),
|
|
58
|
+
createdAt: 1_700_000_003_000,
|
|
59
|
+
metadata: null,
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const messageMetadataSchema = z.object({
|
|
64
|
+
subagentNotification: z
|
|
65
|
+
.object({
|
|
66
|
+
subagentId: z.string(),
|
|
67
|
+
label: z.string(),
|
|
68
|
+
status: z.enum(["running", "completed", "failed", "aborted"]),
|
|
69
|
+
conversationId: z.string().optional(),
|
|
70
|
+
})
|
|
71
|
+
.optional(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
mock.module("../../memory/conversation-crud.js", () => ({
|
|
75
|
+
getConversation: (_id: string) => null,
|
|
76
|
+
getMessages: (id: string) =>
|
|
77
|
+
id === "child-conv-1" ? childMessages : parentMessages,
|
|
78
|
+
messageMetadataSchema,
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
mock.module("../../util/truncate.js", () => ({
|
|
82
|
+
truncate: (s: string) => s,
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
mock.module("../../daemon/date-context.js", () => ({
|
|
86
|
+
formatLocalTimestamp: (_ts: number, _tz?: string) => "TIME",
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
const { formatMessageSliceForTranscript } =
|
|
90
|
+
await import("../transcript-formatter.js");
|
|
91
|
+
|
|
92
|
+
describe("formatMessageSliceForTranscript subagent labels", () => {
|
|
93
|
+
test("embedded subagent transcripts render with generic role labels even when parent display names are provided", () => {
|
|
94
|
+
const out = formatMessageSliceForTranscript(parentMessages, {
|
|
95
|
+
assistantName: "Bob",
|
|
96
|
+
userName: "Alice",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Parent messages use the provided display names.
|
|
100
|
+
expect(out).toContain("## Alice (TIME)");
|
|
101
|
+
expect(out).toContain("## Bob (TIME)");
|
|
102
|
+
|
|
103
|
+
// Subagent block headers must use generic labels — the child "user" message
|
|
104
|
+
// is actually the parent assistant's objective, so labeling it "Alice"
|
|
105
|
+
// would misattribute the assistant's tasking text to the human user.
|
|
106
|
+
expect(out).toContain("### Subagent: research-foo (completed)");
|
|
107
|
+
expect(out).toContain("> **User** (TIME)");
|
|
108
|
+
expect(out).toContain("> **Assistant** (TIME)");
|
|
109
|
+
expect(out).not.toContain("> **Alice**");
|
|
110
|
+
expect(out).not.toContain("> **Bob**");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("without display-name options, parent and subagent both use generic labels", () => {
|
|
114
|
+
const out = formatMessageSliceForTranscript(parentMessages);
|
|
115
|
+
|
|
116
|
+
expect(out).toContain("## User (TIME)");
|
|
117
|
+
expect(out).toContain("## Assistant (TIME)");
|
|
118
|
+
expect(out).toContain("> **User** (TIME)");
|
|
119
|
+
expect(out).toContain("> **Assistant** (TIME)");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* subagent conversation sections when present in message metadata.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { formatLocalTimestamp } from "../daemon/date-context.js";
|
|
8
9
|
import {
|
|
9
10
|
getConversation,
|
|
10
11
|
getMessages,
|
|
@@ -23,10 +24,6 @@ interface ContentBlock {
|
|
|
23
24
|
source?: { media_type?: string; filename?: string };
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function formatTimestamp(ms: number): string {
|
|
27
|
-
return new Date(ms).toISOString().replace("T", " ").slice(0, 19);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
27
|
function extractAnalysisText(blocks: ContentBlock[]): string {
|
|
31
28
|
const parts: string[] = [];
|
|
32
29
|
for (const block of blocks) {
|
|
@@ -67,15 +64,36 @@ function extractAnalysisText(blocks: ContentBlock[]): string {
|
|
|
67
64
|
return parts.join("\n");
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
export interface TranscriptFormatOptions {
|
|
68
|
+
timeZone?: string;
|
|
69
|
+
assistantName?: string | null;
|
|
70
|
+
userName?: string | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolveName(
|
|
74
|
+
name: string | null | undefined,
|
|
75
|
+
fallback: string,
|
|
76
|
+
): string {
|
|
77
|
+
return name && name.length > 0 ? name : fallback;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatRole(
|
|
81
|
+
role: string,
|
|
82
|
+
options: TranscriptFormatOptions = {},
|
|
83
|
+
): string {
|
|
84
|
+
return role === "user"
|
|
85
|
+
? resolveName(options.userName, "User")
|
|
86
|
+
: resolveName(options.assistantName, "Assistant");
|
|
72
87
|
}
|
|
73
88
|
|
|
74
|
-
function formatSubagentMessages(
|
|
89
|
+
function formatSubagentMessages(
|
|
90
|
+
msgs: ReturnType<typeof getMessages>,
|
|
91
|
+
options: TranscriptFormatOptions = {},
|
|
92
|
+
): string {
|
|
75
93
|
const lines: string[] = [];
|
|
76
94
|
for (const msg of msgs) {
|
|
77
|
-
const role = formatRole(msg.role);
|
|
78
|
-
const time =
|
|
95
|
+
const role = formatRole(msg.role, options);
|
|
96
|
+
const time = formatLocalTimestamp(msg.createdAt, options.timeZone);
|
|
79
97
|
const content = parseContent(msg.content);
|
|
80
98
|
const text = extractAnalysisText(content);
|
|
81
99
|
if (text) {
|
|
@@ -103,24 +121,33 @@ type TranscriptMessage = ReturnType<typeof getMessages>[number];
|
|
|
103
121
|
* Format a slice of messages as a transcript body (no top-of-conversation
|
|
104
122
|
* header). Used by background jobs that process incremental slices — the
|
|
105
123
|
* memory-retrospective job re-renders only the messages added since its
|
|
106
|
-
* last successful run rather than the whole conversation. The
|
|
107
|
-
* matches `buildAnalysisTranscript`
|
|
108
|
-
*
|
|
109
|
-
*
|
|
124
|
+
* last successful run rather than the whole conversation. The per-message
|
|
125
|
+
* structural shape matches `buildAnalysisTranscript` (header line, body,
|
|
126
|
+
* optional subagent block) so downstream agents see consistent framing.
|
|
127
|
+
* The participant *labels*, however, intentionally diverge: this function
|
|
128
|
+
* honors `TranscriptFormatOptions` so the memory-retrospective prompt can
|
|
129
|
+
* render the conversation under the assistant and user display names,
|
|
130
|
+
* while `buildAnalysisTranscript` always uses generic "User"/"Assistant"
|
|
131
|
+
* labels for the analyze-conversation flow.
|
|
110
132
|
*/
|
|
111
133
|
export function formatMessageSliceForTranscript(
|
|
112
134
|
messages: TranscriptMessage[],
|
|
135
|
+
options: TranscriptFormatOptions = {},
|
|
113
136
|
): string {
|
|
114
137
|
const lines: string[] = [];
|
|
115
138
|
for (const msg of messages) {
|
|
116
|
-
appendMessageBlock(lines, msg);
|
|
139
|
+
appendMessageBlock(lines, msg, options);
|
|
117
140
|
}
|
|
118
141
|
return lines.join("\n");
|
|
119
142
|
}
|
|
120
143
|
|
|
121
|
-
function appendMessageBlock(
|
|
122
|
-
|
|
123
|
-
|
|
144
|
+
function appendMessageBlock(
|
|
145
|
+
lines: string[],
|
|
146
|
+
msg: TranscriptMessage,
|
|
147
|
+
options: TranscriptFormatOptions = {},
|
|
148
|
+
): void {
|
|
149
|
+
const role = formatRole(msg.role, options);
|
|
150
|
+
const time = formatLocalTimestamp(msg.createdAt, options.timeZone);
|
|
124
151
|
const content = parseContent(msg.content);
|
|
125
152
|
const text = extractAnalysisText(content);
|
|
126
153
|
|
|
@@ -142,7 +169,14 @@ function appendMessageBlock(lines: string[], msg: TranscriptMessage): void {
|
|
|
142
169
|
const subMessages = getMessages(notif.conversationId);
|
|
143
170
|
lines.push(`### Subagent: ${notif.label} (${notif.status})`);
|
|
144
171
|
lines.push("");
|
|
145
|
-
|
|
172
|
+
// Subagent conversations persist the parent assistant's objective
|
|
173
|
+
// as a `user` message (see subagent/manager.ts), so reusing the
|
|
174
|
+
// parent's display-name options would render the assistant's
|
|
175
|
+
// tasking text under the human user's name. Keep child transcripts
|
|
176
|
+
// on generic role labels — and only pass through the time zone.
|
|
177
|
+
lines.push(
|
|
178
|
+
formatSubagentMessages(subMessages, { timeZone: options.timeZone }),
|
|
179
|
+
);
|
|
146
180
|
lines.push("");
|
|
147
181
|
}
|
|
148
182
|
}
|
|
@@ -163,12 +197,12 @@ export function buildAnalysisTranscript(conversationId: string): string {
|
|
|
163
197
|
const lines: string[] = [];
|
|
164
198
|
|
|
165
199
|
lines.push(`# Conversation: ${title}`);
|
|
166
|
-
lines.push(`Created: ${
|
|
200
|
+
lines.push(`Created: ${formatLocalTimestamp(conversation.createdAt)}`);
|
|
167
201
|
lines.push("");
|
|
168
202
|
|
|
169
203
|
for (const msg of allMessages) {
|
|
170
204
|
const role = formatRole(msg.role);
|
|
171
|
-
const time =
|
|
205
|
+
const time = formatLocalTimestamp(msg.createdAt);
|
|
172
206
|
const content = parseContent(msg.content);
|
|
173
207
|
const text = extractAnalysisText(content);
|
|
174
208
|
|
|
@@ -17,6 +17,11 @@ const log = getLogger("filing-service");
|
|
|
17
17
|
|
|
18
18
|
const FILING_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
|
|
19
19
|
|
|
20
|
+
// When compaction skips because a filing run holds the serialization lock,
|
|
21
|
+
// retry on this near-term cadence so phase-aligned 24h timers don't starve
|
|
22
|
+
// compaction across consecutive ticks.
|
|
23
|
+
const COMPACTION_CONTENDED_RETRY_MS = 10 * 60 * 1000; // 10 minutes
|
|
24
|
+
|
|
20
25
|
const FILING_PROMPT_TEMPLATE = `You are running a periodic knowledge base filing job. This is a background maintenance task focused on the buffer.
|
|
21
26
|
|
|
22
27
|
Read \`pkb/buffer.md\`. For each item in the buffer:
|
|
@@ -66,6 +71,7 @@ This is your knowledge base — keep it sharp.`;
|
|
|
66
71
|
|
|
67
72
|
export interface FilingDeps {
|
|
68
73
|
getCurrentHour?: () => number;
|
|
74
|
+
compactionContendedRetryMs?: number;
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
export class FilingService {
|
|
@@ -79,8 +85,10 @@ export class FilingService {
|
|
|
79
85
|
private readonly deps: FilingDeps;
|
|
80
86
|
private timer: ReturnType<typeof setInterval> | null = null;
|
|
81
87
|
private compactionTimer: ReturnType<typeof setInterval> | null = null;
|
|
88
|
+
private compactionRetryTimer: ReturnType<typeof setTimeout> | null = null;
|
|
82
89
|
private activeRun: Promise<void> | null = null;
|
|
83
90
|
private activeCompactionRun: Promise<void> | null = null;
|
|
91
|
+
private stopped = false;
|
|
84
92
|
private _lastRunAt: number | null = null;
|
|
85
93
|
private _nextRunAt: number | null = null;
|
|
86
94
|
private _lastCompactionAt: number | null = null;
|
|
@@ -108,6 +116,7 @@ export class FilingService {
|
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
start(): void {
|
|
119
|
+
this.stopped = false;
|
|
111
120
|
const fullConfig = getConfig();
|
|
112
121
|
if (fullConfig.memory.v2.enabled) {
|
|
113
122
|
log.info("Filing service disabled — memory v2 is active");
|
|
@@ -157,12 +166,14 @@ export class FilingService {
|
|
|
157
166
|
clearInterval(this.compactionTimer);
|
|
158
167
|
this.compactionTimer = null;
|
|
159
168
|
}
|
|
169
|
+
this.clearCompactionRetry();
|
|
160
170
|
this._nextRunAt = null;
|
|
161
171
|
this._nextCompactionAt = null;
|
|
162
172
|
this.start();
|
|
163
173
|
}
|
|
164
174
|
|
|
165
175
|
async stop(): Promise<void> {
|
|
176
|
+
this.stopped = true;
|
|
166
177
|
if (this.timer) {
|
|
167
178
|
clearInterval(this.timer);
|
|
168
179
|
this.timer = null;
|
|
@@ -171,6 +182,7 @@ export class FilingService {
|
|
|
171
182
|
clearInterval(this.compactionTimer);
|
|
172
183
|
this.compactionTimer = null;
|
|
173
184
|
}
|
|
185
|
+
this.clearCompactionRetry();
|
|
174
186
|
this._nextRunAt = null;
|
|
175
187
|
this._nextCompactionAt = null;
|
|
176
188
|
const inflight: Promise<void>[] = [];
|
|
@@ -269,9 +281,13 @@ export class FilingService {
|
|
|
269
281
|
log.debug(
|
|
270
282
|
"Filing run in progress, skipping compaction to avoid concurrent PKB writes",
|
|
271
283
|
);
|
|
284
|
+
this.scheduleCompactionRetry(
|
|
285
|
+
this.deps.compactionContendedRetryMs ?? COMPACTION_CONTENDED_RETRY_MS,
|
|
286
|
+
);
|
|
272
287
|
return false;
|
|
273
288
|
}
|
|
274
289
|
|
|
290
|
+
this.clearCompactionRetry();
|
|
275
291
|
const run = this.executeCompactionRun();
|
|
276
292
|
this.activeCompactionRun = run;
|
|
277
293
|
try {
|
|
@@ -301,6 +317,29 @@ export class FilingService {
|
|
|
301
317
|
this._nextCompactionAt = Date.now() + intervalMs;
|
|
302
318
|
}
|
|
303
319
|
|
|
320
|
+
private scheduleCompactionRetry(delayMs: number): void {
|
|
321
|
+
this.clearCompactionRetry();
|
|
322
|
+
if (this.stopped) return;
|
|
323
|
+
this.compactionRetryTimer = setTimeout(() => {
|
|
324
|
+
this.compactionRetryTimer = null;
|
|
325
|
+
if (this.stopped) return;
|
|
326
|
+
this.runCompactionOnce().catch((err) => {
|
|
327
|
+
log.error({ err }, "Compaction retry failed");
|
|
328
|
+
});
|
|
329
|
+
}, delayMs);
|
|
330
|
+
// unref so the pending retry doesn't keep the daemon process alive on
|
|
331
|
+
// shutdown paths that don't call stop().
|
|
332
|
+
this.compactionRetryTimer.unref?.();
|
|
333
|
+
this._nextCompactionAt = Date.now() + delayMs;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private clearCompactionRetry(): void {
|
|
337
|
+
if (this.compactionRetryTimer) {
|
|
338
|
+
clearTimeout(this.compactionRetryTimer);
|
|
339
|
+
this.compactionRetryTimer = null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
304
343
|
private shouldSkipForDiskPressure(source: "filing" | "compaction"): boolean {
|
|
305
344
|
const diskPressureGate = checkDiskPressureBackgroundGate("background-work");
|
|
306
345
|
if (diskPressureGate.action === "allow") return false;
|