@vellumai/assistant 0.12.3-staging.1 → 0.12.3-staging.2
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/ARCHITECTURE.md +43 -29
- package/Dockerfile +63 -0
- package/docs/architecture/security.md +10 -5
- package/docs/architecture/turn-actor.md +2 -0
- package/docs/desktop-browser-cli.md +45 -4
- package/docs/live-voice-task-outcomes.md +100 -0
- package/docs/mode-sessions.md +183 -0
- package/docs/voice-input-diagnostics.md +58 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
- package/openapi.yaml +665 -14
- package/package.json +1 -1
- package/scripts/smoke-desktop-browser-cli.ts +10 -26
- package/scripts/smoke-desktop-window-theme.ts +2 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
- package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/answered-question-persistence.test.ts +2 -0
- package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
- package/src/__tests__/approval-message-composer.test.ts +1 -118
- package/src/__tests__/approval-routes-http.test.ts +3 -0
- package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
- package/src/__tests__/assistant-event-targeting.test.ts +186 -0
- package/src/__tests__/assistant-stream-state.test.ts +29 -9
- package/src/__tests__/browser-fill-credential.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +4 -1
- package/src/__tests__/build-persisted-content.test.ts +1 -1
- package/src/__tests__/call-conversation-messages.test.ts +5 -0
- package/src/__tests__/call-recovery.test.ts +7 -2
- package/src/__tests__/call-routes-http.test.ts +12 -0
- package/src/__tests__/call-store.test.ts +7 -2
- package/src/__tests__/channel-retry-sweep.test.ts +9 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
- package/src/__tests__/checker.test.ts +11 -3
- package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
- package/src/__tests__/computer-use-tools.test.ts +1 -233
- package/src/__tests__/config-model-image-provider.test.ts +39 -0
- package/src/__tests__/config-schema.test.ts +16 -0
- package/src/__tests__/config-watcher.test.ts +24 -0
- package/src/__tests__/context-search-agent-runner.test.ts +260 -1
- package/src/__tests__/context-search-fanout.test.ts +1 -1
- package/src/__tests__/context-search-types.test.ts +7 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +546 -16
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
- package/src/__tests__/conversation-evictor.test.ts +62 -37
- package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-retry-route.test.ts +22 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
- package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
- package/src/__tests__/conversation-undo.test.ts +61 -0
- package/src/__tests__/credential-execution-client.test.ts +6 -6
- package/src/__tests__/credential-prompt-route.test.ts +35 -3
- package/src/__tests__/cu-unified-flow.test.ts +82 -1
- package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
- package/src/__tests__/dm-backfill.test.ts +91 -1
- package/src/__tests__/document-create-route.test.ts +88 -0
- package/src/__tests__/document-sync-tags.test.ts +27 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
- package/src/__tests__/events-tail-route.test.ts +25 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
- package/src/__tests__/guardian-action-sweep.test.ts +16 -0
- package/src/__tests__/guardian-dispatch.test.ts +13 -0
- package/src/__tests__/headless-browser-interactions.test.ts +64 -20
- package/src/__tests__/heartbeat-service.test.ts +2 -59
- package/src/__tests__/helpers/mock-conversation.ts +21 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
- package/src/__tests__/host-cu-proxy.test.ts +46 -2
- package/src/__tests__/http-user-message-parity.test.ts +2 -0
- package/src/__tests__/image-credentials.test.ts +49 -0
- package/src/__tests__/image-service-dispatcher.test.ts +45 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
- package/src/__tests__/interrupt-on-send.test.ts +57 -43
- package/src/__tests__/interrupt-turn-note.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +49 -1
- package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
- package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
- package/src/__tests__/mode-session-history.test.ts +245 -0
- package/src/__tests__/mode-session-recovery.test.ts +222 -0
- package/src/__tests__/mode-session-wire.test.ts +141 -0
- package/src/__tests__/notification-guardian-path.test.ts +7 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
- package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-api-provider.test.ts +5 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
- package/src/__tests__/process-message-display-content.test.ts +56 -0
- package/src/__tests__/reaction-persistence.test.ts +5 -0
- package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -0
- package/src/__tests__/secure-keys.test.ts +43 -1
- package/src/__tests__/send-endpoint-busy.test.ts +163 -1
- package/src/__tests__/server-history-render.test.ts +91 -1
- package/src/__tests__/settings-routes.test.ts +50 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
- package/src/__tests__/subagent-manager-notify.test.ts +1 -2
- package/src/__tests__/subagent-notify-parent.test.ts +146 -39
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +53 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/twilio-routes.test.ts +1 -0
- package/src/__tests__/web-search-backend-failure.test.ts +1 -1
- package/src/activation/progress-store.test.ts +12 -7
- package/src/agent/loop.ts +99 -41
- package/src/api/events/assistant-turn-start.ts +3 -0
- package/src/api/events/generation-handoff.ts +2 -0
- package/src/api/events/message-complete.ts +2 -0
- package/src/api/events/notification-conversation-created.ts +14 -16
- package/src/api/events/notification-intent.ts +4 -2
- package/src/api/events/tool-result.ts +85 -0
- package/src/api/events/tool-use-start.ts +3 -0
- package/src/api/events/user-message-echo.ts +3 -0
- package/src/api/index.ts +31 -6
- package/src/api/mode-session.test.ts +131 -0
- package/src/api/mode-session.ts +148 -0
- package/src/api/remember-tool.ts +15 -0
- package/src/api/responses/conversation-message.ts +6 -0
- package/src/approvals/AGENTS.md +4 -2
- package/src/browser/__tests__/operations.test.ts +29 -0
- package/src/browser/operation-meta.ts +2 -1
- package/src/browser/operations.ts +29 -0
- package/src/browser/virtual-desktop-target.ts +2 -8
- package/src/bundler/bundle-signer.ts +1 -3
- package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
- package/src/calls/__tests__/call-controller.test.ts +94 -0
- package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
- package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
- package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
- package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
- package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
- package/src/calls/barge-in-guard.ts +5 -0
- package/src/calls/call-controller.ts +196 -1
- package/src/calls/call-domain.ts +4 -0
- package/src/calls/call-store.ts +87 -2
- package/src/calls/guardian-action-sweep.ts +22 -8
- package/src/calls/media-stream-audio-transcode.ts +1 -3
- package/src/calls/typesafe-noul.ts +156 -0
- package/src/calls/voice-control-protocol.ts +6 -0
- package/src/calls/voice-escalation-judge.ts +145 -0
- package/src/calls/voice-escalation-target.ts +10 -0
- package/src/calls/voice-leg-coordinator.ts +121 -3
- package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
- package/src/calls/voice-session-bridge.ts +256 -112
- package/src/calls/voice-triage-escalate.ts +7 -0
- package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
- package/src/cli/commands/__tests__/notifications.test.ts +35 -10
- package/src/cli/commands/browser.help.ts +4 -5
- package/src/cli/commands/browser.ts +0 -1
- package/src/cli/commands/clients.help.ts +46 -0
- package/src/cli/commands/clients.ts +116 -0
- package/src/cli/commands/credentials.help.ts +6 -1
- package/src/cli/commands/notifications.help.ts +16 -2
- package/src/cli/commands/notifications.ts +30 -8
- package/src/cli/commands/roadmap.help.ts +18 -21
- package/src/cli/commands/roadmap.ts +4 -21
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
- package/src/cli/lib/bundled-marketplace.json +84 -34
- package/src/cli/lib/plugin-marketplace.ts +8 -0
- package/src/cli/lib/publish-plugin.ts +1 -2
- package/src/config/__tests__/default-provider.test.ts +3 -1
- package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
- package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
- package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
- package/src/config/call-site-defaults.ts +9 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/profile-text-generation.test.ts +2 -2
- package/src/config/schemas/__tests__/tools.test.ts +31 -0
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -18
- package/src/config/schemas/llm.ts +3 -1
- package/src/config/schemas/mcp.ts +2 -1
- package/src/config/schemas/memory-retrospective.ts +11 -2
- package/src/config/schemas/services.ts +15 -6
- package/src/config/schemas/tools.ts +10 -0
- package/src/config/session-groups-gate.ts +21 -0
- package/src/config/skills.ts +2 -6
- package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
- package/src/conversations/message-consolidation.ts +102 -5
- package/src/credential-execution/ces-connect.test.ts +69 -0
- package/src/credential-execution/ces-connect.ts +115 -0
- package/src/credential-execution/ces-runtime.ts +55 -93
- package/src/credential-execution/executable-discovery.ts +1 -11
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
- package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
- package/src/daemon/browser-mode-session.test.ts +402 -0
- package/src/daemon/browser-mode-session.ts +186 -0
- package/src/daemon/computer-use-mode-session.test.ts +210 -0
- package/src/daemon/computer-use-mode-session.ts +105 -0
- package/src/daemon/config-watcher.ts +10 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
- package/src/daemon/conversation-agent-loop.ts +219 -25
- package/src/daemon/conversation-evictor.ts +5 -24
- package/src/daemon/conversation-history.ts +10 -0
- package/src/daemon/conversation-interrupt-repair.ts +4 -43
- package/src/daemon/conversation-interrupt.ts +7 -11
- package/src/daemon/conversation-messaging.ts +58 -5
- package/src/daemon/conversation-mode-session.test.ts +1667 -0
- package/src/daemon/conversation-mode-session.ts +1008 -0
- package/src/daemon/conversation-process.ts +5 -0
- package/src/daemon/conversation-runtime-assembly.ts +10 -20
- package/src/daemon/conversation-skill-tools.ts +1 -0
- package/src/daemon/conversation-store.ts +4 -4
- package/src/daemon/conversation-surface-snapshots.ts +135 -0
- package/src/daemon/conversation-surfaces.ts +113 -5
- package/src/daemon/conversation-tool-setup.ts +8 -3
- package/src/daemon/conversation.ts +143 -41
- package/src/daemon/handlers/config-model.ts +7 -5
- package/src/daemon/handlers/shared.ts +55 -16
- package/src/daemon/host-cu-proxy.ts +51 -22
- package/src/daemon/lifecycle.ts +30 -13
- package/src/daemon/message-protocol.ts +0 -1
- package/src/daemon/message-provenance.ts +28 -4
- package/src/daemon/mode-session-startup-recovery.ts +23 -0
- package/src/daemon/mode-session-tracking.ts +54 -0
- package/src/daemon/process-message.ts +13 -4
- package/src/daemon/virtual-desktop-context.ts +21 -0
- package/src/desktop/__tests__/fake-desktop.ts +1 -0
- package/src/desktop/desktop-accessibility-script.ts +88 -0
- package/src/desktop/desktop-accessibility.test.ts +160 -0
- package/src/desktop/desktop-accessibility.ts +169 -0
- package/src/desktop/desktop-automation-lease.test.ts +76 -68
- package/src/desktop/desktop-automation-lease.ts +41 -50
- package/src/desktop/desktop-browser-endpoint.ts +4 -0
- package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
- package/src/desktop/desktop-computer-use.test.ts +449 -0
- package/src/desktop/desktop-computer-use.ts +502 -0
- package/src/desktop/desktop-dependencies.ts +47 -293
- package/src/desktop/desktop-display.ts +2 -2
- package/src/desktop/desktop-panel-config.test.ts +13 -3
- package/src/desktop/desktop-panel-config.ts +109 -15
- package/src/desktop/desktop-session-manager.test.ts +69 -21
- package/src/desktop/desktop-session-manager.ts +76 -22
- package/src/desktop/desktop-stream-bridge.test.ts +11 -122
- package/src/desktop/desktop-stream-bridge.ts +6 -8
- package/src/desktop/desktop-window-theme.ts +5 -25
- package/src/desktop/virtual-desktop-feature.ts +52 -2
- package/src/desktop/virtual-desktop-platform.test.ts +5 -9
- package/src/documents/document-store.ts +23 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
- package/src/heartbeat/heartbeat-service.ts +5 -33
- package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
- package/src/ipc/assistant-server.ts +2 -0
- package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
- package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
- package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
- package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
- package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
- package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
- package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
- package/src/live-voice/__tests__/session-controls.test.ts +23 -0
- package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
- package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
- package/src/live-voice/camera-mode-session.test.ts +265 -0
- package/src/live-voice/camera-mode-session.ts +277 -0
- package/src/live-voice/continuation-judge.ts +123 -0
- package/src/live-voice/live-activity-reporter.ts +115 -6
- package/src/live-voice/live-voice-manager.ts +36 -1
- package/src/live-voice/live-voice-photo.ts +88 -7
- package/src/live-voice/live-voice-session-manager.ts +15 -0
- package/src/live-voice/live-voice-session.ts +1443 -747
- package/src/live-voice/live-voice-tts.ts +22 -4
- package/src/live-voice/microphone-idle-gate.ts +56 -0
- package/src/live-voice/protocol.ts +171 -4
- package/src/live-voice/session-controls.ts +11 -3
- package/src/live-voice/task-outcomes.ts +131 -0
- package/src/live-voice/voice-input-diagnostics.ts +117 -0
- package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
- package/src/mcp/__tests__/tool-caps.test.ts +28 -1
- package/src/mcp/manager.ts +3 -0
- package/src/mcp/tool-caps.ts +12 -4
- package/src/media/__tests__/image-models.test.ts +58 -0
- package/src/media/image-credentials.ts +5 -0
- package/src/media/image-models.ts +52 -0
- package/src/media/image-service.ts +8 -0
- package/src/media/openrouter-image-service.test.ts +133 -0
- package/src/media/openrouter-image-service.ts +153 -0
- package/src/media/types.ts +1 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
- package/src/messaging/providers/discord/send.ts +4 -17
- package/src/messaging/providers/discord/transport.ts +2 -2
- package/src/messaging/providers/outlook/client.ts +16 -22
- package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
- package/src/messaging/providers/telegram-bot/send.ts +10 -7
- package/src/messaging/providers/telegram-bot/transport.ts +3 -10
- package/src/messaging/providers/whatsapp/send.test.ts +169 -4
- package/src/monitoring/__tests__/control.test.ts +17 -5
- package/src/monitoring/control.ts +15 -5
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/README.md +83 -99
- package/src/notifications/__tests__/broadcaster.test.ts +1 -1
- package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
- package/src/notifications/__tests__/copy-composer.test.ts +1 -1
- package/src/notifications/__tests__/decision-engine.test.ts +79 -2
- package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
- package/src/notifications/__tests__/notification-utils.test.ts +18 -0
- package/src/notifications/adapters/macos.ts +37 -46
- package/src/notifications/broadcaster.ts +5 -6
- package/src/notifications/channel-allowlist.ts +44 -0
- package/src/notifications/copy-composer.ts +0 -23
- package/src/notifications/decision-engine.ts +24 -14
- package/src/notifications/deterministic-checks.ts +1 -1
- package/src/notifications/emit-signal.ts +123 -37
- package/src/notifications/home-feed-side-effect.ts +9 -7
- package/src/notifications/notification-utils.ts +19 -0
- package/src/notifications/signal.ts +4 -19
- package/src/notifications/types.ts +15 -0
- package/src/oauth/connection-resolver.ts +1 -2
- package/src/oauth/oauth-store.ts +1 -3
- package/src/oauth/provider-serializer.ts +7 -0
- package/src/oauth/seed-providers.ts +30 -0
- package/src/onboarding/onboarding-events-store.ts +99 -12
- package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
- package/src/persistence/client-connection-events-store.ts +377 -0
- package/src/persistence/conversation-crud.ts +55 -6
- package/src/persistence/conversation-mode-sessions.test.ts +424 -0
- package/src/persistence/conversation-mode-sessions.ts +460 -0
- package/src/persistence/conversation-queries.ts +6 -19
- package/src/persistence/llm-usage-store.ts +3 -9
- package/src/persistence/message-metadata.ts +25 -0
- package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
- package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
- package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
- package/src/persistence/schema/conversations.ts +67 -0
- package/src/persistence/schema/infrastructure.ts +29 -0
- package/src/persistence/steps.ts +17 -0
- package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
- package/src/plugin-api/effective-context-window.ts +33 -0
- package/src/plugin-api/index.ts +24 -5
- package/src/plugin-api/plugin-name-env.ts +125 -0
- package/src/plugin-api/resolve-credential.ts +79 -20
- package/src/plugin-api/vision-support.test.ts +22 -0
- package/src/plugin-api/vision-support.ts +17 -5
- package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
- package/src/plugins/active-plugin-env.ts +51 -0
- package/src/plugins/defaults/index.ts +24 -0
- package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
- package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
- package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
- package/src/plugins/defaults/memory/context-search/format.ts +51 -7
- package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
- package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
- package/src/plugins/defaults/memory/context-search/search.ts +8 -14
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
- package/src/plugins/defaults/memory/context-search/types.ts +1 -11
- package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
- package/src/plugins/defaults/memory/graph/tools.ts +35 -64
- package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
- package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
- package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
- package/src/plugins/defaults/memory/tools.test.ts +124 -9
- package/src/plugins/defaults/memory/tools.ts +38 -11
- package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
- package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
- package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
- package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
- package/src/plugins/defaults/memory/worker-control.ts +15 -5
- package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
- package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
- package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
- package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
- package/src/plugins/defaults/task-progress/package.json +14 -0
- package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
- package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
- package/src/providers/anthropic/client.ts +10 -1
- package/src/providers/anthropic-gateway-shared.ts +6 -0
- package/src/providers/fetch-provider-catalog.ts +11 -0
- package/src/providers/gemini/client.ts +25 -8
- package/src/providers/inference/adapter-factory.ts +1 -1
- package/src/providers/jev/client.test.ts +10 -0
- package/src/providers/jev/client.ts +20 -4
- package/src/providers/model-catalog.ts +4 -3
- package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
- package/src/providers/openai/chat-completions-provider.ts +100 -29
- package/src/providers/openai/google-thought-signature.ts +0 -6
- package/src/providers/openai/responses-provider.ts +23 -1
- package/src/providers/ratelimit.ts +1 -3
- package/src/providers/search-provider-catalog.ts +24 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
- package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
- package/src/providers/types.ts +5 -0
- package/src/routes/control.ts +7 -5
- package/src/runtime/AGENTS.md +4 -2
- package/src/runtime/actor-trust-resolver.ts +3 -3
- package/src/runtime/agent-wake.ts +1 -1
- package/src/runtime/approval-message-composer.ts +3 -69
- package/src/runtime/assistant-event-hub.ts +73 -75
- package/src/runtime/assistant-event-publish-options.ts +7 -0
- package/src/runtime/assistant-event-targeting.ts +79 -0
- package/src/runtime/assistant-stream-state.ts +13 -100
- package/src/runtime/background-job-runner.ts +5 -5
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/guardian-action-message-composer.ts +8 -161
- package/src/runtime/guardian-reply-router.ts +28 -32
- package/src/runtime/http-types.ts +6 -0
- package/src/runtime/message-composer-types.ts +3 -61
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
- package/src/runtime/migrations/vbundle-builder.ts +24 -1
- package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
- package/src/runtime/migrations/vbundle-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-validator.ts +1 -0
- package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
- package/src/runtime/pending-interactions.ts +29 -0
- package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
- package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
- package/src/runtime/routes/browser-context.ts +31 -11
- package/src/runtime/routes/browser-routes.ts +43 -4
- package/src/runtime/routes/btw-routes.ts +9 -4
- package/src/runtime/routes/client-routes.ts +108 -0
- package/src/runtime/routes/conversation-management-routes.ts +4 -1
- package/src/runtime/routes/conversation-routes.ts +113 -27
- package/src/runtime/routes/credential-prompt-routes.ts +4 -3
- package/src/runtime/routes/credential-routes.ts +1 -2
- package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
- package/src/runtime/routes/desktop-setup-routes.ts +4 -6
- package/src/runtime/routes/documents-routes.ts +51 -0
- package/src/runtime/routes/email-routes.ts +3 -7
- package/src/runtime/routes/events-routes.ts +75 -54
- package/src/runtime/routes/identity-routes.ts +1 -5
- package/src/runtime/routes/image-generation-routes.ts +8 -14
- package/src/runtime/routes/inbound-message-handler.ts +35 -38
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +73 -2
- package/src/runtime/routes/notification-routes.ts +10 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/plugins-routes.ts +6 -0
- package/src/runtime/routes/roadmap-routes.ts +47 -8
- package/src/runtime/routes/secret-routes.ts +1 -1
- package/src/runtime/routes/settings-routes.ts +10 -4
- package/src/runtime/routes/watcher-routes.ts +1 -3
- package/src/runtime/trust-class.test.ts +7 -6
- package/src/runtime/trust-class.ts +9 -17
- package/src/schedule/__tests__/worker-control.test.ts +36 -5
- package/src/schedule/worker-control.ts +15 -5
- package/src/security/secure-keys.ts +53 -103
- package/src/skills/clawhub.ts +3 -8
- package/src/skills/managed-store.ts +1 -5
- package/src/stt/types.ts +4 -0
- package/src/subagent/notify.ts +54 -34
- package/src/subagent/parent-notification.ts +6 -0
- package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
- package/src/telemetry/phone-call-funnel.ts +144 -0
- package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
- package/src/tools/ask-question/ask-question-tool.ts +6 -4
- package/src/tools/browser/browser-execution.ts +22 -59
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
- package/src/tools/client-os.ts +15 -0
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
- package/src/tools/computer-use/target.ts +42 -0
- package/src/tools/document/document-tool.ts +13 -18
- package/src/tools/execution-target.ts +14 -16
- package/src/tools/host-terminal/host-shell.ts +4 -1
- package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
- package/src/tools/network/__tests__/web-search.test.ts +203 -0
- package/src/tools/network/web-fetch.ts +274 -7
- package/src/tools/network/web-search.ts +468 -6
- package/src/tools/permission-checker.ts +3 -3
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/skills/load.ts +2 -1
- package/src/tools/skills/sandbox-runner.ts +11 -2
- package/src/tools/skills/skill-script-runner.ts +7 -0
- package/src/tools/skills/skill-tool-factory.ts +15 -1
- package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
- package/src/tools/terminal/safe-env.ts +4 -0
- package/src/tools/terminal/shell.ts +2 -0
- package/src/tools/tool-approval-handler.ts +5 -1
- package/src/tools/tool-types.ts +3 -3
- package/src/tools/types.ts +8 -2
- package/src/util/__tests__/worker-process-command.test.ts +5 -4
- package/src/util/abort-reasons.ts +18 -26
- package/src/util/canonicalize-identity.test.ts +21 -0
- package/src/util/canonicalize-identity.ts +16 -0
- package/src/util/errors.ts +13 -3
- package/src/util/provider-error-patterns.ts +3 -0
- package/src/util/worker-process.ts +77 -35
- package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
- package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/computer-use-window-schema.test.ts +0 -24
- package/src/api/events/heartbeat-alert.ts +0 -20
- package/src/daemon/message-types/web-activity.ts +0 -65
- package/src/desktop/desktop-dependencies.test.ts +0 -131
- package/src/tools/computer-use/definitions.ts +0 -607
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
AgentEvent,
|
|
16
16
|
AgentLoopExitReason,
|
|
17
17
|
CheckpointDecision,
|
|
18
|
+
PreparedModelCall,
|
|
18
19
|
} from "../agent/loop.js";
|
|
19
20
|
import { createAssistantMessage } from "../agent/message-types.js";
|
|
20
21
|
import type { AssistantEvent } from "../api/index.js";
|
|
@@ -135,6 +136,7 @@ import {
|
|
|
135
136
|
} from "./conversation-runtime-assembly.js";
|
|
136
137
|
import type { CurrentTurnSurface } from "./conversation-surfaces.js";
|
|
137
138
|
import {
|
|
139
|
+
blockingPendingSurfaceIds,
|
|
138
140
|
hasBlockingPendingSurface,
|
|
139
141
|
markSurfaceCompleted,
|
|
140
142
|
settleRunningTaskProgressSurfaces,
|
|
@@ -152,6 +154,7 @@ import {
|
|
|
152
154
|
unregisterInflightTurn,
|
|
153
155
|
} from "./inflight-turn-registry.js";
|
|
154
156
|
import type { UsageStats } from "./message-protocol.js";
|
|
157
|
+
import { bestEffortModeSessionTracking } from "./mode-session-tracking.js";
|
|
155
158
|
import {
|
|
156
159
|
persistReactionRecords,
|
|
157
160
|
type QueuedReactionRecord,
|
|
@@ -291,6 +294,89 @@ function turnEndedAwaitingUser(ctx: Conversation): boolean {
|
|
|
291
294
|
);
|
|
292
295
|
}
|
|
293
296
|
|
|
297
|
+
function recordModeSessionStructuralWaits(
|
|
298
|
+
ctx: Conversation,
|
|
299
|
+
turnId: string,
|
|
300
|
+
): boolean {
|
|
301
|
+
let recorded = false;
|
|
302
|
+
for (const interaction of getPendingInteractionsByConversation(
|
|
303
|
+
ctx.conversationId,
|
|
304
|
+
)) {
|
|
305
|
+
const kind =
|
|
306
|
+
interaction.kind === "acp_confirmation"
|
|
307
|
+
? "confirmation"
|
|
308
|
+
: interaction.kind;
|
|
309
|
+
if (kind !== "confirmation" && kind !== "question" && kind !== "secret") {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
recorded =
|
|
313
|
+
ctx.modeSessions.recordStructuralWait(turnId, {
|
|
314
|
+
kind,
|
|
315
|
+
responseId: interaction.requestId,
|
|
316
|
+
}) || recorded;
|
|
317
|
+
}
|
|
318
|
+
for (const surfaceId of blockingPendingSurfaceIds(ctx)) {
|
|
319
|
+
recorded =
|
|
320
|
+
ctx.modeSessions.recordStructuralWait(turnId, {
|
|
321
|
+
kind: "surface",
|
|
322
|
+
responseId: surfaceId,
|
|
323
|
+
}) || recorded;
|
|
324
|
+
}
|
|
325
|
+
return recorded;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function settleModeSessionTurn(
|
|
329
|
+
ctx: Conversation,
|
|
330
|
+
turnId: string,
|
|
331
|
+
fallback: { status: "completed" | "interrupted"; endReason: string },
|
|
332
|
+
): void {
|
|
333
|
+
try {
|
|
334
|
+
if (!ctx.modeSessions.getTurnOwner(turnId)) {
|
|
335
|
+
ctx.modeSessions.releaseTurn(turnId);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (
|
|
339
|
+
fallback.status === "completed" &&
|
|
340
|
+
recordModeSessionStructuralWaits(ctx, turnId)
|
|
341
|
+
) {
|
|
342
|
+
ctx.modeSessions.releaseTurn(turnId);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (ctx.modeSessions.keepsSessionOpenAfterTurn(turnId)) {
|
|
346
|
+
ctx.modeSessions.releaseTurn(turnId);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const disposition = ctx.modeSessions.getTerminalDisposition(turnId);
|
|
350
|
+
if (disposition) {
|
|
351
|
+
ctx.modeSessions.releaseTurn(turnId, fallback);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const finalized = ctx.modeSessions.finalizeTurn({
|
|
355
|
+
turnId,
|
|
356
|
+
status: fallback.status,
|
|
357
|
+
endedAt: Date.now(),
|
|
358
|
+
endReason: fallback.endReason,
|
|
359
|
+
lastActivityAt: Date.now(),
|
|
360
|
+
});
|
|
361
|
+
if (!finalized) {
|
|
362
|
+
ctx.modeSessions.releaseTurn(turnId, fallback);
|
|
363
|
+
}
|
|
364
|
+
} catch (err) {
|
|
365
|
+
log.warn(
|
|
366
|
+
{ err, conversationId: ctx.conversationId, turnId },
|
|
367
|
+
"Mode-session turn settlement failed",
|
|
368
|
+
);
|
|
369
|
+
try {
|
|
370
|
+
ctx.modeSessions.releaseTurn(turnId, fallback);
|
|
371
|
+
} catch (releaseErr) {
|
|
372
|
+
log.warn(
|
|
373
|
+
{ err: releaseErr, conversationId: ctx.conversationId, turnId },
|
|
374
|
+
"Mode-session turn cleanup failed",
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
294
380
|
// ── abort watchdog ───────────────────────────────────────────────────
|
|
295
381
|
|
|
296
382
|
/**
|
|
@@ -392,12 +478,16 @@ export async function runAgentLoopImpl(
|
|
|
392
478
|
*/
|
|
393
479
|
replyDeliveredInAppOnly?: boolean;
|
|
394
480
|
/**
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
* the agent loop defaults to `'mainAgent'` for user-initiated turns.
|
|
481
|
+
* Semantic call-site identifier for the turn. It also selects provider
|
|
482
|
+
* configuration unless `inferenceCallSite` is set. When unset, the agent
|
|
483
|
+
* loop defaults to `'mainAgent'` for user-initiated turns.
|
|
399
484
|
*/
|
|
400
485
|
callSite?: LLMCallSite;
|
|
486
|
+
/**
|
|
487
|
+
* Provider configuration source when it differs from the turn's semantic
|
|
488
|
+
* call site. The semantic call site still controls tools and UI behavior.
|
|
489
|
+
*/
|
|
490
|
+
inferenceCallSite?: LLMCallSite;
|
|
401
491
|
/**
|
|
402
492
|
* Optional ad-hoc inference-profile override applied to every LLM call
|
|
403
493
|
* the loop issues. When set, the agent loop sets
|
|
@@ -413,6 +503,8 @@ export async function runAgentLoopImpl(
|
|
|
413
503
|
* sites. Used when a caller explicitly pins a background run to a profile.
|
|
414
504
|
*/
|
|
415
505
|
forceOverrideProfile?: boolean;
|
|
506
|
+
/** Observe the first finalized model request without delaying it. */
|
|
507
|
+
onFirstModelCallPrepared?: (prepared: PreparedModelCall) => void;
|
|
416
508
|
/**
|
|
417
509
|
* Origin tag of this turn (the conversation's `TitleOrigin`, e.g.
|
|
418
510
|
* "memory_consolidation"), threaded from `runBackgroundJob`. Exposed on
|
|
@@ -472,12 +564,12 @@ export async function runAgentLoopImpl(
|
|
|
472
564
|
ctx.currentTurnIsNonInteractive = isNonInteractive;
|
|
473
565
|
|
|
474
566
|
// Default user-initiated turns to the `mainAgent` call site; other invocation
|
|
475
|
-
// contexts
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
// conversations on `subagentSpawn` when no call site is supplied.
|
|
567
|
+
// contexts pass their own semantic `callSite`. Provider configuration uses
|
|
568
|
+
// that site unless a caller supplies `inferenceCallSite` separately.
|
|
569
|
+
// `resolveTurnCallSite` keeps subagent conversations on `subagentSpawn` when
|
|
570
|
+
// no semantic call site is supplied.
|
|
480
571
|
const turnCallSite = resolveTurnCallSite(options?.callSite, ctx);
|
|
572
|
+
const inferenceCallSite = options?.inferenceCallSite ?? turnCallSite;
|
|
481
573
|
// Expose the turn's call site on the live conversation so the runtime
|
|
482
574
|
// injection assembly self-resolves it for the turn's plugin contexts. Set
|
|
483
575
|
// before the prompt sync below: the tool-gated reply section and the tool
|
|
@@ -582,7 +674,11 @@ export async function runAgentLoopImpl(
|
|
|
582
674
|
isResolvableProvider: dispatchProviderResolvable,
|
|
583
675
|
};
|
|
584
676
|
const { config: resolved, profileName } =
|
|
585
|
-
resolveCallSiteConfigWithProfile(
|
|
677
|
+
resolveCallSiteConfigWithProfile(
|
|
678
|
+
inferenceCallSite,
|
|
679
|
+
config.llm,
|
|
680
|
+
resolveOpts,
|
|
681
|
+
);
|
|
586
682
|
let connectionName = resolved.provider_connection;
|
|
587
683
|
try {
|
|
588
684
|
connectionName =
|
|
@@ -617,7 +713,7 @@ export async function runAgentLoopImpl(
|
|
|
617
713
|
|
|
618
714
|
const effectiveContextWindow = resolveEffectiveContextWindow({
|
|
619
715
|
llm: config.llm,
|
|
620
|
-
callSite:
|
|
716
|
+
callSite: inferenceCallSite,
|
|
621
717
|
overrideProfile: turnOverrideProfile ?? undefined,
|
|
622
718
|
forceOverrideProfile,
|
|
623
719
|
selectionSeed: ctx.conversationId,
|
|
@@ -636,7 +732,7 @@ export async function runAgentLoopImpl(
|
|
|
636
732
|
};
|
|
637
733
|
|
|
638
734
|
let currentContextWindowConfig = contextWindowConfigFromEffective(
|
|
639
|
-
resolveCallSiteConfig(
|
|
735
|
+
resolveCallSiteConfig(inferenceCallSite, config.llm, {
|
|
640
736
|
overrideProfile: turnOverrideProfile ?? undefined,
|
|
641
737
|
forceOverrideProfile,
|
|
642
738
|
selectionSeed: ctx.conversationId,
|
|
@@ -652,13 +748,13 @@ export async function runAgentLoopImpl(
|
|
|
652
748
|
if (currentOverrideProfile !== appliedOverrideProfile) {
|
|
653
749
|
currentEffectiveContextWindow = resolveEffectiveContextWindow({
|
|
654
750
|
llm: config.llm,
|
|
655
|
-
callSite:
|
|
751
|
+
callSite: inferenceCallSite,
|
|
656
752
|
overrideProfile: currentOverrideProfile,
|
|
657
753
|
forceOverrideProfile,
|
|
658
754
|
selectionSeed: ctx.conversationId,
|
|
659
755
|
});
|
|
660
756
|
currentContextWindowConfig = contextWindowConfigFromEffective(
|
|
661
|
-
resolveCallSiteConfig(
|
|
757
|
+
resolveCallSiteConfig(inferenceCallSite, config.llm, {
|
|
662
758
|
overrideProfile: currentOverrideProfile,
|
|
663
759
|
forceOverrideProfile,
|
|
664
760
|
onResolutionFallback: logResolutionFallback,
|
|
@@ -677,8 +773,6 @@ export async function runAgentLoopImpl(
|
|
|
677
773
|
ctx.currentTurnOverrideProfile = currentOverrideProfile;
|
|
678
774
|
return currentOverrideProfile;
|
|
679
775
|
};
|
|
680
|
-
const resolveCurrentOverrideProfile = (): string | undefined =>
|
|
681
|
-
refreshCurrentProfileState();
|
|
682
776
|
const resolveCurrentMaxInputTokens = (): number => {
|
|
683
777
|
refreshCurrentProfileState();
|
|
684
778
|
return currentEffectiveContextWindow.maxInputTokens;
|
|
@@ -704,7 +798,7 @@ export async function runAgentLoopImpl(
|
|
|
704
798
|
};
|
|
705
799
|
|
|
706
800
|
// Initial value for `createToolExecutor` to read into
|
|
707
|
-
// `ToolContext.overrideProfile`. `
|
|
801
|
+
// `ToolContext.overrideProfile`. `refreshCurrentProfileState` refreshes
|
|
708
802
|
// this between model calls so a confirmed profile session opened by a tool
|
|
709
803
|
// applies to later tool executions and nested subagents in the same turn.
|
|
710
804
|
ctx.currentTurnOverrideProfile = turnOverrideProfile;
|
|
@@ -980,6 +1074,7 @@ export async function runAgentLoopImpl(
|
|
|
980
1074
|
publishConversationMessagesChanged(ctx.conversationId);
|
|
981
1075
|
}
|
|
982
1076
|
};
|
|
1077
|
+
let eventHandlerDeps: EventHandlerDeps | undefined;
|
|
983
1078
|
|
|
984
1079
|
try {
|
|
985
1080
|
closeTurnFinalization = beginTurnFinalization(ctx.conversationId);
|
|
@@ -1064,13 +1159,19 @@ export async function runAgentLoopImpl(
|
|
|
1064
1159
|
if (!markSurfaceCompleted(ctx, surfaceId, "Dismissed")) {
|
|
1065
1160
|
continue;
|
|
1066
1161
|
}
|
|
1162
|
+
ctx.pendingSurfaceActions.delete(surfaceId);
|
|
1163
|
+
bestEffortModeSessionTracking("stale surface wait invalidation", () =>
|
|
1164
|
+
ctx.modeSessions.invalidateStructuralWait({
|
|
1165
|
+
kind: "surface",
|
|
1166
|
+
responseId: surfaceId,
|
|
1167
|
+
}),
|
|
1168
|
+
);
|
|
1067
1169
|
onEvent({
|
|
1068
1170
|
type: "ui_surface_complete",
|
|
1069
1171
|
conversationId: ctx.conversationId,
|
|
1070
1172
|
surfaceId,
|
|
1071
1173
|
summary: "Dismissed",
|
|
1072
1174
|
});
|
|
1073
|
-
ctx.pendingSurfaceActions.delete(surfaceId);
|
|
1074
1175
|
}
|
|
1075
1176
|
}
|
|
1076
1177
|
|
|
@@ -1305,13 +1406,13 @@ export async function runAgentLoopImpl(
|
|
|
1305
1406
|
// a hand-rolled chain would credit profiles the resolver never consulted
|
|
1306
1407
|
// (e.g. activeProfile on a non-mainAgent turn).
|
|
1307
1408
|
const effectiveProfileKey =
|
|
1308
|
-
selectWinningProfile(
|
|
1409
|
+
selectWinningProfile(inferenceCallSite, config.llm, {
|
|
1309
1410
|
...(turnOverrideProfile != null
|
|
1310
1411
|
? { overrideProfile: turnOverrideProfile }
|
|
1311
1412
|
: {}),
|
|
1312
1413
|
selectionSeed: ctx.conversationId,
|
|
1313
1414
|
}).profileName ??
|
|
1314
|
-
resolveProfilelessModelKey(
|
|
1415
|
+
resolveProfilelessModelKey(inferenceCallSite, config.llm, {
|
|
1315
1416
|
...(turnOverrideProfile != null
|
|
1316
1417
|
? { overrideProfile: turnOverrideProfile }
|
|
1317
1418
|
: {}),
|
|
@@ -1412,6 +1513,7 @@ export async function runAgentLoopImpl(
|
|
|
1412
1513
|
latencyTracker,
|
|
1413
1514
|
errorAttribution: turnErrorAttribution,
|
|
1414
1515
|
};
|
|
1516
|
+
eventHandlerDeps = deps;
|
|
1415
1517
|
const eventHandler = (event: AgentEvent): Promise<void> => {
|
|
1416
1518
|
if (
|
|
1417
1519
|
event.type === "agent_loop_exit" &&
|
|
@@ -1449,7 +1551,10 @@ export async function runAgentLoopImpl(
|
|
|
1449
1551
|
|
|
1450
1552
|
turnStarted = true;
|
|
1451
1553
|
|
|
1452
|
-
rlog.info(
|
|
1554
|
+
rlog.info(
|
|
1555
|
+
{ callSite: turnCallSite, inferenceCallSite },
|
|
1556
|
+
"Starting agent loop run",
|
|
1557
|
+
);
|
|
1453
1558
|
|
|
1454
1559
|
// Trust snapshot the loop forwards to its mid-loop in-place compaction
|
|
1455
1560
|
// (scoping the compactor's image manifest) and the post-compaction
|
|
@@ -1459,6 +1564,18 @@ export async function runAgentLoopImpl(
|
|
|
1459
1564
|
// fields self-resolve from its own conversation id.
|
|
1460
1565
|
const loopTrust = ctx.getTurnOrRestingTrust() ?? FALLBACK_TURN_TRUST;
|
|
1461
1566
|
|
|
1567
|
+
const notifyFirstModelCallPrepared = options?.onFirstModelCallPrepared;
|
|
1568
|
+
let firstModelCallPrepared = false;
|
|
1569
|
+
const onModelCallPrepared = notifyFirstModelCallPrepared
|
|
1570
|
+
? (prepared: PreparedModelCall): void => {
|
|
1571
|
+
if (firstModelCallPrepared) {
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
firstModelCallPrepared = true;
|
|
1575
|
+
notifyFirstModelCallPrepared(prepared);
|
|
1576
|
+
}
|
|
1577
|
+
: undefined;
|
|
1578
|
+
|
|
1462
1579
|
/**
|
|
1463
1580
|
* Shared closure: runs the agent loop with the wrapper's turn context and
|
|
1464
1581
|
* maps the loop's returned checkpoint pause-reason into the wrapper's yield
|
|
@@ -1481,12 +1598,14 @@ export async function runAgentLoopImpl(
|
|
|
1481
1598
|
requestId: reqId,
|
|
1482
1599
|
onCheckpoint,
|
|
1483
1600
|
callSite: turnCallSite,
|
|
1601
|
+
inferenceCallSite,
|
|
1484
1602
|
suppressAssistantText: sendUserMessageActive,
|
|
1485
1603
|
supportsDynamicUi: conversationSupportsDynamicUi(ctx),
|
|
1486
1604
|
trust: loopTrust,
|
|
1487
1605
|
overrideProfile: turnOverrideProfile,
|
|
1488
1606
|
...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
|
|
1489
|
-
resolveOverrideProfile:
|
|
1607
|
+
resolveOverrideProfile: refreshCurrentProfileState,
|
|
1608
|
+
...(onModelCallPrepared !== undefined ? { onModelCallPrepared } : {}),
|
|
1490
1609
|
resolveContextWindow,
|
|
1491
1610
|
compactInPlace,
|
|
1492
1611
|
isNonInteractive,
|
|
@@ -1585,13 +1704,33 @@ export async function runAgentLoopImpl(
|
|
|
1585
1704
|
userMessageInterface: capturedTurnInterfaceContext.userMessageInterface,
|
|
1586
1705
|
assistantMessageInterface:
|
|
1587
1706
|
capturedTurnInterfaceContext.assistantMessageInterface,
|
|
1707
|
+
modeSession: ctx.modeSessions.getTurnOwner(reqId),
|
|
1588
1708
|
};
|
|
1589
|
-
await finalizePendingToolResultRow(
|
|
1709
|
+
const toolResultRowId = await finalizePendingToolResultRow(
|
|
1590
1710
|
state,
|
|
1591
1711
|
ctx.conversationId,
|
|
1592
1712
|
toolResultMetadata,
|
|
1593
1713
|
rlog,
|
|
1594
1714
|
);
|
|
1715
|
+
if (toolResultRowId) {
|
|
1716
|
+
bestEffortModeSessionTracking(
|
|
1717
|
+
"remaining tool result finalization",
|
|
1718
|
+
() => {
|
|
1719
|
+
const toolResultRow = getMessageById(
|
|
1720
|
+
toolResultRowId,
|
|
1721
|
+
ctx.conversationId,
|
|
1722
|
+
);
|
|
1723
|
+
if (toolResultRow) {
|
|
1724
|
+
ctx.modeSessions.trackPersistedRow(
|
|
1725
|
+
reqId,
|
|
1726
|
+
toolResultRowId,
|
|
1727
|
+
toolResultRow.createdAt,
|
|
1728
|
+
{ startsDisplayBoundary: false },
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
},
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1595
1734
|
}
|
|
1596
1735
|
|
|
1597
1736
|
// Persist the budget_yield_unrecovered notice now that any pending
|
|
@@ -1612,6 +1751,7 @@ export async function runAgentLoopImpl(
|
|
|
1612
1751
|
userMessageInterface: capturedTurnInterfaceContext.userMessageInterface,
|
|
1613
1752
|
assistantMessageInterface:
|
|
1614
1753
|
capturedTurnInterfaceContext.assistantMessageInterface,
|
|
1754
|
+
modeSession: ctx.modeSessions.getTurnOwner(reqId),
|
|
1615
1755
|
};
|
|
1616
1756
|
let yieldNoticePersistedId: string | null = null;
|
|
1617
1757
|
try {
|
|
@@ -1622,6 +1762,13 @@ export async function runAgentLoopImpl(
|
|
|
1622
1762
|
{ metadata: yieldNoticeMetadata },
|
|
1623
1763
|
);
|
|
1624
1764
|
yieldNoticePersistedId = yieldRow.id;
|
|
1765
|
+
bestEffortModeSessionTracking("budget yield notice persistence", () =>
|
|
1766
|
+
ctx.modeSessions.trackPersistedRow(
|
|
1767
|
+
reqId,
|
|
1768
|
+
yieldRow.id,
|
|
1769
|
+
yieldRow.createdAt,
|
|
1770
|
+
),
|
|
1771
|
+
);
|
|
1625
1772
|
} catch (err) {
|
|
1626
1773
|
// Non-fatal — a DB hiccup must not escalate a budget-yield exit into
|
|
1627
1774
|
// a turn-level throw. The live SSE event was already emitted, so the
|
|
@@ -1796,6 +1943,7 @@ export async function runAgentLoopImpl(
|
|
|
1796
1943
|
messageKind: PROVIDER_ERROR_MESSAGE_KIND,
|
|
1797
1944
|
providerErrorCode: state.providerErrorCode ?? undefined,
|
|
1798
1945
|
providerErrorCategory: state.providerErrorCategory ?? undefined,
|
|
1946
|
+
modeSession: ctx.modeSessions.getTurnOwner(reqId),
|
|
1799
1947
|
};
|
|
1800
1948
|
// The persisted row re-enters LLM history and is displayed as
|
|
1801
1949
|
// assistant speech, so the managed-billing categories swap the
|
|
@@ -1822,6 +1970,13 @@ export async function runAgentLoopImpl(
|
|
|
1822
1970
|
// (or a downstream handler) doesn't try to clean up an id that
|
|
1823
1971
|
// already corresponds to a finalized row.
|
|
1824
1972
|
state.lastAssistantMessageId = errorRow.id;
|
|
1973
|
+
bestEffortModeSessionTracking("provider error notice persistence", () =>
|
|
1974
|
+
ctx.modeSessions.trackPersistedRow(
|
|
1975
|
+
reqId,
|
|
1976
|
+
errorRow.id,
|
|
1977
|
+
errorRow.createdAt,
|
|
1978
|
+
),
|
|
1979
|
+
);
|
|
1825
1980
|
state.assistantRowAwaitingFinalization = false;
|
|
1826
1981
|
newMessages.push(errorAssistantMessage);
|
|
1827
1982
|
// Pipe the just-assigned message id into any orphaned LLM request log
|
|
@@ -1898,10 +2053,10 @@ export async function runAgentLoopImpl(
|
|
|
1898
2053
|
// contradicts itself. `forceOverrideProfile` floats it above the
|
|
1899
2054
|
// call-site profile exactly as the fallback dispatch did.
|
|
1900
2055
|
{
|
|
1901
|
-
callSite:
|
|
2056
|
+
callSite: inferenceCallSite,
|
|
1902
2057
|
overrideProfile:
|
|
1903
2058
|
state.exchangeInferenceProfile ??
|
|
1904
|
-
|
|
2059
|
+
refreshCurrentProfileState() ??
|
|
1905
2060
|
null,
|
|
1906
2061
|
...(state.exchangeInferenceProfile !== undefined
|
|
1907
2062
|
? { forceOverrideProfile: true }
|
|
@@ -2012,6 +2167,7 @@ export async function runAgentLoopImpl(
|
|
|
2012
2167
|
...(state.lastAssistantMessageId
|
|
2013
2168
|
? { messageId: state.lastAssistantMessageId }
|
|
2014
2169
|
: {}),
|
|
2170
|
+
modeSession: ctx.modeSessions.getTurnOwner(reqId),
|
|
2015
2171
|
});
|
|
2016
2172
|
publishLoopMessagesChanged();
|
|
2017
2173
|
} else {
|
|
@@ -2024,6 +2180,7 @@ export async function runAgentLoopImpl(
|
|
|
2024
2180
|
onEvent({
|
|
2025
2181
|
type: "message_complete",
|
|
2026
2182
|
conversationId: ctx.conversationId,
|
|
2183
|
+
modeSession: ctx.modeSessions.getTurnOwner(reqId),
|
|
2027
2184
|
...(emittedAttachments.length > 0
|
|
2028
2185
|
? { attachments: emittedAttachments }
|
|
2029
2186
|
: {}),
|
|
@@ -2059,6 +2216,28 @@ export async function runAgentLoopImpl(
|
|
|
2059
2216
|
await settlePendingPartialFlush(state, deps);
|
|
2060
2217
|
await settleTurnContent({ ctx, state, rlog });
|
|
2061
2218
|
|
|
2219
|
+
if (yieldedForHandoff) {
|
|
2220
|
+
const nextRequestId = ctx.queue.snapshot()[0]?.requestId;
|
|
2221
|
+
if (nextRequestId) {
|
|
2222
|
+
ctx.modeSessions.transferTurn(reqId, nextRequestId);
|
|
2223
|
+
} else {
|
|
2224
|
+
settleModeSessionTurn(ctx, reqId, {
|
|
2225
|
+
status: "completed",
|
|
2226
|
+
endReason: "handoff_settled",
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
2229
|
+
} else if (abortController.signal.aborted) {
|
|
2230
|
+
settleModeSessionTurn(ctx, reqId, {
|
|
2231
|
+
status: "interrupted",
|
|
2232
|
+
endReason: "cancelled",
|
|
2233
|
+
});
|
|
2234
|
+
} else {
|
|
2235
|
+
settleModeSessionTurn(ctx, reqId, {
|
|
2236
|
+
status: "completed",
|
|
2237
|
+
endReason: "turn_settled",
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2062
2241
|
// Content is settled, so the conversation is free. Release before the
|
|
2063
2242
|
// deferred tail rather than in the `finally`: the tail's memory indexing is
|
|
2064
2243
|
// network-bound and unbounded, and holding the lock across it is what makes
|
|
@@ -2136,6 +2315,21 @@ export async function runAgentLoopImpl(
|
|
|
2136
2315
|
onEvent(buildConversationErrorMessage(ctx.conversationId, classified));
|
|
2137
2316
|
publishLoopMessagesChanged();
|
|
2138
2317
|
}
|
|
2318
|
+
try {
|
|
2319
|
+
if (eventHandlerDeps) {
|
|
2320
|
+
await settlePendingPartialFlush(state, eventHandlerDeps);
|
|
2321
|
+
}
|
|
2322
|
+
await settleTurnContent({ ctx, state, rlog });
|
|
2323
|
+
} catch (settleErr) {
|
|
2324
|
+
rlog.warn(
|
|
2325
|
+
{ err: settleErr },
|
|
2326
|
+
"Failed to settle interrupted turn content before mode-session finalization",
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2329
|
+
settleModeSessionTurn(ctx, reqId, {
|
|
2330
|
+
status: "interrupted",
|
|
2331
|
+
endReason: isUserCancellation(err, errorCtx) ? "cancelled" : "error",
|
|
2332
|
+
});
|
|
2139
2333
|
} finally {
|
|
2140
2334
|
// Backstop release for the cancel/error paths, which throw out of the try
|
|
2141
2335
|
// before the happy path's release runs. Idempotent, so the happy path
|
|
@@ -6,8 +6,7 @@ const log = getLogger("conversation-evictor");
|
|
|
6
6
|
|
|
7
7
|
/** Minimal interface a conversation must satisfy to be evictable. */
|
|
8
8
|
export interface EvictableConversation {
|
|
9
|
-
|
|
10
|
-
hasQueuedMessages(): boolean;
|
|
9
|
+
hasInFlightWork(): boolean;
|
|
11
10
|
dispose(): void;
|
|
12
11
|
}
|
|
13
12
|
|
|
@@ -29,7 +28,7 @@ export interface EvictionResult {
|
|
|
29
28
|
lruEvicted: number;
|
|
30
29
|
/** Conversations evicted due to memory pressure. */
|
|
31
30
|
memoryEvicted: number;
|
|
32
|
-
/** Conversations skipped because they
|
|
31
|
+
/** Conversations skipped because they retained in-flight work. */
|
|
33
32
|
skipped: number;
|
|
34
33
|
}
|
|
35
34
|
|
|
@@ -74,11 +73,6 @@ export class ConversationEvictor {
|
|
|
74
73
|
getSubagentManager().abortAllForParent(conversationId);
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
/** Protect conversations with in-flight subagents from eviction. */
|
|
78
|
-
shouldProtect(conversationId: string): boolean {
|
|
79
|
-
return getSubagentManager().hasActiveChildren(conversationId);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
76
|
/** Record an access for the given conversation (resets its idle clock). */
|
|
83
77
|
touch(conversationId: string): void {
|
|
84
78
|
this.lastAccess.set(conversationId, Date.now());
|
|
@@ -141,11 +135,7 @@ export class ConversationEvictor {
|
|
|
141
135
|
if (now - lastAccessTime < this.ttlMs) {
|
|
142
136
|
continue;
|
|
143
137
|
}
|
|
144
|
-
if (
|
|
145
|
-
conversation.isProcessing() ||
|
|
146
|
-
conversation.hasQueuedMessages() ||
|
|
147
|
-
this.shouldProtect(id)
|
|
148
|
-
) {
|
|
138
|
+
if (conversation.hasInFlightWork()) {
|
|
149
139
|
result.skipped++;
|
|
150
140
|
continue;
|
|
151
141
|
}
|
|
@@ -216,20 +206,11 @@ export class ConversationEvictor {
|
|
|
216
206
|
log.debug({ conversationId: id }, "Evicted idle conversation");
|
|
217
207
|
}
|
|
218
208
|
|
|
219
|
-
/**
|
|
220
|
-
* Return idle (non-processing) conversations sorted by last access time
|
|
221
|
-
* ascending (least recently used first).
|
|
222
|
-
*/
|
|
209
|
+
/** Return evictable conversations in least-recently-used order. */
|
|
223
210
|
private idleConversationsByLru(): Array<[string, EvictableConversation]> {
|
|
224
211
|
const idle: Array<[string, EvictableConversation, number]> = [];
|
|
225
212
|
for (const [id, conversation] of this.conversations) {
|
|
226
|
-
if (conversation.
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (conversation.hasQueuedMessages()) {
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
if (this.shouldProtect(id)) {
|
|
213
|
+
if (conversation.hasInFlightWork()) {
|
|
233
214
|
continue;
|
|
234
215
|
}
|
|
235
216
|
idle.push([id, conversation, this.lastAccess.get(id) ?? 0]);
|
|
@@ -16,6 +16,8 @@ import { relinkLlmRequestLogs } from "../persistence/llm-request-log-store.js";
|
|
|
16
16
|
import { getSummaryFromContextMessage } from "../plugins/defaults/compaction/window-manager.js";
|
|
17
17
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
18
18
|
import { getLogger } from "../util/logger.js";
|
|
19
|
+
import type { ConversationModeSessionCoordinator } from "./conversation-mode-session.js";
|
|
20
|
+
import { bestEffortModeSessionTracking } from "./mode-session-tracking.js";
|
|
19
21
|
import { startsNewTurn } from "./summarize-boundary.js";
|
|
20
22
|
|
|
21
23
|
const log = getLogger("conversation-history");
|
|
@@ -360,6 +362,10 @@ export function consolidateAssistantMessages(
|
|
|
360
362
|
export interface HistoryConversationContext {
|
|
361
363
|
readonly conversationId: string;
|
|
362
364
|
messages: Message[];
|
|
365
|
+
modeSessions: Pick<
|
|
366
|
+
ConversationModeSessionCoordinator,
|
|
367
|
+
"invalidateAllStructuralWaits"
|
|
368
|
+
>;
|
|
363
369
|
isProcessing(): boolean;
|
|
364
370
|
}
|
|
365
371
|
|
|
@@ -377,6 +383,10 @@ export function undo(conversation: HistoryConversationContext): number {
|
|
|
377
383
|
return 0;
|
|
378
384
|
}
|
|
379
385
|
|
|
386
|
+
bestEffortModeSessionTracking("conversation wait invalidation", () =>
|
|
387
|
+
conversation.modeSessions.invalidateAllStructuralWaits(),
|
|
388
|
+
);
|
|
389
|
+
|
|
380
390
|
const removed = conversation.messages.length - lastUserIdx;
|
|
381
391
|
conversation.messages = conversation.messages.slice(0, lastUserIdx);
|
|
382
392
|
|
|
@@ -15,37 +15,6 @@ import type { Conversation } from "./conversation.js";
|
|
|
15
15
|
|
|
16
16
|
const log = getLogger("conversation-interrupt-repair");
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* What the repair leaves behind, for the caller that has to decide whether the
|
|
20
|
-
* model was told its turn was cut off.
|
|
21
|
-
*/
|
|
22
|
-
export interface InterruptRepairResult {
|
|
23
|
-
/**
|
|
24
|
-
* True when the tail of the history carries a preempted `tool_result`,
|
|
25
|
-
* whether the agent loop's own abort handler wrote it or this repair did.
|
|
26
|
-
*
|
|
27
|
-
* That result is the model's notice that a message preempted it. False means
|
|
28
|
-
* the abort landed with no tool call in flight, so the notice has to ride on
|
|
29
|
-
* the interrupting user message instead.
|
|
30
|
-
*/
|
|
31
|
-
preemptedToolResultOnTail: boolean;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Whether the last message answers a tool call with the preemption text. */
|
|
35
|
-
function tailCarriesPreemptedToolResult(messages: Message[]): boolean {
|
|
36
|
-
const tail = messages.at(-1);
|
|
37
|
-
if (!tail || tail.role !== "user") {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
return tail.content.some(
|
|
41
|
-
(block) =>
|
|
42
|
-
// Only a synthetic tool_result carries the preemption text; a
|
|
43
|
-
// server-side web_search_tool_result never does.
|
|
44
|
-
block.type === "tool_result" &&
|
|
45
|
-
block.content === PREEMPTED_TOOL_RESULT_TEXT,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
18
|
/**
|
|
50
19
|
* Give every abandoned `tool_use` block at the tail of the history a
|
|
51
20
|
* `tool_result`, so the next provider request is well formed.
|
|
@@ -78,22 +47,18 @@ function tailCarriesPreemptedToolResult(messages: Message[]): boolean {
|
|
|
78
47
|
export async function repairInterruptedToolUseBlocks(
|
|
79
48
|
conversation: Conversation,
|
|
80
49
|
options: { force?: boolean; requireDurable?: boolean } = {},
|
|
81
|
-
): Promise<
|
|
50
|
+
): Promise<void> {
|
|
82
51
|
const wasSteerArmed = conversation.pendingSteerRepair;
|
|
83
52
|
const wasArmed = conversation.pendingInterruptRepair;
|
|
84
53
|
if (!wasSteerArmed && !wasArmed && options.force !== true) {
|
|
85
|
-
return
|
|
86
|
-
preemptedToolResultOnTail: tailCarriesPreemptedToolResult(
|
|
87
|
-
conversation.messages,
|
|
88
|
-
),
|
|
89
|
-
};
|
|
54
|
+
return;
|
|
90
55
|
}
|
|
91
56
|
conversation.pendingSteerRepair = false;
|
|
92
57
|
conversation.pendingInterruptRepair = false;
|
|
93
58
|
|
|
94
59
|
const messages = conversation.messages;
|
|
95
60
|
if (messages.length === 0) {
|
|
96
|
-
return
|
|
61
|
+
return;
|
|
97
62
|
}
|
|
98
63
|
|
|
99
64
|
// Walk backwards from the tail to find the last assistant message with
|
|
@@ -127,10 +92,7 @@ export async function repairInterruptedToolUseBlocks(
|
|
|
127
92
|
if (pendingToolUseIds.length === 0) {
|
|
128
93
|
// Nothing dangling either because the turn made no tool call before the
|
|
129
94
|
// abort, or because the loop's abort handler already answered the batch.
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
preemptedToolResultOnTail: tailCarriesPreemptedToolResult(messages),
|
|
133
|
-
};
|
|
95
|
+
return;
|
|
134
96
|
}
|
|
135
97
|
|
|
136
98
|
log.info(
|
|
@@ -181,5 +143,4 @@ export async function repairInterruptedToolUseBlocks(
|
|
|
181
143
|
"Failed to persist the synthetic tool_result repair row; the in-memory history carries the repair and the next turn runs on it",
|
|
182
144
|
);
|
|
183
145
|
}
|
|
184
|
-
return { preemptedToolResultOnTail: true };
|
|
185
146
|
}
|