@vellumai/assistant 0.8.5 → 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 +33 -1
- package/ARCHITECTURE.md +1 -1
- package/Dockerfile +21 -4
- package/bun.lock +13 -4
- package/bunfig.toml +6 -1
- 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/credential-execution-service.md +6 -6
- package/docs/plugins.md +114 -86
- 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 +15 -17
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +15 -7
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +8748 -589
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +88 -54
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +284 -48
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +21 -33
- 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 +7 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +23 -40
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +3 -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 +5 -11
- package/src/__tests__/approval-routes-http.test.ts +17 -16
- package/src/__tests__/assert-not-live-db.ts +79 -0
- 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 +11 -27
- package/src/__tests__/assistant-stream-state.test.ts +645 -0
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- 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 +11 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +72 -17
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/call-controller.test.ts +3 -2
- 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 +52 -23
- package/src/__tests__/channel-approvals.test.ts +4 -2
- package/src/__tests__/channel-guardian.test.ts +3 -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 +170 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +388 -79
- package/src/__tests__/checker.test.ts +12 -12
- 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 +6 -17
- package/src/__tests__/compaction-trail-store.test.ts +186 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +14 -16
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +56 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +73 -23
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +23 -34
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +477 -930
- package/src/__tests__/conversation-agent-loop.test.ts +1056 -1068
- package/src/__tests__/conversation-analysis-routes.test.ts +8 -3
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -1
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- 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 +16 -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-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +132 -157
- package/src/__tests__/conversation-fork-route.test.ts +19 -16
- package/src/__tests__/conversation-history-web-search.test.ts +12 -1
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +4 -2
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -3
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +60 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +36 -7
- package/src/__tests__/conversation-process-callsite.test.ts +28 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +85 -51
- package/src/__tests__/conversation-queue.test.ts +605 -455
- package/src/__tests__/conversation-routes-disk-view.test.ts +9 -20
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +35 -10
- package/src/__tests__/conversation-routes-slash-commands.test.ts +40 -8
- package/src/__tests__/conversation-runtime-assembly.test.ts +395 -313
- package/src/__tests__/conversation-runtime-workspace.test.ts +114 -36
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +125 -62
- package/src/__tests__/conversation-slash-unknown.test.ts +18 -15
- package/src/__tests__/conversation-speed-override.test.ts +9 -22
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +136 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
- package/src/__tests__/conversation-surfaces-table-action.test.ts +13 -32
- package/src/__tests__/conversation-sync-tags.test.ts +140 -12
- package/src/__tests__/conversation-title-service.test.ts +136 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +53 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +1 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +21 -17
- package/src/__tests__/conversation-workspace-injection.test.ts +119 -23
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +39 -13
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -6
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +275 -4
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -0
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +119 -36
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +9 -35
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +38 -43
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-page-surface.test.ts +99 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/filing-service.test.ts +3 -2
- package/src/__tests__/first-greeting.test.ts +103 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- 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 +375 -26
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +11 -8
- package/src/__tests__/heartbeat-disk-pressure.test.ts +3 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -1
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- 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-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +4 -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-routes-targeted.test.ts +1 -0
- 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 +11 -5
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +31 -9
- package/src/__tests__/identity-intro-cache.test.ts +140 -96
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +56 -75
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- 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 +78 -58
- 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__/inline-skill-load-permissions.test.ts +4 -4
- 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 +131 -11
- 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-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +346 -39
- package/src/__tests__/llm-schema.test.ts +1 -1
- 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__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- 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 +9 -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-from-url.test.ts +3 -3
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +235 -22
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +38 -1
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +15 -2
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +105 -80
- package/src/__tests__/openai-responses-provider.test.ts +90 -86
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +48 -2
- 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 +86 -13
- 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 +72 -63
- 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 +51 -64
- 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 +38 -32
- package/src/__tests__/process-message-display-content.test.ts +55 -66
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- 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 +215 -8
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- 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__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +6 -1
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +30 -23
- 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-attachment-metadata.test.ts +3 -2
- 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 +124 -1
- package/src/__tests__/schedule-tools.test.ts +160 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +7 -3
- 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__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +87 -44
- package/src/__tests__/server-history-render.test.ts +318 -2
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +52 -21
- package/src/__tests__/skill-feature-flags.test.ts +20 -22
- package/src/__tests__/skill-load-feature-flag.test.ts +15 -15
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- 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 +14 -6
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +25 -12
- package/src/__tests__/subagent-fork-spawn.test.ts +14 -5
- package/src/__tests__/subagent-manager-notify.test.ts +21 -11
- package/src/__tests__/subagent-notify-parent.test.ts +6 -7
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +59 -1
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +4 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +112 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +9 -25
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- 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-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +481 -15
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -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__/twilio-routes.test.ts +3 -2
- 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__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +10 -7
- package/src/__tests__/voice-session-bridge.test.ts +56 -38
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/__tests__/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 +492 -77
- package/src/agent/compaction-circuit.ts +98 -0
- package/src/agent/loop.ts +730 -449
- package/src/api/README.md +126 -2
- package/src/api/constants/call-sites.ts +27 -0
- 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 +49 -0
- package/src/api/events/assistant-text-delta.ts +30 -0
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/assistant-turn-start.ts +31 -0
- 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 +44 -0
- package/src/api/events/document-comment-deleted.ts +22 -0
- package/src/api/events/document-comment-reopened.ts +23 -0
- package/src/api/events/document-comment-resolved.ts +25 -0
- 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 +22 -0
- package/src/api/events/generation-handoff.ts +39 -0
- 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 +40 -0
- 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 +28 -0
- package/src/api/events/question-request.ts +67 -0
- package/src/{events → api/events}/relationship-state-updated.ts +6 -8
- 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 +30 -0
- 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 +517 -3
- 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 +41 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/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/background-wake-routes.test.ts +687 -52
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +5 -1
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +17 -31
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/__tests__/notifications.test.ts +242 -54
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- 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 +168 -203
- package/src/cli/commands/notifications.ts +427 -65
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +6 -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 +47 -55
- 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 +33 -24
- 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 +2 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +10 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +3 -8
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +81 -25
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +2 -211
- 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 +10 -1
- package/src/config/schemas/llm.ts +93 -4
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +8 -227
- 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/services.ts +6 -2
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/config/seed-inference-profiles.ts +38 -18
- 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 +52 -5
- package/src/context/tool-result-truncation.ts +1 -66
- package/src/context/window-manager.ts +141 -26
- package/src/credential-execution/executable-discovery.ts +56 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +9 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +15 -17
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +11 -8
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +11 -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 +1146 -247
- package/src/daemon/conversation-agent-loop.ts +570 -1457
- package/src/daemon/conversation-error.ts +40 -12
- 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 +89 -49
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +183 -130
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +209 -362
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +15 -95
- package/src/daemon/conversation-surfaces.ts +346 -107
- package/src/daemon/conversation-tool-setup.ts +38 -77
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +143 -186
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +7 -4
- package/src/daemon/disk-pressure-guard.ts +54 -50
- package/src/daemon/external-plugins-bootstrap.ts +53 -32
- package/src/daemon/first-greeting.ts +53 -13
- 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 -77
- 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 +65 -67
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- 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 +25 -125
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +5 -18
- package/src/daemon/message-types/integrations.ts +4 -13
- package/src/daemon/message-types/messages.ts +50 -428
- 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/subagents.ts +6 -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 +59 -58
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +30 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- 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 +24 -2
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +34 -0
- package/src/heartbeat/heartbeat-run-store.ts +54 -1
- package/src/heartbeat/heartbeat-service.ts +105 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-types.ts +36 -221
- package/src/home/feed-writer.ts +146 -7
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- 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 +6 -3
- 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 +34 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +59 -20
- package/src/ipc/skill-routes/memory.ts +29 -13
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +39 -88
- 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-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +82 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +11 -6
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/__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-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +386 -115
- 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-connection.ts +29 -19
- package/src/memory/db-init.ts +18 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- 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.test.ts +3 -3
- 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/embedding.test.ts +3 -2
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-store.ts +36 -1
- package/src/memory/jobs-worker.ts +93 -43
- package/src/memory/llm-request-log-source-clickhouse.ts +55 -1
- package/src/memory/llm-request-log-source-local.ts +13 -0
- package/src/memory/llm-request-log-source.ts +21 -6
- package/src/memory/llm-request-log-store.ts +147 -3
- 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 +13 -1
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/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 +9 -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/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/schema/infrastructure.ts +27 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/consolidation-job.test.ts +125 -1
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- 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 +9 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +99 -10
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- 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/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -8
- package/src/notifications/decision-engine.ts +16 -16
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/notifications/preference-extractor.ts +11 -14
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/permissions/prompter.ts +46 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugin-api/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} +297 -120
- 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 -677
- 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 +29 -21
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/__tests__/system-prompt.test.ts +46 -4
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- 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 +12 -12
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/prompts/templates/system-sections.ts +21 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -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 +34 -18
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +176 -37
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +269 -7
- package/src/providers/openai/chat-completions-provider.ts +155 -16
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +54 -57
- package/src/providers/openrouter/client.ts +15 -14
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/providers/provider-send-message.ts +23 -14
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -9
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +20 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +142 -32
- package/src/runtime/__tests__/background-job-runner.test.ts +2 -3
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/access-request-helper.ts +1 -0
- 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 -1069
- 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 +70 -5
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +59 -30
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- 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/migrations/vbundle-builder.ts +3 -2
- 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__/bookmark-routes.test.ts +1 -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 +436 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +129 -1
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +26 -72
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +58 -5
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- 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 +297 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/__tests__/tts-routes.test.ts +9 -5
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- 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 +175 -106
- 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 +201 -23
- 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 +37 -27
- 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 +11 -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 +292 -0
- package/src/runtime/routes/conversation-list-routes.ts +225 -9
- package/src/runtime/routes/conversation-management-routes.ts +96 -28
- package/src/runtime/routes/conversation-query-routes.ts +150 -51
- package/src/runtime/routes/conversation-routes.ts +615 -327
- package/src/runtime/routes/conversation-starter-routes.ts +35 -20
- package/src/runtime/routes/conversations-import-routes.ts +48 -13
- 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 +35 -11
- 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 +32 -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 +149 -16
- 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 +72 -39
- package/src/runtime/routes/identity-routes.ts +257 -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 +524 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +72 -27
- package/src/runtime/routes/index.ts +4 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +26 -31
- 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 +18 -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/llm-context-normalization.ts +7 -2
- 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 +313 -257
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +49 -13
- package/src/runtime/routes/notification-routes.ts +80 -2
- package/src/runtime/routes/oauth-apps.ts +99 -23
- package/src/runtime/routes/oauth-commands-routes.ts +43 -15
- 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 +10 -38
- package/src/runtime/routes/surface-content-routes.ts +21 -6
- package/src/runtime/routes/surface-conversation-resolver.ts +66 -0
- 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 +8 -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 +10 -4
- package/src/runtime/services/analyze-conversation.ts +5 -8
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +17 -3
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +36 -2
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +126 -21
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/cancel.ts +2 -4
- package/src/signals/user-message.ts +10 -16
- 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/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +33 -24
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- 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 +42 -24
- 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/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +295 -289
- 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/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +3 -6
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/executor.ts +18 -107
- 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.test.ts +1 -0
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +222 -6
- package/src/tools/network/managed-search-proxy.ts +183 -0
- 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 +414 -97
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +184 -118
- package/src/tools/schedule/create.ts +14 -1
- 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/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/subagent/spawn.ts +2 -1
- 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-approval-handler.ts +10 -4
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +86 -33
- package/src/tools/ui-surface/definitions.ts +196 -95
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/usage/types.ts +10 -0
- package/src/util/browser.ts +7 -2
- 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/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/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 +16 -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 -376
- 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 -344
- 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 -468
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -563
- package/src/memory/v3/__tests__/filter.test.ts +0 -512
- package/src/memory/v3/__tests__/gate.test.ts +0 -574
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -233
- package/src/memory/v3/__tests__/loop.test.ts +0 -530
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -440
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -312
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -469
- 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 -707
- package/src/memory/v3/__tests__/validate.test.ts +0 -245
- 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/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -249
- package/src/memory/v3/filter.ts +0 -281
- package/src/memory/v3/gate.ts +0 -334
- package/src/memory/v3/index-composition.ts +0 -113
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -382
- 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 -420
- package/src/memory/v3/shadow-middleware.ts +0 -305
- package/src/memory/v3/traversal.ts +0 -206
- 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 -351
- package/src/memory/v3/types.ts +0 -65
- package/src/memory/v3/validate.ts +0 -300
- 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 -127
- 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/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. */
|
|
@@ -111,6 +147,8 @@ export type AgentLoopExitReason =
|
|
|
111
147
|
* leaving `agent_loop_exit_reason` NULL.
|
|
112
148
|
*/
|
|
113
149
|
| "budget_yield_unrecovered"
|
|
150
|
+
/** Provider stopped because the configured output-token limit was reached. */
|
|
151
|
+
| "max_tokens_reached"
|
|
114
152
|
/** User cancellation landed after a non-terminal checkpoint yield. */
|
|
115
153
|
| "aborted_after_checkpoint"
|
|
116
154
|
/** Signal aborted while the catch handler was synthesizing an error turn. */
|
|
@@ -119,9 +157,19 @@ export type AgentLoopExitReason =
|
|
|
119
157
|
| "error";
|
|
120
158
|
|
|
121
159
|
export type AgentEvent =
|
|
160
|
+
/**
|
|
161
|
+
* Emitted once per LLM call inside the loop, immediately before the
|
|
162
|
+
* `provider.sendMessage` invocation. Carries the optional `callSite` tag so
|
|
163
|
+
* downstream handlers (the daemon's persistence pipeline) can decide
|
|
164
|
+
* whether to reserve a row for this call. One `llm_call_started` precedes
|
|
165
|
+
* every `message_complete` for the same call; multi-call agent turns emit
|
|
166
|
+
* one pair per call.
|
|
167
|
+
*/
|
|
168
|
+
| { type: "llm_call_started"; callSite?: LLMCallSite }
|
|
122
169
|
| { type: "text_delta"; text: string }
|
|
123
170
|
| { type: "thinking_delta"; thinking: string }
|
|
124
171
|
| { type: "message_complete"; message: Message }
|
|
172
|
+
| { type: "max_tokens_reached"; stopReason: string }
|
|
125
173
|
| {
|
|
126
174
|
type: "tool_use";
|
|
127
175
|
id: string;
|
|
@@ -157,6 +205,14 @@ export type AgentEvent =
|
|
|
157
205
|
approvalReason?: string;
|
|
158
206
|
riskThreshold?: string;
|
|
159
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;
|
|
160
216
|
}
|
|
161
217
|
| { type: "tool_use_preview_start"; toolUseId: string; toolName: string }
|
|
162
218
|
| {
|
|
@@ -191,7 +247,7 @@ export type AgentEvent =
|
|
|
191
247
|
| { type: "error"; error: Error }
|
|
192
248
|
| {
|
|
193
249
|
/**
|
|
194
|
-
* Emitted when the
|
|
250
|
+
* Emitted when the provider call throws — i.e. the provider
|
|
195
251
|
* rejected the request before returning a usable response. Carries
|
|
196
252
|
* the loop-level raw request we attempted to send (messages, tools,
|
|
197
253
|
* system prompt, provider-agnostic config) plus the thrown error.
|
|
@@ -234,6 +290,60 @@ export type AgentEvent =
|
|
|
234
290
|
*/
|
|
235
291
|
estimatedInputTokens?: number;
|
|
236
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
|
|
237
347
|
| {
|
|
238
348
|
/**
|
|
239
349
|
* Emitted when an agent turn reaches a terminal state. Checkpoint
|
|
@@ -254,8 +364,19 @@ const DEFAULT_CONFIG: AgentLoopConfig = {
|
|
|
254
364
|
minTurnIntervalMs: 150,
|
|
255
365
|
};
|
|
256
366
|
|
|
257
|
-
const
|
|
258
|
-
const
|
|
367
|
+
const MAX_STOP_CONTINUE_RETRIES = 1;
|
|
368
|
+
const MAX_TOKENS_STOP_REASONS = new Set([
|
|
369
|
+
"length",
|
|
370
|
+
"max_output_tokens",
|
|
371
|
+
"max_tokens",
|
|
372
|
+
]);
|
|
373
|
+
|
|
374
|
+
export function isMaxTokensStopReason(
|
|
375
|
+
stopReason: string | null | undefined,
|
|
376
|
+
): boolean {
|
|
377
|
+
if (!stopReason) return false;
|
|
378
|
+
return MAX_TOKENS_STOP_REASONS.has(stopReason.trim().toLowerCase());
|
|
379
|
+
}
|
|
259
380
|
|
|
260
381
|
/**
|
|
261
382
|
* Build a minimal {@link TurnContext} for pipeline invocations inside the
|
|
@@ -264,12 +385,11 @@ const MAX_EMPTY_RESPONSE_RETRIES = 1;
|
|
|
264
385
|
* {@link AgentLoop.run}); this helper is the fallback used only by unit
|
|
265
386
|
* tests that construct `AgentLoop` directly without an orchestrator.
|
|
266
387
|
*
|
|
267
|
-
* When the orchestrator-supplied context is present
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* 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.
|
|
273
393
|
*/
|
|
274
394
|
function buildLoopTurnContext(
|
|
275
395
|
requestId: string | undefined,
|
|
@@ -289,29 +409,6 @@ function buildLoopTurnContext(
|
|
|
289
409
|
};
|
|
290
410
|
}
|
|
291
411
|
|
|
292
|
-
/**
|
|
293
|
-
* Produce a `TurnContext` for a pipeline call inside {@link AgentLoop.run}.
|
|
294
|
-
*
|
|
295
|
-
* When the orchestrator supplied a `turnContext`, clone it and overwrite
|
|
296
|
-
* `requestId` + `turnIndex` with the loop-scoped values so plugin log
|
|
297
|
-
* records correctly attribute the call to the current tool-use iteration
|
|
298
|
-
* while preserving the real `conversationId`, trust context, and
|
|
299
|
-
* `contextWindowManager` the orchestrator assembled for the turn. Without
|
|
300
|
-
* an orchestrator context (unit tests that instantiate `AgentLoop` with no
|
|
301
|
-
* `turnContext`), fall back to {@link buildLoopTurnContext}'s synthesized
|
|
302
|
-
* placeholder.
|
|
303
|
-
*/
|
|
304
|
-
function resolveLoopTurnContext(
|
|
305
|
-
base: TurnContext | undefined,
|
|
306
|
-
requestId: string | undefined,
|
|
307
|
-
turnIndex: number,
|
|
308
|
-
): TurnContext {
|
|
309
|
-
if (base) {
|
|
310
|
-
return { ...base, requestId: requestId ?? base.requestId, turnIndex };
|
|
311
|
-
}
|
|
312
|
-
return buildLoopTurnContext(requestId, turnIndex);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
412
|
/**
|
|
316
413
|
* User-config HTTP status codes that should never page the on-call: billing
|
|
317
414
|
* exhaustion (402), invalid credentials (401), and forbidden/plan-gated (403).
|
|
@@ -354,23 +451,91 @@ export interface ResolvedSystemPrompt {
|
|
|
354
451
|
model?: string;
|
|
355
452
|
}
|
|
356
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
|
+
|
|
357
531
|
/**
|
|
358
532
|
* Callback shape the loop uses to execute a tool invocation.
|
|
359
|
-
*
|
|
360
|
-
* The trailing `turnContext` is optional so in-process tests that wire the
|
|
361
|
-
* callback without an orchestrator keep working. Production sites (the
|
|
362
|
-
* `Conversation`'s `createToolExecutor`) forward the supplied context into
|
|
363
|
-
* `ToolExecutor.execute` so the `toolExecute` pipeline sees the orchestrator's
|
|
364
|
-
* real conversation identity/trust/contextWindowManager instead of the
|
|
365
|
-
* synthesized placeholder `ToolExecutor` would otherwise build from the
|
|
366
|
-
* `ToolContext` alone.
|
|
367
533
|
*/
|
|
368
534
|
export type LoopToolExecutor = (
|
|
369
535
|
name: string,
|
|
370
536
|
input: Record<string, unknown>,
|
|
371
537
|
onOutput?: (chunk: string) => void,
|
|
372
538
|
toolUseId?: string,
|
|
373
|
-
turnContext?: TurnContext,
|
|
374
539
|
) => Promise<{
|
|
375
540
|
content: string;
|
|
376
541
|
isError: boolean;
|
|
@@ -401,6 +566,20 @@ export type LoopToolExecutor = (
|
|
|
401
566
|
activityMetadata?: ToolActivityMetadata;
|
|
402
567
|
}>;
|
|
403
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
|
+
|
|
404
583
|
export class AgentLoop {
|
|
405
584
|
private provider: Provider;
|
|
406
585
|
private systemPrompt: string;
|
|
@@ -412,15 +591,28 @@ export class AgentLoop {
|
|
|
412
591
|
| null;
|
|
413
592
|
private toolExecutor: LoopToolExecutor | null;
|
|
414
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
|
+
|
|
415
603
|
constructor(
|
|
416
604
|
provider: Provider,
|
|
417
605
|
systemPrompt: string,
|
|
418
|
-
|
|
419
|
-
tools?: ToolDefinition[],
|
|
420
|
-
toolExecutor?: LoopToolExecutor,
|
|
421
|
-
resolveTools?: (history: Message[]) => ToolDefinition[],
|
|
422
|
-
resolveSystemPrompt?: (history: Message[]) => ResolvedSystemPrompt,
|
|
606
|
+
options?: AgentLoopConstructorOptions,
|
|
423
607
|
) {
|
|
608
|
+
const {
|
|
609
|
+
config,
|
|
610
|
+
tools,
|
|
611
|
+
toolExecutor,
|
|
612
|
+
resolveTools,
|
|
613
|
+
resolveSystemPrompt,
|
|
614
|
+
conversationId,
|
|
615
|
+
} = options ?? {};
|
|
424
616
|
this.provider = provider;
|
|
425
617
|
this.systemPrompt = systemPrompt;
|
|
426
618
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
@@ -428,16 +620,16 @@ export class AgentLoop {
|
|
|
428
620
|
this.resolveTools = resolveTools ?? null;
|
|
429
621
|
this.resolveSystemPrompt = resolveSystemPrompt ?? null;
|
|
430
622
|
this.toolExecutor = toolExecutor ?? null;
|
|
623
|
+
this.compactionCircuit = new CompactionCircuit(conversationId ?? "");
|
|
431
624
|
}
|
|
432
625
|
|
|
433
626
|
/**
|
|
434
627
|
* Resolve the tool definitions sent to the provider for the given turn.
|
|
435
628
|
*
|
|
436
629
|
* Mirrors the logic of {@link getToolTokenBudget} but returns the tool
|
|
437
|
-
* array itself — callers that need to thread the tool set into
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* 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.
|
|
441
633
|
*/
|
|
442
634
|
getResolvedTools(history?: Message[]): ToolDefinition[] {
|
|
443
635
|
return history && this.resolveTools
|
|
@@ -457,58 +649,167 @@ export class AgentLoop {
|
|
|
457
649
|
return estimateToolsTokens(this.getResolvedTools(history));
|
|
458
650
|
}
|
|
459
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
|
+
|
|
460
765
|
async run(
|
|
461
766
|
messages: Message[],
|
|
462
767
|
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
* Used by per-conversation pinned profiles to override the workspace
|
|
485
|
-
* default for the lifetime of an agent loop run.
|
|
486
|
-
*/
|
|
487
|
-
overrideProfile?: string,
|
|
488
|
-
effectiveMaxInputTokens?: number,
|
|
489
|
-
resolveOverrideProfile?: () => string | undefined,
|
|
490
|
-
resolveEffectiveMaxInputTokens?: () => number | undefined,
|
|
491
|
-
): Promise<Message[]> {
|
|
492
|
-
const history = [...messages];
|
|
493
|
-
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;
|
|
494
789
|
let toolUseTurns = 0;
|
|
495
|
-
let
|
|
496
|
-
let emptyResponseRetries = 0;
|
|
790
|
+
let stopContinueRetries = 0;
|
|
497
791
|
let lastLlmCallTime = 0;
|
|
792
|
+
let exitReason: ExitReason | null = null;
|
|
793
|
+
let appendedNewMessages = false;
|
|
498
794
|
const rlog = requestId ? log.child({ requestId }) : log;
|
|
499
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
|
+
|
|
500
802
|
// Per-run substitution map for sensitive output placeholders.
|
|
501
803
|
// Bindings are accumulated from tool results; placeholders are
|
|
502
804
|
// resolved in streamed deltas and final assistant message text.
|
|
503
805
|
const substitutionMap = new Map<string, string>();
|
|
504
806
|
let streamingPending = "";
|
|
505
807
|
|
|
506
|
-
// Idempotency guard for `emitExit
|
|
507
|
-
//
|
|
508
|
-
//
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
// 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.
|
|
512
813
|
let exitReasonEmitted = false;
|
|
513
814
|
const emitExit = async (reason: AgentLoopExitReason): Promise<void> => {
|
|
514
815
|
if (exitReasonEmitted) return;
|
|
@@ -593,6 +894,15 @@ export class AgentLoop {
|
|
|
593
894
|
providerConfig.cacheTtl = this.config.cacheTtl;
|
|
594
895
|
}
|
|
595
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
|
+
|
|
596
906
|
// Per-call LLM call-site identifier. Surfaces on the per-call
|
|
597
907
|
// `config.callSite` so `RetryProvider.normalizeSendMessageOptions`
|
|
598
908
|
// can route through `resolveCallSiteConfig` against
|
|
@@ -603,6 +913,16 @@ export class AgentLoop {
|
|
|
603
913
|
if (callSite) {
|
|
604
914
|
providerConfig.callSite = callSite;
|
|
605
915
|
providerConfig.usageTracking = "manual";
|
|
916
|
+
// Per-conversation seed for deterministic `mix`-profile expansion.
|
|
917
|
+
// Sourced from the orchestrator-supplied turn context's
|
|
918
|
+
// conversationId so every LLM call in a conversation resolves the
|
|
919
|
+
// same mix arm (stable across turns and retries, and across daemon
|
|
920
|
+
// restarts since the seed is the durable conversation id). Absent
|
|
921
|
+
// for standalone `AgentLoop` instances (unit tests / no turnContext)
|
|
922
|
+
// — those fall back to per-call random mix selection.
|
|
923
|
+
if (turnContext?.conversationId) {
|
|
924
|
+
providerConfig.selectionSeed = turnContext.conversationId;
|
|
925
|
+
}
|
|
606
926
|
}
|
|
607
927
|
|
|
608
928
|
// Per-call inference-profile override. The resolver layers
|
|
@@ -610,12 +930,8 @@ export class AgentLoop {
|
|
|
610
930
|
// `activeProfile` and any call-site named profile. Threading it on
|
|
611
931
|
// every send (rather than once at construction) keeps subagents that
|
|
612
932
|
// share an `AgentLoop` instance but ought to inherit a different
|
|
613
|
-
// profile correct — and matches how `callSite` is plumbed.
|
|
614
|
-
|
|
615
|
-
// profile-session switch before the next model call.
|
|
616
|
-
const effectiveOverrideProfile = resolveOverrideProfile
|
|
617
|
-
? resolveOverrideProfile()
|
|
618
|
-
: overrideProfile;
|
|
933
|
+
// profile correct — and matches how `callSite` is plumbed.
|
|
934
|
+
const effectiveOverrideProfile = resolveEffectiveOverrideProfile();
|
|
619
935
|
if (effectiveOverrideProfile) {
|
|
620
936
|
providerConfig.overrideProfile = effectiveOverrideProfile;
|
|
621
937
|
}
|
|
@@ -658,98 +974,91 @@ export class AgentLoop {
|
|
|
658
974
|
// Also strip old AX tree snapshots to keep TTFT from growing
|
|
659
975
|
// linearly with step count in computer-use sessions.
|
|
660
976
|
const providerHistory = compactAxTreeHistory(
|
|
661
|
-
|
|
977
|
+
stripOldMediaBlocks(history),
|
|
662
978
|
);
|
|
663
979
|
|
|
664
|
-
//
|
|
665
|
-
//
|
|
666
|
-
//
|
|
667
|
-
|
|
668
|
-
// `next(args)`. The default `defaultLlmCallPlugin` contributes a
|
|
669
|
-
// passthrough middleware that forwards to `next(args)` — it
|
|
670
|
-
// registers at module load and sits at the outermost onion layer,
|
|
671
|
-
// so it must yield to keep user-registered `llmCall` middleware
|
|
672
|
-
// reachable. Timeout is `null` (`DEFAULT_TIMEOUTS.llmCall`) — the
|
|
673
|
-
// provider layer already enforces its own HTTP-level budgets.
|
|
674
|
-
//
|
|
675
|
-
// The `onEvent` wrapping is kept inside `args.options` so substitution
|
|
676
|
-
// and streaming behavior exactly match the pre-pipeline call site.
|
|
677
|
-
const llmCallArgs: LLMCallArgs = {
|
|
678
|
-
provider: this.provider,
|
|
679
|
-
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 = {
|
|
680
984
|
tools: currentTools.length > 0 ? currentTools : undefined,
|
|
681
985
|
systemPrompt: turnSystemPrompt,
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
onEvent({ type: "text_delta", text: emit });
|
|
696
|
-
}
|
|
697
|
-
} else {
|
|
698
|
-
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 });
|
|
699
999
|
}
|
|
700
|
-
} else
|
|
701
|
-
onEvent({ type: "
|
|
702
|
-
} else if (event.type === "tool_use_preview_start") {
|
|
703
|
-
onEvent({
|
|
704
|
-
type: "tool_use_preview_start",
|
|
705
|
-
toolUseId: event.toolUseId,
|
|
706
|
-
toolName: event.toolName,
|
|
707
|
-
});
|
|
708
|
-
} else if (event.type === "input_json_delta") {
|
|
709
|
-
onEvent({
|
|
710
|
-
type: "input_json_delta",
|
|
711
|
-
toolName: event.toolName,
|
|
712
|
-
toolUseId: event.toolUseId,
|
|
713
|
-
accumulatedJson: event.accumulatedJson,
|
|
714
|
-
});
|
|
715
|
-
} else if (event.type === "server_tool_start") {
|
|
716
|
-
onEvent({
|
|
717
|
-
type: "server_tool_start",
|
|
718
|
-
name: event.name,
|
|
719
|
-
toolUseId: event.toolUseId,
|
|
720
|
-
input: event.input,
|
|
721
|
-
});
|
|
722
|
-
} else if (event.type === "server_tool_complete") {
|
|
723
|
-
onEvent({
|
|
724
|
-
type: "server_tool_complete",
|
|
725
|
-
toolUseId: event.toolUseId,
|
|
726
|
-
isError: event.isError,
|
|
727
|
-
...(event.content ? { content: event.content } : {}),
|
|
728
|
-
...(event.resolvedInput
|
|
729
|
-
? { resolvedInput: event.resolvedInput }
|
|
730
|
-
: {}),
|
|
731
|
-
...(event.errorCode ? { errorCode: event.errorCode } : {}),
|
|
732
|
-
...(event.errorMessage
|
|
733
|
-
? { errorMessage: event.errorMessage }
|
|
734
|
-
: {}),
|
|
735
|
-
});
|
|
1000
|
+
} else {
|
|
1001
|
+
onEvent({ type: "text_delta", text: event.text });
|
|
736
1002
|
}
|
|
737
|
-
}
|
|
738
|
-
|
|
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
|
+
}
|
|
739
1040
|
},
|
|
1041
|
+
signal,
|
|
740
1042
|
};
|
|
741
1043
|
|
|
742
|
-
// Per-turn pipeline context.
|
|
743
|
-
// `turnContext` into `run()
|
|
744
|
-
//
|
|
745
|
-
//
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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);
|
|
1050
|
+
|
|
1051
|
+
// Announce the LLM-call boundary so downstream handlers (the
|
|
1052
|
+
// daemon's persistence pipeline) can reserve an empty assistant row
|
|
1053
|
+
// and stamp the resulting `messageId` onto every streaming event the
|
|
1054
|
+
// call emits. Emit as late as possible — after history stripping,
|
|
1055
|
+
// arg construction, and turn-context resolution — so the gap
|
|
1056
|
+
// between "we said the call started" and the actual provider HTTP
|
|
1057
|
+
// call is minimized. Awaited so the row is created and the
|
|
1058
|
+
// `assistant_turn_start` wire event reaches the client BEFORE the
|
|
1059
|
+
// provider starts streaming deltas — the deltas downstream will
|
|
1060
|
+
// carry the freshly-reserved id.
|
|
1061
|
+
await onEvent({ type: "llm_call_started", callSite });
|
|
753
1062
|
|
|
754
1063
|
// Inner try/catch narrows error-recording scope to the provider
|
|
755
1064
|
// call itself. The outer agent-loop catch (below) wraps the entire
|
|
@@ -760,21 +1069,11 @@ export class AgentLoop {
|
|
|
760
1069
|
// `llm_request_logs` row, then re-throw so the existing outer catch
|
|
761
1070
|
// continues to handle abort sync, Sentry capture, the `error` event,
|
|
762
1071
|
// and the loop break unchanged.
|
|
763
|
-
let response:
|
|
1072
|
+
let response: ProviderResponse;
|
|
764
1073
|
try {
|
|
765
|
-
response = await
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
(args) =>
|
|
769
|
-
args.provider.sendMessage(
|
|
770
|
-
args.messages,
|
|
771
|
-
args.tools,
|
|
772
|
-
args.systemPrompt,
|
|
773
|
-
args.options,
|
|
774
|
-
),
|
|
775
|
-
llmCallArgs,
|
|
776
|
-
turnCtx,
|
|
777
|
-
DEFAULT_TIMEOUTS.llmCall,
|
|
1074
|
+
response = await this.provider.sendMessage(
|
|
1075
|
+
providerHistory,
|
|
1076
|
+
providerOptions,
|
|
778
1077
|
);
|
|
779
1078
|
} catch (llmCallError) {
|
|
780
1079
|
// Skip recording on abort — the user cancelled the request and
|
|
@@ -792,10 +1091,10 @@ export class AgentLoop {
|
|
|
792
1091
|
// misrepresent both.
|
|
793
1092
|
const rawRequest = {
|
|
794
1093
|
provider: this.provider.name,
|
|
795
|
-
messages:
|
|
796
|
-
tools:
|
|
797
|
-
systemPrompt:
|
|
798
|
-
config:
|
|
1094
|
+
messages: providerHistory,
|
|
1095
|
+
tools: providerOptions.tools,
|
|
1096
|
+
systemPrompt: providerOptions.systemPrompt,
|
|
1097
|
+
config: providerOptions.config,
|
|
799
1098
|
};
|
|
800
1099
|
onEvent({
|
|
801
1100
|
type: "provider_error",
|
|
@@ -862,130 +1161,100 @@ export class AgentLoop {
|
|
|
862
1161
|
"LLM call complete",
|
|
863
1162
|
);
|
|
864
1163
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
//
|
|
877
|
-
// Note: we check ANY prior assistant turn from this run()
|
|
878
|
-
// invocation, not just the most recent one. In multi-step tool-use
|
|
879
|
-
// chains (say-something → call-tool → call-another-tool → end),
|
|
880
|
-
// the "say-something" text lives on an earlier assistant turn while
|
|
881
|
-
// the most recent assistant turn is a pure tool_use with no text.
|
|
882
|
-
// Restricting the check to the most recent assistant turn would
|
|
883
|
-
// falsely nudge in that case and trigger a duplicate re-send of
|
|
884
|
-
// text the user already saw.
|
|
885
|
-
//
|
|
886
|
-
// Scope the scan to messages appended during this run() call only.
|
|
887
|
-
// Assistant text from prior conversation turns (earlier run()
|
|
888
|
-
// invocations passed in via `messages`) must NOT suppress the
|
|
889
|
-
// nudge — those turns completed long ago and have no bearing on
|
|
890
|
-
// whether the current tool-use chain has delivered text yet.
|
|
891
|
-
//
|
|
892
|
-
// The actual decision (nudge vs. accept vs. error) is delegated to
|
|
893
|
-
// the `emptyResponse` plugin pipeline. The pipeline returns a
|
|
894
|
-
// decision; the loop carries out the side-effect (pushing the nudge
|
|
895
|
-
// or surfacing the error). See `plugins/defaults/empty-response.ts`
|
|
896
|
-
// for the default decision logic.
|
|
897
|
-
const hasVisibleText = response.content.some(
|
|
898
|
-
(block) => block.type === "text" && block.text.trim().length > 0,
|
|
899
|
-
);
|
|
900
|
-
const priorAssistantHadVisibleText = (() => {
|
|
901
|
-
for (let i = history.length - 1; i >= initialHistoryLength; i--) {
|
|
902
|
-
const msg = history[i];
|
|
903
|
-
if (msg.role !== "assistant") continue;
|
|
904
|
-
const hasText = msg.content.some(
|
|
905
|
-
(block) =>
|
|
906
|
-
block.type === "text" &&
|
|
907
|
-
typeof (block as { text?: unknown }).text === "string" &&
|
|
908
|
-
(block as { text: string }).text.trim().length > 0,
|
|
909
|
-
);
|
|
910
|
-
if (hasText) return true;
|
|
911
|
-
}
|
|
912
|
-
return false;
|
|
913
|
-
})();
|
|
914
|
-
|
|
915
|
-
const emptyResponseArgs: EmptyResponseArgs = {
|
|
916
|
-
responseContent: response.content,
|
|
917
|
-
toolUseBlocksLength: toolUseBlocks.length,
|
|
918
|
-
toolUseTurns,
|
|
919
|
-
emptyResponseRetries,
|
|
920
|
-
maxEmptyResponseRetries: MAX_EMPTY_RESPONSE_RETRIES,
|
|
921
|
-
priorAssistantHadVisibleText,
|
|
922
|
-
};
|
|
923
|
-
const emptyResponseCtx = resolveLoopTurnContext(
|
|
924
|
-
turnContext,
|
|
925
|
-
requestId,
|
|
926
|
-
toolUseTurns,
|
|
927
|
-
);
|
|
928
|
-
const emptyResponseDecision: EmptyResponseDecision = await runPipeline(
|
|
929
|
-
"emptyResponse",
|
|
930
|
-
getMiddlewaresFor("emptyResponse"),
|
|
931
|
-
async (args) => defaultEmptyResponseTerminal(args),
|
|
932
|
-
emptyResponseArgs,
|
|
933
|
-
emptyResponseCtx,
|
|
934
|
-
DEFAULT_TIMEOUTS.emptyResponse,
|
|
935
|
-
);
|
|
936
|
-
|
|
937
|
-
if (emptyResponseDecision.action === "nudge") {
|
|
938
|
-
// Fall back to the canonical nudge text if the plugin returned
|
|
939
|
-
// `action: "nudge"` but forgot `nudgeText`. Keeps a misbehaving
|
|
940
|
-
// plugin from silently breaking the loop invariant that the
|
|
941
|
-
// model sees a coherent prompt.
|
|
942
|
-
const nudgeText =
|
|
943
|
-
emptyResponseDecision.nudgeText ??
|
|
944
|
-
"<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>";
|
|
945
|
-
emptyResponseRetries++;
|
|
1164
|
+
if (isMaxTokensStopReason(response.stopReason)) {
|
|
1165
|
+
const safeContent = response.content.filter(
|
|
1166
|
+
(block) =>
|
|
1167
|
+
block.type !== "tool_use" &&
|
|
1168
|
+
block.type !== "server_tool_use" &&
|
|
1169
|
+
block.type !== "web_search_tool_result",
|
|
1170
|
+
);
|
|
1171
|
+
const safeAssistantMessage: Message = {
|
|
1172
|
+
role: "assistant",
|
|
1173
|
+
content: safeContent,
|
|
1174
|
+
};
|
|
946
1175
|
rlog.warn(
|
|
947
|
-
{
|
|
948
|
-
|
|
1176
|
+
{
|
|
1177
|
+
turn: toolUseTurns,
|
|
1178
|
+
stopReason: response.stopReason,
|
|
1179
|
+
contentBlocks: response.content.length,
|
|
1180
|
+
safeContentBlocks: safeContent.length,
|
|
1181
|
+
toolUseCount: toolUseBlocks.length,
|
|
1182
|
+
},
|
|
1183
|
+
"LLM response reached output token limit",
|
|
949
1184
|
);
|
|
950
|
-
history.push(
|
|
951
|
-
|
|
952
|
-
|
|
1185
|
+
history.push(safeAssistantMessage);
|
|
1186
|
+
appendedNewMessages = true;
|
|
1187
|
+
await onEvent({
|
|
1188
|
+
type: "max_tokens_reached",
|
|
1189
|
+
stopReason: response.stopReason,
|
|
1190
|
+
});
|
|
1191
|
+
await onEvent({
|
|
1192
|
+
type: "message_complete",
|
|
1193
|
+
message: safeAssistantMessage,
|
|
953
1194
|
});
|
|
954
|
-
|
|
1195
|
+
await emitExit("max_tokens_reached");
|
|
1196
|
+
break;
|
|
955
1197
|
}
|
|
956
1198
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
throw new AssistantError(
|
|
968
|
-
"Model returned empty response after tool results",
|
|
969
|
-
ErrorCode.INTERNAL_ERROR,
|
|
970
|
-
);
|
|
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.
|
|
1203
|
+
const hasVisibleText = response.content.some(
|
|
1204
|
+
(block) => block.type === "text" && block.text.trim().length > 0,
|
|
1205
|
+
);
|
|
1206
|
+
const priorAssistantHadVisibleText = producedVisibleTextThisRun;
|
|
1207
|
+
if (hasVisibleText) {
|
|
1208
|
+
producedVisibleTextThisRun = true;
|
|
971
1209
|
}
|
|
972
1210
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
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
|
+
}
|
|
1239
|
+
|
|
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
|
+
}
|
|
986
1254
|
}
|
|
987
1255
|
|
|
988
1256
|
history.push(assistantMessage);
|
|
1257
|
+
appendedNewMessages = true;
|
|
989
1258
|
|
|
990
1259
|
await onEvent({ type: "message_complete", message: assistantMessage });
|
|
991
1260
|
|
|
@@ -1015,6 +1284,15 @@ export class AgentLoop {
|
|
|
1015
1284
|
}),
|
|
1016
1285
|
);
|
|
1017
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
|
+
}
|
|
1018
1296
|
await emitExit("aborted_post_response");
|
|
1019
1297
|
break;
|
|
1020
1298
|
}
|
|
@@ -1044,14 +1322,6 @@ export class AgentLoop {
|
|
|
1044
1322
|
});
|
|
1045
1323
|
},
|
|
1046
1324
|
toolUse.id,
|
|
1047
|
-
// Forward the loop's resolved `TurnContext` through the
|
|
1048
|
-
// executor callback so `ToolExecutor.execute` can thread the
|
|
1049
|
-
// real orchestrator context into the `toolExecute` pipeline.
|
|
1050
|
-
// Standalone tests that don't wire a `turnContext` into
|
|
1051
|
-
// `AgentLoop.run()` pass `undefined` here and the executor
|
|
1052
|
-
// falls back to the synthesized placeholder — preserving the
|
|
1053
|
-
// existing unit-test behavior.
|
|
1054
|
-
turnCtx,
|
|
1055
1325
|
);
|
|
1056
1326
|
|
|
1057
1327
|
return { toolUse, result };
|
|
@@ -1115,61 +1385,39 @@ export class AgentLoop {
|
|
|
1115
1385
|
}),
|
|
1116
1386
|
);
|
|
1117
1387
|
|
|
1118
|
-
//
|
|
1119
|
-
//
|
|
1120
|
-
//
|
|
1121
|
-
//
|
|
1122
|
-
//
|
|
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.
|
|
1123
1393
|
const contextWindowTokens =
|
|
1124
|
-
|
|
1125
|
-
effectiveMaxInputTokens ??
|
|
1394
|
+
resolveContextWindow?.().maxInputTokens ??
|
|
1126
1395
|
this.config.maxInputTokens ??
|
|
1127
1396
|
180_000;
|
|
1128
|
-
const maxChars = calculateMaxToolResultChars(contextWindowTokens);
|
|
1129
|
-
const truncateMiddlewares = getMiddlewaresFor("toolResultTruncate");
|
|
1130
1397
|
|
|
1131
|
-
|
|
1132
|
-
const
|
|
1398
|
+
const resultBlocks: ContentBlock[] = [];
|
|
1399
|
+
const additionalContextBlocks: ContentBlock[] = [];
|
|
1133
1400
|
for (const block of rawResultBlocks) {
|
|
1134
1401
|
if (block.type !== "tool_result") {
|
|
1135
|
-
|
|
1402
|
+
resultBlocks.push(block);
|
|
1136
1403
|
continue;
|
|
1137
1404
|
}
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
truncateMiddlewares,
|
|
1152
|
-
async (args) => defaultToolResultTruncateTerminal(args),
|
|
1153
|
-
{ content: toolBlock.content, maxChars },
|
|
1154
|
-
turnCtx,
|
|
1155
|
-
DEFAULT_TIMEOUTS.toolResultTruncate,
|
|
1156
|
-
);
|
|
1157
|
-
if (pipelineResult.truncated) {
|
|
1158
|
-
truncatedCount++;
|
|
1159
|
-
truncatedBlocks.push({
|
|
1160
|
-
...toolBlock,
|
|
1161
|
-
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,
|
|
1162
1418
|
});
|
|
1163
|
-
} else {
|
|
1164
|
-
truncatedBlocks.push(block);
|
|
1165
1419
|
}
|
|
1166
1420
|
}
|
|
1167
|
-
const resultBlocks = truncatedBlocks;
|
|
1168
|
-
if (truncatedCount > 0) {
|
|
1169
|
-
log.warn(
|
|
1170
|
-
`Truncated ${truncatedCount} oversized tool result(s) to prevent context overflow`,
|
|
1171
|
-
);
|
|
1172
|
-
}
|
|
1173
1421
|
|
|
1174
1422
|
// Emit tool_result events AFTER truncation so downstream consumers
|
|
1175
1423
|
// (e.g. session persistence) receive the truncated content.
|
|
@@ -1221,59 +1469,20 @@ export class AgentLoop {
|
|
|
1221
1469
|
|
|
1222
1470
|
toolUseTurns++;
|
|
1223
1471
|
|
|
1224
|
-
//
|
|
1225
|
-
//
|
|
1226
|
-
//
|
|
1227
|
-
//
|
|
1228
|
-
//
|
|
1229
|
-
//
|
|
1230
|
-
|
|
1231
|
-
if (hasToolError) {
|
|
1232
|
-
consecutiveErrorTurns++;
|
|
1233
|
-
} else {
|
|
1234
|
-
consecutiveErrorTurns = 0;
|
|
1235
|
-
}
|
|
1236
|
-
const toolErrorArgs: ToolErrorArgs = {
|
|
1237
|
-
hasToolError,
|
|
1238
|
-
consecutiveErrorTurns,
|
|
1239
|
-
maxConsecutiveErrorNudges: MAX_CONSECUTIVE_ERROR_NUDGES,
|
|
1240
|
-
};
|
|
1241
|
-
const toolErrorCtx: TurnContext = resolveLoopTurnContext(
|
|
1242
|
-
turnContext,
|
|
1243
|
-
requestId,
|
|
1244
|
-
toolUseTurns - 1,
|
|
1245
|
-
);
|
|
1246
|
-
const toolErrorDecision = await runPipeline<
|
|
1247
|
-
ToolErrorArgs,
|
|
1248
|
-
ToolErrorDecision
|
|
1249
|
-
>(
|
|
1250
|
-
"toolError",
|
|
1251
|
-
getMiddlewaresFor("toolError"),
|
|
1252
|
-
// Terminal: the canonical nudge decision. The default plugin's
|
|
1253
|
-
// middleware is a passthrough (so later-registered user plugins
|
|
1254
|
-
// aren't shadowed), so this terminal is what actually produces
|
|
1255
|
-
// the decision when no user plugin overrides it. Wiring the
|
|
1256
|
-
// decision here also ensures the nudge fires for direct
|
|
1257
|
-
// AgentLoop callers (tests, benchmarks) that skip
|
|
1258
|
-
// `bootstrapPlugins()` and therefore never register the default.
|
|
1259
|
-
async (args) => defaultToolErrorTerminal(args),
|
|
1260
|
-
toolErrorArgs,
|
|
1261
|
-
toolErrorCtx,
|
|
1262
|
-
DEFAULT_TIMEOUTS.toolError,
|
|
1263
|
-
);
|
|
1264
|
-
if (toolErrorDecision.action === "nudge") {
|
|
1265
|
-
resultBlocks.push({
|
|
1266
|
-
type: "text",
|
|
1267
|
-
text: toolErrorDecision.nudgeText,
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
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);
|
|
1270
1479
|
|
|
1271
|
-
// Add tool results as a user message and continue the loop
|
|
1480
|
+
// Add tool results as a user message and continue the loop.
|
|
1272
1481
|
history.push({ role: "user", content: resultBlocks });
|
|
1273
1482
|
|
|
1274
1483
|
// Invoke checkpoint callback after tool results are in history.
|
|
1275
|
-
//
|
|
1276
|
-
//
|
|
1484
|
+
// Handoff is offered first so a queued message takes precedence over
|
|
1485
|
+
// the mid-loop budget yield below.
|
|
1277
1486
|
if (onCheckpoint) {
|
|
1278
1487
|
const decision = await onCheckpoint({
|
|
1279
1488
|
turnIndex: toolUseTurns - 1, // 0-based (toolUseTurns was already incremented)
|
|
@@ -1281,7 +1490,64 @@ export class AgentLoop {
|
|
|
1281
1490
|
hasToolUse: true,
|
|
1282
1491
|
history,
|
|
1283
1492
|
});
|
|
1284
|
-
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";
|
|
1285
1551
|
break;
|
|
1286
1552
|
}
|
|
1287
1553
|
}
|
|
@@ -1300,6 +1566,15 @@ export class AgentLoop {
|
|
|
1300
1566
|
}),
|
|
1301
1567
|
);
|
|
1302
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
|
+
}
|
|
1303
1578
|
}
|
|
1304
1579
|
await emitExit("aborted_via_error");
|
|
1305
1580
|
break;
|
|
@@ -1313,11 +1588,9 @@ export class AgentLoop {
|
|
|
1313
1588
|
Sentry.captureException(err);
|
|
1314
1589
|
}
|
|
1315
1590
|
onEvent({ type: "error", error: err });
|
|
1316
|
-
// Catch-block fallback.
|
|
1317
|
-
//
|
|
1318
|
-
//
|
|
1319
|
-
// before the throw. Otherwise, this is the genuine
|
|
1320
|
-
// 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.
|
|
1321
1594
|
await emitExit("error");
|
|
1322
1595
|
break;
|
|
1323
1596
|
}
|
|
@@ -1332,7 +1605,12 @@ export class AgentLoop {
|
|
|
1332
1605
|
"Agent loop exited",
|
|
1333
1606
|
);
|
|
1334
1607
|
|
|
1335
|
-
return
|
|
1608
|
+
return {
|
|
1609
|
+
history,
|
|
1610
|
+
exitReason,
|
|
1611
|
+
appendedNewMessages,
|
|
1612
|
+
newMessages: history.slice(newMessagesStart),
|
|
1613
|
+
};
|
|
1336
1614
|
}
|
|
1337
1615
|
}
|
|
1338
1616
|
|
|
@@ -1433,7 +1711,7 @@ export function compactAxTreeHistory(messages: Message[]): Message[] {
|
|
|
1433
1711
|
* turn. Using the last user message unconditionally would leave the most
|
|
1434
1712
|
* recent tool screenshots unprotected from stripping.
|
|
1435
1713
|
*/
|
|
1436
|
-
function
|
|
1714
|
+
function stripOldMediaBlocks(history: Message[]): Message[] {
|
|
1437
1715
|
// Find the last user message that contains tool_result blocks.
|
|
1438
1716
|
let lastToolResultUserIdx = -1;
|
|
1439
1717
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
@@ -1450,29 +1728,32 @@ function stripOldImageBlocks(history: Message[]): Message[] {
|
|
|
1450
1728
|
// Keep the most recent tool-result user message intact (current turn)
|
|
1451
1729
|
if (idx === lastToolResultUserIdx || msg.role !== "user") return msg;
|
|
1452
1730
|
|
|
1453
|
-
// Check if any tool_result blocks
|
|
1454
|
-
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(
|
|
1455
1735
|
(b) =>
|
|
1456
1736
|
b.type === "tool_result" &&
|
|
1457
|
-
(b as ToolResultContent).contentBlocks?.some(
|
|
1458
|
-
(cb) => cb.type === "image",
|
|
1459
|
-
),
|
|
1737
|
+
(b as ToolResultContent).contentBlocks?.some(isMedia),
|
|
1460
1738
|
);
|
|
1461
|
-
if (!
|
|
1739
|
+
if (!hasMedia) return msg;
|
|
1462
1740
|
|
|
1463
|
-
// 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.
|
|
1464
1745
|
return {
|
|
1465
1746
|
...msg,
|
|
1466
1747
|
content: msg.content.map((b) => {
|
|
1467
1748
|
if (b.type !== "tool_result") return b;
|
|
1468
1749
|
const tr = b as ToolResultContent;
|
|
1469
|
-
if (!tr.contentBlocks?.some(
|
|
1750
|
+
if (!tr.contentBlocks?.some(isMedia)) return b;
|
|
1470
1751
|
return {
|
|
1471
1752
|
...tr,
|
|
1472
1753
|
contentBlocks: undefined,
|
|
1473
1754
|
content:
|
|
1474
1755
|
(tr.content || "") +
|
|
1475
|
-
"\n[
|
|
1756
|
+
"\n[Media (image/audio) was captured and shown previously — binary data removed to save context.]",
|
|
1476
1757
|
};
|
|
1477
1758
|
}),
|
|
1478
1759
|
};
|