@vellumai/assistant 0.8.6 → 0.8.7-dev.202606052118.34cd356
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 +4 -4
- package/Dockerfile +21 -4
- package/bun.lock +13 -4
- package/docker-entrypoint.sh +12 -8
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/plugins.md +110 -83
- package/examples/plugins/echo/README.md +13 -12
- package/examples/plugins/echo/register.ts +0 -54
- package/knip.json +1 -0
- package/node_modules/@vellumai/environments/bun.lock +24 -0
- package/node_modules/@vellumai/environments/package.json +18 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
- package/node_modules/@vellumai/environments/src/index.ts +11 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
- package/node_modules/@vellumai/environments/src/types.ts +70 -0
- package/node_modules/@vellumai/environments/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +13 -8
- package/openapi.yaml +6964 -539
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +88 -54
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +188 -45
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +7 -5
- package/src/__tests__/agent-loop-thinking.test.ts +17 -12
- package/src/__tests__/agent-loop.test.ts +238 -422
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +6 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +296 -57
- package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
- package/src/__tests__/app-control-flow.test.ts +6 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +4 -11
- package/src/__tests__/approval-routes-http.test.ts +8 -3
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-event.test.ts +15 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/assistant-stream-state.test.ts +645 -0
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/avatar-e2e.test.ts +7 -37
- package/src/__tests__/avatar-generator.test.ts +12 -42
- package/src/__tests__/avatar-identity-sync.test.ts +28 -3
- package/src/__tests__/background-shell-bash.test.ts +3 -7
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +69 -15
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/call-pointer-messages.test.ts +5 -3
- package/src/__tests__/call-site-routing-provider.test.ts +22 -40
- package/src/__tests__/catalog-files.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +49 -21
- package/src/__tests__/channel-approvals.test.ts +4 -2
- package/src/__tests__/channel-invite-transport.test.ts +1 -5
- package/src/__tests__/channel-readiness-routes.test.ts +0 -4
- package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
- package/src/__tests__/channel-retry-sweep.test.ts +71 -79
- package/src/__tests__/clawhub-files.test.ts +1 -0
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction-events.test.ts +5 -17
- package/src/__tests__/compaction-trail-store.test.ts +1 -79
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/computer-use-tools.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +34 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +70 -25
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +9 -7
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -34
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +476 -963
- package/src/__tests__/conversation-agent-loop.test.ts +823 -1321
- package/src/__tests__/conversation-analysis-routes.test.ts +7 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-clear-safety.test.ts +20 -10
- package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-disk-view.test.ts +10 -17
- package/src/__tests__/conversation-fork-crud.test.ts +86 -172
- package/src/__tests__/conversation-fork-route.test.ts +16 -14
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +10 -7
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -0
- package/src/__tests__/conversation-process-callsite.test.ts +27 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +80 -51
- package/src/__tests__/conversation-queue.test.ts +272 -164
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -2
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +8 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +317 -313
- package/src/__tests__/conversation-runtime-workspace.test.ts +114 -36
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +42 -31
- package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
- package/src/__tests__/conversation-speed-override.test.ts +8 -22
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +90 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
- package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +20 -17
- package/src/__tests__/conversation-workspace-injection.test.ts +114 -23
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +34 -13
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/cross-provider-web-search.test.ts +220 -3
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -0
- package/src/__tests__/disk-pressure-guard.test.ts +66 -0
- package/src/__tests__/disk-pressure-routes.test.ts +9 -2
- package/src/__tests__/dm-persistence.test.ts +12 -3
- package/src/__tests__/dynamic-page-surface.test.ts +99 -0
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/filing-service.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +55 -14
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/gemini-inline-media.test.ts +78 -0
- package/src/__tests__/gemini-provider.test.ts +351 -28
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +10 -8
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +3 -1
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +162 -0
- package/src/__tests__/history-repair-observability.test.ts +1 -1
- package/src/__tests__/history-repair.test.ts +2 -1
- package/src/__tests__/host-app-control-proxy.test.ts +2 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/host-file-edit-tool.test.ts +4 -2
- package/src/__tests__/host-file-proxy.test.ts +31 -0
- package/src/__tests__/host-file-read-tool.test.ts +4 -2
- package/src/__tests__/host-file-write-tool.test.ts +9 -3
- package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
- package/src/__tests__/host-shell-tool.test.ts +9 -4
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +47 -114
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +12 -3
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +214 -0
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-attachments.test.ts +7 -8
- package/src/__tests__/list-messages-hidden-metadata.test.ts +55 -15
- package/src/__tests__/list-messages-page-latest.test.ts +60 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +56 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +23 -47
- package/src/__tests__/llm-usage-store.test.ts +268 -1
- package/src/__tests__/log-export-routes.test.ts +59 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-health-check.test.ts +18 -13
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/messaging-send-tool.test.ts +8 -4
- package/src/__tests__/migration-export-http.test.ts +12 -12
- package/src/__tests__/migration-import-commit-http.test.ts +8 -8
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +205 -20
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/oauth-commands-routes.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +12 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +97 -71
- package/src/__tests__/openai-responses-provider.test.ts +21 -77
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -286
- package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +3 -1
- package/src/__tests__/pipeline-runner.test.ts +31 -43
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-bootstrap.test.ts +62 -51
- package/src/__tests__/plugin-registry.test.ts +0 -27
- package/src/__tests__/plugin-route-contribution.test.ts +6 -16
- package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
- package/src/__tests__/plugin-types.test.ts +8 -173
- package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
- package/src/__tests__/process-message-background-slack.test.ts +17 -16
- package/src/__tests__/process-message-display-content.test.ts +36 -44
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
- package/src/__tests__/provider-error-scenarios.test.ts +7 -6
- package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
- package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
- package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
- package/src/__tests__/provider-usage-tracking.test.ts +0 -6
- package/src/__tests__/ratelimit.test.ts +9 -4
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/relay-server.test.ts +20 -13
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
- package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
- package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +390 -0
- package/src/__tests__/schedule-routes.test.ts +683 -12
- package/src/__tests__/schedule-store.test.ts +108 -0
- package/src/__tests__/schedule-tools.test.ts +160 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
- package/src/__tests__/secret-response-routing.test.ts +13 -11
- package/src/__tests__/send-endpoint-busy.test.ts +6 -2
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +44 -11
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/starter-task-flow.test.ts +6 -6
- package/src/__tests__/strip-memory-injections.test.ts +102 -14
- package/src/__tests__/subagent-call-site-routing.test.ts +3 -3
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +74 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +9 -25
- package/src/__tests__/thread-backfill.test.ts +4 -9
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +481 -16
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +1 -1
- package/src/__tests__/tools-audio-read.test.ts +113 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
- package/src/__tests__/turn-events-store.test.ts +11 -7
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +2 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
- package/src/__tests__/voice-session-bridge.test.ts +19 -10
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +695 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +125 -0
- package/src/acp/auto-install.ts +174 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +52 -11
- package/src/acp/resolve-agent.test.ts +147 -6
- package/src/acp/resolve-agent.ts +81 -7
- package/src/acp/resume-hint.ts +22 -0
- package/src/acp/session-manager.ts +487 -71
- package/src/agent/compaction-circuit.ts +98 -0
- package/src/agent/loop.ts +651 -450
- package/src/api/README.md +19 -17
- package/src/api/constants/tool-execution.ts +21 -0
- package/src/api/events/assistant-activity-state.ts +75 -0
- package/src/api/events/assistant-outbound-attachment.ts +25 -27
- package/src/api/events/assistant-text-delta.ts +6 -8
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/assistant-turn-start.ts +5 -7
- package/src/api/events/avatar-updated.ts +24 -0
- package/src/api/events/compaction-circuit-closed.ts +26 -0
- package/src/api/events/compaction-circuit-open.ts +28 -0
- package/src/api/events/confirmation-request.ts +114 -0
- package/src/api/events/contact-request.ts +33 -0
- package/src/api/events/conversation-error.ts +77 -0
- package/src/api/events/conversation-list-invalidated.ts +38 -0
- package/src/api/events/conversation-title-updated.ts +24 -0
- package/src/api/events/disk-pressure-status-changed.ts +61 -0
- package/src/api/events/document-comment-created.ts +24 -28
- package/src/api/events/document-comment-deleted.ts +6 -8
- package/src/api/events/document-comment-reopened.ts +6 -8
- package/src/api/events/document-comment-resolved.ts +8 -10
- package/src/api/events/document-editor-update.ts +27 -0
- package/src/api/events/error.ts +32 -0
- package/src/api/events/generation-cancelled.ts +4 -6
- package/src/api/events/generation-handoff.ts +13 -15
- package/src/api/events/home-feed-updated.ts +26 -0
- package/src/api/events/identity-changed.ts +32 -0
- package/src/api/events/interaction-resolved.ts +50 -0
- package/src/api/events/message-complete.ts +10 -12
- package/src/api/events/message-dequeued.ts +21 -0
- package/src/api/events/message-queued-deleted.ts +23 -0
- package/src/api/events/message-queued.ts +22 -0
- package/src/api/events/message-request-complete.ts +29 -0
- package/src/api/events/navigate-settings.ts +20 -0
- package/src/api/events/notification-intent.ts +33 -0
- package/src/api/events/open-url.ts +6 -8
- package/src/api/events/question-request.ts +67 -0
- package/src/api/events/relationship-state-updated.ts +4 -6
- package/src/api/events/secret-request.ts +42 -0
- package/src/api/events/subagent-event.ts +79 -0
- package/src/api/events/subagent-spawned.ts +40 -0
- package/src/api/events/subagent-status-changed.ts +65 -0
- package/src/api/events/sync-changed.ts +29 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-result.ts +129 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/tool-use-start.ts +8 -10
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/events/turn-profile-auto-routed.ts +28 -0
- package/src/api/events/ui-surface-complete.ts +30 -0
- package/src/api/events/ui-surface-dismiss.ts +22 -0
- package/src/api/events/ui-surface-show.ts +67 -0
- package/src/api/events/ui-surface-update.ts +26 -0
- package/src/api/events/usage-update.ts +34 -0
- package/src/api/events/user-message-echo.ts +35 -0
- package/src/api/index.ts +389 -0
- package/src/api/requests/dictation.ts +45 -0
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/api/responses/disk-pressure-status.ts +26 -0
- package/src/api/responses/home.ts +217 -0
- package/src/api/responses/llm-context-response.ts +2 -0
- package/src/api/responses/memory-v3-selection-log.ts +50 -0
- package/src/api/responses/subagent-detail.ts +48 -0
- package/src/approvals/guardian-decision-primitive.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +7 -10
- package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
- package/src/avatar/__tests__/avatar-store.test.ts +198 -0
- package/src/avatar/avatar-manifest.ts +195 -0
- package/src/avatar/avatar-store.ts +113 -0
- package/src/avatar/traits-png-sync.ts +8 -2
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +5 -1
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +13 -27
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/conversations.ts +15 -1
- package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
- package/src/cli/commands/db/__tests__/status.test.ts +253 -0
- package/src/cli/commands/db/format.ts +48 -0
- package/src/cli/commands/db/index.ts +29 -0
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
- package/src/cli/commands/db/repair-step-integrity.ts +146 -0
- package/src/cli/commands/db/repair-steps.ts +164 -0
- package/src/cli/commands/db/repair.ts +141 -0
- package/src/cli/commands/db/status.ts +366 -0
- package/src/cli/commands/memory-v3.ts +159 -445
- package/src/cli/commands/notifications.ts +112 -60
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/program.ts +4 -5
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +4 -4
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +24 -13
- package/src/config/bundled-skills/acp/SKILL.md +64 -30
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -387
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +2 -7
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +68 -12
- package/src/config/schemas/__tests__/memory-v2.test.ts +2 -226
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/call-site-catalog.ts +8 -15
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +3 -3
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +8 -253
- package/src/config/schemas/memory-v3.ts +47 -0
- package/src/config/schemas/memory.ts +6 -1
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +55 -32
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +42 -0
- package/src/context/tool-result-truncation.ts +1 -66
- package/src/context/window-manager.ts +141 -26
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
- package/src/daemon/approval-generators.ts +4 -4
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +7 -1
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +793 -215
- package/src/daemon/conversation-agent-loop.ts +487 -1478
- package/src/daemon/conversation-error.ts +7 -7
- package/src/daemon/conversation-history.ts +27 -10
- package/src/daemon/conversation-launch.ts +4 -8
- package/src/daemon/conversation-lifecycle.ts +13 -42
- package/src/daemon/conversation-messaging.ts +8 -9
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +109 -93
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +209 -382
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +15 -95
- package/src/daemon/conversation-surfaces.ts +277 -73
- package/src/daemon/conversation-tool-setup.ts +5 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +123 -146
- package/src/daemon/daemon-skill-host.ts +2 -6
- package/src/daemon/disk-pressure-guard.ts +35 -29
- package/src/daemon/external-plugins-bootstrap.ts +53 -32
- package/src/daemon/first-greeting.ts +26 -4
- package/src/daemon/guardian-action-generators.ts +2 -2
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +9 -23
- package/src/daemon/handlers/shared.ts +158 -82
- package/src/daemon/handlers/skills.ts +53 -20
- package/src/daemon/host-app-control-proxy.ts +54 -1
- package/src/daemon/host-cu-proxy.ts +46 -22
- package/src/daemon/host-file-proxy.ts +25 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -6
- package/src/daemon/lifecycle.ts +53 -55
- package/src/daemon/message-protocol.ts +2 -3
- package/src/daemon/message-provenance.ts +49 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/contacts.ts +3 -20
- package/src/daemon/message-types/conversations.ts +13 -111
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +4 -17
- package/src/daemon/message-types/integrations.ts +2 -6
- package/src/daemon/message-types/messages.ts +37 -400
- package/src/daemon/message-types/notifications.ts +2 -32
- package/src/daemon/message-types/settings.ts +3 -8
- package/src/daemon/message-types/skills.ts +4 -0
- package/src/daemon/message-types/surfaces.ts +138 -3
- package/src/daemon/message-types/sync.ts +12 -25
- package/src/daemon/message-types/workspace.ts +3 -11
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +50 -49
- package/src/daemon/server.ts +14 -0
- package/src/daemon/tool-side-effects.ts +10 -7
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +32 -0
- package/src/daemon/wake-target-adapter.ts +14 -2
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +6 -1
- package/src/heartbeat/heartbeat-run-store.ts +54 -1
- package/src/heartbeat/heartbeat-service.ts +42 -0
- package/src/home/feed-types.ts +36 -221
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/email-ipc.test.ts +0 -9
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
- package/src/ipc/routes/route-adapter.ts +45 -6
- package/src/ipc/skill-routes/__tests__/memory.test.ts +33 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
- package/src/ipc/skill-routes/memory.ts +29 -14
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +13 -61
- package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/conversation-queries.test.ts +192 -8
- package/src/memory/__tests__/db-maintenance.test.ts +128 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
- package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/context-search/agent-runner.ts +2 -4
- package/src/memory/conversation-crud.ts +39 -8
- package/src/memory/conversation-queries.ts +78 -22
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +14 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/consolidation.ts +8 -11
- package/src/memory/graph/conversation-graph-memory.ts +106 -8
- package/src/memory/graph/extraction.ts +6 -9
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.ts +20 -26
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/job-handlers/conversation-starters.ts +45 -34
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs-store.ts +36 -1
- package/src/memory/jobs-worker.ts +82 -43
- package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
- package/src/memory/llm-request-log-source-local.ts +0 -11
- package/src/memory/llm-request-log-source.ts +9 -25
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +234 -50
- package/src/memory/memory-marker.ts +17 -0
- package/src/memory/memory-retrospective-job.ts +6 -2
- package/src/memory/memory-v2-activation-log-store.ts +1 -83
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
- package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
- package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
- package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +27 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +125 -1
- package/src/memory/v2/__tests__/migration.test.ts +11 -3
- package/src/memory/v2/__tests__/page-index.test.ts +37 -1
- package/src/memory/v2/__tests__/router.test.ts +14 -4
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +99 -10
- package/src/memory/v2/migration.ts +5 -3
- package/src/memory/v2/page-index.ts +11 -0
- package/src/memory/v2/router.ts +8 -11
- package/src/memory/v2/sweep-job.ts +8 -11
- package/src/memory/v2/types.ts +1 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
- package/src/messaging/providers/slack/render-transcript.ts +2 -2
- package/src/messaging/style-analyzer.ts +8 -11
- package/src/notifications/conversation-pairing.ts +8 -13
- package/src/notifications/decision-engine.ts +16 -16
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/notifications/preference-extractor.ts +11 -14
- package/src/permissions/prompter.ts +46 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +10 -1
- package/src/plugin-api/types.ts +176 -4
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +15 -0
- package/src/plugins/defaults/compaction/register.ts +24 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/package.json +15 -0
- package/src/plugins/defaults/empty-response/register.ts +23 -0
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/history-repair/package.json +15 -0
- package/src/plugins/defaults/history-repair/register.ts +24 -0
- package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
- package/src/plugins/defaults/index.ts +22 -49
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors.ts → memory-retrieval/injectors.ts} +295 -112
- package/src/plugins/defaults/memory-v3-shadow/__tests__/assign.test.ts +242 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +118 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/core.test.ts +39 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +36 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +37 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/health.test.ts +219 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +330 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +288 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/needle.test.ts +107 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +436 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/reconcile.test.ts +274 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +61 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/router.test.ts +332 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +179 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selector.test.ts +470 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +432 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/snapshot.test.ts +168 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/tree.test.ts +192 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +54 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +106 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +44 -0
- package/src/plugins/defaults/memory-v3-shadow/assign.ts +268 -0
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +124 -0
- package/src/plugins/defaults/memory-v3-shadow/core.ts +26 -0
- package/src/plugins/defaults/memory-v3-shadow/data/README.md +84 -0
- package/src/plugins/defaults/memory-v3-shadow/data/assignments.json +5 -0
- package/src/plugins/defaults/memory-v3-shadow/data/core.json +1 -0
- package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-x.md +9 -0
- package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-y.md +9 -0
- package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-b/topic-z.md +9 -0
- package/src/plugins/defaults/memory-v3-shadow/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +314 -0
- package/src/plugins/defaults/memory-v3-shadow/needle.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +126 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +15 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +34 -0
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/plugins/defaults/memory-v3-shadow/reconcile.ts +523 -0
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +32 -0
- package/src/plugins/defaults/memory-v3-shadow/router.ts +190 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +84 -0
- package/src/plugins/defaults/memory-v3-shadow/selector.ts +226 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +349 -0
- package/src/plugins/defaults/memory-v3-shadow/snapshot.ts +209 -0
- package/src/plugins/defaults/memory-v3-shadow/tree.ts +174 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +88 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +15 -0
- package/src/plugins/defaults/title-generate/register.ts +35 -0
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +15 -0
- package/src/plugins/defaults/tool-error/register.ts +23 -0
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +24 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +132 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/pipeline.ts +8 -35
- package/src/plugins/registry.ts +8 -25
- package/src/plugins/types.ts +62 -721
- package/src/plugins/user-loader.ts +4 -3
- package/src/proactive-artifact/aux-message-injector.ts +4 -5
- package/src/proactive-artifact/job.test.ts +28 -21
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
- package/src/prompts/templates/BOOTSTRAP.md +7 -3
- package/src/prompts/templates/system-sections.ts +21 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -25
- package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
- package/src/providers/anthropic/client.ts +61 -34
- package/src/providers/call-site-routing.ts +1 -9
- package/src/providers/gemini/client.ts +152 -34
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -2
- package/src/providers/openai/chat-completions-provider.ts +45 -4
- package/src/providers/openai/responses-provider.ts +1 -4
- package/src/providers/openrouter/client.ts +2 -6
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/providers/provider-send-message.ts +6 -6
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/retry.ts +0 -5
- package/src/providers/types.ts +11 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +141 -32
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +95 -23
- package/src/runtime/assistant-event-hub.ts +38 -8
- package/src/runtime/assistant-stream-state.ts +368 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
- package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
- package/src/runtime/auth/route-policy.ts +42 -1079
- package/src/runtime/background-job-runner.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -1
- package/src/runtime/channel-approvals.ts +4 -15
- package/src/runtime/channel-invite-transport.ts +5 -6
- package/src/runtime/channel-readiness-service.ts +2 -5
- package/src/runtime/channel-retry-sweep.ts +12 -16
- package/src/runtime/http-router.ts +35 -43
- package/src/runtime/http-types.ts +23 -71
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/invite-instruction-generator.ts +3 -3
- package/src/runtime/pending-interactions.ts +3 -2
- package/src/runtime/routes/__tests__/acp-routes.test.ts +253 -55
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
- package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +9 -5
- package/src/runtime/routes/acp-routes.test.ts +186 -100
- package/src/runtime/routes/acp-routes.ts +110 -35
- package/src/runtime/routes/app-management-routes.ts +93 -131
- package/src/runtime/routes/app-routes.ts +38 -20
- package/src/runtime/routes/approval-routes.ts +17 -5
- package/src/runtime/routes/attachment-routes.ts +51 -16
- package/src/runtime/routes/audio-routes.ts +1 -0
- package/src/runtime/routes/audit-routes.ts +5 -0
- package/src/runtime/routes/auth-routes.ts +5 -0
- package/src/runtime/routes/avatar-routes.ts +264 -59
- package/src/runtime/routes/background-tool-routes.ts +9 -0
- package/src/runtime/routes/background-wake-routes.ts +13 -3
- package/src/runtime/routes/backup-routes.ts +45 -0
- package/src/runtime/routes/bookmark-routes.ts +13 -0
- package/src/runtime/routes/brain-graph-routes.ts +9 -0
- package/src/runtime/routes/browser-routes.ts +6 -1
- package/src/runtime/routes/browser-tabs-routes.ts +11 -10
- package/src/runtime/routes/btw-routes.ts +34 -24
- package/src/runtime/routes/cache-routes.ts +13 -0
- package/src/runtime/routes/call-routes.ts +21 -10
- package/src/runtime/routes/channel-availability-routes.ts +5 -1
- package/src/runtime/routes/channel-readiness-routes.ts +37 -4
- package/src/runtime/routes/channel-route-definitions.ts +21 -0
- package/src/runtime/routes/channel-verification-routes.ts +21 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
- package/src/runtime/routes/client-routes.ts +9 -0
- package/src/runtime/routes/consolidation-routes.ts +133 -25
- package/src/runtime/routes/contact-prompt-routes.ts +9 -0
- package/src/runtime/routes/contact-routes.ts +90 -23
- package/src/runtime/routes/content-source-routes.ts +5 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
- package/src/runtime/routes/conversation-attention-routes.ts +5 -0
- package/src/runtime/routes/conversation-cli-routes.ts +54 -7
- package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
- package/src/runtime/routes/conversation-list-routes.ts +81 -12
- package/src/runtime/routes/conversation-management-routes.ts +57 -14
- package/src/runtime/routes/conversation-query-routes.ts +90 -41
- package/src/runtime/routes/conversation-routes.ts +446 -204
- package/src/runtime/routes/conversation-starter-routes.ts +35 -20
- package/src/runtime/routes/conversations-import-routes.ts +30 -8
- package/src/runtime/routes/credential-prompt-routes.ts +5 -0
- package/src/runtime/routes/credential-routes.ts +25 -6
- package/src/runtime/routes/debug-bash-routes.ts +5 -0
- package/src/runtime/routes/debug-routes.ts +11 -2
- package/src/runtime/routes/defer-routes.ts +13 -0
- package/src/runtime/routes/diagnostics-routes.ts +37 -46
- package/src/runtime/routes/disk-pressure-routes.ts +17 -31
- package/src/runtime/routes/document-comments-routes.ts +46 -27
- package/src/runtime/routes/documents-routes.ts +25 -10
- package/src/runtime/routes/domain-routes.ts +98 -51
- package/src/runtime/routes/email-routes.ts +33 -0
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +107 -8
- package/src/runtime/routes/filing-routes.ts +9 -4
- package/src/runtime/routes/gateway-log-routes.ts +31 -4
- package/src/runtime/routes/global-search-routes.ts +53 -50
- package/src/runtime/routes/group-routes.ts +21 -5
- package/src/runtime/routes/guardian-action-routes.ts +9 -0
- package/src/runtime/routes/guardian-approval-interception.ts +0 -31
- package/src/runtime/routes/heartbeat-routes.ts +57 -21
- package/src/runtime/routes/home-feed-routes.ts +23 -19
- package/src/runtime/routes/home-state-routes.ts +8 -40
- package/src/runtime/routes/host-app-control-routes.ts +6 -1
- package/src/runtime/routes/host-bash-routes.ts +5 -0
- package/src/runtime/routes/host-browser-routes.ts +13 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -1
- package/src/runtime/routes/host-file-routes.ts +26 -6
- package/src/runtime/routes/host-transfer-routes.ts +13 -2
- package/src/runtime/routes/http-adapter.ts +1 -2
- package/src/runtime/routes/identity-intro-cache.ts +28 -40
- package/src/runtime/routes/identity-routes.ts +236 -20
- package/src/runtime/routes/image-generation-routes.ts +45 -2
- package/src/runtime/routes/inbound-message-handler.ts +16 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
- package/src/runtime/routes/inference-send-routes.ts +11 -11
- package/src/runtime/routes/integrations/a2a.ts +32 -7
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +23 -3
- package/src/runtime/routes/integrations/slack/share.ts +36 -8
- package/src/runtime/routes/integrations/telegram.ts +34 -9
- package/src/runtime/routes/integrations/twilio.ts +77 -7
- package/src/runtime/routes/integrations/vercel.ts +3 -3
- package/src/runtime/routes/internal-oauth-routes.ts +5 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/internal-twilio-routes.ts +13 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
- package/src/runtime/routes/log-export-routes.ts +36 -10
- package/src/runtime/routes/mcp-auth-routes.ts +25 -0
- package/src/runtime/routes/memory-item-routes.ts +21 -10
- package/src/runtime/routes/memory-v2-routes.ts +105 -44
- package/src/runtime/routes/memory-v3-routes.ts +306 -408
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +29 -0
- package/src/runtime/routes/notification-routes.ts +17 -1
- package/src/runtime/routes/oauth-apps.ts +99 -23
- package/src/runtime/routes/oauth-commands-routes.ts +37 -14
- package/src/runtime/routes/oauth-connect-routes.ts +9 -0
- package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
- package/src/runtime/routes/oauth-providers.ts +79 -15
- package/src/runtime/routes/platform-routes.ts +102 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +9 -6
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
- package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
- package/src/runtime/routes/playground/force-compact.ts +2 -2
- package/src/runtime/routes/playground/helpers.ts +1 -2
- package/src/runtime/routes/playground/inject-failures.ts +13 -8
- package/src/runtime/routes/playground/reset-circuit.ts +14 -9
- package/src/runtime/routes/playground/seed-conversation.ts +1 -1
- package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
- package/src/runtime/routes/playground/state.ts +4 -3
- package/src/runtime/routes/plugins-routes.ts +22 -19
- package/src/runtime/routes/profiler-routes.ts +17 -4
- package/src/runtime/routes/ps-routes.ts +5 -0
- package/src/runtime/routes/publish-routes.ts +13 -3
- package/src/runtime/routes/question-routes.ts +5 -0
- package/src/runtime/routes/recording-routes.ts +25 -12
- package/src/runtime/routes/rename-conversation-routes.ts +10 -0
- package/src/runtime/routes/sanity-routes.ts +9 -2
- package/src/runtime/routes/schedule-routes.ts +288 -88
- package/src/runtime/routes/secret-routes.ts +31 -6
- package/src/runtime/routes/sequence-routes.ts +33 -0
- package/src/runtime/routes/settings-routes.ts +65 -19
- package/src/runtime/routes/skills-routes.ts +166 -73
- package/src/runtime/routes/slack-channel-routes.ts +5 -0
- package/src/runtime/routes/stt-routes.ts +13 -6
- package/src/runtime/routes/subagents-routes.ts +24 -18
- package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
- package/src/runtime/routes/surface-action-routes.ts +9 -0
- package/src/runtime/routes/surface-content-routes.ts +10 -2
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/task-routes.ts +37 -0
- package/src/runtime/routes/telemetry-routes.ts +9 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trace-event-routes.ts +42 -1
- package/src/runtime/routes/trust-rules-routes.ts +31 -2
- package/src/runtime/routes/tts-routes.ts +48 -6
- package/src/runtime/routes/types.ts +83 -16
- package/src/runtime/routes/ui-request-routes.ts +5 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
- package/src/runtime/routes/usage-routes.ts +118 -42
- package/src/runtime/routes/user-routes-cli.ts +9 -0
- package/src/runtime/routes/user-routes.ts +5 -1
- package/src/runtime/routes/wake-conversation-routes.ts +5 -0
- package/src/runtime/routes/watcher-routes.ts +21 -0
- package/src/runtime/routes/webhook-routes.ts +50 -2
- package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
- package/src/runtime/routes/work-items-routes.ts +49 -23
- package/src/runtime/routes/workspace-commit-routes.ts +5 -0
- package/src/runtime/routes/workspace-routes.test.ts +42 -0
- package/src/runtime/routes/workspace-routes.ts +124 -9
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +8 -4
- package/src/runtime/services/analyze-conversation.ts +5 -8
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/sync/resource-sync-events.ts +16 -2
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +28 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +15 -6
- package/src/signals/cancel.ts +2 -4
- package/src/signals/user-message.ts +5 -8
- package/src/skills/catalog-files.ts +4 -1
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -0
- package/src/skills/skillssh-files.ts +1 -0
- package/src/subagent/manager.ts +20 -11
- package/src/telemetry/types.ts +55 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +250 -4
- package/src/telemetry/usage-telemetry-reporter.ts +88 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +198 -93
- package/src/tools/acp/spawn.ts +32 -70
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/definitions.ts +8 -4
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
- package/src/tools/ask-question/ask-question-tool.ts +85 -90
- package/src/tools/computer-use/definitions.ts +28 -24
- package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
- package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
- package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
- package/src/tools/credentials/vault.ts +112 -111
- package/src/tools/execution-target.ts +1 -1
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/edit.ts +45 -42
- package/src/tools/filesystem/list.ts +33 -30
- package/src/tools/filesystem/read.ts +54 -35
- package/src/tools/filesystem/write.ts +69 -32
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-fetch.ts +49 -46
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +223 -61
- package/src/tools/registry.ts +39 -16
- package/src/tools/schedule/create.ts +13 -0
- package/src/tools/schedule/update.ts +16 -0
- package/src/tools/shared/filesystem/audio-read.ts +122 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/execute.ts +34 -31
- package/src/tools/skills/load.ts +29 -23
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/system/avatar-generator.ts +13 -22
- package/src/tools/system/request-permission.ts +30 -27
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/terminal/shell.ts +190 -61
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +74 -23
- package/src/tools/ui-surface/definitions.ts +99 -10
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/usage/types.ts +10 -0
- package/src/util/errors.ts +2 -2
- package/src/util/map-limit.ts +27 -0
- package/src/util/mutex.ts +47 -0
- package/src/util/platform.ts +15 -12
- package/src/work-items/work-item-runner.ts +7 -2
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
- package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
- package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
- package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
- package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/src/workspace/provider-commit-message-generator.ts +15 -17
- package/tsconfig.json +4 -1
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -262
- package/src/__tests__/empty-response-pipeline.test.ts +0 -301
- package/src/__tests__/history-repair-pipeline.test.ts +0 -396
- package/src/__tests__/llm-call-pipeline.test.ts +0 -281
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -514
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -481
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -344
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
- package/src/cli/commands/memory-v3-render.ts +0 -491
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/daemon/message-types/disk-pressure.ts +0 -9
- package/src/email/feature-gate.ts +0 -23
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
- package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -706
- package/src/memory/v3/__tests__/filter.test.ts +0 -560
- package/src/memory/v3/__tests__/gate.test.ts +0 -637
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
- package/src/memory/v3/__tests__/loop.test.ts +0 -775
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -489
- package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -508
- package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
- package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
- package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
- package/src/memory/v3/__tests__/validate.test.ts +0 -277
- package/src/memory/v3/auto-edges.ts +0 -223
- package/src/memory/v3/coactivation-store.ts +0 -124
- package/src/memory/v3/consolidation-job.ts +0 -323
- package/src/memory/v3/coretrieval-seed.ts +0 -240
- package/src/memory/v3/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -286
- package/src/memory/v3/filter.ts +0 -286
- package/src/memory/v3/gate.ts +0 -349
- package/src/memory/v3/index-composition.ts +0 -126
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -430
- package/src/memory/v3/maintenance.ts +0 -144
- package/src/memory/v3/prompt-context.ts +0 -33
- package/src/memory/v3/prompts/consolidation.ts +0 -458
- package/src/memory/v3/prompts/system-prompts.ts +0 -196
- package/src/memory/v3/retriever.ts +0 -33
- package/src/memory/v3/scouts.ts +0 -431
- package/src/memory/v3/shadow-diff.ts +0 -287
- package/src/memory/v3/shadow-middleware.ts +0 -347
- package/src/memory/v3/traversal.ts +0 -211
- package/src/memory/v3/tree-index.ts +0 -237
- package/src/memory/v3/tree-store.ts +0 -394
- package/src/memory/v3/tree-walk.ts +0 -356
- package/src/memory/v3/types.ts +0 -65
- package/src/memory/v3/validate.ts +0 -323
- package/src/plugins/defaults/circuit-breaker.ts +0 -141
- package/src/plugins/defaults/compaction.ts +0 -141
- package/src/plugins/defaults/empty-response.ts +0 -124
- package/src/plugins/defaults/history-repair.ts +0 -83
- package/src/plugins/defaults/llm-call.ts +0 -77
- package/src/plugins/defaults/memory-retrieval.ts +0 -219
- package/src/plugins/defaults/overflow-reduce.ts +0 -185
- package/src/plugins/defaults/persistence.ts +0 -146
- package/src/plugins/defaults/title-generate.ts +0 -90
- package/src/plugins/defaults/token-estimate.ts +0 -101
- package/src/plugins/defaults/tool-error.ts +0 -119
- package/src/plugins/defaults/tool-execute.ts +0 -87
- package/src/plugins/defaults/tool-result-truncate.ts +0 -84
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
- package/src/skills/category-inference.ts +0 -111
|
@@ -14,15 +14,11 @@
|
|
|
14
14
|
import { createRequire } from "node:module";
|
|
15
15
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
16
16
|
|
|
17
|
-
import type {
|
|
18
|
-
AgentEvent,
|
|
19
|
-
CheckpointDecision,
|
|
20
|
-
CheckpointInfo,
|
|
21
|
-
} from "../agent/loop.js";
|
|
17
|
+
import type { LoopToolExecutor } from "../agent/loop.js";
|
|
22
18
|
import type { LLMConfig } from "../config/schemas/llm.js";
|
|
23
19
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
24
20
|
import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
|
|
25
|
-
import type {
|
|
21
|
+
import type { Message, Provider, ToolDefinition } from "../providers/types.js";
|
|
26
22
|
|
|
27
23
|
const conversationCrudRealSnapshot = {
|
|
28
24
|
...(createRequire(import.meta.url)(
|
|
@@ -91,6 +87,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
91
87
|
memory: { retrieval: { scratchpadInjection: { enabled: true } } },
|
|
92
88
|
ui: {},
|
|
93
89
|
compaction: { enabled: true, autoThreshold: 0.7 },
|
|
90
|
+
conversations: { skipAutoRetitling: true },
|
|
94
91
|
}),
|
|
95
92
|
loadRawConfig: () => ({}),
|
|
96
93
|
saveRawConfig: () => {},
|
|
@@ -102,10 +99,10 @@ mock.module("../config/loader.js", () => ({
|
|
|
102
99
|
// Token estimator — controllable per-test via mockEstimateTokens.
|
|
103
100
|
// Can be a number (constant), a no-arg function, or a function that
|
|
104
101
|
// receives the messages array for dynamic behavior based on content.
|
|
105
|
-
// Both the calibrated entry point (`estimatePromptTokens`,
|
|
106
|
-
// convergence path) and the raw entry point
|
|
107
|
-
// used by the
|
|
108
|
-
//
|
|
102
|
+
// Both the calibrated entry point (`estimatePromptTokens`, which backs the
|
|
103
|
+
// preflight overflow gate and the convergence path) and the raw entry point
|
|
104
|
+
// (`estimatePromptTokensRaw`, used by the pre-send calibration capture) are
|
|
105
|
+
// stubbed so either call site can drive the test.
|
|
109
106
|
let mockEstimateTokens: number | ((msgs?: Message[]) => number) = 1000;
|
|
110
107
|
mock.module("../context/token-estimator.js", () => ({
|
|
111
108
|
estimatePromptTokens: (msgs: Message[]) =>
|
|
@@ -116,8 +113,16 @@ mock.module("../context/token-estimator.js", () => ({
|
|
|
116
113
|
typeof mockEstimateTokens === "function"
|
|
117
114
|
? mockEstimateTokens(msgs)
|
|
118
115
|
: mockEstimateTokens,
|
|
119
|
-
//
|
|
120
|
-
//
|
|
116
|
+
// The preflight overflow gate calls this calibrated wrapper directly, so it
|
|
117
|
+
// must honor `mockEstimateTokens` too — otherwise the real implementation
|
|
118
|
+
// (which sums tool tokens onto the real calibrated estimate) ignores the
|
|
119
|
+
// per-test value and the overflow scenarios below never trigger.
|
|
120
|
+
estimatePromptTokensWithTools: (history: Message[]) =>
|
|
121
|
+
typeof mockEstimateTokens === "function"
|
|
122
|
+
? mockEstimateTokens(history)
|
|
123
|
+
: mockEstimateTokens,
|
|
124
|
+
// `estimatePromptTokensWithTools` folds tool tokens in via this helper; 0
|
|
125
|
+
// keeps the stubbed value unchanged.
|
|
121
126
|
estimateToolsTokens: () => 0,
|
|
122
127
|
// Conversation agent loop now calls this helper to canonicalize the
|
|
123
128
|
// provider key shared with the calibration system. The tests here
|
|
@@ -269,15 +274,6 @@ mock.module("../daemon/conversation-runtime-assembly.js", () => ({
|
|
|
269
274
|
blocks: {},
|
|
270
275
|
}),
|
|
271
276
|
stripInjectionsForCompaction: (msgs: Message[]) => msgs,
|
|
272
|
-
findLastInjectedNowContent: () => null,
|
|
273
|
-
readNowScratchpad: () => null,
|
|
274
|
-
readPkbContext: () => null,
|
|
275
|
-
getPkbAutoInjectList: () => [
|
|
276
|
-
"INDEX.md",
|
|
277
|
-
"essentials.md",
|
|
278
|
-
"threads.md",
|
|
279
|
-
"buffer.md",
|
|
280
|
-
],
|
|
281
277
|
isSlackChannelConversation: () => false,
|
|
282
278
|
getSlackCompactionWatermarkForPrefix: () => null,
|
|
283
279
|
loadSlackChronologicalContext: () => null,
|
|
@@ -291,7 +287,7 @@ mock.module("../daemon/date-context.js", () => ({
|
|
|
291
287
|
formatTurnTimestamp: () => "2026-01-01 (Thursday) 00:00:00 +00:00 (UTC)",
|
|
292
288
|
}));
|
|
293
289
|
|
|
294
|
-
mock.module("../
|
|
290
|
+
mock.module("../plugins/defaults/history-repair/terminal.js", () => ({
|
|
295
291
|
repairHistory: (msgs: Message[]) => ({
|
|
296
292
|
messages: msgs,
|
|
297
293
|
stats: {
|
|
@@ -425,37 +421,55 @@ mock.module("../memory/archive-store.js", () => ({
|
|
|
425
421
|
|
|
426
422
|
// ── Imports (after mocks) ────────────────────────────────────────────
|
|
427
423
|
|
|
424
|
+
import { AgentLoop } from "../agent/loop.js";
|
|
428
425
|
import {
|
|
429
426
|
type AgentLoopConversationContext,
|
|
430
427
|
runAgentLoopImpl,
|
|
431
428
|
} from "../daemon/conversation-agent-loop.js";
|
|
429
|
+
import {
|
|
430
|
+
createMockProvider,
|
|
431
|
+
type ScriptedResponse,
|
|
432
|
+
textResponse,
|
|
433
|
+
toolUseResponse,
|
|
434
|
+
} from "./helpers/mock-provider.js";
|
|
432
435
|
|
|
433
436
|
// ── Test helpers ─────────────────────────────────────────────────────
|
|
434
437
|
|
|
435
|
-
type AgentLoopRun = (
|
|
436
|
-
messages: Message[],
|
|
437
|
-
onEvent: (event: AgentEvent) => void,
|
|
438
|
-
signal?: AbortSignal,
|
|
439
|
-
requestId?: string,
|
|
440
|
-
onCheckpoint?: (
|
|
441
|
-
checkpoint: CheckpointInfo,
|
|
442
|
-
) => CheckpointDecision | Promise<CheckpointDecision>,
|
|
443
|
-
) => Promise<Message[]>;
|
|
444
|
-
|
|
445
438
|
function makeCtx(
|
|
446
439
|
overrides?: Partial<AgentLoopConversationContext> & {
|
|
447
|
-
|
|
440
|
+
providerResponses?: ScriptedResponse[];
|
|
441
|
+
loopProvider?: Provider;
|
|
442
|
+
loopTools?: ToolDefinition[];
|
|
443
|
+
toolExecutor?: LoopToolExecutor;
|
|
448
444
|
},
|
|
449
445
|
): AgentLoopConversationContext {
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
446
|
+
const {
|
|
447
|
+
providerResponses,
|
|
448
|
+
loopProvider,
|
|
449
|
+
loopTools,
|
|
450
|
+
toolExecutor,
|
|
451
|
+
...ctxOverrides
|
|
452
|
+
} = overrides ?? {};
|
|
453
|
+
const conversationId = ctxOverrides.conversationId ?? "test-conv";
|
|
454
|
+
|
|
455
|
+
// Drive the real `AgentLoop` against a scripted provider, mocking only the
|
|
456
|
+
// provider HTTP boundary. The loop owns its mid-loop budget gate, inline
|
|
457
|
+
// compaction, and event emission, so these overflow tests exercise the real
|
|
458
|
+
// escalation/persistence path.
|
|
459
|
+
const loopProviderName =
|
|
460
|
+
(ctxOverrides.provider as { name?: string } | undefined)?.name ??
|
|
461
|
+
"mock-provider";
|
|
462
|
+
const provider =
|
|
463
|
+
loopProvider ??
|
|
464
|
+
createMockProvider(
|
|
465
|
+
providerResponses ?? [textResponse("response")],
|
|
466
|
+
loopProviderName,
|
|
467
|
+
).provider;
|
|
468
|
+
const agentLoop = new AgentLoop(provider, "system prompt", {
|
|
469
|
+
conversationId,
|
|
470
|
+
tools: loopTools ?? [],
|
|
471
|
+
toolExecutor,
|
|
472
|
+
});
|
|
459
473
|
|
|
460
474
|
return {
|
|
461
475
|
conversationId: "test-conv",
|
|
@@ -463,18 +477,16 @@ function makeCtx(
|
|
|
463
477
|
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
|
464
478
|
] as Message[],
|
|
465
479
|
processing: true,
|
|
480
|
+
isProcessing(this: { processing: boolean }) {
|
|
481
|
+
return this.processing;
|
|
482
|
+
},
|
|
483
|
+
setProcessing(this: { processing: boolean }, value: boolean) {
|
|
484
|
+
this.processing = value;
|
|
485
|
+
},
|
|
466
486
|
abortController: new AbortController(),
|
|
467
487
|
currentRequestId: "test-req",
|
|
468
488
|
|
|
469
|
-
agentLoop
|
|
470
|
-
run: agentLoopRun,
|
|
471
|
-
getToolTokenBudget: () => 0,
|
|
472
|
-
getResolvedTools: () => [],
|
|
473
|
-
// Tests in this file don't exercise calibration, so returning
|
|
474
|
-
// undefined is fine — the estimator falls back to the per-provider
|
|
475
|
-
// aggregate key.
|
|
476
|
-
getActiveModel: () => undefined,
|
|
477
|
-
} as unknown as AgentLoopConversationContext["agentLoop"],
|
|
489
|
+
agentLoop,
|
|
478
490
|
provider: {
|
|
479
491
|
name: "mock-provider",
|
|
480
492
|
sendMessage: async () => ({
|
|
@@ -503,8 +515,6 @@ function makeCtx(
|
|
|
503
515
|
currentTurnSurfaces: [],
|
|
504
516
|
|
|
505
517
|
workingDir: "/tmp",
|
|
506
|
-
workspaceTopLevelContext: null,
|
|
507
|
-
workspaceTopLevelDirty: false,
|
|
508
518
|
channelCapabilities: undefined,
|
|
509
519
|
commandIntent: undefined,
|
|
510
520
|
trustContext: undefined,
|
|
@@ -541,7 +551,6 @@ function makeCtx(
|
|
|
541
551
|
getWorkspaceGitService: () => ({ ensureInitialized: async () => {} }),
|
|
542
552
|
commitTurnChanges: async () => {},
|
|
543
553
|
|
|
544
|
-
refreshWorkspaceTopLevelContextIfNeeded: () => {},
|
|
545
554
|
markWorkspaceTopLevelDirty: () => {},
|
|
546
555
|
emitActivityState: () => {},
|
|
547
556
|
getQueueDepth: () => 0,
|
|
@@ -567,9 +576,10 @@ function makeCtx(
|
|
|
567
576
|
injectedTokens: 0,
|
|
568
577
|
}),
|
|
569
578
|
retrackCachedNodes: () => {},
|
|
579
|
+
recordPkbQueryVectors: () => {},
|
|
570
580
|
} as unknown as AgentLoopConversationContext["graphMemory"],
|
|
571
581
|
|
|
572
|
-
...
|
|
582
|
+
...ctxOverrides,
|
|
573
583
|
} as AgentLoopConversationContext;
|
|
574
584
|
}
|
|
575
585
|
|
|
@@ -638,15 +648,15 @@ beforeEach(() => {
|
|
|
638
648
|
recordUsageMock.mockClear();
|
|
639
649
|
setAgentLoopExitReasonOnLatestLogMock.mockClear();
|
|
640
650
|
addMessageMock.mockClear();
|
|
641
|
-
// Reset the plugin registry and re-register every default so the
|
|
642
|
-
//
|
|
643
|
-
//
|
|
644
|
-
// (`reduceContextOverflow`, `syncMessageToDisk`, …) these tests install.
|
|
651
|
+
// Reset the plugin registry and re-register every default so the compaction
|
|
652
|
+
// pipeline dispatches to the default middleware, which in turn hits the
|
|
653
|
+
// mocked collaborators (`syncMessageToDisk`, …) these tests install.
|
|
645
654
|
resetPluginRegistryAndRegisterDefaults();
|
|
646
655
|
});
|
|
647
656
|
|
|
648
657
|
describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
649
658
|
test("usage update context max follows active main-agent profile budget", async () => {
|
|
659
|
+
// GIVEN an active main-agent profile that narrows the context budget
|
|
650
660
|
mockLlmConfig = {
|
|
651
661
|
...structuredClone(defaultLlmConfig),
|
|
652
662
|
activeProfile: "short-context",
|
|
@@ -658,27 +668,22 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
658
668
|
},
|
|
659
669
|
};
|
|
660
670
|
|
|
671
|
+
// AND a provider turn that reports 12k input tokens of usage
|
|
661
672
|
const ctx = makeCtx({
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
type: "
|
|
665
|
-
inputTokens: 12_000,
|
|
666
|
-
outputTokens: 300,
|
|
673
|
+
providerResponses: [
|
|
674
|
+
{
|
|
675
|
+
content: [{ type: "text", text: "response" }],
|
|
667
676
|
model: "mock-model",
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
{
|
|
673
|
-
role: "assistant" as const,
|
|
674
|
-
content: [{ type: "text" as const, text: "response" }],
|
|
675
|
-
},
|
|
676
|
-
];
|
|
677
|
-
},
|
|
677
|
+
usage: { inputTokens: 12_000, outputTokens: 300 },
|
|
678
|
+
stopReason: "end_turn",
|
|
679
|
+
},
|
|
680
|
+
],
|
|
678
681
|
});
|
|
679
682
|
|
|
683
|
+
// WHEN the turn runs to completion
|
|
680
684
|
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
681
685
|
|
|
686
|
+
// THEN the recorded main-agent usage carries the profile's max budget
|
|
682
687
|
const mainAgentUsageCall = recordUsageMock.mock.calls.find(
|
|
683
688
|
(call) => call[5] === "main_agent",
|
|
684
689
|
);
|
|
@@ -691,10 +696,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
691
696
|
|
|
692
697
|
// ── Test 1 ────────────────────────────────────────────────────────
|
|
693
698
|
// BUG: When the agent loop makes progress (adds messages to history)
|
|
694
|
-
// before hitting context_too_large, the convergence loop
|
|
695
|
-
//
|
|
696
|
-
//
|
|
697
|
-
// invoked — the error is surfaced immediately at line 1163-1175
|
|
699
|
+
// before hitting context_too_large, the convergence loop's progress
|
|
700
|
+
// check must recognize that the loop appended messages. If it fails to,
|
|
701
|
+
// the reducer is never invoked — the error is surfaced immediately
|
|
698
702
|
// without any compaction attempt.
|
|
699
703
|
//
|
|
700
704
|
// Expected behavior (PR 2 fix): After progress + context_too_large,
|
|
@@ -734,125 +738,31 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
734
738
|
};
|
|
735
739
|
};
|
|
736
740
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
// then hits context_too_large on next LLM call
|
|
746
|
-
const progressMessages: Message[] = [
|
|
747
|
-
...messages,
|
|
748
|
-
{
|
|
749
|
-
role: "assistant" as const,
|
|
750
|
-
content: [
|
|
751
|
-
{ type: "text", text: "Let me check that." },
|
|
752
|
-
{
|
|
753
|
-
type: "tool_use",
|
|
754
|
-
id: "tu-progress",
|
|
755
|
-
name: "bash",
|
|
756
|
-
input: { command: "ls" },
|
|
757
|
-
},
|
|
758
|
-
] as ContentBlock[],
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
role: "user" as const,
|
|
762
|
-
content: [
|
|
763
|
-
{
|
|
764
|
-
type: "tool_result",
|
|
765
|
-
tool_use_id: "tu-progress",
|
|
766
|
-
content: "file1.ts\nfile2.ts",
|
|
767
|
-
is_error: false,
|
|
768
|
-
},
|
|
769
|
-
] as ContentBlock[],
|
|
770
|
-
},
|
|
771
|
-
];
|
|
741
|
+
// Run 1 makes progress (a tool turn) then the following provider call
|
|
742
|
+
// rejects with a context_too_large error; after the convergence reducer
|
|
743
|
+
// compacts, the rerun recovers with plain text.
|
|
744
|
+
const { provider } = createMockProvider([
|
|
745
|
+
toolUseResponse("tu-progress", "bash", { command: "ls" }),
|
|
746
|
+
new Error("prompt is too long: 242201 tokens > 200000 maximum"),
|
|
747
|
+
textResponse("recovered after compaction"),
|
|
748
|
+
]);
|
|
772
749
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
750
|
+
const ctx = makeCtx({
|
|
751
|
+
loopProvider: provider,
|
|
752
|
+
loopTools: [
|
|
753
|
+
{
|
|
777
754
|
name: "bash",
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
toolUseId: "tu-progress",
|
|
783
|
-
content: "file1.ts\nfile2.ts",
|
|
784
|
-
isError: false,
|
|
785
|
-
});
|
|
786
|
-
onEvent({
|
|
787
|
-
type: "message_complete",
|
|
788
|
-
message: {
|
|
789
|
-
role: "assistant",
|
|
790
|
-
content: [
|
|
791
|
-
{ type: "text", text: "Let me check that." },
|
|
792
|
-
{
|
|
793
|
-
type: "tool_use",
|
|
794
|
-
id: "tu-progress",
|
|
795
|
-
name: "bash",
|
|
796
|
-
input: { command: "ls" },
|
|
797
|
-
},
|
|
798
|
-
],
|
|
755
|
+
description: "Run a shell command",
|
|
756
|
+
input_schema: {
|
|
757
|
+
type: "object",
|
|
758
|
+
properties: { command: { type: "string" } },
|
|
799
759
|
},
|
|
800
|
-
});
|
|
801
|
-
onEvent({
|
|
802
|
-
type: "usage",
|
|
803
|
-
inputTokens: 100,
|
|
804
|
-
outputTokens: 50,
|
|
805
|
-
model: "test-model",
|
|
806
|
-
providerDurationMs: 100,
|
|
807
|
-
});
|
|
808
|
-
|
|
809
|
-
// Then context_too_large error occurs on the *next* LLM call
|
|
810
|
-
onEvent({
|
|
811
|
-
type: "error",
|
|
812
|
-
error: new Error(
|
|
813
|
-
"prompt is too long: 242201 tokens > 200000 maximum",
|
|
814
|
-
),
|
|
815
|
-
});
|
|
816
|
-
onEvent({
|
|
817
|
-
type: "usage",
|
|
818
|
-
inputTokens: 0,
|
|
819
|
-
outputTokens: 0,
|
|
820
|
-
model: "test-model",
|
|
821
|
-
providerDurationMs: 10,
|
|
822
|
-
});
|
|
823
|
-
|
|
824
|
-
// Return the history WITH progress (more messages than input)
|
|
825
|
-
return progressMessages;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
// Second call (after compaction): succeed
|
|
829
|
-
onEvent({
|
|
830
|
-
type: "message_complete",
|
|
831
|
-
message: {
|
|
832
|
-
role: "assistant",
|
|
833
|
-
content: [{ type: "text", text: "recovered after compaction" }],
|
|
834
760
|
},
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
model: "test-model",
|
|
841
|
-
providerDurationMs: 100,
|
|
842
|
-
});
|
|
843
|
-
return [
|
|
844
|
-
...messages,
|
|
845
|
-
{
|
|
846
|
-
role: "assistant" as const,
|
|
847
|
-
content: [
|
|
848
|
-
{ type: "text", text: "recovered after compaction" },
|
|
849
|
-
] as ContentBlock[],
|
|
850
|
-
},
|
|
851
|
-
];
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
const ctx = makeCtx({
|
|
855
|
-
agentLoopRun,
|
|
761
|
+
],
|
|
762
|
+
toolExecutor: async () => ({
|
|
763
|
+
content: "file1.ts\nfile2.ts",
|
|
764
|
+
isError: false,
|
|
765
|
+
}),
|
|
856
766
|
contextWindowManager: {
|
|
857
767
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
858
768
|
maybeCompact: async () => ({ compacted: false }),
|
|
@@ -881,13 +791,14 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
881
791
|
// This test should PASS against current code (when no progress is made).
|
|
882
792
|
test("overflow recovery compacts below limit even when estimation underestimates", async () => {
|
|
883
793
|
const events: ServerMessage[] = [];
|
|
884
|
-
let callCount = 0;
|
|
885
794
|
let reducerCalled = false;
|
|
886
795
|
|
|
887
|
-
//
|
|
796
|
+
// GIVEN the estimator reports 185k — under the 190k preflight budget
|
|
797
|
+
// (200k * 0.95), so the turn proceeds to the provider rather than
|
|
798
|
+
// compacting up front.
|
|
888
799
|
mockEstimateTokens = 185_000;
|
|
889
800
|
|
|
890
|
-
//
|
|
801
|
+
// AND the post-run convergence reducer successfully compacts
|
|
891
802
|
mockReducerStepFn = (msgs: Message[]) => {
|
|
892
803
|
reducerCalled = true;
|
|
893
804
|
return {
|
|
@@ -917,96 +828,46 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
917
828
|
};
|
|
918
829
|
};
|
|
919
830
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
// Provider rejects with "prompt is too long: 242201 tokens > 200000"
|
|
927
|
-
// even though estimator said 185k
|
|
928
|
-
onEvent({
|
|
929
|
-
type: "error",
|
|
930
|
-
error: new Error(
|
|
931
|
-
"prompt is too long: 242201 tokens > 200000 maximum",
|
|
932
|
-
),
|
|
933
|
-
});
|
|
934
|
-
onEvent({
|
|
935
|
-
type: "usage",
|
|
936
|
-
inputTokens: 0,
|
|
937
|
-
outputTokens: 0,
|
|
938
|
-
model: "test-model",
|
|
939
|
-
providerDurationMs: 10,
|
|
940
|
-
});
|
|
941
|
-
// No progress — return same messages
|
|
942
|
-
return messages;
|
|
943
|
-
}
|
|
944
|
-
// Second call succeeds
|
|
945
|
-
onEvent({
|
|
946
|
-
type: "message_complete",
|
|
947
|
-
message: {
|
|
948
|
-
role: "assistant",
|
|
949
|
-
content: [{ type: "text", text: "recovered" }],
|
|
950
|
-
},
|
|
951
|
-
});
|
|
952
|
-
onEvent({
|
|
953
|
-
type: "usage",
|
|
954
|
-
inputTokens: 80_000,
|
|
955
|
-
outputTokens: 200,
|
|
956
|
-
model: "test-model",
|
|
957
|
-
providerDurationMs: 500,
|
|
958
|
-
});
|
|
959
|
-
return [
|
|
960
|
-
...messages,
|
|
961
|
-
{
|
|
962
|
-
role: "assistant" as const,
|
|
963
|
-
content: [{ type: "text", text: "recovered" }] as ContentBlock[],
|
|
964
|
-
},
|
|
965
|
-
];
|
|
966
|
-
};
|
|
831
|
+
// AND a provider that rejects the first call as too long (revealing the
|
|
832
|
+
// real 242k count the estimator missed), then succeeds on the rerun.
|
|
833
|
+
const { provider, calls } = createMockProvider([
|
|
834
|
+
new Error("prompt is too long: 242201 tokens > 200000 maximum"),
|
|
835
|
+
textResponse("recovered"),
|
|
836
|
+
]);
|
|
967
837
|
|
|
968
838
|
const ctx = makeCtx({
|
|
969
|
-
|
|
839
|
+
loopProvider: provider,
|
|
970
840
|
contextWindowManager: {
|
|
971
841
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
972
842
|
maybeCompact: async () => ({ compacted: false }),
|
|
973
843
|
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
974
844
|
});
|
|
975
845
|
|
|
846
|
+
// WHEN the turn runs
|
|
976
847
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
977
848
|
|
|
978
|
-
//
|
|
849
|
+
// THEN the convergence reducer ran and the rerun recovered without a
|
|
850
|
+
// user-facing conversation_error.
|
|
979
851
|
expect(reducerCalled).toBe(true);
|
|
980
|
-
// Should recover without conversation_error
|
|
981
852
|
const conversationError = events.find(
|
|
982
853
|
(e) => e.type === "conversation_error",
|
|
983
854
|
);
|
|
984
855
|
expect(conversationError).toBeUndefined();
|
|
985
|
-
expect(
|
|
856
|
+
expect(calls.length).toBe(2);
|
|
986
857
|
});
|
|
987
858
|
|
|
988
859
|
// ── Test 3 ────────────────────────────────────────────────────────
|
|
989
|
-
//
|
|
990
|
-
// "242201 tokens > 200000"), the reducer
|
|
991
|
-
// the actual limit
|
|
992
|
-
//
|
|
993
|
-
//
|
|
994
|
-
//
|
|
995
|
-
//
|
|
996
|
-
//
|
|
997
|
-
// Expected behavior (PR 4 fix): `targetInputTokensOverride` should
|
|
998
|
-
// be adjusted based on the ratio between estimated and actual tokens.
|
|
999
|
-
// BUG: The targetTokens passed to the reducer is preflightBudget = 190k.
|
|
1000
|
-
// But when the actual token count is 242k (1.31x the estimate of 185k),
|
|
1001
|
-
// the target should be adjusted downward to account for the estimation
|
|
1002
|
-
// inaccuracy. For example: 190k / 1.31 ≈ 145k.
|
|
1003
|
-
// Planned fix: targetInputTokensOverride should be adjusted based on
|
|
1004
|
-
// the ratio between estimated and actual tokens.
|
|
860
|
+
// When the provider rejection reveals the actual token count (e.g.,
|
|
861
|
+
// "242201 tokens > 200000"), the overflow reducer's `targetTokens`
|
|
862
|
+
// should be a budget below the actual limit, not below the estimator's
|
|
863
|
+
// inaccurate budget. With a preflightBudget of 190k but an actual count
|
|
864
|
+
// of 242k (1.31x the estimate of 185k), the target is adjusted downward
|
|
865
|
+
// based on the observed mismatch (190k / 1.31 ≈ 145k) so the reducer
|
|
866
|
+
// converges toward the real ceiling rather than the optimistic estimate.
|
|
1005
867
|
test.todo(
|
|
1006
868
|
"forced compaction targets a lower budget when estimation has been inaccurate",
|
|
1007
869
|
async () => {
|
|
1008
870
|
const events: ServerMessage[] = [];
|
|
1009
|
-
let callCount = 0;
|
|
1010
871
|
let capturedTargetTokens: number | undefined;
|
|
1011
872
|
|
|
1012
873
|
// Estimator says 185k (below 190k budget = 200k * 0.95)
|
|
@@ -1042,55 +903,16 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1042
903
|
};
|
|
1043
904
|
};
|
|
1044
905
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
onEvent({
|
|
1053
|
-
type: "error",
|
|
1054
|
-
error: new Error(
|
|
1055
|
-
"prompt is too long: 242201 tokens > 200000 maximum",
|
|
1056
|
-
),
|
|
1057
|
-
});
|
|
1058
|
-
onEvent({
|
|
1059
|
-
type: "usage",
|
|
1060
|
-
inputTokens: 0,
|
|
1061
|
-
outputTokens: 0,
|
|
1062
|
-
model: "test-model",
|
|
1063
|
-
providerDurationMs: 10,
|
|
1064
|
-
});
|
|
1065
|
-
// No progress — return same messages
|
|
1066
|
-
return messages;
|
|
1067
|
-
}
|
|
1068
|
-
// Second call succeeds after compaction
|
|
1069
|
-
onEvent({
|
|
1070
|
-
type: "message_complete",
|
|
1071
|
-
message: {
|
|
1072
|
-
role: "assistant",
|
|
1073
|
-
content: [{ type: "text", text: "recovered" }],
|
|
1074
|
-
},
|
|
1075
|
-
});
|
|
1076
|
-
onEvent({
|
|
1077
|
-
type: "usage",
|
|
1078
|
-
inputTokens: 80_000,
|
|
1079
|
-
outputTokens: 200,
|
|
1080
|
-
model: "test-model",
|
|
1081
|
-
providerDurationMs: 500,
|
|
1082
|
-
});
|
|
1083
|
-
return [
|
|
1084
|
-
...messages,
|
|
1085
|
-
{
|
|
1086
|
-
role: "assistant" as const,
|
|
1087
|
-
content: [{ type: "text", text: "recovered" }] as ContentBlock[],
|
|
1088
|
-
},
|
|
1089
|
-
];
|
|
1090
|
-
};
|
|
906
|
+
// The provider rejects the first call with a context_too_large error
|
|
907
|
+
// (actual tokens 242201, far above the 185k estimate); after forced
|
|
908
|
+
// compaction re-targets a lower budget, the rerun recovers with text.
|
|
909
|
+
const { provider, calls } = createMockProvider([
|
|
910
|
+
new Error("prompt is too long: 242201 tokens > 200000 maximum"),
|
|
911
|
+
textResponse("recovered"),
|
|
912
|
+
]);
|
|
1091
913
|
|
|
1092
914
|
const ctx = makeCtx({
|
|
1093
|
-
|
|
915
|
+
loopProvider: provider,
|
|
1094
916
|
contextWindowManager: {
|
|
1095
917
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1096
918
|
maybeCompact: async () => ({ compacted: false }),
|
|
@@ -1120,7 +942,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1120
942
|
(e) => e.type === "conversation_error",
|
|
1121
943
|
);
|
|
1122
944
|
expect(conversationError).toBeUndefined();
|
|
1123
|
-
expect(
|
|
945
|
+
expect(calls.length).toBe(2);
|
|
1124
946
|
},
|
|
1125
947
|
);
|
|
1126
948
|
|
|
@@ -1134,7 +956,6 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1134
956
|
async () => {
|
|
1135
957
|
const events: ServerMessage[] = [];
|
|
1136
958
|
const longHistory = buildLongConversation(75);
|
|
1137
|
-
let callCount = 0;
|
|
1138
959
|
let reducerCalled = false;
|
|
1139
960
|
|
|
1140
961
|
// Estimator says ~195k — just above budget so preflight reducer runs
|
|
@@ -1170,38 +991,14 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1170
991
|
};
|
|
1171
992
|
};
|
|
1172
993
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
onEvent({
|
|
1179
|
-
type: "message_complete",
|
|
1180
|
-
message: {
|
|
1181
|
-
role: "assistant",
|
|
1182
|
-
content: [{ type: "text", text: "Here's the analysis..." }],
|
|
1183
|
-
},
|
|
1184
|
-
});
|
|
1185
|
-
onEvent({
|
|
1186
|
-
type: "usage",
|
|
1187
|
-
inputTokens: 50_000,
|
|
1188
|
-
outputTokens: 300,
|
|
1189
|
-
model: "test-model",
|
|
1190
|
-
providerDurationMs: 800,
|
|
1191
|
-
});
|
|
1192
|
-
return [
|
|
1193
|
-
...messages,
|
|
1194
|
-
{
|
|
1195
|
-
role: "assistant" as const,
|
|
1196
|
-
content: [
|
|
1197
|
-
{ type: "text", text: "Here's the analysis..." },
|
|
1198
|
-
] as ContentBlock[],
|
|
1199
|
-
},
|
|
1200
|
-
];
|
|
1201
|
-
};
|
|
994
|
+
// After the preflight reducer compacts the long history under budget,
|
|
995
|
+
// a single provider call completes the turn with plain text.
|
|
996
|
+
const { provider, calls } = createMockProvider([
|
|
997
|
+
textResponse("Here's the analysis..."),
|
|
998
|
+
]);
|
|
1202
999
|
|
|
1203
1000
|
const ctx = makeCtx({
|
|
1204
|
-
|
|
1001
|
+
loopProvider: provider,
|
|
1205
1002
|
messages: longHistory,
|
|
1206
1003
|
contextWindowManager: {
|
|
1207
1004
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
@@ -1216,7 +1013,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1216
1013
|
// Preflight should trigger the reducer since 195k > 190k budget
|
|
1217
1014
|
expect(reducerCalled).toBe(true);
|
|
1218
1015
|
// Should succeed
|
|
1219
|
-
expect(
|
|
1016
|
+
expect(calls.length).toBe(1);
|
|
1220
1017
|
const conversationError = events.find(
|
|
1221
1018
|
(e) => e.type === "conversation_error",
|
|
1222
1019
|
);
|
|
@@ -1260,118 +1057,31 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1260
1057
|
};
|
|
1261
1058
|
};
|
|
1262
1059
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
const progressMessages: Message[] = [
|
|
1272
|
-
...messages,
|
|
1273
|
-
{
|
|
1274
|
-
role: "assistant" as const,
|
|
1275
|
-
content: [
|
|
1276
|
-
{ type: "text", text: "Running analysis..." },
|
|
1277
|
-
{
|
|
1278
|
-
type: "tool_use",
|
|
1279
|
-
id: "tu-1",
|
|
1280
|
-
name: "bash",
|
|
1281
|
-
input: { command: "find . -name '*.ts'" },
|
|
1282
|
-
},
|
|
1283
|
-
] as ContentBlock[],
|
|
1284
|
-
},
|
|
1285
|
-
{
|
|
1286
|
-
role: "user" as const,
|
|
1287
|
-
content: [
|
|
1288
|
-
{
|
|
1289
|
-
type: "tool_result",
|
|
1290
|
-
tool_use_id: "tu-1",
|
|
1291
|
-
content: "file1.ts\nfile2.ts\nfile3.ts",
|
|
1292
|
-
is_error: false,
|
|
1293
|
-
},
|
|
1294
|
-
] as ContentBlock[],
|
|
1295
|
-
},
|
|
1296
|
-
];
|
|
1060
|
+
// Run 1 makes progress (a tool turn) then the following provider call
|
|
1061
|
+
// rejects with context_too_large; after emergency compaction the rerun
|
|
1062
|
+
// recovers with plain text.
|
|
1063
|
+
const { provider } = createMockProvider([
|
|
1064
|
+
toolUseResponse("tu-1", "bash", { command: "find . -name '*.ts'" }),
|
|
1065
|
+
new Error("context_length_exceeded"),
|
|
1066
|
+
textResponse("recovered"),
|
|
1067
|
+
]);
|
|
1297
1068
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1069
|
+
const ctx = makeCtx({
|
|
1070
|
+
loopProvider: provider,
|
|
1071
|
+
loopTools: [
|
|
1072
|
+
{
|
|
1301
1073
|
name: "bash",
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
toolUseId: "tu-1",
|
|
1307
|
-
content: "file1.ts\nfile2.ts\nfile3.ts",
|
|
1308
|
-
isError: false,
|
|
1309
|
-
});
|
|
1310
|
-
onEvent({
|
|
1311
|
-
type: "message_complete",
|
|
1312
|
-
message: {
|
|
1313
|
-
role: "assistant",
|
|
1314
|
-
content: [
|
|
1315
|
-
{ type: "text", text: "Running analysis..." },
|
|
1316
|
-
{
|
|
1317
|
-
type: "tool_use",
|
|
1318
|
-
id: "tu-1",
|
|
1319
|
-
name: "bash",
|
|
1320
|
-
input: { command: "find . -name '*.ts'" },
|
|
1321
|
-
},
|
|
1322
|
-
],
|
|
1074
|
+
description: "Run a shell command",
|
|
1075
|
+
input_schema: {
|
|
1076
|
+
type: "object",
|
|
1077
|
+
properties: { command: { type: "string" } },
|
|
1323
1078
|
},
|
|
1324
|
-
});
|
|
1325
|
-
onEvent({
|
|
1326
|
-
type: "usage",
|
|
1327
|
-
inputTokens: 190_000,
|
|
1328
|
-
outputTokens: 100,
|
|
1329
|
-
model: "test-model",
|
|
1330
|
-
providerDurationMs: 200,
|
|
1331
|
-
});
|
|
1332
|
-
|
|
1333
|
-
// Then context_too_large on the next LLM call within the loop
|
|
1334
|
-
onEvent({
|
|
1335
|
-
type: "error",
|
|
1336
|
-
error: new Error("context_length_exceeded"),
|
|
1337
|
-
});
|
|
1338
|
-
onEvent({
|
|
1339
|
-
type: "usage",
|
|
1340
|
-
inputTokens: 0,
|
|
1341
|
-
outputTokens: 0,
|
|
1342
|
-
model: "test-model",
|
|
1343
|
-
providerDurationMs: 10,
|
|
1344
|
-
});
|
|
1345
|
-
|
|
1346
|
-
return progressMessages;
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
// After emergency compaction, succeed
|
|
1350
|
-
onEvent({
|
|
1351
|
-
type: "message_complete",
|
|
1352
|
-
message: {
|
|
1353
|
-
role: "assistant",
|
|
1354
|
-
content: [{ type: "text", text: "recovered" }],
|
|
1355
1079
|
},
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
model: "test-model",
|
|
1362
|
-
providerDurationMs: 200,
|
|
1363
|
-
});
|
|
1364
|
-
return [
|
|
1365
|
-
...messages,
|
|
1366
|
-
{
|
|
1367
|
-
role: "assistant" as const,
|
|
1368
|
-
content: [{ type: "text", text: "recovered" }] as ContentBlock[],
|
|
1369
|
-
},
|
|
1370
|
-
];
|
|
1371
|
-
};
|
|
1372
|
-
|
|
1373
|
-
const ctx = makeCtx({
|
|
1374
|
-
agentLoopRun,
|
|
1080
|
+
],
|
|
1081
|
+
toolExecutor: async () => ({
|
|
1082
|
+
content: "file1.ts\nfile2.ts\nfile3.ts",
|
|
1083
|
+
isError: false,
|
|
1084
|
+
}),
|
|
1375
1085
|
contextWindowManager: {
|
|
1376
1086
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1377
1087
|
maybeCompact: async (
|
|
@@ -1448,117 +1158,30 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1448
1158
|
return 170_000;
|
|
1449
1159
|
};
|
|
1450
1160
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
) => {
|
|
1459
|
-
// Prime the assistant row anchor — production code emits this from
|
|
1460
|
-
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1461
|
-
await onEvent({ type: "llm_call_started" });
|
|
1462
|
-
agentLoopCallCount++;
|
|
1463
|
-
|
|
1464
|
-
if (agentLoopCallCount === 1) {
|
|
1465
|
-
// Simulate a tool round: assistant calls a tool, results come back
|
|
1466
|
-
const withProgress: Message[] = [
|
|
1467
|
-
...messages,
|
|
1468
|
-
{
|
|
1469
|
-
role: "assistant" as const,
|
|
1470
|
-
content: [
|
|
1471
|
-
{ type: "text", text: "Let me check." },
|
|
1472
|
-
{
|
|
1473
|
-
type: "tool_use",
|
|
1474
|
-
id: "tu-1",
|
|
1475
|
-
name: "bash",
|
|
1476
|
-
input: { command: "ls" },
|
|
1477
|
-
},
|
|
1478
|
-
] as ContentBlock[],
|
|
1479
|
-
},
|
|
1480
|
-
{
|
|
1481
|
-
role: "user" as const,
|
|
1482
|
-
content: [
|
|
1483
|
-
{
|
|
1484
|
-
type: "tool_result",
|
|
1485
|
-
tool_use_id: "tu-1",
|
|
1486
|
-
content: "file1.ts\nfile2.ts",
|
|
1487
|
-
is_error: false,
|
|
1488
|
-
},
|
|
1489
|
-
] as ContentBlock[],
|
|
1490
|
-
},
|
|
1491
|
-
];
|
|
1492
|
-
|
|
1493
|
-
onEvent({
|
|
1494
|
-
type: "message_complete",
|
|
1495
|
-
message: {
|
|
1496
|
-
role: "assistant",
|
|
1497
|
-
content: [
|
|
1498
|
-
{ type: "text", text: "Let me check." },
|
|
1499
|
-
{
|
|
1500
|
-
type: "tool_use",
|
|
1501
|
-
id: "tu-1",
|
|
1502
|
-
name: "bash",
|
|
1503
|
-
input: { command: "ls" },
|
|
1504
|
-
},
|
|
1505
|
-
],
|
|
1506
|
-
},
|
|
1507
|
-
});
|
|
1508
|
-
onEvent({
|
|
1509
|
-
type: "usage",
|
|
1510
|
-
inputTokens: 100,
|
|
1511
|
-
outputTokens: 50,
|
|
1512
|
-
model: "test-model",
|
|
1513
|
-
providerDurationMs: 100,
|
|
1514
|
-
});
|
|
1515
|
-
|
|
1516
|
-
// Call onCheckpoint — this should trigger the mid-loop budget check
|
|
1517
|
-
// which sees 170_000 > 161_500 and returns "yield"
|
|
1518
|
-
if (onCheckpoint) {
|
|
1519
|
-
const decision = await onCheckpoint({
|
|
1520
|
-
turnIndex: 0,
|
|
1521
|
-
toolCount: 1,
|
|
1522
|
-
hasToolUse: true,
|
|
1523
|
-
history: withProgress,
|
|
1524
|
-
});
|
|
1525
|
-
if (decision === "yield") {
|
|
1526
|
-
// Agent loop stops when checkpoint yields
|
|
1527
|
-
return withProgress;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
return withProgress;
|
|
1532
|
-
}
|
|
1161
|
+
// A tool round trips the mid-loop budget gate (170k > 161_500); the
|
|
1162
|
+
// gate compacts in place (productive) and the loop continues, so the
|
|
1163
|
+
// post-compaction provider call completes the turn with plain text.
|
|
1164
|
+
const { provider, calls } = createMockProvider([
|
|
1165
|
+
toolUseResponse("tu-1", "bash", { command: "ls" }),
|
|
1166
|
+
textResponse("done after compaction"),
|
|
1167
|
+
]);
|
|
1533
1168
|
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
message: {
|
|
1538
|
-
role: "assistant",
|
|
1539
|
-
content: [{ type: "text", text: "done after compaction" }],
|
|
1540
|
-
},
|
|
1541
|
-
});
|
|
1542
|
-
onEvent({
|
|
1543
|
-
type: "usage",
|
|
1544
|
-
inputTokens: 50,
|
|
1545
|
-
outputTokens: 25,
|
|
1546
|
-
model: "test-model",
|
|
1547
|
-
providerDurationMs: 100,
|
|
1548
|
-
});
|
|
1549
|
-
return [
|
|
1550
|
-
...messages,
|
|
1169
|
+
const ctx = makeCtx({
|
|
1170
|
+
loopProvider: provider,
|
|
1171
|
+
loopTools: [
|
|
1551
1172
|
{
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1173
|
+
name: "bash",
|
|
1174
|
+
description: "Run a shell command",
|
|
1175
|
+
input_schema: {
|
|
1176
|
+
type: "object",
|
|
1177
|
+
properties: { command: { type: "string" } },
|
|
1178
|
+
},
|
|
1556
1179
|
},
|
|
1557
|
-
]
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1180
|
+
],
|
|
1181
|
+
toolExecutor: async () => ({
|
|
1182
|
+
content: "file1.ts\nfile2.ts",
|
|
1183
|
+
isError: false,
|
|
1184
|
+
}),
|
|
1562
1185
|
contextWindowManager: {
|
|
1563
1186
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1564
1187
|
maybeCompact: async () => {
|
|
@@ -1592,8 +1215,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1592
1215
|
// The mid-loop budget check should have triggered compaction
|
|
1593
1216
|
expect(compactionCalled).toBe(true);
|
|
1594
1217
|
|
|
1595
|
-
//
|
|
1596
|
-
|
|
1218
|
+
// Provider called twice: the tool turn that tripped the gate, then the
|
|
1219
|
+
// post-compaction turn that completed the run.
|
|
1220
|
+
expect(calls.length).toBe(2);
|
|
1597
1221
|
|
|
1598
1222
|
// No conversation_error should be emitted
|
|
1599
1223
|
const conversationError = events.find(
|
|
@@ -1634,110 +1258,36 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1634
1258
|
return 175_000;
|
|
1635
1259
|
};
|
|
1636
1260
|
|
|
1637
|
-
let agentLoopCallCount = 0;
|
|
1638
1261
|
let contextTooLargeEmitted = false;
|
|
1639
1262
|
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
if (agentLoopCallCount === 1) {
|
|
1653
|
-
const currentHistory = [...messages];
|
|
1654
|
-
|
|
1655
|
-
// Simulate 5 tool rounds — but the checkpoint should yield at round 3
|
|
1656
|
-
for (let i = 0; i < 5; i++) {
|
|
1657
|
-
const toolId = `tu-${i}`;
|
|
1658
|
-
const assistantMsg: Message = {
|
|
1659
|
-
role: "assistant" as const,
|
|
1660
|
-
content: [
|
|
1661
|
-
{ type: "text", text: `Step ${i}` },
|
|
1662
|
-
{
|
|
1663
|
-
type: "tool_use",
|
|
1664
|
-
id: toolId,
|
|
1665
|
-
name: "bash",
|
|
1666
|
-
input: { command: `cmd-${i}` },
|
|
1667
|
-
},
|
|
1668
|
-
] as ContentBlock[],
|
|
1669
|
-
};
|
|
1670
|
-
const resultMsg: Message = {
|
|
1671
|
-
role: "user" as const,
|
|
1672
|
-
content: [
|
|
1673
|
-
{
|
|
1674
|
-
type: "tool_result",
|
|
1675
|
-
tool_use_id: toolId,
|
|
1676
|
-
content: "x".repeat(10_000),
|
|
1677
|
-
is_error: false,
|
|
1678
|
-
},
|
|
1679
|
-
] as ContentBlock[],
|
|
1680
|
-
};
|
|
1681
|
-
currentHistory.push(assistantMsg, resultMsg);
|
|
1682
|
-
|
|
1683
|
-
onEvent({
|
|
1684
|
-
type: "message_complete",
|
|
1685
|
-
message: assistantMsg,
|
|
1686
|
-
});
|
|
1687
|
-
onEvent({
|
|
1688
|
-
type: "usage",
|
|
1689
|
-
inputTokens: 50_000 + i * 20_000,
|
|
1690
|
-
outputTokens: 50,
|
|
1691
|
-
model: "test-model",
|
|
1692
|
-
providerDurationMs: 100,
|
|
1693
|
-
});
|
|
1694
|
-
|
|
1695
|
-
if (onCheckpoint) {
|
|
1696
|
-
const decision = await onCheckpoint({
|
|
1697
|
-
turnIndex: i,
|
|
1698
|
-
toolCount: 1,
|
|
1699
|
-
hasToolUse: true,
|
|
1700
|
-
history: currentHistory,
|
|
1701
|
-
});
|
|
1702
|
-
if (decision === "yield") {
|
|
1703
|
-
return currentHistory;
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
|
-
return currentHistory;
|
|
1709
|
-
}
|
|
1263
|
+
// Each tool round produces a large result; the estimate grows with each
|
|
1264
|
+
// checkpoint until tool round 3 trips the mid-loop gate (175k > 161_500).
|
|
1265
|
+
// Compaction runs in place (productive) and the loop continues, so the
|
|
1266
|
+
// following plain-text provider call completes the turn. The provider
|
|
1267
|
+
// never rejects with context_too_large.
|
|
1268
|
+
const { provider, calls } = createMockProvider([
|
|
1269
|
+
toolUseResponse("tu-0", "bash", { command: "cmd-0" }),
|
|
1270
|
+
toolUseResponse("tu-1", "bash", { command: "cmd-1" }),
|
|
1271
|
+
toolUseResponse("tu-2", "bash", { command: "cmd-2" }),
|
|
1272
|
+
textResponse("completed after mid-loop compaction"),
|
|
1273
|
+
]);
|
|
1710
1274
|
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
message: {
|
|
1715
|
-
role: "assistant",
|
|
1716
|
-
content: [
|
|
1717
|
-
{ type: "text", text: "completed after mid-loop compaction" },
|
|
1718
|
-
],
|
|
1719
|
-
},
|
|
1720
|
-
});
|
|
1721
|
-
onEvent({
|
|
1722
|
-
type: "usage",
|
|
1723
|
-
inputTokens: 60_000,
|
|
1724
|
-
outputTokens: 100,
|
|
1725
|
-
model: "test-model",
|
|
1726
|
-
providerDurationMs: 200,
|
|
1727
|
-
});
|
|
1728
|
-
return [
|
|
1729
|
-
...messages,
|
|
1275
|
+
const ctx = makeCtx({
|
|
1276
|
+
loopProvider: provider,
|
|
1277
|
+
loopTools: [
|
|
1730
1278
|
{
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1279
|
+
name: "bash",
|
|
1280
|
+
description: "Run a shell command",
|
|
1281
|
+
input_schema: {
|
|
1282
|
+
type: "object",
|
|
1283
|
+
properties: { command: { type: "string" } },
|
|
1284
|
+
},
|
|
1735
1285
|
},
|
|
1736
|
-
]
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1286
|
+
],
|
|
1287
|
+
toolExecutor: async () => ({
|
|
1288
|
+
content: "x".repeat(10_000),
|
|
1289
|
+
isError: false,
|
|
1290
|
+
}),
|
|
1741
1291
|
contextWindowManager: {
|
|
1742
1292
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1743
1293
|
maybeCompact: async () => {
|
|
@@ -1784,8 +1334,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1784
1334
|
// The provider should NEVER have rejected with context_too_large
|
|
1785
1335
|
expect(contextTooLargeEmitted).toBe(false);
|
|
1786
1336
|
|
|
1787
|
-
//
|
|
1788
|
-
|
|
1337
|
+
// Provider called four times: three tool rounds (the third trips the
|
|
1338
|
+
// mid-loop gate) plus the post-compaction text turn that completes.
|
|
1339
|
+
expect(calls.length).toBe(4);
|
|
1789
1340
|
|
|
1790
1341
|
// No conversation_error
|
|
1791
1342
|
const conversationError = events.find(
|
|
@@ -1814,88 +1365,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1814
1365
|
return 170_000;
|
|
1815
1366
|
};
|
|
1816
1367
|
|
|
1817
|
-
|
|
1818
|
-
const agentLoopRun: AgentLoopRun = async (
|
|
1819
|
-
messages,
|
|
1820
|
-
onEvent,
|
|
1821
|
-
_signal,
|
|
1822
|
-
_requestId,
|
|
1823
|
-
onCheckpoint,
|
|
1824
|
-
) => {
|
|
1825
|
-
// Prime the assistant row anchor — production code emits this from
|
|
1826
|
-
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1827
|
-
await onEvent({ type: "llm_call_started" });
|
|
1828
|
-
agentLoopCallCount++;
|
|
1829
|
-
|
|
1830
|
-
// Every call: simulate tool progress then yield at checkpoint
|
|
1831
|
-
const withProgress: Message[] = [
|
|
1832
|
-
...messages,
|
|
1833
|
-
{
|
|
1834
|
-
role: "assistant" as const,
|
|
1835
|
-
content: [
|
|
1836
|
-
{ type: "text", text: `Tool call ${agentLoopCallCount}` },
|
|
1837
|
-
{
|
|
1838
|
-
type: "tool_use",
|
|
1839
|
-
id: `tu-${agentLoopCallCount}`,
|
|
1840
|
-
name: "bash",
|
|
1841
|
-
input: { command: "ls" },
|
|
1842
|
-
},
|
|
1843
|
-
] as ContentBlock[],
|
|
1844
|
-
},
|
|
1845
|
-
{
|
|
1846
|
-
role: "user" as const,
|
|
1847
|
-
content: [
|
|
1848
|
-
{
|
|
1849
|
-
type: "tool_result",
|
|
1850
|
-
tool_use_id: `tu-${agentLoopCallCount}`,
|
|
1851
|
-
content: "output",
|
|
1852
|
-
is_error: false,
|
|
1853
|
-
},
|
|
1854
|
-
] as ContentBlock[],
|
|
1855
|
-
},
|
|
1856
|
-
];
|
|
1857
|
-
|
|
1858
|
-
onEvent({
|
|
1859
|
-
type: "message_complete",
|
|
1860
|
-
message: {
|
|
1861
|
-
role: "assistant",
|
|
1862
|
-
content: [
|
|
1863
|
-
{ type: "text", text: `Tool call ${agentLoopCallCount}` },
|
|
1864
|
-
{
|
|
1865
|
-
type: "tool_use",
|
|
1866
|
-
id: `tu-${agentLoopCallCount}`,
|
|
1867
|
-
name: "bash",
|
|
1868
|
-
input: { command: "ls" },
|
|
1869
|
-
},
|
|
1870
|
-
],
|
|
1871
|
-
},
|
|
1872
|
-
});
|
|
1873
|
-
onEvent({
|
|
1874
|
-
type: "usage",
|
|
1875
|
-
inputTokens: 100,
|
|
1876
|
-
outputTokens: 50,
|
|
1877
|
-
model: "test-model",
|
|
1878
|
-
providerDurationMs: 100,
|
|
1879
|
-
});
|
|
1880
|
-
|
|
1881
|
-
// Always yield at checkpoint — simulates compaction not helping
|
|
1882
|
-
if (onCheckpoint) {
|
|
1883
|
-
const decision = await onCheckpoint({
|
|
1884
|
-
turnIndex: 0,
|
|
1885
|
-
toolCount: 1,
|
|
1886
|
-
hasToolUse: true,
|
|
1887
|
-
history: withProgress,
|
|
1888
|
-
});
|
|
1889
|
-
if (decision === "yield") {
|
|
1890
|
-
return withProgress;
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
return withProgress;
|
|
1895
|
-
};
|
|
1896
|
-
|
|
1897
|
-
let compactionCallCount = 0;
|
|
1898
|
-
// Convergence reducer: reduce tokens enough to succeed
|
|
1368
|
+
// The convergence reducer reduces tokens enough for the rerun to recover.
|
|
1899
1369
|
let convergenceReducerCalled = false;
|
|
1900
1370
|
mockReducerStepFn = (msgs: Message[]) => {
|
|
1901
1371
|
convergenceReducerCalled = true;
|
|
@@ -1911,15 +1381,43 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1911
1381
|
};
|
|
1912
1382
|
};
|
|
1913
1383
|
|
|
1384
|
+
// Every provider call returns a tool_use, so each loop run does a tool
|
|
1385
|
+
// turn that trips the mid-loop budget gate. On the initial run the gate
|
|
1386
|
+
// calls compaction (which surfaces `exhausted: true`); the convergence
|
|
1387
|
+
// rerun runs without a compaction hook and yields "budget" directly.
|
|
1388
|
+
// With the reducer exhausted, the convergence loop terminates with the
|
|
1389
|
+
// turn still over budget and the orchestrator stamps `context_too_large`.
|
|
1390
|
+
const { provider, calls } = createMockProvider([
|
|
1391
|
+
toolUseResponse("tu-1", "bash", { command: "ls" }),
|
|
1392
|
+
]);
|
|
1393
|
+
|
|
1394
|
+
let compactionCallCount = 0;
|
|
1914
1395
|
const ctx = makeCtx({
|
|
1915
|
-
|
|
1396
|
+
loopProvider: provider,
|
|
1397
|
+
loopTools: [
|
|
1398
|
+
{
|
|
1399
|
+
name: "bash",
|
|
1400
|
+
description: "Run a shell command",
|
|
1401
|
+
input_schema: {
|
|
1402
|
+
type: "object",
|
|
1403
|
+
properties: { command: { type: "string" } },
|
|
1404
|
+
},
|
|
1405
|
+
},
|
|
1406
|
+
],
|
|
1407
|
+
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
1916
1408
|
contextWindowManager: {
|
|
1917
1409
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1918
1410
|
maybeCompact: async () => {
|
|
1919
1411
|
compactionCallCount++;
|
|
1920
|
-
// Compaction
|
|
1412
|
+
// Compaction's internal retry budget is exhausted — the
|
|
1413
|
+
// compactor itself ran maxAttempts passes and still couldn't
|
|
1414
|
+
// drop below the auto-threshold. `maybeCompact` surfaces this
|
|
1415
|
+
// via `exhausted: true` so the loop yields "budget" and the
|
|
1416
|
+
// orchestrator escalates straight to the convergence loop
|
|
1417
|
+
// instead of looping on a stuck compactor.
|
|
1921
1418
|
return {
|
|
1922
1419
|
compacted: true,
|
|
1420
|
+
exhausted: true,
|
|
1923
1421
|
messages: [
|
|
1924
1422
|
{
|
|
1925
1423
|
role: "user" as const,
|
|
@@ -1944,14 +1442,20 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1944
1442
|
|
|
1945
1443
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1946
1444
|
|
|
1947
|
-
// 1 initial auto-compact +
|
|
1948
|
-
|
|
1445
|
+
// 1 initial auto-compact + 1 mid-loop compaction = 2 total. The
|
|
1446
|
+
// first mid-loop call surfaces `exhausted: true`, so the
|
|
1447
|
+
// orchestrator escalates immediately without retrying maybeCompact
|
|
1448
|
+
// — the retry budget for the compactor itself lives inside
|
|
1449
|
+
// `ContextWindowManager.maybeCompact`.
|
|
1450
|
+
expect(compactionCallCount).toBe(2);
|
|
1949
1451
|
|
|
1950
|
-
//
|
|
1951
|
-
|
|
1452
|
+
// Provider calls: 1 initial tool turn (yields budget) + 1 convergence
|
|
1453
|
+
// rerun that recovers. No mid-loop re-entries because the orchestrator
|
|
1454
|
+
// broke out on `exhausted` before re-invoking the loop.
|
|
1455
|
+
expect(calls.length).toBe(2);
|
|
1952
1456
|
|
|
1953
|
-
// After
|
|
1954
|
-
// have been triggered (contextTooLargeDetected set to true)
|
|
1457
|
+
// After the compactor exhausted itself, the convergence loop
|
|
1458
|
+
// should have been triggered (contextTooLargeDetected set to true)
|
|
1955
1459
|
expect(convergenceReducerCalled).toBe(true);
|
|
1956
1460
|
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
1957
1461
|
"test-conv",
|
|
@@ -1959,6 +1463,102 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1959
1463
|
);
|
|
1960
1464
|
});
|
|
1961
1465
|
|
|
1466
|
+
// ── Test 8b ───────────────────────────────────────────────────────
|
|
1467
|
+
// Counterpart to Test 8: when a mid-loop `maybeCompact` returns
|
|
1468
|
+
// productive (`compacted: true`, no `exhausted` flag), the loop
|
|
1469
|
+
// compacts in place and continues the run itself — it never yields
|
|
1470
|
+
// "budget", so the orchestrator does not escalate to the convergence
|
|
1471
|
+
// loop. Mid-loop iteration is now wholly internal to `AgentLoop.run`;
|
|
1472
|
+
// the orchestrator only reacts to the binary `exhausted`/timeout
|
|
1473
|
+
// signal carried back as a "budget" exit.
|
|
1474
|
+
test("productive mid-loop compaction continues in place without escalating", async () => {
|
|
1475
|
+
const events: ServerMessage[] = [];
|
|
1476
|
+
|
|
1477
|
+
// Budget = 200_000 * 0.95 = 190_000
|
|
1478
|
+
// Mid-loop threshold = 190_000 * 0.85 = 161_500
|
|
1479
|
+
let estimateCallCount = 0;
|
|
1480
|
+
mockEstimateTokens = () => {
|
|
1481
|
+
estimateCallCount++;
|
|
1482
|
+
// Preflight: below budget.
|
|
1483
|
+
if (estimateCallCount === 1) return 100_000;
|
|
1484
|
+
// Every checkpoint estimate: above threshold — always trips the
|
|
1485
|
+
// yield. Simulates a long turn where each tool call's result
|
|
1486
|
+
// inflates the context past 85% even after a successful compaction.
|
|
1487
|
+
return 170_000;
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
// A single tool round reaches one checkpoint; the in-loop budget gate
|
|
1491
|
+
// trips there and compaction runs in place. The loop continues the run
|
|
1492
|
+
// itself — the following provider call returns plain text and the turn
|
|
1493
|
+
// completes — so the orchestrator never re-enters the convergence loop.
|
|
1494
|
+
const { provider, calls } = createMockProvider([
|
|
1495
|
+
toolUseResponse("tu-1", "bash", { command: "ls" }),
|
|
1496
|
+
textResponse("final answer"),
|
|
1497
|
+
]);
|
|
1498
|
+
|
|
1499
|
+
// Compaction reports `estimatedInputTokens` well below the 161_500
|
|
1500
|
+
// threshold — the "compaction is productive" signal (no `exhausted`
|
|
1501
|
+
// flag) that lets the loop continue in place.
|
|
1502
|
+
let compactionCallCount = 0;
|
|
1503
|
+
const ctx = makeCtx({
|
|
1504
|
+
loopProvider: provider,
|
|
1505
|
+
loopTools: [
|
|
1506
|
+
{
|
|
1507
|
+
name: "bash",
|
|
1508
|
+
description: "Run a shell command",
|
|
1509
|
+
input_schema: {
|
|
1510
|
+
type: "object",
|
|
1511
|
+
properties: { command: { type: "string" } },
|
|
1512
|
+
},
|
|
1513
|
+
},
|
|
1514
|
+
],
|
|
1515
|
+
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
1516
|
+
contextWindowManager: {
|
|
1517
|
+
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1518
|
+
maybeCompact: async () => {
|
|
1519
|
+
compactionCallCount++;
|
|
1520
|
+
return {
|
|
1521
|
+
compacted: true,
|
|
1522
|
+
messages: [
|
|
1523
|
+
{
|
|
1524
|
+
role: "user" as const,
|
|
1525
|
+
content: [{ type: "text", text: "Hello" }],
|
|
1526
|
+
},
|
|
1527
|
+
] as Message[],
|
|
1528
|
+
compactedPersistedMessages: 5,
|
|
1529
|
+
summaryText: "Compaction summary",
|
|
1530
|
+
previousEstimatedInputTokens: 170_000,
|
|
1531
|
+
estimatedInputTokens: 100_000,
|
|
1532
|
+
maxInputTokens: 200_000,
|
|
1533
|
+
thresholdTokens: 160_000,
|
|
1534
|
+
compactedMessages: 10,
|
|
1535
|
+
summaryCalls: 1,
|
|
1536
|
+
summaryInputTokens: 500,
|
|
1537
|
+
summaryOutputTokens: 200,
|
|
1538
|
+
summaryModel: "mock-model",
|
|
1539
|
+
};
|
|
1540
|
+
},
|
|
1541
|
+
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
1542
|
+
});
|
|
1543
|
+
|
|
1544
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1545
|
+
|
|
1546
|
+
// 1 initial auto-compact + 1 productive mid-loop compaction.
|
|
1547
|
+
expect(compactionCallCount).toBe(2);
|
|
1548
|
+
// The loop continued in place after compacting: a tool turn followed by
|
|
1549
|
+
// the post-compaction text turn, both within a single run.
|
|
1550
|
+
expect(calls.length).toBe(2);
|
|
1551
|
+
|
|
1552
|
+
// No escalation to the convergence loop because the mid-loop
|
|
1553
|
+
// `maybeCompact` returned productive (no `exhausted` flag), and the turn
|
|
1554
|
+
// completed normally.
|
|
1555
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).not.toHaveBeenCalledWith(
|
|
1556
|
+
"test-conv",
|
|
1557
|
+
"context_too_large",
|
|
1558
|
+
);
|
|
1559
|
+
expect(events.find((e) => e.type === "conversation_error")).toBeUndefined();
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1962
1562
|
// ── Test 9 ────────────────────────────────────────────────────────
|
|
1963
1563
|
// When the convergence loop reruns the agent loop and it still yields
|
|
1964
1564
|
// at checkpoint (yieldedForBudget), the loop must continue reducing
|
|
@@ -1978,84 +1578,13 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1978
1578
|
return 170_000;
|
|
1979
1579
|
};
|
|
1980
1580
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
) => {
|
|
1989
|
-
// Prime the assistant row anchor — production code emits this from
|
|
1990
|
-
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
1991
|
-
await onEvent({ type: "llm_call_started" });
|
|
1992
|
-
agentLoopCallCount++;
|
|
1993
|
-
|
|
1994
|
-
const withProgress: Message[] = [
|
|
1995
|
-
...messages,
|
|
1996
|
-
{
|
|
1997
|
-
role: "assistant" as const,
|
|
1998
|
-
content: [
|
|
1999
|
-
{ type: "text", text: `Tool call ${agentLoopCallCount}` },
|
|
2000
|
-
{
|
|
2001
|
-
type: "tool_use",
|
|
2002
|
-
id: `tu-${agentLoopCallCount}`,
|
|
2003
|
-
name: "bash",
|
|
2004
|
-
input: { command: "ls" },
|
|
2005
|
-
},
|
|
2006
|
-
] as ContentBlock[],
|
|
2007
|
-
},
|
|
2008
|
-
{
|
|
2009
|
-
role: "user" as const,
|
|
2010
|
-
content: [
|
|
2011
|
-
{
|
|
2012
|
-
type: "tool_result",
|
|
2013
|
-
tool_use_id: `tu-${agentLoopCallCount}`,
|
|
2014
|
-
content: "output",
|
|
2015
|
-
is_error: false,
|
|
2016
|
-
},
|
|
2017
|
-
] as ContentBlock[],
|
|
2018
|
-
},
|
|
2019
|
-
];
|
|
2020
|
-
|
|
2021
|
-
onEvent({
|
|
2022
|
-
type: "message_complete",
|
|
2023
|
-
message: {
|
|
2024
|
-
role: "assistant",
|
|
2025
|
-
content: [
|
|
2026
|
-
{ type: "text", text: `Tool call ${agentLoopCallCount}` },
|
|
2027
|
-
{
|
|
2028
|
-
type: "tool_use",
|
|
2029
|
-
id: `tu-${agentLoopCallCount}`,
|
|
2030
|
-
name: "bash",
|
|
2031
|
-
input: { command: "ls" },
|
|
2032
|
-
},
|
|
2033
|
-
],
|
|
2034
|
-
},
|
|
2035
|
-
});
|
|
2036
|
-
onEvent({
|
|
2037
|
-
type: "usage",
|
|
2038
|
-
inputTokens: 100,
|
|
2039
|
-
outputTokens: 50,
|
|
2040
|
-
model: "test-model",
|
|
2041
|
-
providerDurationMs: 100,
|
|
2042
|
-
});
|
|
2043
|
-
|
|
2044
|
-
// Always yield at checkpoint — simulates reduction not helping enough
|
|
2045
|
-
if (onCheckpoint) {
|
|
2046
|
-
const decision = await onCheckpoint({
|
|
2047
|
-
turnIndex: 0,
|
|
2048
|
-
toolCount: 1,
|
|
2049
|
-
hasToolUse: true,
|
|
2050
|
-
history: withProgress,
|
|
2051
|
-
});
|
|
2052
|
-
if (decision === "yield") {
|
|
2053
|
-
return withProgress;
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
return withProgress;
|
|
2058
|
-
};
|
|
1581
|
+
// Every provider call returns a tool_use, so each loop run does a tool
|
|
1582
|
+
// turn that trips the mid-loop budget gate and yields "budget". The
|
|
1583
|
+
// initial run's gate calls compaction (exhausted); the convergence
|
|
1584
|
+
// reruns run without a compaction hook and yield directly.
|
|
1585
|
+
const { provider, calls } = createMockProvider([
|
|
1586
|
+
toolUseResponse("tu-1", "bash", { command: "ls" }),
|
|
1587
|
+
]);
|
|
2059
1588
|
|
|
2060
1589
|
// Convergence reducer: first call returns non-exhausted, second returns exhausted
|
|
2061
1590
|
let reducerCallCount = 0;
|
|
@@ -2087,9 +1616,25 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
2087
1616
|
};
|
|
2088
1617
|
|
|
2089
1618
|
const ctx = makeCtx({
|
|
2090
|
-
|
|
1619
|
+
loopProvider: provider,
|
|
1620
|
+
loopTools: [
|
|
1621
|
+
{
|
|
1622
|
+
name: "bash",
|
|
1623
|
+
description: "Run a shell command",
|
|
1624
|
+
input_schema: {
|
|
1625
|
+
type: "object",
|
|
1626
|
+
properties: { command: { type: "string" } },
|
|
1627
|
+
},
|
|
1628
|
+
},
|
|
1629
|
+
],
|
|
1630
|
+
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
2091
1631
|
contextWindowManager: {
|
|
2092
1632
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
1633
|
+
// Under the new architecture (Compaction Re-homing Arc, Bullet 1)
|
|
1634
|
+
// the retry budget lives inside `ContextWindowManager._maybeCompact`,
|
|
1635
|
+
// so a single daemon-level call represents the full manager retry
|
|
1636
|
+
// sequence. Signal `exhausted: true` immediately to escalate the
|
|
1637
|
+
// mid-loop to the convergence reducer.
|
|
2093
1638
|
maybeCompact: async () => ({
|
|
2094
1639
|
compacted: true,
|
|
2095
1640
|
messages: [
|
|
@@ -2109,6 +1654,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
2109
1654
|
summaryInputTokens: 500,
|
|
2110
1655
|
summaryOutputTokens: 200,
|
|
2111
1656
|
summaryModel: "mock-model",
|
|
1657
|
+
exhausted: true,
|
|
2112
1658
|
}),
|
|
2113
1659
|
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
2114
1660
|
});
|
|
@@ -2119,8 +1665,11 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
2119
1665
|
// once more after yieldedForBudget triggered re-entry
|
|
2120
1666
|
expect(reducerCallCount).toBe(2);
|
|
2121
1667
|
|
|
2122
|
-
//
|
|
2123
|
-
|
|
1668
|
+
// Provider calls: 1 initial run + 2 convergence reruns = 3 calls, each a
|
|
1669
|
+
// tool turn that yields "budget". The mid-loop no longer drives
|
|
1670
|
+
// daemon-level retries — the manager owns its retry budget and signals
|
|
1671
|
+
// exhaustion via the `exhausted` flag.
|
|
1672
|
+
expect(calls.length).toBe(3);
|
|
2124
1673
|
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
2125
1674
|
"test-conv",
|
|
2126
1675
|
"context_too_large",
|
|
@@ -2220,35 +1769,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
2220
1769
|
};
|
|
2221
1770
|
};
|
|
2222
1771
|
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
2226
|
-
await onEvent({ type: "llm_call_started" });
|
|
2227
|
-
onEvent({
|
|
2228
|
-
type: "message_complete",
|
|
2229
|
-
message: {
|
|
2230
|
-
role: "assistant",
|
|
2231
|
-
content: [{ type: "text", text: "done" }],
|
|
2232
|
-
},
|
|
2233
|
-
});
|
|
2234
|
-
onEvent({
|
|
2235
|
-
type: "usage",
|
|
2236
|
-
inputTokens: 170_000,
|
|
2237
|
-
outputTokens: 200,
|
|
2238
|
-
model: "test-model",
|
|
2239
|
-
providerDurationMs: 500,
|
|
2240
|
-
});
|
|
2241
|
-
return [
|
|
2242
|
-
...messages,
|
|
2243
|
-
{
|
|
2244
|
-
role: "assistant" as const,
|
|
2245
|
-
content: [{ type: "text", text: "done" }] as ContentBlock[],
|
|
2246
|
-
},
|
|
2247
|
-
];
|
|
2248
|
-
};
|
|
2249
|
-
|
|
1772
|
+
// The preflight overflow reducer runs in the orchestrator before the loop,
|
|
1773
|
+
// so a single successful provider turn is enough to drive the path.
|
|
2250
1774
|
const ctx = makeCtx({
|
|
2251
|
-
|
|
1775
|
+
providerResponses: [textResponse("done")],
|
|
2252
1776
|
contextWindowManager: {
|
|
2253
1777
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
2254
1778
|
maybeCompact: async () => ({ compacted: false }),
|
|
@@ -2319,111 +1843,100 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
2319
1843
|
// emergency compaction + final agentLoop.run path executes.
|
|
2320
1844
|
mockOverflowAction = "auto_compress_latest_turn";
|
|
2321
1845
|
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
onCheckpoint,
|
|
2329
|
-
) => {
|
|
2330
|
-
// Prime the assistant row anchor — production code emits this from
|
|
2331
|
-
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
2332
|
-
await onEvent({ type: "llm_call_started" });
|
|
2333
|
-
agentLoopCallCount++;
|
|
2334
|
-
|
|
2335
|
-
const withProgress: Message[] = [
|
|
2336
|
-
...messages,
|
|
2337
|
-
{
|
|
2338
|
-
role: "assistant" as const,
|
|
2339
|
-
content: [
|
|
2340
|
-
{ type: "text", text: `tool call ${agentLoopCallCount}` },
|
|
2341
|
-
{
|
|
2342
|
-
type: "tool_use",
|
|
2343
|
-
id: `tu-${agentLoopCallCount}`,
|
|
2344
|
-
name: "bash",
|
|
2345
|
-
input: { command: "ls" },
|
|
2346
|
-
},
|
|
2347
|
-
] as ContentBlock[],
|
|
2348
|
-
},
|
|
2349
|
-
{
|
|
2350
|
-
role: "user" as const,
|
|
2351
|
-
content: [
|
|
2352
|
-
{
|
|
2353
|
-
type: "tool_result",
|
|
2354
|
-
tool_use_id: `tu-${agentLoopCallCount}`,
|
|
2355
|
-
content: "output",
|
|
2356
|
-
is_error: false,
|
|
2357
|
-
},
|
|
2358
|
-
] as ContentBlock[],
|
|
2359
|
-
},
|
|
2360
|
-
];
|
|
2361
|
-
|
|
2362
|
-
onEvent({
|
|
2363
|
-
type: "message_complete",
|
|
2364
|
-
message: {
|
|
2365
|
-
role: "assistant",
|
|
2366
|
-
content: [
|
|
2367
|
-
{ type: "text", text: `tool call ${agentLoopCallCount}` },
|
|
2368
|
-
{
|
|
2369
|
-
type: "tool_use",
|
|
2370
|
-
id: `tu-${agentLoopCallCount}`,
|
|
2371
|
-
name: "bash",
|
|
2372
|
-
input: { command: "ls" },
|
|
2373
|
-
},
|
|
2374
|
-
],
|
|
2375
|
-
},
|
|
2376
|
-
});
|
|
2377
|
-
onEvent({
|
|
2378
|
-
type: "usage",
|
|
2379
|
-
inputTokens: 100,
|
|
2380
|
-
outputTokens: 50,
|
|
2381
|
-
model: "test-model",
|
|
2382
|
-
providerDurationMs: 100,
|
|
2383
|
-
});
|
|
2384
|
-
|
|
2385
|
-
// Every checkpoint yields — including the final auto_compress rerun.
|
|
2386
|
-
if (onCheckpoint) {
|
|
2387
|
-
const decision = await onCheckpoint({
|
|
2388
|
-
turnIndex: 0,
|
|
2389
|
-
toolCount: 1,
|
|
2390
|
-
hasToolUse: true,
|
|
2391
|
-
history: withProgress,
|
|
2392
|
-
});
|
|
2393
|
-
if (decision === "yield") {
|
|
2394
|
-
return withProgress;
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
|
|
2398
|
-
return withProgress;
|
|
2399
|
-
};
|
|
1846
|
+
// Every provider call returns a tool_use, so each loop run does a tool
|
|
1847
|
+
// turn that trips the mid-loop budget gate and yields "budget" —
|
|
1848
|
+
// including the final auto_compress rerun.
|
|
1849
|
+
const { provider } = createMockProvider([
|
|
1850
|
+
toolUseResponse("tu-1", "bash", { command: "ls" }),
|
|
1851
|
+
]);
|
|
2400
1852
|
|
|
1853
|
+
// `maybeCompact` is invoked through three distinct call sites:
|
|
1854
|
+
// 1. Start-of-turn compaction (no `force` option) — return a no-op
|
|
1855
|
+
// so the start-of-turn pass doesn't perturb state. The mock's
|
|
1856
|
+
// `shouldCompact` already returns `needed: false`, but the
|
|
1857
|
+
// orchestrator still invokes the compaction pipeline.
|
|
1858
|
+
// 2. Mid-loop after the initial agent-loop yield (`force: true`) —
|
|
1859
|
+
// must signal `exhausted: true` so the daemon escalates to the
|
|
1860
|
+
// convergence reducer instead of looping forever.
|
|
1861
|
+
// 3. auto_compress_latest_turn emergency compaction (`force: true`,
|
|
1862
|
+
// `minKeepRecentUserTurns: 0`) — succeeds and drops tokens below
|
|
1863
|
+
// threshold; the subsequent rerun yields again and is classified
|
|
1864
|
+
// as BUDGET_YIELD_UNRECOVERED.
|
|
1865
|
+
let forcedMaybeCompactCallCount = 0;
|
|
2401
1866
|
const ctx = makeCtx({
|
|
2402
|
-
|
|
1867
|
+
loopProvider: provider,
|
|
1868
|
+
loopTools: [
|
|
1869
|
+
{
|
|
1870
|
+
name: "bash",
|
|
1871
|
+
description: "Run a shell command",
|
|
1872
|
+
input_schema: {
|
|
1873
|
+
type: "object",
|
|
1874
|
+
properties: { command: { type: "string" } },
|
|
1875
|
+
},
|
|
1876
|
+
},
|
|
1877
|
+
],
|
|
1878
|
+
toolExecutor: async () => ({ content: "output", isError: false }),
|
|
2403
1879
|
contextWindowManager: {
|
|
2404
1880
|
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
1881
|
+
maybeCompact: async (
|
|
1882
|
+
_msgs: Message[],
|
|
1883
|
+
_signal: AbortSignal,
|
|
1884
|
+
opts?: { force?: boolean },
|
|
1885
|
+
) => {
|
|
1886
|
+
// Start-of-turn calls pass no `force` option; route them to a
|
|
1887
|
+
// no-op so only the mid-loop and emergency paths drive the test.
|
|
1888
|
+
if (!opts?.force) {
|
|
1889
|
+
return { compacted: false };
|
|
1890
|
+
}
|
|
1891
|
+
forcedMaybeCompactCallCount++;
|
|
1892
|
+
if (forcedMaybeCompactCallCount === 1) {
|
|
1893
|
+
// Mid-loop call — under the new architecture (Compaction
|
|
1894
|
+
// Re-homing Arc, Bullet 1) the manager owns its own retry
|
|
1895
|
+
// budget; signal exhaustion to escalate to convergence.
|
|
1896
|
+
return {
|
|
1897
|
+
compacted: true,
|
|
1898
|
+
messages: [
|
|
1899
|
+
{
|
|
1900
|
+
role: "user" as const,
|
|
1901
|
+
content: [{ type: "text", text: "mid-loop compacted" }],
|
|
1902
|
+
},
|
|
1903
|
+
] as Message[],
|
|
1904
|
+
compactedPersistedMessages: 5,
|
|
1905
|
+
summaryText: "Mid-loop summary",
|
|
1906
|
+
previousEstimatedInputTokens: 170_000,
|
|
1907
|
+
estimatedInputTokens: 165_000,
|
|
1908
|
+
maxInputTokens: 200_000,
|
|
1909
|
+
thresholdTokens: 160_000,
|
|
1910
|
+
compactedMessages: 10,
|
|
1911
|
+
summaryCalls: 1,
|
|
1912
|
+
summaryInputTokens: 500,
|
|
1913
|
+
summaryOutputTokens: 200,
|
|
1914
|
+
summaryModel: "mock-model",
|
|
1915
|
+
exhausted: true,
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
// Emergency compaction call from auto_compress_latest_turn.
|
|
1919
|
+
return {
|
|
1920
|
+
compacted: true,
|
|
1921
|
+
messages: [
|
|
1922
|
+
{
|
|
1923
|
+
role: "user" as const,
|
|
1924
|
+
content: [{ type: "text", text: "compacted" }],
|
|
1925
|
+
},
|
|
1926
|
+
] as Message[],
|
|
1927
|
+
compactedPersistedMessages: 5,
|
|
1928
|
+
summaryText: "Emergency summary",
|
|
1929
|
+
previousEstimatedInputTokens: 170_000,
|
|
1930
|
+
estimatedInputTokens: 90_000,
|
|
1931
|
+
maxInputTokens: 200_000,
|
|
1932
|
+
thresholdTokens: 160_000,
|
|
1933
|
+
compactedMessages: 10,
|
|
1934
|
+
summaryCalls: 1,
|
|
1935
|
+
summaryInputTokens: 500,
|
|
1936
|
+
summaryOutputTokens: 200,
|
|
1937
|
+
summaryModel: "mock-model",
|
|
1938
|
+
};
|
|
1939
|
+
},
|
|
2427
1940
|
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
2428
1941
|
});
|
|
2429
1942
|
|