@vellumai/assistant 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -2
- package/docker-entrypoint.sh +0 -1
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +1492 -100
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +302 -33
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +2 -1
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
- package/src/__tests__/conversation-agent-loop.test.ts +188 -129
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- 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-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +324 -0
- package/src/__tests__/conversation-lifecycle.test.ts +53 -12
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
- 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 +2 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- 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-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -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-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +7 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- 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__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -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-shell-tool.test.ts +1 -1
- 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__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/injector-chain.test.ts +2 -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__/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 +77 -17
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +161 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +151 -0
- package/src/__tests__/openai-responses-provider.test.ts +118 -16
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -5
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- 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__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +161 -124
- package/src/__tests__/terminal-tools.test.ts +12 -2
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +89 -53
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +94 -10
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +65 -20
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +483 -0
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/program.ts +2 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +258 -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-open.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +24 -12
- package/src/config/call-site-defaults.ts +20 -0
- package/src/config/feature-flag-registry.json +115 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
- package/src/config/schemas/call-site-catalog.ts +35 -0
- package/src/config/schemas/llm.ts +14 -0
- package/src/config/schemas/memory-v2.ts +294 -1
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +60 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- 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 +155 -36
- package/src/daemon/conversation-agent-loop.ts +307 -88
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +149 -118
- 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 +145 -84
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +70 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +54 -32
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +2 -0
- package/src/daemon/handlers/conversations.ts +90 -3
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +5 -5
- package/src/daemon/host-file-proxy.ts +5 -5
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +11 -11
- package/src/daemon/lifecycle.ts +40 -23
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +14 -9
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +66 -7
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/daemon/trust-context.ts +6 -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/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +85 -0
- package/src/home/suggested-prompts.ts +168 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +191 -100
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +26 -0
- package/src/memory/db-maintenance.ts +30 -21
- 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/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +68 -15
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +11 -3
- package/src/memory/memory-retrospective-job.ts +413 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +41 -14
- 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/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/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +34 -0
- package/src/memory/migrations/registry.ts +58 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +22 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +158 -112
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +660 -4
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +42 -25
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +369 -62
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/messaging/providers/index.ts +7 -1
- 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__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +38 -50
- package/src/notifications/home-feed-side-effect.ts +60 -30
- 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/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +8 -5
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +6 -3
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +100 -20
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +69 -19
- package/src/prompts/system-prompt.ts +118 -216
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +10 -2
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +281 -9
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +159 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/gemini/client.ts +49 -6
- 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/adapter-factory.ts +18 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +91 -1
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +63 -23
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +86 -23
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/types.ts +25 -0
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- 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 +152 -56
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +43 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -6
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +50 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -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/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +147 -2
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +196 -31
- package/src/runtime/routes/conversation-routes.ts +472 -425
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- 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 +17 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- 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/acl-enforcement.ts +96 -3
- 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 +20 -4
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +427 -0
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +188 -0
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- 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/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
- package/src/tools/browser/browser-execution.ts +106 -0
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
- package/src/tools/browser/cdp-client/factory.ts +171 -4
- package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
- package/src/tools/browser/cdp-client/types.ts +101 -0
- package/src/tools/browser/pinned-tabs.ts +146 -0
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- 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 +187 -2
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -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 +216 -73
- package/src/tools/network/web-search.ts +216 -98
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +31 -98
- package/src/tools/ui-surface/definitions.ts +9 -23
- package/src/types/onboarding-context.ts +4 -0
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -7
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- 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/prompts/cache-boundary.ts +0 -8
- 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
package/src/memory/v2/router.ts
CHANGED
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
* since NOW.md only changes when the model rewrites it. We set the 1h
|
|
22
22
|
* TTL explicitly here to match the provider-side breakpoints; the
|
|
23
23
|
* default 5m would force unnecessary cache re-creation.
|
|
24
|
-
* The
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* The trailing user-message block holds `<last_turn>` content that changes
|
|
25
|
+
* every call (new user turn + new prior assistant reply), so we pass
|
|
26
|
+
* `disableTurnStartCache: true` to the provider to suppress its auto-applied
|
|
27
|
+
* 1h breakpoint there — caching it would create unused cache entries (pure
|
|
28
|
+
* cache_creation cost with no future hit).
|
|
27
29
|
*
|
|
28
30
|
* This module is pure orchestration — it does not mutate activation state,
|
|
29
31
|
* write any files, or update the conversation. PR 10 wires it into
|
|
@@ -47,7 +49,15 @@ import type {
|
|
|
47
49
|
ToolDefinition,
|
|
48
50
|
} from "../../providers/types.js";
|
|
49
51
|
import { getLogger } from "../../util/logger.js";
|
|
50
|
-
import {
|
|
52
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
53
|
+
import { computeInjectionScores } from "./injection-events.js";
|
|
54
|
+
import type { PageIndex } from "./page-index.js";
|
|
55
|
+
import {
|
|
56
|
+
getPageIndex,
|
|
57
|
+
partitionPageIndex,
|
|
58
|
+
splitTier1,
|
|
59
|
+
splitTier2,
|
|
60
|
+
} from "./page-index.js";
|
|
51
61
|
import { resolveRouterPrompt } from "./prompts/router.js";
|
|
52
62
|
import type { EverInjectedEntry } from "./types.js";
|
|
53
63
|
|
|
@@ -66,14 +76,28 @@ export type RouterFailureReason =
|
|
|
66
76
|
| "api_error"
|
|
67
77
|
| "empty_index";
|
|
68
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Tags which batch a router-selected slug came from. Tier 3 carries the
|
|
81
|
+
* batch index so the inspector can distinguish e.g. `tier3:0` from
|
|
82
|
+
* `tier3:3` — useful for debugging hash bucketing and batch-quality
|
|
83
|
+
* regressions per tier 3 bucket.
|
|
84
|
+
*/
|
|
85
|
+
export type RouterSource = "tier1" | "tier2" | `tier3:${number}`;
|
|
86
|
+
|
|
69
87
|
/**
|
|
70
88
|
* Result of a single router call. `selectedSlugs` preserves the order the
|
|
71
89
|
* model returned and is already capped at `config.memory.v2.router.max_page_ids`
|
|
72
|
-
* with out-of-range IDs dropped.
|
|
90
|
+
* with out-of-range IDs dropped. `sourceBySlug` attributes each selection
|
|
91
|
+
* to the batch it came from for inspector display.
|
|
73
92
|
*/
|
|
74
93
|
export interface RouterResult {
|
|
75
94
|
/** Selected page slugs in the order the model returned them. */
|
|
76
95
|
selectedSlugs: string[];
|
|
96
|
+
/**
|
|
97
|
+
* Per-slug provenance covering every entry in `selectedSlugs`. Empty when
|
|
98
|
+
* `failureReason !== null` or no batch returned any selections.
|
|
99
|
+
*/
|
|
100
|
+
sourceBySlug: ReadonlyMap<string, RouterSource>;
|
|
77
101
|
/** `null` on success; one of the failure reasons above otherwise. */
|
|
78
102
|
failureReason: RouterFailureReason | null;
|
|
79
103
|
}
|
|
@@ -112,98 +136,305 @@ const RouterResultSchema = z.object({
|
|
|
112
136
|
page_ids: z.array(z.number().int()),
|
|
113
137
|
});
|
|
114
138
|
|
|
115
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* Per-batch internal result. The orchestrator stamps provenance during the
|
|
141
|
+
* union so individual batches never need to know their own tier tag.
|
|
142
|
+
*/
|
|
143
|
+
interface RouterBatchResult {
|
|
144
|
+
selectedSlugs: string[];
|
|
145
|
+
failureReason: RouterFailureReason | null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Empty orchestrator result. */
|
|
116
149
|
function emptyResult(reason: RouterFailureReason | null): RouterResult {
|
|
150
|
+
return { selectedSlugs: [], sourceBySlug: new Map(), failureReason: reason };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Empty batch result — slimmer shape; orchestrator builds provenance. */
|
|
154
|
+
function emptyBatchResult(
|
|
155
|
+
reason: RouterFailureReason | null,
|
|
156
|
+
): RouterBatchResult {
|
|
117
157
|
return { selectedSlugs: [], failureReason: reason };
|
|
118
158
|
}
|
|
119
159
|
|
|
160
|
+
/**
|
|
161
|
+
* One `(assistant, user)` turn pair rendered inside `<last_turn>`. The
|
|
162
|
+
* pair represents the assistant's reply followed by the user message
|
|
163
|
+
* that came after. The most recent pair's `userMessage` is the
|
|
164
|
+
* just-arrived turn that triggered the router; older pairs are walked
|
|
165
|
+
* back from conversation history. `assistantMessage` is the empty
|
|
166
|
+
* string for the oldest pair when there was no prior assistant reply
|
|
167
|
+
* (conversation start) — `runRouterBatch` skips the `[assistant]:`
|
|
168
|
+
* line entirely in that case.
|
|
169
|
+
*/
|
|
170
|
+
export interface RouterTurnPair {
|
|
171
|
+
assistantMessage: string;
|
|
172
|
+
userMessage: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
120
175
|
interface RunRouterParams {
|
|
121
176
|
workspaceDir: string;
|
|
122
|
-
|
|
123
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Recent assistant/user turn pairs, oldest first. Must contain at
|
|
179
|
+
* least one entry. The last entry's `userMessage` is the just-arrived
|
|
180
|
+
* user turn the router is routing for; entries before it are walked
|
|
181
|
+
* back from conversation history. The number of pairs the production
|
|
182
|
+
* caller passes is controlled by `memory.v2.router.historical_pairs`.
|
|
183
|
+
*/
|
|
184
|
+
recentTurnPairs: readonly RouterTurnPair[];
|
|
124
185
|
/** Verbatim contents to inject into `<now>...</now>` on this turn. */
|
|
125
186
|
nowText: string;
|
|
126
187
|
/** Slugs already injected on prior turns (used to seed `<already_injected_ids>`). */
|
|
127
188
|
priorEverInjected: readonly EverInjectedEntry[];
|
|
128
189
|
config: AssistantConfig;
|
|
129
190
|
signal?: AbortSignal;
|
|
191
|
+
/**
|
|
192
|
+
* Database handle for reading EMA scores when `tier2_size` is set. When
|
|
193
|
+
* absent, tier 2 is silently skipped (pages flow tier 1 → tier 3). The
|
|
194
|
+
* production caller (`injectViaRouter`) always passes it; tests that
|
|
195
|
+
* only exercise tier 1 / tier 3 paths can omit it.
|
|
196
|
+
*/
|
|
197
|
+
database?: DrizzleDb;
|
|
198
|
+
/**
|
|
199
|
+
* Per-call profile override forwarded to `getConfiguredProvider`. When
|
|
200
|
+
* set, the `memoryRouter` call site resolves against this profile name
|
|
201
|
+
* instead of the workspace active profile. The simulator route uses
|
|
202
|
+
* this to compare different profiles against the same query; live
|
|
203
|
+
* router callers leave it unset.
|
|
204
|
+
*/
|
|
205
|
+
overrideProfile?: string;
|
|
206
|
+
/**
|
|
207
|
+
* Skip the post-union truncation to `max_page_ids`. Used by the
|
|
208
|
+
* simulator so the playground can show the full untruncated router
|
|
209
|
+
* output across all batches. Live callers (`injectViaRouter`) leave
|
|
210
|
+
* this unset so the bounded-injection contract holds.
|
|
211
|
+
*/
|
|
212
|
+
disableUnionCap?: boolean;
|
|
213
|
+
/**
|
|
214
|
+
* Per-call inline router system-prompt override. Takes precedence
|
|
215
|
+
* over `memory.v2.router.router_prompt_path` and the bundled body.
|
|
216
|
+
* Used by the simulator playground for ad-hoc prompt comparisons.
|
|
217
|
+
* Live callers leave this unset.
|
|
218
|
+
*/
|
|
219
|
+
routerPromptOverride?: string;
|
|
130
220
|
}
|
|
131
221
|
|
|
132
222
|
/**
|
|
133
|
-
* Run the router for one turn.
|
|
134
|
-
*
|
|
223
|
+
* Run the router for one turn.
|
|
224
|
+
*
|
|
225
|
+
* Top-level orchestration. When `config.memory.v2.router.batch_size` is
|
|
226
|
+
* `null` (default), the entire page index is sent in one call — bit-
|
|
227
|
+
* identical to the pre-batching code path so v3's KV cache is preserved.
|
|
228
|
+
* When set, `partitionPageIndex` splits the index into stable hash-bucketed
|
|
229
|
+
* batches and we fire one provider call per batch in parallel; the selected
|
|
230
|
+
* slugs are unioned across batches.
|
|
135
231
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* 3. Build system + user prompts. The system prompt is the rendered
|
|
142
|
-
* router template with the page index inlined and gets one ephemeral
|
|
143
|
-
* breakpoint at the end (the page-index block). The user message is
|
|
144
|
-
* *two* text blocks: the cached `<now>` block and the uncached
|
|
145
|
-
* already-injected/last-turn block.
|
|
146
|
-
* 4. Force `tool_choice` so the model can only emit `select_pages_to_inject`.
|
|
147
|
-
* 5. Parse the tool input via Zod. Anything off-shape collapses to
|
|
148
|
-
* `schema_mismatch`.
|
|
149
|
-
* 6. Map IDs to slugs through the page index, dropping IDs outside
|
|
150
|
-
* `[1, N]` and truncating at `max_page_ids`.
|
|
232
|
+
* Per-batch failure does not abort the turn — as long as at least one batch
|
|
233
|
+
* returns a usable selection, the union is returned with `failureReason:
|
|
234
|
+
* null`. Only when EVERY batch fails do we surface a failure; in that case
|
|
235
|
+
* the first batch's reason is returned for parity with the single-batch
|
|
236
|
+
* v3 behavior.
|
|
151
237
|
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
238
|
+
* Single batch error semantics, preserved from v3:
|
|
239
|
+
* - `empty_index` — workspace has no concept pages or skill entries.
|
|
240
|
+
* - `no_provider` — `getConfiguredProvider("memoryRouter")` returned null.
|
|
241
|
+
* - `api_error` — any uncaught throw during the provider call (incl. abort).
|
|
242
|
+
* - `tool_use_missing` — the model returned no `select_pages_to_inject` tool_use.
|
|
243
|
+
* - `schema_mismatch` — tool input failed Zod validation.
|
|
157
244
|
*/
|
|
158
245
|
export async function runRouter(
|
|
159
246
|
params: RunRouterParams,
|
|
160
247
|
): Promise<RouterResult> {
|
|
161
|
-
const {
|
|
162
|
-
workspaceDir,
|
|
163
|
-
userMessage,
|
|
164
|
-
assistantMessage,
|
|
165
|
-
nowText,
|
|
166
|
-
priorEverInjected,
|
|
167
|
-
config,
|
|
168
|
-
signal,
|
|
169
|
-
} = params;
|
|
248
|
+
const { workspaceDir, priorEverInjected, config } = params;
|
|
170
249
|
|
|
171
250
|
const pageIndex = await getPageIndex(workspaceDir);
|
|
172
251
|
if (pageIndex.entries.length === 0) {
|
|
173
252
|
return emptyResult("empty_index");
|
|
174
253
|
}
|
|
175
254
|
|
|
176
|
-
const provider = await getConfiguredProvider("memoryRouter"
|
|
255
|
+
const provider = await getConfiguredProvider("memoryRouter", {
|
|
256
|
+
...(params.overrideProfile !== undefined
|
|
257
|
+
? { overrideProfile: params.overrideProfile }
|
|
258
|
+
: {}),
|
|
259
|
+
});
|
|
177
260
|
if (!provider) {
|
|
178
261
|
log.warn("memoryRouter provider unavailable; router skipped");
|
|
179
262
|
return emptyResult("no_provider");
|
|
180
263
|
}
|
|
181
264
|
|
|
265
|
+
const batchSize = config.memory?.v2?.router?.batch_size ?? null;
|
|
266
|
+
const tier1Size = config.memory?.v2?.router?.tier1_size ?? null;
|
|
267
|
+
const tier2Size = config.memory?.v2?.router?.tier2_size ?? null;
|
|
268
|
+
|
|
269
|
+
// Carve in tier order so each later tier sees only what's left. With
|
|
270
|
+
// every tier disabled (defaults) we hit the bit-identical single-batch
|
|
271
|
+
// path that preserves v3's KV cache.
|
|
272
|
+
const { tier1, rest: afterTier1 } = splitTier1(pageIndex, tier1Size);
|
|
273
|
+
|
|
274
|
+
let tier2: PageIndex | null = null;
|
|
275
|
+
let afterTier2: PageIndex = afterTier1;
|
|
276
|
+
if (tier2Size !== null && params.database && afterTier1.entries.length > 0) {
|
|
277
|
+
const slugs = afterTier1.entries.map((e) => e.slug);
|
|
278
|
+
const scores = computeInjectionScores(params.database, slugs, Date.now());
|
|
279
|
+
const split = splitTier2(afterTier1, tier2Size, scores);
|
|
280
|
+
tier2 = split.tier2;
|
|
281
|
+
afterTier2 = split.rest;
|
|
282
|
+
} else if (tier2Size !== null && !params.database) {
|
|
283
|
+
log.warn(
|
|
284
|
+
"tier2_size set but no database passed to runRouter; skipping tier 2",
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const tier3Batches = partitionPageIndex(afterTier2, batchSize).filter(
|
|
289
|
+
(b) => b.entries.length > 0,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Tag each batch with its provenance string. Tier 3 batches carry their
|
|
293
|
+
// bucket index so the inspector can attribute selections per-bucket.
|
|
294
|
+
const taggedBatches: Array<{ source: RouterSource; index: PageIndex }> = [];
|
|
295
|
+
if (tier1) taggedBatches.push({ source: "tier1", index: tier1 });
|
|
296
|
+
if (tier2) taggedBatches.push({ source: "tier2", index: tier2 });
|
|
297
|
+
tier3Batches.forEach((index, i) => {
|
|
298
|
+
taggedBatches.push({ source: `tier3:${i}` as const, index });
|
|
299
|
+
});
|
|
300
|
+
if (taggedBatches.length === 0) {
|
|
301
|
+
return emptyResult("empty_index");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const batchResults = await Promise.all(
|
|
305
|
+
taggedBatches.map(({ index }) =>
|
|
306
|
+
runRouterBatch({
|
|
307
|
+
...params,
|
|
308
|
+
batchIndex: index,
|
|
309
|
+
priorEverInjected,
|
|
310
|
+
provider,
|
|
311
|
+
}),
|
|
312
|
+
),
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const successes = batchResults.filter((r) => r.failureReason === null);
|
|
316
|
+
if (successes.length === 0) {
|
|
317
|
+
// For the single-batch (K=null) path this preserves v3's behavior:
|
|
318
|
+
// one batch, one failure reason surfaces directly.
|
|
319
|
+
return emptyResult(batchResults[0].failureReason);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Union selected slugs preserving first-seen order across batches; batch
|
|
323
|
+
// ordering is deterministic so the union and provenance map are stable.
|
|
324
|
+
// First-seen wins if a slug somehow appears in multiple batches (shouldn't
|
|
325
|
+
// happen — tier 1/2/3 partition is disjoint — but be defensive).
|
|
326
|
+
const sourceBySlug = new Map<string, RouterSource>();
|
|
327
|
+
const selectedSlugs: string[] = [];
|
|
328
|
+
for (let i = 0; i < batchResults.length; i++) {
|
|
329
|
+
const result = batchResults[i];
|
|
330
|
+
const source = taggedBatches[i].source;
|
|
331
|
+
for (const slug of result.selectedSlugs) {
|
|
332
|
+
if (sourceBySlug.has(slug)) continue;
|
|
333
|
+
sourceBySlug.set(slug, source);
|
|
334
|
+
selectedSlugs.push(slug);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (successes.length < batchResults.length) {
|
|
338
|
+
log.warn(
|
|
339
|
+
{
|
|
340
|
+
totalBatches: batchResults.length,
|
|
341
|
+
failedBatches: batchResults.length - successes.length,
|
|
342
|
+
failureReasons: batchResults
|
|
343
|
+
.filter((r) => r.failureReason !== null)
|
|
344
|
+
.map((r) => r.failureReason),
|
|
345
|
+
},
|
|
346
|
+
"Some router batches failed; returning union of successful batches",
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Each per-batch call caps at max_page_ids, but the union across batches can
|
|
351
|
+
// exceed it (e.g. 10 batches × 10 selections each ≫ 25 cap). Apply a final
|
|
352
|
+
// truncation so RouterResult honors the contract that injection.ts trusts.
|
|
353
|
+
// Iteration order above is tier 1 → tier 2 → tier 3:0 → … so earlier-tier
|
|
354
|
+
// slugs win the truncation. The simulator passes `disableUnionCap` so the
|
|
355
|
+
// playground can show the full untruncated union for analysis.
|
|
356
|
+
if (!params.disableUnionCap) {
|
|
357
|
+
const maxPageIds = config.memory?.v2?.router?.max_page_ids ?? 25;
|
|
358
|
+
if (selectedSlugs.length > maxPageIds) {
|
|
359
|
+
log.warn(
|
|
360
|
+
{ unionSize: selectedSlugs.length, max: maxPageIds },
|
|
361
|
+
"Router union across batches exceeded max_page_ids; truncating",
|
|
362
|
+
);
|
|
363
|
+
const dropped = selectedSlugs.splice(maxPageIds);
|
|
364
|
+
for (const slug of dropped) sourceBySlug.delete(slug);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return { selectedSlugs, sourceBySlug, failureReason: null };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
interface RunRouterBatchParams extends RunRouterParams {
|
|
371
|
+
batchIndex: PageIndex;
|
|
372
|
+
provider: NonNullable<Awaited<ReturnType<typeof getConfiguredProvider>>>;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Route one batch of the page index. Uses batch-local IDs everywhere
|
|
377
|
+
* (including `<already_injected_ids>`, which is filtered to slugs present
|
|
378
|
+
* in this batch). Provider is passed in by the orchestrator so we don't
|
|
379
|
+
* re-resolve it N times for an N-batch turn.
|
|
380
|
+
*/
|
|
381
|
+
async function runRouterBatch(
|
|
382
|
+
params: RunRouterBatchParams,
|
|
383
|
+
): Promise<RouterBatchResult> {
|
|
384
|
+
const {
|
|
385
|
+
workspaceDir,
|
|
386
|
+
recentTurnPairs,
|
|
387
|
+
nowText,
|
|
388
|
+
priorEverInjected,
|
|
389
|
+
config,
|
|
390
|
+
signal,
|
|
391
|
+
batchIndex,
|
|
392
|
+
provider,
|
|
393
|
+
} = params;
|
|
394
|
+
|
|
182
395
|
const systemPrompt = resolveRouterPrompt(
|
|
183
396
|
config.memory?.v2?.router?.router_prompt_path ?? null,
|
|
184
397
|
workspaceDir,
|
|
185
398
|
{
|
|
186
399
|
assistantName: getAssistantName(),
|
|
187
400
|
userName: resolveUserName(workspaceDir),
|
|
188
|
-
pageIndexBlock:
|
|
401
|
+
pageIndexBlock: batchIndex.rendered,
|
|
189
402
|
},
|
|
403
|
+
params.routerPromptOverride ?? null,
|
|
190
404
|
);
|
|
191
405
|
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
406
|
+
// Filter prior-injected to slugs present in THIS batch and map to
|
|
407
|
+
// batch-local IDs. The model in batch B can't reference global IDs that
|
|
408
|
+
// aren't in its prompt, so listing them would just be noise.
|
|
195
409
|
const priorIds: number[] = [];
|
|
196
410
|
for (const entry of priorEverInjected) {
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
411
|
+
const local = batchIndex.bySlug.get(entry.slug);
|
|
412
|
+
if (local) priorIds.push(local.id);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Trim the pairs down to the configured `<last_turn>` content budget,
|
|
416
|
+
// newest-message-first so the just-arrived user turn keeps full claim
|
|
417
|
+
// on the cap and the oldest still-includable message is front-truncated
|
|
418
|
+
// (rather than dropping the most recent message). `null` is a no-op.
|
|
419
|
+
const cappedPairs = applyHistoricalCharBudget(
|
|
420
|
+
recentTurnPairs,
|
|
421
|
+
config.memory?.v2?.router?.historical_pairs_max_chars ?? null,
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
// Render `<last_turn>` chronologically: each pair emits the prior
|
|
425
|
+
// assistant reply followed by the user message that came after.
|
|
426
|
+
// `assistantMessage` is the empty string on the oldest pair when there
|
|
427
|
+
// was no prior assistant reply (conversation start) — skip that line
|
|
428
|
+
// so we don't emit a dangling `[assistant]:`.
|
|
429
|
+
const lastTurnLines: string[] = [];
|
|
430
|
+
for (const pair of cappedPairs) {
|
|
431
|
+
if (pair.assistantMessage.trim().length > 0) {
|
|
432
|
+
lastTurnLines.push(`[assistant]: ${pair.assistantMessage}`);
|
|
433
|
+
}
|
|
434
|
+
lastTurnLines.push(`[user]: ${pair.userMessage}`);
|
|
199
435
|
}
|
|
436
|
+
const lastTurnBlock = `<last_turn>\n${lastTurnLines.join("\n")}\n</last_turn>`;
|
|
200
437
|
|
|
201
|
-
// Cache breakpoint 2 — `<now>` is stable across most turns (NOW.md only
|
|
202
|
-
// changes when the model rewrites it), so the bulk of the user message
|
|
203
|
-
// rides the cache. We use a 1h TTL to match the system-prompt breakpoint
|
|
204
|
-
// and the provider's auto-applied breakpoints. The trailing block has no
|
|
205
|
-
// `cache_control`; the Anthropic provider auto-applies a 1h breakpoint on
|
|
206
|
-
// the last text block of a turn-starting user message, which covers it.
|
|
207
438
|
const userMsg: Message = {
|
|
208
439
|
role: "user",
|
|
209
440
|
content: [
|
|
@@ -212,7 +443,7 @@ export async function runRouter(
|
|
|
212
443
|
type: "text",
|
|
213
444
|
text:
|
|
214
445
|
`<already_injected_ids>\n${priorIds.join(", ")}\n</already_injected_ids>\n\n` +
|
|
215
|
-
|
|
446
|
+
lastTurnBlock,
|
|
216
447
|
},
|
|
217
448
|
],
|
|
218
449
|
};
|
|
@@ -230,13 +461,14 @@ export async function runRouter(
|
|
|
230
461
|
config: {
|
|
231
462
|
callSite: "memoryRouter" as const,
|
|
232
463
|
tool_choice: { type: "tool" as const, name: ROUTER_TOOL_NAME },
|
|
464
|
+
disableTurnStartCache: true,
|
|
233
465
|
},
|
|
234
466
|
...(signal ? { signal } : {}),
|
|
235
467
|
},
|
|
236
468
|
);
|
|
237
469
|
} catch (err) {
|
|
238
470
|
log.warn({ err }, "Router provider call threw; treating as api_error");
|
|
239
|
-
return
|
|
471
|
+
return emptyBatchResult("api_error");
|
|
240
472
|
}
|
|
241
473
|
|
|
242
474
|
const toolBlock = extractToolUse(response);
|
|
@@ -245,7 +477,7 @@ export async function runRouter(
|
|
|
245
477
|
{ stopReason: response.stopReason },
|
|
246
478
|
"Router model returned no select_pages_to_inject tool_use block",
|
|
247
479
|
);
|
|
248
|
-
return
|
|
480
|
+
return emptyBatchResult("tool_use_missing");
|
|
249
481
|
}
|
|
250
482
|
|
|
251
483
|
const parsed = RouterResultSchema.safeParse(toolBlock.input);
|
|
@@ -254,11 +486,10 @@ export async function runRouter(
|
|
|
254
486
|
{ error: parsed.error.message },
|
|
255
487
|
"Router tool input did not match schema",
|
|
256
488
|
);
|
|
257
|
-
return
|
|
489
|
+
return emptyBatchResult("schema_mismatch");
|
|
258
490
|
}
|
|
259
491
|
|
|
260
|
-
const N =
|
|
261
|
-
|
|
492
|
+
const N = batchIndex.entries.length;
|
|
262
493
|
const inRangeIds: number[] = [];
|
|
263
494
|
const droppedIds: number[] = [];
|
|
264
495
|
for (const id of parsed.data.page_ids) {
|
|
@@ -275,9 +506,8 @@ export async function runRouter(
|
|
|
275
506
|
);
|
|
276
507
|
}
|
|
277
508
|
|
|
278
|
-
// De-duplicate BEFORE applying the cap —
|
|
279
|
-
//
|
|
280
|
-
// dedupes to `[1]`, under-filling the cap.
|
|
509
|
+
// De-duplicate BEFORE applying the cap — `[1, 1, 2]` with max=2 must
|
|
510
|
+
// yield 2 distinct slugs, not collapse to 1 after slicing duplicates.
|
|
281
511
|
const dedupedIds = Array.from(new Set(inRangeIds));
|
|
282
512
|
|
|
283
513
|
const truncated = dedupedIds.length > maxPageIds;
|
|
@@ -291,7 +521,7 @@ export async function runRouter(
|
|
|
291
521
|
|
|
292
522
|
const selectedSlugs: string[] = [];
|
|
293
523
|
for (const id of finalIds) {
|
|
294
|
-
const entry =
|
|
524
|
+
const entry = batchIndex.byId.get(id);
|
|
295
525
|
if (!entry) continue;
|
|
296
526
|
selectedSlugs.push(entry.slug);
|
|
297
527
|
}
|
|
@@ -299,6 +529,83 @@ export async function runRouter(
|
|
|
299
529
|
return { selectedSlugs, failureReason: null };
|
|
300
530
|
}
|
|
301
531
|
|
|
532
|
+
/** Truncation marker prepended to a front-truncated historical message. */
|
|
533
|
+
const HISTORICAL_TRUNCATION_MARKER = "…";
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Apply the `<last_turn>` content character budget to a chronological
|
|
537
|
+
* pairs array. The just-arrived user message has first claim on the
|
|
538
|
+
* budget; older messages are added newest-first until exhausted. The
|
|
539
|
+
* oldest still-includable message is front-truncated with a leading
|
|
540
|
+
* `…` so it joins coherently with the next message in time. Older pairs
|
|
541
|
+
* whose content doesn't fit are dropped entirely.
|
|
542
|
+
*
|
|
543
|
+
* Counts message content only — framing characters (`[assistant]: `,
|
|
544
|
+
* `[user]: `, newlines) are not deducted from the budget. The cap is a
|
|
545
|
+
* conservative upper bound on the dialogue content surfaced to the
|
|
546
|
+
* router, not on the exact rendered block size.
|
|
547
|
+
*
|
|
548
|
+
* Exported for tests; production calls it via `runRouterBatch`.
|
|
549
|
+
*/
|
|
550
|
+
export function applyHistoricalCharBudget(
|
|
551
|
+
pairs: readonly RouterTurnPair[],
|
|
552
|
+
maxChars: number | null,
|
|
553
|
+
): RouterTurnPair[] {
|
|
554
|
+
if (maxChars === null || maxChars <= 0) return [...pairs];
|
|
555
|
+
|
|
556
|
+
type WalkedMsg = {
|
|
557
|
+
role: "user" | "assistant";
|
|
558
|
+
text: string;
|
|
559
|
+
pairIdx: number;
|
|
560
|
+
};
|
|
561
|
+
// Walk every message newest-first. Within a single pair the user
|
|
562
|
+
// message came AFTER the assistant message chronologically, so the
|
|
563
|
+
// user line gets first claim on the budget.
|
|
564
|
+
const walked: WalkedMsg[] = [];
|
|
565
|
+
for (let i = pairs.length - 1; i >= 0; i--) {
|
|
566
|
+
walked.push({ role: "user", text: pairs[i].userMessage, pairIdx: i });
|
|
567
|
+
walked.push({
|
|
568
|
+
role: "assistant",
|
|
569
|
+
text: pairs[i].assistantMessage,
|
|
570
|
+
pairIdx: i,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
let used = 0;
|
|
575
|
+
const included = new Map<number, { assistant: string; user: string }>();
|
|
576
|
+
for (const msg of walked) {
|
|
577
|
+
const remaining = maxChars - used;
|
|
578
|
+
if (remaining <= 0) break;
|
|
579
|
+
let textToInclude: string;
|
|
580
|
+
let stop = false;
|
|
581
|
+
if (msg.text.length <= remaining) {
|
|
582
|
+
textToInclude = msg.text;
|
|
583
|
+
used += msg.text.length;
|
|
584
|
+
} else {
|
|
585
|
+
// Front-truncate so the surviving suffix of an older message
|
|
586
|
+
// connects to the next message (in chronological order) without
|
|
587
|
+
// a syntactic seam. The marker counts toward the budget so the
|
|
588
|
+
// emitted text never exceeds `maxChars` cumulatively.
|
|
589
|
+
if (remaining <= HISTORICAL_TRUNCATION_MARKER.length) break;
|
|
590
|
+
const keepChars = remaining - HISTORICAL_TRUNCATION_MARKER.length;
|
|
591
|
+
textToInclude = HISTORICAL_TRUNCATION_MARKER + msg.text.slice(-keepChars);
|
|
592
|
+
used = maxChars;
|
|
593
|
+
stop = true;
|
|
594
|
+
}
|
|
595
|
+
const slot = included.get(msg.pairIdx) ?? { assistant: "", user: "" };
|
|
596
|
+
if (msg.role === "user") slot.user = textToInclude;
|
|
597
|
+
else slot.assistant = textToInclude;
|
|
598
|
+
included.set(msg.pairIdx, slot);
|
|
599
|
+
if (stop) break;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const sortedIdxs = [...included.keys()].sort((a, b) => a - b);
|
|
603
|
+
return sortedIdxs.map((idx) => {
|
|
604
|
+
const slot = included.get(idx)!;
|
|
605
|
+
return { assistantMessage: slot.assistant, userMessage: slot.user };
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
302
609
|
/**
|
|
303
610
|
* Build a text content block carrying an ephemeral `cache_control`
|
|
304
611
|
* breakpoint with a 1h TTL. The Anthropic SDK accepts the field as an extra
|