@vellumai/assistant 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -59,6 +59,7 @@ import { commitAppTurnChanges } from "../memory/app-git-service.js";
|
|
|
59
59
|
import { getApp, listAppFiles, resolveAppDir } from "../memory/app-store.js";
|
|
60
60
|
import { enqueueAutoAnalysisOnCompaction } from "../memory/auto-analysis-enqueue.js";
|
|
61
61
|
import {
|
|
62
|
+
deleteMessageById,
|
|
62
63
|
getConversation,
|
|
63
64
|
getConversationOriginChannel,
|
|
64
65
|
getConversationOriginInterface,
|
|
@@ -79,7 +80,10 @@ import {
|
|
|
79
80
|
} from "../memory/conversation-title-service.js";
|
|
80
81
|
import { isBackgroundConversationType } from "../memory/conversation-types.js";
|
|
81
82
|
import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
82
|
-
import {
|
|
83
|
+
import {
|
|
84
|
+
backfillMessageIdOnLogs,
|
|
85
|
+
recordSyntheticAgentErrorMessageLog,
|
|
86
|
+
} from "../memory/llm-request-log-store.js";
|
|
83
87
|
import { recordMemoryRecallLog } from "../memory/memory-recall-log-store.js";
|
|
84
88
|
import { enqueueMemoryRetrospectiveOnCompaction } from "../memory/memory-retrospective-enqueue.js";
|
|
85
89
|
import { PKB_WORKSPACE_SCOPE } from "../memory/pkb/types.js";
|
|
@@ -216,16 +220,37 @@ import type {
|
|
|
216
220
|
} from "./message-protocol.js";
|
|
217
221
|
import type { MemoryRecalled } from "./message-types/memory.js";
|
|
218
222
|
import type { ConfirmationStateChanged } from "./message-types/messages.js";
|
|
219
|
-
import {
|
|
220
|
-
conversationMetadataSyncTag,
|
|
221
|
-
SYNC_TAGS,
|
|
222
|
-
} from "./message-types/sync.js";
|
|
223
|
+
import { conversationMetadataSyncTag } from "./message-types/sync.js";
|
|
223
224
|
import { parseActualTokensFromError } from "./parse-actual-tokens-from-error.js";
|
|
224
225
|
import type { TraceEmitter } from "./trace-emitter.js";
|
|
225
226
|
import type { TrustContext } from "./trust-context.js";
|
|
226
227
|
import { stripHistoricalWebSearchResults } from "./web-search-history.js";
|
|
227
228
|
|
|
228
229
|
const log = getLogger("conversation-agent-loop");
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Best-effort persistence of the history-stripped marker after an
|
|
233
|
+
* injection-strip event (compaction / overflow recovery). The marker is a
|
|
234
|
+
* durability hint, not turn-critical state — a transient SQLite write failure
|
|
235
|
+
* (SQLITE_BUSY, disk-full, read-only FS) must not abort the turn. Logs a
|
|
236
|
+
* warning and continues on failure, preserving the long-standing non-fatal
|
|
237
|
+
* contract for this metadata write.
|
|
238
|
+
*/
|
|
239
|
+
function markHistoryStrippedBestEffort(
|
|
240
|
+
conversationId: string,
|
|
241
|
+
strippedAt: number,
|
|
242
|
+
logger: ReturnType<typeof getLogger>,
|
|
243
|
+
): void {
|
|
244
|
+
try {
|
|
245
|
+
setConversationHistoryStrippedAt(conversationId, strippedAt);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
logger.warn(
|
|
248
|
+
{ err },
|
|
249
|
+
"Failed to persist history-stripped marker after compaction strip (non-fatal)",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
229
254
|
const DISK_PRESSURE_ERROR_CODE = "DISK_SPACE_CRITICAL" as const;
|
|
230
255
|
const DISK_PRESSURE_ERROR_CATEGORY = "disk_pressure";
|
|
231
256
|
|
|
@@ -513,7 +538,12 @@ export interface AgentLoopConversationContext {
|
|
|
513
538
|
surfaceType: SurfaceType;
|
|
514
539
|
title?: string;
|
|
515
540
|
data: SurfaceData;
|
|
516
|
-
actions?: Array<{
|
|
541
|
+
actions?: Array<{
|
|
542
|
+
id: string;
|
|
543
|
+
label: string;
|
|
544
|
+
style?: string;
|
|
545
|
+
data?: Record<string, unknown>;
|
|
546
|
+
}>;
|
|
517
547
|
display?: string;
|
|
518
548
|
persistent?: boolean;
|
|
519
549
|
}>;
|
|
@@ -541,12 +571,6 @@ export interface AgentLoopConversationContext {
|
|
|
541
571
|
* turn start.
|
|
542
572
|
*/
|
|
543
573
|
toolRoutedProfile?: string;
|
|
544
|
-
/**
|
|
545
|
-
* True when the user has explicitly selected an inference profile for this
|
|
546
|
-
* conversation (via the composer profile picker). When set, tool-based
|
|
547
|
-
* auto-routing is suppressed — the user's explicit choice takes precedence.
|
|
548
|
-
*/
|
|
549
|
-
hasExplicitProfileOverride?: boolean;
|
|
550
574
|
commandIntent?: { type: string; payload?: string; languageCode?: string };
|
|
551
575
|
trustContext?: TrustContext;
|
|
552
576
|
/** Task-run scope for the current turn. Cleared at turn end so queued/drained turns don't inherit it. */
|
|
@@ -724,8 +748,6 @@ export async function runAgentLoopImpl(
|
|
|
724
748
|
options?.overrideProfile ??
|
|
725
749
|
getConversationOverrideProfileFromRow(turnStartConversation);
|
|
726
750
|
|
|
727
|
-
ctx.hasExplicitProfileOverride = !!userExplicitOverride;
|
|
728
|
-
|
|
729
751
|
const config = getConfig();
|
|
730
752
|
|
|
731
753
|
// Tool-based auto-routing: the switch_inference_profile tool lets the model
|
|
@@ -746,12 +768,14 @@ export async function runAgentLoopImpl(
|
|
|
746
768
|
llm: config.llm,
|
|
747
769
|
callSite: turnCallSite,
|
|
748
770
|
overrideProfile: turnOverrideProfile ?? undefined,
|
|
771
|
+
selectionSeed: ctx.conversationId,
|
|
749
772
|
});
|
|
750
773
|
let currentEffectiveContextWindow: EffectiveContextWindow =
|
|
751
774
|
effectiveContextWindow;
|
|
752
775
|
let currentContextWindowConfig = contextWindowConfigFromEffective(
|
|
753
776
|
resolveCallSiteConfig(turnCallSite, config.llm, {
|
|
754
777
|
overrideProfile: turnOverrideProfile ?? undefined,
|
|
778
|
+
selectionSeed: ctx.conversationId,
|
|
755
779
|
}).contextWindow,
|
|
756
780
|
currentEffectiveContextWindow,
|
|
757
781
|
);
|
|
@@ -770,10 +794,12 @@ export async function runAgentLoopImpl(
|
|
|
770
794
|
llm: config.llm,
|
|
771
795
|
callSite: turnCallSite,
|
|
772
796
|
overrideProfile: currentOverrideProfile,
|
|
797
|
+
selectionSeed: ctx.conversationId,
|
|
773
798
|
});
|
|
774
799
|
currentContextWindowConfig = contextWindowConfigFromEffective(
|
|
775
800
|
resolveCallSiteConfig(turnCallSite, config.llm, {
|
|
776
801
|
overrideProfile: currentOverrideProfile,
|
|
802
|
+
selectionSeed: ctx.conversationId,
|
|
777
803
|
}).contextWindow,
|
|
778
804
|
currentEffectiveContextWindow,
|
|
779
805
|
);
|
|
@@ -1028,10 +1054,7 @@ export async function runAgentLoopImpl(
|
|
|
1028
1054
|
});
|
|
1029
1055
|
onEvent({
|
|
1030
1056
|
type: "sync_changed",
|
|
1031
|
-
tags: [
|
|
1032
|
-
SYNC_TAGS.conversationsList,
|
|
1033
|
-
conversationMetadataSyncTag(ctx.conversationId),
|
|
1034
|
-
],
|
|
1057
|
+
tags: [conversationMetadataSyncTag(ctx.conversationId)],
|
|
1035
1058
|
});
|
|
1036
1059
|
},
|
|
1037
1060
|
};
|
|
@@ -2312,7 +2335,7 @@ export async function runAgentLoopImpl(
|
|
|
2312
2335
|
// so we compact the "raw" persistent messages.
|
|
2313
2336
|
const rawHistory = stripInjectionsForCompaction(updatedHistory);
|
|
2314
2337
|
ctx.messages = rawHistory;
|
|
2315
|
-
|
|
2338
|
+
markHistoryStrippedBestEffort(ctx.conversationId, Date.now(), rlog);
|
|
2316
2339
|
|
|
2317
2340
|
ctx.emitActivityState(
|
|
2318
2341
|
"thinking",
|
|
@@ -2596,7 +2619,7 @@ export async function runAgentLoopImpl(
|
|
|
2596
2619
|
|
|
2597
2620
|
if (updatedHistory.length > preRunHistoryLength) {
|
|
2598
2621
|
ctx.messages = stripInjectionsForCompaction(updatedHistory);
|
|
2599
|
-
|
|
2622
|
+
markHistoryStrippedBestEffort(ctx.conversationId, Date.now(), rlog);
|
|
2600
2623
|
convergenceStripped = true;
|
|
2601
2624
|
preRepairMessages = updatedHistory;
|
|
2602
2625
|
preRunHistoryLength = updatedHistory.length;
|
|
@@ -2841,7 +2864,7 @@ export async function runAgentLoopImpl(
|
|
|
2841
2864
|
// pre-rerun messages.
|
|
2842
2865
|
if (updatedHistory.length > preRunHistoryLength) {
|
|
2843
2866
|
ctx.messages = stripInjectionsForCompaction(updatedHistory);
|
|
2844
|
-
|
|
2867
|
+
markHistoryStrippedBestEffort(ctx.conversationId, Date.now(), rlog);
|
|
2845
2868
|
convergenceStripped = true;
|
|
2846
2869
|
preRepairMessages = updatedHistory;
|
|
2847
2870
|
preRunHistoryLength = updatedHistory.length;
|
|
@@ -3110,8 +3133,12 @@ export async function runAgentLoopImpl(
|
|
|
3110
3133
|
assistantMessageInterface:
|
|
3111
3134
|
capturedTurnInterfaceContext.assistantMessageInterface,
|
|
3112
3135
|
};
|
|
3136
|
+
let yieldNoticePersistedId: string | null = null;
|
|
3113
3137
|
try {
|
|
3114
|
-
await runPipeline<
|
|
3138
|
+
const yieldPersistResult = (await runPipeline<
|
|
3139
|
+
PersistArgs,
|
|
3140
|
+
PersistResult
|
|
3141
|
+
>(
|
|
3115
3142
|
"persistence",
|
|
3116
3143
|
getMiddlewaresFor("persistence"),
|
|
3117
3144
|
defaultPersistenceTerminal,
|
|
@@ -3124,7 +3151,8 @@ export async function runAgentLoopImpl(
|
|
|
3124
3151
|
},
|
|
3125
3152
|
buildPluginTurnContext(ctx, reqId),
|
|
3126
3153
|
DEFAULT_TIMEOUTS.persistence,
|
|
3127
|
-
);
|
|
3154
|
+
)) as PersistAddResult;
|
|
3155
|
+
yieldNoticePersistedId = yieldPersistResult.message.id;
|
|
3128
3156
|
} catch (err) {
|
|
3129
3157
|
// Non-fatal — a DB hiccup must not escalate a budget-yield exit into
|
|
3130
3158
|
// a turn-level throw. The live SSE event was already emitted, so the
|
|
@@ -3134,6 +3162,48 @@ export async function runAgentLoopImpl(
|
|
|
3134
3162
|
"Failed to persist budget_yield_unrecovered notice (non-fatal)",
|
|
3135
3163
|
);
|
|
3136
3164
|
}
|
|
3165
|
+
// Record a synthetic `llm_request_logs` row for the yield so the
|
|
3166
|
+
// inspector's call rail surfaces a clickable, distinctly-rendered
|
|
3167
|
+
// entry for the failure itself. Without this row, the loop yields
|
|
3168
|
+
// silently — the user sees the notice in chat but the inspector
|
|
3169
|
+
// call list ends at the last actual LLM call with no way to scope
|
|
3170
|
+
// the "what compactions led to this failure?" question to the
|
|
3171
|
+
// yield event.
|
|
3172
|
+
//
|
|
3173
|
+
// Recorded *before* emitTerminalExit so the synthetic row exists
|
|
3174
|
+
// by the time the dispatcher's post-loop hook runs. The row
|
|
3175
|
+
// already carries `agent_loop_exit_reason` at insert time, so
|
|
3176
|
+
// `setAgentLoopExitReasonOnLatestLog`'s IS NULL guard skips it
|
|
3177
|
+
// and stamps the prior real mainAgent call instead — preserving
|
|
3178
|
+
// the existing "latest LLM call carries the exit reason"
|
|
3179
|
+
// invariant other consumers depend on.
|
|
3180
|
+
//
|
|
3181
|
+
// `preparedRequest` snapshots the best-known LLM request state
|
|
3182
|
+
// at yield time — `updatedHistory` (the conversation state the
|
|
3183
|
+
// next call would have been built from) plus the input-token
|
|
3184
|
+
// budget that just failed. Mirrors the role of `request_payload`
|
|
3185
|
+
// on real LLM-call rows; the notice text lives on
|
|
3186
|
+
// `response_payload`.
|
|
3187
|
+
if (yieldNoticePersistedId !== null && budgetYieldClassification) {
|
|
3188
|
+
try {
|
|
3189
|
+
recordSyntheticAgentErrorMessageLog({
|
|
3190
|
+
conversationId: ctx.conversationId,
|
|
3191
|
+
messageId: yieldNoticePersistedId,
|
|
3192
|
+
exitReason: "budget_yield_unrecovered",
|
|
3193
|
+
noticeText: budgetYieldClassification.userMessage,
|
|
3194
|
+
preparedRequest: {
|
|
3195
|
+
messages: updatedHistory,
|
|
3196
|
+
maxInputTokensBudget: resolveCurrentMaxInputTokens() ?? null,
|
|
3197
|
+
},
|
|
3198
|
+
createdAt: Date.now(),
|
|
3199
|
+
});
|
|
3200
|
+
} catch (err) {
|
|
3201
|
+
rlog.warn(
|
|
3202
|
+
{ err },
|
|
3203
|
+
"Failed to record budget_yield_unrecovered synthetic call log (non-fatal)",
|
|
3204
|
+
);
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3137
3207
|
await emitTerminalExit?.("budget_yield_unrecovered");
|
|
3138
3208
|
}
|
|
3139
3209
|
|
|
@@ -3154,6 +3224,34 @@ export async function runAgentLoopImpl(
|
|
|
3154
3224
|
!abortController.signal.aborted &&
|
|
3155
3225
|
!yieldedForHandoff
|
|
3156
3226
|
) {
|
|
3227
|
+
// Drop any reservation stranded by the failed LLM call before
|
|
3228
|
+
// inserting the synthetic error message. The B3 pre-allocation
|
|
3229
|
+
// path reserves an empty assistant row at `llm_call_started`;
|
|
3230
|
+
// when the call exits through the provider-error branch (no
|
|
3231
|
+
// `message_complete`), `assistantRowAwaitingFinalization` stays
|
|
3232
|
+
// true. Without this delete the transcript would carry both the
|
|
3233
|
+
// empty reserved row AND the error message — and downstream sync
|
|
3234
|
+
// (`syncLastAssistantMessageToDisk`) would mis-target the empty
|
|
3235
|
+
// row. After delete we set `lastAssistantMessageId` to the new
|
|
3236
|
+
// error row's id so the post-loop emission paths still point at
|
|
3237
|
+
// a real message.
|
|
3238
|
+
if (
|
|
3239
|
+
state.assistantRowAwaitingFinalization &&
|
|
3240
|
+
state.lastAssistantMessageId
|
|
3241
|
+
) {
|
|
3242
|
+
// Direct `deleteMessageById` (not via the `persistence` pipeline):
|
|
3243
|
+
// see the same rationale on the matching cleanup in
|
|
3244
|
+
// `handleLlmCallStarted` — an unfinalized reservation has no
|
|
3245
|
+
// observable history for plugins.
|
|
3246
|
+
try {
|
|
3247
|
+
deleteMessageById(state.lastAssistantMessageId);
|
|
3248
|
+
} catch (err) {
|
|
3249
|
+
rlog.warn(
|
|
3250
|
+
{ err, messageId: state.lastAssistantMessageId },
|
|
3251
|
+
"Failed to clean up stranded reserved assistant row on provider-error path (non-fatal)",
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3157
3255
|
const errChannelMeta = {
|
|
3158
3256
|
...provenanceFromTrustContext(ctx.trustContext),
|
|
3159
3257
|
userMessageChannel: capturedTurnChannelContext.userMessageChannel,
|
|
@@ -3181,6 +3279,15 @@ export async function runAgentLoopImpl(
|
|
|
3181
3279
|
DEFAULT_TIMEOUTS.persistence,
|
|
3182
3280
|
)) as PersistAddResult;
|
|
3183
3281
|
persistedErrorAssistantMessage = true;
|
|
3282
|
+
// Repoint `lastAssistantMessageId` at the synthetic error row so the
|
|
3283
|
+
// post-loop sync, attachment resolution, and `message_complete`/
|
|
3284
|
+
// `generation_handoff` emissions all reference a real, persisted
|
|
3285
|
+
// message id. The previous reservation (if any) was already deleted
|
|
3286
|
+
// above. Mark finalization complete so the next LLM call in this run
|
|
3287
|
+
// (or a downstream handler) doesn't try to clean up an id that
|
|
3288
|
+
// already corresponds to a finalized row.
|
|
3289
|
+
state.lastAssistantMessageId = errorPersistResult.message.id;
|
|
3290
|
+
state.assistantRowAwaitingFinalization = false;
|
|
3184
3291
|
newMessages.push(errorAssistantMessage);
|
|
3185
3292
|
// Pipe the just-assigned message id into any orphaned LLM request log
|
|
3186
3293
|
// row(s) for this turn. The success path links rows via
|
|
@@ -3446,10 +3553,7 @@ export async function runAgentLoopImpl(
|
|
|
3446
3553
|
});
|
|
3447
3554
|
onEvent({
|
|
3448
3555
|
type: "sync_changed",
|
|
3449
|
-
tags: [
|
|
3450
|
-
SYNC_TAGS.conversationsList,
|
|
3451
|
-
conversationMetadataSyncTag(ctx.conversationId),
|
|
3452
|
-
],
|
|
3556
|
+
tags: [conversationMetadataSyncTag(ctx.conversationId)],
|
|
3453
3557
|
});
|
|
3454
3558
|
},
|
|
3455
3559
|
signal: abortController.signal,
|
|
@@ -3694,7 +3798,7 @@ export async function applyCompactionResult(
|
|
|
3694
3798
|
result.summaryText,
|
|
3695
3799
|
ctx.contextCompactedMessageCount,
|
|
3696
3800
|
);
|
|
3697
|
-
|
|
3801
|
+
markHistoryStrippedBestEffort(ctx.conversationId, compactedAt, log);
|
|
3698
3802
|
if (options.slackContextCompactionWatermarkTs) {
|
|
3699
3803
|
updateConversationSlackContextWatermark(
|
|
3700
3804
|
ctx.conversationId,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ConnectionResolutionError } from "../providers/connection-resolution.js";
|
|
1
2
|
import { getProviderRoutingSource } from "../providers/registry.js";
|
|
2
3
|
import { isAbortReason } from "../util/abort-reasons.js";
|
|
3
4
|
import { ProviderError, ProviderNotConfiguredError } from "../util/errors.js";
|
|
@@ -251,14 +252,28 @@ export function classifyConversationError(
|
|
|
251
252
|
if (error instanceof ProviderNotConfiguredError) {
|
|
252
253
|
return {
|
|
253
254
|
...providerNotConfiguredClassification({
|
|
254
|
-
connectionName:
|
|
255
|
-
error.connectionName ?? attribution.connectionName,
|
|
255
|
+
connectionName: error.connectionName ?? attribution.connectionName,
|
|
256
256
|
profileName: error.profileName ?? attribution.profileName,
|
|
257
257
|
}),
|
|
258
258
|
debugDetails,
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
if (error instanceof ConnectionResolutionError) {
|
|
263
|
+
return {
|
|
264
|
+
code: "PROVIDER_NOT_CONFIGURED",
|
|
265
|
+
userMessage:
|
|
266
|
+
"No compatible provider connection found for this profile. Check your provider connections in Settings.",
|
|
267
|
+
retryable: true,
|
|
268
|
+
debugDetails,
|
|
269
|
+
errorCategory: "provider_not_configured",
|
|
270
|
+
...(error.connectionName ? { connectionName: error.connectionName } : {}),
|
|
271
|
+
...(attribution.profileName
|
|
272
|
+
? { profileName: attribution.profileName }
|
|
273
|
+
: {}),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
262
277
|
// Phase-specific overrides
|
|
263
278
|
if (ctx.phase === "regenerate") {
|
|
264
279
|
const base = classifyCore(error, message, attribution);
|
|
@@ -790,6 +805,21 @@ export function budgetYieldUnrecoveredClassification(): ClassifiedConversationEr
|
|
|
790
805
|
};
|
|
791
806
|
}
|
|
792
807
|
|
|
808
|
+
/**
|
|
809
|
+
* Classify a model response that stopped because the output-token limit was
|
|
810
|
+
* reached. The turn may have produced useful partial text, so the recovery is
|
|
811
|
+
* a follow-up user turn rather than a retry of the same request.
|
|
812
|
+
*/
|
|
813
|
+
export function maxTokensReachedClassification(): ClassifiedConversationError {
|
|
814
|
+
return {
|
|
815
|
+
code: "MAX_TOKENS_REACHED",
|
|
816
|
+
userMessage:
|
|
817
|
+
"I hit the response limit before I could finish. Continue and I'll pick up from where I stopped.",
|
|
818
|
+
retryable: true,
|
|
819
|
+
errorCategory: "max_tokens_reached",
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
793
823
|
/**
|
|
794
824
|
* Build a `conversation_error` server message from a classified error.
|
|
795
825
|
*/
|
|
@@ -811,8 +841,6 @@ export function buildConversationErrorMessage(
|
|
|
811
841
|
...(classified.connectionName
|
|
812
842
|
? { connectionName: classified.connectionName }
|
|
813
843
|
: {}),
|
|
814
|
-
...(classified.profileName
|
|
815
|
-
? { profileName: classified.profileName }
|
|
816
|
-
: {}),
|
|
844
|
+
...(classified.profileName ? { profileName: classified.profileName } : {}),
|
|
817
845
|
};
|
|
818
846
|
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from "../memory/attachments-store.js";
|
|
27
27
|
import {
|
|
28
28
|
addMessage,
|
|
29
|
+
extractImageSourcePaths,
|
|
29
30
|
getConversation,
|
|
30
31
|
provenanceFromTrustContext,
|
|
31
32
|
setConversationOriginChannelIfUnset,
|
|
@@ -284,22 +285,44 @@ export function buildSlackMetaForPersistence(params: {
|
|
|
284
285
|
return writeSlackMetadata(slackMeta);
|
|
285
286
|
}
|
|
286
287
|
|
|
288
|
+
// ── EnqueueMessageOptions ────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
/** Options for `enqueueMessage`. Only `content` is required; everything
|
|
291
|
+
* else has a sensible default or is genuinely optional. */
|
|
292
|
+
export interface EnqueueMessageOptions {
|
|
293
|
+
content: string;
|
|
294
|
+
attachments?: UserMessageAttachment[];
|
|
295
|
+
onEvent?: (msg: ServerMessage) => void;
|
|
296
|
+
requestId?: string;
|
|
297
|
+
activeSurfaceId?: string;
|
|
298
|
+
currentPage?: string;
|
|
299
|
+
metadata?: Record<string, unknown>;
|
|
300
|
+
isInteractive?: boolean;
|
|
301
|
+
displayContent?: string;
|
|
302
|
+
transport?: ConversationTransportMetadata;
|
|
303
|
+
clientMessageId?: string;
|
|
304
|
+
}
|
|
305
|
+
|
|
287
306
|
// ── enqueueMessage ───────────────────────────────────────────────────
|
|
288
307
|
|
|
289
308
|
export function enqueueMessage(
|
|
290
309
|
ctx: MessagingConversationContext,
|
|
291
|
-
|
|
292
|
-
attachments: UserMessageAttachment[],
|
|
293
|
-
onEvent: (msg: ServerMessage) => void,
|
|
294
|
-
requestId: string,
|
|
295
|
-
activeSurfaceId?: string,
|
|
296
|
-
currentPage?: string,
|
|
297
|
-
metadata?: Record<string, unknown>,
|
|
298
|
-
options?: { isInteractive?: boolean },
|
|
299
|
-
displayContent?: string,
|
|
300
|
-
transport?: ConversationTransportMetadata,
|
|
301
|
-
clientMessageId?: string,
|
|
310
|
+
options: EnqueueMessageOptions,
|
|
302
311
|
): { queued: boolean; requestId: string; rejected?: boolean } {
|
|
312
|
+
const {
|
|
313
|
+
content,
|
|
314
|
+
attachments = [],
|
|
315
|
+
onEvent,
|
|
316
|
+
requestId = crypto.randomUUID(),
|
|
317
|
+
activeSurfaceId,
|
|
318
|
+
currentPage,
|
|
319
|
+
metadata,
|
|
320
|
+
isInteractive,
|
|
321
|
+
displayContent,
|
|
322
|
+
transport,
|
|
323
|
+
clientMessageId,
|
|
324
|
+
} = options;
|
|
325
|
+
|
|
303
326
|
if (!ctx.processing) {
|
|
304
327
|
return { queued: false, requestId };
|
|
305
328
|
}
|
|
@@ -316,20 +339,20 @@ export function enqueueMessage(
|
|
|
316
339
|
content,
|
|
317
340
|
attachments,
|
|
318
341
|
requestId,
|
|
319
|
-
onEvent,
|
|
342
|
+
onEvent: onEvent ?? (() => {}),
|
|
320
343
|
activeSurfaceId,
|
|
321
344
|
currentPage,
|
|
322
345
|
metadata,
|
|
323
346
|
turnChannelContext,
|
|
324
347
|
turnInterfaceContext,
|
|
325
|
-
isInteractive
|
|
348
|
+
isInteractive,
|
|
326
349
|
transport,
|
|
327
350
|
displayContent,
|
|
328
351
|
sentAt: Date.now(),
|
|
329
352
|
clientMessageId,
|
|
330
353
|
});
|
|
331
354
|
if (!accepted) {
|
|
332
|
-
onEvent({
|
|
355
|
+
onEvent?.({
|
|
333
356
|
type: "error",
|
|
334
357
|
conversationId: ctx.conversationId,
|
|
335
358
|
message:
|
|
@@ -341,16 +364,26 @@ export function enqueueMessage(
|
|
|
341
364
|
return { queued: true, requestId };
|
|
342
365
|
}
|
|
343
366
|
|
|
367
|
+
// ── PersistMessageOptions ────────────────────────────────────────────
|
|
368
|
+
|
|
369
|
+
/** Shared options for `persistUserMessage` and `persistQueuedMessageBody`. */
|
|
370
|
+
export interface PersistMessageOptions {
|
|
371
|
+
content: string;
|
|
372
|
+
attachments?: UserMessageAttachment[];
|
|
373
|
+
requestId?: string;
|
|
374
|
+
metadata?: Record<string, unknown>;
|
|
375
|
+
displayContent?: string;
|
|
376
|
+
clientMessageId?: string;
|
|
377
|
+
}
|
|
378
|
+
|
|
344
379
|
// ── persistUserMessage ───────────────────────────────────────────────
|
|
345
380
|
|
|
346
381
|
export async function persistUserMessage(
|
|
347
382
|
ctx: MessagingConversationContext,
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
displayContent?: string,
|
|
353
|
-
): Promise<string> {
|
|
383
|
+
options: PersistMessageOptions,
|
|
384
|
+
): Promise<{ id: string; deduplicated: boolean }> {
|
|
385
|
+
const { content, attachments = [] } = options;
|
|
386
|
+
|
|
354
387
|
if (ctx.processing) {
|
|
355
388
|
throw new Error("Conversation is already processing a message");
|
|
356
389
|
}
|
|
@@ -359,20 +392,23 @@ export async function persistUserMessage(
|
|
|
359
392
|
throw new Error("Message content or attachments are required");
|
|
360
393
|
}
|
|
361
394
|
|
|
362
|
-
const reqId = requestId ?? uuid();
|
|
395
|
+
const reqId = options.requestId ?? uuid();
|
|
363
396
|
ctx.currentRequestId = reqId;
|
|
364
397
|
ctx.processing = true;
|
|
365
398
|
ctx.abortController = new AbortController();
|
|
366
399
|
|
|
367
400
|
try {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
content,
|
|
401
|
+
const result = await persistQueuedMessageBody(ctx, {
|
|
402
|
+
...options,
|
|
371
403
|
attachments,
|
|
372
|
-
reqId,
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
404
|
+
requestId: reqId,
|
|
405
|
+
});
|
|
406
|
+
if (result.deduplicated) {
|
|
407
|
+
ctx.processing = false;
|
|
408
|
+
ctx.abortController = null;
|
|
409
|
+
ctx.currentRequestId = undefined;
|
|
410
|
+
}
|
|
411
|
+
return result;
|
|
376
412
|
} catch (err) {
|
|
377
413
|
ctx.processing = false;
|
|
378
414
|
ctx.abortController = null;
|
|
@@ -394,12 +430,16 @@ export async function persistUserMessage(
|
|
|
394
430
|
*/
|
|
395
431
|
export async function persistQueuedMessageBody(
|
|
396
432
|
ctx: MessagingConversationContext,
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
433
|
+
options: PersistMessageOptions,
|
|
434
|
+
): Promise<{ id: string; deduplicated: boolean }> {
|
|
435
|
+
const {
|
|
436
|
+
content,
|
|
437
|
+
attachments = [],
|
|
438
|
+
requestId = uuid(),
|
|
439
|
+
metadata,
|
|
440
|
+
displayContent,
|
|
441
|
+
clientMessageId,
|
|
442
|
+
} = options;
|
|
403
443
|
const attachmentInputs = attachments.map((attachment) => ({
|
|
404
444
|
id: attachment.id,
|
|
405
445
|
filename: attachment.filename,
|
|
@@ -431,13 +471,7 @@ export async function persistQueuedMessageBody(
|
|
|
431
471
|
const turnIfCtx =
|
|
432
472
|
extractTurnInterfaceContext(metadata) ?? ctx.getTurnInterfaceContext();
|
|
433
473
|
const provenance = provenanceFromTrustContext(ctx.trustContext);
|
|
434
|
-
const imageSourcePaths
|
|
435
|
-
for (let i = 0; i < attachments.length; i++) {
|
|
436
|
-
const a = attachments[i];
|
|
437
|
-
if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
|
|
438
|
-
imageSourcePaths[`${i}:${a.filename}`] = a.filePath;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
474
|
+
const imageSourcePaths = extractImageSourcePaths(attachments);
|
|
441
475
|
|
|
442
476
|
// Strip the transient `slackInbound` carrier key from the persisted
|
|
443
477
|
// metadata — it's an in-memory plumbing field, not a stored column value.
|
|
@@ -468,7 +502,7 @@ export async function persistQueuedMessageBody(
|
|
|
468
502
|
assistantMessageInterface: turnIfCtx.assistantMessageInterface,
|
|
469
503
|
}
|
|
470
504
|
: {}),
|
|
471
|
-
...(
|
|
505
|
+
...(imageSourcePaths ? { imageSourcePaths } : {}),
|
|
472
506
|
...(slackMeta ? { slackMeta } : {}),
|
|
473
507
|
};
|
|
474
508
|
|
|
@@ -486,8 +520,15 @@ export async function persistQueuedMessageBody(
|
|
|
486
520
|
"user",
|
|
487
521
|
contentToPersist,
|
|
488
522
|
mergedMetadata,
|
|
523
|
+
undefined,
|
|
524
|
+
clientMessageId,
|
|
489
525
|
);
|
|
490
526
|
|
|
527
|
+
if (persistedUserMessage.deduplicated) {
|
|
528
|
+
ctx.messages.pop();
|
|
529
|
+
return { id: persistedUserMessage.id, deduplicated: true };
|
|
530
|
+
}
|
|
531
|
+
|
|
491
532
|
if (turnCtx) {
|
|
492
533
|
setConversationOriginChannelIfUnset(
|
|
493
534
|
ctx.conversationId,
|
|
@@ -569,7 +610,7 @@ export async function persistQueuedMessageBody(
|
|
|
569
610
|
);
|
|
570
611
|
}
|
|
571
612
|
|
|
572
|
-
return persistedUserMessage.id;
|
|
613
|
+
return { id: persistedUserMessage.id, deduplicated: false };
|
|
573
614
|
} catch (err) {
|
|
574
615
|
ctx.messages.pop();
|
|
575
616
|
throw err;
|