@vellumai/assistant 0.8.2 → 0.8.4
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 +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- 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-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- 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 +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -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__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- 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/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- 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 +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/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/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- 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-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- 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/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- 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 +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/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 +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__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 +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- 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 +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- 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__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `assistant/src/memory/v2/injection-events.ts` and its sibling
|
|
3
|
+
* migration `256-memory-v2-injection-events.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Coverage matrix:
|
|
6
|
+
* - Migration creates the table + both indexes; safe to re-run.
|
|
7
|
+
* - Backfill replays router-sourced concepts from memory_v2_activation_logs
|
|
8
|
+
* and is idempotent on a forced re-run with cleared checkpoint.
|
|
9
|
+
* - Backfill is a no-op when the activation-logs table doesn't exist
|
|
10
|
+
* (pre-234 DB).
|
|
11
|
+
* - recordInjectionEvents appends one row per slug per call; empty list
|
|
12
|
+
* is a no-op.
|
|
13
|
+
* - computeInjectionScore matches the closed-form decay at known deltas
|
|
14
|
+
* (0d ≈ 1, 3d ≈ 0.5, 6d ≈ 0.25) and sums multiple events linearly.
|
|
15
|
+
* - computeInjectionScores returns the same per-slug values in batch and
|
|
16
|
+
* omits slugs with no events.
|
|
17
|
+
*
|
|
18
|
+
* Uses an in-memory bun:sqlite database — no real workspace DB.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Database } from "bun:sqlite";
|
|
22
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
23
|
+
|
|
24
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
25
|
+
|
|
26
|
+
import { makeMockLogger } from "../../../__tests__/helpers/mock-logger.js";
|
|
27
|
+
|
|
28
|
+
mock.module("../../../util/logger.js", () => ({
|
|
29
|
+
getLogger: () => makeMockLogger(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
import type { DrizzleDb } from "../../db-connection.js";
|
|
33
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
34
|
+
import { migrateMemoryV2ActivationLogs } from "../../migrations/234-memory-v2-activation-logs.js";
|
|
35
|
+
import {
|
|
36
|
+
downMemoryV2InjectionEvents,
|
|
37
|
+
migrateMemoryV2InjectionEvents,
|
|
38
|
+
} from "../../migrations/256-memory-v2-injection-events.js";
|
|
39
|
+
import * as schema from "../../schema.js";
|
|
40
|
+
import {
|
|
41
|
+
computeInjectionScore,
|
|
42
|
+
computeInjectionScores,
|
|
43
|
+
INJECTION_SCORE_HALF_LIFE_MS,
|
|
44
|
+
recordInjectionEvents,
|
|
45
|
+
} from "../injection-events.js";
|
|
46
|
+
|
|
47
|
+
// memory_checkpoints is required by withCrashRecovery and is normally
|
|
48
|
+
// created by an early core migration. Stand it up by hand so we can run
|
|
49
|
+
// the v2 migrations in isolation against a fresh in-memory DB.
|
|
50
|
+
const CHECKPOINTS_DDL = /*sql*/ `
|
|
51
|
+
CREATE TABLE memory_checkpoints (
|
|
52
|
+
key TEXT PRIMARY KEY,
|
|
53
|
+
value TEXT NOT NULL,
|
|
54
|
+
updated_at INTEGER NOT NULL
|
|
55
|
+
)
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
let sqlite: Database;
|
|
59
|
+
let database: DrizzleDb;
|
|
60
|
+
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
sqlite = new Database(":memory:");
|
|
63
|
+
database = drizzle(sqlite, { schema });
|
|
64
|
+
getSqliteFrom(database).exec(CHECKPOINTS_DDL);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
sqlite.close();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
function insertActivationLog(
|
|
72
|
+
rawDb: Database,
|
|
73
|
+
args: {
|
|
74
|
+
id: string;
|
|
75
|
+
concepts: Array<{ slug: string; source: string; status?: string }>;
|
|
76
|
+
createdAt: number;
|
|
77
|
+
},
|
|
78
|
+
): void {
|
|
79
|
+
rawDb
|
|
80
|
+
.prepare(
|
|
81
|
+
`INSERT INTO memory_v2_activation_logs (
|
|
82
|
+
id, conversation_id, message_id, turn, mode,
|
|
83
|
+
concepts_json, skills_json, config_json, created_at
|
|
84
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
85
|
+
)
|
|
86
|
+
.run(
|
|
87
|
+
args.id,
|
|
88
|
+
"conv-1",
|
|
89
|
+
`msg-${args.id}`,
|
|
90
|
+
1,
|
|
91
|
+
"router",
|
|
92
|
+
JSON.stringify(args.concepts),
|
|
93
|
+
"[]",
|
|
94
|
+
"{}",
|
|
95
|
+
args.createdAt,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
describe("migrateMemoryV2InjectionEvents", () => {
|
|
100
|
+
test("creates table and both indexes; safe to re-run", () => {
|
|
101
|
+
migrateMemoryV2InjectionEvents(database);
|
|
102
|
+
migrateMemoryV2InjectionEvents(database);
|
|
103
|
+
|
|
104
|
+
const raw = getSqliteFrom(database);
|
|
105
|
+
const table = raw
|
|
106
|
+
.query(
|
|
107
|
+
`SELECT name FROM sqlite_master WHERE type='table' AND name='memory_v2_injection_events'`,
|
|
108
|
+
)
|
|
109
|
+
.get();
|
|
110
|
+
expect(table).toBeTruthy();
|
|
111
|
+
|
|
112
|
+
const indexNames = new Set(
|
|
113
|
+
(
|
|
114
|
+
raw
|
|
115
|
+
.query(
|
|
116
|
+
`SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='memory_v2_injection_events'`,
|
|
117
|
+
)
|
|
118
|
+
.all() as Array<{ name: string }>
|
|
119
|
+
).map((r) => r.name),
|
|
120
|
+
);
|
|
121
|
+
expect(indexNames.has("idx_memory_v2_injection_events_slug_time")).toBe(
|
|
122
|
+
true,
|
|
123
|
+
);
|
|
124
|
+
expect(indexNames.has("idx_memory_v2_injection_events_time")).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("backfill replays router-sourced concepts and ignores carry_over", () => {
|
|
128
|
+
migrateMemoryV2ActivationLogs(database);
|
|
129
|
+
const raw = getSqliteFrom(database);
|
|
130
|
+
insertActivationLog(raw, {
|
|
131
|
+
id: "log-1",
|
|
132
|
+
concepts: [
|
|
133
|
+
{ slug: "alice", source: "router", status: "injected" },
|
|
134
|
+
{ slug: "bob", source: "router", status: "in_context" },
|
|
135
|
+
{ slug: "ghost", source: "carry_over", status: "not_injected" },
|
|
136
|
+
],
|
|
137
|
+
createdAt: 1_000_000,
|
|
138
|
+
});
|
|
139
|
+
insertActivationLog(raw, {
|
|
140
|
+
id: "log-2",
|
|
141
|
+
concepts: [{ slug: "alice", source: "router", status: "injected" }],
|
|
142
|
+
createdAt: 2_000_000,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
migrateMemoryV2InjectionEvents(database);
|
|
146
|
+
|
|
147
|
+
const rows = raw
|
|
148
|
+
.query(
|
|
149
|
+
`SELECT slug, injected_at FROM memory_v2_injection_events ORDER BY injected_at, slug`,
|
|
150
|
+
)
|
|
151
|
+
.all() as Array<{ slug: string; injected_at: number }>;
|
|
152
|
+
expect(rows).toEqual([
|
|
153
|
+
{ slug: "alice", injected_at: 1_000_000 },
|
|
154
|
+
{ slug: "bob", injected_at: 1_000_000 },
|
|
155
|
+
{ slug: "alice", injected_at: 2_000_000 },
|
|
156
|
+
]);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("backfill is a no-op when memory_v2_activation_logs is absent", () => {
|
|
160
|
+
// No activation-logs migration applied first.
|
|
161
|
+
expect(() => migrateMemoryV2InjectionEvents(database)).not.toThrow();
|
|
162
|
+
const { n } = getSqliteFrom(database)
|
|
163
|
+
.query(`SELECT COUNT(*) as n FROM memory_v2_injection_events`)
|
|
164
|
+
.get() as { n: number };
|
|
165
|
+
expect(n).toBe(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("forced re-run does not double-insert existing events", () => {
|
|
169
|
+
migrateMemoryV2ActivationLogs(database);
|
|
170
|
+
const raw = getSqliteFrom(database);
|
|
171
|
+
insertActivationLog(raw, {
|
|
172
|
+
id: "log-1",
|
|
173
|
+
concepts: [{ slug: "alice", source: "router", status: "injected" }],
|
|
174
|
+
createdAt: 1_000_000,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
migrateMemoryV2InjectionEvents(database);
|
|
178
|
+
// Simulate someone manually clearing the checkpoint — the in-table
|
|
179
|
+
// guard should still prevent re-backfill.
|
|
180
|
+
raw
|
|
181
|
+
.prepare(
|
|
182
|
+
`DELETE FROM memory_checkpoints WHERE key = 'migration_memory_v2_injection_events_v1'`,
|
|
183
|
+
)
|
|
184
|
+
.run();
|
|
185
|
+
migrateMemoryV2InjectionEvents(database);
|
|
186
|
+
|
|
187
|
+
const { n } = raw
|
|
188
|
+
.query(`SELECT COUNT(*) as n FROM memory_v2_injection_events`)
|
|
189
|
+
.get() as { n: number };
|
|
190
|
+
expect(n).toBe(1);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("downMemoryV2InjectionEvents drops the table", () => {
|
|
194
|
+
migrateMemoryV2InjectionEvents(database);
|
|
195
|
+
downMemoryV2InjectionEvents(database);
|
|
196
|
+
const table = getSqliteFrom(database)
|
|
197
|
+
.query(
|
|
198
|
+
`SELECT name FROM sqlite_master WHERE type='table' AND name='memory_v2_injection_events'`,
|
|
199
|
+
)
|
|
200
|
+
.get();
|
|
201
|
+
expect(table).toBeFalsy();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("recordInjectionEvents", () => {
|
|
206
|
+
beforeEach(() => {
|
|
207
|
+
migrateMemoryV2InjectionEvents(database);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("appends one row per slug at the same timestamp", () => {
|
|
211
|
+
const t = 1_000_000;
|
|
212
|
+
recordInjectionEvents(database, ["alice", "bob", "alice"], t);
|
|
213
|
+
const rows = getSqliteFrom(database)
|
|
214
|
+
.query(
|
|
215
|
+
`SELECT slug, injected_at FROM memory_v2_injection_events ORDER BY id`,
|
|
216
|
+
)
|
|
217
|
+
.all();
|
|
218
|
+
expect(rows).toEqual([
|
|
219
|
+
{ slug: "alice", injected_at: t },
|
|
220
|
+
{ slug: "bob", injected_at: t },
|
|
221
|
+
{ slug: "alice", injected_at: t },
|
|
222
|
+
]);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("empty list is a no-op", () => {
|
|
226
|
+
recordInjectionEvents(database, [], 1_000_000);
|
|
227
|
+
const { n } = getSqliteFrom(database)
|
|
228
|
+
.query(`SELECT COUNT(*) as n FROM memory_v2_injection_events`)
|
|
229
|
+
.get() as { n: number };
|
|
230
|
+
expect(n).toBe(0);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("computeInjectionScore", () => {
|
|
235
|
+
beforeEach(() => {
|
|
236
|
+
migrateMemoryV2InjectionEvents(database);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("returns 0 for a slug with no events", () => {
|
|
240
|
+
expect(computeInjectionScore(database, "missing", Date.now())).toBe(0);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("single event 0 days ago → score ≈ 1", () => {
|
|
244
|
+
const now = 10_000_000_000;
|
|
245
|
+
recordInjectionEvents(database, ["alice"], now);
|
|
246
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(1, 5);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("single event 3 days (one half-life) ago → score ≈ 0.5", () => {
|
|
250
|
+
const now = 10_000_000_000;
|
|
251
|
+
recordInjectionEvents(
|
|
252
|
+
database,
|
|
253
|
+
["alice"],
|
|
254
|
+
now - INJECTION_SCORE_HALF_LIFE_MS,
|
|
255
|
+
);
|
|
256
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(0.5, 5);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("single event 6 days (two half-lives) ago → score ≈ 0.25", () => {
|
|
260
|
+
const now = 10_000_000_000;
|
|
261
|
+
recordInjectionEvents(
|
|
262
|
+
database,
|
|
263
|
+
["alice"],
|
|
264
|
+
now - 2 * INJECTION_SCORE_HALF_LIFE_MS,
|
|
265
|
+
);
|
|
266
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(0.25, 5);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("multiple events sum independently", () => {
|
|
270
|
+
const now = 10_000_000_000;
|
|
271
|
+
recordInjectionEvents(database, ["alice"], now);
|
|
272
|
+
recordInjectionEvents(
|
|
273
|
+
database,
|
|
274
|
+
["alice"],
|
|
275
|
+
now - INJECTION_SCORE_HALF_LIFE_MS,
|
|
276
|
+
);
|
|
277
|
+
recordInjectionEvents(
|
|
278
|
+
database,
|
|
279
|
+
["alice"],
|
|
280
|
+
now - 2 * INJECTION_SCORE_HALF_LIFE_MS,
|
|
281
|
+
);
|
|
282
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(1.75, 5);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe("computeInjectionScores", () => {
|
|
287
|
+
beforeEach(() => {
|
|
288
|
+
migrateMemoryV2InjectionEvents(database);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("returns the same per-slug values as the single-slug helper", () => {
|
|
292
|
+
const now = 10_000_000_000;
|
|
293
|
+
recordInjectionEvents(database, ["alice", "bob"], now);
|
|
294
|
+
recordInjectionEvents(
|
|
295
|
+
database,
|
|
296
|
+
["alice"],
|
|
297
|
+
now - INJECTION_SCORE_HALF_LIFE_MS,
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const scores = computeInjectionScores(
|
|
301
|
+
database,
|
|
302
|
+
["alice", "bob", "ghost"],
|
|
303
|
+
now,
|
|
304
|
+
);
|
|
305
|
+
expect(scores.get("alice")).toBeCloseTo(1.5, 5);
|
|
306
|
+
expect(scores.get("bob")).toBeCloseTo(1, 5);
|
|
307
|
+
// ghost has no events — omitted from the result, not present as 0.
|
|
308
|
+
expect(scores.has("ghost")).toBe(false);
|
|
309
|
+
expect(scores.get("alice")).toBeCloseTo(
|
|
310
|
+
computeInjectionScore(database, "alice", now),
|
|
311
|
+
5,
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("empty slug list returns empty map", () => {
|
|
316
|
+
expect(computeInjectionScores(database, [], Date.now()).size).toBe(0);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
@@ -161,6 +161,38 @@ mock.module("../skill-store.js", () => ({
|
|
|
161
161
|
listSkillEntries: () => Array.from(skillState.entries.values()),
|
|
162
162
|
}));
|
|
163
163
|
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// CLI-command-store mock
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
//
|
|
168
|
+
// Mirrors the skill-store mock. CLI subcommand synthetic entries flow through
|
|
169
|
+
// the unified pipeline under the `cli-commands/<name>` slug prefix and render
|
|
170
|
+
// under `### CLI Commands You Can Use`. Tests stage `cliCommandState.entries`
|
|
171
|
+
// and rely on `stageTurn` plumbing to land slugs in the candidate set.
|
|
172
|
+
|
|
173
|
+
interface CliCommandEntryStub {
|
|
174
|
+
id: string;
|
|
175
|
+
description: string;
|
|
176
|
+
content: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const cliCommandState = {
|
|
180
|
+
entries: new Map<string, CliCommandEntryStub>(),
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
mock.module("../cli-command-store.js", () => ({
|
|
184
|
+
getCliCommandCapability: (idOrSlug: string) => {
|
|
185
|
+
const id = idOrSlug.startsWith("cli-commands/")
|
|
186
|
+
? idOrSlug.slice("cli-commands/".length)
|
|
187
|
+
: idOrSlug;
|
|
188
|
+
return cliCommandState.entries.get(id) ?? null;
|
|
189
|
+
},
|
|
190
|
+
isCliCommandSlug: (slug: string) => slug.startsWith("cli-commands/"),
|
|
191
|
+
CLI_COMMAND_SLUG_PREFIX: "cli-commands/",
|
|
192
|
+
cliCommandSlugFor: (name: string) => `cli-commands/${name}`,
|
|
193
|
+
listCliCommandEntries: () => Array.from(cliCommandState.entries.values()),
|
|
194
|
+
}));
|
|
195
|
+
|
|
164
196
|
// ---------------------------------------------------------------------------
|
|
165
197
|
// Activation-log store mock
|
|
166
198
|
// ---------------------------------------------------------------------------
|
|
@@ -228,6 +260,8 @@ mock.module("../page-store.js", () => ({
|
|
|
228
260
|
interface RouterResultStub {
|
|
229
261
|
selectedSlugs: string[];
|
|
230
262
|
failureReason: string | null;
|
|
263
|
+
/** Tier provenance per slug. Defaults to `tier3:0` for any selected slug. */
|
|
264
|
+
sourceBySlug?: Map<string, string>;
|
|
231
265
|
}
|
|
232
266
|
|
|
233
267
|
const routerState = {
|
|
@@ -238,12 +272,20 @@ const routerState = {
|
|
|
238
272
|
mock.module("../router.js", () => ({
|
|
239
273
|
runRouter: async () => {
|
|
240
274
|
routerState.callCount++;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
275
|
+
const result = routerState.nextResult ?? {
|
|
276
|
+
selectedSlugs: [],
|
|
277
|
+
failureReason: null,
|
|
278
|
+
};
|
|
279
|
+
// Synthesize a default sourceBySlug for stubs that don't set one — pre-
|
|
280
|
+
// tier-provenance tests stage `selectedSlugs` only and expect every pick
|
|
281
|
+
// to flow through as a router selection. Treating them as `tier3:0` is
|
|
282
|
+
// the closest equivalent under the new model.
|
|
283
|
+
if (!result.sourceBySlug) {
|
|
284
|
+
const map = new Map<string, string>();
|
|
285
|
+
for (const slug of result.selectedSlugs) map.set(slug, "tier3:0");
|
|
286
|
+
result.sourceBySlug = map;
|
|
287
|
+
}
|
|
288
|
+
return result;
|
|
247
289
|
},
|
|
248
290
|
}));
|
|
249
291
|
|
|
@@ -356,8 +398,10 @@ import type { SkillEntry } from "../types.js";
|
|
|
356
398
|
const { getSqliteFrom } = await import("../../db-connection.js");
|
|
357
399
|
const { migrateActivationState } =
|
|
358
400
|
await import("../../migrations/232-activation-state.js");
|
|
401
|
+
const { migrateMemoryV2InjectionEvents } =
|
|
402
|
+
await import("../../migrations/256-memory-v2-injection-events.js");
|
|
359
403
|
const schema = await import("../../schema.js");
|
|
360
|
-
const {
|
|
404
|
+
const { clearEverInjected, hydrate, save } =
|
|
361
405
|
await import("../activation-store.js");
|
|
362
406
|
const { injectMemoryV2Block } = await import("../injection.js");
|
|
363
407
|
const { _resetMemoryV2QdrantForTests } = await import("../qdrant.js");
|
|
@@ -377,6 +421,7 @@ function createTestDb(): DrizzleDb {
|
|
|
377
421
|
)
|
|
378
422
|
`);
|
|
379
423
|
migrateActivationState(db);
|
|
424
|
+
migrateMemoryV2InjectionEvents(db);
|
|
380
425
|
return db;
|
|
381
426
|
}
|
|
382
427
|
|
|
@@ -484,6 +529,7 @@ function resetState(): void {
|
|
|
484
529
|
state.queryResponses.dense.length = 0;
|
|
485
530
|
state.queryResponses.sparse.length = 0;
|
|
486
531
|
skillState.entries.clear();
|
|
532
|
+
cliCommandState.entries.clear();
|
|
487
533
|
telemetryState.recordCalls.length = 0;
|
|
488
534
|
telemetryState.recordShouldThrow = false;
|
|
489
535
|
pageStoreState.failingSlugs.clear();
|
|
@@ -503,6 +549,13 @@ function stageSkills(entries: SkillEntry[]): void {
|
|
|
503
549
|
}
|
|
504
550
|
}
|
|
505
551
|
|
|
552
|
+
/** Stage cli-command-store cache entries for the upcoming render. */
|
|
553
|
+
function stageCliCommands(entries: CliCommandEntryStub[]): void {
|
|
554
|
+
for (const entry of entries) {
|
|
555
|
+
cliCommandState.entries.set(entry.id, entry);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
506
559
|
let db: DrizzleDb;
|
|
507
560
|
beforeEach(() => {
|
|
508
561
|
db = createTestDb();
|
|
@@ -653,10 +706,10 @@ describe("injectMemoryV2Block", () => {
|
|
|
653
706
|
config: makeConfig(),
|
|
654
707
|
});
|
|
655
708
|
|
|
656
|
-
// Simulate compaction:
|
|
709
|
+
// Simulate compaction: clear the entire everInjected list.
|
|
657
710
|
const beforeEvict = await hydrate(db, "conv-1");
|
|
658
711
|
expect(beforeEvict).not.toBeNull();
|
|
659
|
-
const afterEvict =
|
|
712
|
+
const afterEvict = clearEverInjected(beforeEvict!);
|
|
660
713
|
expect(afterEvict.everInjected).toEqual([]);
|
|
661
714
|
await save(db, "conv-1", afterEvict);
|
|
662
715
|
|
|
@@ -703,7 +756,7 @@ describe("injectMemoryV2Block", () => {
|
|
|
703
756
|
|
|
704
757
|
expect(result.block).not.toBeNull();
|
|
705
758
|
expect(result.block).toContain(
|
|
706
|
-
"
|
|
759
|
+
'Use `file_read("memory/concepts/path/to/file.md")` to read the full pages for any of the injected memory summaries you want more information on.',
|
|
707
760
|
);
|
|
708
761
|
expect(result.block).toContain(
|
|
709
762
|
"# memory/concepts/summarized-page.md\nA short prose description",
|
|
@@ -736,11 +789,13 @@ describe("injectMemoryV2Block", () => {
|
|
|
736
789
|
});
|
|
737
790
|
|
|
738
791
|
expect(result.block).not.toBeNull();
|
|
739
|
-
//
|
|
740
|
-
const
|
|
741
|
-
result.block!.match(
|
|
792
|
+
// Header appears exactly once.
|
|
793
|
+
const headerCount = (
|
|
794
|
+
result.block!.match(
|
|
795
|
+
/Use `file_read\("memory\/concepts\/path\/to\/file\.md"\)` to read/g,
|
|
796
|
+
) ?? []
|
|
742
797
|
).length;
|
|
743
|
-
expect(
|
|
798
|
+
expect(headerCount).toBe(1);
|
|
744
799
|
// summarized-page → short form (path + summary, no body, no frontmatter).
|
|
745
800
|
expect(result.block).toContain("# memory/concepts/summarized-page.md\nA");
|
|
746
801
|
expect(result.block).not.toContain("Long-form body content");
|
|
@@ -1060,6 +1115,153 @@ describe("injectMemoryV2Block", () => {
|
|
|
1060
1115
|
expect(result.block).toBeNull();
|
|
1061
1116
|
});
|
|
1062
1117
|
|
|
1118
|
+
// ---------------------------------------------------------------------------
|
|
1119
|
+
// CLI-command synthetic entries — same unified-pool plumbing as skills.
|
|
1120
|
+
// ---------------------------------------------------------------------------
|
|
1121
|
+
|
|
1122
|
+
test("renders a retrieved cli-commands/<name> slug under CLI Commands You Can Use", async () => {
|
|
1123
|
+
stageTurn([{ slug: "cli-commands/attachment", denseScore: 0.9 }]);
|
|
1124
|
+
stageCliCommands([
|
|
1125
|
+
{
|
|
1126
|
+
id: "attachment",
|
|
1127
|
+
description: "Manage file attachments for conversations",
|
|
1128
|
+
content: 'The "assistant attachment" CLI command is available...',
|
|
1129
|
+
},
|
|
1130
|
+
]);
|
|
1131
|
+
|
|
1132
|
+
const result = await injectMemoryV2Block({
|
|
1133
|
+
database: db,
|
|
1134
|
+
conversationId: "conv-1",
|
|
1135
|
+
currentTurn: 1,
|
|
1136
|
+
userMessage: "How do I register a video?",
|
|
1137
|
+
assistantMessage: "",
|
|
1138
|
+
nowText: "Now",
|
|
1139
|
+
messageId: "msg-1",
|
|
1140
|
+
config: makeConfig(),
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
expect(result.toInject).toEqual(["cli-commands/attachment"]);
|
|
1144
|
+
expect(result.block).not.toBeNull();
|
|
1145
|
+
const headerIdx = result.block!.indexOf("### CLI Commands You Can Use");
|
|
1146
|
+
const lineIdx = result.block!.indexOf(
|
|
1147
|
+
"- `assistant attachment`: Manage file attachments for conversations",
|
|
1148
|
+
);
|
|
1149
|
+
expect(headerIdx).toBeGreaterThan(-1);
|
|
1150
|
+
expect(lineIdx).toBeGreaterThan(headerIdx);
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
test("renders concepts, skills, then cli-commands in that order in mixed blocks", async () => {
|
|
1154
|
+
stageTurn([
|
|
1155
|
+
{ slug: "alice-vscode", denseScore: 0.95 },
|
|
1156
|
+
{ slug: "skills/example-skill-a", denseScore: 0.85 },
|
|
1157
|
+
{ slug: "cli-commands/config", denseScore: 0.75 },
|
|
1158
|
+
]);
|
|
1159
|
+
stageSkills([
|
|
1160
|
+
{
|
|
1161
|
+
id: "example-skill-a",
|
|
1162
|
+
content:
|
|
1163
|
+
'The "Example Skill A" skill (example-skill-a) is available. Helps with examples.',
|
|
1164
|
+
},
|
|
1165
|
+
]);
|
|
1166
|
+
stageCliCommands([
|
|
1167
|
+
{
|
|
1168
|
+
id: "config",
|
|
1169
|
+
description: "Manage configuration",
|
|
1170
|
+
content: 'The "assistant config" CLI command is available...',
|
|
1171
|
+
},
|
|
1172
|
+
]);
|
|
1173
|
+
|
|
1174
|
+
const result = await injectMemoryV2Block({
|
|
1175
|
+
database: db,
|
|
1176
|
+
conversationId: "conv-1",
|
|
1177
|
+
currentTurn: 1,
|
|
1178
|
+
userMessage: "Help me",
|
|
1179
|
+
assistantMessage: "",
|
|
1180
|
+
nowText: "Now",
|
|
1181
|
+
messageId: "msg-1",
|
|
1182
|
+
config: makeConfig(),
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
expect(new Set(result.toInject)).toEqual(
|
|
1186
|
+
new Set([
|
|
1187
|
+
"alice-vscode",
|
|
1188
|
+
"skills/example-skill-a",
|
|
1189
|
+
"cli-commands/config",
|
|
1190
|
+
]),
|
|
1191
|
+
);
|
|
1192
|
+
const conceptIdx = result.block!.indexOf(
|
|
1193
|
+
"# memory/concepts/alice-vscode.md",
|
|
1194
|
+
);
|
|
1195
|
+
const skillsIdx = result.block!.indexOf("### Skills You Can Use");
|
|
1196
|
+
const cliIdx = result.block!.indexOf("### CLI Commands You Can Use");
|
|
1197
|
+
expect(conceptIdx).toBeGreaterThan(-1);
|
|
1198
|
+
expect(skillsIdx).toBeGreaterThan(conceptIdx);
|
|
1199
|
+
expect(cliIdx).toBeGreaterThan(skillsIdx);
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
test("cli-command slugs whose entry is missing from the cache are dropped silently", async () => {
|
|
1203
|
+
stageTurn([{ slug: "cli-commands/missing-command", denseScore: 0.9 }]);
|
|
1204
|
+
|
|
1205
|
+
const result = await injectMemoryV2Block({
|
|
1206
|
+
database: db,
|
|
1207
|
+
conversationId: "conv-1",
|
|
1208
|
+
currentTurn: 1,
|
|
1209
|
+
userMessage: "anything",
|
|
1210
|
+
assistantMessage: "",
|
|
1211
|
+
nowText: "Now",
|
|
1212
|
+
messageId: "msg-1",
|
|
1213
|
+
config: makeConfig(),
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
expect(result.toInject).toEqual([]);
|
|
1217
|
+
expect(result.block).toBeNull();
|
|
1218
|
+
|
|
1219
|
+
const persisted = await hydrate(db, "conv-1");
|
|
1220
|
+
expect(persisted!.everInjected).toEqual([]);
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
test("cli-commands participate in everInjected so they dedupe across turns", async () => {
|
|
1224
|
+
const entry = {
|
|
1225
|
+
id: "config",
|
|
1226
|
+
description: "Manage configuration",
|
|
1227
|
+
content: 'The "assistant config" CLI command is available...',
|
|
1228
|
+
};
|
|
1229
|
+
stageTurn([{ slug: "cli-commands/config", denseScore: 0.9 }]);
|
|
1230
|
+
stageCliCommands([entry]);
|
|
1231
|
+
const result1 = await injectMemoryV2Block({
|
|
1232
|
+
database: db,
|
|
1233
|
+
conversationId: "conv-1",
|
|
1234
|
+
currentTurn: 1,
|
|
1235
|
+
userMessage: "config",
|
|
1236
|
+
assistantMessage: "",
|
|
1237
|
+
nowText: "Now",
|
|
1238
|
+
messageId: "msg-1",
|
|
1239
|
+
config: makeConfig(),
|
|
1240
|
+
});
|
|
1241
|
+
expect(result1.toInject).toEqual(["cli-commands/config"]);
|
|
1242
|
+
expect(result1.block).toContain("### CLI Commands You Can Use");
|
|
1243
|
+
|
|
1244
|
+
stageTurn([{ slug: "cli-commands/config", denseScore: 0.9 }]);
|
|
1245
|
+
stageCliCommands([entry]);
|
|
1246
|
+
const result2 = await injectMemoryV2Block({
|
|
1247
|
+
database: db,
|
|
1248
|
+
conversationId: "conv-1",
|
|
1249
|
+
currentTurn: 2,
|
|
1250
|
+
userMessage: "more config",
|
|
1251
|
+
assistantMessage: "ok",
|
|
1252
|
+
nowText: "Now",
|
|
1253
|
+
messageId: "msg-2",
|
|
1254
|
+
config: makeConfig(),
|
|
1255
|
+
});
|
|
1256
|
+
expect(result2.toInject).toEqual([]);
|
|
1257
|
+
expect(result2.block).toBeNull();
|
|
1258
|
+
|
|
1259
|
+
const persisted = await hydrate(db, "conv-1");
|
|
1260
|
+
expect(persisted!.everInjected).toEqual([
|
|
1261
|
+
{ slug: "cli-commands/config", turn: 1 },
|
|
1262
|
+
]);
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1063
1265
|
test("context-load mode renders topNow even when every slug was previously injected", async () => {
|
|
1064
1266
|
// Turn 1 (per-turn): seed alice as injected.
|
|
1065
1267
|
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
@@ -1633,7 +1835,9 @@ describe("injectMemoryV2Block", () => {
|
|
|
1633
1835
|
expect(row.mode).toBe("router");
|
|
1634
1836
|
const aliceRow = row.concepts.find((c) => c.slug === "alice-vscode");
|
|
1635
1837
|
expect(aliceRow).toBeDefined();
|
|
1636
|
-
|
|
1838
|
+
// Default-stub provenance is `tier3:0` (single-batch path); see the
|
|
1839
|
+
// runRouter mock for the synthesis rule.
|
|
1840
|
+
expect(aliceRow!.source).toBe("tier3:0");
|
|
1637
1841
|
expect(aliceRow!.status).toBe("injected");
|
|
1638
1842
|
expect(aliceRow!.finalActivation).toBe(0);
|
|
1639
1843
|
expect(aliceRow!.ownActivation).toBe(0);
|
|
@@ -1815,7 +2019,7 @@ describe("injectMemoryV2Block", () => {
|
|
|
1815
2019
|
);
|
|
1816
2020
|
expect(phantom).toBeDefined();
|
|
1817
2021
|
expect(phantom!.status).toBe("page_missing");
|
|
1818
|
-
expect(phantom!.source).toBe("
|
|
2022
|
+
expect(phantom!.source).toBe("tier3:0");
|
|
1819
2023
|
});
|
|
1820
2024
|
|
|
1821
2025
|
test("flag-on: router re-picking a prior-everInjected slug does NOT re-render it; non-overlapping picks render and append to everInjected", async () => {
|
|
@@ -1920,7 +2124,7 @@ describe("injectMemoryV2Block", () => {
|
|
|
1920
2124
|
expect(bobRow).toBeDefined();
|
|
1921
2125
|
expect(aliceRow!.source).toBe("carry_over");
|
|
1922
2126
|
expect(aliceRow!.status).toBe("in_context");
|
|
1923
|
-
expect(bobRow!.source).toBe("
|
|
2127
|
+
expect(bobRow!.source).toBe("tier3:0");
|
|
1924
2128
|
expect(bobRow!.status).toBe("injected");
|
|
1925
2129
|
});
|
|
1926
2130
|
|