@vellumai/assistant 0.8.5 → 0.8.6
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/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
and,
|
|
3
|
+
asc,
|
|
4
|
+
desc,
|
|
5
|
+
eq,
|
|
6
|
+
gt,
|
|
7
|
+
gte,
|
|
8
|
+
inArray,
|
|
9
|
+
isNull,
|
|
10
|
+
lt,
|
|
11
|
+
lte,
|
|
12
|
+
ne,
|
|
13
|
+
or,
|
|
14
|
+
sql,
|
|
15
|
+
} from "drizzle-orm";
|
|
2
16
|
import { v4 as uuid } from "uuid";
|
|
3
17
|
|
|
18
|
+
import { CALL_SITE_SYNTHETIC_AGENT_ERROR_MESSAGE } from "../api/constants/call-sites.js";
|
|
4
19
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
5
20
|
import { AssistantError, ProviderError } from "../util/errors.js";
|
|
6
21
|
import {
|
|
@@ -105,6 +120,80 @@ export function recordRequestLog(
|
|
|
105
120
|
return id;
|
|
106
121
|
}
|
|
107
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Insert a synthetic `llm_request_logs` row for an agent-loop error
|
|
125
|
+
* message that has no LLM call backing it but should appear in the
|
|
126
|
+
* inspector rail. Today the only caller is the
|
|
127
|
+
* `budget_yield_unrecovered` persistence path
|
|
128
|
+
* (`conversation-agent-loop.ts`); the helper is named generically so
|
|
129
|
+
* the next out-of-funds / provider-error / etc. path can route through
|
|
130
|
+
* the same primitive.
|
|
131
|
+
*
|
|
132
|
+
* The caller persists the user-visible assistant message separately
|
|
133
|
+
* via the `persistence` pipeline; this helper only writes the synthetic
|
|
134
|
+
* call row. `messageId` should be the id of the just-persisted notice
|
|
135
|
+
* so `getRequestLogsByMessageId` surfaces both together.
|
|
136
|
+
*
|
|
137
|
+
* Payload semantics mirror real LLM-call rows:
|
|
138
|
+
* - `requestPayload`: the best-known LLM request body the loop was
|
|
139
|
+
* about to send when it yielded — typically the prepared messages
|
|
140
|
+
* snapshot and any input-token budget context. Stored as JSON so
|
|
141
|
+
* the Raw tab renders it consistently with real calls.
|
|
142
|
+
* - `responsePayload`: the synthetic notice text the user saw plus
|
|
143
|
+
* the exit reason. This is the "response" from the user's point of
|
|
144
|
+
* view — what came back from a call that never actually happened.
|
|
145
|
+
*
|
|
146
|
+
* Stamps `agent_loop_exit_reason` directly so the row already carries
|
|
147
|
+
* the reason at insert time — the post-loop
|
|
148
|
+
* `setAgentLoopExitReasonOnLatestLog` query then skips it (its IS NULL
|
|
149
|
+
* guard) and stamps the prior real LLM call instead, preserving the
|
|
150
|
+
* existing "latest LLM call carries the exit reason" invariant that
|
|
151
|
+
* other consumers depend on.
|
|
152
|
+
*/
|
|
153
|
+
export function recordSyntheticAgentErrorMessageLog(args: {
|
|
154
|
+
conversationId: string;
|
|
155
|
+
messageId: string;
|
|
156
|
+
exitReason: string;
|
|
157
|
+
/** User-visible notice text — goes into `response_payload`. */
|
|
158
|
+
noticeText: string;
|
|
159
|
+
/**
|
|
160
|
+
* Best-known LLM request state at the moment the loop gave up.
|
|
161
|
+
* `null` when no prepared request was available (rare — generally
|
|
162
|
+
* we know at least the conversation history we were about to send).
|
|
163
|
+
*/
|
|
164
|
+
preparedRequest: unknown | null;
|
|
165
|
+
createdAt: number;
|
|
166
|
+
}): string {
|
|
167
|
+
const db = getDb();
|
|
168
|
+
const id = uuid();
|
|
169
|
+
const requestPayload = JSON.stringify({
|
|
170
|
+
syntheticAgentErrorMessage: {
|
|
171
|
+
exitReason: args.exitReason,
|
|
172
|
+
preparedRequest: args.preparedRequest,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
const responsePayload = JSON.stringify({
|
|
176
|
+
syntheticAgentErrorMessage: {
|
|
177
|
+
exitReason: args.exitReason,
|
|
178
|
+
noticeText: args.noticeText,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
db.insert(llmRequestLogs)
|
|
182
|
+
.values({
|
|
183
|
+
id,
|
|
184
|
+
conversationId: args.conversationId,
|
|
185
|
+
messageId: args.messageId,
|
|
186
|
+
provider: null,
|
|
187
|
+
requestPayload,
|
|
188
|
+
responsePayload,
|
|
189
|
+
createdAt: args.createdAt,
|
|
190
|
+
agentLoopExitReason: args.exitReason,
|
|
191
|
+
callSite: CALL_SITE_SYNTHETIC_AGENT_ERROR_MESSAGE,
|
|
192
|
+
})
|
|
193
|
+
.run();
|
|
194
|
+
return id;
|
|
195
|
+
}
|
|
196
|
+
|
|
108
197
|
/**
|
|
109
198
|
* Stamp an `agent_loop_exit_reason` onto the most-recent unstamped
|
|
110
199
|
* `llm_request_logs` row for the given conversation. Called by the
|
|
@@ -208,7 +297,9 @@ function selectLogsByMessageIds(messageIds: string[]): LogRow[] {
|
|
|
208
297
|
* not apply turn recovery: the `conversation_id` column already includes
|
|
209
298
|
* linked, unlinked, and orphaned rows for the full conversation.
|
|
210
299
|
*/
|
|
211
|
-
export function getRequestLogsByConversationId(
|
|
300
|
+
export function getRequestLogsByConversationId(
|
|
301
|
+
conversationId: string,
|
|
302
|
+
): LogRow[] {
|
|
212
303
|
const db = getDb();
|
|
213
304
|
return db
|
|
214
305
|
.select({
|
|
@@ -310,6 +401,100 @@ function selectUnlinkedLogsInRange(
|
|
|
310
401
|
.all();
|
|
311
402
|
}
|
|
312
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Fetch every `compactionAgent` log row in the conversation whose
|
|
406
|
+
* `createdAt` falls in the **open window** `(afterCreatedAt, beforeCreatedAt)`,
|
|
407
|
+
* ordered chronologically.
|
|
408
|
+
*
|
|
409
|
+
* Drives the Inspector's Compaction tab. The caller resolves both
|
|
410
|
+
* bounds:
|
|
411
|
+
* - `beforeCreatedAt` = the selected LLM call's `createdAt` (ceiling).
|
|
412
|
+
* - `afterCreatedAt` = the previous non-`compactionAgent` call's
|
|
413
|
+
* `createdAt` (floor), or `null` when the selected call is the first
|
|
414
|
+
* real call in the conversation.
|
|
415
|
+
*
|
|
416
|
+
* Both bounds are **strict**: the selected call itself never appears in
|
|
417
|
+
* its own trail (`<` ceiling), and compactions that fed an earlier real
|
|
418
|
+
* call's context don't bleed into this call's window (`>` floor). When
|
|
419
|
+
* `afterCreatedAt` is `null` the floor is dropped entirely — every
|
|
420
|
+
* preceding compaction is in scope, which is the right behavior for the
|
|
421
|
+
* very first real call in the conversation.
|
|
422
|
+
*
|
|
423
|
+
* NULL `callSite` rows (pre-migration-264) are excluded by the explicit
|
|
424
|
+
* `callSite = 'compactionAgent'` predicate without a separate IS NOT
|
|
425
|
+
* NULL clause.
|
|
426
|
+
*/
|
|
427
|
+
export function getCompactionLogsBetween(
|
|
428
|
+
conversationId: string,
|
|
429
|
+
afterCreatedAt: number | null,
|
|
430
|
+
beforeCreatedAt: number,
|
|
431
|
+
): LogRow[] {
|
|
432
|
+
const db = getDb();
|
|
433
|
+
const predicates = [
|
|
434
|
+
eq(llmRequestLogs.conversationId, conversationId),
|
|
435
|
+
eq(llmRequestLogs.callSite, "compactionAgent"),
|
|
436
|
+
lt(llmRequestLogs.createdAt, beforeCreatedAt),
|
|
437
|
+
];
|
|
438
|
+
if (afterCreatedAt !== null) {
|
|
439
|
+
predicates.push(gt(llmRequestLogs.createdAt, afterCreatedAt));
|
|
440
|
+
}
|
|
441
|
+
return db
|
|
442
|
+
.select({
|
|
443
|
+
id: llmRequestLogs.id,
|
|
444
|
+
conversationId: llmRequestLogs.conversationId,
|
|
445
|
+
messageId: llmRequestLogs.messageId,
|
|
446
|
+
provider: llmRequestLogs.provider,
|
|
447
|
+
requestPayload: llmRequestLogs.requestPayload,
|
|
448
|
+
responsePayload: llmRequestLogs.responsePayload,
|
|
449
|
+
createdAt: llmRequestLogs.createdAt,
|
|
450
|
+
agentLoopExitReason: llmRequestLogs.agentLoopExitReason,
|
|
451
|
+
callSite: llmRequestLogs.callSite,
|
|
452
|
+
})
|
|
453
|
+
.from(llmRequestLogs)
|
|
454
|
+
.where(and(...predicates))
|
|
455
|
+
.orderBy(asc(llmRequestLogs.createdAt), asc(llmRequestLogs.id))
|
|
456
|
+
.all();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Find the `createdAt` of the most recent non-`compactionAgent` LLM
|
|
461
|
+
* call in the conversation strictly before `beforeCreatedAt`, or `null`
|
|
462
|
+
* when no such call exists (i.e. the cutoff is the first real call).
|
|
463
|
+
*
|
|
464
|
+
* Pairs with `getCompactionLogsBetween` to bound the compaction trail
|
|
465
|
+
* to the window between the prior real call and the selected call.
|
|
466
|
+
*
|
|
467
|
+
* "Non-compactionAgent" means `callSite IS NULL OR callSite !=
|
|
468
|
+
* 'compactionAgent'`. NULL rows are pre-migration-264 (no backfill) and
|
|
469
|
+
* are treated as real agent calls — they were `mainAgent` in practice.
|
|
470
|
+
* The OR-with-IS NULL is required because SQL's three-valued logic
|
|
471
|
+
* makes `callSite != 'compactionAgent'` return UNKNOWN (not TRUE) for
|
|
472
|
+
* NULL rows, which would otherwise filter them out.
|
|
473
|
+
*/
|
|
474
|
+
export function getPreviousNonCompactionCallCreatedAt(
|
|
475
|
+
conversationId: string,
|
|
476
|
+
beforeCreatedAt: number,
|
|
477
|
+
): number | null {
|
|
478
|
+
const db = getDb();
|
|
479
|
+
const row = db
|
|
480
|
+
.select({ createdAt: llmRequestLogs.createdAt })
|
|
481
|
+
.from(llmRequestLogs)
|
|
482
|
+
.where(
|
|
483
|
+
and(
|
|
484
|
+
eq(llmRequestLogs.conversationId, conversationId),
|
|
485
|
+
lt(llmRequestLogs.createdAt, beforeCreatedAt),
|
|
486
|
+
or(
|
|
487
|
+
isNull(llmRequestLogs.callSite),
|
|
488
|
+
ne(llmRequestLogs.callSite, "compactionAgent"),
|
|
489
|
+
),
|
|
490
|
+
),
|
|
491
|
+
)
|
|
492
|
+
.orderBy(desc(llmRequestLogs.createdAt), desc(llmRequestLogs.id))
|
|
493
|
+
.limit(1)
|
|
494
|
+
.get();
|
|
495
|
+
return row?.createdAt ?? null;
|
|
496
|
+
}
|
|
497
|
+
|
|
313
498
|
export function getRequestLogById(logId: string): LogRow | null {
|
|
314
499
|
const db = getDb();
|
|
315
500
|
return (
|
|
@@ -323,7 +508,7 @@ export function getRequestLogById(logId: string): LogRow | null {
|
|
|
323
508
|
responsePayload: llmRequestLogs.responsePayload,
|
|
324
509
|
createdAt: llmRequestLogs.createdAt,
|
|
325
510
|
agentLoopExitReason: llmRequestLogs.agentLoopExitReason,
|
|
326
|
-
|
|
511
|
+
callSite: llmRequestLogs.callSite,
|
|
327
512
|
})
|
|
328
513
|
.from(llmRequestLogs)
|
|
329
514
|
.where(eq(llmRequestLogs.id, logId))
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { and, desc, eq, inArray, isNull } from "drizzle-orm";
|
|
1
|
+
import { and, desc, eq, gte, inArray, isNull, lte, ne } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
4
|
import { getDb } from "./db-connection.js";
|
|
5
5
|
import { memoryV2ActivationLogs } from "./schema.js";
|
|
6
|
+
import type { ShadowDiffLogRow } from "./v3/shadow-diff.js";
|
|
6
7
|
|
|
7
8
|
export interface MemoryV2ConceptRowRecord {
|
|
8
9
|
slug: string;
|
|
@@ -94,6 +95,14 @@ export interface MemoryV2ConceptRowRecord {
|
|
|
94
95
|
| "not_injected"
|
|
95
96
|
| "page_missing"
|
|
96
97
|
| "corrupt";
|
|
98
|
+
/**
|
|
99
|
+
* v3 shadow only: the retrieval lane that surfaced this slug
|
|
100
|
+
* (`hot` | `sparse` | `dense` | `tree` | `edge`). Lets a shadow run be
|
|
101
|
+
* analyzed by provenance — which lane each v3 pick came from. Undefined on
|
|
102
|
+
* `router`/`per-turn`/etc. v2 rows; stored in the JSON concept blob, so older
|
|
103
|
+
* rows decode with `undefined`.
|
|
104
|
+
*/
|
|
105
|
+
lane?: string;
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
export interface MemoryV2ConfigSnapshot {
|
|
@@ -157,12 +166,16 @@ export function backfillMemoryV2ActivationMessageId(
|
|
|
157
166
|
messageId: string,
|
|
158
167
|
): void {
|
|
159
168
|
const db = getDb();
|
|
169
|
+
// `v3_shadow` rows are detached telemetry written outside the live turn with
|
|
170
|
+
// a null messageId; they are not tied to any specific message. Excluding them
|
|
171
|
+
// keeps their messageId null instead of stamping them with a later turn's id.
|
|
160
172
|
db.update(memoryV2ActivationLogs)
|
|
161
173
|
.set({ messageId })
|
|
162
174
|
.where(
|
|
163
175
|
and(
|
|
164
176
|
eq(memoryV2ActivationLogs.conversationId, conversationId),
|
|
165
177
|
isNull(memoryV2ActivationLogs.messageId),
|
|
178
|
+
ne(memoryV2ActivationLogs.mode, "v3_shadow"),
|
|
166
179
|
),
|
|
167
180
|
)
|
|
168
181
|
.run();
|
|
@@ -202,3 +215,84 @@ export function getMemoryV2ActivationLogByMessageIds(
|
|
|
202
215
|
config: JSON.parse(row.configJson) as MemoryV2ConfigSnapshot,
|
|
203
216
|
};
|
|
204
217
|
}
|
|
218
|
+
|
|
219
|
+
function parseConcepts(conceptsJson: string): MemoryV2ConceptRowRecord[] {
|
|
220
|
+
try {
|
|
221
|
+
const parsed = JSON.parse(conceptsJson);
|
|
222
|
+
return Array.isArray(parsed) ? (parsed as MemoryV2ConceptRowRecord[]) : [];
|
|
223
|
+
} catch {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Read the activation-log rows the v3 shadow-diff needs: every `v3_shadow` row
|
|
230
|
+
* (optionally newer than `sinceMs`), plus the `router` rows that could pair
|
|
231
|
+
* with them. The router read is bounded to the shadow rows' conversations and
|
|
232
|
+
* their time span (padded by `paddingMs`) so a shadow-diff over a few turns
|
|
233
|
+
* never scans the entire multi-GB log table. Returns `{ shadow, router }` for
|
|
234
|
+
* {@link computeShadowDiff} to pair and diff.
|
|
235
|
+
*/
|
|
236
|
+
export function readActivationLogsForShadowDiff(opts: {
|
|
237
|
+
sinceMs: number | null;
|
|
238
|
+
paddingMs: number;
|
|
239
|
+
}): { shadow: ShadowDiffLogRow[]; router: ShadowDiffLogRow[] } {
|
|
240
|
+
const db = getDb();
|
|
241
|
+
|
|
242
|
+
const shadow = db
|
|
243
|
+
.select({
|
|
244
|
+
conversationId: memoryV2ActivationLogs.conversationId,
|
|
245
|
+
createdAt: memoryV2ActivationLogs.createdAt,
|
|
246
|
+
conceptsJson: memoryV2ActivationLogs.conceptsJson,
|
|
247
|
+
})
|
|
248
|
+
.from(memoryV2ActivationLogs)
|
|
249
|
+
.where(
|
|
250
|
+
opts.sinceMs !== null
|
|
251
|
+
? and(
|
|
252
|
+
eq(memoryV2ActivationLogs.mode, "v3_shadow"),
|
|
253
|
+
gte(memoryV2ActivationLogs.createdAt, opts.sinceMs),
|
|
254
|
+
)
|
|
255
|
+
: eq(memoryV2ActivationLogs.mode, "v3_shadow"),
|
|
256
|
+
)
|
|
257
|
+
.orderBy(memoryV2ActivationLogs.createdAt)
|
|
258
|
+
.all()
|
|
259
|
+
.map((row) => ({
|
|
260
|
+
conversationId: row.conversationId,
|
|
261
|
+
createdAt: row.createdAt,
|
|
262
|
+
concepts: parseConcepts(row.conceptsJson),
|
|
263
|
+
}));
|
|
264
|
+
|
|
265
|
+
if (shadow.length === 0) return { shadow: [], router: [] };
|
|
266
|
+
|
|
267
|
+
const convIds = [...new Set(shadow.map((s) => s.conversationId))];
|
|
268
|
+
let tMin = Number.POSITIVE_INFINITY;
|
|
269
|
+
let tMax = Number.NEGATIVE_INFINITY;
|
|
270
|
+
for (const s of shadow) {
|
|
271
|
+
if (s.createdAt < tMin) tMin = s.createdAt;
|
|
272
|
+
if (s.createdAt > tMax) tMax = s.createdAt;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const router = db
|
|
276
|
+
.select({
|
|
277
|
+
conversationId: memoryV2ActivationLogs.conversationId,
|
|
278
|
+
createdAt: memoryV2ActivationLogs.createdAt,
|
|
279
|
+
conceptsJson: memoryV2ActivationLogs.conceptsJson,
|
|
280
|
+
})
|
|
281
|
+
.from(memoryV2ActivationLogs)
|
|
282
|
+
.where(
|
|
283
|
+
and(
|
|
284
|
+
eq(memoryV2ActivationLogs.mode, "router"),
|
|
285
|
+
inArray(memoryV2ActivationLogs.conversationId, convIds),
|
|
286
|
+
gte(memoryV2ActivationLogs.createdAt, tMin - opts.paddingMs),
|
|
287
|
+
lte(memoryV2ActivationLogs.createdAt, tMax + opts.paddingMs),
|
|
288
|
+
),
|
|
289
|
+
)
|
|
290
|
+
.all()
|
|
291
|
+
.map((row) => ({
|
|
292
|
+
conversationId: row.conversationId,
|
|
293
|
+
createdAt: row.createdAt,
|
|
294
|
+
concepts: parseConcepts(row.conceptsJson),
|
|
295
|
+
}));
|
|
296
|
+
|
|
297
|
+
return { shadow, router };
|
|
298
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drops the `status` column from the `provider_connections` table.
|
|
5
|
+
*
|
|
6
|
+
* Connection status ("active" | "disabled") has been removed — connections
|
|
7
|
+
* either exist or they don't. Profile status (enabled/disabled) is a separate
|
|
8
|
+
* concept and is untouched by this migration.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent: checks PRAGMA table_info before issuing the DROP so re-running
|
|
11
|
+
* on a database that already lacks the column is a no-op.
|
|
12
|
+
*
|
|
13
|
+
* SQLite 3.35.0+ (Bun bundles a recent SQLite) supports ALTER TABLE DROP COLUMN.
|
|
14
|
+
*/
|
|
15
|
+
export function migrateDropProviderConnectionStatus(database: DrizzleDb): void {
|
|
16
|
+
const raw = getSqliteFrom(database);
|
|
17
|
+
|
|
18
|
+
const columns = raw
|
|
19
|
+
.query(`PRAGMA table_info(provider_connections)`)
|
|
20
|
+
.all() as Array<{ name: string }>;
|
|
21
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
22
|
+
|
|
23
|
+
if (columnNames.has("status")) {
|
|
24
|
+
raw.exec(`ALTER TABLE provider_connections DROP COLUMN status`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds `client_message_id` (nullable TEXT) to the `messages` table and
|
|
5
|
+
* creates a partial unique index on `(conversation_id, client_message_id)`
|
|
6
|
+
* for rows where `client_message_id IS NOT NULL`.
|
|
7
|
+
*
|
|
8
|
+
* The client-generated correlation nonce was previously wire-protocol-only
|
|
9
|
+
* (echoed on SSE `user_message_echo` events for optimistic-row dedup) but
|
|
10
|
+
* never persisted. Storing it enables server-side idempotency: a duplicate
|
|
11
|
+
* INSERT with the same `(conversation_id, client_message_id)` pair is
|
|
12
|
+
* silently skipped via `ON CONFLICT DO NOTHING`.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent — re-running is a no-op once the column and index exist.
|
|
15
|
+
*/
|
|
16
|
+
export function migrateMessagesClientMessageId(database: DrizzleDb): void {
|
|
17
|
+
const raw = getSqliteFrom(database);
|
|
18
|
+
|
|
19
|
+
const columns = raw.query(`PRAGMA table_info(messages)`).all() as Array<{
|
|
20
|
+
name: string;
|
|
21
|
+
}>;
|
|
22
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
23
|
+
|
|
24
|
+
if (!columnNames.has("client_message_id")) {
|
|
25
|
+
raw.exec(`ALTER TABLE messages ADD COLUMN client_message_id TEXT`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Partial unique index: only enforced when client_message_id is non-NULL.
|
|
29
|
+
// Messages without a client_message_id (assistant messages, system-generated
|
|
30
|
+
// messages, legacy rows) are unconstrained.
|
|
31
|
+
const indexes = raw.query(`PRAGMA index_list(messages)`).all() as Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
}>;
|
|
34
|
+
const indexNames = new Set(indexes.map((i) => i.name));
|
|
35
|
+
|
|
36
|
+
if (!indexNames.has("idx_messages_conv_client_msg_id")) {
|
|
37
|
+
raw.exec(
|
|
38
|
+
`CREATE UNIQUE INDEX idx_messages_conv_client_msg_id
|
|
39
|
+
ON messages (conversation_id, client_message_id)
|
|
40
|
+
WHERE client_message_id IS NOT NULL`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -251,6 +251,8 @@ export {
|
|
|
251
251
|
migrateMemoryV3AutoEdges,
|
|
252
252
|
} from "./263-memory-v3-auto-edges.js";
|
|
253
253
|
export { migrateLlmRequestLogCallSite } from "./264-llm-request-log-call-site.js";
|
|
254
|
+
export { migrateDropProviderConnectionStatus } from "./265-drop-provider-connection-status.js";
|
|
255
|
+
export { migrateMessagesClientMessageId } from "./266-messages-client-message-id.js";
|
|
254
256
|
export {
|
|
255
257
|
MIGRATION_REGISTRY,
|
|
256
258
|
type MigrationRegistryEntry,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
1
2
|
import {
|
|
2
3
|
index,
|
|
3
4
|
integer,
|
|
4
5
|
real,
|
|
5
6
|
sqliteTable,
|
|
6
7
|
text,
|
|
8
|
+
uniqueIndex,
|
|
7
9
|
} from "drizzle-orm/sqlite-core";
|
|
8
10
|
|
|
9
11
|
export const conversations = sqliteTable(
|
|
@@ -66,8 +68,14 @@ export const messages = sqliteTable(
|
|
|
66
68
|
content: text("content").notNull(),
|
|
67
69
|
createdAt: integer("created_at").notNull(),
|
|
68
70
|
metadata: text("metadata"),
|
|
71
|
+
clientMessageId: text("client_message_id"),
|
|
69
72
|
},
|
|
70
|
-
(table) => [
|
|
73
|
+
(table) => [
|
|
74
|
+
index("idx_messages_conversation_id").on(table.conversationId),
|
|
75
|
+
uniqueIndex("idx_messages_conv_client_msg_id")
|
|
76
|
+
.on(table.conversationId, table.clientMessageId)
|
|
77
|
+
.where(sql`client_message_id IS NOT NULL`),
|
|
78
|
+
],
|
|
71
79
|
);
|
|
72
80
|
|
|
73
81
|
export const toolInvocations = sqliteTable(
|
|
@@ -15,7 +15,6 @@ export const providerConnections = sqliteTable(
|
|
|
15
15
|
name: text("name").primaryKey(),
|
|
16
16
|
provider: text("provider").notNull(),
|
|
17
17
|
auth: text("auth").notNull(),
|
|
18
|
-
status: text("status").notNull().default("active"),
|
|
19
18
|
label: text("label"),
|
|
20
19
|
baseUrl: text("base_url"),
|
|
21
20
|
models: text("models"),
|
|
@@ -202,7 +202,10 @@ afterAll(() => {
|
|
|
202
202
|
rmSync(tmpWorkspace, { recursive: true, force: true });
|
|
203
203
|
});
|
|
204
204
|
|
|
205
|
-
const { getDb
|
|
205
|
+
const { getDb } = await import("../../db-connection.js");
|
|
206
|
+
const { resetDbForTesting } = await import(
|
|
207
|
+
"../../../__tests__/db-test-helpers.js"
|
|
208
|
+
);
|
|
206
209
|
const { initializeDb } = await import("../../db-init.js");
|
|
207
210
|
const { rawExec } = await import("../../raw-query.js");
|
|
208
211
|
const { conversations, memoryJobs, messages } = await import("../../schema.js");
|
|
@@ -247,7 +250,7 @@ function makeJob(
|
|
|
247
250
|
}
|
|
248
251
|
|
|
249
252
|
beforeEach(() => {
|
|
250
|
-
|
|
253
|
+
resetDbForTesting();
|
|
251
254
|
initializeDb();
|
|
252
255
|
// The shared template-DB caching does not clear WAL state between tests,
|
|
253
256
|
// so explicitly truncate every table this suite writes to. Without this,
|
|
@@ -25,6 +25,15 @@ describe("harness/metrics recallAtK", () => {
|
|
|
25
25
|
test("empty ground truth is vacuously complete (recall 1)", () => {
|
|
26
26
|
expect(recallAtK([], new Set<string>(), 5)).toBe(1);
|
|
27
27
|
});
|
|
28
|
+
|
|
29
|
+
test("duplicate selections cannot push recall above 1.0", () => {
|
|
30
|
+
// A retriever emitting the same slug twice must not double-count it.
|
|
31
|
+
expect(recallAtK(["a", "a"], new Set(["a"]), 10)).toBe(1);
|
|
32
|
+
// Duplicates inside the top-k window still count once.
|
|
33
|
+
const gt = new Set(["a", "b"]);
|
|
34
|
+
expect(recallAtK(["a", "a", "b"], gt, 10)).toBeCloseTo(1);
|
|
35
|
+
expect(recallAtK(["a", "a", "b"], gt, 2)).toBeCloseTo(0.5);
|
|
36
|
+
});
|
|
28
37
|
});
|
|
29
38
|
|
|
30
39
|
describe("harness/metrics evalTurn", () => {
|
|
@@ -195,7 +195,7 @@ describe("harness/replay-input reconstructInput", () => {
|
|
|
195
195
|
expect(r).toBeNull();
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
-
test("priorEverInjected
|
|
198
|
+
test("priorEverInjected mirrors production everInjected retention from earlier router turns", async () => {
|
|
199
199
|
insertMessage("u1", "c1", "user", "u1", 10);
|
|
200
200
|
insertMessage("a1", "c1", "assistant", "a1", 20);
|
|
201
201
|
insertMessage("u2", "c1", "user", "u2", 30);
|
|
@@ -207,7 +207,12 @@ describe("harness/replay-input reconstructInput", () => {
|
|
|
207
207
|
[
|
|
208
208
|
makeConcept("p1", "injected"),
|
|
209
209
|
makeConcept("p2", "in_context"),
|
|
210
|
-
|
|
210
|
+
// page_missing / corrupt concept pages are retained in production's
|
|
211
|
+
// everInjected (so they aren't re-attempted every turn), so the replay
|
|
212
|
+
// must include them too.
|
|
213
|
+
makeConcept("p3", "page_missing"),
|
|
214
|
+
makeConcept("p4", "corrupt"),
|
|
215
|
+
makeConcept("p5", "not_injected"),
|
|
211
216
|
],
|
|
212
217
|
20,
|
|
213
218
|
);
|
|
@@ -219,7 +224,7 @@ describe("harness/replay-input reconstructInput", () => {
|
|
|
219
224
|
WORKSPACE,
|
|
220
225
|
);
|
|
221
226
|
const slugs = (r?.input.priorEverInjected ?? []).map((e) => e.slug).sort();
|
|
222
|
-
expect(slugs).toEqual(["p1", "p2"]);
|
|
223
|
-
expect(r?.meta.priorEverInjectedCount).toBe(
|
|
227
|
+
expect(slugs).toEqual(["p1", "p2", "p3", "p4"]);
|
|
228
|
+
expect(r?.meta.priorEverInjectedCount).toBe(4);
|
|
224
229
|
});
|
|
225
230
|
});
|
|
@@ -92,6 +92,32 @@ describe("harness/runner runComparison", () => {
|
|
|
92
92
|
expect(loop?.aggregate.meanRecallAtK[5]).toBeCloseTo(0.75);
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
+
test("threads the abort signal into each retriever's input", async () => {
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
const seenSignals: (AbortSignal | undefined)[] = [];
|
|
98
|
+
const capturingRetriever: Retriever = {
|
|
99
|
+
name: "router",
|
|
100
|
+
retrieve: async (input): Promise<RetrievalOutput> => {
|
|
101
|
+
seenSignals.push(input.signal);
|
|
102
|
+
return { selectedSlugs: [], sourceBySlug: new Map() };
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Fresh reconstructed input per turn so we exercise the per-turn assignment.
|
|
107
|
+
await runComparison({
|
|
108
|
+
retrievers: [capturingRetriever],
|
|
109
|
+
oracleTurns: [oracleTurn("c1", 1, ["a"])],
|
|
110
|
+
reconstruct: async () => ({
|
|
111
|
+
...STUB_RECONSTRUCTED,
|
|
112
|
+
input: { ...STUB_INPUT },
|
|
113
|
+
}),
|
|
114
|
+
ks: [5],
|
|
115
|
+
signal: controller.signal,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(seenSignals).toEqual([controller.signal]);
|
|
119
|
+
});
|
|
120
|
+
|
|
95
121
|
test("skips turns whose reconstruction returns null", async () => {
|
|
96
122
|
const report = await runComparison({
|
|
97
123
|
retrievers: [fixedRetriever("router", ["a"])],
|
|
@@ -99,7 +99,10 @@ afterAll(() => {
|
|
|
99
99
|
rmSync(tmpWorkspace, { recursive: true, force: true });
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
const {
|
|
102
|
+
const { getDb } = await import("../../db-connection.js");
|
|
103
|
+
const { resetDbForTesting } = await import(
|
|
104
|
+
"../../../__tests__/db-test-helpers.js"
|
|
105
|
+
);
|
|
103
106
|
const { initializeDb } = await import("../../db-init.js");
|
|
104
107
|
const { messages, conversations } = await import("../../schema.js");
|
|
105
108
|
const { memoryV2SweepJob } = await import("../sweep-job.js");
|
|
@@ -212,7 +215,7 @@ function seedMessages(
|
|
|
212
215
|
}
|
|
213
216
|
|
|
214
217
|
beforeEach(() => {
|
|
215
|
-
|
|
218
|
+
resetDbForTesting();
|
|
216
219
|
initializeDb();
|
|
217
220
|
// Fresh memory dir per test — keeps assertions on file contents independent.
|
|
218
221
|
rmSync(join(tmpWorkspace, "memory"), { recursive: true, force: true });
|
|
@@ -275,7 +278,7 @@ describe("memoryV2SweepJob — no recent messages", () => {
|
|
|
275
278
|
});
|
|
276
279
|
|
|
277
280
|
// Per-test conversation id ensures each test seeds a row that doesn't
|
|
278
|
-
// collide with the previous test's row in the (shared) test DB. `
|
|
281
|
+
// collide with the previous test's row in the (shared) test DB. `resetDbForTesting`
|
|
279
282
|
// is called in the outer beforeEach, but bun's mock module flow keeps the
|
|
280
283
|
// DB intact long enough for the SQL inserts here to clash.
|
|
281
284
|
let convCounter = 0;
|
|
@@ -36,6 +36,10 @@ export interface AggregateEval {
|
|
|
36
36
|
/**
|
|
37
37
|
* recall@k = |topK(selected) ∩ G| / |G|. An empty ground-truth set is defined
|
|
38
38
|
* as recall 1 (nothing to recall — vacuously complete).
|
|
39
|
+
*
|
|
40
|
+
* The top-k window is deduped before intersecting with the ground-truth set so
|
|
41
|
+
* a retriever that emits the same slug twice (e.g. `['a','a']`) cannot count it
|
|
42
|
+
* twice and push recall above 1.0. Recall is therefore bounded in [0, 1].
|
|
39
43
|
*/
|
|
40
44
|
export function recallAtK(
|
|
41
45
|
selected: readonly string[],
|
|
@@ -44,7 +48,7 @@ export function recallAtK(
|
|
|
44
48
|
): number {
|
|
45
49
|
if (groundTruth.size === 0) return 1;
|
|
46
50
|
let hit = 0;
|
|
47
|
-
for (const slug of selected.slice(0, k)) {
|
|
51
|
+
for (const slug of new Set(selected.slice(0, k))) {
|
|
48
52
|
if (groundTruth.has(slug)) hit++;
|
|
49
53
|
}
|
|
50
54
|
return hit / groundTruth.size;
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
* - `nowText`: read from current workspace files (`loadNowText`). NOT stored
|
|
11
11
|
* in the log, so it may differ from what the live turn saw —
|
|
12
12
|
* always-approximate; see `ReconstructionMeta.nowReconstructedFromCurrent`.
|
|
13
|
-
* - `priorEverInjected`: the union of
|
|
14
|
-
* `mode='router'` logs in the same conversation (turn < target).
|
|
13
|
+
* - `priorEverInjected`: the union of retained slugs from earlier
|
|
14
|
+
* `mode='router'` logs in the same conversation (turn < target). Retained
|
|
15
|
+
* statuses mirror production's `everInjected` (injected / in_context, plus
|
|
16
|
+
* page_missing / corrupt — see `PRIOR_STATUSES`).
|
|
15
17
|
*
|
|
16
18
|
* The anchor is the turn's assistant reply; the messages the router saw are
|
|
17
19
|
* those strictly before it, so we fetch a bounded recent window up to the
|
|
@@ -172,7 +174,21 @@ export async function reconstructInput(
|
|
|
172
174
|
};
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
|
|
177
|
+
// Production's `everInjected` retains a slug once it is rendered, EXCEPT for
|
|
178
|
+
// missing synthetic slugs (skills/CLI commands whose capability cache is empty
|
|
179
|
+
// — see `missingSyntheticSlugs` in `injection.ts`). Concept pages that turn out
|
|
180
|
+
// `page_missing` or `corrupt` at render time are still retained so they aren't
|
|
181
|
+
// re-attempted every turn (see the `page_missing ... DOES land in everInjected`
|
|
182
|
+
// case in `injection.test.ts`). The replay must mirror that retention or it
|
|
183
|
+
// builds a narrower prior-state than live routing, skewing comparisons. Missing
|
|
184
|
+
// synthetic slugs never enter the missing/corrupt buckets — they log as
|
|
185
|
+
// `injected` — so widening here introduces no new synthetic-slug discrepancy.
|
|
186
|
+
const PRIOR_STATUSES = new Set<string>([
|
|
187
|
+
"injected",
|
|
188
|
+
"in_context",
|
|
189
|
+
"page_missing",
|
|
190
|
+
"corrupt",
|
|
191
|
+
]);
|
|
176
192
|
|
|
177
193
|
/**
|
|
178
194
|
* Union of slugs injected on earlier `mode='router'` turns in this conversation
|
|
@@ -74,6 +74,12 @@ export async function runComparison(
|
|
|
74
74
|
}
|
|
75
75
|
turnsScored++;
|
|
76
76
|
|
|
77
|
+
// Thread the abort signal into the reconstructed input so retrievers that
|
|
78
|
+
// wrap LLM calls (e.g. the router retriever forwarding to `runRouter`) abort
|
|
79
|
+
// the in-flight per-turn call on caller disconnect — the loop gating below
|
|
80
|
+
// only stops scheduling new work, it can't cancel the current retrieval.
|
|
81
|
+
if (signal) reconstructed.input.signal = signal;
|
|
82
|
+
|
|
77
83
|
const byRetriever: Record<string, TurnEval> = {};
|
|
78
84
|
for (const retriever of retrievers) {
|
|
79
85
|
if (signal?.aborted) break;
|