@vellumai/assistant 0.8.6 → 0.8.7-dev.202606052118.34cd356
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -4
- package/Dockerfile +21 -4
- package/bun.lock +13 -4
- package/docker-entrypoint.sh +12 -8
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/plugins.md +110 -83
- package/examples/plugins/echo/README.md +13 -12
- package/examples/plugins/echo/register.ts +0 -54
- package/knip.json +1 -0
- package/node_modules/@vellumai/environments/bun.lock +24 -0
- package/node_modules/@vellumai/environments/package.json +18 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
- package/node_modules/@vellumai/environments/src/index.ts +11 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
- package/node_modules/@vellumai/environments/src/types.ts +70 -0
- package/node_modules/@vellumai/environments/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +13 -8
- package/openapi.yaml +6964 -539
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +88 -54
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +188 -45
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +7 -5
- package/src/__tests__/agent-loop-thinking.test.ts +17 -12
- package/src/__tests__/agent-loop.test.ts +238 -422
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +6 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +296 -57
- package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
- package/src/__tests__/app-control-flow.test.ts +6 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +4 -11
- package/src/__tests__/approval-routes-http.test.ts +8 -3
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-event.test.ts +15 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/assistant-stream-state.test.ts +645 -0
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/avatar-e2e.test.ts +7 -37
- package/src/__tests__/avatar-generator.test.ts +12 -42
- package/src/__tests__/avatar-identity-sync.test.ts +28 -3
- package/src/__tests__/background-shell-bash.test.ts +3 -7
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +69 -15
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/call-pointer-messages.test.ts +5 -3
- package/src/__tests__/call-site-routing-provider.test.ts +22 -40
- package/src/__tests__/catalog-files.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +49 -21
- package/src/__tests__/channel-approvals.test.ts +4 -2
- package/src/__tests__/channel-invite-transport.test.ts +1 -5
- package/src/__tests__/channel-readiness-routes.test.ts +0 -4
- package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
- package/src/__tests__/channel-retry-sweep.test.ts +71 -79
- package/src/__tests__/clawhub-files.test.ts +1 -0
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction-events.test.ts +5 -17
- package/src/__tests__/compaction-trail-store.test.ts +1 -79
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/computer-use-tools.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +34 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +70 -25
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +9 -7
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -34
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +476 -963
- package/src/__tests__/conversation-agent-loop.test.ts +823 -1321
- package/src/__tests__/conversation-analysis-routes.test.ts +7 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-clear-safety.test.ts +20 -10
- package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-disk-view.test.ts +10 -17
- package/src/__tests__/conversation-fork-crud.test.ts +86 -172
- package/src/__tests__/conversation-fork-route.test.ts +16 -14
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +10 -7
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -0
- package/src/__tests__/conversation-process-callsite.test.ts +27 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +80 -51
- package/src/__tests__/conversation-queue.test.ts +272 -164
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -2
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +8 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +317 -313
- package/src/__tests__/conversation-runtime-workspace.test.ts +114 -36
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +42 -31
- package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
- package/src/__tests__/conversation-speed-override.test.ts +8 -22
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +90 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
- package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +20 -17
- package/src/__tests__/conversation-workspace-injection.test.ts +114 -23
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +34 -13
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/cross-provider-web-search.test.ts +220 -3
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -0
- package/src/__tests__/disk-pressure-guard.test.ts +66 -0
- package/src/__tests__/disk-pressure-routes.test.ts +9 -2
- package/src/__tests__/dm-persistence.test.ts +12 -3
- package/src/__tests__/dynamic-page-surface.test.ts +99 -0
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/filing-service.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +55 -14
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/gemini-inline-media.test.ts +78 -0
- package/src/__tests__/gemini-provider.test.ts +351 -28
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +10 -8
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +3 -1
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +162 -0
- package/src/__tests__/history-repair-observability.test.ts +1 -1
- package/src/__tests__/history-repair.test.ts +2 -1
- package/src/__tests__/host-app-control-proxy.test.ts +2 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/host-file-edit-tool.test.ts +4 -2
- package/src/__tests__/host-file-proxy.test.ts +31 -0
- package/src/__tests__/host-file-read-tool.test.ts +4 -2
- package/src/__tests__/host-file-write-tool.test.ts +9 -3
- package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
- package/src/__tests__/host-shell-tool.test.ts +9 -4
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +47 -114
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +12 -3
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +214 -0
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-attachments.test.ts +7 -8
- package/src/__tests__/list-messages-hidden-metadata.test.ts +55 -15
- package/src/__tests__/list-messages-page-latest.test.ts +60 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +56 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +23 -47
- package/src/__tests__/llm-usage-store.test.ts +268 -1
- package/src/__tests__/log-export-routes.test.ts +59 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-health-check.test.ts +18 -13
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/messaging-send-tool.test.ts +8 -4
- package/src/__tests__/migration-export-http.test.ts +12 -12
- package/src/__tests__/migration-import-commit-http.test.ts +8 -8
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +205 -20
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/oauth-commands-routes.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +12 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +97 -71
- package/src/__tests__/openai-responses-provider.test.ts +21 -77
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -286
- package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +3 -1
- package/src/__tests__/pipeline-runner.test.ts +31 -43
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-bootstrap.test.ts +62 -51
- package/src/__tests__/plugin-registry.test.ts +0 -27
- package/src/__tests__/plugin-route-contribution.test.ts +6 -16
- package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
- package/src/__tests__/plugin-types.test.ts +8 -173
- package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
- package/src/__tests__/process-message-background-slack.test.ts +17 -16
- package/src/__tests__/process-message-display-content.test.ts +36 -44
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
- package/src/__tests__/provider-error-scenarios.test.ts +7 -6
- package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
- package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
- package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
- package/src/__tests__/provider-usage-tracking.test.ts +0 -6
- package/src/__tests__/ratelimit.test.ts +9 -4
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/relay-server.test.ts +20 -13
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
- package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
- package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +390 -0
- package/src/__tests__/schedule-routes.test.ts +683 -12
- package/src/__tests__/schedule-store.test.ts +108 -0
- package/src/__tests__/schedule-tools.test.ts +160 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
- package/src/__tests__/secret-response-routing.test.ts +13 -11
- package/src/__tests__/send-endpoint-busy.test.ts +6 -2
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +44 -11
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/starter-task-flow.test.ts +6 -6
- package/src/__tests__/strip-memory-injections.test.ts +102 -14
- package/src/__tests__/subagent-call-site-routing.test.ts +3 -3
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +74 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +9 -25
- package/src/__tests__/thread-backfill.test.ts +4 -9
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +481 -16
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +1 -1
- package/src/__tests__/tools-audio-read.test.ts +113 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
- package/src/__tests__/turn-events-store.test.ts +11 -7
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +2 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
- package/src/__tests__/voice-session-bridge.test.ts +19 -10
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +695 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +125 -0
- package/src/acp/auto-install.ts +174 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +52 -11
- package/src/acp/resolve-agent.test.ts +147 -6
- package/src/acp/resolve-agent.ts +81 -7
- package/src/acp/resume-hint.ts +22 -0
- package/src/acp/session-manager.ts +487 -71
- package/src/agent/compaction-circuit.ts +98 -0
- package/src/agent/loop.ts +651 -450
- package/src/api/README.md +19 -17
- package/src/api/constants/tool-execution.ts +21 -0
- package/src/api/events/assistant-activity-state.ts +75 -0
- package/src/api/events/assistant-outbound-attachment.ts +25 -27
- package/src/api/events/assistant-text-delta.ts +6 -8
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/assistant-turn-start.ts +5 -7
- package/src/api/events/avatar-updated.ts +24 -0
- package/src/api/events/compaction-circuit-closed.ts +26 -0
- package/src/api/events/compaction-circuit-open.ts +28 -0
- package/src/api/events/confirmation-request.ts +114 -0
- package/src/api/events/contact-request.ts +33 -0
- package/src/api/events/conversation-error.ts +77 -0
- package/src/api/events/conversation-list-invalidated.ts +38 -0
- package/src/api/events/conversation-title-updated.ts +24 -0
- package/src/api/events/disk-pressure-status-changed.ts +61 -0
- package/src/api/events/document-comment-created.ts +24 -28
- package/src/api/events/document-comment-deleted.ts +6 -8
- package/src/api/events/document-comment-reopened.ts +6 -8
- package/src/api/events/document-comment-resolved.ts +8 -10
- package/src/api/events/document-editor-update.ts +27 -0
- package/src/api/events/error.ts +32 -0
- package/src/api/events/generation-cancelled.ts +4 -6
- package/src/api/events/generation-handoff.ts +13 -15
- package/src/api/events/home-feed-updated.ts +26 -0
- package/src/api/events/identity-changed.ts +32 -0
- package/src/api/events/interaction-resolved.ts +50 -0
- package/src/api/events/message-complete.ts +10 -12
- package/src/api/events/message-dequeued.ts +21 -0
- package/src/api/events/message-queued-deleted.ts +23 -0
- package/src/api/events/message-queued.ts +22 -0
- package/src/api/events/message-request-complete.ts +29 -0
- package/src/api/events/navigate-settings.ts +20 -0
- package/src/api/events/notification-intent.ts +33 -0
- package/src/api/events/open-url.ts +6 -8
- package/src/api/events/question-request.ts +67 -0
- package/src/api/events/relationship-state-updated.ts +4 -6
- package/src/api/events/secret-request.ts +42 -0
- package/src/api/events/subagent-event.ts +79 -0
- package/src/api/events/subagent-spawned.ts +40 -0
- package/src/api/events/subagent-status-changed.ts +65 -0
- package/src/api/events/sync-changed.ts +29 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-result.ts +129 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/tool-use-start.ts +8 -10
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/events/turn-profile-auto-routed.ts +28 -0
- package/src/api/events/ui-surface-complete.ts +30 -0
- package/src/api/events/ui-surface-dismiss.ts +22 -0
- package/src/api/events/ui-surface-show.ts +67 -0
- package/src/api/events/ui-surface-update.ts +26 -0
- package/src/api/events/usage-update.ts +34 -0
- package/src/api/events/user-message-echo.ts +35 -0
- package/src/api/index.ts +389 -0
- package/src/api/requests/dictation.ts +45 -0
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/api/responses/disk-pressure-status.ts +26 -0
- package/src/api/responses/home.ts +217 -0
- package/src/api/responses/llm-context-response.ts +2 -0
- package/src/api/responses/memory-v3-selection-log.ts +50 -0
- package/src/api/responses/subagent-detail.ts +48 -0
- package/src/approvals/guardian-decision-primitive.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +7 -10
- package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
- package/src/avatar/__tests__/avatar-store.test.ts +198 -0
- package/src/avatar/avatar-manifest.ts +195 -0
- package/src/avatar/avatar-store.ts +113 -0
- package/src/avatar/traits-png-sync.ts +8 -2
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +5 -1
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +13 -27
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/conversations.ts +15 -1
- package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
- package/src/cli/commands/db/__tests__/status.test.ts +253 -0
- package/src/cli/commands/db/format.ts +48 -0
- package/src/cli/commands/db/index.ts +29 -0
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
- package/src/cli/commands/db/repair-step-integrity.ts +146 -0
- package/src/cli/commands/db/repair-steps.ts +164 -0
- package/src/cli/commands/db/repair.ts +141 -0
- package/src/cli/commands/db/status.ts +366 -0
- package/src/cli/commands/memory-v3.ts +159 -445
- package/src/cli/commands/notifications.ts +112 -60
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/program.ts +4 -5
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +4 -4
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +24 -13
- package/src/config/bundled-skills/acp/SKILL.md +64 -30
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -387
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +2 -7
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +68 -12
- package/src/config/schemas/__tests__/memory-v2.test.ts +2 -226
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/call-site-catalog.ts +8 -15
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +3 -3
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +8 -253
- package/src/config/schemas/memory-v3.ts +47 -0
- package/src/config/schemas/memory.ts +6 -1
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +55 -32
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +42 -0
- package/src/context/tool-result-truncation.ts +1 -66
- package/src/context/window-manager.ts +141 -26
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
- package/src/daemon/approval-generators.ts +4 -4
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +7 -1
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +793 -215
- package/src/daemon/conversation-agent-loop.ts +487 -1478
- package/src/daemon/conversation-error.ts +7 -7
- package/src/daemon/conversation-history.ts +27 -10
- package/src/daemon/conversation-launch.ts +4 -8
- package/src/daemon/conversation-lifecycle.ts +13 -42
- package/src/daemon/conversation-messaging.ts +8 -9
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +109 -93
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +209 -382
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +15 -95
- package/src/daemon/conversation-surfaces.ts +277 -73
- package/src/daemon/conversation-tool-setup.ts +5 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +123 -146
- package/src/daemon/daemon-skill-host.ts +2 -6
- package/src/daemon/disk-pressure-guard.ts +35 -29
- package/src/daemon/external-plugins-bootstrap.ts +53 -32
- package/src/daemon/first-greeting.ts +26 -4
- package/src/daemon/guardian-action-generators.ts +2 -2
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +9 -23
- package/src/daemon/handlers/shared.ts +158 -82
- package/src/daemon/handlers/skills.ts +53 -20
- package/src/daemon/host-app-control-proxy.ts +54 -1
- package/src/daemon/host-cu-proxy.ts +46 -22
- package/src/daemon/host-file-proxy.ts +25 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -6
- package/src/daemon/lifecycle.ts +53 -55
- package/src/daemon/message-protocol.ts +2 -3
- package/src/daemon/message-provenance.ts +49 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/contacts.ts +3 -20
- package/src/daemon/message-types/conversations.ts +13 -111
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +4 -17
- package/src/daemon/message-types/integrations.ts +2 -6
- package/src/daemon/message-types/messages.ts +37 -400
- package/src/daemon/message-types/notifications.ts +2 -32
- package/src/daemon/message-types/settings.ts +3 -8
- package/src/daemon/message-types/skills.ts +4 -0
- package/src/daemon/message-types/surfaces.ts +138 -3
- package/src/daemon/message-types/sync.ts +12 -25
- package/src/daemon/message-types/workspace.ts +3 -11
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +50 -49
- package/src/daemon/server.ts +14 -0
- package/src/daemon/tool-side-effects.ts +10 -7
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +32 -0
- package/src/daemon/wake-target-adapter.ts +14 -2
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +6 -1
- package/src/heartbeat/heartbeat-run-store.ts +54 -1
- package/src/heartbeat/heartbeat-service.ts +42 -0
- package/src/home/feed-types.ts +36 -221
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/email-ipc.test.ts +0 -9
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
- package/src/ipc/routes/route-adapter.ts +45 -6
- package/src/ipc/skill-routes/__tests__/memory.test.ts +33 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
- package/src/ipc/skill-routes/memory.ts +29 -14
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +13 -61
- package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/conversation-queries.test.ts +192 -8
- package/src/memory/__tests__/db-maintenance.test.ts +128 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
- package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/context-search/agent-runner.ts +2 -4
- package/src/memory/conversation-crud.ts +39 -8
- package/src/memory/conversation-queries.ts +78 -22
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +14 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/consolidation.ts +8 -11
- package/src/memory/graph/conversation-graph-memory.ts +106 -8
- package/src/memory/graph/extraction.ts +6 -9
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.ts +20 -26
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/job-handlers/conversation-starters.ts +45 -34
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs-store.ts +36 -1
- package/src/memory/jobs-worker.ts +82 -43
- package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
- package/src/memory/llm-request-log-source-local.ts +0 -11
- package/src/memory/llm-request-log-source.ts +9 -25
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +234 -50
- package/src/memory/memory-marker.ts +17 -0
- package/src/memory/memory-retrospective-job.ts +6 -2
- package/src/memory/memory-v2-activation-log-store.ts +1 -83
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
- package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
- package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
- package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +27 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +125 -1
- package/src/memory/v2/__tests__/migration.test.ts +11 -3
- package/src/memory/v2/__tests__/page-index.test.ts +37 -1
- package/src/memory/v2/__tests__/router.test.ts +14 -4
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +99 -10
- package/src/memory/v2/migration.ts +5 -3
- package/src/memory/v2/page-index.ts +11 -0
- package/src/memory/v2/router.ts +8 -11
- package/src/memory/v2/sweep-job.ts +8 -11
- package/src/memory/v2/types.ts +1 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
- package/src/messaging/providers/slack/render-transcript.ts +2 -2
- package/src/messaging/style-analyzer.ts +8 -11
- package/src/notifications/conversation-pairing.ts +8 -13
- package/src/notifications/decision-engine.ts +16 -16
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/notifications/preference-extractor.ts +11 -14
- package/src/permissions/prompter.ts +46 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +10 -1
- package/src/plugin-api/types.ts +176 -4
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +15 -0
- package/src/plugins/defaults/compaction/register.ts +24 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/package.json +15 -0
- package/src/plugins/defaults/empty-response/register.ts +23 -0
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/history-repair/package.json +15 -0
- package/src/plugins/defaults/history-repair/register.ts +24 -0
- package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
- package/src/plugins/defaults/index.ts +22 -49
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors.ts → memory-retrieval/injectors.ts} +295 -112
- package/src/plugins/defaults/memory-v3-shadow/__tests__/assign.test.ts +242 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +118 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/core.test.ts +39 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +36 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +37 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/health.test.ts +219 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +330 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +288 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/needle.test.ts +107 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +436 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/reconcile.test.ts +274 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +61 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/router.test.ts +332 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +179 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selector.test.ts +470 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +432 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/snapshot.test.ts +168 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/tree.test.ts +192 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +54 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +106 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +44 -0
- package/src/plugins/defaults/memory-v3-shadow/assign.ts +268 -0
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +124 -0
- package/src/plugins/defaults/memory-v3-shadow/core.ts +26 -0
- package/src/plugins/defaults/memory-v3-shadow/data/README.md +84 -0
- package/src/plugins/defaults/memory-v3-shadow/data/assignments.json +5 -0
- package/src/plugins/defaults/memory-v3-shadow/data/core.json +1 -0
- package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-x.md +9 -0
- package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-y.md +9 -0
- package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-b/topic-z.md +9 -0
- package/src/plugins/defaults/memory-v3-shadow/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +314 -0
- package/src/plugins/defaults/memory-v3-shadow/needle.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +126 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +15 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +34 -0
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/plugins/defaults/memory-v3-shadow/reconcile.ts +523 -0
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +32 -0
- package/src/plugins/defaults/memory-v3-shadow/router.ts +190 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +84 -0
- package/src/plugins/defaults/memory-v3-shadow/selector.ts +226 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +349 -0
- package/src/plugins/defaults/memory-v3-shadow/snapshot.ts +209 -0
- package/src/plugins/defaults/memory-v3-shadow/tree.ts +174 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +88 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +15 -0
- package/src/plugins/defaults/title-generate/register.ts +35 -0
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +15 -0
- package/src/plugins/defaults/tool-error/register.ts +23 -0
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +24 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +132 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/pipeline.ts +8 -35
- package/src/plugins/registry.ts +8 -25
- package/src/plugins/types.ts +62 -721
- package/src/plugins/user-loader.ts +4 -3
- package/src/proactive-artifact/aux-message-injector.ts +4 -5
- package/src/proactive-artifact/job.test.ts +28 -21
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
- package/src/prompts/templates/BOOTSTRAP.md +7 -3
- package/src/prompts/templates/system-sections.ts +21 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -25
- package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
- package/src/providers/anthropic/client.ts +61 -34
- package/src/providers/call-site-routing.ts +1 -9
- package/src/providers/gemini/client.ts +152 -34
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -2
- package/src/providers/openai/chat-completions-provider.ts +45 -4
- package/src/providers/openai/responses-provider.ts +1 -4
- package/src/providers/openrouter/client.ts +2 -6
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/providers/provider-send-message.ts +6 -6
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/retry.ts +0 -5
- package/src/providers/types.ts +11 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +141 -32
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +95 -23
- package/src/runtime/assistant-event-hub.ts +38 -8
- package/src/runtime/assistant-stream-state.ts +368 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
- package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
- package/src/runtime/auth/route-policy.ts +42 -1079
- package/src/runtime/background-job-runner.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -1
- package/src/runtime/channel-approvals.ts +4 -15
- package/src/runtime/channel-invite-transport.ts +5 -6
- package/src/runtime/channel-readiness-service.ts +2 -5
- package/src/runtime/channel-retry-sweep.ts +12 -16
- package/src/runtime/http-router.ts +35 -43
- package/src/runtime/http-types.ts +23 -71
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/invite-instruction-generator.ts +3 -3
- package/src/runtime/pending-interactions.ts +3 -2
- package/src/runtime/routes/__tests__/acp-routes.test.ts +253 -55
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
- package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +9 -5
- package/src/runtime/routes/acp-routes.test.ts +186 -100
- package/src/runtime/routes/acp-routes.ts +110 -35
- package/src/runtime/routes/app-management-routes.ts +93 -131
- package/src/runtime/routes/app-routes.ts +38 -20
- package/src/runtime/routes/approval-routes.ts +17 -5
- package/src/runtime/routes/attachment-routes.ts +51 -16
- package/src/runtime/routes/audio-routes.ts +1 -0
- package/src/runtime/routes/audit-routes.ts +5 -0
- package/src/runtime/routes/auth-routes.ts +5 -0
- package/src/runtime/routes/avatar-routes.ts +264 -59
- package/src/runtime/routes/background-tool-routes.ts +9 -0
- package/src/runtime/routes/background-wake-routes.ts +13 -3
- package/src/runtime/routes/backup-routes.ts +45 -0
- package/src/runtime/routes/bookmark-routes.ts +13 -0
- package/src/runtime/routes/brain-graph-routes.ts +9 -0
- package/src/runtime/routes/browser-routes.ts +6 -1
- package/src/runtime/routes/browser-tabs-routes.ts +11 -10
- package/src/runtime/routes/btw-routes.ts +34 -24
- package/src/runtime/routes/cache-routes.ts +13 -0
- package/src/runtime/routes/call-routes.ts +21 -10
- package/src/runtime/routes/channel-availability-routes.ts +5 -1
- package/src/runtime/routes/channel-readiness-routes.ts +37 -4
- package/src/runtime/routes/channel-route-definitions.ts +21 -0
- package/src/runtime/routes/channel-verification-routes.ts +21 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
- package/src/runtime/routes/client-routes.ts +9 -0
- package/src/runtime/routes/consolidation-routes.ts +133 -25
- package/src/runtime/routes/contact-prompt-routes.ts +9 -0
- package/src/runtime/routes/contact-routes.ts +90 -23
- package/src/runtime/routes/content-source-routes.ts +5 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
- package/src/runtime/routes/conversation-attention-routes.ts +5 -0
- package/src/runtime/routes/conversation-cli-routes.ts +54 -7
- package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
- package/src/runtime/routes/conversation-list-routes.ts +81 -12
- package/src/runtime/routes/conversation-management-routes.ts +57 -14
- package/src/runtime/routes/conversation-query-routes.ts +90 -41
- package/src/runtime/routes/conversation-routes.ts +446 -204
- package/src/runtime/routes/conversation-starter-routes.ts +35 -20
- package/src/runtime/routes/conversations-import-routes.ts +30 -8
- package/src/runtime/routes/credential-prompt-routes.ts +5 -0
- package/src/runtime/routes/credential-routes.ts +25 -6
- package/src/runtime/routes/debug-bash-routes.ts +5 -0
- package/src/runtime/routes/debug-routes.ts +11 -2
- package/src/runtime/routes/defer-routes.ts +13 -0
- package/src/runtime/routes/diagnostics-routes.ts +37 -46
- package/src/runtime/routes/disk-pressure-routes.ts +17 -31
- package/src/runtime/routes/document-comments-routes.ts +46 -27
- package/src/runtime/routes/documents-routes.ts +25 -10
- package/src/runtime/routes/domain-routes.ts +98 -51
- package/src/runtime/routes/email-routes.ts +33 -0
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +107 -8
- package/src/runtime/routes/filing-routes.ts +9 -4
- package/src/runtime/routes/gateway-log-routes.ts +31 -4
- package/src/runtime/routes/global-search-routes.ts +53 -50
- package/src/runtime/routes/group-routes.ts +21 -5
- package/src/runtime/routes/guardian-action-routes.ts +9 -0
- package/src/runtime/routes/guardian-approval-interception.ts +0 -31
- package/src/runtime/routes/heartbeat-routes.ts +57 -21
- package/src/runtime/routes/home-feed-routes.ts +23 -19
- package/src/runtime/routes/home-state-routes.ts +8 -40
- package/src/runtime/routes/host-app-control-routes.ts +6 -1
- package/src/runtime/routes/host-bash-routes.ts +5 -0
- package/src/runtime/routes/host-browser-routes.ts +13 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -1
- package/src/runtime/routes/host-file-routes.ts +26 -6
- package/src/runtime/routes/host-transfer-routes.ts +13 -2
- package/src/runtime/routes/http-adapter.ts +1 -2
- package/src/runtime/routes/identity-intro-cache.ts +28 -40
- package/src/runtime/routes/identity-routes.ts +236 -20
- package/src/runtime/routes/image-generation-routes.ts +45 -2
- package/src/runtime/routes/inbound-message-handler.ts +16 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
- package/src/runtime/routes/inference-send-routes.ts +11 -11
- package/src/runtime/routes/integrations/a2a.ts +32 -7
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +23 -3
- package/src/runtime/routes/integrations/slack/share.ts +36 -8
- package/src/runtime/routes/integrations/telegram.ts +34 -9
- package/src/runtime/routes/integrations/twilio.ts +77 -7
- package/src/runtime/routes/integrations/vercel.ts +3 -3
- package/src/runtime/routes/internal-oauth-routes.ts +5 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/internal-twilio-routes.ts +13 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
- package/src/runtime/routes/log-export-routes.ts +36 -10
- package/src/runtime/routes/mcp-auth-routes.ts +25 -0
- package/src/runtime/routes/memory-item-routes.ts +21 -10
- package/src/runtime/routes/memory-v2-routes.ts +105 -44
- package/src/runtime/routes/memory-v3-routes.ts +306 -408
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +29 -0
- package/src/runtime/routes/notification-routes.ts +17 -1
- package/src/runtime/routes/oauth-apps.ts +99 -23
- package/src/runtime/routes/oauth-commands-routes.ts +37 -14
- package/src/runtime/routes/oauth-connect-routes.ts +9 -0
- package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
- package/src/runtime/routes/oauth-providers.ts +79 -15
- package/src/runtime/routes/platform-routes.ts +102 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +9 -6
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
- package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
- package/src/runtime/routes/playground/force-compact.ts +2 -2
- package/src/runtime/routes/playground/helpers.ts +1 -2
- package/src/runtime/routes/playground/inject-failures.ts +13 -8
- package/src/runtime/routes/playground/reset-circuit.ts +14 -9
- package/src/runtime/routes/playground/seed-conversation.ts +1 -1
- package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
- package/src/runtime/routes/playground/state.ts +4 -3
- package/src/runtime/routes/plugins-routes.ts +22 -19
- package/src/runtime/routes/profiler-routes.ts +17 -4
- package/src/runtime/routes/ps-routes.ts +5 -0
- package/src/runtime/routes/publish-routes.ts +13 -3
- package/src/runtime/routes/question-routes.ts +5 -0
- package/src/runtime/routes/recording-routes.ts +25 -12
- package/src/runtime/routes/rename-conversation-routes.ts +10 -0
- package/src/runtime/routes/sanity-routes.ts +9 -2
- package/src/runtime/routes/schedule-routes.ts +288 -88
- package/src/runtime/routes/secret-routes.ts +31 -6
- package/src/runtime/routes/sequence-routes.ts +33 -0
- package/src/runtime/routes/settings-routes.ts +65 -19
- package/src/runtime/routes/skills-routes.ts +166 -73
- package/src/runtime/routes/slack-channel-routes.ts +5 -0
- package/src/runtime/routes/stt-routes.ts +13 -6
- package/src/runtime/routes/subagents-routes.ts +24 -18
- package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
- package/src/runtime/routes/surface-action-routes.ts +9 -0
- package/src/runtime/routes/surface-content-routes.ts +10 -2
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/task-routes.ts +37 -0
- package/src/runtime/routes/telemetry-routes.ts +9 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trace-event-routes.ts +42 -1
- package/src/runtime/routes/trust-rules-routes.ts +31 -2
- package/src/runtime/routes/tts-routes.ts +48 -6
- package/src/runtime/routes/types.ts +83 -16
- package/src/runtime/routes/ui-request-routes.ts +5 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
- package/src/runtime/routes/usage-routes.ts +118 -42
- package/src/runtime/routes/user-routes-cli.ts +9 -0
- package/src/runtime/routes/user-routes.ts +5 -1
- package/src/runtime/routes/wake-conversation-routes.ts +5 -0
- package/src/runtime/routes/watcher-routes.ts +21 -0
- package/src/runtime/routes/webhook-routes.ts +50 -2
- package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
- package/src/runtime/routes/work-items-routes.ts +49 -23
- package/src/runtime/routes/workspace-commit-routes.ts +5 -0
- package/src/runtime/routes/workspace-routes.test.ts +42 -0
- package/src/runtime/routes/workspace-routes.ts +124 -9
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +8 -4
- package/src/runtime/services/analyze-conversation.ts +5 -8
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/sync/resource-sync-events.ts +16 -2
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +28 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +15 -6
- package/src/signals/cancel.ts +2 -4
- package/src/signals/user-message.ts +5 -8
- package/src/skills/catalog-files.ts +4 -1
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -0
- package/src/skills/skillssh-files.ts +1 -0
- package/src/subagent/manager.ts +20 -11
- package/src/telemetry/types.ts +55 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +250 -4
- package/src/telemetry/usage-telemetry-reporter.ts +88 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +198 -93
- package/src/tools/acp/spawn.ts +32 -70
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/definitions.ts +8 -4
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
- package/src/tools/ask-question/ask-question-tool.ts +85 -90
- package/src/tools/computer-use/definitions.ts +28 -24
- package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
- package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
- package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
- package/src/tools/credentials/vault.ts +112 -111
- package/src/tools/execution-target.ts +1 -1
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/edit.ts +45 -42
- package/src/tools/filesystem/list.ts +33 -30
- package/src/tools/filesystem/read.ts +54 -35
- package/src/tools/filesystem/write.ts +69 -32
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-fetch.ts +49 -46
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +223 -61
- package/src/tools/registry.ts +39 -16
- package/src/tools/schedule/create.ts +13 -0
- package/src/tools/schedule/update.ts +16 -0
- package/src/tools/shared/filesystem/audio-read.ts +122 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/execute.ts +34 -31
- package/src/tools/skills/load.ts +29 -23
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/system/avatar-generator.ts +13 -22
- package/src/tools/system/request-permission.ts +30 -27
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/terminal/shell.ts +190 -61
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +74 -23
- package/src/tools/ui-surface/definitions.ts +99 -10
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/usage/types.ts +10 -0
- package/src/util/errors.ts +2 -2
- package/src/util/map-limit.ts +27 -0
- package/src/util/mutex.ts +47 -0
- package/src/util/platform.ts +15 -12
- package/src/work-items/work-item-runner.ts +7 -2
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
- package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
- package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
- package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
- package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/src/workspace/provider-commit-message-generator.ts +15 -17
- package/tsconfig.json +4 -1
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -262
- package/src/__tests__/empty-response-pipeline.test.ts +0 -301
- package/src/__tests__/history-repair-pipeline.test.ts +0 -396
- package/src/__tests__/llm-call-pipeline.test.ts +0 -281
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -514
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -481
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -344
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
- package/src/cli/commands/memory-v3-render.ts +0 -491
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/daemon/message-types/disk-pressure.ts +0 -9
- package/src/email/feature-gate.ts +0 -23
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
- package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -706
- package/src/memory/v3/__tests__/filter.test.ts +0 -560
- package/src/memory/v3/__tests__/gate.test.ts +0 -637
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
- package/src/memory/v3/__tests__/loop.test.ts +0 -775
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -489
- package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -508
- package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
- package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
- package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
- package/src/memory/v3/__tests__/validate.test.ts +0 -277
- package/src/memory/v3/auto-edges.ts +0 -223
- package/src/memory/v3/coactivation-store.ts +0 -124
- package/src/memory/v3/consolidation-job.ts +0 -323
- package/src/memory/v3/coretrieval-seed.ts +0 -240
- package/src/memory/v3/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -286
- package/src/memory/v3/filter.ts +0 -286
- package/src/memory/v3/gate.ts +0 -349
- package/src/memory/v3/index-composition.ts +0 -126
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -430
- package/src/memory/v3/maintenance.ts +0 -144
- package/src/memory/v3/prompt-context.ts +0 -33
- package/src/memory/v3/prompts/consolidation.ts +0 -458
- package/src/memory/v3/prompts/system-prompts.ts +0 -196
- package/src/memory/v3/retriever.ts +0 -33
- package/src/memory/v3/scouts.ts +0 -431
- package/src/memory/v3/shadow-diff.ts +0 -287
- package/src/memory/v3/shadow-middleware.ts +0 -347
- package/src/memory/v3/traversal.ts +0 -211
- package/src/memory/v3/tree-index.ts +0 -237
- package/src/memory/v3/tree-store.ts +0 -394
- package/src/memory/v3/tree-walk.ts +0 -356
- package/src/memory/v3/types.ts +0 -65
- package/src/memory/v3/validate.ts +0 -323
- package/src/plugins/defaults/circuit-breaker.ts +0 -141
- package/src/plugins/defaults/compaction.ts +0 -141
- package/src/plugins/defaults/empty-response.ts +0 -124
- package/src/plugins/defaults/history-repair.ts +0 -83
- package/src/plugins/defaults/llm-call.ts +0 -77
- package/src/plugins/defaults/memory-retrieval.ts +0 -219
- package/src/plugins/defaults/overflow-reduce.ts +0 -185
- package/src/plugins/defaults/persistence.ts +0 -146
- package/src/plugins/defaults/title-generate.ts +0 -90
- package/src/plugins/defaults/token-estimate.ts +0 -101
- package/src/plugins/defaults/tool-error.ts +0 -119
- package/src/plugins/defaults/tool-execute.ts +0 -87
- package/src/plugins/defaults/tool-result-truncate.ts +0 -84
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
- package/src/skills/category-inference.ts +0 -111
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
import { getConfig } from "../config/loader.js";
|
|
18
18
|
import { recordEstimate } from "../context/estimator-calibration.js";
|
|
19
19
|
import { getCalibrationProviderKey } from "../context/token-estimator.js";
|
|
20
|
+
import type { ContextWindowResult } from "../context/window-manager.js";
|
|
20
21
|
import { projectAssistantMessage } from "../memory/conversation-attention-store.js";
|
|
21
22
|
import {
|
|
22
23
|
deleteMessageById,
|
|
@@ -24,8 +25,12 @@ import {
|
|
|
24
25
|
getMessageById,
|
|
25
26
|
messageMetadataSchema,
|
|
26
27
|
provenanceFromTrustContext,
|
|
28
|
+
reserveMessage,
|
|
29
|
+
setConversationHistoryStrippedAt,
|
|
30
|
+
setLastNotifiedInferenceProfile,
|
|
27
31
|
updateMessageContent,
|
|
28
32
|
} from "../memory/conversation-crud.js";
|
|
33
|
+
import { syncMessageToDisk } from "../memory/conversation-disk-view.js";
|
|
29
34
|
import { indexMessageNow } from "../memory/indexer.js";
|
|
30
35
|
import {
|
|
31
36
|
backfillMessageIdOnLogs,
|
|
@@ -41,20 +46,24 @@ import {
|
|
|
41
46
|
type SlackMessageMetadata,
|
|
42
47
|
writeSlackMetadata,
|
|
43
48
|
} from "../messaging/providers/slack/message-metadata.js";
|
|
44
|
-
import { defaultPersistenceTerminal } from "../plugins/defaults/persistence.js";
|
|
45
|
-
import { DEFAULT_TIMEOUTS, runPipeline } from "../plugins/pipeline.js";
|
|
46
|
-
import { getMiddlewaresFor } from "../plugins/registry.js";
|
|
47
49
|
import type {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} from "../plugins/types.js";
|
|
53
|
-
import type { ContentBlock, ImageContent } from "../providers/types.js";
|
|
50
|
+
ContentBlock,
|
|
51
|
+
ImageContent,
|
|
52
|
+
Message,
|
|
53
|
+
} from "../providers/types.js";
|
|
54
54
|
import { isContextOverflowError } from "../providers/types.js";
|
|
55
|
+
import {
|
|
56
|
+
getCurrentSeq,
|
|
57
|
+
recordPersistedSeq,
|
|
58
|
+
} from "../runtime/assistant-stream-state.js";
|
|
55
59
|
import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
|
|
56
60
|
import { redactSecrets } from "../security/secret-scanner.js";
|
|
57
61
|
import { extractDomain } from "../tools/network/domain-normalize.js";
|
|
62
|
+
import {
|
|
63
|
+
classifyWebSearchFailure,
|
|
64
|
+
logWebSearchBackendFailure,
|
|
65
|
+
WEB_SEARCH_BACKEND_FAILURE_MESSAGE,
|
|
66
|
+
} from "../tools/network/web-search-error.js";
|
|
58
67
|
import {
|
|
59
68
|
buildPricingUsage,
|
|
60
69
|
resolveStructuredPricing,
|
|
@@ -67,7 +76,10 @@ import {
|
|
|
67
76
|
cleanAssistantContent,
|
|
68
77
|
drainDirectiveDisplayBuffer,
|
|
69
78
|
} from "./assistant-attachments.js";
|
|
70
|
-
import type {
|
|
79
|
+
import type {
|
|
80
|
+
AgentLoopConversationContext,
|
|
81
|
+
AssistantSurface,
|
|
82
|
+
} from "./conversation-agent-loop.js";
|
|
71
83
|
import {
|
|
72
84
|
buildConversationErrorMessage,
|
|
73
85
|
classifyConversationError,
|
|
@@ -84,6 +96,7 @@ import type {
|
|
|
84
96
|
} from "./message-protocol.js";
|
|
85
97
|
import { conversationMetadataSyncTag } from "./message-types/sync.js";
|
|
86
98
|
import type {
|
|
99
|
+
ToolActivityMetadata,
|
|
87
100
|
WebSearchMetadata,
|
|
88
101
|
WebSearchResultItem,
|
|
89
102
|
} from "./message-types/web-activity.js";
|
|
@@ -91,33 +104,28 @@ import type {
|
|
|
91
104
|
const log = getLogger("agent-loop-handlers");
|
|
92
105
|
|
|
93
106
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* `runAgentLoopImpl` invocation. The handlers fire after the orchestrator
|
|
100
|
-
* has completed its in-turn pipeline work but before `ctx.turnCount++` runs
|
|
101
|
-
* in the outer `finally` block, so this value always reflects the turn the
|
|
102
|
-
* handler's event belongs to. Trust pulls from the per-turn snapshot first,
|
|
103
|
-
* then the conversation-level context, then the canonical `unknown`
|
|
104
|
-
* fallback so the required field stays populated for edge cases (fresh
|
|
105
|
-
* conversations before the trust resolver runs, heartbeat turns that never
|
|
106
|
-
* bind an actor).
|
|
107
|
+
* Persist the history-stripped marker after the loop strips runtime injections
|
|
108
|
+
* for compaction / overflow recovery. The marker is a durability hint, not
|
|
109
|
+
* turn-critical state — a transient SQLite write failure (SQLITE_BUSY,
|
|
110
|
+
* disk-full, read-only FS) must not abort the turn, so failures log a warning
|
|
111
|
+
* and continue.
|
|
107
112
|
*/
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
},
|
|
118
|
-
};
|
|
113
|
+
export function markHistoryStrippedBestEffort(conversationId: string): void {
|
|
114
|
+
try {
|
|
115
|
+
setConversationHistoryStrippedAt(conversationId, Date.now());
|
|
116
|
+
} catch (err) {
|
|
117
|
+
log.warn(
|
|
118
|
+
{ err, conversationId },
|
|
119
|
+
"Failed to persist history-stripped marker after compaction strip (non-fatal)",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
119
122
|
}
|
|
120
123
|
|
|
124
|
+
// ── Partial-persistence tunables ─────────────────────────────────────
|
|
125
|
+
// Debounce for mid-turn `updateContent` writes from text deltas.
|
|
126
|
+
// Indexer + projector still fire ONLY at `handleMessageComplete`.
|
|
127
|
+
const PARTIAL_PERSIST_DEBOUNCE_MS = 1000;
|
|
128
|
+
|
|
121
129
|
// ── Types ────────────────────────────────────────────────────────────
|
|
122
130
|
|
|
123
131
|
export interface PendingToolResult {
|
|
@@ -129,6 +137,16 @@ export interface PendingToolResult {
|
|
|
129
137
|
/** Mutable state shared across event handlers within a single agent loop run. */
|
|
130
138
|
export interface EventHandlerState {
|
|
131
139
|
llmCallStartedEmitted: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Profile key whose `model_profile` notice has been assembled into the turn
|
|
142
|
+
* context but not yet marked notified. Set when the turn injects the notice,
|
|
143
|
+
* and consumed the first time the model actually receives that context — i.e.
|
|
144
|
+
* on the first `message_complete`. Persisting on delivery (rather than inline
|
|
145
|
+
* before the provider call) means a cancelled or failed turn re-sends the
|
|
146
|
+
* notice next turn instead of silently marking the profile notified without
|
|
147
|
+
* the model ever having seen it.
|
|
148
|
+
*/
|
|
149
|
+
pendingNotifiedInferenceProfile: string | null;
|
|
132
150
|
pendingDirectiveDisplayBuffer: string;
|
|
133
151
|
firstAssistantText: string;
|
|
134
152
|
/** Most recent resolved provider for the current exchange's usage accounting. */
|
|
@@ -178,6 +196,15 @@ export interface EventHandlerState {
|
|
|
178
196
|
*/
|
|
179
197
|
assistantRowAwaitingFinalization: boolean;
|
|
180
198
|
readonly pendingToolResults: Map<string, PendingToolResult>;
|
|
199
|
+
/**
|
|
200
|
+
* Reservation of the grouped `user` tool-result row for the current batch,
|
|
201
|
+
* resolving to the row id. Shared across the concurrent `handleToolResult`
|
|
202
|
+
* calls of one parallel-tool batch so they reserve exactly one row and write
|
|
203
|
+
* into it as sibling results land. `undefined` until the first result of a
|
|
204
|
+
* batch triggers a reservation (reset on a failed reservation so the next
|
|
205
|
+
* arrival can retry) and again after the batch is finalized.
|
|
206
|
+
*/
|
|
207
|
+
pendingToolResultRowReservation: Promise<string> | undefined;
|
|
181
208
|
readonly persistedToolUseIds: Set<string>;
|
|
182
209
|
readonly accumulatedDirectives: DirectiveRequest[];
|
|
183
210
|
readonly accumulatedToolContentBlocks: ContentBlock[];
|
|
@@ -185,7 +212,6 @@ export interface EventHandlerState {
|
|
|
185
212
|
readonly toolContentBlockToolNames: Map<number, string>;
|
|
186
213
|
readonly directiveWarnings: string[];
|
|
187
214
|
readonly toolUseIdToName: Map<string, string>;
|
|
188
|
-
currentTurnToolNames: string[];
|
|
189
215
|
/** Sticky for the whole run: this turn created/refreshed an app. */
|
|
190
216
|
appBuildToolUsedThisRun: boolean;
|
|
191
217
|
/** Tracks whether the first text delta has been emitted this turn for activity state transitions. */
|
|
@@ -231,6 +257,13 @@ export interface EventHandlerState {
|
|
|
231
257
|
riskDirectoryScopeOptions?: Array<{ scope: string; label: string }>;
|
|
232
258
|
}
|
|
233
259
|
>;
|
|
260
|
+
/**
|
|
261
|
+
* Structured tool activity (web_search / web_fetch) keyed by tool_use_id,
|
|
262
|
+
* captured when a result lands so it can be persisted on the tool's content
|
|
263
|
+
* block and survive a history reopen. Populated for both external provider
|
|
264
|
+
* tools (in handleToolResult) and native server tools (server_tool_complete).
|
|
265
|
+
*/
|
|
266
|
+
readonly toolActivityMetadata: Map<string, ToolActivityMetadata>;
|
|
234
267
|
/** tool_use_ids emitted in the current turn (populated in handleToolUse, cleared after annotation). */
|
|
235
268
|
currentTurnToolUseIds: string[];
|
|
236
269
|
/** Wall-clock time (ms since epoch) when the agent loop turn started, used as the display timestamp for assistant messages. */
|
|
@@ -239,6 +272,38 @@ export interface EventHandlerState {
|
|
|
239
272
|
readonly serverToolStartedAt: Map<string, number>;
|
|
240
273
|
/** Original input from server_tool_start, keyed by tool_use_id, so the complete handler can read the query. */
|
|
241
274
|
readonly serverToolInputs: Map<string, Record<string, unknown>>;
|
|
275
|
+
/** Request ids for which a user-facing web_search backend-failure notice was already surfaced this turn (dedup noisy repeats). Keyed by request id; each turn has a fresh request id, so this grows at most one entry per turn. */
|
|
276
|
+
readonly webSearchBackendFailureNotified: Set<string>;
|
|
277
|
+
/** Active debounce timer for partial persistence; `undefined` when idle. */
|
|
278
|
+
pendingPartialFlushTimer: ReturnType<typeof setTimeout> | undefined;
|
|
279
|
+
/** In-flight partial flush write awaited at finalize to avoid overwrite races. */
|
|
280
|
+
pendingPartialFlushPromise: Promise<void> | undefined;
|
|
281
|
+
/**
|
|
282
|
+
* Running mirror of the in-flight assistant message's streamed content
|
|
283
|
+
* (text and thinking), flushed to the assistant row on the partial-persist
|
|
284
|
+
* debounce so a mid-turn snapshot reflects what the user is watching live.
|
|
285
|
+
*/
|
|
286
|
+
currentMessageContent: ContentBlock[];
|
|
287
|
+
/**
|
|
288
|
+
* `seq` of the most recent streamed content delta mirrored into
|
|
289
|
+
* `currentMessageContent`. Recorded as the conversation's persisted `seq`
|
|
290
|
+
* after each flush commits (the debounced partial flushes and the
|
|
291
|
+
* `message_complete` finalize), so the snapshot's advertised `seq` tracks
|
|
292
|
+
* exactly the streamed content the durable row holds. `undefined` until the
|
|
293
|
+
* first content delta of the in-flight message. Because every streamed
|
|
294
|
+
* content type rides the same mirror-and-flush path, this single field
|
|
295
|
+
* never claims content a flush has not yet written.
|
|
296
|
+
*/
|
|
297
|
+
lastPersistedContentSeq: number | undefined;
|
|
298
|
+
/**
|
|
299
|
+
* Whether the reducer has compacted `ctx.messages`, gating the Slack
|
|
300
|
+
* chronological-transcript override on re-injection. The captured
|
|
301
|
+
* transcript is the full persisted history; blindly replaying it after
|
|
302
|
+
* compaction would overwrite the reduced messages and undo compaction, so
|
|
303
|
+
* once this is `true` the override falls back to the reduced
|
|
304
|
+
* `ctx.messages`.
|
|
305
|
+
*/
|
|
306
|
+
reducerCompacted: boolean;
|
|
242
307
|
}
|
|
243
308
|
|
|
244
309
|
/** Immutable context shared across event handlers within a single agent loop run. */
|
|
@@ -252,6 +317,18 @@ export interface EventHandlerDeps {
|
|
|
252
317
|
readonly rlog: pino.Logger;
|
|
253
318
|
readonly turnChannelContext: TurnChannelContext;
|
|
254
319
|
readonly turnInterfaceContext: TurnInterfaceContext;
|
|
320
|
+
/**
|
|
321
|
+
* Commit a successful inline compaction to durable state. Invoked from the
|
|
322
|
+
* `compaction_completed` dispatch case (when `result.compacted`) with the
|
|
323
|
+
* loop's compaction result and the stripped pre-compaction `basis`. Supplied
|
|
324
|
+
* by the orchestrator because the body writes Conversation DB-record fields,
|
|
325
|
+
* projects Slack provenance, and emits transport the loop is intentionally
|
|
326
|
+
* blind to.
|
|
327
|
+
*/
|
|
328
|
+
readonly applyCompaction: (
|
|
329
|
+
result: ContextWindowResult,
|
|
330
|
+
basis: Message[],
|
|
331
|
+
) => Promise<void>;
|
|
255
332
|
}
|
|
256
333
|
|
|
257
334
|
// ── Factory ──────────────────────────────────────────────────────────
|
|
@@ -259,6 +336,7 @@ export interface EventHandlerDeps {
|
|
|
259
336
|
export function createEventHandlerState(): EventHandlerState {
|
|
260
337
|
return {
|
|
261
338
|
llmCallStartedEmitted: false,
|
|
339
|
+
pendingNotifiedInferenceProfile: null,
|
|
262
340
|
pendingDirectiveDisplayBuffer: "",
|
|
263
341
|
firstAssistantText: "",
|
|
264
342
|
exchangeProviderName: undefined,
|
|
@@ -279,13 +357,13 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
279
357
|
lastAssistantMessageId: undefined,
|
|
280
358
|
assistantRowAwaitingFinalization: false,
|
|
281
359
|
pendingToolResults: new Map(),
|
|
360
|
+
pendingToolResultRowReservation: undefined,
|
|
282
361
|
persistedToolUseIds: new Set(),
|
|
283
362
|
accumulatedDirectives: [],
|
|
284
363
|
accumulatedToolContentBlocks: [],
|
|
285
364
|
toolContentBlockToolNames: new Map(),
|
|
286
365
|
directiveWarnings: [],
|
|
287
366
|
toolUseIdToName: new Map(),
|
|
288
|
-
currentTurnToolNames: [],
|
|
289
367
|
appBuildToolUsedThisRun: false,
|
|
290
368
|
firstTextDeltaEmitted: false,
|
|
291
369
|
firstThinkingDeltaEmitted: false,
|
|
@@ -295,13 +373,167 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
295
373
|
requestIdToToolUseId: new Map(),
|
|
296
374
|
toolConfirmationOutcomes: new Map(),
|
|
297
375
|
toolRiskOutcomes: new Map(),
|
|
376
|
+
toolActivityMetadata: new Map(),
|
|
298
377
|
currentTurnToolUseIds: [],
|
|
299
378
|
turnStartedAt: Date.now(),
|
|
300
379
|
serverToolStartedAt: new Map(),
|
|
301
380
|
serverToolInputs: new Map(),
|
|
381
|
+
webSearchBackendFailureNotified: new Set(),
|
|
382
|
+
pendingPartialFlushTimer: undefined,
|
|
383
|
+
pendingPartialFlushPromise: undefined,
|
|
384
|
+
currentMessageContent: [],
|
|
385
|
+
lastPersistedContentSeq: undefined,
|
|
386
|
+
reducerCompacted: false,
|
|
302
387
|
};
|
|
303
388
|
}
|
|
304
389
|
|
|
390
|
+
// ── Partial-persistence helpers ──────────────────────────────────────
|
|
391
|
+
|
|
392
|
+
/** Canonical persisted-content build: clean → append surfaces → redact. */
|
|
393
|
+
export function buildPersistedAssistantContent(
|
|
394
|
+
rawBlocks: readonly ContentBlock[],
|
|
395
|
+
surfaces: readonly AssistantSurface[],
|
|
396
|
+
activityByToolUseId?: ReadonlyMap<string, ToolActivityMetadata>,
|
|
397
|
+
): ContentBlock[] {
|
|
398
|
+
const { cleanedContent } = cleanAssistantContent(rawBlocks);
|
|
399
|
+
const cleaned = cleanedContent as ContentBlock[];
|
|
400
|
+
const withSurfaces: ContentBlock[] = [...cleaned];
|
|
401
|
+
for (const surface of surfaces) {
|
|
402
|
+
withSurfaces.push({
|
|
403
|
+
type: "ui_surface",
|
|
404
|
+
surfaceId: surface.surfaceId,
|
|
405
|
+
surfaceType: surface.surfaceType,
|
|
406
|
+
title: surface.title,
|
|
407
|
+
data: surface.data,
|
|
408
|
+
actions: surface.actions,
|
|
409
|
+
display: surface.display,
|
|
410
|
+
...(surface.persistent ? { persistent: true } : {}),
|
|
411
|
+
...(surface.toolCallId ? { toolCallId: surface.toolCallId } : {}),
|
|
412
|
+
} as unknown as ContentBlock);
|
|
413
|
+
}
|
|
414
|
+
return withSurfaces.map((block) => {
|
|
415
|
+
if (block.type === "text") {
|
|
416
|
+
const tb = block as Extract<ContentBlock, { type: "text" }>;
|
|
417
|
+
return { ...tb, text: redactSecrets(tb.text) };
|
|
418
|
+
}
|
|
419
|
+
// Native server tools (Anthropic web_search) resolve mid-stream — their
|
|
420
|
+
// `server_tool_complete` fires before `message_complete` — so the captured
|
|
421
|
+
// activity is available at persist time. Stamp it on the server_tool_use
|
|
422
|
+
// block here so the web-search card survives a history reopen. External
|
|
423
|
+
// tool_use activity arrives only with the later tool_result, so it is
|
|
424
|
+
// stamped in `annotatePersistedAssistantMessage` instead.
|
|
425
|
+
if (block.type === "server_tool_use" && activityByToolUseId) {
|
|
426
|
+
const activity = activityByToolUseId.get(block.id);
|
|
427
|
+
if (activity) {
|
|
428
|
+
return { ...block, _activityMetadata: activity } as ContentBlock;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return block;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** Append a streamed text chunk to `state.currentMessageContent`, fusing into tail text block. */
|
|
436
|
+
function appendTextToCurrentMessage(
|
|
437
|
+
state: EventHandlerState,
|
|
438
|
+
text: string,
|
|
439
|
+
): void {
|
|
440
|
+
if (text.length === 0) return;
|
|
441
|
+
const tail = state.currentMessageContent.at(-1);
|
|
442
|
+
if (tail && tail.type === "text") {
|
|
443
|
+
tail.text = tail.text + text;
|
|
444
|
+
} else {
|
|
445
|
+
state.currentMessageContent.push({ type: "text", text });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Append a streamed thinking chunk to `state.currentMessageContent`, fusing
|
|
451
|
+
* into the tail thinking block. The streamed delta carries no provider
|
|
452
|
+
* `signature` (that arrives only when the block closes), so the mirrored block
|
|
453
|
+
* holds an empty one; `message_complete` overwrites the row with the
|
|
454
|
+
* authoritative signed content before it is ever sent back to a provider.
|
|
455
|
+
*/
|
|
456
|
+
function appendThinkingToCurrentMessage(
|
|
457
|
+
state: EventHandlerState,
|
|
458
|
+
thinking: string,
|
|
459
|
+
): void {
|
|
460
|
+
if (thinking.length === 0) return;
|
|
461
|
+
const tail = state.currentMessageContent.at(-1);
|
|
462
|
+
if (tail && tail.type === "thinking") {
|
|
463
|
+
tail.thinking = tail.thinking + thinking;
|
|
464
|
+
} else {
|
|
465
|
+
state.currentMessageContent.push({
|
|
466
|
+
type: "thinking",
|
|
467
|
+
thinking,
|
|
468
|
+
signature: "",
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Reset partial-persist accumulator and any pending flush state. Idempotent. */
|
|
474
|
+
function resetPartialPersistAccumulator(state: EventHandlerState): void {
|
|
475
|
+
if (state.pendingPartialFlushTimer !== undefined) {
|
|
476
|
+
clearTimeout(state.pendingPartialFlushTimer);
|
|
477
|
+
state.pendingPartialFlushTimer = undefined;
|
|
478
|
+
}
|
|
479
|
+
state.currentMessageContent = [];
|
|
480
|
+
state.lastPersistedContentSeq = undefined;
|
|
481
|
+
state.pendingPartialFlushPromise = undefined;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Flush `state.currentMessageContent` to the persisted assistant row. */
|
|
485
|
+
async function flushAccumulatedContent(
|
|
486
|
+
state: EventHandlerState,
|
|
487
|
+
deps: EventHandlerDeps,
|
|
488
|
+
): Promise<void> {
|
|
489
|
+
const messageId = state.lastAssistantMessageId;
|
|
490
|
+
if (messageId === undefined) return;
|
|
491
|
+
if (state.currentMessageContent.length === 0) return;
|
|
492
|
+
|
|
493
|
+
const built = buildPersistedAssistantContent(
|
|
494
|
+
state.currentMessageContent,
|
|
495
|
+
[],
|
|
496
|
+
state.toolActivityMetadata,
|
|
497
|
+
);
|
|
498
|
+
const contentJson = JSON.stringify(built);
|
|
499
|
+
// Pair the seq with the exact content snapshot taken above: deltas that
|
|
500
|
+
// arrive while the write is in flight bump `lastPersistedContentSeq`
|
|
501
|
+
// again, but they are not part of this write.
|
|
502
|
+
const flushedSeq = state.lastPersistedContentSeq;
|
|
503
|
+
|
|
504
|
+
try {
|
|
505
|
+
updateMessageContent(messageId, contentJson);
|
|
506
|
+
// Record only after the write commits, so the snapshot seq never
|
|
507
|
+
// claims content that is not yet durable.
|
|
508
|
+
if (flushedSeq != null) {
|
|
509
|
+
recordPersistedSeq(deps.ctx.conversationId, flushedSeq);
|
|
510
|
+
}
|
|
511
|
+
} catch (err) {
|
|
512
|
+
deps.rlog.warn(
|
|
513
|
+
{ err, messageId },
|
|
514
|
+
"partial flush of accumulated assistant content failed; finalize at message_complete will recover",
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/** Schedule a debounced partial flush. First-scheduled wins; no-op when timer pending. */
|
|
520
|
+
function schedulePartialFlush(
|
|
521
|
+
state: EventHandlerState,
|
|
522
|
+
deps: EventHandlerDeps,
|
|
523
|
+
): void {
|
|
524
|
+
if (state.pendingPartialFlushTimer !== undefined) return;
|
|
525
|
+
state.pendingPartialFlushTimer = setTimeout(() => {
|
|
526
|
+
state.pendingPartialFlushTimer = undefined;
|
|
527
|
+
const flushPromise = flushAccumulatedContent(state, deps);
|
|
528
|
+
state.pendingPartialFlushPromise = flushPromise;
|
|
529
|
+
void flushPromise.finally(() => {
|
|
530
|
+
if (state.pendingPartialFlushPromise === flushPromise) {
|
|
531
|
+
state.pendingPartialFlushPromise = undefined;
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}, PARTIAL_PERSIST_DEBOUNCE_MS);
|
|
535
|
+
}
|
|
536
|
+
|
|
305
537
|
// ── Shared Helper ────────────────────────────────────────────────────
|
|
306
538
|
|
|
307
539
|
// providerNameOverride should be supplied when the caller already knows the
|
|
@@ -511,12 +743,6 @@ export async function handleLlmCallStarted(
|
|
|
511
743
|
// the `assistantRowAwaitingFinalization` flag — `handleMessageComplete`
|
|
512
744
|
// clears it after the successful `updateContent`, so the previous call's
|
|
513
745
|
// committed row is never touched here.
|
|
514
|
-
//
|
|
515
|
-
// Direct `deleteMessageById` (not via the `persistence` pipeline) is
|
|
516
|
-
// intentional: a never-finalized reservation has no segments, no
|
|
517
|
-
// attachments, and no observable history — undoing it isn't a real
|
|
518
|
-
// persistence event for plugins to react to, so routing through the
|
|
519
|
-
// pipeline would only widen the mock surface for no observability win.
|
|
520
746
|
if (state.assistantRowAwaitingFinalization && state.lastAssistantMessageId) {
|
|
521
747
|
try {
|
|
522
748
|
deleteMessageById(state.lastAssistantMessageId);
|
|
@@ -530,24 +756,22 @@ export async function handleLlmCallStarted(
|
|
|
530
756
|
}
|
|
531
757
|
|
|
532
758
|
const metadata = buildAssistantChannelMetadata(state, deps);
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
conversationId: deps.ctx.conversationId,
|
|
540
|
-
role: "assistant",
|
|
541
|
-
metadata,
|
|
542
|
-
},
|
|
543
|
-
buildHandlerTurnContext(deps),
|
|
544
|
-
DEFAULT_TIMEOUTS.persistence,
|
|
545
|
-
)) as PersistReserveResult;
|
|
546
|
-
state.lastAssistantMessageId = reserveResult.message.id;
|
|
759
|
+
const reservedRow = await reserveMessage(
|
|
760
|
+
deps.ctx.conversationId,
|
|
761
|
+
"assistant",
|
|
762
|
+
metadata,
|
|
763
|
+
);
|
|
764
|
+
state.lastAssistantMessageId = reservedRow.id;
|
|
547
765
|
state.assistantRowAwaitingFinalization = true;
|
|
766
|
+
// Fresh row → fresh accumulator. If an earlier (failed) LLM call
|
|
767
|
+
// within the same run left partial state behind, the
|
|
768
|
+
// `assistantRowAwaitingFinalization` cleanup above already deleted
|
|
769
|
+
// the orphan row, so the accumulator content would point at a
|
|
770
|
+
// non-existent id. Reset here so the new row starts from zero.
|
|
771
|
+
resetPartialPersistAccumulator(state);
|
|
548
772
|
deps.onEvent({
|
|
549
773
|
type: "assistant_turn_start",
|
|
550
|
-
messageId:
|
|
774
|
+
messageId: reservedRow.id,
|
|
551
775
|
conversationId: deps.ctx.conversationId,
|
|
552
776
|
});
|
|
553
777
|
}
|
|
@@ -568,13 +792,10 @@ function handleTextDelta(
|
|
|
568
792
|
if (drained.emitText.length > 0) {
|
|
569
793
|
if (!state.firstTextDeltaEmitted) {
|
|
570
794
|
state.firstTextDeltaEmitted = true;
|
|
571
|
-
deps.ctx.emitActivityState(
|
|
572
|
-
|
|
573
|
-
"
|
|
574
|
-
|
|
575
|
-
deps.reqId,
|
|
576
|
-
"Thinking",
|
|
577
|
-
);
|
|
795
|
+
deps.ctx.emitActivityState("streaming", "first_text_delta", {
|
|
796
|
+
requestId: deps.reqId,
|
|
797
|
+
statusText: "Thinking",
|
|
798
|
+
});
|
|
578
799
|
}
|
|
579
800
|
deps.onEvent({
|
|
580
801
|
type: "assistant_text_delta",
|
|
@@ -583,6 +804,15 @@ function handleTextDelta(
|
|
|
583
804
|
messageId: state.lastAssistantMessageId,
|
|
584
805
|
});
|
|
585
806
|
if (deps.shouldGenerateTitle) state.firstAssistantText += drained.emitText;
|
|
807
|
+
// Mirror the drained delta into state.currentMessageContent so partial
|
|
808
|
+
// flushes mid-turn see the same content the user is watching live.
|
|
809
|
+
appendTextToCurrentMessage(state, drained.emitText);
|
|
810
|
+
// The hub stamps `seq` synchronously on the delta emitted above, so
|
|
811
|
+
// `getCurrentSeq()` here is that delta's seq -- the position the
|
|
812
|
+
// mirrored content now reflects. A partial flush snapshots this to
|
|
813
|
+
// record how far the durable rows track the live stream.
|
|
814
|
+
state.lastPersistedContentSeq = getCurrentSeq();
|
|
815
|
+
schedulePartialFlush(state, deps);
|
|
586
816
|
}
|
|
587
817
|
}
|
|
588
818
|
|
|
@@ -603,13 +833,10 @@ function handleThinkingDelta(
|
|
|
603
833
|
// assistantStatusText for every assistant_activity_state event.
|
|
604
834
|
if (lastToolName) {
|
|
605
835
|
const statusText = `Processing ${friendlyToolName(lastToolName)} results`;
|
|
606
|
-
deps.ctx.emitActivityState(
|
|
607
|
-
|
|
608
|
-
"thinking_delta",
|
|
609
|
-
"assistant_turn",
|
|
610
|
-
deps.reqId,
|
|
836
|
+
deps.ctx.emitActivityState("thinking", "thinking_delta", {
|
|
837
|
+
requestId: deps.reqId,
|
|
611
838
|
statusText,
|
|
612
|
-
);
|
|
839
|
+
});
|
|
613
840
|
}
|
|
614
841
|
}
|
|
615
842
|
if (!deps.ctx.streamThinking) return;
|
|
@@ -620,6 +847,14 @@ function handleThinkingDelta(
|
|
|
620
847
|
conversationId: deps.ctx.conversationId,
|
|
621
848
|
messageId: state.lastAssistantMessageId,
|
|
622
849
|
});
|
|
850
|
+
// Mirror thinking into the same running view as text so the debounced
|
|
851
|
+
// partial flush persists it mid-turn -- long reasoning streams survive a
|
|
852
|
+
// refresh that outlives the SSE replay window, exactly as long answers do.
|
|
853
|
+
appendThinkingToCurrentMessage(state, event.thinking);
|
|
854
|
+
// The hub stamps `seq` synchronously on the delta emitted above, so
|
|
855
|
+
// `getCurrentSeq()` is that delta's position in the mirrored content.
|
|
856
|
+
state.lastPersistedContentSeq = getCurrentSeq();
|
|
857
|
+
schedulePartialFlush(state, deps);
|
|
623
858
|
}
|
|
624
859
|
|
|
625
860
|
export function handleToolUse(
|
|
@@ -628,7 +863,6 @@ export function handleToolUse(
|
|
|
628
863
|
event: Extract<AgentEvent, { type: "tool_use" }>,
|
|
629
864
|
): void {
|
|
630
865
|
state.toolUseIdToName.set(event.id, event.name);
|
|
631
|
-
state.currentTurnToolNames.push(event.name);
|
|
632
866
|
if (event.name === "app_create" || event.name === "app_refresh") {
|
|
633
867
|
state.appBuildToolUsedThisRun = true;
|
|
634
868
|
}
|
|
@@ -636,13 +870,10 @@ export function handleToolUse(
|
|
|
636
870
|
state.currentToolUseId = event.id;
|
|
637
871
|
state.currentTurnToolUseIds.push(event.id);
|
|
638
872
|
const statusText = computeToolUseStatusText(event.name, event.input);
|
|
639
|
-
deps.ctx.emitActivityState(
|
|
640
|
-
|
|
641
|
-
"tool_use_start",
|
|
642
|
-
"assistant_turn",
|
|
643
|
-
deps.reqId,
|
|
873
|
+
deps.ctx.emitActivityState("tool_running", "tool_use_start", {
|
|
874
|
+
requestId: deps.reqId,
|
|
644
875
|
statusText,
|
|
645
|
-
);
|
|
876
|
+
});
|
|
646
877
|
deps.onEvent({
|
|
647
878
|
type: "tool_use_start",
|
|
648
879
|
toolName: event.name,
|
|
@@ -651,6 +882,14 @@ export function handleToolUse(
|
|
|
651
882
|
toolUseId: event.id,
|
|
652
883
|
messageId: state.lastAssistantMessageId,
|
|
653
884
|
});
|
|
885
|
+
// `message_complete` always precedes tool events (see handleMessageComplete),
|
|
886
|
+
// so this tool_use block is already durable in the assistant row. The
|
|
887
|
+
// `tool_use_start` emitted just above is therefore the newest stamped event
|
|
888
|
+
// whose content the `/messages` snapshot already reflects -- advance the
|
|
889
|
+
// persisted seq to it. Without this the snapshot would advertise a seq below
|
|
890
|
+
// an event it already incorporates, and a client applying `seq > snapshot.seq`
|
|
891
|
+
// would replay this tool start.
|
|
892
|
+
recordPersistedSeq(deps.ctx.conversationId, getCurrentSeq());
|
|
654
893
|
}
|
|
655
894
|
|
|
656
895
|
export function handleToolUsePreviewStart(
|
|
@@ -666,13 +905,10 @@ export function handleToolUsePreviewStart(
|
|
|
666
905
|
messageId: state.lastAssistantMessageId,
|
|
667
906
|
});
|
|
668
907
|
const statusText = `Preparing ${friendlyToolName(event.toolName)}...`;
|
|
669
|
-
deps.ctx.emitActivityState(
|
|
670
|
-
|
|
671
|
-
"preview_start",
|
|
672
|
-
"assistant_turn",
|
|
673
|
-
deps.reqId,
|
|
908
|
+
deps.ctx.emitActivityState("tool_running", "preview_start", {
|
|
909
|
+
requestId: deps.reqId,
|
|
674
910
|
statusText,
|
|
675
|
-
);
|
|
911
|
+
});
|
|
676
912
|
}
|
|
677
913
|
|
|
678
914
|
function handleToolOutputChunk(
|
|
@@ -771,11 +1007,244 @@ export function handleInputJsonDelta(
|
|
|
771
1007
|
});
|
|
772
1008
|
}
|
|
773
1009
|
|
|
774
|
-
|
|
1010
|
+
/**
|
|
1011
|
+
* Build the persisted `tool_result` content blocks for the buffered results,
|
|
1012
|
+
* redacting secrets from both the flat content and any structured blocks. All
|
|
1013
|
+
* results of one assistant turn share a single `user` row (the shape providers
|
|
1014
|
+
* expect for tool_result-in-user-turn).
|
|
1015
|
+
*/
|
|
1016
|
+
function buildToolResultBlocks(
|
|
1017
|
+
pending: ReadonlyMap<string, PendingToolResult>,
|
|
1018
|
+
) {
|
|
1019
|
+
return Array.from(pending.entries()).map(([toolUseId, result]) => ({
|
|
1020
|
+
type: "tool_result",
|
|
1021
|
+
tool_use_id: toolUseId,
|
|
1022
|
+
content: redactSecrets(result.content),
|
|
1023
|
+
is_error: result.isError,
|
|
1024
|
+
...(result.contentBlocks
|
|
1025
|
+
? {
|
|
1026
|
+
contentBlocks: result.contentBlocks.map((block) =>
|
|
1027
|
+
block.type === "text"
|
|
1028
|
+
? { ...block, text: redactSecrets(block.text) }
|
|
1029
|
+
: block,
|
|
1030
|
+
),
|
|
1031
|
+
}
|
|
1032
|
+
: {}),
|
|
1033
|
+
}));
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Channel/interface provenance metadata for the grouped tool-result row,
|
|
1038
|
+
* stamped from the turn context so the row carries the same provenance the
|
|
1039
|
+
* snapshot reflects from the moment it lands in SQLite.
|
|
1040
|
+
*/
|
|
1041
|
+
function buildToolResultMetadata(
|
|
1042
|
+
deps: EventHandlerDeps,
|
|
1043
|
+
): Record<string, unknown> {
|
|
1044
|
+
return {
|
|
1045
|
+
...provenanceFromTrustContext(deps.ctx.trustContext),
|
|
1046
|
+
userMessageChannel: deps.turnChannelContext.userMessageChannel,
|
|
1047
|
+
assistantMessageChannel: deps.turnChannelContext.assistantMessageChannel,
|
|
1048
|
+
userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
|
|
1049
|
+
assistantMessageInterface:
|
|
1050
|
+
deps.turnInterfaceContext.assistantMessageInterface,
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Reserve the grouped `user` tool-result row for the current batch exactly
|
|
1056
|
+
* once. Parallel tool results are dispatched without awaiting (`agent/loop.ts`
|
|
1057
|
+
* emits each `tool_result` synchronously), so concurrent `handleToolResult`
|
|
1058
|
+
* calls can reach this before the first reservation resolves; sharing one
|
|
1059
|
+
* in-flight reservation promise keeps the whole batch in a single row. A
|
|
1060
|
+
* failed reservation resets the promise so the next caller can retry rather
|
|
1061
|
+
* than inheriting a settled rejection.
|
|
1062
|
+
*/
|
|
1063
|
+
function ensureToolResultRowReserved(
|
|
1064
|
+
state: EventHandlerState,
|
|
1065
|
+
conversationId: string,
|
|
1066
|
+
metadata: Record<string, unknown>,
|
|
1067
|
+
): Promise<string> {
|
|
1068
|
+
if (state.pendingToolResultRowReservation === undefined) {
|
|
1069
|
+
state.pendingToolResultRowReservation = reserveMessage(
|
|
1070
|
+
conversationId,
|
|
1071
|
+
"user",
|
|
1072
|
+
metadata,
|
|
1073
|
+
)
|
|
1074
|
+
.then((reserved) => reserved.id)
|
|
1075
|
+
.catch((err) => {
|
|
1076
|
+
state.pendingToolResultRowReservation = undefined;
|
|
1077
|
+
throw err;
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
return state.pendingToolResultRowReservation;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Persist the buffered tool results into their grouped `user` row as each
|
|
1085
|
+
* result arrives, so a long-running tool's output survives a refresh that
|
|
1086
|
+
* outlives the SSE replay window. The row is reserved once per batch and
|
|
1087
|
+
* rewritten in place as sibling parallel results land, keeping all
|
|
1088
|
+
* `tool_result` blocks of one turn in a single message. `seq` is the position
|
|
1089
|
+
* stamped on the triggering `tool_result` event, captured by the caller before
|
|
1090
|
+
* any await so it reflects exactly the content now durable in the row.
|
|
1091
|
+
* Indexing and the buffer drain are deferred to `finalizePendingToolResultRow`.
|
|
1092
|
+
*/
|
|
1093
|
+
async function persistPendingToolResultRow(
|
|
1094
|
+
state: EventHandlerState,
|
|
1095
|
+
deps: EventHandlerDeps,
|
|
1096
|
+
seq: number,
|
|
1097
|
+
): Promise<void> {
|
|
1098
|
+
if (state.pendingToolResults.size === 0) return;
|
|
1099
|
+
const rowId = await ensureToolResultRowReserved(
|
|
1100
|
+
state,
|
|
1101
|
+
deps.ctx.conversationId,
|
|
1102
|
+
buildToolResultMetadata(deps),
|
|
1103
|
+
);
|
|
1104
|
+
// Serialize the content after the reservation resolves so the last of the
|
|
1105
|
+
// concurrent writers reflects the fullest batch.
|
|
1106
|
+
updateMessageContent(
|
|
1107
|
+
rowId,
|
|
1108
|
+
JSON.stringify(buildToolResultBlocks(state.pendingToolResults)),
|
|
1109
|
+
);
|
|
1110
|
+
recordPersistedSeq(deps.ctx.conversationId, seq);
|
|
1111
|
+
const conv = getConversation(deps.ctx.conversationId);
|
|
1112
|
+
if (conv != null) {
|
|
1113
|
+
syncMessageToDisk(deps.ctx.conversationId, rowId, conv.createdAt);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Finalize the grouped tool-result row at a turn/loop boundary: ensure the row
|
|
1119
|
+
* is reserved (a fallback for the case where every on-arrival write failed),
|
|
1120
|
+
* rewrite it to the full batch, sync it to disk, index it for memory recall,
|
|
1121
|
+
* and clear the batch state. Shared by `message_complete` and the orchestrator
|
|
1122
|
+
* loop-exit flush so an aborted or yielded turn finalizes the same reserved row
|
|
1123
|
+
* instead of writing a duplicate.
|
|
1124
|
+
*/
|
|
1125
|
+
export async function finalizePendingToolResultRow(
|
|
1126
|
+
state: EventHandlerState,
|
|
1127
|
+
conversationId: string,
|
|
1128
|
+
metadata: Record<string, unknown>,
|
|
1129
|
+
rlog: pino.Logger,
|
|
1130
|
+
): Promise<void> {
|
|
1131
|
+
if (state.pendingToolResults.size === 0) return;
|
|
1132
|
+
const rowId = await ensureToolResultRowReserved(
|
|
1133
|
+
state,
|
|
1134
|
+
conversationId,
|
|
1135
|
+
metadata,
|
|
1136
|
+
);
|
|
1137
|
+
const contentJson = JSON.stringify(
|
|
1138
|
+
buildToolResultBlocks(state.pendingToolResults),
|
|
1139
|
+
);
|
|
1140
|
+
updateMessageContent(rowId, contentJson);
|
|
1141
|
+
// Sync the row to the JSONL disk view so it stays in lockstep with the DB.
|
|
1142
|
+
// `getConversation` returns `ConversationRow | null`, so `!= null` gates on a
|
|
1143
|
+
// real row (skipping the sync when the conversation was not found rather than
|
|
1144
|
+
// asking the disk-view to resolve a missing id).
|
|
1145
|
+
const conv = getConversation(conversationId);
|
|
1146
|
+
if (conv != null) {
|
|
1147
|
+
syncMessageToDisk(conversationId, rowId, conv.createdAt);
|
|
1148
|
+
}
|
|
1149
|
+
// `reserveMessage` + `updateMessageContent` are CRUD-only, so index the
|
|
1150
|
+
// finalized tool-result content explicitly here (mirroring the assistant-row
|
|
1151
|
+
// finalize) once it is durable. Non-fatal: a memory hiccup must not escalate
|
|
1152
|
+
// a successful turn into a throw.
|
|
1153
|
+
const row = getMessageById(rowId, conversationId);
|
|
1154
|
+
if (row) {
|
|
1155
|
+
let provenanceTrustClass:
|
|
1156
|
+
| "guardian"
|
|
1157
|
+
| "trusted_contact"
|
|
1158
|
+
| "unknown"
|
|
1159
|
+
| undefined;
|
|
1160
|
+
let automated: boolean | undefined;
|
|
1161
|
+
if (row.metadata) {
|
|
1162
|
+
try {
|
|
1163
|
+
const parsedMeta = messageMetadataSchema.safeParse(
|
|
1164
|
+
JSON.parse(row.metadata),
|
|
1165
|
+
);
|
|
1166
|
+
if (parsedMeta.success) {
|
|
1167
|
+
provenanceTrustClass = parsedMeta.data.provenanceTrustClass;
|
|
1168
|
+
automated = parsedMeta.data.automated;
|
|
1169
|
+
}
|
|
1170
|
+
} catch {
|
|
1171
|
+
// Malformed metadata JSON — index with undefined provenance fields.
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
try {
|
|
1175
|
+
await indexMessageNow(
|
|
1176
|
+
{
|
|
1177
|
+
messageId: rowId,
|
|
1178
|
+
conversationId,
|
|
1179
|
+
role: "user",
|
|
1180
|
+
content: contentJson,
|
|
1181
|
+
createdAt: row.createdAt,
|
|
1182
|
+
scopeId: "default",
|
|
1183
|
+
provenanceTrustClass,
|
|
1184
|
+
automated,
|
|
1185
|
+
},
|
|
1186
|
+
getConfig().memory,
|
|
1187
|
+
);
|
|
1188
|
+
} catch (err) {
|
|
1189
|
+
rlog.warn(
|
|
1190
|
+
{ err, conversationId, messageId: rowId },
|
|
1191
|
+
"Failed to index tool-result message for memory (non-fatal)",
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
for (const id of state.pendingToolResults.keys()) {
|
|
1196
|
+
state.persistedToolUseIds.add(id);
|
|
1197
|
+
}
|
|
1198
|
+
state.pendingToolResults.clear();
|
|
1199
|
+
state.pendingToolResultRowReservation = undefined;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
export async function handleToolResult(
|
|
775
1203
|
state: EventHandlerState,
|
|
776
1204
|
deps: EventHandlerDeps,
|
|
777
1205
|
event: Extract<AgentEvent, { type: "tool_result" }>,
|
|
778
|
-
): void {
|
|
1206
|
+
): Promise<void> {
|
|
1207
|
+
// A synthesized cancellation (the tool never executed) is captured for
|
|
1208
|
+
// persistence and forwarded to the client like any result, but skips every
|
|
1209
|
+
// side effect that assumes the tool ran. A real result already captured or
|
|
1210
|
+
// persisted for the same tool wins, so only fill genuine gaps.
|
|
1211
|
+
if (event.cancelled) {
|
|
1212
|
+
if (
|
|
1213
|
+
state.pendingToolResults.has(event.toolUseId) ||
|
|
1214
|
+
state.persistedToolUseIds.has(event.toolUseId)
|
|
1215
|
+
) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
state.pendingToolResults.set(event.toolUseId, {
|
|
1219
|
+
content: event.content,
|
|
1220
|
+
isError: event.isError,
|
|
1221
|
+
});
|
|
1222
|
+
state.currentToolUseId = undefined;
|
|
1223
|
+
deps.onEvent({
|
|
1224
|
+
type: "tool_result",
|
|
1225
|
+
toolName: "",
|
|
1226
|
+
result: event.content,
|
|
1227
|
+
isError: event.isError,
|
|
1228
|
+
conversationId: deps.ctx.conversationId,
|
|
1229
|
+
messageId: state.lastAssistantMessageId,
|
|
1230
|
+
toolUseId: event.toolUseId,
|
|
1231
|
+
});
|
|
1232
|
+
// Capture the seq synchronously (before the persist await) so it reflects
|
|
1233
|
+
// the just-stamped tool_result event, then persist on arrival. A failure
|
|
1234
|
+
// here is non-fatal: the buffered result is still drained at
|
|
1235
|
+
// `message_complete`.
|
|
1236
|
+
const cancelledSeq = getCurrentSeq();
|
|
1237
|
+
try {
|
|
1238
|
+
await persistPendingToolResultRow(state, deps, cancelledSeq);
|
|
1239
|
+
} catch (err) {
|
|
1240
|
+
log.warn(
|
|
1241
|
+
{ err, conversationId: deps.ctx.conversationId },
|
|
1242
|
+
"Failed to persist cancelled tool result on arrival (non-fatal; retried at message_complete)",
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
779
1248
|
const imageBlocks = event.contentBlocks?.filter(
|
|
780
1249
|
(b): b is ImageContent => b.type === "image",
|
|
781
1250
|
);
|
|
@@ -826,6 +1295,13 @@ export function handleToolResult(
|
|
|
826
1295
|
});
|
|
827
1296
|
}
|
|
828
1297
|
|
|
1298
|
+
// Capture tool activity (web_search / web_fetch) so it can be persisted on
|
|
1299
|
+
// the tool_use block and the activity card survives a history reopen,
|
|
1300
|
+
// matching the live tool_result event's activityMetadata.
|
|
1301
|
+
if (event.activityMetadata) {
|
|
1302
|
+
state.toolActivityMetadata.set(event.toolUseId, event.activityMetadata);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
829
1305
|
const toolName = state.toolUseIdToName.get(event.toolUseId);
|
|
830
1306
|
if (toolName === "file_write" || toolName === "bash") {
|
|
831
1307
|
deps.ctx.markWorkspaceTopLevelDirty();
|
|
@@ -860,13 +1336,10 @@ export function handleToolResult(
|
|
|
860
1336
|
const statusText = `Processing ${friendlyToolName(
|
|
861
1337
|
state.lastCompletedToolName ?? "",
|
|
862
1338
|
)} results`;
|
|
863
|
-
deps.ctx.emitActivityState(
|
|
864
|
-
|
|
865
|
-
"tool_result_received",
|
|
866
|
-
"assistant_turn",
|
|
867
|
-
deps.reqId,
|
|
1339
|
+
deps.ctx.emitActivityState("thinking", "tool_result_received", {
|
|
1340
|
+
requestId: deps.reqId,
|
|
868
1341
|
statusText,
|
|
869
|
-
);
|
|
1342
|
+
});
|
|
870
1343
|
|
|
871
1344
|
// Once all tools for this turn have completed, annotate the persisted
|
|
872
1345
|
// assistant message with timing and confirmation metadata.
|
|
@@ -910,6 +1383,20 @@ export function handleToolResult(
|
|
|
910
1383
|
riskThreshold: event.riskThreshold,
|
|
911
1384
|
activityMetadata: event.activityMetadata,
|
|
912
1385
|
});
|
|
1386
|
+
|
|
1387
|
+
// Capture the seq synchronously (before the persist await) so it reflects the
|
|
1388
|
+
// just-stamped tool_result event, then persist the grouped row on arrival. A
|
|
1389
|
+
// failure here is non-fatal: the buffered result is still drained at
|
|
1390
|
+
// `message_complete`.
|
|
1391
|
+
const resultSeq = getCurrentSeq();
|
|
1392
|
+
try {
|
|
1393
|
+
await persistPendingToolResultRow(state, deps, resultSeq);
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
log.warn(
|
|
1396
|
+
{ err, conversationId: deps.ctx.conversationId },
|
|
1397
|
+
"Failed to persist tool result on arrival (non-fatal; retried at message_complete)",
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
913
1400
|
}
|
|
914
1401
|
|
|
915
1402
|
/**
|
|
@@ -981,6 +1468,16 @@ function annotatePersistedAssistantMessage(
|
|
|
981
1468
|
rec._riskDirectoryScopeOptions = risk.riskDirectoryScopeOptions;
|
|
982
1469
|
modified = true;
|
|
983
1470
|
}
|
|
1471
|
+
// External provider tools (brave/perplexity/tavily) + web_fetch produce
|
|
1472
|
+
// their activity only when the tool_result lands, after message_complete
|
|
1473
|
+
// has already persisted this block — so it is stamped here. Native
|
|
1474
|
+
// server_tool_use activity is stamped earlier, at persist time, in
|
|
1475
|
+
// `buildPersistedAssistantContent`.
|
|
1476
|
+
const activity = state.toolActivityMetadata.get(id);
|
|
1477
|
+
if (activity) {
|
|
1478
|
+
rec._activityMetadata = activity;
|
|
1479
|
+
modified = true;
|
|
1480
|
+
}
|
|
984
1481
|
}
|
|
985
1482
|
}
|
|
986
1483
|
|
|
@@ -998,6 +1495,7 @@ function annotatePersistedAssistantMessage(
|
|
|
998
1495
|
actions: surface.actions,
|
|
999
1496
|
display: surface.display,
|
|
1000
1497
|
...(surface.persistent ? { persistent: true } : {}),
|
|
1498
|
+
...(surface.toolCallId ? { toolCallId: surface.toolCallId } : {}),
|
|
1001
1499
|
} as unknown as ContentBlock);
|
|
1002
1500
|
}
|
|
1003
1501
|
modified = true;
|
|
@@ -1142,9 +1640,44 @@ export async function handleMessageComplete(
|
|
|
1142
1640
|
deps: EventHandlerDeps,
|
|
1143
1641
|
event: Extract<AgentEvent, { type: "message_complete" }>,
|
|
1144
1642
|
): Promise<void> {
|
|
1643
|
+
// The model has now received the turn context, so persist any pending
|
|
1644
|
+
// inference-profile-change notification. Guarded by the pending slot so it
|
|
1645
|
+
// fires once per turn; a turn that fails before reaching delivery leaves the
|
|
1646
|
+
// slot unconsumed and re-sends the notice next turn.
|
|
1647
|
+
if (state.pendingNotifiedInferenceProfile != null) {
|
|
1648
|
+
setLastNotifiedInferenceProfile(
|
|
1649
|
+
deps.ctx.conversationId,
|
|
1650
|
+
state.pendingNotifiedInferenceProfile,
|
|
1651
|
+
);
|
|
1652
|
+
state.pendingNotifiedInferenceProfile = null;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1145
1655
|
// Reset per-turn tool tracking for the new turn.
|
|
1146
1656
|
state.currentTurnToolUseIds = [];
|
|
1147
1657
|
|
|
1658
|
+
// Cancel any pending debounced partial flush and await an already
|
|
1659
|
+
// in-flight one before the authoritative `updateContent` below.
|
|
1660
|
+
// Without the timer-clear, a timer that fires during this handler
|
|
1661
|
+
// could double-write (idempotent in content but wastes a write) or
|
|
1662
|
+
// race ahead of the indexer/projector and serve a stale snapshot.
|
|
1663
|
+
// Without the await, a partial pipeline call that was dispatched a
|
|
1664
|
+
// moment before this handler can settle AFTER the final write and
|
|
1665
|
+
// overwrite the authoritative row.
|
|
1666
|
+
if (state.pendingPartialFlushTimer !== undefined) {
|
|
1667
|
+
clearTimeout(state.pendingPartialFlushTimer);
|
|
1668
|
+
state.pendingPartialFlushTimer = undefined;
|
|
1669
|
+
}
|
|
1670
|
+
if (state.pendingPartialFlushPromise !== undefined) {
|
|
1671
|
+
try {
|
|
1672
|
+
await state.pendingPartialFlushPromise;
|
|
1673
|
+
} catch {
|
|
1674
|
+
// The partial flush swallows its own pipeline errors via
|
|
1675
|
+
// `rlog.warn`; the `try`/`catch` here is defensive against
|
|
1676
|
+
// future changes that might surface them.
|
|
1677
|
+
}
|
|
1678
|
+
state.pendingPartialFlushPromise = undefined;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1148
1681
|
// Flush any remaining directive display buffer
|
|
1149
1682
|
if (state.pendingDirectiveDisplayBuffer.length > 0) {
|
|
1150
1683
|
deps.onEvent({
|
|
@@ -1158,70 +1691,25 @@ export async function handleMessageComplete(
|
|
|
1158
1691
|
state.pendingDirectiveDisplayBuffer = "";
|
|
1159
1692
|
}
|
|
1160
1693
|
|
|
1161
|
-
//
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
contentBlocks: result.contentBlocks.map((block) =>
|
|
1172
|
-
block.type === "text"
|
|
1173
|
-
? { ...block, text: redactSecrets(block.text) }
|
|
1174
|
-
: block,
|
|
1175
|
-
),
|
|
1176
|
-
}
|
|
1177
|
-
: {}),
|
|
1178
|
-
}),
|
|
1179
|
-
);
|
|
1180
|
-
const toolResultMetadata = {
|
|
1181
|
-
...provenanceFromTrustContext(deps.ctx.trustContext),
|
|
1182
|
-
userMessageChannel: deps.turnChannelContext.userMessageChannel,
|
|
1183
|
-
assistantMessageChannel: deps.turnChannelContext.assistantMessageChannel,
|
|
1184
|
-
userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
|
|
1185
|
-
assistantMessageInterface:
|
|
1186
|
-
deps.turnInterfaceContext.assistantMessageInterface,
|
|
1187
|
-
};
|
|
1188
|
-
// Route the add + disk-view sync through the `persistence` pipeline so
|
|
1189
|
-
// plugins can observe or override both operations together. The default
|
|
1190
|
-
// plugin's terminal performs the add and, when `syncToDisk` is true,
|
|
1191
|
-
// immediately calls `syncMessageToDisk` against the just-persisted row.
|
|
1192
|
-
// `getConversation` returns `ConversationRow | null`, so `!= null`
|
|
1193
|
-
// gates on a real row (skipping the sync when the conversation was
|
|
1194
|
-
// not found rather than asking the disk-view to resolve a missing id).
|
|
1195
|
-
const convForToolResult = getConversation(deps.ctx.conversationId);
|
|
1196
|
-
await runPipeline<PersistArgs, PersistResult>(
|
|
1197
|
-
"persistence",
|
|
1198
|
-
getMiddlewaresFor("persistence"),
|
|
1199
|
-
defaultPersistenceTerminal,
|
|
1200
|
-
{
|
|
1201
|
-
op: "add",
|
|
1202
|
-
conversationId: deps.ctx.conversationId,
|
|
1203
|
-
role: "user",
|
|
1204
|
-
content: JSON.stringify(toolResultBlocks),
|
|
1205
|
-
metadata: toolResultMetadata,
|
|
1206
|
-
syncToDisk: convForToolResult != null,
|
|
1207
|
-
createdAtMs: convForToolResult?.createdAt,
|
|
1208
|
-
},
|
|
1209
|
-
buildHandlerTurnContext(deps),
|
|
1210
|
-
DEFAULT_TIMEOUTS.persistence,
|
|
1211
|
-
);
|
|
1212
|
-
for (const id of state.pendingToolResults.keys()) {
|
|
1213
|
-
state.persistedToolUseIds.add(id);
|
|
1214
|
-
}
|
|
1215
|
-
state.pendingToolResults.clear();
|
|
1216
|
-
}
|
|
1694
|
+
// Finalize the grouped tool-result row. Each result was persisted into this
|
|
1695
|
+
// row as it arrived (`persistPendingToolResultRow`); this rewrites it to the
|
|
1696
|
+
// full batch (covering the case where a mid-arrival write failed), indexes it
|
|
1697
|
+
// for memory recall, and clears the batch state.
|
|
1698
|
+
await finalizePendingToolResultRow(
|
|
1699
|
+
state,
|
|
1700
|
+
deps.ctx.conversationId,
|
|
1701
|
+
buildToolResultMetadata(deps),
|
|
1702
|
+
deps.rlog,
|
|
1703
|
+
);
|
|
1217
1704
|
|
|
1218
|
-
//
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
const
|
|
1705
|
+
// Accumulate directives + warnings from the assistant content for
|
|
1706
|
+
// downstream attachment processing. `cleanAssistantContent` is also
|
|
1707
|
+
// called inside {@link buildPersistedAssistantContent} below; running
|
|
1708
|
+
// it here separately is the cheapest way to keep the directive
|
|
1709
|
+
// side-effects local to this handler while letting the shared helper
|
|
1710
|
+
// own the persisted-content shape.
|
|
1711
|
+
const { directives: msgDirectives, warnings: msgWarnings } =
|
|
1712
|
+
cleanAssistantContent(event.message.content);
|
|
1225
1713
|
state.accumulatedDirectives.push(...msgDirectives);
|
|
1226
1714
|
state.directiveWarnings.push(...msgWarnings);
|
|
1227
1715
|
if (msgDirectives.length > 0) {
|
|
@@ -1243,31 +1731,15 @@ export async function handleMessageComplete(
|
|
|
1243
1731
|
// are applied in handleToolResult after all tools for the turn complete,
|
|
1244
1732
|
// then the persisted message is updated via updateMessageContent.
|
|
1245
1733
|
|
|
1246
|
-
// Build content
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
actions: surface.actions,
|
|
1256
|
-
display: surface.display,
|
|
1257
|
-
...(surface.persistent ? { persistent: true } : {}),
|
|
1258
|
-
} as unknown as ContentBlock);
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
// Redact known-pattern secrets from assistant text blocks before they are
|
|
1262
|
-
// written to durable storage. Non-text blocks (images, UI surfaces) pass
|
|
1263
|
-
// through unchanged. The live model history retains the original values.
|
|
1264
|
-
const contentForPersistence = contentWithSurfaces.map((block) => {
|
|
1265
|
-
if (block.type === "text") {
|
|
1266
|
-
const tb = block as Extract<ContentBlock, { type: "text" }>;
|
|
1267
|
-
return { ...tb, text: redactSecrets(tb.text) };
|
|
1268
|
-
}
|
|
1269
|
-
return block;
|
|
1270
|
-
});
|
|
1734
|
+
// Build the canonical persisted content (cleaned + surfaces +
|
|
1735
|
+
// redacted) via the shared helper. The partial-persist flush uses
|
|
1736
|
+
// the same helper with `surfaces=[]` so a mid-turn snapshot lands in
|
|
1737
|
+
// the same shape as the finalize.
|
|
1738
|
+
const contentForPersistence = buildPersistedAssistantContent(
|
|
1739
|
+
event.message.content as ContentBlock[],
|
|
1740
|
+
deps.ctx.currentTurnSurfaces,
|
|
1741
|
+
state.toolActivityMetadata,
|
|
1742
|
+
);
|
|
1271
1743
|
|
|
1272
1744
|
// The row was reserved at `llm_call_started` (with channel metadata
|
|
1273
1745
|
// stamped at that point) and `state.lastAssistantMessageId` carries its
|
|
@@ -1283,25 +1755,30 @@ export async function handleMessageComplete(
|
|
|
1283
1755
|
);
|
|
1284
1756
|
}
|
|
1285
1757
|
const contentJson = JSON.stringify(contentForPersistence);
|
|
1286
|
-
|
|
1287
|
-
"persistence",
|
|
1288
|
-
getMiddlewaresFor("persistence"),
|
|
1289
|
-
defaultPersistenceTerminal,
|
|
1290
|
-
{
|
|
1291
|
-
op: "updateContent",
|
|
1292
|
-
messageId: assistantMessageId,
|
|
1293
|
-
content: contentJson,
|
|
1294
|
-
},
|
|
1295
|
-
buildHandlerTurnContext(deps),
|
|
1296
|
-
DEFAULT_TIMEOUTS.persistence,
|
|
1297
|
-
);
|
|
1758
|
+
updateMessageContent(assistantMessageId, contentJson);
|
|
1298
1759
|
state.assistantRowAwaitingFinalization = false;
|
|
1760
|
+
// The assistant row now holds the authoritative content (text + thinking +
|
|
1761
|
+
// tool_use blocks from `event.message`), and any drained tool-result rows
|
|
1762
|
+
// are durable. `lastPersistedContentSeq` is the last streamed text/thinking
|
|
1763
|
+
// delta's seq -- the highest stamped content event this row reflects -- so
|
|
1764
|
+
// recording it is honest. A drained tool result was stamped earlier in the
|
|
1765
|
+
// turn, so this seq already covers it; a call that streams no content (a
|
|
1766
|
+
// pure tool call) advances instead via `tool_use_start`. `recordPersistedSeq`
|
|
1767
|
+
// clamps monotonically, so a lower value here never regresses the seq.
|
|
1768
|
+
if (state.lastPersistedContentSeq != null) {
|
|
1769
|
+
recordPersistedSeq(deps.ctx.conversationId, state.lastPersistedContentSeq);
|
|
1770
|
+
}
|
|
1771
|
+
// Reset the partial-persist mirror so subsequent calls in this turn
|
|
1772
|
+
// start with an empty running view.
|
|
1773
|
+
state.currentMessageContent = [];
|
|
1774
|
+
state.lastPersistedContentSeq = undefined;
|
|
1299
1775
|
|
|
1300
|
-
// ── Indexing + attention projection
|
|
1776
|
+
// ── Indexing + attention projection ──
|
|
1301
1777
|
// `reserveMessage` + `updateMessageContent` are CRUD-only: they don't run
|
|
1302
|
-
// the memory indexer or the attention-cursor projector
|
|
1303
|
-
//
|
|
1304
|
-
//
|
|
1778
|
+
// the memory indexer or the attention-cursor projector (unlike `addMessage`,
|
|
1779
|
+
// which runs both as side-effects of the insert). Because the assistant row
|
|
1780
|
+
// is reserved empty and finalized via `updateMessageContent`, both must be
|
|
1781
|
+
// invoked explicitly here to keep the assistant row's external state
|
|
1305
1782
|
// (Qdrant segments, conversation attention cursor) in lockstep with the
|
|
1306
1783
|
// finalized content. Both are non-fatal — a memory hiccup must not
|
|
1307
1784
|
// escalate a successful generation into a turn-level throw. Indexing
|
|
@@ -1419,8 +1896,10 @@ export async function handleMessageComplete(
|
|
|
1419
1896
|
|
|
1420
1897
|
deps.ctx.currentTurnSurfaces = [];
|
|
1421
1898
|
|
|
1422
|
-
// Emit trace event
|
|
1423
|
-
|
|
1899
|
+
// Emit trace event. Char count is computed from the cleaned +
|
|
1900
|
+
// redacted text blocks (UI surface blocks filtered out via the
|
|
1901
|
+
// type guard) — same shape as what was just persisted.
|
|
1902
|
+
const charCount = contentForPersistence
|
|
1424
1903
|
.filter(
|
|
1425
1904
|
(b): b is Extract<ContentBlock, { type: "text" }> => b.type === "text",
|
|
1426
1905
|
)
|
|
@@ -1630,19 +2109,16 @@ export async function dispatchAgentEvent(
|
|
|
1630
2109
|
handleInputJsonDelta(state, deps, event);
|
|
1631
2110
|
break;
|
|
1632
2111
|
case "tool_result":
|
|
1633
|
-
handleToolResult(state, deps, event);
|
|
2112
|
+
await handleToolResult(state, deps, event);
|
|
1634
2113
|
break;
|
|
1635
2114
|
case "server_tool_start": {
|
|
1636
2115
|
const query =
|
|
1637
2116
|
typeof event.input.query === "string" ? event.input.query : "";
|
|
1638
2117
|
const statusText = formatSearchStatusText(event.name, query);
|
|
1639
|
-
deps.ctx.emitActivityState(
|
|
1640
|
-
|
|
1641
|
-
"tool_use_start",
|
|
1642
|
-
"assistant_turn",
|
|
1643
|
-
deps.reqId,
|
|
2118
|
+
deps.ctx.emitActivityState("tool_running", "tool_use_start", {
|
|
2119
|
+
requestId: deps.reqId,
|
|
1644
2120
|
statusText,
|
|
1645
|
-
);
|
|
2121
|
+
});
|
|
1646
2122
|
state.serverToolStartedAt.set(event.toolUseId, Date.now());
|
|
1647
2123
|
state.serverToolInputs.set(event.toolUseId, event.input);
|
|
1648
2124
|
deps.onEvent({
|
|
@@ -1656,13 +2132,10 @@ export async function dispatchAgentEvent(
|
|
|
1656
2132
|
break;
|
|
1657
2133
|
}
|
|
1658
2134
|
case "server_tool_complete": {
|
|
1659
|
-
deps.ctx.emitActivityState(
|
|
1660
|
-
|
|
1661
|
-
"
|
|
1662
|
-
|
|
1663
|
-
deps.reqId,
|
|
1664
|
-
"Thinking",
|
|
1665
|
-
);
|
|
2135
|
+
deps.ctx.emitActivityState("streaming", "tool_result_received", {
|
|
2136
|
+
requestId: deps.reqId,
|
|
2137
|
+
statusText: "Thinking",
|
|
2138
|
+
});
|
|
1666
2139
|
|
|
1667
2140
|
// Prefer `resolvedInput` (Anthropic's accumulated server-tool input,
|
|
1668
2141
|
// populated on content_block_stop) over the input captured at
|
|
@@ -1705,9 +2178,65 @@ export async function dispatchAgentEvent(
|
|
|
1705
2178
|
// for them would mis-label the provider and ship empty results.
|
|
1706
2179
|
const isAnthropicNative = deps.ctx.provider.name === "anthropic";
|
|
1707
2180
|
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
2181
|
+
// Classify provider failures through the shared normalizer so the same
|
|
2182
|
+
// friendly copy propagates to every client via WebSearchMetadata, while
|
|
2183
|
+
// the raw provider detail stays in telemetry only (ATL-727).
|
|
2184
|
+
const classification = classifyWebSearchFailure({
|
|
2185
|
+
errorCode: event.errorCode,
|
|
2186
|
+
error: event.errorMessage,
|
|
2187
|
+
isError: event.isError,
|
|
2188
|
+
hasResults: results.length > 0,
|
|
2189
|
+
});
|
|
2190
|
+
|
|
2191
|
+
let errorMessage: string | undefined;
|
|
2192
|
+
let fallbackShown = false;
|
|
2193
|
+
if (event.isError) {
|
|
2194
|
+
// A genuine backend failure OR an unclassifiable, message-less native
|
|
2195
|
+
// failure (e.g. `isError:true` with no `error_code`) both surface the
|
|
2196
|
+
// friendly backend copy: a terse "Search failed" placeholder is the
|
|
2197
|
+
// confusing copy this normalization exists to eliminate (ATL-727).
|
|
2198
|
+
// Recoverable categories that carry a real user message
|
|
2199
|
+
// (query_too_long, max_uses_exceeded) keep their own copy.
|
|
2200
|
+
const useBackendCopy =
|
|
2201
|
+
classification.isBackendFailure || !classification.userMessage;
|
|
2202
|
+
if (useBackendCopy) {
|
|
2203
|
+
// Dedup the user-facing friendly notice per turn (request id) so a
|
|
2204
|
+
// burst of failures surfaces at most one full notice. The raw
|
|
2205
|
+
// provider error is preserved on every failure via telemetry below.
|
|
2206
|
+
const alreadyNotified = state.webSearchBackendFailureNotified.has(
|
|
2207
|
+
deps.reqId,
|
|
2208
|
+
);
|
|
2209
|
+
if (alreadyNotified) {
|
|
2210
|
+
errorMessage = "Search is still having trouble.";
|
|
2211
|
+
} else {
|
|
2212
|
+
state.webSearchBackendFailureNotified.add(deps.reqId);
|
|
2213
|
+
errorMessage = WEB_SEARCH_BACKEND_FAILURE_MESSAGE;
|
|
2214
|
+
fallbackShown = true;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
// Backend-failure telemetry (provider outages / rate limits) must
|
|
2218
|
+
// fire only for genuine backend classifications so it does not
|
|
2219
|
+
// count recoverable input/quota errors — or a message-less unknown
|
|
2220
|
+
// failure that merely borrows the friendly copy — as provider
|
|
2221
|
+
// outages.
|
|
2222
|
+
if (classification.isBackendFailure) {
|
|
2223
|
+
logWebSearchBackendFailure(deps.rlog, {
|
|
2224
|
+
provider: isAnthropicNative
|
|
2225
|
+
? "anthropic-native"
|
|
2226
|
+
: deps.ctx.provider.name,
|
|
2227
|
+
requestId: deps.reqId,
|
|
2228
|
+
errorCategory: classification.category,
|
|
2229
|
+
rawDetail: classification.rawDetail,
|
|
2230
|
+
fallbackShown,
|
|
2231
|
+
queryLength: query.length,
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
2234
|
+
} else {
|
|
2235
|
+
// Recoverable, non-backend categories with their own user-facing
|
|
2236
|
+
// copy (query_too_long, max_uses_exceeded) keep that message.
|
|
2237
|
+
errorMessage = classification.userMessage;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
1711
2240
|
|
|
1712
2241
|
const metadata: WebSearchMetadata | undefined = isAnthropicNative
|
|
1713
2242
|
? {
|
|
@@ -1724,6 +2253,14 @@ export async function dispatchAgentEvent(
|
|
|
1724
2253
|
.map((r) => `${r.title}\n${r.url}`)
|
|
1725
2254
|
.join("\n\n");
|
|
1726
2255
|
|
|
2256
|
+
// Capture activity so it persists on the server_tool_use block and the
|
|
2257
|
+
// web-search card survives a history reopen, matching the live event.
|
|
2258
|
+
if (metadata) {
|
|
2259
|
+
state.toolActivityMetadata.set(event.toolUseId, {
|
|
2260
|
+
webSearch: metadata,
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
|
|
1727
2264
|
deps.onEvent({
|
|
1728
2265
|
type: "tool_result",
|
|
1729
2266
|
toolName: "web_search",
|
|
@@ -1736,6 +2273,44 @@ export async function dispatchAgentEvent(
|
|
|
1736
2273
|
});
|
|
1737
2274
|
break;
|
|
1738
2275
|
}
|
|
2276
|
+
case "context_compacting":
|
|
2277
|
+
deps.ctx.emitActivityState("thinking", "context_compacting", {
|
|
2278
|
+
requestId: deps.reqId,
|
|
2279
|
+
statusText: "Compacting context",
|
|
2280
|
+
});
|
|
2281
|
+
break;
|
|
2282
|
+
case "compaction_circuit_open":
|
|
2283
|
+
case "compaction_circuit_closed":
|
|
2284
|
+
// Circuit-breaker transitions are already in wire-contract shape
|
|
2285
|
+
// (a subset of ServerMessage), so forward them to the client sink
|
|
2286
|
+
// unchanged. They drive the client's "auto-compaction paused"
|
|
2287
|
+
// banner.
|
|
2288
|
+
deps.onEvent(event);
|
|
2289
|
+
break;
|
|
2290
|
+
case "compaction_completed":
|
|
2291
|
+
// Always commit the loop-stripped `basis` as the durable message base
|
|
2292
|
+
// so re-injection re-applies onto the stripped history even when the
|
|
2293
|
+
// pipeline ran but did not compact. When it did compact, commit the
|
|
2294
|
+
// durable result (DB-record fields, Slack provenance, SSE) — which
|
|
2295
|
+
// overwrites `ctx.messages` with the compacted history — and flip the
|
|
2296
|
+
// per-turn re-injection guards the orchestrator reads. This runs
|
|
2297
|
+
// before the loop's `reinject` hook (the loop awaits this dispatch),
|
|
2298
|
+
// so the guards are set in time. A failed durable commit re-throws
|
|
2299
|
+
// below to abort the turn rather than re-injecting against
|
|
2300
|
+
// half-applied state.
|
|
2301
|
+
deps.ctx.messages = event.basis;
|
|
2302
|
+
if (event.result.compacted) {
|
|
2303
|
+
await deps.applyCompaction(event.result, event.basis);
|
|
2304
|
+
state.reducerCompacted = true;
|
|
2305
|
+
}
|
|
2306
|
+
break;
|
|
2307
|
+
case "history_stripped":
|
|
2308
|
+
// Record the history-stripped DB marker right after the loop strips
|
|
2309
|
+
// injections (before the pipeline). Best-effort: a transient marker
|
|
2310
|
+
// write must not abort the turn, so unlike `compaction_completed` this
|
|
2311
|
+
// is not on the re-throw allowlist below.
|
|
2312
|
+
markHistoryStrippedBestEffort(deps.ctx.conversationId);
|
|
2313
|
+
break;
|
|
1739
2314
|
case "error":
|
|
1740
2315
|
handleError(state, deps, event);
|
|
1741
2316
|
break;
|
|
@@ -1788,10 +2363,13 @@ export async function dispatchAgentEvent(
|
|
|
1788
2363
|
// - message_complete: persists assistant message to DB, sets state flags
|
|
1789
2364
|
// - error: sets recovery flags (contextTooLargeDetected, orderingErrorDetected)
|
|
1790
2365
|
// - usage: records token accounting
|
|
2366
|
+
// - compaction_completed: durable compaction commit; aborting the turn is
|
|
2367
|
+
// safer than re-injecting against a half-applied compaction
|
|
1791
2368
|
if (
|
|
1792
2369
|
event.type === "message_complete" ||
|
|
1793
2370
|
event.type === "error" ||
|
|
1794
|
-
event.type === "usage"
|
|
2371
|
+
event.type === "usage" ||
|
|
2372
|
+
event.type === "compaction_completed"
|
|
1795
2373
|
) {
|
|
1796
2374
|
throw err;
|
|
1797
2375
|
}
|