@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
package/src/agent/loop.ts
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
import * as Sentry from "@sentry/node";
|
|
2
2
|
|
|
3
3
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
4
|
+
import { stripInjectionsForCompaction } from "../context/strip-injections.js";
|
|
4
5
|
import {
|
|
5
6
|
estimatePromptTokensRaw,
|
|
7
|
+
estimatePromptTokensWithTools,
|
|
6
8
|
estimateToolsTokens,
|
|
7
9
|
getCalibrationProviderKey,
|
|
8
10
|
} from "../context/token-estimator.js";
|
|
9
|
-
import {
|
|
11
|
+
import type { ContextWindowResult } from "../context/window-manager.js";
|
|
10
12
|
import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ToolErrorArgs,
|
|
22
|
-
ToolErrorDecision,
|
|
23
|
-
ToolResultTruncateArgs,
|
|
24
|
-
ToolResultTruncateResult,
|
|
25
|
-
TurnContext,
|
|
26
|
-
} from "../plugins/types.js";
|
|
13
|
+
import { HOOKS } from "../plugin-api/constants.js";
|
|
14
|
+
import type { PostToolUseContext, StopContext } from "../plugin-api/types.js";
|
|
15
|
+
import {
|
|
16
|
+
DEFAULT_COMPACTION_PLUGIN_NAME,
|
|
17
|
+
defaultCompact,
|
|
18
|
+
} from "../plugins/defaults/compaction/compact.js";
|
|
19
|
+
import type { PostCompactionHookInput } from "../plugins/defaults/memory-retrieval/hooks/post-compact.js";
|
|
20
|
+
import { runHook } from "../plugins/pipeline.js";
|
|
21
|
+
import type { CompactionCircuitEvent, TurnContext } from "../plugins/types.js";
|
|
22
|
+
import { PluginExecutionError } from "../plugins/types.js";
|
|
27
23
|
import { normalizeThinkingConfigForWire } from "../providers/thinking-config.js";
|
|
28
24
|
import type {
|
|
29
25
|
ContentBlock,
|
|
30
26
|
Message,
|
|
31
27
|
Provider,
|
|
28
|
+
ProviderResponse,
|
|
29
|
+
SendMessageOptions,
|
|
32
30
|
ToolDefinition,
|
|
33
31
|
ToolResultContent,
|
|
34
32
|
} from "../providers/types.js";
|
|
@@ -37,12 +35,20 @@ import {
|
|
|
37
35
|
applyStreamingSubstitution,
|
|
38
36
|
applySubstitutions,
|
|
39
37
|
} from "../tools/sensitive-output-placeholders.js";
|
|
40
|
-
import {
|
|
38
|
+
import { ProviderError } from "../util/errors.js";
|
|
41
39
|
import { getLogger } from "../util/logger.js";
|
|
42
40
|
import { isRetryableNetworkError } from "../util/retry.js";
|
|
41
|
+
import { CompactionCircuit } from "./compaction-circuit.js";
|
|
43
42
|
|
|
44
43
|
const log = getLogger("agent-loop");
|
|
45
44
|
|
|
45
|
+
/** Fraction of the preflight budget at which a checkpoint triggers mid-loop compaction. */
|
|
46
|
+
const MID_LOOP_YIELD_THRESHOLD_RATIO = 0.85;
|
|
47
|
+
|
|
48
|
+
/** In-context message count above which the budget gate raises the safety-margin floor. */
|
|
49
|
+
const LONG_HISTORY_MESSAGE_THRESHOLD = 50;
|
|
50
|
+
const LONG_HISTORY_SAFETY_MARGIN_FLOOR = 0.15;
|
|
51
|
+
|
|
46
52
|
export interface AgentLoopConfig {
|
|
47
53
|
maxTokens: number;
|
|
48
54
|
maxInputTokens?: number; // context window size for tool result truncation
|
|
@@ -66,7 +72,39 @@ export interface CheckpointInfo {
|
|
|
66
72
|
history: Message[]; // current history snapshot for token estimation
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Why a checkpoint paused the loop. Surfaced back to the caller via
|
|
77
|
+
* {@link AgentLoopRunResult.exitReason} so the orchestrator reacts to
|
|
78
|
+
* the loop's own signal (hand off to a queued message vs. compact and
|
|
79
|
+
* re-enter) instead of the checkpoint callback mutating orchestrator state.
|
|
80
|
+
*/
|
|
81
|
+
export type ExitReason = "handoff" | "budget";
|
|
82
|
+
|
|
83
|
+
export type CheckpointDecision = "continue" | ExitReason;
|
|
84
|
+
|
|
85
|
+
/** Result of {@link AgentLoop.run}. */
|
|
86
|
+
export interface AgentLoopRunResult {
|
|
87
|
+
/** Full conversation history after the run, including everything appended this run. */
|
|
88
|
+
history: Message[];
|
|
89
|
+
/**
|
|
90
|
+
* Reason the loop paused at a checkpoint, or `null` on a terminal stop
|
|
91
|
+
* (completion, error, abort, or a tool-requested yield-to-user).
|
|
92
|
+
*/
|
|
93
|
+
exitReason: ExitReason | null;
|
|
94
|
+
/**
|
|
95
|
+
* Whether the loop produced at least one new assistant message this run —
|
|
96
|
+
* the forward-progress signal for the ordering-error retry gate and the
|
|
97
|
+
* overflow convergence fold (immune to in-loop compaction shrinking history
|
|
98
|
+
* below a pre-run length).
|
|
99
|
+
*/
|
|
100
|
+
appendedNewMessages: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Slice of `history` appended this run, measured from the loop's input or
|
|
103
|
+
* from the compacted base when it compacts in place. The loop owns this
|
|
104
|
+
* boundary, so it cannot desync the way an externally-held index can.
|
|
105
|
+
*/
|
|
106
|
+
newMessages: Message[];
|
|
107
|
+
}
|
|
70
108
|
|
|
71
109
|
/**
|
|
72
110
|
* Why an agent turn reached a terminal state.
|
|
@@ -89,8 +127,6 @@ export type CheckpointDecision = "continue" | "yield";
|
|
|
89
127
|
export type AgentLoopExitReason =
|
|
90
128
|
/** `if (signal?.aborted) break;` at the top of the loop. */
|
|
91
129
|
| "aborted_pre_call"
|
|
92
|
-
/** Empty assistant response after the configured retry budget. */
|
|
93
|
-
| "empty_response_exhausted"
|
|
94
130
|
/** Assistant message has no tool-use blocks (or no tool executor). */
|
|
95
131
|
| "no_tool_calls"
|
|
96
132
|
/** Signal aborted while building the user-side tool-results message. */
|
|
@@ -169,6 +205,14 @@ export type AgentEvent =
|
|
|
169
205
|
approvalReason?: string;
|
|
170
206
|
riskThreshold?: string;
|
|
171
207
|
activityMetadata?: ToolActivityMetadata;
|
|
208
|
+
/**
|
|
209
|
+
* Set when the loop synthesizes this result for a tool_use that never
|
|
210
|
+
* executed (a "Cancelled by user" block on abort). The daemon still
|
|
211
|
+
* captures it into `pendingToolResults` and forwards it to the client,
|
|
212
|
+
* but skips the side effects that assume the tool ran — marking the
|
|
213
|
+
* workspace dirty and emitting a post-tool "thinking" activity state.
|
|
214
|
+
*/
|
|
215
|
+
cancelled?: boolean;
|
|
172
216
|
}
|
|
173
217
|
| { type: "tool_use_preview_start"; toolUseId: string; toolName: string }
|
|
174
218
|
| {
|
|
@@ -203,7 +247,7 @@ export type AgentEvent =
|
|
|
203
247
|
| { type: "error"; error: Error }
|
|
204
248
|
| {
|
|
205
249
|
/**
|
|
206
|
-
* Emitted when the
|
|
250
|
+
* Emitted when the provider call throws — i.e. the provider
|
|
207
251
|
* rejected the request before returning a usable response. Carries
|
|
208
252
|
* the loop-level raw request we attempted to send (messages, tools,
|
|
209
253
|
* system prompt, provider-agnostic config) plus the thrown error.
|
|
@@ -246,6 +290,60 @@ export type AgentEvent =
|
|
|
246
290
|
*/
|
|
247
291
|
estimatedInputTokens?: number;
|
|
248
292
|
}
|
|
293
|
+
| {
|
|
294
|
+
/**
|
|
295
|
+
* Emitted when the loop begins compacting the running history because
|
|
296
|
+
* the mid-loop budget gate tripped. The daemon's event dispatcher
|
|
297
|
+
* translates it into a "compacting context" activity state so clients
|
|
298
|
+
* surface that the turn paused to summarize context.
|
|
299
|
+
*/
|
|
300
|
+
type: "context_compacting";
|
|
301
|
+
}
|
|
302
|
+
| {
|
|
303
|
+
/**
|
|
304
|
+
* Emitted after the loop's inline mid-loop compaction pipeline runs,
|
|
305
|
+
* immediately before re-injection — whether or not the pipeline actually
|
|
306
|
+
* compacted. The daemon's event dispatcher always commits `basis` (the
|
|
307
|
+
* stripped pre-compaction history) as the conversation's durable message
|
|
308
|
+
* state, so re-injection ({@link MidLoopCompaction.reinject}) re-applies
|
|
309
|
+
* injections onto the stripped base rather than stacking on top of the
|
|
310
|
+
* still-injected messages. When `result.compacted` is set it
|
|
311
|
+
* additionally commits the durable compaction result (DB-record fields,
|
|
312
|
+
* graph-memory side effects, SSE) and flips the per-turn re-injection
|
|
313
|
+
* guards on the handler state.
|
|
314
|
+
*
|
|
315
|
+
* Treated as a critical event: a failed durable commit re-throws so the
|
|
316
|
+
* turn aborts rather than re-injecting against half-applied state.
|
|
317
|
+
*
|
|
318
|
+
* `basis` is the stripped pre-compaction history the summary was built
|
|
319
|
+
* from; the dispatcher uses it to project Slack provenance onto the
|
|
320
|
+
* compacted result.
|
|
321
|
+
*/
|
|
322
|
+
type: "compaction_completed";
|
|
323
|
+
result: ContextWindowResult;
|
|
324
|
+
basis: Message[];
|
|
325
|
+
}
|
|
326
|
+
| {
|
|
327
|
+
/**
|
|
328
|
+
* Emitted right after the loop strips runtime injections from the
|
|
329
|
+
* running history, before the compaction pipeline runs. The daemon's
|
|
330
|
+
* event dispatcher records the history-stripped marker — a Conversation
|
|
331
|
+
* DB-record field read back at load time to strip embedded injection
|
|
332
|
+
* prefixes from pre-strip messages. Best-effort: a transient marker
|
|
333
|
+
* write must not abort the turn, so unlike `compaction_completed` this
|
|
334
|
+
* event is not treated as critical.
|
|
335
|
+
*/
|
|
336
|
+
type: "history_stripped";
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Circuit-breaker transitions emitted when auto-compaction is paused
|
|
340
|
+
* (`compaction_circuit_open`, after three consecutive summary-LLM
|
|
341
|
+
* failures) or resumed (`compaction_circuit_closed`). These are already
|
|
342
|
+
* in wire-contract shape; the daemon's event dispatcher forwards them to
|
|
343
|
+
* the client unchanged so the "auto-compaction paused" banner shows and
|
|
344
|
+
* dismisses.
|
|
345
|
+
*/
|
|
346
|
+
| CompactionCircuitEvent
|
|
249
347
|
| {
|
|
250
348
|
/**
|
|
251
349
|
* Emitted when an agent turn reaches a terminal state. Checkpoint
|
|
@@ -266,8 +364,7 @@ const DEFAULT_CONFIG: AgentLoopConfig = {
|
|
|
266
364
|
minTurnIntervalMs: 150,
|
|
267
365
|
};
|
|
268
366
|
|
|
269
|
-
const
|
|
270
|
-
const MAX_EMPTY_RESPONSE_RETRIES = 1;
|
|
367
|
+
const MAX_STOP_CONTINUE_RETRIES = 1;
|
|
271
368
|
const MAX_TOKENS_STOP_REASONS = new Set([
|
|
272
369
|
"length",
|
|
273
370
|
"max_output_tokens",
|
|
@@ -288,12 +385,11 @@ export function isMaxTokensStopReason(
|
|
|
288
385
|
* {@link AgentLoop.run}); this helper is the fallback used only by unit
|
|
289
386
|
* tests that construct `AgentLoop` directly without an orchestrator.
|
|
290
387
|
*
|
|
291
|
-
* When the orchestrator-supplied context is present
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* current tool-use iteration.
|
|
388
|
+
* When the orchestrator-supplied context is present it is used directly so the
|
|
389
|
+
* pipeline sees the real `conversationId`, trust, and `contextWindowManager`.
|
|
390
|
+
* In the fallback path the returned context is still useful for pipeline
|
|
391
|
+
* logging: `requestId` surfaces in every structured record, and `turnIndex`
|
|
392
|
+
* reflects the current tool-use iteration.
|
|
297
393
|
*/
|
|
298
394
|
function buildLoopTurnContext(
|
|
299
395
|
requestId: string | undefined,
|
|
@@ -313,29 +409,6 @@ function buildLoopTurnContext(
|
|
|
313
409
|
};
|
|
314
410
|
}
|
|
315
411
|
|
|
316
|
-
/**
|
|
317
|
-
* Produce a `TurnContext` for a pipeline call inside {@link AgentLoop.run}.
|
|
318
|
-
*
|
|
319
|
-
* When the orchestrator supplied a `turnContext`, clone it and overwrite
|
|
320
|
-
* `requestId` + `turnIndex` with the loop-scoped values so plugin log
|
|
321
|
-
* records correctly attribute the call to the current tool-use iteration
|
|
322
|
-
* while preserving the real `conversationId`, trust context, and
|
|
323
|
-
* `contextWindowManager` the orchestrator assembled for the turn. Without
|
|
324
|
-
* an orchestrator context (unit tests that instantiate `AgentLoop` with no
|
|
325
|
-
* `turnContext`), fall back to {@link buildLoopTurnContext}'s synthesized
|
|
326
|
-
* placeholder.
|
|
327
|
-
*/
|
|
328
|
-
function resolveLoopTurnContext(
|
|
329
|
-
base: TurnContext | undefined,
|
|
330
|
-
requestId: string | undefined,
|
|
331
|
-
turnIndex: number,
|
|
332
|
-
): TurnContext {
|
|
333
|
-
if (base) {
|
|
334
|
-
return { ...base, requestId: requestId ?? base.requestId, turnIndex };
|
|
335
|
-
}
|
|
336
|
-
return buildLoopTurnContext(requestId, turnIndex);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
412
|
/**
|
|
340
413
|
* User-config HTTP status codes that should never page the on-call: billing
|
|
341
414
|
* exhaustion (402), invalid credentials (401), and forbidden/plan-gated (403).
|
|
@@ -378,23 +451,91 @@ export interface ResolvedSystemPrompt {
|
|
|
378
451
|
model?: string;
|
|
379
452
|
}
|
|
380
453
|
|
|
454
|
+
/**
|
|
455
|
+
* Orchestrator-supplied hook the loop invokes when the mid-loop budget gate
|
|
456
|
+
* trips and inline compaction runs. The loop owns the trigger, the
|
|
457
|
+
* compaction call, the result interpretation (circuit-breaker
|
|
458
|
+
* bookkeeping + the exhaustion decision), and the inline continue; this hook
|
|
459
|
+
* bridges the injection state the loop is intentionally blind to. Durable
|
|
460
|
+
* persistence is signalled out-of-band via the `history_stripped` (marker)
|
|
461
|
+
* and `compaction_completed` (basis commit + successful summary) {@link
|
|
462
|
+
* AgentEvent}s; the {@link MidLoopCompaction.postCompactionHook} is
|
|
463
|
+
* orchestrator-supplied, and its inputs migrate loop-ward as the loop
|
|
464
|
+
* subsumes the re-injection ceremony.
|
|
465
|
+
*/
|
|
466
|
+
export interface MidLoopCompaction {
|
|
467
|
+
/**
|
|
468
|
+
* Re-apply runtime injections onto the post-compaction history and return
|
|
469
|
+
* the history to continue from. The loop supplies its own working state via
|
|
470
|
+
* {@link PostCompactionHookInput} so the hook re-injects from that rather
|
|
471
|
+
* than reading it back from orchestrator state.
|
|
472
|
+
*/
|
|
473
|
+
postCompactionHook: (input: PostCompactionHookInput) => Promise<Message[]>;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export interface AgentLoopRunOptions {
|
|
477
|
+
signal?: AbortSignal;
|
|
478
|
+
requestId?: string;
|
|
479
|
+
onCheckpoint?: (
|
|
480
|
+
checkpoint: CheckpointInfo,
|
|
481
|
+
) => CheckpointDecision | Promise<CheckpointDecision>;
|
|
482
|
+
callSite?: LLMCallSite;
|
|
483
|
+
/**
|
|
484
|
+
* Per-turn context supplied by the orchestrator. Every pipeline
|
|
485
|
+
* invocation inside the loop clones from this value (overwriting only
|
|
486
|
+
* `turnIndex`/`requestId`) so middleware sees the real conversation
|
|
487
|
+
* identity, trust class, and `contextWindowManager` rather than the
|
|
488
|
+
* `"agent-loop"` sentinel used when the loop is instantiated standalone
|
|
489
|
+
* in unit tests.
|
|
490
|
+
*/
|
|
491
|
+
turnContext?: TurnContext;
|
|
492
|
+
/**
|
|
493
|
+
* Ad-hoc inference-profile override applied to every LLM call the loop
|
|
494
|
+
* issues. When set, each `SendMessageOptions.config` carries
|
|
495
|
+
* `overrideProfile = <name>` so the provider's resolver layers
|
|
496
|
+
* `llm.profiles[<name>]` between the workspace `activeProfile` and any
|
|
497
|
+
* call-site named profile. Missing profile names silently fall through.
|
|
498
|
+
*/
|
|
499
|
+
overrideProfile?: string;
|
|
500
|
+
resolveOverrideProfile?: () => string | undefined;
|
|
501
|
+
/**
|
|
502
|
+
* Resolves the orchestrator's effective context window for this turn: the
|
|
503
|
+
* provider max-input-token ceiling (read by tool-result truncation) plus the
|
|
504
|
+
* `overflowRecovery` config that drives the mid-loop budget gate. Resolved
|
|
505
|
+
* fresh per checkpoint so a mid-turn profile change is reflected. Absent →
|
|
506
|
+
* truncation falls back to `this.config.maxInputTokens` and the budget gate
|
|
507
|
+
* is skipped (agent wakes pass `overflowRecovery.enabled = false`).
|
|
508
|
+
*/
|
|
509
|
+
resolveContextWindow?: () => {
|
|
510
|
+
maxInputTokens: number;
|
|
511
|
+
overflowRecovery: { enabled: boolean; safetyMarginRatio: number };
|
|
512
|
+
};
|
|
513
|
+
/**
|
|
514
|
+
* Hooks for inline mid-loop compaction. When supplied and the budget gate
|
|
515
|
+
* trips, the loop compacts in place and continues instead of yielding
|
|
516
|
+
* `exitReason = "budget"`. Callers without a compaction path (agent wakes,
|
|
517
|
+
* convergence/auto-compress reruns) omit this and keep yielding for budget.
|
|
518
|
+
*/
|
|
519
|
+
compaction?: MidLoopCompaction;
|
|
520
|
+
/**
|
|
521
|
+
* When true, the latest user message carries a volatile per-turn block
|
|
522
|
+
* (e.g. a memory-v3 `<memory>` injection) that varies across otherwise
|
|
523
|
+
* identical turns. Forwarded to each `SendMessageOptions.config` so the
|
|
524
|
+
* provider anchors its long-TTL cache breakpoint on the most recent STABLE
|
|
525
|
+
* user message instead of the volatile latest one, keeping the cached
|
|
526
|
+
* prefix reusable. Default unset → existing behavior.
|
|
527
|
+
*/
|
|
528
|
+
mutableLatestUserMessage?: boolean;
|
|
529
|
+
}
|
|
530
|
+
|
|
381
531
|
/**
|
|
382
532
|
* Callback shape the loop uses to execute a tool invocation.
|
|
383
|
-
*
|
|
384
|
-
* The trailing `turnContext` is optional so in-process tests that wire the
|
|
385
|
-
* callback without an orchestrator keep working. Production sites (the
|
|
386
|
-
* `Conversation`'s `createToolExecutor`) forward the supplied context into
|
|
387
|
-
* `ToolExecutor.execute` so the `toolExecute` pipeline sees the orchestrator's
|
|
388
|
-
* real conversation identity/trust/contextWindowManager instead of the
|
|
389
|
-
* synthesized placeholder `ToolExecutor` would otherwise build from the
|
|
390
|
-
* `ToolContext` alone.
|
|
391
533
|
*/
|
|
392
534
|
export type LoopToolExecutor = (
|
|
393
535
|
name: string,
|
|
394
536
|
input: Record<string, unknown>,
|
|
395
537
|
onOutput?: (chunk: string) => void,
|
|
396
538
|
toolUseId?: string,
|
|
397
|
-
turnContext?: TurnContext,
|
|
398
539
|
) => Promise<{
|
|
399
540
|
content: string;
|
|
400
541
|
isError: boolean;
|
|
@@ -425,6 +566,20 @@ export type LoopToolExecutor = (
|
|
|
425
566
|
activityMetadata?: ToolActivityMetadata;
|
|
426
567
|
}>;
|
|
427
568
|
|
|
569
|
+
export interface AgentLoopConstructorOptions {
|
|
570
|
+
config?: Partial<AgentLoopConfig>;
|
|
571
|
+
tools?: ToolDefinition[];
|
|
572
|
+
toolExecutor?: LoopToolExecutor;
|
|
573
|
+
resolveTools?: (history: Message[]) => ToolDefinition[];
|
|
574
|
+
resolveSystemPrompt?: (history: Message[]) => ResolvedSystemPrompt;
|
|
575
|
+
/**
|
|
576
|
+
* Conversation this loop drives. Used to scope the loop-held compaction
|
|
577
|
+
* circuit breaker; defaults to an empty key for test loops that never
|
|
578
|
+
* exercise compaction.
|
|
579
|
+
*/
|
|
580
|
+
conversationId?: string;
|
|
581
|
+
}
|
|
582
|
+
|
|
428
583
|
export class AgentLoop {
|
|
429
584
|
private provider: Provider;
|
|
430
585
|
private systemPrompt: string;
|
|
@@ -436,15 +591,28 @@ export class AgentLoop {
|
|
|
436
591
|
| null;
|
|
437
592
|
private toolExecutor: LoopToolExecutor | null;
|
|
438
593
|
|
|
594
|
+
/**
|
|
595
|
+
* Loop-held compaction circuit breaker. The loop has a 1:1 lifetime with its
|
|
596
|
+
* conversation, so it is the source of truth for the cross-turn failure
|
|
597
|
+
* counter and cooldown deadline. Non-loop callers (the orchestrator's
|
|
598
|
+
* compaction paths, `Conversation.forceCompact`, and the dev-only playground
|
|
599
|
+
* routes) reach it via `agentLoop.compactionCircuit`.
|
|
600
|
+
*/
|
|
601
|
+
readonly compactionCircuit: CompactionCircuit;
|
|
602
|
+
|
|
439
603
|
constructor(
|
|
440
604
|
provider: Provider,
|
|
441
605
|
systemPrompt: string,
|
|
442
|
-
|
|
443
|
-
tools?: ToolDefinition[],
|
|
444
|
-
toolExecutor?: LoopToolExecutor,
|
|
445
|
-
resolveTools?: (history: Message[]) => ToolDefinition[],
|
|
446
|
-
resolveSystemPrompt?: (history: Message[]) => ResolvedSystemPrompt,
|
|
606
|
+
options?: AgentLoopConstructorOptions,
|
|
447
607
|
) {
|
|
608
|
+
const {
|
|
609
|
+
config,
|
|
610
|
+
tools,
|
|
611
|
+
toolExecutor,
|
|
612
|
+
resolveTools,
|
|
613
|
+
resolveSystemPrompt,
|
|
614
|
+
conversationId,
|
|
615
|
+
} = options ?? {};
|
|
448
616
|
this.provider = provider;
|
|
449
617
|
this.systemPrompt = systemPrompt;
|
|
450
618
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
@@ -452,16 +620,16 @@ export class AgentLoop {
|
|
|
452
620
|
this.resolveTools = resolveTools ?? null;
|
|
453
621
|
this.resolveSystemPrompt = resolveSystemPrompt ?? null;
|
|
454
622
|
this.toolExecutor = toolExecutor ?? null;
|
|
623
|
+
this.compactionCircuit = new CompactionCircuit(conversationId ?? "");
|
|
455
624
|
}
|
|
456
625
|
|
|
457
626
|
/**
|
|
458
627
|
* Resolve the tool definitions sent to the provider for the given turn.
|
|
459
628
|
*
|
|
460
629
|
* Mirrors the logic of {@link getToolTokenBudget} but returns the tool
|
|
461
|
-
* array itself — callers that need to thread the tool set into
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
* resolver fork.
|
|
630
|
+
* array itself — callers that need to thread the tool set into the token
|
|
631
|
+
* estimate (`estimatePromptTokensWithTools`, whose args include `tools`)
|
|
632
|
+
* use this rather than re-implementing the dynamic-vs-static resolver fork.
|
|
465
633
|
*/
|
|
466
634
|
getResolvedTools(history?: Message[]): ToolDefinition[] {
|
|
467
635
|
return history && this.resolveTools
|
|
@@ -481,58 +649,167 @@ export class AgentLoop {
|
|
|
481
649
|
return estimateToolsTokens(this.getResolvedTools(history));
|
|
482
650
|
}
|
|
483
651
|
|
|
652
|
+
/**
|
|
653
|
+
* Calibrated prompt-token estimate for `history`, including the
|
|
654
|
+
* resolved-tool budget for the turn.
|
|
655
|
+
*/
|
|
656
|
+
private estimateTokens(history: Message[]): number {
|
|
657
|
+
return estimatePromptTokensWithTools(
|
|
658
|
+
history,
|
|
659
|
+
this.systemPrompt,
|
|
660
|
+
this.getResolvedTools(history),
|
|
661
|
+
getCalibrationProviderKey(this.provider),
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Record a compaction outcome against the loop's circuit breaker. Three
|
|
667
|
+
* consecutive failures trip a cooldown that suspends auto-compaction; a
|
|
668
|
+
* success resets the counter. Any open/closed transition is emitted on the
|
|
669
|
+
* loop's own event channel via `onEvent`.
|
|
670
|
+
*
|
|
671
|
+
* Bookkeeping is best-effort — a failure here must not turn a recoverable
|
|
672
|
+
* compaction outcome into a user-visible turn failure.
|
|
673
|
+
*/
|
|
674
|
+
private async recordCompactionOutcome(
|
|
675
|
+
turnContext: TurnContext,
|
|
676
|
+
summaryFailed: boolean,
|
|
677
|
+
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
678
|
+
): Promise<void> {
|
|
679
|
+
try {
|
|
680
|
+
await this.compactionCircuit.recordOutcome(summaryFailed, onEvent);
|
|
681
|
+
} catch (recordError) {
|
|
682
|
+
log.error(
|
|
683
|
+
{ err: recordError, requestId: turnContext.requestId },
|
|
684
|
+
"Recording a compaction outcome against the circuit breaker failed; suppressing to keep the agent loop alive",
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Compact the running history in place when the mid-loop budget gate trips.
|
|
691
|
+
*
|
|
692
|
+
* Calls the default compaction plugin on the stripped history, then
|
|
693
|
+
* re-applies injections via the supplied hooks. Returns the history to
|
|
694
|
+
* continue from, or `null` when the compactor exhausted its retry budget so
|
|
695
|
+
* the caller yields `exitReason = "budget"` and the orchestrator escalates.
|
|
696
|
+
*/
|
|
697
|
+
private async compact(
|
|
698
|
+
history: Message[],
|
|
699
|
+
turnContext: TurnContext,
|
|
700
|
+
compaction: MidLoopCompaction,
|
|
701
|
+
signal: AbortSignal | undefined,
|
|
702
|
+
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
703
|
+
overrideProfile: string | null,
|
|
704
|
+
): Promise<Message[] | null> {
|
|
705
|
+
await onEvent({ type: "context_compacting" });
|
|
706
|
+
// Strip runtime injections so the compactor summarizes the raw persistent
|
|
707
|
+
// messages.
|
|
708
|
+
const rawHistory = stripInjectionsForCompaction(history);
|
|
709
|
+
// Record the history-stripped marker right after stripping, before the
|
|
710
|
+
// pipeline runs.
|
|
711
|
+
await onEvent({ type: "history_stripped" });
|
|
712
|
+
const manager = turnContext.contextWindowManager;
|
|
713
|
+
if (manager == null) {
|
|
714
|
+
throw new PluginExecutionError(
|
|
715
|
+
"default-compaction: turnContext.contextWindowManager is missing — orchestrator must attach it before invoking compaction",
|
|
716
|
+
DEFAULT_COMPACTION_PLUGIN_NAME,
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
// The mid-loop budget gate is reached only when this turn decides to
|
|
720
|
+
// compact in place, so `force` past the auto-threshold check.
|
|
721
|
+
// `actorTrustClass` comes from the turn context (the actor whose turn
|
|
722
|
+
// triggered compaction) so the compactor's image manifest excludes
|
|
723
|
+
// guardian-only attachments for untrusted actors. `overrideProfile` is the
|
|
724
|
+
// turn's resolved inference-profile override for the summary call.
|
|
725
|
+
const compactResult = await defaultCompact({
|
|
726
|
+
manager,
|
|
727
|
+
messages: rawHistory,
|
|
728
|
+
signal,
|
|
729
|
+
force: true,
|
|
730
|
+
actorTrustClass: turnContext.trust.trustClass,
|
|
731
|
+
overrideProfile,
|
|
732
|
+
});
|
|
733
|
+
// `force: true` bypasses the auto-threshold gate, but early returns
|
|
734
|
+
// for "no eligible messages" / "insufficient messages" still leave
|
|
735
|
+
// `summaryFailed` undefined. Only record an outcome when the summary LLM
|
|
736
|
+
// actually ran.
|
|
737
|
+
if (compactResult.summaryFailed !== undefined) {
|
|
738
|
+
await this.recordCompactionOutcome(
|
|
739
|
+
turnContext,
|
|
740
|
+
compactResult.summaryFailed,
|
|
741
|
+
onEvent,
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
// Emit unconditionally: the dispatcher commits the stripped `basis` as the
|
|
745
|
+
// durable message base whether or not the pipeline compacted (re-injection
|
|
746
|
+
// reads it), and runs the durable compaction commit only when
|
|
747
|
+
// `result.compacted`.
|
|
748
|
+
await onEvent({
|
|
749
|
+
type: "compaction_completed",
|
|
750
|
+
result: compactResult,
|
|
751
|
+
basis: rawHistory,
|
|
752
|
+
});
|
|
753
|
+
if (compactResult.exhausted ?? false) {
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
// Re-inject onto the same base the `compaction_completed` dispatch commits:
|
|
757
|
+
// the compacted messages when the pipeline compacted, the stripped
|
|
758
|
+
// pre-compaction history otherwise.
|
|
759
|
+
return compaction.postCompactionHook({
|
|
760
|
+
history: compactResult.compacted ? compactResult.messages : rawHistory,
|
|
761
|
+
turnContext,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
484
765
|
async run(
|
|
485
766
|
messages: Message[],
|
|
486
767
|
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
* Used by per-conversation pinned profiles to override the workspace
|
|
509
|
-
* default for the lifetime of an agent loop run.
|
|
510
|
-
*/
|
|
511
|
-
overrideProfile?: string,
|
|
512
|
-
effectiveMaxInputTokens?: number,
|
|
513
|
-
resolveOverrideProfile?: () => string | undefined,
|
|
514
|
-
resolveEffectiveMaxInputTokens?: () => number | undefined,
|
|
515
|
-
): Promise<Message[]> {
|
|
516
|
-
const history = [...messages];
|
|
517
|
-
const initialHistoryLength = messages.length;
|
|
768
|
+
options?: AgentLoopRunOptions,
|
|
769
|
+
): Promise<AgentLoopRunResult> {
|
|
770
|
+
const {
|
|
771
|
+
signal,
|
|
772
|
+
requestId,
|
|
773
|
+
onCheckpoint,
|
|
774
|
+
callSite,
|
|
775
|
+
turnContext,
|
|
776
|
+
overrideProfile,
|
|
777
|
+
resolveOverrideProfile,
|
|
778
|
+
resolveContextWindow,
|
|
779
|
+
compaction,
|
|
780
|
+
mutableLatestUserMessage,
|
|
781
|
+
} = options ?? {};
|
|
782
|
+
let history = [...messages];
|
|
783
|
+
// Index into `history` where this run's appended output begins. It starts
|
|
784
|
+
// after the input and resets to the compacted base whenever the loop
|
|
785
|
+
// compacts in place, so `history.slice(newMessagesStart)` is always exactly
|
|
786
|
+
// what the loop produced since the last (re-injected) base.
|
|
787
|
+
let newMessagesStart = history.length;
|
|
788
|
+
let producedVisibleTextThisRun = false;
|
|
518
789
|
let toolUseTurns = 0;
|
|
519
|
-
let
|
|
520
|
-
let emptyResponseRetries = 0;
|
|
790
|
+
let stopContinueRetries = 0;
|
|
521
791
|
let lastLlmCallTime = 0;
|
|
792
|
+
let exitReason: ExitReason | null = null;
|
|
793
|
+
let appendedNewMessages = false;
|
|
522
794
|
const rlog = requestId ? log.child({ requestId }) : log;
|
|
523
795
|
|
|
796
|
+
// Resolve the inference-profile override that applies right now. The
|
|
797
|
+
// optional resolver lets a turn observe a confirmed mid-turn profile switch
|
|
798
|
+
// before the next model call; absent a resolver the turn-start value holds.
|
|
799
|
+
const resolveEffectiveOverrideProfile = (): string | undefined =>
|
|
800
|
+
resolveOverrideProfile ? resolveOverrideProfile() : overrideProfile;
|
|
801
|
+
|
|
524
802
|
// Per-run substitution map for sensitive output placeholders.
|
|
525
803
|
// Bindings are accumulated from tool results; placeholders are
|
|
526
804
|
// resolved in streamed deltas and final assistant message text.
|
|
527
805
|
const substitutionMap = new Map<string, string>();
|
|
528
806
|
let streamingPending = "";
|
|
529
807
|
|
|
530
|
-
// Idempotency guard for `emitExit
|
|
531
|
-
//
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
//
|
|
535
|
-
// double-emits if a new break site is added without checking this.
|
|
808
|
+
// Idempotency guard for `emitExit`: the first reason stamped wins. A break
|
|
809
|
+
// site that stamps a specific reason before unwinding into the catch
|
|
810
|
+
// handler keeps that reason instead of the generic "error", and the guard
|
|
811
|
+
// also defends against accidental double-emits if a new break site is
|
|
812
|
+
// added without checking this.
|
|
536
813
|
let exitReasonEmitted = false;
|
|
537
814
|
const emitExit = async (reason: AgentLoopExitReason): Promise<void> => {
|
|
538
815
|
if (exitReasonEmitted) return;
|
|
@@ -617,6 +894,15 @@ export class AgentLoop {
|
|
|
617
894
|
providerConfig.cacheTtl = this.config.cacheTtl;
|
|
618
895
|
}
|
|
619
896
|
|
|
897
|
+
// Cache-anchor signal for volatile latest-user-message turns (e.g.
|
|
898
|
+
// memory-v3 injects its `<memory>` block into the latest user
|
|
899
|
+
// message). Not part of the call-site schema, so it is always sourced
|
|
900
|
+
// from the per-run option regardless of `callSite`. Only set when true
|
|
901
|
+
// so the wire/config stays byte-identical when off.
|
|
902
|
+
if (mutableLatestUserMessage) {
|
|
903
|
+
providerConfig.mutableLatestUserMessage = true;
|
|
904
|
+
}
|
|
905
|
+
|
|
620
906
|
// Per-call LLM call-site identifier. Surfaces on the per-call
|
|
621
907
|
// `config.callSite` so `RetryProvider.normalizeSendMessageOptions`
|
|
622
908
|
// can route through `resolveCallSiteConfig` against
|
|
@@ -644,12 +930,8 @@ export class AgentLoop {
|
|
|
644
930
|
// `activeProfile` and any call-site named profile. Threading it on
|
|
645
931
|
// every send (rather than once at construction) keeps subagents that
|
|
646
932
|
// share an `AgentLoop` instance but ought to inherit a different
|
|
647
|
-
// profile correct — and matches how `callSite` is plumbed.
|
|
648
|
-
|
|
649
|
-
// profile-session switch before the next model call.
|
|
650
|
-
const effectiveOverrideProfile = resolveOverrideProfile
|
|
651
|
-
? resolveOverrideProfile()
|
|
652
|
-
: overrideProfile;
|
|
933
|
+
// profile correct — and matches how `callSite` is plumbed.
|
|
934
|
+
const effectiveOverrideProfile = resolveEffectiveOverrideProfile();
|
|
653
935
|
if (effectiveOverrideProfile) {
|
|
654
936
|
providerConfig.overrideProfile = effectiveOverrideProfile;
|
|
655
937
|
}
|
|
@@ -692,98 +974,79 @@ export class AgentLoop {
|
|
|
692
974
|
// Also strip old AX tree snapshots to keep TTFT from growing
|
|
693
975
|
// linearly with step count in computer-use sessions.
|
|
694
976
|
const providerHistory = compactAxTreeHistory(
|
|
695
|
-
|
|
977
|
+
stripOldMediaBlocks(history),
|
|
696
978
|
);
|
|
697
979
|
|
|
698
|
-
//
|
|
699
|
-
//
|
|
700
|
-
//
|
|
701
|
-
|
|
702
|
-
// `next(args)`. The default `defaultLlmCallPlugin` contributes a
|
|
703
|
-
// passthrough middleware that forwards to `next(args)` — it
|
|
704
|
-
// registers at module load and sits at the outermost onion layer,
|
|
705
|
-
// so it must yield to keep user-registered `llmCall` middleware
|
|
706
|
-
// reachable. Timeout is `null` (`DEFAULT_TIMEOUTS.llmCall`) — the
|
|
707
|
-
// provider layer already enforces its own HTTP-level budgets.
|
|
708
|
-
//
|
|
709
|
-
// The `onEvent` wrapping is kept inside `args.options` so substitution
|
|
710
|
-
// and streaming behavior exactly match the pre-pipeline call site.
|
|
711
|
-
const llmCallArgs: LLMCallArgs = {
|
|
712
|
-
provider: this.provider,
|
|
713
|
-
messages: providerHistory,
|
|
980
|
+
// The `onEvent` wrapping below applies sensitive-output placeholder
|
|
981
|
+
// substitution to streamed text while forwarding every other event
|
|
982
|
+
// type through unchanged.
|
|
983
|
+
const providerOptions: SendMessageOptions = {
|
|
714
984
|
tools: currentTools.length > 0 ? currentTools : undefined,
|
|
715
985
|
systemPrompt: turnSystemPrompt,
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
onEvent({ type: "text_delta", text: emit });
|
|
730
|
-
}
|
|
731
|
-
} else {
|
|
732
|
-
onEvent({ type: "text_delta", text: event.text });
|
|
986
|
+
config: providerConfig,
|
|
987
|
+
onEvent: (event) => {
|
|
988
|
+
if (event.type === "text_delta") {
|
|
989
|
+
// Apply sensitive-output placeholder substitution (chunk-safe)
|
|
990
|
+
if (substitutionMap.size > 0) {
|
|
991
|
+
const combined = streamingPending + event.text;
|
|
992
|
+
const { emit, pending } = applyStreamingSubstitution(
|
|
993
|
+
combined,
|
|
994
|
+
substitutionMap,
|
|
995
|
+
);
|
|
996
|
+
streamingPending = pending;
|
|
997
|
+
if (emit.length > 0) {
|
|
998
|
+
onEvent({ type: "text_delta", text: emit });
|
|
733
999
|
}
|
|
734
|
-
} else
|
|
735
|
-
onEvent({ type: "
|
|
736
|
-
} else if (event.type === "tool_use_preview_start") {
|
|
737
|
-
onEvent({
|
|
738
|
-
type: "tool_use_preview_start",
|
|
739
|
-
toolUseId: event.toolUseId,
|
|
740
|
-
toolName: event.toolName,
|
|
741
|
-
});
|
|
742
|
-
} else if (event.type === "input_json_delta") {
|
|
743
|
-
onEvent({
|
|
744
|
-
type: "input_json_delta",
|
|
745
|
-
toolName: event.toolName,
|
|
746
|
-
toolUseId: event.toolUseId,
|
|
747
|
-
accumulatedJson: event.accumulatedJson,
|
|
748
|
-
});
|
|
749
|
-
} else if (event.type === "server_tool_start") {
|
|
750
|
-
onEvent({
|
|
751
|
-
type: "server_tool_start",
|
|
752
|
-
name: event.name,
|
|
753
|
-
toolUseId: event.toolUseId,
|
|
754
|
-
input: event.input,
|
|
755
|
-
});
|
|
756
|
-
} else if (event.type === "server_tool_complete") {
|
|
757
|
-
onEvent({
|
|
758
|
-
type: "server_tool_complete",
|
|
759
|
-
toolUseId: event.toolUseId,
|
|
760
|
-
isError: event.isError,
|
|
761
|
-
...(event.content ? { content: event.content } : {}),
|
|
762
|
-
...(event.resolvedInput
|
|
763
|
-
? { resolvedInput: event.resolvedInput }
|
|
764
|
-
: {}),
|
|
765
|
-
...(event.errorCode ? { errorCode: event.errorCode } : {}),
|
|
766
|
-
...(event.errorMessage
|
|
767
|
-
? { errorMessage: event.errorMessage }
|
|
768
|
-
: {}),
|
|
769
|
-
});
|
|
1000
|
+
} else {
|
|
1001
|
+
onEvent({ type: "text_delta", text: event.text });
|
|
770
1002
|
}
|
|
771
|
-
}
|
|
772
|
-
|
|
1003
|
+
} else if (event.type === "thinking_delta") {
|
|
1004
|
+
onEvent({ type: "thinking_delta", thinking: event.thinking });
|
|
1005
|
+
} else if (event.type === "tool_use_preview_start") {
|
|
1006
|
+
onEvent({
|
|
1007
|
+
type: "tool_use_preview_start",
|
|
1008
|
+
toolUseId: event.toolUseId,
|
|
1009
|
+
toolName: event.toolName,
|
|
1010
|
+
});
|
|
1011
|
+
} else if (event.type === "input_json_delta") {
|
|
1012
|
+
onEvent({
|
|
1013
|
+
type: "input_json_delta",
|
|
1014
|
+
toolName: event.toolName,
|
|
1015
|
+
toolUseId: event.toolUseId,
|
|
1016
|
+
accumulatedJson: event.accumulatedJson,
|
|
1017
|
+
});
|
|
1018
|
+
} else if (event.type === "server_tool_start") {
|
|
1019
|
+
onEvent({
|
|
1020
|
+
type: "server_tool_start",
|
|
1021
|
+
name: event.name,
|
|
1022
|
+
toolUseId: event.toolUseId,
|
|
1023
|
+
input: event.input,
|
|
1024
|
+
});
|
|
1025
|
+
} else if (event.type === "server_tool_complete") {
|
|
1026
|
+
onEvent({
|
|
1027
|
+
type: "server_tool_complete",
|
|
1028
|
+
toolUseId: event.toolUseId,
|
|
1029
|
+
isError: event.isError,
|
|
1030
|
+
...(event.content ? { content: event.content } : {}),
|
|
1031
|
+
...(event.resolvedInput
|
|
1032
|
+
? { resolvedInput: event.resolvedInput }
|
|
1033
|
+
: {}),
|
|
1034
|
+
...(event.errorCode ? { errorCode: event.errorCode } : {}),
|
|
1035
|
+
...(event.errorMessage
|
|
1036
|
+
? { errorMessage: event.errorMessage }
|
|
1037
|
+
: {}),
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
773
1040
|
},
|
|
1041
|
+
signal,
|
|
774
1042
|
};
|
|
775
1043
|
|
|
776
|
-
// Per-turn pipeline context.
|
|
777
|
-
// `turnContext` into `run()
|
|
778
|
-
//
|
|
779
|
-
//
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
const turnCtx = resolveLoopTurnContext(
|
|
783
|
-
turnContext,
|
|
784
|
-
requestId,
|
|
785
|
-
toolUseTurns,
|
|
786
|
-
);
|
|
1044
|
+
// Per-turn pipeline context. Real call sites thread a full
|
|
1045
|
+
// `turnContext` into `run()` and it is used directly; standalone
|
|
1046
|
+
// unit-test instantiations that never plumb a context through fall
|
|
1047
|
+
// back to a synthesized placeholder scoped to the tool-use iteration.
|
|
1048
|
+
const turnCtx =
|
|
1049
|
+
turnContext ?? buildLoopTurnContext(requestId, toolUseTurns);
|
|
787
1050
|
|
|
788
1051
|
// Announce the LLM-call boundary so downstream handlers (the
|
|
789
1052
|
// daemon's persistence pipeline) can reserve an empty assistant row
|
|
@@ -806,21 +1069,11 @@ export class AgentLoop {
|
|
|
806
1069
|
// `llm_request_logs` row, then re-throw so the existing outer catch
|
|
807
1070
|
// continues to handle abort sync, Sentry capture, the `error` event,
|
|
808
1071
|
// and the loop break unchanged.
|
|
809
|
-
let response:
|
|
1072
|
+
let response: ProviderResponse;
|
|
810
1073
|
try {
|
|
811
|
-
response = await
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
(args) =>
|
|
815
|
-
args.provider.sendMessage(
|
|
816
|
-
args.messages,
|
|
817
|
-
args.tools,
|
|
818
|
-
args.systemPrompt,
|
|
819
|
-
args.options,
|
|
820
|
-
),
|
|
821
|
-
llmCallArgs,
|
|
822
|
-
turnCtx,
|
|
823
|
-
DEFAULT_TIMEOUTS.llmCall,
|
|
1074
|
+
response = await this.provider.sendMessage(
|
|
1075
|
+
providerHistory,
|
|
1076
|
+
providerOptions,
|
|
824
1077
|
);
|
|
825
1078
|
} catch (llmCallError) {
|
|
826
1079
|
// Skip recording on abort — the user cancelled the request and
|
|
@@ -838,10 +1091,10 @@ export class AgentLoop {
|
|
|
838
1091
|
// misrepresent both.
|
|
839
1092
|
const rawRequest = {
|
|
840
1093
|
provider: this.provider.name,
|
|
841
|
-
messages:
|
|
842
|
-
tools:
|
|
843
|
-
systemPrompt:
|
|
844
|
-
config:
|
|
1094
|
+
messages: providerHistory,
|
|
1095
|
+
tools: providerOptions.tools,
|
|
1096
|
+
systemPrompt: providerOptions.systemPrompt,
|
|
1097
|
+
config: providerOptions.config,
|
|
845
1098
|
};
|
|
846
1099
|
onEvent({
|
|
847
1100
|
type: "provider_error",
|
|
@@ -930,6 +1183,7 @@ export class AgentLoop {
|
|
|
930
1183
|
"LLM response reached output token limit",
|
|
931
1184
|
);
|
|
932
1185
|
history.push(safeAssistantMessage);
|
|
1186
|
+
appendedNewMessages = true;
|
|
933
1187
|
await onEvent({
|
|
934
1188
|
type: "max_tokens_reached",
|
|
935
1189
|
stopReason: response.stopReason,
|
|
@@ -942,130 +1196,65 @@ export class AgentLoop {
|
|
|
942
1196
|
break;
|
|
943
1197
|
}
|
|
944
1198
|
|
|
945
|
-
//
|
|
946
|
-
//
|
|
947
|
-
//
|
|
948
|
-
// the
|
|
949
|
-
//
|
|
950
|
-
// Only nudge when the model hasn't already delivered text to the user
|
|
951
|
-
// earlier in this tool-use chain. If a prior assistant turn in history
|
|
952
|
-
// contained visible text (e.g. the model said its piece before calling
|
|
953
|
-
// a side-effect tool like `remember`), an empty follow-up is the model
|
|
954
|
-
// correctly ending its turn — nudging would mislead it into thinking
|
|
955
|
-
// its earlier text didn't land and cause a verbatim re-send.
|
|
956
|
-
//
|
|
957
|
-
// Note: we check ANY prior assistant turn from this run()
|
|
958
|
-
// invocation, not just the most recent one. In multi-step tool-use
|
|
959
|
-
// chains (say-something → call-tool → call-another-tool → end),
|
|
960
|
-
// the "say-something" text lives on an earlier assistant turn while
|
|
961
|
-
// the most recent assistant turn is a pure tool_use with no text.
|
|
962
|
-
// Restricting the check to the most recent assistant turn would
|
|
963
|
-
// falsely nudge in that case and trigger a duplicate re-send of
|
|
964
|
-
// text the user already saw.
|
|
965
|
-
//
|
|
966
|
-
// Scope the scan to messages appended during this run() call only.
|
|
967
|
-
// Assistant text from prior conversation turns (earlier run()
|
|
968
|
-
// invocations passed in via `messages`) must NOT suppress the
|
|
969
|
-
// nudge — those turns completed long ago and have no bearing on
|
|
970
|
-
// whether the current tool-use chain has delivered text yet.
|
|
971
|
-
//
|
|
972
|
-
// The actual decision (nudge vs. accept vs. error) is delegated to
|
|
973
|
-
// the `emptyResponse` plugin pipeline. The pipeline returns a
|
|
974
|
-
// decision; the loop carries out the side-effect (pushing the nudge
|
|
975
|
-
// or surfacing the error). See `plugins/defaults/empty-response.ts`
|
|
976
|
-
// for the default decision logic.
|
|
1199
|
+
// The model's "stop" moment: a response with no tool calls is about to
|
|
1200
|
+
// yield to the user. The `stop` hook (below) decides whether to accept
|
|
1201
|
+
// the turn or re-query with a follow-up; `priorAssistantHadVisibleText`
|
|
1202
|
+
// gates the ops log for the post-tool empty case.
|
|
977
1203
|
const hasVisibleText = response.content.some(
|
|
978
1204
|
(block) => block.type === "text" && block.text.trim().length > 0,
|
|
979
1205
|
);
|
|
980
|
-
const priorAssistantHadVisibleText =
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
if (msg.role !== "assistant") continue;
|
|
984
|
-
const hasText = msg.content.some(
|
|
985
|
-
(block) =>
|
|
986
|
-
block.type === "text" &&
|
|
987
|
-
typeof (block as { text?: unknown }).text === "string" &&
|
|
988
|
-
(block as { text: string }).text.trim().length > 0,
|
|
989
|
-
);
|
|
990
|
-
if (hasText) return true;
|
|
991
|
-
}
|
|
992
|
-
return false;
|
|
993
|
-
})();
|
|
994
|
-
|
|
995
|
-
const emptyResponseArgs: EmptyResponseArgs = {
|
|
996
|
-
responseContent: response.content,
|
|
997
|
-
toolUseBlocksLength: toolUseBlocks.length,
|
|
998
|
-
toolUseTurns,
|
|
999
|
-
emptyResponseRetries,
|
|
1000
|
-
maxEmptyResponseRetries: MAX_EMPTY_RESPONSE_RETRIES,
|
|
1001
|
-
priorAssistantHadVisibleText,
|
|
1002
|
-
};
|
|
1003
|
-
const emptyResponseCtx = resolveLoopTurnContext(
|
|
1004
|
-
turnContext,
|
|
1005
|
-
requestId,
|
|
1006
|
-
toolUseTurns,
|
|
1007
|
-
);
|
|
1008
|
-
const emptyResponseDecision: EmptyResponseDecision = await runPipeline(
|
|
1009
|
-
"emptyResponse",
|
|
1010
|
-
getMiddlewaresFor("emptyResponse"),
|
|
1011
|
-
async (args) => defaultEmptyResponseTerminal(args),
|
|
1012
|
-
emptyResponseArgs,
|
|
1013
|
-
emptyResponseCtx,
|
|
1014
|
-
DEFAULT_TIMEOUTS.emptyResponse,
|
|
1015
|
-
);
|
|
1016
|
-
|
|
1017
|
-
if (emptyResponseDecision.action === "nudge") {
|
|
1018
|
-
// Fall back to the canonical nudge text if the plugin returned
|
|
1019
|
-
// `action: "nudge"` but forgot `nudgeText`. Keeps a misbehaving
|
|
1020
|
-
// plugin from silently breaking the loop invariant that the
|
|
1021
|
-
// model sees a coherent prompt.
|
|
1022
|
-
const nudgeText =
|
|
1023
|
-
emptyResponseDecision.nudgeText ??
|
|
1024
|
-
"<system_notice>Your previous response was empty. You must respond to the user with a summary of what you found or did. Do not use any tools — just respond with text.</system_notice>";
|
|
1025
|
-
emptyResponseRetries++;
|
|
1026
|
-
rlog.warn(
|
|
1027
|
-
{ turn: toolUseTurns, retry: emptyResponseRetries },
|
|
1028
|
-
"Model returned empty response after tool results — retrying",
|
|
1029
|
-
);
|
|
1030
|
-
history.push({
|
|
1031
|
-
role: "user",
|
|
1032
|
-
content: [{ type: "text", text: nudgeText }],
|
|
1033
|
-
});
|
|
1034
|
-
continue;
|
|
1206
|
+
const priorAssistantHadVisibleText = producedVisibleTextThisRun;
|
|
1207
|
+
if (hasVisibleText) {
|
|
1208
|
+
producedVisibleTextThisRun = true;
|
|
1035
1209
|
}
|
|
1036
1210
|
|
|
1037
|
-
if (
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
);
|
|
1051
|
-
|
|
1211
|
+
if (toolUseBlocks.length === 0) {
|
|
1212
|
+
// The model stopped requesting tools — the run's stop boundary. The
|
|
1213
|
+
// `stop` hook decides whether to let the turn end or re-query with a
|
|
1214
|
+
// follow-up turn. It receives the full history and, when it asks to
|
|
1215
|
+
// continue, appends the follow-up turn itself.
|
|
1216
|
+
const stopCtx: StopContext = {
|
|
1217
|
+
conversationId: turnCtx.conversationId,
|
|
1218
|
+
messages: [...history],
|
|
1219
|
+
responseContent: response.content,
|
|
1220
|
+
stopReason: response.stopReason,
|
|
1221
|
+
decision: "stop",
|
|
1222
|
+
logger: rlog,
|
|
1223
|
+
};
|
|
1224
|
+
const finalStopCtx = await runHook(HOOKS.STOP, stopCtx);
|
|
1225
|
+
|
|
1226
|
+
if (finalStopCtx.decision === "continue") {
|
|
1227
|
+
// The loop owns the retry budget: a hook always asks to continue
|
|
1228
|
+
// when a nudge is warranted, and the loop stops anyway once the
|
|
1229
|
+
// budget is spent. This bounds the hook-driven re-query loop.
|
|
1230
|
+
if (stopContinueRetries < MAX_STOP_CONTINUE_RETRIES) {
|
|
1231
|
+
stopContinueRetries++;
|
|
1232
|
+
rlog.warn(
|
|
1233
|
+
{ turn: toolUseTurns, retry: stopContinueRetries },
|
|
1234
|
+
"Model returned empty response after tool results — retrying",
|
|
1235
|
+
);
|
|
1236
|
+
history = finalStopCtx.messages;
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1052
1239
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1240
|
+
// Budget spent — accept the empty turn. Emit a dedicated log line
|
|
1241
|
+
// for the post-tool empty case so ops dashboards that grep on it
|
|
1242
|
+
// keep working.
|
|
1243
|
+
if (
|
|
1244
|
+
!hasVisibleText &&
|
|
1245
|
+
toolUseTurns > 0 &&
|
|
1246
|
+
!priorAssistantHadVisibleText
|
|
1247
|
+
) {
|
|
1248
|
+
rlog.error(
|
|
1249
|
+
{ turn: toolUseTurns, retries: stopContinueRetries },
|
|
1250
|
+
"Model returned empty response after tool results — retries exhausted",
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1066
1254
|
}
|
|
1067
1255
|
|
|
1068
1256
|
history.push(assistantMessage);
|
|
1257
|
+
appendedNewMessages = true;
|
|
1069
1258
|
|
|
1070
1259
|
await onEvent({ type: "message_complete", message: assistantMessage });
|
|
1071
1260
|
|
|
@@ -1095,6 +1284,15 @@ export class AgentLoop {
|
|
|
1095
1284
|
}),
|
|
1096
1285
|
);
|
|
1097
1286
|
history.push({ role: "user", content: cancelledBlocks });
|
|
1287
|
+
for (const toolUse of toolUseBlocks) {
|
|
1288
|
+
await onEvent({
|
|
1289
|
+
type: "tool_result",
|
|
1290
|
+
toolUseId: toolUse.id,
|
|
1291
|
+
content: "Cancelled by user",
|
|
1292
|
+
isError: true,
|
|
1293
|
+
cancelled: true,
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1098
1296
|
await emitExit("aborted_post_response");
|
|
1099
1297
|
break;
|
|
1100
1298
|
}
|
|
@@ -1124,14 +1322,6 @@ export class AgentLoop {
|
|
|
1124
1322
|
});
|
|
1125
1323
|
},
|
|
1126
1324
|
toolUse.id,
|
|
1127
|
-
// Forward the loop's resolved `TurnContext` through the
|
|
1128
|
-
// executor callback so `ToolExecutor.execute` can thread the
|
|
1129
|
-
// real orchestrator context into the `toolExecute` pipeline.
|
|
1130
|
-
// Standalone tests that don't wire a `turnContext` into
|
|
1131
|
-
// `AgentLoop.run()` pass `undefined` here and the executor
|
|
1132
|
-
// falls back to the synthesized placeholder — preserving the
|
|
1133
|
-
// existing unit-test behavior.
|
|
1134
|
-
turnCtx,
|
|
1135
1325
|
);
|
|
1136
1326
|
|
|
1137
1327
|
return { toolUse, result };
|
|
@@ -1195,61 +1385,39 @@ export class AgentLoop {
|
|
|
1195
1385
|
}),
|
|
1196
1386
|
);
|
|
1197
1387
|
|
|
1198
|
-
//
|
|
1199
|
-
//
|
|
1200
|
-
//
|
|
1201
|
-
//
|
|
1202
|
-
//
|
|
1388
|
+
// Run the `post-tool-use` hook once per tool result, after the tool
|
|
1389
|
+
// returns and before the result joins the provider-bound history.
|
|
1390
|
+
// The default tool-result-truncate plugin tail-drops oversized output
|
|
1391
|
+
// to fit the context window; user hooks can swap in a smarter strategy
|
|
1392
|
+
// (e.g. a summariser) or observe results for side effects.
|
|
1203
1393
|
const contextWindowTokens =
|
|
1204
|
-
|
|
1205
|
-
effectiveMaxInputTokens ??
|
|
1394
|
+
resolveContextWindow?.().maxInputTokens ??
|
|
1206
1395
|
this.config.maxInputTokens ??
|
|
1207
1396
|
180_000;
|
|
1208
|
-
const maxChars = calculateMaxToolResultChars(contextWindowTokens);
|
|
1209
|
-
const truncateMiddlewares = getMiddlewaresFor("toolResultTruncate");
|
|
1210
1397
|
|
|
1211
|
-
|
|
1212
|
-
const
|
|
1398
|
+
const resultBlocks: ContentBlock[] = [];
|
|
1399
|
+
const additionalContextBlocks: ContentBlock[] = [];
|
|
1213
1400
|
for (const block of rawResultBlocks) {
|
|
1214
1401
|
if (block.type !== "tool_result") {
|
|
1215
|
-
|
|
1216
|
-
continue;
|
|
1217
|
-
}
|
|
1218
|
-
const toolBlock = block as ToolResultContent;
|
|
1219
|
-
if (
|
|
1220
|
-
typeof toolBlock.content !== "string" ||
|
|
1221
|
-
toolBlock.content.length <= maxChars
|
|
1222
|
-
) {
|
|
1223
|
-
truncatedBlocks.push(block);
|
|
1402
|
+
resultBlocks.push(block);
|
|
1224
1403
|
continue;
|
|
1225
1404
|
}
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
truncatedBlocks.push({
|
|
1240
|
-
...toolBlock,
|
|
1241
|
-
content: pipelineResult.content,
|
|
1405
|
+
const postToolUseCtx: PostToolUseContext = {
|
|
1406
|
+
conversationId: turnCtx.conversationId,
|
|
1407
|
+
toolResponse: block as ToolResultContent,
|
|
1408
|
+
messages: history,
|
|
1409
|
+
maxInputTokens: contextWindowTokens,
|
|
1410
|
+
logger: rlog,
|
|
1411
|
+
};
|
|
1412
|
+
const finalCtx = await runHook(HOOKS.POST_TOOL_USE, postToolUseCtx);
|
|
1413
|
+
resultBlocks.push(finalCtx.toolResponse);
|
|
1414
|
+
if (finalCtx.additionalContext !== undefined) {
|
|
1415
|
+
additionalContextBlocks.push({
|
|
1416
|
+
type: "text",
|
|
1417
|
+
text: finalCtx.additionalContext,
|
|
1242
1418
|
});
|
|
1243
|
-
} else {
|
|
1244
|
-
truncatedBlocks.push(block);
|
|
1245
1419
|
}
|
|
1246
1420
|
}
|
|
1247
|
-
const resultBlocks = truncatedBlocks;
|
|
1248
|
-
if (truncatedCount > 0) {
|
|
1249
|
-
log.warn(
|
|
1250
|
-
`Truncated ${truncatedCount} oversized tool result(s) to prevent context overflow`,
|
|
1251
|
-
);
|
|
1252
|
-
}
|
|
1253
1421
|
|
|
1254
1422
|
// Emit tool_result events AFTER truncation so downstream consumers
|
|
1255
1423
|
// (e.g. session persistence) receive the truncated content.
|
|
@@ -1301,59 +1469,20 @@ export class AgentLoop {
|
|
|
1301
1469
|
|
|
1302
1470
|
toolUseTurns++;
|
|
1303
1471
|
|
|
1304
|
-
//
|
|
1305
|
-
//
|
|
1306
|
-
//
|
|
1307
|
-
//
|
|
1308
|
-
//
|
|
1309
|
-
//
|
|
1310
|
-
|
|
1311
|
-
if (hasToolError) {
|
|
1312
|
-
consecutiveErrorTurns++;
|
|
1313
|
-
} else {
|
|
1314
|
-
consecutiveErrorTurns = 0;
|
|
1315
|
-
}
|
|
1316
|
-
const toolErrorArgs: ToolErrorArgs = {
|
|
1317
|
-
hasToolError,
|
|
1318
|
-
consecutiveErrorTurns,
|
|
1319
|
-
maxConsecutiveErrorNudges: MAX_CONSECUTIVE_ERROR_NUDGES,
|
|
1320
|
-
};
|
|
1321
|
-
const toolErrorCtx: TurnContext = resolveLoopTurnContext(
|
|
1322
|
-
turnContext,
|
|
1323
|
-
requestId,
|
|
1324
|
-
toolUseTurns - 1,
|
|
1325
|
-
);
|
|
1326
|
-
const toolErrorDecision = await runPipeline<
|
|
1327
|
-
ToolErrorArgs,
|
|
1328
|
-
ToolErrorDecision
|
|
1329
|
-
>(
|
|
1330
|
-
"toolError",
|
|
1331
|
-
getMiddlewaresFor("toolError"),
|
|
1332
|
-
// Terminal: the canonical nudge decision. The default plugin's
|
|
1333
|
-
// middleware is a passthrough (so later-registered user plugins
|
|
1334
|
-
// aren't shadowed), so this terminal is what actually produces
|
|
1335
|
-
// the decision when no user plugin overrides it. Wiring the
|
|
1336
|
-
// decision here also ensures the nudge fires for direct
|
|
1337
|
-
// AgentLoop callers (tests, benchmarks) that skip
|
|
1338
|
-
// `bootstrapPlugins()` and therefore never register the default.
|
|
1339
|
-
async (args) => defaultToolErrorTerminal(args),
|
|
1340
|
-
toolErrorArgs,
|
|
1341
|
-
toolErrorCtx,
|
|
1342
|
-
DEFAULT_TIMEOUTS.toolError,
|
|
1343
|
-
);
|
|
1344
|
-
if (toolErrorDecision.action === "nudge") {
|
|
1345
|
-
resultBlocks.push({
|
|
1346
|
-
type: "text",
|
|
1347
|
-
text: toolErrorDecision.nudgeText,
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1472
|
+
// Append any guidance a post-tool-use hook surfaced via
|
|
1473
|
+
// `additionalContext` (e.g. tool-error retry coaching) as separate
|
|
1474
|
+
// blocks. They join the provider-bound history below but were not part
|
|
1475
|
+
// of the tool_result events emitted above, so the model sees the
|
|
1476
|
+
// guidance while the client-facing and persisted tool output stay the
|
|
1477
|
+
// tool's actual result.
|
|
1478
|
+
resultBlocks.push(...additionalContextBlocks);
|
|
1350
1479
|
|
|
1351
|
-
// Add tool results as a user message and continue the loop
|
|
1480
|
+
// Add tool results as a user message and continue the loop.
|
|
1352
1481
|
history.push({ role: "user", content: resultBlocks });
|
|
1353
1482
|
|
|
1354
1483
|
// Invoke checkpoint callback after tool results are in history.
|
|
1355
|
-
//
|
|
1356
|
-
//
|
|
1484
|
+
// Handoff is offered first so a queued message takes precedence over
|
|
1485
|
+
// the mid-loop budget yield below.
|
|
1357
1486
|
if (onCheckpoint) {
|
|
1358
1487
|
const decision = await onCheckpoint({
|
|
1359
1488
|
turnIndex: toolUseTurns - 1, // 0-based (toolUseTurns was already incremented)
|
|
@@ -1361,7 +1490,64 @@ export class AgentLoop {
|
|
|
1361
1490
|
hasToolUse: true,
|
|
1362
1491
|
history,
|
|
1363
1492
|
});
|
|
1364
|
-
if (decision
|
|
1493
|
+
if (decision !== "continue") {
|
|
1494
|
+
exitReason = decision;
|
|
1495
|
+
break;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// Mid-loop budget gate: when overflow recovery is enabled, estimate
|
|
1500
|
+
// the running context size as it approaches the preflight budget.
|
|
1501
|
+
// With a `compaction` hook the loop compacts in place and continues;
|
|
1502
|
+
// without one it yields (`exitReason = "budget"`) so the orchestrator
|
|
1503
|
+
// can recover before the next provider call risks a hard
|
|
1504
|
+
// context-too-large rejection. Keyed off the loop's own
|
|
1505
|
+
// `history.length` (the messages actually in context this turn,
|
|
1506
|
+
// including tool iterations) rather than the durable conversation
|
|
1507
|
+
// count.
|
|
1508
|
+
const contextWindow = resolveContextWindow?.();
|
|
1509
|
+
if (contextWindow?.overflowRecovery.enabled) {
|
|
1510
|
+
const { maxInputTokens, overflowRecovery } = contextWindow;
|
|
1511
|
+
const safetyMargin =
|
|
1512
|
+
history.length > LONG_HISTORY_MESSAGE_THRESHOLD
|
|
1513
|
+
? Math.max(
|
|
1514
|
+
overflowRecovery.safetyMarginRatio,
|
|
1515
|
+
LONG_HISTORY_SAFETY_MARGIN_FLOOR,
|
|
1516
|
+
)
|
|
1517
|
+
: overflowRecovery.safetyMarginRatio;
|
|
1518
|
+
const preflightBudget = Math.floor(
|
|
1519
|
+
maxInputTokens * (1 - safetyMargin),
|
|
1520
|
+
);
|
|
1521
|
+
const midLoopThreshold =
|
|
1522
|
+
preflightBudget * MID_LOOP_YIELD_THRESHOLD_RATIO;
|
|
1523
|
+
const estimated = this.estimateTokens(history);
|
|
1524
|
+
if (estimated > midLoopThreshold) {
|
|
1525
|
+
if (compaction) {
|
|
1526
|
+
rlog.info(
|
|
1527
|
+
{ phase: "mid-loop", estimated, threshold: midLoopThreshold },
|
|
1528
|
+
"Token estimate approaching budget — compacting in place",
|
|
1529
|
+
);
|
|
1530
|
+
const compacted = await this.compact(
|
|
1531
|
+
history,
|
|
1532
|
+
turnCtx,
|
|
1533
|
+
compaction,
|
|
1534
|
+
signal,
|
|
1535
|
+
onEvent,
|
|
1536
|
+
resolveEffectiveOverrideProfile() ?? null,
|
|
1537
|
+
);
|
|
1538
|
+
if (compacted) {
|
|
1539
|
+
history = compacted;
|
|
1540
|
+
// The compacted, re-injected array is the new base; output
|
|
1541
|
+
// produced after this point is what the orchestrator persists.
|
|
1542
|
+
newMessagesStart = history.length;
|
|
1543
|
+
continue;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
rlog.warn(
|
|
1547
|
+
{ phase: "mid-loop", estimated, threshold: midLoopThreshold },
|
|
1548
|
+
"Token estimate approaching budget — yielding for compaction",
|
|
1549
|
+
);
|
|
1550
|
+
exitReason = "budget";
|
|
1365
1551
|
break;
|
|
1366
1552
|
}
|
|
1367
1553
|
}
|
|
@@ -1380,6 +1566,15 @@ export class AgentLoop {
|
|
|
1380
1566
|
}),
|
|
1381
1567
|
);
|
|
1382
1568
|
history.push({ role: "user", content: cancelledBlocks });
|
|
1569
|
+
for (const toolUse of toolUseBlocks) {
|
|
1570
|
+
await onEvent({
|
|
1571
|
+
type: "tool_result",
|
|
1572
|
+
toolUseId: toolUse.id,
|
|
1573
|
+
content: "Cancelled by user",
|
|
1574
|
+
isError: true,
|
|
1575
|
+
cancelled: true,
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1383
1578
|
}
|
|
1384
1579
|
await emitExit("aborted_via_error");
|
|
1385
1580
|
break;
|
|
@@ -1393,11 +1588,9 @@ export class AgentLoop {
|
|
|
1393
1588
|
Sentry.captureException(err);
|
|
1394
1589
|
}
|
|
1395
1590
|
onEvent({ type: "error", error: err });
|
|
1396
|
-
// Catch-block fallback.
|
|
1397
|
-
//
|
|
1398
|
-
//
|
|
1399
|
-
// before the throw. Otherwise, this is the genuine
|
|
1400
|
-
// unhandled-error exit.
|
|
1591
|
+
// Catch-block fallback. A break site that stamped a more specific
|
|
1592
|
+
// reason before unwinding here keeps it; the guard makes this a no-op.
|
|
1593
|
+
// Otherwise this is the genuine unhandled-error exit.
|
|
1401
1594
|
await emitExit("error");
|
|
1402
1595
|
break;
|
|
1403
1596
|
}
|
|
@@ -1412,7 +1605,12 @@ export class AgentLoop {
|
|
|
1412
1605
|
"Agent loop exited",
|
|
1413
1606
|
);
|
|
1414
1607
|
|
|
1415
|
-
return
|
|
1608
|
+
return {
|
|
1609
|
+
history,
|
|
1610
|
+
exitReason,
|
|
1611
|
+
appendedNewMessages,
|
|
1612
|
+
newMessages: history.slice(newMessagesStart),
|
|
1613
|
+
};
|
|
1416
1614
|
}
|
|
1417
1615
|
}
|
|
1418
1616
|
|
|
@@ -1513,7 +1711,7 @@ export function compactAxTreeHistory(messages: Message[]): Message[] {
|
|
|
1513
1711
|
* turn. Using the last user message unconditionally would leave the most
|
|
1514
1712
|
* recent tool screenshots unprotected from stripping.
|
|
1515
1713
|
*/
|
|
1516
|
-
function
|
|
1714
|
+
function stripOldMediaBlocks(history: Message[]): Message[] {
|
|
1517
1715
|
// Find the last user message that contains tool_result blocks.
|
|
1518
1716
|
let lastToolResultUserIdx = -1;
|
|
1519
1717
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
@@ -1530,29 +1728,32 @@ function stripOldImageBlocks(history: Message[]): Message[] {
|
|
|
1530
1728
|
// Keep the most recent tool-result user message intact (current turn)
|
|
1531
1729
|
if (idx === lastToolResultUserIdx || msg.role !== "user") return msg;
|
|
1532
1730
|
|
|
1533
|
-
// Check if any tool_result blocks
|
|
1534
|
-
const
|
|
1731
|
+
// Check if any tool_result blocks carry embedded media (image or audio).
|
|
1732
|
+
const isMedia = (cb: ContentBlock) =>
|
|
1733
|
+
cb.type === "image" || cb.type === "file";
|
|
1734
|
+
const hasMedia = msg.content.some(
|
|
1535
1735
|
(b) =>
|
|
1536
1736
|
b.type === "tool_result" &&
|
|
1537
|
-
(b as ToolResultContent).contentBlocks?.some(
|
|
1538
|
-
(cb) => cb.type === "image",
|
|
1539
|
-
),
|
|
1737
|
+
(b as ToolResultContent).contentBlocks?.some(isMedia),
|
|
1540
1738
|
);
|
|
1541
|
-
if (!
|
|
1739
|
+
if (!hasMedia) return msg;
|
|
1542
1740
|
|
|
1543
|
-
// Strip
|
|
1741
|
+
// Strip media from tool_result blocks, replacing with a text marker. The
|
|
1742
|
+
// model already saw/heard the media in the turn it was captured; resending
|
|
1743
|
+
// the bytes every turn (a 12 MB audio clip isn't optimized like images)
|
|
1744
|
+
// bloats the request until compaction.
|
|
1544
1745
|
return {
|
|
1545
1746
|
...msg,
|
|
1546
1747
|
content: msg.content.map((b) => {
|
|
1547
1748
|
if (b.type !== "tool_result") return b;
|
|
1548
1749
|
const tr = b as ToolResultContent;
|
|
1549
|
-
if (!tr.contentBlocks?.some(
|
|
1750
|
+
if (!tr.contentBlocks?.some(isMedia)) return b;
|
|
1550
1751
|
return {
|
|
1551
1752
|
...tr,
|
|
1552
1753
|
contentBlocks: undefined,
|
|
1553
1754
|
content:
|
|
1554
1755
|
(tr.content || "") +
|
|
1555
|
-
"\n[
|
|
1756
|
+
"\n[Media (image/audio) was captured and shown previously — binary data removed to save context.]",
|
|
1556
1757
|
};
|
|
1557
1758
|
}),
|
|
1558
1759
|
};
|