@vellumai/assistant 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +33 -1
- package/ARCHITECTURE.md +3 -3
- package/bunfig.toml +6 -1
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/knip.json +2 -1
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +2748 -216
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/anthropic-provider.test.ts +34 -37
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +12 -28
- package/src/__tests__/audit-log-rotation.test.ts +72 -18
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +8 -11
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +5 -5
- package/src/__tests__/call-controller.test.ts +3 -3
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +6 -5
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +18 -27
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +215 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -16
- package/src/__tests__/computer-use-tools.test.ts +14 -18
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -1
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +231 -2
- package/src/__tests__/conversation-agent-loop.test.ts +581 -54
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +31 -24
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- 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 +61 -0
- package/src/__tests__/conversation-fork-crud.test.ts +239 -15
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +53 -11
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +14 -13
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +112 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +68 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +40 -147
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +218 -35
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -1
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +19 -19
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +6 -5
- package/src/__tests__/credential-vault-unit.test.ts +21 -21
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +236 -0
- package/src/__tests__/gemini-provider.test.ts +104 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +10 -7
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +5 -0
- package/src/__tests__/heartbeat-service.test.ts +5 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +6 -5
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- 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 +408 -9
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +16 -2
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +4 -6
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +4 -3
- package/src/__tests__/openai-provider.test.ts +54 -9
- package/src/__tests__/openai-responses-provider.test.ts +176 -14
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -2
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +11 -13
- package/src/__tests__/plugin-tool-contribution.test.ts +50 -40
- 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 +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +84 -84
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +16 -18
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +48 -37
- package/src/__tests__/skill-projection.benchmark.test.ts +7 -13
- package/src/__tests__/skill-tool-factory.test.ts +97 -96
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +6 -5
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +183 -131
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- 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 +35 -12
- package/src/__tests__/tool-executor.test.ts +64 -72
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -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 +3 -2
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +3 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-git-service.test.ts +6 -5
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +6 -7
- package/src/agent/loop.ts +88 -0
- package/src/api/README.md +127 -0
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/api/events/relationship-state-updated.ts +25 -0
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +129 -0
- package/src/api/package.json +10 -0
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +868 -0
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -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 +1 -0
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/domain.ts +91 -41
- 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 +176 -1
- package/src/cli/commands/memory-v3-render.ts +491 -0
- package/src/cli/commands/memory-v3.ts +567 -0
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +4 -0
- package/src/config/assistant-feature-flags.ts +39 -46
- package/src/config/bundled-skills/document-editor/SKILL.md +16 -3
- package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.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/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 +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +8 -7
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +228 -1
- package/src/config/schemas/call-site-catalog.ts +21 -7
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +102 -2
- package/src/config/schemas/memory-v2.ts +272 -0
- package/src/config/schemas/memory.ts +2 -1
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/compactor.ts +52 -0
- package/src/context/token-estimator.ts +10 -5
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +2 -3
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +390 -80
- package/src/daemon/conversation-agent-loop.ts +244 -90
- package/src/daemon/conversation-error.ts +64 -6
- package/src/daemon/conversation-lifecycle.ts +27 -22
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +38 -17
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +77 -32
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +40 -75
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +39 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +11 -3
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +4 -4
- package/src/daemon/host-file-proxy.ts +4 -4
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +10 -10
- package/src/daemon/lifecycle.ts +29 -26
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +11 -24
- package/src/daemon/message-types/conversations.ts +22 -27
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -14
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +25 -48
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +62 -0
- package/src/daemon/tool-setup-types.ts +7 -0
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +30 -1
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/home-greeting.ts +0 -9
- package/src/home/suggested-prompts.ts +27 -154
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +35 -40
- 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 +242 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +423 -182
- 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-connection.ts +29 -19
- package/src/memory/db-init.ts +14 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/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/graph/retriever.test.ts +3 -3
- 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/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +66 -22
- package/src/memory/llm-request-log-source-clickhouse.ts +122 -2
- package/src/memory/llm-request-log-source-local.ts +31 -0
- package/src/memory/llm-request-log-source.ts +40 -2
- package/src/memory/llm-request-log-store.ts +228 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-enqueue.ts +8 -1
- package/src/memory/memory-retrospective-job.ts +5 -0
- package/src/memory/memory-v2-activation-log-store.ts +110 -7
- 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/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +19 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/schema/conversations.ts +10 -2
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/schema/infrastructure.ts +21 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- 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 +83 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +230 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +135 -0
- package/src/memory/v2/__tests__/injection.test.ts +127 -98
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +171 -3
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +128 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +240 -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 +112 -0
- package/src/memory/v2/harness/trace.ts +64 -0
- package/src/memory/v2/injection.ts +21 -15
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +171 -18
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +466 -0
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +706 -0
- package/src/memory/v3/__tests__/filter.test.ts +560 -0
- package/src/memory/v3/__tests__/gate.test.ts +637 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +291 -0
- package/src/memory/v3/__tests__/loop.test.ts +775 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +489 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +398 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +508 -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 +784 -0
- package/src/memory/v3/__tests__/validate.test.ts +277 -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/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +286 -0
- package/src/memory/v3/filter.ts +286 -0
- package/src/memory/v3/gate.ts +349 -0
- package/src/memory/v3/index-composition.ts +126 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +430 -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 +431 -0
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +347 -0
- package/src/memory/v3/traversal.ts +211 -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 +356 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +323 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +40 -50
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/permissions/prompter.ts +3 -3
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +2 -2
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/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 +20 -19
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +68 -29
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +39 -7
- package/src/prompts/system-prompt.ts +84 -221
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +9 -13
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/prompts/templates/system-sections.ts +230 -8
- package/src/providers/__tests__/connection-model-compat.test.ts +233 -0
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +32 -66
- package/src/providers/call-site-routing.ts +42 -6
- package/src/providers/connection-model-compat.ts +61 -0
- package/src/providers/connection-resolution.ts +47 -14
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +70 -6
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +78 -1
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +116 -15
- package/src/providers/openai/codex-models.ts +20 -0
- package/src/providers/openai/responses-provider.ts +87 -30
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +20 -5
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +50 -7
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/types.ts +9 -0
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/agent-wake.ts +1 -0
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +46 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +12 -4
- package/src/runtime/pending-interactions.ts +0 -1
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +204 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +76 -9
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +356 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +4 -10
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +159 -4
- package/src/runtime/routes/conversation-management-routes.ts +108 -26
- package/src/runtime/routes/conversation-query-routes.ts +200 -44
- package/src/runtime/routes/conversation-routes.ts +409 -521
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/documents-routes.ts +10 -1
- 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 +35 -8
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/host-browser-routes.ts +10 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +10 -0
- 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 +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +215 -5
- package/src/runtime/routes/memory-v3-routes.ts +474 -0
- package/src/runtime/routes/migration-routes.ts +32 -28
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- 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 +5 -4
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/runtime/verification-outbound-actions.ts +73 -1
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/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 +36 -28
- 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__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
- package/src/tools/browser/browser-execution.ts +16 -3
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
- package/src/tools/browser/cdp-client/factory.ts +100 -17
- package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
- package/src/tools/browser/cdp-client/types.ts +65 -0
- package/src/tools/browser/pinned-tabs.ts +96 -40
- package/src/tools/computer-use/definitions.ts +282 -336
- 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-tool.ts +189 -7
- package/src/tools/execution-target.ts +18 -23
- package/src/tools/executor.ts +24 -56
- 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.test.ts +1 -0
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.test.ts +1 -0
- 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 -10
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -9
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-fetch.ts +3 -9
- package/src/tools/network/web-search.ts +224 -76
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +150 -123
- package/src/tools/schedule/create.ts +1 -1
- 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 +18 -44
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +32 -101
- package/src/tools/ui-surface/definitions.ts +104 -108
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/browser.ts +7 -2
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -2
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +6 -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/daemon/query-complexity-router.ts +0 -75
- package/src/prompts/cache-boundary.ts +0 -8
|
@@ -159,8 +159,8 @@ mock.module("../daemon/disk-pressure-policy.js", () => ({
|
|
|
159
159
|
const updateMessageMetadataMock = mock(
|
|
160
160
|
(_id: string, _updates: Record<string, unknown>) => {},
|
|
161
161
|
);
|
|
162
|
-
const
|
|
163
|
-
(_conversationId: string) => {},
|
|
162
|
+
const setConversationHistoryStrippedAtMock = mock(
|
|
163
|
+
(_conversationId: string, _historyStrippedAt: number | null) => {},
|
|
164
164
|
);
|
|
165
165
|
const updateConversationSlackContextWatermarkMock = mock(
|
|
166
166
|
(_conversationId: string, _watermarkTs: string, _compactedAt?: number) => {},
|
|
@@ -176,12 +176,17 @@ let mockConversationRow: Record<string, unknown> = {
|
|
|
176
176
|
title: null,
|
|
177
177
|
};
|
|
178
178
|
let mockMessageById: Record<string, unknown> | null = null;
|
|
179
|
+
const deleteMessageByIdMock = mock(() => ({
|
|
180
|
+
segmentIds: [],
|
|
181
|
+
deletedSummaryIds: [],
|
|
182
|
+
}));
|
|
183
|
+
const reserveMessageMock = mock(async () => ({ id: "msg-reserve" }));
|
|
184
|
+
const updateMessageContentMock = mock(() => {});
|
|
179
185
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
180
186
|
setConversationOriginChannelIfUnset: () => {},
|
|
181
187
|
updateConversationUsage: () => {},
|
|
182
188
|
updateMessageMetadata: updateMessageMetadataMock,
|
|
183
|
-
|
|
184
|
-
clearStrippedInjectionMetadataForConversationMock,
|
|
189
|
+
setConversationHistoryStrippedAt: setConversationHistoryStrippedAtMock,
|
|
185
190
|
getMessages: () => [],
|
|
186
191
|
getConversation: () => mockConversationRow,
|
|
187
192
|
provenanceFromTrustContext: () => ({
|
|
@@ -190,7 +195,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
190
195
|
}),
|
|
191
196
|
getConversationOriginInterface: () => null,
|
|
192
197
|
addMessage: () => ({ id: "mock-msg-id" }),
|
|
193
|
-
deleteMessageById:
|
|
198
|
+
deleteMessageById: deleteMessageByIdMock,
|
|
194
199
|
updateConversationContextWindow: () => {},
|
|
195
200
|
updateConversationSlackContextWatermark:
|
|
196
201
|
updateConversationSlackContextWatermarkMock,
|
|
@@ -198,6 +203,36 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
198
203
|
getConversationOriginChannel: () => null,
|
|
199
204
|
getMessageById: () => mockMessageById,
|
|
200
205
|
getLastUserTimestampBefore: () => 0,
|
|
206
|
+
reserveMessage: reserveMessageMock,
|
|
207
|
+
updateMessageContent: updateMessageContentMock,
|
|
208
|
+
// The real schema is a Zod object; tests don't exercise validation,
|
|
209
|
+
// so a passthrough is sufficient — the production code at
|
|
210
|
+
// `handleMessageComplete` only branches on `success` and reads two
|
|
211
|
+
// fields off `data`. `safeParse` of an empty object satisfies the
|
|
212
|
+
// schema (every field is optional).
|
|
213
|
+
messageMetadataSchema: {
|
|
214
|
+
safeParse: (input: unknown) => ({ success: true, data: input ?? {} }),
|
|
215
|
+
},
|
|
216
|
+
}));
|
|
217
|
+
|
|
218
|
+
// The B3 indexing-restoration path imports `indexMessageNow` from
|
|
219
|
+
// `../memory/indexer.js` and `projectAssistantMessage` from
|
|
220
|
+
// `../memory/conversation-attention-store.js`; without these stubs the
|
|
221
|
+
// real modules would try to open a SQLite DB and read a real config.
|
|
222
|
+
const indexMessageNowMock = mock(async () => ({
|
|
223
|
+
indexedSegments: 0,
|
|
224
|
+
enqueuedJobs: 0,
|
|
225
|
+
}));
|
|
226
|
+
const projectAssistantMessageMock = mock(() => false);
|
|
227
|
+
const publishSyncInvalidationMock = mock(async () => {});
|
|
228
|
+
mock.module("../memory/indexer.js", () => ({
|
|
229
|
+
indexMessageNow: indexMessageNowMock,
|
|
230
|
+
}));
|
|
231
|
+
mock.module("../memory/conversation-attention-store.js", () => ({
|
|
232
|
+
projectAssistantMessage: projectAssistantMessageMock,
|
|
233
|
+
}));
|
|
234
|
+
mock.module("../runtime/sync/sync-publisher.js", () => ({
|
|
235
|
+
publishSyncInvalidation: publishSyncInvalidationMock,
|
|
201
236
|
}));
|
|
202
237
|
|
|
203
238
|
afterAll(() => {
|
|
@@ -684,10 +719,8 @@ beforeEach(() => {
|
|
|
684
719
|
mockHasProactiveArtifactCompleted = true;
|
|
685
720
|
mockTryClaimProactiveArtifactTrigger = false;
|
|
686
721
|
runProactiveArtifactJobMock.mockClear();
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
() => {},
|
|
690
|
-
);
|
|
722
|
+
setConversationHistoryStrippedAtMock.mockClear();
|
|
723
|
+
setConversationHistoryStrippedAtMock.mockImplementation(() => {});
|
|
691
724
|
applyRuntimeInjectionsMock.mockClear();
|
|
692
725
|
buildUnifiedTurnContextBlockMock.mockClear();
|
|
693
726
|
resolveTurnTimezoneContextMock.mockClear();
|
|
@@ -695,6 +728,13 @@ beforeEach(() => {
|
|
|
695
728
|
mockSlackChronologicalContext = null;
|
|
696
729
|
loadSlackChronologicalContextMock.mockClear();
|
|
697
730
|
getSlackCompactionWatermarkForPrefixMock.mockClear();
|
|
731
|
+
deleteMessageByIdMock.mockClear();
|
|
732
|
+
reserveMessageMock.mockClear();
|
|
733
|
+
updateMessageContentMock.mockClear();
|
|
734
|
+
indexMessageNowMock.mockClear();
|
|
735
|
+
projectAssistantMessageMock.mockClear();
|
|
736
|
+
publishSyncInvalidationMock.mockClear();
|
|
737
|
+
mockMessageById = null;
|
|
698
738
|
// Orchestrator pipelines (overflowReduce, persistence, …) run through the
|
|
699
739
|
// plugin registry; reset and re-register every default so the pipelines
|
|
700
740
|
// dispatch to middleware backed by the mocked collaborators these tests
|
|
@@ -786,6 +826,9 @@ describe("session-agent-loop", () => {
|
|
|
786
826
|
_requestId,
|
|
787
827
|
onCheckpoint,
|
|
788
828
|
) => {
|
|
829
|
+
// Prime the assistant row anchor for LLM call 1 — production code
|
|
830
|
+
// emits this from `AgentLoop.run` just before `provider.sendMessage`.
|
|
831
|
+
await onEvent({ type: "llm_call_started" });
|
|
789
832
|
await onEvent({
|
|
790
833
|
type: "message_complete",
|
|
791
834
|
message: {
|
|
@@ -811,6 +854,9 @@ describe("session-agent-loop", () => {
|
|
|
811
854
|
hasToolUse: true,
|
|
812
855
|
history: messages,
|
|
813
856
|
});
|
|
857
|
+
// Prime the anchor again for LLM call 2 — multi-call agent turns
|
|
858
|
+
// reserve a fresh assistant row per LLM call.
|
|
859
|
+
await onEvent({ type: "llm_call_started" });
|
|
814
860
|
await onEvent({
|
|
815
861
|
type: "message_complete",
|
|
816
862
|
message: {
|
|
@@ -1067,6 +1113,9 @@ describe("session-agent-loop", () => {
|
|
|
1067
1113
|
const events: ServerMessage[] = [];
|
|
1068
1114
|
|
|
1069
1115
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1116
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1117
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1118
|
+
await onEvent({ type: "llm_call_started" });
|
|
1070
1119
|
// Simulate tool_use + error during execution
|
|
1071
1120
|
onEvent({
|
|
1072
1121
|
type: "tool_use",
|
|
@@ -1116,6 +1165,9 @@ describe("session-agent-loop", () => {
|
|
|
1116
1165
|
const events: ServerMessage[] = [];
|
|
1117
1166
|
|
|
1118
1167
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1168
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1169
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1170
|
+
await onEvent({ type: "llm_call_started" });
|
|
1119
1171
|
onEvent({
|
|
1120
1172
|
type: "message_complete",
|
|
1121
1173
|
message: {
|
|
@@ -1176,6 +1228,9 @@ describe("session-agent-loop", () => {
|
|
|
1176
1228
|
};
|
|
1177
1229
|
|
|
1178
1230
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1231
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1232
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1233
|
+
await onEvent({ type: "llm_call_started" });
|
|
1179
1234
|
onEvent({
|
|
1180
1235
|
type: "message_complete",
|
|
1181
1236
|
message: {
|
|
@@ -1218,20 +1273,8 @@ describe("session-agent-loop", () => {
|
|
|
1218
1273
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1219
1274
|
|
|
1220
1275
|
expect(recordRequestLogMock).toHaveBeenCalledTimes(1);
|
|
1221
|
-
const call = recordRequestLogMock.mock.calls[0] as unknown as [
|
|
1222
|
-
|
|
1223
|
-
string,
|
|
1224
|
-
string,
|
|
1225
|
-
undefined,
|
|
1226
|
-
string,
|
|
1227
|
-
];
|
|
1228
|
-
expect(call).toEqual([
|
|
1229
|
-
"test-conv",
|
|
1230
|
-
JSON.stringify(rawRequest),
|
|
1231
|
-
JSON.stringify(rawResponse),
|
|
1232
|
-
undefined,
|
|
1233
|
-
"fireworks",
|
|
1234
|
-
]);
|
|
1276
|
+
const call = recordRequestLogMock.mock.calls[0] as unknown as unknown[];
|
|
1277
|
+
expect(call[4]).toBe("fireworks");
|
|
1235
1278
|
});
|
|
1236
1279
|
|
|
1237
1280
|
test("record request log falls back to the runtime provider when no actual provider is supplied", async () => {
|
|
@@ -1253,6 +1296,9 @@ describe("session-agent-loop", () => {
|
|
|
1253
1296
|
};
|
|
1254
1297
|
|
|
1255
1298
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1299
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1300
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1301
|
+
await onEvent({ type: "llm_call_started" });
|
|
1256
1302
|
onEvent({
|
|
1257
1303
|
type: "message_complete",
|
|
1258
1304
|
message: {
|
|
@@ -1335,6 +1381,9 @@ describe("session-agent-loop", () => {
|
|
|
1335
1381
|
};
|
|
1336
1382
|
|
|
1337
1383
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1384
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1385
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1386
|
+
await onEvent({ type: "llm_call_started" });
|
|
1338
1387
|
onEvent({
|
|
1339
1388
|
type: "message_complete",
|
|
1340
1389
|
message: {
|
|
@@ -1377,20 +1426,9 @@ describe("session-agent-loop", () => {
|
|
|
1377
1426
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1378
1427
|
|
|
1379
1428
|
expect(recordRequestLogMock).toHaveBeenCalledTimes(1);
|
|
1380
|
-
const call = recordRequestLogMock.mock.calls[0] as unknown as [
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
string,
|
|
1384
|
-
undefined,
|
|
1385
|
-
string,
|
|
1386
|
-
];
|
|
1387
|
-
expect(call).toEqual([
|
|
1388
|
-
"test-conv",
|
|
1389
|
-
JSON.stringify(rawRequest),
|
|
1390
|
-
JSON.stringify(rawResponse),
|
|
1391
|
-
undefined,
|
|
1392
|
-
"openai",
|
|
1393
|
-
]);
|
|
1429
|
+
const call = recordRequestLogMock.mock.calls[0] as unknown as unknown[];
|
|
1430
|
+
expect(call[1]).toBe(JSON.stringify(rawRequest));
|
|
1431
|
+
expect(call[2]).toBe(JSON.stringify(rawResponse));
|
|
1394
1432
|
});
|
|
1395
1433
|
});
|
|
1396
1434
|
|
|
@@ -1414,6 +1452,9 @@ describe("session-agent-loop", () => {
|
|
|
1414
1452
|
}> = [];
|
|
1415
1453
|
|
|
1416
1454
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1455
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1456
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1457
|
+
await onEvent({ type: "llm_call_started" });
|
|
1417
1458
|
onEvent({ type: "text_delta", text: "Hi." });
|
|
1418
1459
|
onEvent({
|
|
1419
1460
|
type: "message_complete",
|
|
@@ -1489,6 +1530,9 @@ describe("session-agent-loop", () => {
|
|
|
1489
1530
|
}> = [];
|
|
1490
1531
|
|
|
1491
1532
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1533
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1534
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1535
|
+
await onEvent({ type: "llm_call_started" });
|
|
1492
1536
|
// No text_delta — pure tool-call response
|
|
1493
1537
|
onEvent({
|
|
1494
1538
|
type: "message_complete",
|
|
@@ -1552,6 +1596,9 @@ describe("session-agent-loop", () => {
|
|
|
1552
1596
|
const events: ServerMessage[] = [];
|
|
1553
1597
|
|
|
1554
1598
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1599
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1600
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1601
|
+
await onEvent({ type: "llm_call_started" });
|
|
1555
1602
|
onEvent({
|
|
1556
1603
|
type: "message_complete",
|
|
1557
1604
|
message: {
|
|
@@ -1664,6 +1711,9 @@ describe("session-agent-loop", () => {
|
|
|
1664
1711
|
});
|
|
1665
1712
|
|
|
1666
1713
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1714
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1715
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1716
|
+
await onEvent({ type: "llm_call_started" });
|
|
1667
1717
|
onEvent({
|
|
1668
1718
|
type: "message_complete",
|
|
1669
1719
|
message: {
|
|
@@ -1754,6 +1804,11 @@ describe("session-agent-loop", () => {
|
|
|
1754
1804
|
};
|
|
1755
1805
|
|
|
1756
1806
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1807
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1808
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
1809
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
1810
|
+
// its own row.
|
|
1811
|
+
await onEvent({ type: "llm_call_started" });
|
|
1757
1812
|
callCount++;
|
|
1758
1813
|
if (callCount === 1) {
|
|
1759
1814
|
onEvent({
|
|
@@ -1881,6 +1936,11 @@ describe("session-agent-loop", () => {
|
|
|
1881
1936
|
};
|
|
1882
1937
|
|
|
1883
1938
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
1939
|
+
// Prime the assistant row anchor — production code emits this from
|
|
1940
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
1941
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
1942
|
+
// its own row.
|
|
1943
|
+
await onEvent({ type: "llm_call_started" });
|
|
1884
1944
|
callCount++;
|
|
1885
1945
|
if (callCount === 1) {
|
|
1886
1946
|
onEvent({
|
|
@@ -1966,6 +2026,11 @@ describe("session-agent-loop", () => {
|
|
|
1966
2026
|
mockOverflowAction = "auto_compress_latest_turn";
|
|
1967
2027
|
|
|
1968
2028
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
2029
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2030
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
2031
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
2032
|
+
// its own row.
|
|
2033
|
+
await onEvent({ type: "llm_call_started" });
|
|
1969
2034
|
callCount++;
|
|
1970
2035
|
if (callCount <= 2) {
|
|
1971
2036
|
onEvent({
|
|
@@ -2042,6 +2107,154 @@ describe("session-agent-loop", () => {
|
|
|
2042
2107
|
expect(complete).toBeDefined();
|
|
2043
2108
|
});
|
|
2044
2109
|
|
|
2110
|
+
test("emits budget_yield_unrecovered when auto_compress rerun yields at mid-loop budget", async () => {
|
|
2111
|
+
// Regression test for the silent-stall failure mode:
|
|
2112
|
+
// when every recovery layer has been applied (tier reducer
|
|
2113
|
+
// exhausted + auto_compress_latest_turn emergency compaction)
|
|
2114
|
+
// and the final `agentLoop.run` STILL yields at the mid-loop
|
|
2115
|
+
// budget checkpoint, the orchestrator used to fall through to
|
|
2116
|
+
// post-turn cleanup with NO `agent_loop_exit_reason` emitted —
|
|
2117
|
+
// the turn just stopped mid-action and `llm_request_logs` showed
|
|
2118
|
+
// a NULL exit reason on the final row. We now emit
|
|
2119
|
+
// `budget_yield_unrecovered` so the inspector and dashboards can
|
|
2120
|
+
// attribute the silent stall.
|
|
2121
|
+
const events: ServerMessage[] = [];
|
|
2122
|
+
let callCount = 0;
|
|
2123
|
+
|
|
2124
|
+
// Reducer exhausts all 4 tiers on first call so the convergence
|
|
2125
|
+
// loop runs exactly one iteration before falling through to
|
|
2126
|
+
// the auto_compress_latest_turn branch.
|
|
2127
|
+
mockReducerStepFn = (msgs: Message[]) => ({
|
|
2128
|
+
messages: msgs,
|
|
2129
|
+
tier: "injection_downgrade",
|
|
2130
|
+
state: {
|
|
2131
|
+
appliedTiers: [
|
|
2132
|
+
"forced_compaction",
|
|
2133
|
+
"tool_result_truncation",
|
|
2134
|
+
"media_stubbing",
|
|
2135
|
+
"injection_downgrade",
|
|
2136
|
+
],
|
|
2137
|
+
injectionMode: "minimal",
|
|
2138
|
+
exhausted: true,
|
|
2139
|
+
},
|
|
2140
|
+
estimatedTokens: 120_000,
|
|
2141
|
+
});
|
|
2142
|
+
|
|
2143
|
+
mockOverflowAction = "auto_compress_latest_turn";
|
|
2144
|
+
|
|
2145
|
+
// Sits between the preflight budget (preflightBudget =
|
|
2146
|
+
// 100k * 0.95 = 95k — anything above triggers preflight reducer
|
|
2147
|
+
// *before* we get to the convergence/auto_compress path under
|
|
2148
|
+
// test) and the mid-loop threshold (preflightBudget * 0.85 =
|
|
2149
|
+
// ≈80.75k — anything above flips yieldedForBudget on a checkpoint
|
|
2150
|
+
// call). 90k satisfies both so the path reaches call 3.
|
|
2151
|
+
mockEstimateTokens = 90_000;
|
|
2152
|
+
|
|
2153
|
+
const agentLoopRun: AgentLoopRun = async (
|
|
2154
|
+
messages,
|
|
2155
|
+
onEvent,
|
|
2156
|
+
_signal,
|
|
2157
|
+
_reqId,
|
|
2158
|
+
onCheckpoint,
|
|
2159
|
+
) => {
|
|
2160
|
+
callCount++;
|
|
2161
|
+
if (callCount <= 2) {
|
|
2162
|
+
// Calls 1 (initial) and 2 (convergence rerun): error so
|
|
2163
|
+
// `state.contextTooLargeDetected` stays true through
|
|
2164
|
+
// convergence exit and we enter the auto_compress branch.
|
|
2165
|
+
onEvent({
|
|
2166
|
+
type: "error",
|
|
2167
|
+
error: new Error("context_length_exceeded"),
|
|
2168
|
+
});
|
|
2169
|
+
onEvent({
|
|
2170
|
+
type: "usage",
|
|
2171
|
+
inputTokens: 100,
|
|
2172
|
+
outputTokens: 0,
|
|
2173
|
+
model: "test-model",
|
|
2174
|
+
providerDurationMs: 50,
|
|
2175
|
+
});
|
|
2176
|
+
return messages;
|
|
2177
|
+
}
|
|
2178
|
+
// Call 3: the auto_compress_latest_turn rerun. Invoke
|
|
2179
|
+
// onCheckpoint so the orchestrator's mid-loop budget check
|
|
2180
|
+
// flips `yieldedForBudget` to true, then return without
|
|
2181
|
+
// finishing — mirroring what AgentLoop.run does when its
|
|
2182
|
+
// checkpoint returns "yield".
|
|
2183
|
+
if (onCheckpoint) {
|
|
2184
|
+
await onCheckpoint({
|
|
2185
|
+
turnIndex: 0,
|
|
2186
|
+
toolCount: 1,
|
|
2187
|
+
hasToolUse: true,
|
|
2188
|
+
history: messages,
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
return messages;
|
|
2192
|
+
};
|
|
2193
|
+
|
|
2194
|
+
const ctx = makeCtx({
|
|
2195
|
+
agentLoopRun,
|
|
2196
|
+
hasNoClient: true,
|
|
2197
|
+
contextWindowManager: {
|
|
2198
|
+
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
2199
|
+
maybeCompact: async () => ({
|
|
2200
|
+
compacted: true,
|
|
2201
|
+
messages: [
|
|
2202
|
+
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
|
2203
|
+
] as Message[],
|
|
2204
|
+
compactedPersistedMessages: 3,
|
|
2205
|
+
summaryText: "Compressed summary",
|
|
2206
|
+
previousEstimatedInputTokens: 120000,
|
|
2207
|
+
estimatedInputTokens: 30000,
|
|
2208
|
+
maxInputTokens: 100000,
|
|
2209
|
+
thresholdTokens: 80000,
|
|
2210
|
+
compactedMessages: 5,
|
|
2211
|
+
summaryCalls: 1,
|
|
2212
|
+
summaryInputTokens: 300,
|
|
2213
|
+
summaryOutputTokens: 100,
|
|
2214
|
+
summaryModel: "mock-model",
|
|
2215
|
+
}),
|
|
2216
|
+
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
2217
|
+
});
|
|
2218
|
+
|
|
2219
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
2220
|
+
|
|
2221
|
+
// Observability emit: exit reason was stamped onto the latest
|
|
2222
|
+
// llm_request_logs row.
|
|
2223
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
2224
|
+
"test-conv",
|
|
2225
|
+
"budget_yield_unrecovered",
|
|
2226
|
+
);
|
|
2227
|
+
|
|
2228
|
+
// We did NOT also emit context_too_large — the auto_compress
|
|
2229
|
+
// branch resets `contextTooLargeDetected` before its rerun and
|
|
2230
|
+
// the rerun's yield-for-budget keeps it false, so the error
|
|
2231
|
+
// branch above stays skipped.
|
|
2232
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).not.toHaveBeenCalledWith(
|
|
2233
|
+
"test-conv",
|
|
2234
|
+
"context_too_large",
|
|
2235
|
+
);
|
|
2236
|
+
|
|
2237
|
+
// User-facing emit: the classified BUDGET_YIELD_UNRECOVERED
|
|
2238
|
+
// error is sent to the client so the UI can render a notice
|
|
2239
|
+
// instead of leaving the turn looking like a silent ghost. The
|
|
2240
|
+
// assistant-side notice persistence is exercised in the overflow
|
|
2241
|
+
// suite (`conversation-agent-loop-overflow.test.ts`); this test
|
|
2242
|
+
// owns the observability + emit contract.
|
|
2243
|
+
const conversationError = events.find(
|
|
2244
|
+
(e) => e.type === "conversation_error",
|
|
2245
|
+
);
|
|
2246
|
+
expect(conversationError).toBeDefined();
|
|
2247
|
+
if (conversationError && "code" in conversationError) {
|
|
2248
|
+
expect(conversationError.code).toBe("BUDGET_YIELD_UNRECOVERED");
|
|
2249
|
+
expect(conversationError.retryable).toBe(true);
|
|
2250
|
+
expect(conversationError.errorCategory).toBe(
|
|
2251
|
+
"budget_yield_unrecovered",
|
|
2252
|
+
);
|
|
2253
|
+
} else {
|
|
2254
|
+
throw new Error("conversation_error missing `code` field");
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2257
|
+
|
|
2045
2258
|
test("recovery loop is bounded by maxAttempts", async () => {
|
|
2046
2259
|
const events: ServerMessage[] = [];
|
|
2047
2260
|
let reducerCalls = 0;
|
|
@@ -2115,6 +2328,9 @@ describe("session-agent-loop", () => {
|
|
|
2115
2328
|
|
|
2116
2329
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
2117
2330
|
agentLoopCalls++;
|
|
2331
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2332
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
2333
|
+
await onEvent({ type: "llm_call_started" });
|
|
2118
2334
|
onEvent({
|
|
2119
2335
|
type: "message_complete",
|
|
2120
2336
|
message: {
|
|
@@ -2163,6 +2379,11 @@ describe("session-agent-loop", () => {
|
|
|
2163
2379
|
let callCount = 0;
|
|
2164
2380
|
|
|
2165
2381
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
2382
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2383
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
2384
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
2385
|
+
// its own row.
|
|
2386
|
+
await onEvent({ type: "llm_call_started" });
|
|
2166
2387
|
callCount++;
|
|
2167
2388
|
if (callCount === 1) {
|
|
2168
2389
|
onEvent({
|
|
@@ -2247,6 +2468,11 @@ describe("session-agent-loop", () => {
|
|
|
2247
2468
|
_reqId,
|
|
2248
2469
|
onCheckpoint,
|
|
2249
2470
|
) => {
|
|
2471
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2472
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
2473
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
2474
|
+
// its own row.
|
|
2475
|
+
await onEvent({ type: "llm_call_started" });
|
|
2250
2476
|
// Simulate tool use followed by checkpoint
|
|
2251
2477
|
onEvent({ type: "tool_use", id: "tu-1", name: "file_read", input: {} });
|
|
2252
2478
|
onEvent({
|
|
@@ -2320,6 +2546,11 @@ describe("session-agent-loop", () => {
|
|
|
2320
2546
|
_reqId,
|
|
2321
2547
|
onCheckpoint,
|
|
2322
2548
|
) => {
|
|
2549
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2550
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
2551
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
2552
|
+
// its own row.
|
|
2553
|
+
await onEvent({ type: "llm_call_started" });
|
|
2323
2554
|
onEvent({ type: "tool_use", id: "tu-1", name: "file_read", input: {} });
|
|
2324
2555
|
onEvent({
|
|
2325
2556
|
type: "tool_result",
|
|
@@ -2382,6 +2613,9 @@ describe("session-agent-loop", () => {
|
|
|
2382
2613
|
const abortController = new AbortController();
|
|
2383
2614
|
|
|
2384
2615
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
2616
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2617
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
2618
|
+
await onEvent({ type: "llm_call_started" });
|
|
2385
2619
|
onEvent({
|
|
2386
2620
|
type: "message_complete",
|
|
2387
2621
|
message: {
|
|
@@ -2442,6 +2676,9 @@ describe("session-agent-loop", () => {
|
|
|
2442
2676
|
resolveAssistantAttachmentsMock.mockClear();
|
|
2443
2677
|
|
|
2444
2678
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
2679
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2680
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
2681
|
+
await onEvent({ type: "llm_call_started" });
|
|
2445
2682
|
onEvent({
|
|
2446
2683
|
type: "message_complete",
|
|
2447
2684
|
message: {
|
|
@@ -2481,6 +2718,9 @@ describe("session-agent-loop", () => {
|
|
|
2481
2718
|
test("increments turnCount after successful run", async () => {
|
|
2482
2719
|
const ctx = makeCtx({
|
|
2483
2720
|
agentLoopRun: async (messages, onEvent) => {
|
|
2721
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2722
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
2723
|
+
await onEvent({ type: "llm_call_started" });
|
|
2484
2724
|
onEvent({
|
|
2485
2725
|
type: "message_complete",
|
|
2486
2726
|
message: {
|
|
@@ -2514,6 +2754,9 @@ describe("session-agent-loop", () => {
|
|
|
2514
2754
|
test("clears processing state and abort controller", async () => {
|
|
2515
2755
|
const ctx = makeCtx({
|
|
2516
2756
|
agentLoopRun: async (messages, onEvent) => {
|
|
2757
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2758
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
2759
|
+
await onEvent({ type: "llm_call_started" });
|
|
2517
2760
|
onEvent({
|
|
2518
2761
|
type: "message_complete",
|
|
2519
2762
|
message: {
|
|
@@ -2577,8 +2820,13 @@ describe("session-agent-loop", () => {
|
|
|
2577
2820
|
const ctx = makeCtx({
|
|
2578
2821
|
agentLoopRun: async (
|
|
2579
2822
|
messages: Message[],
|
|
2580
|
-
onEvent: (event: AgentEvent) => void
|
|
2823
|
+
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
2581
2824
|
) => {
|
|
2825
|
+
// Prime the assistant row anchor — production code emits this from
|
|
2826
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Must be
|
|
2827
|
+
// awaited so the assistant row is reserved before message_complete
|
|
2828
|
+
// tries to write into it.
|
|
2829
|
+
await onEvent({ type: "llm_call_started" });
|
|
2582
2830
|
onEvent({
|
|
2583
2831
|
type: "message_complete",
|
|
2584
2832
|
message: {
|
|
@@ -2922,6 +3170,280 @@ describe("session-agent-loop", () => {
|
|
|
2922
3170
|
});
|
|
2923
3171
|
});
|
|
2924
3172
|
|
|
3173
|
+
describe("B3 pre-allocation: indexing + cleanup", () => {
|
|
3174
|
+
test("handleMessageComplete indexes and projects the finalized assistant row", async () => {
|
|
3175
|
+
// The pre-B3 path inserted assistant rows via `addMessage`, which ran
|
|
3176
|
+
// the memory indexer and the conversation-attention projector as
|
|
3177
|
+
// side-effects of the insert. B3 splits the write into
|
|
3178
|
+
// `reserveMessage` + `updateMessageContent`, both of which are CRUD-only,
|
|
3179
|
+
// so the indexing + projection calls had to be re-driven explicitly
|
|
3180
|
+
// after `updateContent` succeeds. Codex P1 caught a regression where
|
|
3181
|
+
// this path was missing entirely; this test pins it down.
|
|
3182
|
+
mockMessageById = {
|
|
3183
|
+
id: "msg-reserve",
|
|
3184
|
+
conversationId: "test-conv",
|
|
3185
|
+
createdAt: 1234567,
|
|
3186
|
+
role: "assistant",
|
|
3187
|
+
content: "[]",
|
|
3188
|
+
metadata: null,
|
|
3189
|
+
};
|
|
3190
|
+
// Force attention projection to report a state change so we also
|
|
3191
|
+
// observe the sync-invalidation publish path on the same turn.
|
|
3192
|
+
projectAssistantMessageMock.mockImplementationOnce(() => true);
|
|
3193
|
+
|
|
3194
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3195
|
+
await onEvent({ type: "llm_call_started" });
|
|
3196
|
+
// `message_complete` is awaited so `handleMessageComplete` (and its
|
|
3197
|
+
// async indexer + projector chain) completes before the next event
|
|
3198
|
+
// or before the loop returns. Without the await the projector's
|
|
3199
|
+
// synchronous call still races against the test's assertion phase
|
|
3200
|
+
// because the indexer's `await` yields microtasks.
|
|
3201
|
+
await onEvent({
|
|
3202
|
+
type: "message_complete",
|
|
3203
|
+
message: {
|
|
3204
|
+
role: "assistant",
|
|
3205
|
+
content: [{ type: "text", text: "indexed reply" }],
|
|
3206
|
+
},
|
|
3207
|
+
});
|
|
3208
|
+
onEvent({
|
|
3209
|
+
type: "usage",
|
|
3210
|
+
inputTokens: 10,
|
|
3211
|
+
outputTokens: 5,
|
|
3212
|
+
model: "test",
|
|
3213
|
+
providerDurationMs: 50,
|
|
3214
|
+
});
|
|
3215
|
+
return [
|
|
3216
|
+
...messages,
|
|
3217
|
+
{
|
|
3218
|
+
role: "assistant" as const,
|
|
3219
|
+
content: [
|
|
3220
|
+
{ type: "text", text: "indexed reply" },
|
|
3221
|
+
] as ContentBlock[],
|
|
3222
|
+
},
|
|
3223
|
+
];
|
|
3224
|
+
};
|
|
3225
|
+
|
|
3226
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3227
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3228
|
+
|
|
3229
|
+
// Indexer fired with the reserved row's id + the finalized content.
|
|
3230
|
+
expect(indexMessageNowMock).toHaveBeenCalledTimes(1);
|
|
3231
|
+
const indexCallArgs = indexMessageNowMock.mock.calls[0] as unknown as [
|
|
3232
|
+
{
|
|
3233
|
+
messageId: string;
|
|
3234
|
+
conversationId: string;
|
|
3235
|
+
role: string;
|
|
3236
|
+
content: string;
|
|
3237
|
+
createdAt: number;
|
|
3238
|
+
scopeId: string;
|
|
3239
|
+
},
|
|
3240
|
+
unknown,
|
|
3241
|
+
];
|
|
3242
|
+
const indexCall = indexCallArgs[0];
|
|
3243
|
+
expect(indexCall).toMatchObject({
|
|
3244
|
+
messageId: "msg-reserve",
|
|
3245
|
+
conversationId: "test-conv",
|
|
3246
|
+
role: "assistant",
|
|
3247
|
+
createdAt: 1234567,
|
|
3248
|
+
scopeId: "default",
|
|
3249
|
+
});
|
|
3250
|
+
expect(indexCall.content).toContain("indexed reply");
|
|
3251
|
+
|
|
3252
|
+
// Attention projector fired with the same row coordinates.
|
|
3253
|
+
expect(projectAssistantMessageMock).toHaveBeenCalledTimes(1);
|
|
3254
|
+
const projectCall = projectAssistantMessageMock.mock
|
|
3255
|
+
.calls[0] as unknown as [
|
|
3256
|
+
{ conversationId: string; messageId: string; messageAt: number },
|
|
3257
|
+
];
|
|
3258
|
+
expect(projectCall[0]).toEqual({
|
|
3259
|
+
conversationId: "test-conv",
|
|
3260
|
+
messageId: "msg-reserve",
|
|
3261
|
+
messageAt: 1234567,
|
|
3262
|
+
});
|
|
3263
|
+
|
|
3264
|
+
// Projection reported a state change → sync invalidation fires with
|
|
3265
|
+
// the conversation `:metadata` tag. The mock also receives a
|
|
3266
|
+
// `:messages` invalidation from the orchestrator's
|
|
3267
|
+
// `publishLoopMessagesChanged` post-loop emit, so we filter by tag
|
|
3268
|
+
// rather than asserting a total call count.
|
|
3269
|
+
const metadataPublishes = (
|
|
3270
|
+
publishSyncInvalidationMock.mock.calls as unknown as Array<[string[]]>
|
|
3271
|
+
).filter((args) => args[0]?.includes("conversation:test-conv:metadata"));
|
|
3272
|
+
expect(metadataPublishes).toHaveLength(1);
|
|
3273
|
+
});
|
|
3274
|
+
|
|
3275
|
+
test("handleMessageComplete skips sync invalidation when attention state unchanged", async () => {
|
|
3276
|
+
// Mirror of the previous test but with the default projector return
|
|
3277
|
+
// (`false`). The projection still runs every turn, but the sync
|
|
3278
|
+
// invalidation publish must be gated on attention-state movement to
|
|
3279
|
+
// avoid flooding clients with no-op metadata refreshes.
|
|
3280
|
+
mockMessageById = {
|
|
3281
|
+
id: "msg-reserve",
|
|
3282
|
+
conversationId: "test-conv",
|
|
3283
|
+
createdAt: 999,
|
|
3284
|
+
role: "assistant",
|
|
3285
|
+
content: "[]",
|
|
3286
|
+
metadata: null,
|
|
3287
|
+
};
|
|
3288
|
+
|
|
3289
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3290
|
+
await onEvent({ type: "llm_call_started" });
|
|
3291
|
+
// See sibling test — `message_complete` must be awaited so the
|
|
3292
|
+
// projector call lands before the assertion phase.
|
|
3293
|
+
await onEvent({
|
|
3294
|
+
type: "message_complete",
|
|
3295
|
+
message: {
|
|
3296
|
+
role: "assistant",
|
|
3297
|
+
content: [{ type: "text", text: "quiet" }],
|
|
3298
|
+
},
|
|
3299
|
+
});
|
|
3300
|
+
onEvent({
|
|
3301
|
+
type: "usage",
|
|
3302
|
+
inputTokens: 1,
|
|
3303
|
+
outputTokens: 1,
|
|
3304
|
+
model: "test",
|
|
3305
|
+
providerDurationMs: 1,
|
|
3306
|
+
});
|
|
3307
|
+
return [
|
|
3308
|
+
...messages,
|
|
3309
|
+
{
|
|
3310
|
+
role: "assistant" as const,
|
|
3311
|
+
content: [{ type: "text", text: "quiet" }] as ContentBlock[],
|
|
3312
|
+
},
|
|
3313
|
+
];
|
|
3314
|
+
};
|
|
3315
|
+
|
|
3316
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3317
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3318
|
+
|
|
3319
|
+
expect(projectAssistantMessageMock).toHaveBeenCalledTimes(1);
|
|
3320
|
+
// The mock will still receive a `:messages` invalidation from the
|
|
3321
|
+
// orchestrator's `publishLoopMessagesChanged` — filter to the
|
|
3322
|
+
// `:metadata` tag and assert it never landed.
|
|
3323
|
+
const metadataPublishes = (
|
|
3324
|
+
publishSyncInvalidationMock.mock.calls as unknown as Array<[string[]]>
|
|
3325
|
+
).filter((args) => args[0]?.includes("conversation:test-conv:metadata"));
|
|
3326
|
+
expect(metadataPublishes).toHaveLength(0);
|
|
3327
|
+
});
|
|
3328
|
+
|
|
3329
|
+
test("handleLlmCallStarted deletes a stranded reservation before reserving a new row", async () => {
|
|
3330
|
+
// Simulates a retry path: the first LLM call reserves an assistant row
|
|
3331
|
+
// but exits without `message_complete` (e.g. context-overflow rescue,
|
|
3332
|
+
// ordering-error rescue, image-overflow rescue). The next
|
|
3333
|
+
// `llm_call_started` must delete the stranded row so the transcript
|
|
3334
|
+
// does not accumulate empty assistant bubbles.
|
|
3335
|
+
reserveMessageMock
|
|
3336
|
+
.mockImplementationOnce(async () => ({ id: "msg-strand-A" }))
|
|
3337
|
+
.mockImplementationOnce(async () => ({ id: "msg-strand-B" }));
|
|
3338
|
+
// Indexer/projector mocks default to no-op; no finalized row in this
|
|
3339
|
+
// test, so `mockMessageById` stays null.
|
|
3340
|
+
|
|
3341
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3342
|
+
// First LLM call: reserve msg-strand-A, never finalize.
|
|
3343
|
+
await onEvent({ type: "llm_call_started" });
|
|
3344
|
+
// Second LLM call: should delete msg-strand-A before reserving
|
|
3345
|
+
// msg-strand-B.
|
|
3346
|
+
await onEvent({ type: "llm_call_started" });
|
|
3347
|
+
// Finalize the second one so the loop has a valid assistant message
|
|
3348
|
+
// and exits cleanly.
|
|
3349
|
+
onEvent({
|
|
3350
|
+
type: "message_complete",
|
|
3351
|
+
message: {
|
|
3352
|
+
role: "assistant",
|
|
3353
|
+
content: [{ type: "text", text: "retry succeeded" }],
|
|
3354
|
+
},
|
|
3355
|
+
});
|
|
3356
|
+
onEvent({
|
|
3357
|
+
type: "usage",
|
|
3358
|
+
inputTokens: 5,
|
|
3359
|
+
outputTokens: 3,
|
|
3360
|
+
model: "test",
|
|
3361
|
+
providerDurationMs: 25,
|
|
3362
|
+
});
|
|
3363
|
+
return [
|
|
3364
|
+
...messages,
|
|
3365
|
+
{
|
|
3366
|
+
role: "assistant" as const,
|
|
3367
|
+
content: [
|
|
3368
|
+
{ type: "text", text: "retry succeeded" },
|
|
3369
|
+
] as ContentBlock[],
|
|
3370
|
+
},
|
|
3371
|
+
];
|
|
3372
|
+
};
|
|
3373
|
+
|
|
3374
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3375
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3376
|
+
|
|
3377
|
+
// Exactly one delete fires — for msg-strand-A, before the second
|
|
3378
|
+
// reserve. The second reservation is committed via `updateContent`
|
|
3379
|
+
// (not deleted), and after the run completes
|
|
3380
|
+
// `assistantRowAwaitingFinalization` is false, so no further delete
|
|
3381
|
+
// is attempted on shutdown.
|
|
3382
|
+
expect(deleteMessageByIdMock).toHaveBeenCalledTimes(1);
|
|
3383
|
+
const strandDeleteCall = deleteMessageByIdMock.mock
|
|
3384
|
+
.calls[0] as unknown as [string];
|
|
3385
|
+
expect(strandDeleteCall[0]).toBe("msg-strand-A");
|
|
3386
|
+
expect(reserveMessageMock).toHaveBeenCalledTimes(2);
|
|
3387
|
+
});
|
|
3388
|
+
|
|
3389
|
+
test("provider-error branch deletes the orphaned reservation and repoints lastAssistantMessageId", async () => {
|
|
3390
|
+
// Codex P2 regression: B3 reserves an empty assistant row at
|
|
3391
|
+
// `llm_call_started`. When the call exits via the provider-error
|
|
3392
|
+
// branch (no `message_complete`), the synthetic error message is
|
|
3393
|
+
// inserted separately. Without cleanup the transcript would carry
|
|
3394
|
+
// both the empty reserved row AND the error message, and
|
|
3395
|
+
// `syncLastAssistantMessageToDisk` (which reads
|
|
3396
|
+
// `state.lastAssistantMessageId`) would mis-target the deleted
|
|
3397
|
+
// reservation id.
|
|
3398
|
+
reserveMessageMock.mockImplementationOnce(async () => ({
|
|
3399
|
+
id: "msg-orphaned-reservation",
|
|
3400
|
+
}));
|
|
3401
|
+
|
|
3402
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3403
|
+
// Reserve the orphan.
|
|
3404
|
+
await onEvent({ type: "llm_call_started" });
|
|
3405
|
+
// Provider rejects — writes the llm_request_log row and arms
|
|
3406
|
+
// `state.providerErrorUserMessage` via `handleError`.
|
|
3407
|
+
onEvent({
|
|
3408
|
+
type: "provider_error",
|
|
3409
|
+
error: new Error("upstream 500"),
|
|
3410
|
+
rawRequest: { model: "gpt-4.1", messages: [] },
|
|
3411
|
+
actualProvider: "openai",
|
|
3412
|
+
});
|
|
3413
|
+
onEvent({
|
|
3414
|
+
type: "error",
|
|
3415
|
+
error: new Error("upstream 500"),
|
|
3416
|
+
});
|
|
3417
|
+
// No assistant message in the result — the synthetic-error branch
|
|
3418
|
+
// below the agent loop fires.
|
|
3419
|
+
return messages;
|
|
3420
|
+
};
|
|
3421
|
+
|
|
3422
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3423
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3424
|
+
|
|
3425
|
+
// The orphan was deleted exactly once, before the synthetic error
|
|
3426
|
+
// message landed.
|
|
3427
|
+
expect(deleteMessageByIdMock).toHaveBeenCalledTimes(1);
|
|
3428
|
+
const deleteCall = deleteMessageByIdMock.mock.calls[0] as unknown as [
|
|
3429
|
+
string,
|
|
3430
|
+
];
|
|
3431
|
+
expect(deleteCall[0]).toBe("msg-orphaned-reservation");
|
|
3432
|
+
|
|
3433
|
+
// Post-loop `syncLastAssistantMessageToDisk` targets the synthetic
|
|
3434
|
+
// error row's id (`mock-msg-id` from the mocked `addMessage`), NOT
|
|
3435
|
+
// the deleted reservation id. This is the externally-observable
|
|
3436
|
+
// proof that `state.lastAssistantMessageId` was repointed.
|
|
3437
|
+
expect(syncMessageToDiskMock).toHaveBeenCalled();
|
|
3438
|
+
const syncCalls = syncMessageToDiskMock.mock.calls as unknown as Array<
|
|
3439
|
+
[string, string, number]
|
|
3440
|
+
>;
|
|
3441
|
+
const lastSync = syncCalls[syncCalls.length - 1];
|
|
3442
|
+
expect(lastSync?.[1]).toBe("mock-msg-id");
|
|
3443
|
+
expect(lastSync?.[1]).not.toBe("msg-orphaned-reservation");
|
|
3444
|
+
});
|
|
3445
|
+
});
|
|
3446
|
+
|
|
2925
3447
|
describe("pkbSystemReminderBlock metadata persistence", () => {
|
|
2926
3448
|
test("persists pkbSystemReminderBlock in full mode with PKB active", async () => {
|
|
2927
3449
|
const reminder = "<system_reminder>\npkb content\n</system_reminder>";
|
|
@@ -3769,8 +4291,8 @@ describe("session-agent-loop", () => {
|
|
|
3769
4291
|
});
|
|
3770
4292
|
});
|
|
3771
4293
|
|
|
3772
|
-
describe("compaction-strip
|
|
3773
|
-
test("
|
|
4294
|
+
describe("compaction-strip marker persistence", () => {
|
|
4295
|
+
test("records historyStrippedAt when convergence strip runs", async () => {
|
|
3774
4296
|
// Reducer: succeed on first call, returning reduced messages.
|
|
3775
4297
|
mockReducerStepFn = (msgs: Message[]) => ({
|
|
3776
4298
|
messages: msgs,
|
|
@@ -3786,6 +4308,11 @@ describe("session-agent-loop", () => {
|
|
|
3786
4308
|
let callCount = 0;
|
|
3787
4309
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3788
4310
|
callCount++;
|
|
4311
|
+
// Prime the assistant row anchor — production code emits this from
|
|
4312
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
4313
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
4314
|
+
// its own row.
|
|
4315
|
+
await onEvent({ type: "llm_call_started" });
|
|
3789
4316
|
if (callCount === 1) {
|
|
3790
4317
|
// Trigger convergence path: error + appended assistant message so
|
|
3791
4318
|
// updatedHistory.length > preRunHistoryLength at the strip site.
|
|
@@ -3841,21 +4368,16 @@ describe("session-agent-loop", () => {
|
|
|
3841
4368
|
|
|
3842
4369
|
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
3843
4370
|
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
(call) => call[0] === "test-conv",
|
|
3849
|
-
);
|
|
3850
|
-
expect(clearCalls.length).toBeGreaterThanOrEqual(1);
|
|
4371
|
+
const stripCalls = setConversationHistoryStrippedAtMock.mock.calls.filter(
|
|
4372
|
+
(call) => call[0] === "test-conv",
|
|
4373
|
+
);
|
|
4374
|
+
expect(stripCalls.length).toBeGreaterThanOrEqual(1);
|
|
3851
4375
|
});
|
|
3852
4376
|
|
|
3853
|
-
test("strip-site
|
|
3854
|
-
|
|
3855
|
-
(
|
|
3856
|
-
|
|
3857
|
-
},
|
|
3858
|
-
);
|
|
4377
|
+
test("strip-site marker write is non-fatal when the helper throws", async () => {
|
|
4378
|
+
setConversationHistoryStrippedAtMock.mockImplementation(() => {
|
|
4379
|
+
throw new Error("db write failed");
|
|
4380
|
+
});
|
|
3859
4381
|
|
|
3860
4382
|
mockReducerStepFn = (msgs: Message[]) => ({
|
|
3861
4383
|
messages: msgs,
|
|
@@ -3871,6 +4393,11 @@ describe("session-agent-loop", () => {
|
|
|
3871
4393
|
let callCount = 0;
|
|
3872
4394
|
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3873
4395
|
callCount++;
|
|
4396
|
+
// Prime the assistant row anchor — production code emits this from
|
|
4397
|
+
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
4398
|
+
// need this on every invocation: each agent-loop iteration reserves
|
|
4399
|
+
// its own row.
|
|
4400
|
+
await onEvent({ type: "llm_call_started" });
|
|
3874
4401
|
if (callCount === 1) {
|
|
3875
4402
|
onEvent({
|
|
3876
4403
|
type: "error",
|
|
@@ -3922,7 +4449,7 @@ describe("session-agent-loop", () => {
|
|
|
3922
4449
|
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
3923
4450
|
});
|
|
3924
4451
|
|
|
3925
|
-
// Must not throw — the strip-site
|
|
4452
|
+
// Must not throw — the strip-site marker write is wrapped in try/catch.
|
|
3926
4453
|
await expect(
|
|
3927
4454
|
runAgentLoopImpl(ctx, "hello", "msg-1", () => {}),
|
|
3928
4455
|
).resolves.toBeUndefined();
|