@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
|
@@ -26,7 +26,7 @@ import type { QdrantSparseVector } from "../qdrant-client.js";
|
|
|
26
26
|
import { memorySummaries } from "../schema.js";
|
|
27
27
|
import { conversations } from "../schema/conversations.js";
|
|
28
28
|
import {
|
|
29
|
-
|
|
29
|
+
clearEverInjected as clearV2EverInjected,
|
|
30
30
|
hydrate as hydrateV2State,
|
|
31
31
|
save as saveV2State,
|
|
32
32
|
} from "../v2/activation-store.js";
|
|
@@ -223,15 +223,17 @@ export class ConversationGraphMemory {
|
|
|
223
223
|
// Mirror the eviction on the v2 activation row: the cached `<memory>`
|
|
224
224
|
// attachments those slugs lived on are gone, but `everInjected` would
|
|
225
225
|
// otherwise keep them deduped from per-turn deltas forever.
|
|
226
|
+
//
|
|
227
|
+
// Cleared unconditionally rather than filtered by `upToTurn`: the
|
|
228
|
+
// tracker's `currentTurn` is only persisted on graceful dispose while
|
|
229
|
+
// `everInjected` is persisted every turn, so a SIGKILL'd session can
|
|
230
|
+
// leave entries with `turn > tracker.currentTurn` that a turn-bounded
|
|
231
|
+
// filter would skip.
|
|
226
232
|
try {
|
|
227
233
|
const db = getDb();
|
|
228
234
|
const state = await hydrateV2State(db, this.conversationId);
|
|
229
235
|
if (state) {
|
|
230
|
-
await saveV2State(
|
|
231
|
-
db,
|
|
232
|
-
this.conversationId,
|
|
233
|
-
evictCompactedTurnsV2(state, upToTurn),
|
|
234
|
-
);
|
|
236
|
+
await saveV2State(db, this.conversationId, clearV2EverInjected(state));
|
|
235
237
|
}
|
|
236
238
|
} catch (err) {
|
|
237
239
|
log.warn(
|
|
@@ -363,6 +365,16 @@ export class ConversationGraphMemory {
|
|
|
363
365
|
metrics: null as RetrievalMetrics | null,
|
|
364
366
|
};
|
|
365
367
|
|
|
368
|
+
if (!config.memory.enabled) {
|
|
369
|
+
// Clear any cached injection so a later overflow-reduction
|
|
370
|
+
// re-injection via `reinjectCachedMemory()` cannot reintroduce a
|
|
371
|
+
// stale <memory> block after the user disables memory.
|
|
372
|
+
this.lastInjectedBlock = null;
|
|
373
|
+
this.lastInjectedNodeIds = [];
|
|
374
|
+
this.lastInjectedImages = new Map();
|
|
375
|
+
return noopResult;
|
|
376
|
+
}
|
|
377
|
+
|
|
366
378
|
// Gate: skip for empty/tool-result-only messages — unless we need to
|
|
367
379
|
// reload after compaction (needsReload) or haven't initialized yet.
|
|
368
380
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -438,8 +450,10 @@ export class ConversationGraphMemory {
|
|
|
438
450
|
// turns. v1's `loadContextMemory` produced these as a side effect of
|
|
439
451
|
// hybrid retrieval; the v2 path skips that retrieval, so embed
|
|
440
452
|
// explicitly here.
|
|
453
|
+
const userQueryText = rawUserText ?? userQuery ?? "";
|
|
441
454
|
const userQueryEmbed = await this.computeQueryVectors(
|
|
442
|
-
|
|
455
|
+
userQueryText,
|
|
456
|
+
userQueryText,
|
|
443
457
|
config,
|
|
444
458
|
signal,
|
|
445
459
|
);
|
|
@@ -598,8 +612,16 @@ export class ConversationGraphMemory {
|
|
|
598
612
|
if (v2.routed) {
|
|
599
613
|
// Surface a per-turn query embedding so PKB hint search still runs
|
|
600
614
|
// on v2 turns. v1's `retrieveForTurn` produced these as a side effect;
|
|
601
|
-
// the v2 path skips that retrieval, so embed explicitly here.
|
|
615
|
+
// the v2 path skips that retrieval, so embed explicitly here. Match
|
|
616
|
+
// v1's split: dense embeds the combined assistant+user text (short
|
|
617
|
+
// referential follow-ups like "do that one" need the assistant turn
|
|
618
|
+
// for semantic grounding), while sparse uses the user message alone
|
|
619
|
+
// to keep lexical signal focused on what the user actually said.
|
|
620
|
+
const denseQueryText = [assistantLast, userLast]
|
|
621
|
+
.filter((m) => m.length > 0)
|
|
622
|
+
.join("\n\n");
|
|
602
623
|
const perTurnEmbed = await this.computeQueryVectors(
|
|
624
|
+
denseQueryText,
|
|
603
625
|
userLast,
|
|
604
626
|
config,
|
|
605
627
|
signal,
|
|
@@ -697,24 +719,30 @@ export class ConversationGraphMemory {
|
|
|
697
719
|
* static fallback rather than blocking the turn.
|
|
698
720
|
*/
|
|
699
721
|
private async computeQueryVectors(
|
|
700
|
-
|
|
722
|
+
denseText: string,
|
|
723
|
+
sparseText: string,
|
|
701
724
|
config: AssistantConfig,
|
|
702
725
|
signal: AbortSignal,
|
|
703
726
|
): Promise<{ dense?: number[]; sparse?: QdrantSparseVector }> {
|
|
704
|
-
const
|
|
705
|
-
|
|
727
|
+
const trimmedDense = denseText.trim();
|
|
728
|
+
const trimmedSparse = sparseText.trim();
|
|
706
729
|
let dense: number[] | undefined;
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
730
|
+
if (trimmedDense.length > 0) {
|
|
731
|
+
try {
|
|
732
|
+
const result = await embedWithRetry(config, [trimmedDense], { signal });
|
|
733
|
+
dense = result.vectors[0];
|
|
734
|
+
} catch (err) {
|
|
735
|
+
log.warn(
|
|
736
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
737
|
+
"Failed to embed query for PKB hints on v2 path",
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
let sparse: QdrantSparseVector | undefined;
|
|
742
|
+
if (trimmedSparse.length > 0) {
|
|
743
|
+
const sparseRaw = generateSparseEmbedding(trimmedSparse);
|
|
744
|
+
sparse = sparseRaw.indices.length > 0 ? sparseRaw : undefined;
|
|
715
745
|
}
|
|
716
|
-
const sparseRaw = generateSparseEmbedding(trimmed);
|
|
717
|
-
const sparse = sparseRaw.indices.length > 0 ? sparseRaw : undefined;
|
|
718
746
|
return { dense, sparse };
|
|
719
747
|
}
|
|
720
748
|
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// Memory Tool definitions for agentic recall and remember.
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
6
|
-
import type { AssistantConfig } from "../../config/types.js";
|
|
7
5
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
8
6
|
import {
|
|
9
7
|
ALL_RECALL_SOURCES,
|
|
@@ -20,7 +18,7 @@ const RECALL_DEPTHS = ["fast", "standard", "deep"] as const;
|
|
|
20
18
|
export const graphRecallDefinition: ToolDefinition = {
|
|
21
19
|
name: "recall",
|
|
22
20
|
description:
|
|
23
|
-
'Search local information the moment you feel uncertain. Use recall for memory, past conversations, and workspace files — before you guess, before you ask, before you hedge. Auto-injection is incomplete by design; it surfaces patterns, not the specifics you need to answer well. If you catch yourself reaching for "I think", "I believe", "if I remember", "didn\'t we", "last time" — that\'s the signal. Recall. If
|
|
21
|
+
'Search local information the moment you feel uncertain. Use recall for memory, past conversations, and workspace files — before you guess, before you ask, before you hedge. Auto-injection is incomplete by design; it surfaces patterns, not the specifics you need to answer well. If you catch yourself reaching for "I think", "I believe", "if I remember", "didn\'t we", "last time" — that\'s the signal. Recall. If a turn references someone, a place, a decision, a document, or prior work you should be able to find locally — recall. Call it multiple times per conversation if the turn warrants it. Be specific in your query for best results.',
|
|
24
22
|
input_schema: {
|
|
25
23
|
type: "object",
|
|
26
24
|
properties: {
|
|
@@ -56,36 +54,13 @@ export const graphRecallDefinition: ToolDefinition = {
|
|
|
56
54
|
};
|
|
57
55
|
|
|
58
56
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* backstop enabled.
|
|
57
|
+
* `remember` tool description. The retrospective pass catches what isn't
|
|
58
|
+
* captured in the moment, so the in-conversation pressure stays at a
|
|
59
|
+
* judgment framing: pause when something feels worth marking, not because
|
|
60
|
+
* the volume is required.
|
|
64
61
|
*/
|
|
65
|
-
const
|
|
66
|
-
"Remember anything concrete
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Relaxed `remember` tool description used when `memory-retrospective` is
|
|
70
|
-
* ON. The retrospective pass catches what isn't captured in the moment, so
|
|
71
|
-
* the in-conversation pressure eases to a judgment framing: pause when
|
|
72
|
-
* something feels worth marking, not because the volume is required.
|
|
73
|
-
*/
|
|
74
|
-
const REMEMBER_DESCRIPTION_RELAXED =
|
|
75
|
-
"Remember anything concrete the user shared: corrections, plans, decisions, felt moments, names, dates, commitments, preferences. Corrections are the highest priority — call `remember` the same turn the correction lands. You don't have to call this on every turn; a retrospective pass reviews the conversation after each message-count / time interval and saves what you didn't capture. Use judgment: pause and remember when something feels worth marking, not because the volume is required.";
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Return the description that should appear in the `remember` tool
|
|
79
|
-
* registration for the current config. The variant is selected by the
|
|
80
|
-
* `memory-retrospective` assistant feature flag. Exposed as a function so
|
|
81
|
-
* the tool registrar can compute the value at registration time without
|
|
82
|
-
* importing config layers into the static definition.
|
|
83
|
-
*/
|
|
84
|
-
export function getRememberDescription(config: AssistantConfig): string {
|
|
85
|
-
return isAssistantFeatureFlagEnabled("memory-retrospective", config)
|
|
86
|
-
? REMEMBER_DESCRIPTION_RELAXED
|
|
87
|
-
: REMEMBER_DESCRIPTION_DEFAULT;
|
|
88
|
-
}
|
|
62
|
+
const REMEMBER_DESCRIPTION =
|
|
63
|
+
"Remember anything concrete shared in conversation: corrections, plans, decisions, felt moments, names, dates, commitments, preferences. Corrections are the highest priority — call `remember` the same turn the correction lands. You don't have to call this on every turn; a retrospective pass reviews the conversation after each message-count / time interval and saves what you didn't capture. Use judgment: pause and remember when something feels worth marking, not because the volume is required.";
|
|
89
64
|
|
|
90
65
|
/**
|
|
91
66
|
* Save a fact to the assistant's knowledge base. The fact is appended to
|
|
@@ -94,16 +69,10 @@ export function getRememberDescription(config: AssistantConfig): string {
|
|
|
94
69
|
* writes go under `memory/`; otherwise they go under `pkb/`. Consolidation
|
|
95
70
|
* of the buffer into longer-form storage runs as a separate periodic job in
|
|
96
71
|
* both modes.
|
|
97
|
-
*
|
|
98
|
-
* The static `description` field carries the default (high-pressure) text
|
|
99
|
-
* so any direct importer that doesn't go through `getRememberDescription`
|
|
100
|
-
* still gets a valid tool definition. The registered `RememberTool` in
|
|
101
|
-
* `tools/memory/register.ts` overrides this at registration time with the
|
|
102
|
-
* flag-aware variant.
|
|
103
72
|
*/
|
|
104
73
|
export const graphRememberDefinition: ToolDefinition = {
|
|
105
74
|
name: "remember",
|
|
106
|
-
description:
|
|
75
|
+
description: REMEMBER_DESCRIPTION,
|
|
107
76
|
input_schema: {
|
|
108
77
|
type: "object",
|
|
109
78
|
properties: {
|
|
@@ -115,7 +84,7 @@ export const graphRememberDefinition: ToolDefinition = {
|
|
|
115
84
|
finish_turn: {
|
|
116
85
|
type: "boolean",
|
|
117
86
|
description:
|
|
118
|
-
"When you have nothing else to say and want to
|
|
87
|
+
"When you have nothing else to say and want to yield the turn you MUST set this to true. When true, your turn ends after this tool call. It's critical that you do this in order to avoid unnecessary LLM calls.",
|
|
119
88
|
},
|
|
120
89
|
},
|
|
121
90
|
required: ["content"],
|
package/src/memory/indexer.ts
CHANGED
|
@@ -169,32 +169,30 @@ export async function indexMessageNow(
|
|
|
169
169
|
const batchSize = config.extraction.batchSize ?? 10;
|
|
170
170
|
const idleTimeoutMs = config.extraction.idleTimeoutMs ?? 300_000;
|
|
171
171
|
|
|
172
|
+
// Reading config here is best-effort: when it fails we treat v2 as
|
|
173
|
+
// inactive (failing-open to v1) so a config error never silently
|
|
174
|
+
// drops the extraction or summarization paths.
|
|
175
|
+
let triggerConfig: ReturnType<typeof getConfig> | null = null;
|
|
176
|
+
try {
|
|
177
|
+
triggerConfig = getConfig();
|
|
178
|
+
} catch (err) {
|
|
179
|
+
log.debug(
|
|
180
|
+
{ err, conversationId: input.conversationId },
|
|
181
|
+
"Skipping feature-gated extraction triggers: failed to load config",
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const v2Config =
|
|
186
|
+
triggerConfig != null && triggerConfig.memory.v2.enabled
|
|
187
|
+
? triggerConfig
|
|
188
|
+
: null;
|
|
189
|
+
|
|
172
190
|
// Recursion guard: skip graph extraction + auto-analysis enqueues
|
|
173
191
|
// when the source conversation is itself an auto-analysis
|
|
174
192
|
// conversation. The analysis agent writes memory directly via tools,
|
|
175
193
|
// so extracting from its reflective musings would double-count and
|
|
176
194
|
// analyzing its own output would loop indefinitely.
|
|
177
|
-
// Summaries still run — they feed the graph retrieval pipeline and
|
|
178
|
-
// are not recursion-prone.
|
|
179
195
|
if (!isAutoAnalysisSource) {
|
|
180
|
-
// Reading config here is best-effort: when it fails we treat v2 as
|
|
181
|
-
// inactive (failing-open to v1) so a config error never silently
|
|
182
|
-
// drops both extraction paths.
|
|
183
|
-
let triggerConfig: ReturnType<typeof getConfig> | null = null;
|
|
184
|
-
try {
|
|
185
|
-
triggerConfig = getConfig();
|
|
186
|
-
} catch (err) {
|
|
187
|
-
log.debug(
|
|
188
|
-
{ err, conversationId: input.conversationId },
|
|
189
|
-
"Skipping feature-gated extraction triggers: failed to load config",
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const v2Config =
|
|
194
|
-
triggerConfig != null && triggerConfig.memory.v2.enabled
|
|
195
|
-
? triggerConfig
|
|
196
|
-
: null;
|
|
197
|
-
|
|
198
196
|
// ── Graph extraction (v1) ───────────────────────────────────────
|
|
199
197
|
// Suppressed when v2 is active — v2 reads memory from buffer.md
|
|
200
198
|
// and concept pages, so the v1 graph would be stale data nobody
|
|
@@ -302,15 +300,22 @@ export async function indexMessageNow(
|
|
|
302
300
|
}
|
|
303
301
|
}
|
|
304
302
|
|
|
305
|
-
// ── Conversation summarization (
|
|
306
|
-
// Summaries feed the graph retrieval pipeline
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
303
|
+
// ── Conversation summarization (v1) ───────────────────────────────
|
|
304
|
+
// Summaries feed the v1 graph retrieval pipeline (fetchRecentSummaries,
|
|
305
|
+
// semantic search). Suppressed when v2 is active — v2 readers (concept
|
|
306
|
+
// pages, activation pipeline) do not consume `memorySummaries`, so the
|
|
307
|
+
// summarization LLM call would produce rows nothing reads. Stale rows
|
|
308
|
+
// from before v2 was enabled are short-circuited at dispatch in
|
|
309
|
+
// jobs-worker.ts. Debounced on the same idle timeout — no threshold
|
|
310
|
+
// trigger needed since summaries compress the whole conversation, not
|
|
311
|
+
// incremental batches.
|
|
312
|
+
if (v2Config == null) {
|
|
313
|
+
upsertDebouncedJob(
|
|
314
|
+
"build_conversation_summary",
|
|
315
|
+
{ conversationId: input.conversationId },
|
|
316
|
+
Date.now() + idleTimeoutMs,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
314
319
|
}
|
|
315
320
|
|
|
316
321
|
if (skippedShortSegments > 0) {
|
|
@@ -363,6 +363,59 @@ export function findActiveVoiceInvites(params: {
|
|
|
363
363
|
return rows.map(rowToInvite);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// claimA2AInvite — validate + consume an A2A invite token
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
export function claimA2AInvite(params: {
|
|
371
|
+
tokenHash: string;
|
|
372
|
+
redeemedByExternalUserId: string;
|
|
373
|
+
}): { claimed: boolean; invite: IngressInvite | null; error?: string } {
|
|
374
|
+
const invite = findByTokenHash(params.tokenHash);
|
|
375
|
+
|
|
376
|
+
if (!invite) {
|
|
377
|
+
return { claimed: false, invite: null, error: "not_found" };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (invite.sourceChannel !== "a2a") {
|
|
381
|
+
return { claimed: false, invite, error: "wrong_channel" };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Idempotency: if already redeemed by the same acceptor, return success
|
|
385
|
+
if (invite.status === "redeemed") {
|
|
386
|
+
if (invite.redeemedByExternalUserId === params.redeemedByExternalUserId) {
|
|
387
|
+
return { claimed: true, invite };
|
|
388
|
+
}
|
|
389
|
+
return { claimed: false, invite, error: "already_redeemed_by_other" };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (invite.status !== "active") {
|
|
393
|
+
return { claimed: false, invite, error: "not_found" };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (Date.now() >= invite.expiresAt) {
|
|
397
|
+
markInviteExpired(invite.id);
|
|
398
|
+
return { claimed: false, invite, error: "expired" };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (invite.useCount >= invite.maxUses) {
|
|
402
|
+
return { claimed: false, invite, error: "already_redeemed" };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const recorded = recordInviteUse({
|
|
406
|
+
inviteId: invite.id,
|
|
407
|
+
externalUserId: params.redeemedByExternalUserId,
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
if (!recorded) {
|
|
411
|
+
return { claimed: false, invite, error: "not_found" };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Re-read to get updated state
|
|
415
|
+
const updated = findByTokenHash(params.tokenHash);
|
|
416
|
+
return { claimed: true, invite: updated };
|
|
417
|
+
}
|
|
418
|
+
|
|
366
419
|
// ---------------------------------------------------------------------------
|
|
367
420
|
// findByInviteCodeHash
|
|
368
421
|
// ---------------------------------------------------------------------------
|
|
@@ -151,6 +151,8 @@ type MemoryJob = ReturnType<MemoryJobMod["claimMemoryJobs"]>[number];
|
|
|
151
151
|
const { embedConceptPageJob, enqueueEmbedConceptPageJob } =
|
|
152
152
|
await import("../embed-concept-page.js");
|
|
153
153
|
const { writePage } = await import("../../v2/page-store.js");
|
|
154
|
+
const { _resetQdrantBreaker, isQdrantBreakerOpen, withQdrantBreaker } =
|
|
155
|
+
await import("../../qdrant-circuit-breaker.js");
|
|
154
156
|
|
|
155
157
|
// Use a tiny vectorSize so the cache-dim check matches our stub vector.
|
|
156
158
|
const TEST_CONFIG = {
|
|
@@ -186,6 +188,7 @@ beforeEach(() => {
|
|
|
186
188
|
embedWithBackendCalls.length = 0;
|
|
187
189
|
upsertCalls.length = 0;
|
|
188
190
|
deleteCalls.length = 0;
|
|
191
|
+
_resetQdrantBreaker();
|
|
189
192
|
});
|
|
190
193
|
|
|
191
194
|
afterEach(() => {
|
|
@@ -436,6 +439,76 @@ describe("embedConceptPageJob — defensive", () => {
|
|
|
436
439
|
});
|
|
437
440
|
});
|
|
438
441
|
|
|
442
|
+
describe("embedConceptPageJob — Qdrant breaker integration", () => {
|
|
443
|
+
test("half-open probe success closes the breaker so embed catch-up unthrottles", async () => {
|
|
444
|
+
// Trip the breaker by recording 5 consecutive Qdrant failures. Without
|
|
445
|
+
// this fix, `embed_concept_page` bypassed the breaker entirely — winning
|
|
446
|
+
// the half-open probe slot did not transition state back to closed and
|
|
447
|
+
// the embed lane stayed throttled at one job per tick indefinitely.
|
|
448
|
+
for (let i = 0; i < 5; i++) {
|
|
449
|
+
try {
|
|
450
|
+
await withQdrantBreaker(async () => {
|
|
451
|
+
throw new Error("simulated qdrant failure");
|
|
452
|
+
});
|
|
453
|
+
} catch {
|
|
454
|
+
// expected
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
expect(isQdrantBreakerOpen()).toBe(true);
|
|
458
|
+
|
|
459
|
+
// Advance time past the 30s cooldown so the next breaker call transitions
|
|
460
|
+
// open → half-open and allows the probe through.
|
|
461
|
+
const originalNow = Date.now;
|
|
462
|
+
Date.now = () => originalNow() + 60_000;
|
|
463
|
+
try {
|
|
464
|
+
await writePage(tmpWorkspace, {
|
|
465
|
+
slug: "probe-success",
|
|
466
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
467
|
+
body: "Probe body.\n",
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
await embedConceptPageJob(
|
|
471
|
+
makeJob({ slug: "probe-success" }),
|
|
472
|
+
TEST_CONFIG,
|
|
473
|
+
);
|
|
474
|
+
} finally {
|
|
475
|
+
Date.now = originalNow;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
expect(upsertCalls).toHaveLength(1);
|
|
479
|
+
// Probe succeeded → breaker should now be closed (not open, not
|
|
480
|
+
// half-open), restoring full embed-lane concurrency.
|
|
481
|
+
expect(isQdrantBreakerOpen()).toBe(false);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
test("half-open probe success on the delete path also closes the breaker", async () => {
|
|
485
|
+
// Same flow as above but exercising the missing-page branch — both v2
|
|
486
|
+
// Qdrant calls (`upsert` and `delete`) must close the breaker on success.
|
|
487
|
+
for (let i = 0; i < 5; i++) {
|
|
488
|
+
try {
|
|
489
|
+
await withQdrantBreaker(async () => {
|
|
490
|
+
throw new Error("simulated qdrant failure");
|
|
491
|
+
});
|
|
492
|
+
} catch {
|
|
493
|
+
// expected
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
expect(isQdrantBreakerOpen()).toBe(true);
|
|
497
|
+
|
|
498
|
+
const originalNow = Date.now;
|
|
499
|
+
Date.now = () => originalNow() + 60_000;
|
|
500
|
+
try {
|
|
501
|
+
// No `writePage` — the handler takes the delete branch.
|
|
502
|
+
await embedConceptPageJob(makeJob({ slug: "missing-slug" }), TEST_CONFIG);
|
|
503
|
+
} finally {
|
|
504
|
+
Date.now = originalNow;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
expect(deleteCalls).toEqual(["missing-slug"]);
|
|
508
|
+
expect(isQdrantBreakerOpen()).toBe(false);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
439
512
|
describe("enqueueEmbedConceptPageJob", () => {
|
|
440
513
|
test("enqueues a pending embed_concept_page job with the slug payload", () => {
|
|
441
514
|
const id = enqueueEmbedConceptPageJob({ slug: "alice-prefers-vs-code" });
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
import { embeddingInputContentHash } from "../embedding-types.js";
|
|
35
35
|
import { asString, blobToVector, vectorToBlob } from "../job-utils.js";
|
|
36
36
|
import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
|
|
37
|
+
import { withQdrantBreaker } from "../qdrant-circuit-breaker.js";
|
|
37
38
|
import { memoryEmbeddings } from "../schema.js";
|
|
38
39
|
import { readPage } from "../v2/page-store.js";
|
|
39
40
|
import {
|
|
@@ -81,7 +82,10 @@ export async function embedConceptPageJob(
|
|
|
81
82
|
if (!page) {
|
|
82
83
|
// Page was deleted out from under us — clean up the prior embedding so
|
|
83
84
|
// retrieval no longer surfaces a slug whose disk-side prose is gone.
|
|
84
|
-
|
|
85
|
+
// Route through the Qdrant breaker so success on the half-open probe
|
|
86
|
+
// slot transitions the breaker back to closed and unthrottles embed
|
|
87
|
+
// catch-up.
|
|
88
|
+
await withQdrantBreaker(() => deleteConceptPageEmbedding(slug));
|
|
85
89
|
return;
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -280,16 +284,21 @@ export async function embedConceptPageJob(
|
|
|
280
284
|
? await applyCorrectionIfCalibrated(summaryDense, writeProvider, writeModel)
|
|
281
285
|
: undefined;
|
|
282
286
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
287
|
+
// Route through the Qdrant breaker so a probe-slot success transitions the
|
|
288
|
+
// breaker back to closed; without this wrapper the embed lane stays
|
|
289
|
+
// throttled at one job per tick indefinitely after a half-open success.
|
|
290
|
+
await withQdrantBreaker(() =>
|
|
291
|
+
upsertConceptPageEmbedding({
|
|
292
|
+
slug,
|
|
293
|
+
dense: correctedDense,
|
|
294
|
+
sparse,
|
|
295
|
+
summary:
|
|
296
|
+
correctedSummaryDense && summarySparse
|
|
297
|
+
? { dense: correctedSummaryDense, sparse: summarySparse }
|
|
298
|
+
: undefined,
|
|
299
|
+
updatedAt: now,
|
|
300
|
+
}),
|
|
301
|
+
);
|
|
293
302
|
}
|
|
294
303
|
|
|
295
304
|
/** SQLite cache row shape returned by `readEmbeddingCache`. */
|
|
@@ -95,7 +95,6 @@ const V1_QDRANT_JOB_TYPES = new Set<MemoryJobType>([
|
|
|
95
95
|
"embed_attachment",
|
|
96
96
|
"embed_graph_node",
|
|
97
97
|
"embed_pkb_file",
|
|
98
|
-
"graph_trigger_embed",
|
|
99
98
|
"rebuild_index",
|
|
100
99
|
"delete_qdrant_vectors",
|
|
101
100
|
]);
|
|
@@ -525,6 +524,12 @@ async function processJob(
|
|
|
525
524
|
pruneOldTraceEventsJob(job, config);
|
|
526
525
|
return;
|
|
527
526
|
case "build_conversation_summary":
|
|
527
|
+
// Stale rows enqueued before v2 was enabled must not consume the
|
|
528
|
+
// `conversationSummarization` LLM budget — v2 readers do not consume
|
|
529
|
+
// `memorySummaries`, mirroring the `graph_extract` gate below.
|
|
530
|
+
if (config.memory.v2.enabled) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
528
533
|
await buildConversationSummaryJob(job, config);
|
|
529
534
|
return;
|
|
530
535
|
case "backfill":
|
|
@@ -57,6 +57,7 @@ interface ClickHouseRow {
|
|
|
57
57
|
request_payload: string;
|
|
58
58
|
response_payload: string;
|
|
59
59
|
created_at: string;
|
|
60
|
+
agent_loop_exit_reason: string;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
/** Injectable fetch override for tests. Defaults to globalThis.fetch. */
|
|
@@ -123,7 +124,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
123
124
|
provider,
|
|
124
125
|
request_payload,
|
|
125
126
|
response_payload,
|
|
126
|
-
toUnixTimestamp64Milli(created_at) AS created_at
|
|
127
|
+
toUnixTimestamp64Milli(created_at) AS created_at,
|
|
128
|
+
agent_loop_exit_reason
|
|
127
129
|
FROM ${this.tableRef()}
|
|
128
130
|
WHERE assistant_id = {assistant_id:String}
|
|
129
131
|
AND id = {log_id:String}
|
|
@@ -172,14 +174,21 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
172
174
|
private async selectByMessageIds(ids: string[]): Promise<LogRow[]> {
|
|
173
175
|
if (ids.length === 0) return [];
|
|
174
176
|
const aid = await this.assistantId();
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
// Bind each id as its own {id_N:String} placeholder. The IDs ultimately
|
|
178
|
+
// come from a caller-supplied path parameter — `getAssistantMessageIdsInTurn`
|
|
179
|
+
// passes the input straight through when the message lookup misses — so
|
|
180
|
+
// inline literal building (even with quote-doubling) is unsafe: ClickHouse
|
|
181
|
+
// honors `\'` as an escaped quote inside string literals, letting a
|
|
182
|
+
// backslash-suffixed id break out of the IN clause and bypass the
|
|
183
|
+
// `assistant_id` scope filter. Type-bound parameters carry value, not
|
|
184
|
+
// syntax, regardless of content.
|
|
185
|
+
const params: Record<string, string> = { assistant_id: aid };
|
|
186
|
+
const placeholders: string[] = [];
|
|
187
|
+
for (let i = 0; i < ids.length; i++) {
|
|
188
|
+
const key = `id_${i}`;
|
|
189
|
+
params[key] = ids[i]!;
|
|
190
|
+
placeholders.push(`{${key}:String}`);
|
|
191
|
+
}
|
|
183
192
|
const sql = `SELECT
|
|
184
193
|
id,
|
|
185
194
|
conversation_id,
|
|
@@ -187,14 +196,15 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
187
196
|
provider,
|
|
188
197
|
request_payload,
|
|
189
198
|
response_payload,
|
|
190
|
-
toUnixTimestamp64Milli(created_at) AS created_at
|
|
199
|
+
toUnixTimestamp64Milli(created_at) AS created_at,
|
|
200
|
+
agent_loop_exit_reason
|
|
191
201
|
FROM ${this.tableRef()}
|
|
192
202
|
WHERE assistant_id = {assistant_id:String}
|
|
193
|
-
AND message_id IN ${
|
|
203
|
+
AND message_id IN (${placeholders.join(",")})
|
|
194
204
|
ORDER BY created_at ASC, id ASC
|
|
195
205
|
LIMIT 1 BY id
|
|
196
206
|
FORMAT JSONEachRow`;
|
|
197
|
-
const rows = await this.exec(sql,
|
|
207
|
+
const rows = await this.exec(sql, params);
|
|
198
208
|
return rows.map((r) => this.toLogRow(r));
|
|
199
209
|
}
|
|
200
210
|
|
|
@@ -276,6 +286,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
276
286
|
requestPayload: row.request_payload,
|
|
277
287
|
responsePayload: row.response_payload,
|
|
278
288
|
createdAt: Number(row.created_at),
|
|
289
|
+
agentLoopExitReason:
|
|
290
|
+
row.agent_loop_exit_reason === "" ? null : row.agent_loop_exit_reason,
|
|
279
291
|
};
|
|
280
292
|
}
|
|
281
293
|
|