@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/context/compactor.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
getAttachmentMetadataForMessage,
|
|
28
28
|
} from "../memory/attachments-store.js";
|
|
29
29
|
import { getMessages } from "../memory/conversation-crud.js";
|
|
30
|
+
import { recordRequestLog } from "../memory/llm-request-log-store.js";
|
|
30
31
|
import type {
|
|
31
32
|
ContentBlock,
|
|
32
33
|
ImageContent,
|
|
@@ -50,6 +51,49 @@ const log = getLogger("compactor");
|
|
|
50
51
|
*/
|
|
51
52
|
const COMPACTION_CALL_SITE: LLMCallSite = "mainAgent";
|
|
52
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Tag stamped on `llm_request_logs.call_site` for compaction-driven rows.
|
|
56
|
+
*
|
|
57
|
+
* Distinct from `COMPACTION_CALL_SITE` (above) on purpose: that constant
|
|
58
|
+
* names the **provider config resolution** site (set to `mainAgent` so we
|
|
59
|
+
* inherit the agent's profile and keep the prefix cache warm). This
|
|
60
|
+
* constant names the **observability** site — what the row IS — so
|
|
61
|
+
* inspectors can filter "show me only compaction calls". They're
|
|
62
|
+
* semantically different even though both come from the same enum.
|
|
63
|
+
*/
|
|
64
|
+
const COMPACTION_LOG_CALL_SITE: LLMCallSite = "compactionAgent";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Best-effort: persist a successful compaction LLM call into
|
|
68
|
+
* `llm_request_logs` with `call_site = "compactionAgent"`. The compactor
|
|
69
|
+
* opts out of automatic usage tracking (`usageTracking: "manual"`), so
|
|
70
|
+
* its calls otherwise never reach `recordRequestLog` via the agent-loop
|
|
71
|
+
* dispatcher. Failures are swallowed (warn-logged) so a DB hiccup never
|
|
72
|
+
* escalates compaction into a failure.
|
|
73
|
+
*/
|
|
74
|
+
function recordCompactionRequestLog(
|
|
75
|
+
conversationId: string,
|
|
76
|
+
response: ProviderResponse,
|
|
77
|
+
provider: Provider,
|
|
78
|
+
): void {
|
|
79
|
+
if (!response.rawRequest || !response.rawResponse) return;
|
|
80
|
+
try {
|
|
81
|
+
recordRequestLog(
|
|
82
|
+
conversationId,
|
|
83
|
+
JSON.stringify(response.rawRequest),
|
|
84
|
+
JSON.stringify(response.rawResponse),
|
|
85
|
+
undefined,
|
|
86
|
+
response.actualProvider ?? provider.name,
|
|
87
|
+
COMPACTION_LOG_CALL_SITE,
|
|
88
|
+
);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
log.warn(
|
|
91
|
+
{ err, conversationId },
|
|
92
|
+
"Failed to persist compaction LLM request log (non-fatal)",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
53
97
|
const RESULT_TAG_OPEN = "<compaction_result>";
|
|
54
98
|
const RESULT_TAG_CLOSE = "</compaction_result>";
|
|
55
99
|
|
|
@@ -155,6 +199,11 @@ export interface CompactionRunResult {
|
|
|
155
199
|
thresholdTokens: number;
|
|
156
200
|
compactedMessages: number;
|
|
157
201
|
compactedPersistedMessages: number;
|
|
202
|
+
/**
|
|
203
|
+
* Number of recent ("tail") messages preserved verbatim alongside the
|
|
204
|
+
* summary. Omitted on no-op / skipped results — defaults to 0 at render.
|
|
205
|
+
*/
|
|
206
|
+
preservedTailMessages?: number;
|
|
158
207
|
summaryCalls: number;
|
|
159
208
|
summaryInputTokens: number;
|
|
160
209
|
summaryOutputTokens: number;
|
|
@@ -327,7 +376,7 @@ export function renderImageManifest(entries: ManifestEntry[]): string {
|
|
|
327
376
|
* runtime emitted — typically
|
|
328
377
|
* `2026-04-02 (Thursday) 01:52:33 -05:00 (America/Chicago)`).
|
|
329
378
|
*/
|
|
330
|
-
function extractTurnContextTimestamp(message: Message): string | null {
|
|
379
|
+
export function extractTurnContextTimestamp(message: Message): string | null {
|
|
331
380
|
if (message.role !== "user") return null;
|
|
332
381
|
for (const block of message.content) {
|
|
333
382
|
if (block.type !== "text") continue;
|
|
@@ -669,6 +718,10 @@ export async function runAssistantDrivenCompaction(
|
|
|
669
718
|
};
|
|
670
719
|
}
|
|
671
720
|
|
|
721
|
+
// Persist the compaction LLM call into `llm_request_logs` with
|
|
722
|
+
// `call_site = "compactionAgent"`. Non-fatal on DB error — see helper.
|
|
723
|
+
recordCompactionRequestLog(args.conversationId, response, args.provider);
|
|
724
|
+
|
|
672
725
|
const rawText = extractTextFromResponse(response.content);
|
|
673
726
|
const parsed = parseCompactionResult(rawText);
|
|
674
727
|
if (!parsed) {
|
|
@@ -841,6 +894,7 @@ export async function runAssistantDrivenCompaction(
|
|
|
841
894
|
thresholdTokens,
|
|
842
895
|
compactedMessages: compactableMessages.length,
|
|
843
896
|
compactedPersistedMessages,
|
|
897
|
+
preservedTailMessages: args.messages.length - tailIndex,
|
|
844
898
|
summaryCalls: 1,
|
|
845
899
|
summaryInputTokens: response.usage.inputTokens,
|
|
846
900
|
summaryOutputTokens: response.usage.outputTokens,
|
|
@@ -1037,6 +1091,10 @@ export async function runEmergencyCompaction(
|
|
|
1037
1091
|
};
|
|
1038
1092
|
}
|
|
1039
1093
|
|
|
1094
|
+
// Persist the emergency compaction LLM call into `llm_request_logs` with
|
|
1095
|
+
// `call_site = "compactionAgent"`. Non-fatal on DB error — see helper.
|
|
1096
|
+
recordCompactionRequestLog(args.conversationId, response, args.provider);
|
|
1097
|
+
|
|
1040
1098
|
const rawText = extractTextFromResponse(response.content);
|
|
1041
1099
|
const parsed = parseCompactionResult(rawText);
|
|
1042
1100
|
if (!parsed) {
|
|
@@ -1090,6 +1148,7 @@ export async function runEmergencyCompaction(
|
|
|
1090
1148
|
thresholdTokens,
|
|
1091
1149
|
compactedMessages: compactedCount,
|
|
1092
1150
|
compactedPersistedMessages: Math.max(0, compactedCount - nonPersistedAway),
|
|
1151
|
+
preservedTailMessages: keptTail.length,
|
|
1093
1152
|
summaryCalls: 1,
|
|
1094
1153
|
summaryInputTokens: response.usage.inputTokens,
|
|
1095
1154
|
summaryOutputTokens: response.usage.outputTokens,
|
|
@@ -52,6 +52,24 @@ const IMAGE_MAX_PIXELS = 1_200_000;
|
|
|
52
52
|
const IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
53
53
|
const IMAGE_MAX_TOKENS = 1_600;
|
|
54
54
|
|
|
55
|
+
// Gemini prices images differently: any side ≤384px counts as a single 258-token
|
|
56
|
+
// tile; anything larger is resized so the longest side is ≤3072px and then
|
|
57
|
+
// split into 768x768 tiles at 258 tokens each. A 4000x4000 image clamps to
|
|
58
|
+
// 3072x3072 → ceil(3072/768)^2 = 16 tiles = 4,128 tokens. Without the clamp
|
|
59
|
+
// we'd over-count it as 36 tiles (~9,288 tokens) and trigger spurious
|
|
60
|
+
// compaction. The clamped 16-tile, 4,128-token figure is also the per-image
|
|
61
|
+
// ceiling we fall back to when dimensions are unparseable (e.g. HEIC/HEIF
|
|
62
|
+
// from iOS attachments) — the generic 1,600 cap can under-count Gemini
|
|
63
|
+
// images by ~2.5x.
|
|
64
|
+
// See: https://ai.google.dev/gemini-api/docs/tokens#multimodal-tokens
|
|
65
|
+
const GEMINI_IMAGE_SMALL_THRESHOLD = 384;
|
|
66
|
+
const GEMINI_IMAGE_TILE_SIZE = 768;
|
|
67
|
+
const GEMINI_IMAGE_TOKENS_PER_TILE = 258;
|
|
68
|
+
const GEMINI_IMAGE_MAX_DIMENSION = 3072;
|
|
69
|
+
const GEMINI_IMAGE_MAX_TOKENS =
|
|
70
|
+
Math.ceil(GEMINI_IMAGE_MAX_DIMENSION / GEMINI_IMAGE_TILE_SIZE) ** 2 *
|
|
71
|
+
GEMINI_IMAGE_TOKENS_PER_TILE;
|
|
72
|
+
|
|
55
73
|
// Anthropic renders each PDF page as an image (~1,568 tokens at standard
|
|
56
74
|
// resolution) plus any extracted text. Typical PDF pages are 50-150 KB.
|
|
57
75
|
// Using ~100 KB/page and ~1,600 tokens/page gives ~0.016 tokens/byte.
|
|
@@ -129,16 +147,41 @@ function estimateImageTokensByDimensions(
|
|
|
129
147
|
return Math.ceil(scaledWidth * scaledHeight * IMAGE_TOKENS_PER_PIXEL);
|
|
130
148
|
}
|
|
131
149
|
|
|
150
|
+
function estimateGeminiImageTokens(width: number, height: number): number {
|
|
151
|
+
if (
|
|
152
|
+
width <= GEMINI_IMAGE_SMALL_THRESHOLD &&
|
|
153
|
+
height <= GEMINI_IMAGE_SMALL_THRESHOLD
|
|
154
|
+
) {
|
|
155
|
+
return GEMINI_IMAGE_TOKENS_PER_TILE;
|
|
156
|
+
}
|
|
157
|
+
// Gemini resizes images so the longest side is ≤3072px before tiling.
|
|
158
|
+
const clampedWidth = Math.min(width, GEMINI_IMAGE_MAX_DIMENSION);
|
|
159
|
+
const clampedHeight = Math.min(height, GEMINI_IMAGE_MAX_DIMENSION);
|
|
160
|
+
const tilesWide = Math.ceil(clampedWidth / GEMINI_IMAGE_TILE_SIZE);
|
|
161
|
+
const tilesHigh = Math.ceil(clampedHeight / GEMINI_IMAGE_TILE_SIZE);
|
|
162
|
+
return tilesWide * tilesHigh * GEMINI_IMAGE_TOKENS_PER_TILE;
|
|
163
|
+
}
|
|
164
|
+
|
|
132
165
|
function estimateImageTokens(
|
|
133
166
|
block: Extract<ContentBlock, { type: "image" }>,
|
|
167
|
+
options?: TokenEstimatorOptions,
|
|
134
168
|
): number {
|
|
135
169
|
const dims = parseImageDimensions(block.source.data, block.source.media_type);
|
|
136
170
|
if (dims) {
|
|
171
|
+
if (options?.providerName === "gemini") {
|
|
172
|
+
return estimateGeminiImageTokens(dims.width, dims.height);
|
|
173
|
+
}
|
|
137
174
|
return estimateImageTokensByDimensions(dims.width, dims.height);
|
|
138
175
|
}
|
|
139
|
-
// Dimensions unparseable (corrupt header,
|
|
140
|
-
//
|
|
141
|
-
//
|
|
176
|
+
// Dimensions unparseable (corrupt header, or formats parseImageDimensions
|
|
177
|
+
// doesn't recognize like HEIC/HEIF coming from iOS attachments). Fall back
|
|
178
|
+
// to the per-provider per-image ceiling rather than the raw base64 length,
|
|
179
|
+
// which over-counts by 30-100x. Gemini's tile pricing tops out well above
|
|
180
|
+
// the universal 1,600-token cap, so use its max-tile budget instead to
|
|
181
|
+
// avoid under-counting large iPhone screenshots.
|
|
182
|
+
if (options?.providerName === "gemini") {
|
|
183
|
+
return GEMINI_IMAGE_MAX_TOKENS;
|
|
184
|
+
}
|
|
142
185
|
return IMAGE_MAX_TOKENS;
|
|
143
186
|
}
|
|
144
187
|
|
|
@@ -186,7 +229,7 @@ export function estimateContentBlockTokens(
|
|
|
186
229
|
return (
|
|
187
230
|
IMAGE_BLOCK_OVERHEAD_TOKENS +
|
|
188
231
|
estimateTextTokens(block.source.media_type) +
|
|
189
|
-
estimateImageTokens(block)
|
|
232
|
+
estimateImageTokens(block, options)
|
|
190
233
|
);
|
|
191
234
|
case "file":
|
|
192
235
|
return (
|
|
@@ -51,6 +51,11 @@ export interface ContextWindowResult {
|
|
|
51
51
|
thresholdTokens: number;
|
|
52
52
|
compactedMessages: number;
|
|
53
53
|
compactedPersistedMessages: number;
|
|
54
|
+
/**
|
|
55
|
+
* Number of recent ("tail") messages preserved verbatim alongside the
|
|
56
|
+
* summary. Omitted on no-op / skipped results — defaults to 0 at render.
|
|
57
|
+
*/
|
|
58
|
+
preservedTailMessages?: number;
|
|
54
59
|
summaryCalls: number;
|
|
55
60
|
summaryInputTokens: number;
|
|
56
61
|
summaryOutputTokens: number;
|
|
@@ -216,6 +221,26 @@ export class ContextWindowManager {
|
|
|
216
221
|
return getConfig().compaction;
|
|
217
222
|
}
|
|
218
223
|
|
|
224
|
+
get maxInputTokens(): number {
|
|
225
|
+
return this.config.maxInputTokens;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Estimate the prompt-token cost of `messages` using the same path as the
|
|
230
|
+
* auto-compaction pre-check. Clears the system-prompt cache so the next
|
|
231
|
+
* turn re-resolves it (the system prompt is lazy and may have changed).
|
|
232
|
+
*/
|
|
233
|
+
estimateInputTokens(messages: Message[]): number {
|
|
234
|
+
try {
|
|
235
|
+
return estimatePromptTokens(messages, this.systemPrompt, {
|
|
236
|
+
providerName: this.estimationProviderName,
|
|
237
|
+
toolTokenBudget: this.toolTokenBudget,
|
|
238
|
+
});
|
|
239
|
+
} finally {
|
|
240
|
+
this.clearSystemPromptCache();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
219
244
|
/**
|
|
220
245
|
* Cheap pre-check — estimate the current token count and compare against
|
|
221
246
|
* `compaction.autoThreshold`. Callers pass the estimate back through
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { MessageRow } from "../../memory/conversation-crud.js";
|
|
4
|
+
import {
|
|
5
|
+
findDisplayTurnEndIndex,
|
|
6
|
+
isToolResultOnlyUserMessage,
|
|
7
|
+
mergeConsecutiveAssistantMessages,
|
|
8
|
+
mergeToolResultsIntoAssistantMessages,
|
|
9
|
+
} from "../message-consolidation.js";
|
|
10
|
+
|
|
11
|
+
function makeMsg(
|
|
12
|
+
role: string,
|
|
13
|
+
content: string,
|
|
14
|
+
overrides: Partial<MessageRow> = {},
|
|
15
|
+
): MessageRow {
|
|
16
|
+
return {
|
|
17
|
+
id: `msg-${Math.random().toString(36).slice(2, 10)}`,
|
|
18
|
+
conversationId: "conv-1",
|
|
19
|
+
role,
|
|
20
|
+
content,
|
|
21
|
+
createdAt: Date.now(),
|
|
22
|
+
displayOrder: 0,
|
|
23
|
+
seen: 1,
|
|
24
|
+
metadata: null,
|
|
25
|
+
...overrides,
|
|
26
|
+
} as MessageRow;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("isToolResultOnlyUserMessage", () => {
|
|
30
|
+
test("returns false for assistant rows", () => {
|
|
31
|
+
expect(isToolResultOnlyUserMessage(makeMsg("assistant", "hello"))).toBe(
|
|
32
|
+
false,
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("returns false for plain user text (non-JSON content)", () => {
|
|
37
|
+
expect(isToolResultOnlyUserMessage(makeMsg("user", "hello world"))).toBe(
|
|
38
|
+
false,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("returns false when JSON content is not an array", () => {
|
|
43
|
+
expect(
|
|
44
|
+
isToolResultOnlyUserMessage(
|
|
45
|
+
makeMsg("user", JSON.stringify({ type: "tool_result" })),
|
|
46
|
+
),
|
|
47
|
+
).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("returns true for a single tool_result block", () => {
|
|
51
|
+
expect(
|
|
52
|
+
isToolResultOnlyUserMessage(
|
|
53
|
+
makeMsg(
|
|
54
|
+
"user",
|
|
55
|
+
JSON.stringify([
|
|
56
|
+
{ type: "tool_result", tool_use_id: "abc", content: "ok" },
|
|
57
|
+
]),
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("returns true for a web_search_tool_result block", () => {
|
|
64
|
+
expect(
|
|
65
|
+
isToolResultOnlyUserMessage(
|
|
66
|
+
makeMsg(
|
|
67
|
+
"user",
|
|
68
|
+
JSON.stringify([
|
|
69
|
+
{ type: "web_search_tool_result", tool_use_id: "abc" },
|
|
70
|
+
]),
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("returns true when tool_result is wrapped with system_notice blocks", () => {
|
|
77
|
+
expect(
|
|
78
|
+
isToolResultOnlyUserMessage(
|
|
79
|
+
makeMsg(
|
|
80
|
+
"user",
|
|
81
|
+
JSON.stringify([
|
|
82
|
+
{ type: "tool_result", tool_use_id: "abc" },
|
|
83
|
+
{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: "<system_notice>info</system_notice>",
|
|
86
|
+
},
|
|
87
|
+
]),
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("returns false when mixed with real user text", () => {
|
|
94
|
+
expect(
|
|
95
|
+
isToolResultOnlyUserMessage(
|
|
96
|
+
makeMsg(
|
|
97
|
+
"user",
|
|
98
|
+
JSON.stringify([
|
|
99
|
+
{ type: "tool_result", tool_use_id: "abc" },
|
|
100
|
+
{ type: "text", text: "thanks!" },
|
|
101
|
+
]),
|
|
102
|
+
),
|
|
103
|
+
),
|
|
104
|
+
).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("returns false when there are no tool_result blocks (system_notice alone)", () => {
|
|
108
|
+
expect(
|
|
109
|
+
isToolResultOnlyUserMessage(
|
|
110
|
+
makeMsg(
|
|
111
|
+
"user",
|
|
112
|
+
JSON.stringify([
|
|
113
|
+
{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: "<system_notice>info</system_notice>",
|
|
116
|
+
},
|
|
117
|
+
]),
|
|
118
|
+
),
|
|
119
|
+
),
|
|
120
|
+
).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("returns false for malformed JSON content", () => {
|
|
124
|
+
expect(
|
|
125
|
+
isToolResultOnlyUserMessage(makeMsg("user", "{not json")),
|
|
126
|
+
).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("returns false when array contains a non-object block", () => {
|
|
130
|
+
expect(
|
|
131
|
+
isToolResultOnlyUserMessage(
|
|
132
|
+
makeMsg("user", JSON.stringify(["bare string"])),
|
|
133
|
+
),
|
|
134
|
+
).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("findDisplayTurnEndIndex", () => {
|
|
139
|
+
test("returns startIdx unchanged for negative index", () => {
|
|
140
|
+
const messages = [makeMsg("user", "hi")];
|
|
141
|
+
expect(findDisplayTurnEndIndex(messages, -1)).toBe(-1);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("returns startIdx unchanged for out-of-range index", () => {
|
|
145
|
+
const messages = [makeMsg("user", "hi")];
|
|
146
|
+
expect(findDisplayTurnEndIndex(messages, 5)).toBe(5);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("returns startIdx unchanged for non-assistant rows", () => {
|
|
150
|
+
const messages = [
|
|
151
|
+
makeMsg("user", "hi"),
|
|
152
|
+
makeMsg("assistant", "back"),
|
|
153
|
+
];
|
|
154
|
+
expect(findDisplayTurnEndIndex(messages, 0)).toBe(0);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("returns startIdx for a lone assistant row at end of array", () => {
|
|
158
|
+
const messages = [
|
|
159
|
+
makeMsg("user", "hi"),
|
|
160
|
+
makeMsg("assistant", "back"),
|
|
161
|
+
];
|
|
162
|
+
expect(findDisplayTurnEndIndex(messages, 1)).toBe(1);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("advances past consecutive assistant rows", () => {
|
|
166
|
+
const messages = [
|
|
167
|
+
makeMsg("user", "hi"),
|
|
168
|
+
makeMsg("assistant", "step 1"),
|
|
169
|
+
makeMsg("assistant", "step 2"),
|
|
170
|
+
makeMsg("assistant", "step 3"),
|
|
171
|
+
makeMsg("user", "next"),
|
|
172
|
+
];
|
|
173
|
+
expect(findDisplayTurnEndIndex(messages, 1)).toBe(3);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("advances across tool-result-only user rows between assistants", () => {
|
|
177
|
+
const messages = [
|
|
178
|
+
makeMsg("user", "lookup data"),
|
|
179
|
+
makeMsg("assistant", "calling tool"),
|
|
180
|
+
makeMsg(
|
|
181
|
+
"user",
|
|
182
|
+
JSON.stringify([
|
|
183
|
+
{ type: "tool_result", tool_use_id: "t1", content: "ok" },
|
|
184
|
+
]),
|
|
185
|
+
),
|
|
186
|
+
makeMsg("assistant", "here are the results"),
|
|
187
|
+
makeMsg("user", "thanks"),
|
|
188
|
+
];
|
|
189
|
+
expect(findDisplayTurnEndIndex(messages, 1)).toBe(3);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("stops at a real-text user row even after tool-result-only rows", () => {
|
|
193
|
+
const messages = [
|
|
194
|
+
makeMsg("user", "first"),
|
|
195
|
+
makeMsg("assistant", "tool call"),
|
|
196
|
+
makeMsg(
|
|
197
|
+
"user",
|
|
198
|
+
JSON.stringify([
|
|
199
|
+
{ type: "tool_result", tool_use_id: "t1" },
|
|
200
|
+
]),
|
|
201
|
+
),
|
|
202
|
+
makeMsg("assistant", "intermediate"),
|
|
203
|
+
makeMsg("user", "real user follow-up"),
|
|
204
|
+
makeMsg("assistant", "should not be included"),
|
|
205
|
+
];
|
|
206
|
+
expect(findDisplayTurnEndIndex(messages, 1)).toBe(3);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("handles assistant followed by tool-result-only user at end of array", () => {
|
|
210
|
+
// No tail assistant follows — the suppressed user row still belongs to
|
|
211
|
+
// the cluster (its tool_result content gets folded into the preceding
|
|
212
|
+
// assistant by the read-path collapse).
|
|
213
|
+
const messages = [
|
|
214
|
+
makeMsg("user", "hi"),
|
|
215
|
+
makeMsg("assistant", "tool call"),
|
|
216
|
+
makeMsg(
|
|
217
|
+
"user",
|
|
218
|
+
JSON.stringify([
|
|
219
|
+
{ type: "tool_result", tool_use_id: "t1" },
|
|
220
|
+
]),
|
|
221
|
+
),
|
|
222
|
+
];
|
|
223
|
+
expect(findDisplayTurnEndIndex(messages, 1)).toBe(2);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("handles a long mixed cluster", () => {
|
|
227
|
+
const messages = [
|
|
228
|
+
makeMsg("user", "go"),
|
|
229
|
+
makeMsg("assistant", "A"),
|
|
230
|
+
makeMsg("assistant", "B"),
|
|
231
|
+
makeMsg(
|
|
232
|
+
"user",
|
|
233
|
+
JSON.stringify([{ type: "tool_result", tool_use_id: "t1" }]),
|
|
234
|
+
),
|
|
235
|
+
makeMsg("assistant", "C"),
|
|
236
|
+
makeMsg(
|
|
237
|
+
"user",
|
|
238
|
+
JSON.stringify([
|
|
239
|
+
{ type: "tool_result", tool_use_id: "t2" },
|
|
240
|
+
{ type: "text", text: "<system_notice>x</system_notice>" },
|
|
241
|
+
]),
|
|
242
|
+
),
|
|
243
|
+
makeMsg("assistant", "D"),
|
|
244
|
+
makeMsg("user", "done"),
|
|
245
|
+
];
|
|
246
|
+
expect(findDisplayTurnEndIndex(messages, 1)).toBe(6);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe("mergeToolResultsIntoAssistantMessages", () => {
|
|
251
|
+
test("suppresses tool-result-only user rows and lifts blocks onto preceding assistant", () => {
|
|
252
|
+
const messages = [
|
|
253
|
+
makeMsg(
|
|
254
|
+
"assistant",
|
|
255
|
+
JSON.stringify([{ type: "tool_use", id: "t1", name: "x", input: {} }]),
|
|
256
|
+
),
|
|
257
|
+
makeMsg(
|
|
258
|
+
"user",
|
|
259
|
+
JSON.stringify([
|
|
260
|
+
{ type: "tool_result", tool_use_id: "t1", content: "ok" },
|
|
261
|
+
]),
|
|
262
|
+
),
|
|
263
|
+
];
|
|
264
|
+
const merged = mergeToolResultsIntoAssistantMessages(messages);
|
|
265
|
+
expect(merged).toHaveLength(1);
|
|
266
|
+
expect(merged[0].role).toBe("assistant");
|
|
267
|
+
const blocks = JSON.parse(merged[0].content) as Array<{ type: string }>;
|
|
268
|
+
expect(blocks.map((b) => b.type)).toEqual(["tool_use", "tool_result"]);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("keeps mixed user messages with their non-tool-result content", () => {
|
|
272
|
+
const messages = [
|
|
273
|
+
makeMsg(
|
|
274
|
+
"assistant",
|
|
275
|
+
JSON.stringify([{ type: "tool_use", id: "t1", name: "x", input: {} }]),
|
|
276
|
+
),
|
|
277
|
+
makeMsg(
|
|
278
|
+
"user",
|
|
279
|
+
JSON.stringify([
|
|
280
|
+
{ type: "tool_result", tool_use_id: "t1", content: "ok" },
|
|
281
|
+
{ type: "text", text: "thanks!" },
|
|
282
|
+
]),
|
|
283
|
+
),
|
|
284
|
+
];
|
|
285
|
+
const merged = mergeToolResultsIntoAssistantMessages(messages);
|
|
286
|
+
expect(merged).toHaveLength(2);
|
|
287
|
+
expect(merged[1].role).toBe("user");
|
|
288
|
+
const userBlocks = JSON.parse(merged[1].content) as Array<{ type: string }>;
|
|
289
|
+
expect(userBlocks.map((b) => b.type)).toEqual(["text"]);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("passes plain user text through unchanged", () => {
|
|
293
|
+
const messages = [
|
|
294
|
+
makeMsg("user", "hi"),
|
|
295
|
+
makeMsg("assistant", "hello"),
|
|
296
|
+
];
|
|
297
|
+
const merged = mergeToolResultsIntoAssistantMessages(messages);
|
|
298
|
+
expect(merged).toHaveLength(2);
|
|
299
|
+
expect(merged[0].content).toBe("hi");
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe("mergeConsecutiveAssistantMessages", () => {
|
|
304
|
+
test("collapses adjacent assistant rows onto the first row", () => {
|
|
305
|
+
const a = makeMsg(
|
|
306
|
+
"assistant",
|
|
307
|
+
JSON.stringify([{ type: "text", text: "part 1" }]),
|
|
308
|
+
{ id: "anchor" },
|
|
309
|
+
);
|
|
310
|
+
const b = makeMsg(
|
|
311
|
+
"assistant",
|
|
312
|
+
JSON.stringify([{ type: "text", text: "part 2" }]),
|
|
313
|
+
{ id: "tail" },
|
|
314
|
+
);
|
|
315
|
+
const { messages, mergedIdMap } = mergeConsecutiveAssistantMessages([
|
|
316
|
+
makeMsg("user", "hi"),
|
|
317
|
+
a,
|
|
318
|
+
b,
|
|
319
|
+
]);
|
|
320
|
+
expect(messages).toHaveLength(2);
|
|
321
|
+
expect(messages[1].id).toBe("anchor");
|
|
322
|
+
const blocks = JSON.parse(messages[1].content) as Array<{ text: string }>;
|
|
323
|
+
expect(blocks.map((blk) => blk.text)).toEqual(["part 1", "part 2"]);
|
|
324
|
+
expect(mergedIdMap.get("anchor")).toEqual(["tail"]);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("leaves a single assistant row unchanged", () => {
|
|
328
|
+
const messages = [
|
|
329
|
+
makeMsg("user", "hi"),
|
|
330
|
+
makeMsg(
|
|
331
|
+
"assistant",
|
|
332
|
+
JSON.stringify([{ type: "text", text: "hello" }]),
|
|
333
|
+
),
|
|
334
|
+
];
|
|
335
|
+
const { messages: result, mergedIdMap } =
|
|
336
|
+
mergeConsecutiveAssistantMessages(messages);
|
|
337
|
+
expect(result).toHaveLength(2);
|
|
338
|
+
expect(mergedIdMap.size).toBe(0);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("does not merge assistant rows separated by a real user row", () => {
|
|
342
|
+
const messages = [
|
|
343
|
+
makeMsg("assistant", JSON.stringify([{ type: "text", text: "A" }])),
|
|
344
|
+
makeMsg("user", "interrupt"),
|
|
345
|
+
makeMsg("assistant", JSON.stringify([{ type: "text", text: "B" }])),
|
|
346
|
+
];
|
|
347
|
+
const { messages: result } = mergeConsecutiveAssistantMessages(messages);
|
|
348
|
+
expect(result).toHaveLength(3);
|
|
349
|
+
});
|
|
350
|
+
});
|