@vellumai/assistant 0.8.1 → 0.8.2
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 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -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 +325 -3
- 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-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 +41 -0
- 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 +1 -0
- 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 +2 -0
- 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-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 +76 -9
- 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__/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 +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- 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 +266 -10
- 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-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -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-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -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__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- 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} +1 -1
- 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 +142 -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} +8 -8
- 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__/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 +737 -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-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -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 +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -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__/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 +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- 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/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/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- 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 +34 -0
- 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.ts +169 -9
- 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 +16 -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 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- 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 +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -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/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- 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 +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- 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 +110 -10
- package/src/memory/db-init.ts +6 -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 +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- 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 +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- 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/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- 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/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- 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/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/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- 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 +36 -3
- 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 +105 -32
- 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 +31 -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 +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- 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 +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- 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__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- 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 +121 -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 +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -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 +6 -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 +65 -21
- 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/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 +57 -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 +15 -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/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 +72 -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/registry.ts +8 -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/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, asc, desc, eq, gt, or } from "drizzle-orm";
|
|
1
|
+
import { and, asc, desc, eq, gt, or, sql } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
4
|
import type {
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
} from "../usage/types.js";
|
|
9
9
|
import { getDb } from "./db-connection.js";
|
|
10
10
|
import { rawAll } from "./raw-query.js";
|
|
11
|
-
import { llmUsageEvents } from "./schema.js";
|
|
11
|
+
import { conversations, llmUsageEvents } from "./schema.js";
|
|
12
12
|
import {
|
|
13
13
|
bucketEventsByDay,
|
|
14
14
|
bucketEventsByHour,
|
|
@@ -123,15 +123,96 @@ export function listUsageEvents(options?: { limit?: number }): UsageEvent[] {
|
|
|
123
123
|
return rows.map(rowToUsageEvent);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Telemetry-flavoured `UsageEvent`: the persisted columns plus the two
|
|
128
|
+
* JOIN-computed conversation-level fields the reporter needs to emit
|
|
129
|
+
* for analytics (`avg turns per conversation`, `tokens on first turn`,
|
|
130
|
+
* foreground/background split on llm_usage rows themselves).
|
|
131
|
+
*
|
|
132
|
+
* Lives next to the query that produces it so the shape stays in lockstep
|
|
133
|
+
* with the SELECT; broader `UsageEvent` consumers stay untouched.
|
|
134
|
+
*/
|
|
135
|
+
export interface UnreportedUsageEvent extends UsageEvent {
|
|
136
|
+
/**
|
|
137
|
+
* Type of the parent conversation (`"standard"` / `"background"` /
|
|
138
|
+
* `"scheduled"`). Null when the LLM call has no `conversationId`
|
|
139
|
+
* (memory consolidation, background embedding work, etc.) and so no
|
|
140
|
+
* `conversations` row to join against.
|
|
141
|
+
*/
|
|
142
|
+
conversationType: string | null;
|
|
143
|
+
/**
|
|
144
|
+
* 1-indexed position of the user turn this LLM call belongs to within
|
|
145
|
+
* the parent conversation, counting only real user turns (tool-result
|
|
146
|
+
* rows persisted with role="user" are excluded — same filter as the
|
|
147
|
+
* turn-event eligibility predicate). Computed as the count of user
|
|
148
|
+
* messages with `created_at <= this LLM call's created_at` in the
|
|
149
|
+
* parent conversation. Null when there's no parent conversation, or
|
|
150
|
+
* when the LLM call fired before any user turn (rare — covers seed
|
|
151
|
+
* agent starts).
|
|
152
|
+
*/
|
|
153
|
+
turnIndex: number | null;
|
|
154
|
+
}
|
|
155
|
+
|
|
126
156
|
export function queryUnreportedUsageEvents(
|
|
127
157
|
afterCreatedAt: number,
|
|
128
158
|
afterId: string | undefined,
|
|
129
159
|
limit: number,
|
|
130
|
-
):
|
|
160
|
+
): UnreportedUsageEvent[] {
|
|
131
161
|
const db = getDb();
|
|
162
|
+
// JOIN to `conversations` to attach `conversationType`. LEFT JOIN
|
|
163
|
+
// because `llm_usage_events.conversationId` is nullable — calls that
|
|
164
|
+
// aren't tied to a conversation (memory consolidation, etc.) still
|
|
165
|
+
// need to flush through telemetry.
|
|
166
|
+
//
|
|
167
|
+
// `turnIndex` is a correlated subquery counting real user turns in
|
|
168
|
+
// the same conversation up to and including this LLM call's
|
|
169
|
+
// `created_at`. The filter mirrors `queryUnreportedTurnEvents` so the
|
|
170
|
+
// two indexes stay aligned: an LLM call fired during processing of
|
|
171
|
+
// turn N reports `turn_index = N`, matching what the turn event
|
|
172
|
+
// stream emitted for the triggering user message.
|
|
132
173
|
const rows = db
|
|
133
|
-
.select(
|
|
174
|
+
.select({
|
|
175
|
+
id: llmUsageEvents.id,
|
|
176
|
+
createdAt: llmUsageEvents.createdAt,
|
|
177
|
+
conversationId: llmUsageEvents.conversationId,
|
|
178
|
+
runId: llmUsageEvents.runId,
|
|
179
|
+
requestId: llmUsageEvents.requestId,
|
|
180
|
+
actor: llmUsageEvents.actor,
|
|
181
|
+
callSite: llmUsageEvents.callSite,
|
|
182
|
+
inferenceProfile: llmUsageEvents.inferenceProfile,
|
|
183
|
+
inferenceProfileSource: llmUsageEvents.inferenceProfileSource,
|
|
184
|
+
provider: llmUsageEvents.provider,
|
|
185
|
+
model: llmUsageEvents.model,
|
|
186
|
+
inputTokens: llmUsageEvents.inputTokens,
|
|
187
|
+
outputTokens: llmUsageEvents.outputTokens,
|
|
188
|
+
cacheCreationInputTokens: llmUsageEvents.cacheCreationInputTokens,
|
|
189
|
+
cacheReadInputTokens: llmUsageEvents.cacheReadInputTokens,
|
|
190
|
+
estimatedCostUsd: llmUsageEvents.estimatedCostUsd,
|
|
191
|
+
pricingStatus: llmUsageEvents.pricingStatus,
|
|
192
|
+
conversationType: conversations.conversationType,
|
|
193
|
+
// Null when conversationId is null (no parent conversation).
|
|
194
|
+
// Otherwise the count of eligible user turns up to and including
|
|
195
|
+
// this LLM call's createdAt. The COALESCE guard returns null
|
|
196
|
+
// (rather than 0) for the "no user turn yet" edge case so the
|
|
197
|
+
// analytics layer can distinguish "before-first-turn" LLM calls.
|
|
198
|
+
turnIndex: sql<number | null>`(
|
|
199
|
+
CASE WHEN ${llmUsageEvents.conversationId} IS NULL THEN NULL
|
|
200
|
+
ELSE (
|
|
201
|
+
SELECT COUNT(*) FROM messages AS m2
|
|
202
|
+
WHERE m2.conversation_id = ${llmUsageEvents.conversationId}
|
|
203
|
+
AND m2.role = 'user'
|
|
204
|
+
AND m2.content NOT LIKE '%"type":"tool\\_result"%' ESCAPE '\\'
|
|
205
|
+
AND m2.content NOT LIKE '%"type":"web\\_search\\_tool\\_result"%' ESCAPE '\\'
|
|
206
|
+
AND m2.created_at <= ${llmUsageEvents.createdAt}
|
|
207
|
+
)
|
|
208
|
+
END
|
|
209
|
+
)`.as("turn_index"),
|
|
210
|
+
})
|
|
134
211
|
.from(llmUsageEvents)
|
|
212
|
+
.leftJoin(
|
|
213
|
+
conversations,
|
|
214
|
+
eq(llmUsageEvents.conversationId, conversations.id),
|
|
215
|
+
)
|
|
135
216
|
.where(
|
|
136
217
|
afterId
|
|
137
218
|
? or(
|
|
@@ -146,7 +227,15 @@ export function queryUnreportedUsageEvents(
|
|
|
146
227
|
.orderBy(asc(llmUsageEvents.createdAt), asc(llmUsageEvents.id))
|
|
147
228
|
.limit(limit)
|
|
148
229
|
.all();
|
|
149
|
-
return rows.map(
|
|
230
|
+
return rows.map((row) => ({
|
|
231
|
+
...rowToUsageEvent(row),
|
|
232
|
+
conversationType: row.conversationType,
|
|
233
|
+
// SQLite returns COUNT(*) as 0 when no rows match; the CASE in the
|
|
234
|
+
// subquery already collapses the no-conversation case to NULL.
|
|
235
|
+
// Convert the integer column to `number | null` for the typed
|
|
236
|
+
// return value.
|
|
237
|
+
turnIndex: row.turnIndex === null ? null : Number(row.turnIndex),
|
|
238
|
+
}));
|
|
150
239
|
}
|
|
151
240
|
|
|
152
241
|
// ---------------------------------------------------------------------------
|
|
@@ -252,6 +341,37 @@ interface GroupRow {
|
|
|
252
341
|
event_count: number;
|
|
253
342
|
}
|
|
254
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Return aggregate usage for a single conversation (e.g. a subagent).
|
|
346
|
+
*/
|
|
347
|
+
export function getConversationUsageTotals(conversationId: string): {
|
|
348
|
+
inputTokens: number;
|
|
349
|
+
outputTokens: number;
|
|
350
|
+
estimatedCost: number;
|
|
351
|
+
} {
|
|
352
|
+
const rows = rawAll<{
|
|
353
|
+
total_input: number;
|
|
354
|
+
total_output: number;
|
|
355
|
+
total_cost: number | null;
|
|
356
|
+
}>(
|
|
357
|
+
/*sql*/ `
|
|
358
|
+
SELECT
|
|
359
|
+
COALESCE(SUM(input_tokens + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)), 0) AS total_input,
|
|
360
|
+
COALESCE(SUM(output_tokens), 0) AS total_output,
|
|
361
|
+
COALESCE(SUM(estimated_cost_usd), 0) AS total_cost
|
|
362
|
+
FROM llm_usage_events
|
|
363
|
+
WHERE conversation_id = ?1
|
|
364
|
+
`,
|
|
365
|
+
conversationId,
|
|
366
|
+
);
|
|
367
|
+
const row = rows[0];
|
|
368
|
+
return {
|
|
369
|
+
inputTokens: row.total_input,
|
|
370
|
+
outputTokens: row.total_output,
|
|
371
|
+
estimatedCost: row.total_cost ?? 0,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
255
375
|
/**
|
|
256
376
|
* Return aggregate totals for all usage events within the given time range.
|
|
257
377
|
*/
|
|
@@ -24,8 +24,23 @@
|
|
|
24
24
|
// conversation might be the active one. We're conservative and only
|
|
25
25
|
// sweep when no job exists at all, since the worst-case false-positive
|
|
26
26
|
// is leaving a few extra orphans for the next sweep to catch.)
|
|
27
|
+
// - AND the row is NOT the most-recent retrospective for its source
|
|
28
|
+
// conversation. The next retrospective run reads the most-recent prior
|
|
29
|
+
// retro via `findMostRecentRetrospectiveFor` to seed its
|
|
30
|
+
// `<already_remembered>` dedup block; sweeping it would force the
|
|
31
|
+
// next run to re-save facts the prior pass already captured.
|
|
27
32
|
|
|
28
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
and,
|
|
35
|
+
eq,
|
|
36
|
+
inArray,
|
|
37
|
+
isNotNull,
|
|
38
|
+
isNull,
|
|
39
|
+
lt,
|
|
40
|
+
notInArray,
|
|
41
|
+
or,
|
|
42
|
+
sql,
|
|
43
|
+
} from "drizzle-orm";
|
|
29
44
|
|
|
30
45
|
import { getLogger } from "../util/logger.js";
|
|
31
46
|
import { deleteConversation } from "./conversation-crud.js";
|
|
@@ -53,7 +68,14 @@ export function sweepOrphanMemoryRetrospectiveConversations(
|
|
|
53
68
|
const cutoff = now - ORPHAN_AGE_MS;
|
|
54
69
|
const db = getDb();
|
|
55
70
|
|
|
56
|
-
|
|
71
|
+
// Job payloads encode the SOURCE conversation id (the conversation being
|
|
72
|
+
// analyzed), not the background-conversation id of the retrospective itself.
|
|
73
|
+
// The background conversation links back to its source via
|
|
74
|
+
// `forkParentConversationId` (set when bootstrapped — see
|
|
75
|
+
// memory-retrospective-job.ts). To protect in-flight jobs we therefore
|
|
76
|
+
// compare source-id to source-id by filtering on
|
|
77
|
+
// `conversations.forkParentConversationId`, not `conversations.id`.
|
|
78
|
+
const activeJobSourceConversationIds = db
|
|
57
79
|
.select({
|
|
58
80
|
conversationId: sql<string>`json_extract(${memoryJobs.payload}, '$.conversationId')`,
|
|
59
81
|
})
|
|
@@ -68,6 +90,40 @@ export function sweepOrphanMemoryRetrospectiveConversations(
|
|
|
68
90
|
.map((row) => row.conversationId)
|
|
69
91
|
.filter((id): id is string => typeof id === "string" && id.length > 0);
|
|
70
92
|
|
|
93
|
+
// Compute the most-recent retro per source so we can preserve it.
|
|
94
|
+
// `findMostRecentRetrospectiveFor` (called by the next retrospective run)
|
|
95
|
+
// pulls dedup context from this row; sweeping it would re-introduce the
|
|
96
|
+
// unbounded-growth bug PR #30331 was created to fix.
|
|
97
|
+
const allRetros = db
|
|
98
|
+
.select({
|
|
99
|
+
id: conversations.id,
|
|
100
|
+
forkParentConversationId: conversations.forkParentConversationId,
|
|
101
|
+
createdAt: conversations.createdAt,
|
|
102
|
+
})
|
|
103
|
+
.from(conversations)
|
|
104
|
+
.where(
|
|
105
|
+
and(
|
|
106
|
+
eq(conversations.source, MEMORY_RETROSPECTIVE_SOURCE),
|
|
107
|
+
isNotNull(conversations.forkParentConversationId),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
.all();
|
|
111
|
+
const mostRecentPerSource = new Map<
|
|
112
|
+
string,
|
|
113
|
+
{ id: string; createdAt: number }
|
|
114
|
+
>();
|
|
115
|
+
for (const row of allRetros) {
|
|
116
|
+
const parent = row.forkParentConversationId;
|
|
117
|
+
if (parent === null) continue;
|
|
118
|
+
const cur = mostRecentPerSource.get(parent);
|
|
119
|
+
if (!cur || row.createdAt > cur.createdAt) {
|
|
120
|
+
mostRecentPerSource.set(parent, { id: row.id, createdAt: row.createdAt });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const preservedIds = new Set(
|
|
124
|
+
Array.from(mostRecentPerSource.values(), (v) => v.id),
|
|
125
|
+
);
|
|
126
|
+
|
|
71
127
|
const orphans = db
|
|
72
128
|
.select({ id: conversations.id })
|
|
73
129
|
.from(conversations)
|
|
@@ -79,12 +135,23 @@ export function sweepOrphanMemoryRetrospectiveConversations(
|
|
|
79
135
|
// last_message_at value are too fresh to assess.
|
|
80
136
|
isNotNull(conversations.lastMessageAt),
|
|
81
137
|
lt(conversations.lastMessageAt, cutoff),
|
|
82
|
-
|
|
83
|
-
?
|
|
138
|
+
activeJobSourceConversationIds.length > 0
|
|
139
|
+
? // `forkParentConversationId` is nullable, and SQLite's
|
|
140
|
+
// `NULL NOT IN (...)` evaluates to unknown (falsy), so legacy
|
|
141
|
+
// rows with a null parent would never match. Include them
|
|
142
|
+
// explicitly so the sweep covers them.
|
|
143
|
+
or(
|
|
144
|
+
isNull(conversations.forkParentConversationId),
|
|
145
|
+
notInArray(
|
|
146
|
+
conversations.forkParentConversationId,
|
|
147
|
+
activeJobSourceConversationIds,
|
|
148
|
+
),
|
|
149
|
+
)
|
|
84
150
|
: sql`1=1`,
|
|
85
151
|
),
|
|
86
152
|
)
|
|
87
|
-
.all()
|
|
153
|
+
.all()
|
|
154
|
+
.filter((row) => !preservedIds.has(row.id));
|
|
88
155
|
|
|
89
156
|
let swept = 0;
|
|
90
157
|
for (const row of orphans) {
|
|
@@ -152,7 +152,7 @@ export function stringifyMessageContent(stored: string): string {
|
|
|
152
152
|
return stored.trim();
|
|
153
153
|
}
|
|
154
154
|
if (typeof parsed === "string") return parsed.trim();
|
|
155
|
-
if (!Array.isArray(parsed)) return
|
|
155
|
+
if (!Array.isArray(parsed)) return stored.trim();
|
|
156
156
|
const parts: string[] = [];
|
|
157
157
|
for (const block of parsed) {
|
|
158
158
|
if (
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
-
import { migrateExtConvBindingsChannelChatUnique } from "./010-ext-conv-bindings-channel-chat-unique.js";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
* External conversation bindings table with indexes
|
|
4
|
+
* External conversation bindings table with indexes.
|
|
6
5
|
*/
|
|
7
6
|
export function createExternalConversationBindingsTables(
|
|
8
7
|
database: DrizzleDb,
|
|
@@ -12,6 +11,7 @@ export function createExternalConversationBindingsTables(
|
|
|
12
11
|
conversation_id TEXT PRIMARY KEY REFERENCES conversations(id) ON DELETE CASCADE,
|
|
13
12
|
source_channel TEXT NOT NULL,
|
|
14
13
|
external_chat_id TEXT NOT NULL,
|
|
14
|
+
external_thread_id TEXT,
|
|
15
15
|
external_user_id TEXT,
|
|
16
16
|
display_name TEXT,
|
|
17
17
|
username TEXT,
|
|
@@ -25,9 +25,20 @@ export function createExternalConversationBindingsTables(
|
|
|
25
25
|
database.run(
|
|
26
26
|
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat ON external_conversation_bindings(source_channel, external_chat_id)`,
|
|
27
27
|
);
|
|
28
|
+
database.run(
|
|
29
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)`,
|
|
30
|
+
);
|
|
28
31
|
database.run(
|
|
29
32
|
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel ON external_conversation_bindings(source_channel)`,
|
|
30
33
|
);
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
database.run(/*sql*/ `
|
|
35
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_no_thread_unique
|
|
36
|
+
ON external_conversation_bindings(source_channel, external_chat_id)
|
|
37
|
+
WHERE external_thread_id IS NULL
|
|
38
|
+
`);
|
|
39
|
+
database.run(/*sql*/ `
|
|
40
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread_unique
|
|
41
|
+
ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)
|
|
42
|
+
WHERE external_thread_id IS NOT NULL
|
|
43
|
+
`);
|
|
33
44
|
}
|
|
@@ -1133,7 +1133,7 @@ describe("migrateDeletePrivateConversations", () => {
|
|
|
1133
1133
|
1,
|
|
1134
1134
|
'text',
|
|
1135
1135
|
'eA==',
|
|
1136
|
-
${now}
|
|
1136
|
+
${now - 60_000}
|
|
1137
1137
|
);
|
|
1138
1138
|
`);
|
|
1139
1139
|
|
|
@@ -1153,4 +1153,41 @@ describe("migrateDeletePrivateConversations", () => {
|
|
|
1153
1153
|
countWhere(raw, "attachments", `id = 'conv-standard-attachment'`),
|
|
1154
1154
|
).toBe(1);
|
|
1155
1155
|
});
|
|
1156
|
+
|
|
1157
|
+
test("preserves pre-staged uploads (unlinked attachments) created after migration starts", () => {
|
|
1158
|
+
const db = createTestDb();
|
|
1159
|
+
const raw = getSqliteFrom(db);
|
|
1160
|
+
const now = Date.now();
|
|
1161
|
+
|
|
1162
|
+
bootstrapTables(raw);
|
|
1163
|
+
seedConversation(raw, "conv-standard", "standard");
|
|
1164
|
+
// created_at in the future ensures it lands after the migration's start
|
|
1165
|
+
// snapshot regardless of clock resolution / test-runner scheduling.
|
|
1166
|
+
raw.exec(/*sql*/ `
|
|
1167
|
+
INSERT INTO attachments (
|
|
1168
|
+
id,
|
|
1169
|
+
original_filename,
|
|
1170
|
+
mime_type,
|
|
1171
|
+
size_bytes,
|
|
1172
|
+
kind,
|
|
1173
|
+
data_base64,
|
|
1174
|
+
created_at
|
|
1175
|
+
) VALUES (
|
|
1176
|
+
'pre-staged-upload',
|
|
1177
|
+
'pending.txt',
|
|
1178
|
+
'text/plain',
|
|
1179
|
+
1,
|
|
1180
|
+
'text',
|
|
1181
|
+
'eA==',
|
|
1182
|
+
${now + 60_000}
|
|
1183
|
+
);
|
|
1184
|
+
`);
|
|
1185
|
+
|
|
1186
|
+
migrateDeletePrivateConversations(db);
|
|
1187
|
+
|
|
1188
|
+
expect(countWhere(raw, "attachments", `id = 'pre-staged-upload'`)).toBe(1);
|
|
1189
|
+
expect(
|
|
1190
|
+
countWhere(raw, "attachments", `id = 'conv-standard-attachment'`),
|
|
1191
|
+
).toBe(1);
|
|
1192
|
+
});
|
|
1156
1193
|
});
|
|
@@ -12,6 +12,12 @@ const PRIVATE_GRAPH_NODE_IDS = /*sql*/ `
|
|
|
12
12
|
`;
|
|
13
13
|
|
|
14
14
|
export function migrateDeletePrivateConversations(database: DrizzleDb): void {
|
|
15
|
+
// Snapshot the migration's start time. The trailing orphan-attachment sweep
|
|
16
|
+
// uses this as an upper bound so it cleans up leaks from prior runs of this
|
|
17
|
+
// migration (those rows were created before this run started) without
|
|
18
|
+
// touching pre-staged uploads created during or after the migration.
|
|
19
|
+
const migrationStartTs = Date.now();
|
|
20
|
+
|
|
15
21
|
database.run(/*sql*/ `
|
|
16
22
|
DELETE FROM tool_invocations
|
|
17
23
|
WHERE conversation_id IN (${PRIVATE_CONVERSATION_IDS})
|
|
@@ -204,6 +210,7 @@ export function migrateDeletePrivateConversations(database: DrizzleDb): void {
|
|
|
204
210
|
FROM message_attachments ma
|
|
205
211
|
WHERE ma.attachment_id = attachments.id
|
|
206
212
|
)
|
|
213
|
+
AND created_at <= ${migrationStartTs}
|
|
207
214
|
`);
|
|
208
215
|
database.run(/*sql*/ `
|
|
209
216
|
DELETE FROM memory_summaries
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add a provider thread anchor to external conversation bindings.
|
|
5
|
+
*
|
|
6
|
+
* Slack conversations are keyed by `(channel_id, thread_ts)`, while legacy
|
|
7
|
+
* channels still use only `(source_channel, external_chat_id)`. SQLite treats
|
|
8
|
+
* NULLs as distinct in unique indexes, so use two partial unique indexes:
|
|
9
|
+
* one for legacy/no-thread bindings and one for threaded bindings.
|
|
10
|
+
*/
|
|
11
|
+
export function migrateExternalConversationBindingThreadId(
|
|
12
|
+
database: DrizzleDb,
|
|
13
|
+
): void {
|
|
14
|
+
const raw = getSqliteFrom(database);
|
|
15
|
+
|
|
16
|
+
const tableExists = raw
|
|
17
|
+
.query(
|
|
18
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'external_conversation_bindings'`,
|
|
19
|
+
)
|
|
20
|
+
.get();
|
|
21
|
+
if (!tableExists) return;
|
|
22
|
+
|
|
23
|
+
const columns = raw
|
|
24
|
+
.query(`PRAGMA table_info(external_conversation_bindings)`)
|
|
25
|
+
.all() as Array<{ name: string }>;
|
|
26
|
+
if (!columns.some((column) => column.name === "external_thread_id")) {
|
|
27
|
+
raw.exec(
|
|
28
|
+
`ALTER TABLE external_conversation_bindings ADD COLUMN external_thread_id TEXT`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
raw.exec("BEGIN");
|
|
34
|
+
|
|
35
|
+
raw.exec(`DROP INDEX IF EXISTS idx_ext_conv_bindings_channel_chat_unique`);
|
|
36
|
+
|
|
37
|
+
raw.exec(/*sql*/ `
|
|
38
|
+
DELETE FROM external_conversation_bindings
|
|
39
|
+
WHERE rowid NOT IN (
|
|
40
|
+
SELECT rowid FROM (
|
|
41
|
+
SELECT rowid,
|
|
42
|
+
ROW_NUMBER() OVER (
|
|
43
|
+
PARTITION BY source_channel, external_chat_id, COALESCE(external_thread_id, '')
|
|
44
|
+
ORDER BY updated_at DESC, created_at DESC, rowid DESC
|
|
45
|
+
) AS rn
|
|
46
|
+
FROM external_conversation_bindings
|
|
47
|
+
)
|
|
48
|
+
WHERE rn = 1
|
|
49
|
+
)
|
|
50
|
+
`);
|
|
51
|
+
|
|
52
|
+
raw.exec(/*sql*/ `
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread
|
|
54
|
+
ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)
|
|
55
|
+
`);
|
|
56
|
+
|
|
57
|
+
raw.exec(/*sql*/ `
|
|
58
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_no_thread_unique
|
|
59
|
+
ON external_conversation_bindings(source_channel, external_chat_id)
|
|
60
|
+
WHERE external_thread_id IS NULL
|
|
61
|
+
`);
|
|
62
|
+
|
|
63
|
+
raw.exec(/*sql*/ `
|
|
64
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ext_conv_bindings_channel_chat_thread_unique
|
|
65
|
+
ON external_conversation_bindings(source_channel, external_chat_id, external_thread_id)
|
|
66
|
+
WHERE external_thread_id IS NOT NULL
|
|
67
|
+
`);
|
|
68
|
+
|
|
69
|
+
raw.exec("COMMIT");
|
|
70
|
+
} catch (err) {
|
|
71
|
+
try {
|
|
72
|
+
raw.exec("ROLLBACK");
|
|
73
|
+
} catch {
|
|
74
|
+
/* no active transaction */
|
|
75
|
+
}
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create the onboarding_events table for tracking pre-chat onboarding
|
|
5
|
+
* selections (tools, tasks, tone, Google connect status).
|
|
6
|
+
*/
|
|
7
|
+
export function createOnboardingEventsTable(database: DrizzleDb): void {
|
|
8
|
+
database.run(/*sql*/ `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS onboarding_events (
|
|
10
|
+
id TEXT PRIMARY KEY,
|
|
11
|
+
created_at INTEGER NOT NULL,
|
|
12
|
+
screen TEXT NOT NULL,
|
|
13
|
+
tools_json TEXT,
|
|
14
|
+
tasks_json TEXT,
|
|
15
|
+
tone TEXT,
|
|
16
|
+
google_connected INTEGER,
|
|
17
|
+
google_scopes_json TEXT,
|
|
18
|
+
ab_variant TEXT
|
|
19
|
+
)
|
|
20
|
+
`);
|
|
21
|
+
}
|