@vellumai/assistant 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/Dockerfile +1 -0
- package/bun.lock +11 -2
- package/bunfig.toml +6 -1
- package/docker-entrypoint.sh +8 -6
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +67 -31
- package/examples/plugins/echo/register.ts +4 -7
- package/knip.json +1 -0
- package/node_modules/@vellumai/environments/bun.lock +24 -0
- package/node_modules/@vellumai/environments/package.json +18 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
- package/node_modules/@vellumai/environments/src/index.ts +11 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
- package/node_modules/@vellumai/environments/src/types.ts +70 -0
- package/node_modules/@vellumai/environments/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +15 -17
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +10 -3
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +5585 -469
- package/package.json +7 -3
- package/scripts/generate-openapi.ts +20 -13
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +336 -42
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +21 -33
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
- package/src/__tests__/agent-loop-thinking.test.ts +17 -12
- package/src/__tests__/agent-loop.test.ts +207 -341
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +5 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +23 -40
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/anthropic-provider.test.ts +201 -55
- package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
- package/src/__tests__/app-control-flow.test.ts +5 -0
- package/src/__tests__/approval-cascade.test.ts +5 -11
- package/src/__tests__/approval-routes-http.test.ts +13 -15
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-event.test.ts +15 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -27
- 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__/avatar-e2e.test.ts +7 -37
- package/src/__tests__/avatar-generator.test.ts +12 -42
- package/src/__tests__/avatar-identity-sync.test.ts +28 -3
- package/src/__tests__/background-shell-bash.test.ts +3 -7
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +10 -14
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/call-pointer-messages.test.ts +5 -3
- package/src/__tests__/call-site-routing-provider.test.ts +22 -40
- package/src/__tests__/catalog-files.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +51 -22
- package/src/__tests__/channel-approvals.test.ts +3 -1
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-invite-transport.test.ts +1 -5
- package/src/__tests__/channel-readiness-routes.test.ts +0 -4
- package/src/__tests__/channel-readiness-slack-remote.test.ts +170 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +388 -79
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
- package/src/__tests__/clawhub-files.test.ts +1 -0
- package/src/__tests__/compaction-events.test.ts +6 -17
- package/src/__tests__/compaction-pipeline.test.ts +1 -1
- package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
- package/src/__tests__/compaction-trail-store.test.ts +186 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +14 -16
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +56 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +19 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +4 -2
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -27
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +464 -90
- package/src/__tests__/conversation-agent-loop.test.ts +1069 -64
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -3
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -1
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-clear-safety.test.ts +20 -10
- package/src/__tests__/conversation-confirmation-signals.test.ts +16 -45
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-disk-view.test.ts +10 -17
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +132 -157
- package/src/__tests__/conversation-fork-route.test.ts +19 -16
- package/src/__tests__/conversation-history-web-search.test.ts +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-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +4 -2
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -3
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +87 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +30 -7
- package/src/__tests__/conversation-process-callsite.test.ts +28 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +58 -44
- package/src/__tests__/conversation-queue.test.ts +603 -455
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -20
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +35 -10
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -4
- package/src/__tests__/conversation-runtime-assembly.test.ts +98 -22
- package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +120 -62
- package/src/__tests__/conversation-slash-unknown.test.ts +18 -15
- package/src/__tests__/conversation-speed-override.test.ts +9 -22
- package/src/__tests__/conversation-stream-state.test.ts +484 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +52 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
- package/src/__tests__/conversation-surfaces-table-action.test.ts +13 -32
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +53 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +1 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +53 -22
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +32 -7
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -6
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +61 -3
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +119 -36
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +9 -35
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +33 -42
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-page-surface.test.ts +68 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-pipeline.test.ts +127 -5
- 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 +3 -2
- package/src/__tests__/first-greeting.test.ts +103 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-inline-media.test.ts +78 -0
- package/src/__tests__/gemini-provider.test.ts +375 -26
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +10 -7
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +3 -1
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/history-repair-hook.test.ts +161 -0
- package/src/__tests__/history-repair-observability.test.ts +1 -1
- package/src/__tests__/history-repair.test.ts +2 -1
- package/src/__tests__/host-app-control-proxy.test.ts +2 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-edit-tool.test.ts +4 -2
- package/src/__tests__/host-file-proxy.test.ts +31 -0
- package/src/__tests__/host-file-read-tool.test.ts +4 -2
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-write-tool.test.ts +9 -3
- package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
- package/src/__tests__/host-shell-tool.test.ts +11 -5
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +31 -9
- package/src/__tests__/identity-intro-cache.test.ts +154 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +51 -74
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-background-turn.test.ts +1 -1
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-disk-pressure.test.ts +4 -18
- package/src/__tests__/injector-document-comments.test.ts +1 -1
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
- package/src/__tests__/injector-v3-suppression.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +7 -8
- package/src/__tests__/list-messages-hidden-metadata.test.ts +93 -11
- package/src/__tests__/list-messages-page-latest.test.ts +0 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
- package/src/__tests__/llm-call-pipeline.test.ts +21 -15
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +346 -39
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/llm-usage-store.test.ts +45 -0
- package/src/__tests__/log-export-routes.test.ts +59 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/mcp-health-check.test.ts +18 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
- package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +9 -4
- package/src/__tests__/migration-export-http.test.ts +12 -12
- package/src/__tests__/migration-import-commit-http.test.ts +8 -8
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +44 -22
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +38 -1
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +13 -2
- package/src/__tests__/openai-provider.test.ts +74 -79
- package/src/__tests__/openai-responses-provider.test.ts +90 -86
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +48 -2
- package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
- package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
- package/src/__tests__/persistence-pipeline.test.ts +154 -27
- package/src/__tests__/persistence-secret-redaction.test.ts +85 -13
- package/src/__tests__/pipeline-runner.test.ts +2 -3
- package/src/__tests__/plugin-bootstrap.test.ts +60 -36
- package/src/__tests__/plugin-route-contribution.test.ts +6 -16
- package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +51 -64
- package/src/__tests__/plugin-types.test.ts +7 -14
- package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
- package/src/__tests__/process-message-background-slack.test.ts +38 -32
- package/src/__tests__/process-message-display-content.test.ts +49 -64
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
- package/src/__tests__/provider-error-scenarios.test.ts +7 -6
- package/src/__tests__/provider-platform-proxy-integration.test.ts +215 -8
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
- package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
- package/src/__tests__/provider-usage-tracking.test.ts +0 -6
- package/src/__tests__/ratelimit.test.ts +9 -4
- package/src/__tests__/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 +30 -23
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
- package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
- package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
- package/src/__tests__/schedule-routes.test.ts +80 -10
- package/src/__tests__/schedule-store.test.ts +83 -1
- package/src/__tests__/schedule-tools.test.ts +125 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +7 -3
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
- package/src/__tests__/secret-response-routing.test.ts +13 -11
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +83 -43
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +19 -21
- package/src/__tests__/skill-feature-flags.test.ts +20 -22
- package/src/__tests__/skill-load-feature-flag.test.ts +15 -15
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/starter-task-flow.test.ts +6 -6
- package/src/__tests__/strip-memory-injections.test.ts +102 -14
- package/src/__tests__/subagent-call-site-routing.test.ts +13 -5
- 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 +4 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +92 -0
- package/src/__tests__/terminal-tools.test.ts +3 -24
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/thread-backfill.test.ts +4 -9
- package/src/__tests__/title-generate-pipeline.test.ts +1 -1
- package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
- package/src/__tests__/tool-error-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
- 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 +14 -11
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
- package/src/__tests__/tool-result-truncation.test.ts +3 -1
- package/src/__tests__/tools-audio-read.test.ts +113 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
- package/src/__tests__/turn-events-store.test.ts +11 -7
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +10 -7
- package/src/__tests__/voice-session-bridge.test.ts +50 -35
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
- package/src/acp/prepare-agent-env.ts +52 -11
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/compaction-circuit.ts +140 -0
- package/src/agent/loop.ts +489 -85
- package/src/api/README.md +126 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/constants/tool-execution.ts +21 -0
- package/src/api/events/assistant-activity-state.ts +75 -0
- package/src/api/events/assistant-outbound-attachment.ts +49 -0
- package/src/api/events/assistant-text-delta.ts +30 -0
- package/src/api/events/assistant-turn-start.ts +31 -0
- package/src/api/events/avatar-updated.ts +24 -0
- package/src/api/events/compaction-circuit-closed.ts +26 -0
- package/src/api/events/compaction-circuit-open.ts +28 -0
- package/src/api/events/confirmation-request.ts +114 -0
- package/src/api/events/contact-request.ts +33 -0
- package/src/api/events/conversation-error.ts +77 -0
- package/src/api/events/conversation-list-invalidated.ts +38 -0
- package/src/api/events/conversation-title-updated.ts +24 -0
- package/src/api/events/disk-pressure-status-changed.ts +61 -0
- package/src/api/events/document-comment-created.ts +44 -0
- package/src/api/events/document-comment-deleted.ts +22 -0
- package/src/api/events/document-comment-reopened.ts +23 -0
- package/src/api/events/document-comment-resolved.ts +25 -0
- package/src/api/events/document-editor-update.ts +27 -0
- package/src/api/events/error.ts +32 -0
- package/src/api/events/generation-cancelled.ts +22 -0
- package/src/api/events/generation-handoff.ts +39 -0
- package/src/api/events/home-feed-updated.ts +26 -0
- package/src/api/events/identity-changed.ts +32 -0
- package/src/api/events/interaction-resolved.ts +50 -0
- package/src/api/events/message-complete.ts +40 -0
- package/src/api/events/message-dequeued.ts +21 -0
- package/src/api/events/message-queued-deleted.ts +23 -0
- package/src/api/events/message-queued.ts +22 -0
- package/src/api/events/message-request-complete.ts +29 -0
- package/src/api/events/navigate-settings.ts +20 -0
- package/src/api/events/notification-intent.ts +33 -0
- package/src/api/events/open-url.ts +28 -0
- package/src/api/events/question-request.ts +67 -0
- package/src/{events → api/events}/relationship-state-updated.ts +6 -8
- package/src/api/events/secret-request.ts +42 -0
- package/src/api/events/subagent-event.ts +79 -0
- package/src/api/events/subagent-spawned.ts +40 -0
- package/src/api/events/subagent-status-changed.ts +65 -0
- package/src/api/events/sync-changed.ts +29 -0
- package/src/api/events/tool-result.ts +129 -0
- package/src/api/events/tool-use-start.ts +30 -0
- package/src/api/events/turn-profile-auto-routed.ts +28 -0
- package/src/api/events/ui-surface-complete.ts +30 -0
- package/src/api/events/ui-surface-dismiss.ts +22 -0
- package/src/api/events/ui-surface-show.ts +67 -0
- package/src/api/events/ui-surface-update.ts +26 -0
- package/src/api/events/usage-update.ts +34 -0
- package/src/api/events/user-message-echo.ts +35 -0
- package/src/api/index.ts +482 -3
- package/src/api/requests/dictation.ts +45 -0
- package/src/api/responses/disk-pressure-status.ts +26 -0
- package/src/api/responses/home.ts +217 -0
- package/src/api/responses/llm-context-response.ts +41 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/api/responses/memory-v3-selection-log.ts +50 -0
- package/src/api/responses/subagent-detail.ts +48 -0
- package/src/approvals/guardian-decision-primitive.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +6 -9
- package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
- package/src/avatar/__tests__/avatar-store.test.ts +193 -0
- package/src/avatar/avatar-manifest.ts +195 -0
- package/src/avatar/avatar-store.ts +113 -0
- package/src/avatar/traits-png-sync.ts +8 -2
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +4 -1
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +17 -31
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/conversations.ts +15 -1
- package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
- package/src/cli/commands/db/__tests__/status.test.ts +253 -0
- package/src/cli/commands/db/format.ts +48 -0
- package/src/cli/commands/db/index.ts +29 -0
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
- package/src/cli/commands/db/repair-step-integrity.ts +146 -0
- package/src/cli/commands/db/repair-steps.ts +164 -0
- package/src/cli/commands/db/repair.ts +141 -0
- package/src/cli/commands/db/status.ts +366 -0
- package/src/cli/commands/memory-v3.ts +168 -203
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +6 -5
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/assistant-feature-flags.ts +25 -44
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +2 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +10 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +3 -8
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +42 -26
- 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 +1 -211
- package/src/config/schemas/call-site-catalog.ts +8 -15
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +92 -4
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +0 -227
- package/src/config/schemas/memory-v3.ts +39 -0
- package/src/config/schemas/memory.ts +6 -1
- package/src/config/schemas/services.ts +6 -2
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/compactor.ts +54 -31
- package/src/context/token-estimator.ts +29 -5
- package/src/context/tool-result-truncation.ts +1 -43
- package/src/context/window-manager.ts +138 -20
- 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 +15 -17
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/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 +11 -6
- package/src/daemon/approval-generators.ts +4 -4
- package/src/daemon/config-watcher.ts +7 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +613 -155
- package/src/daemon/conversation-agent-loop.ts +409 -605
- package/src/daemon/conversation-error.ts +40 -12
- package/src/daemon/conversation-history.ts +22 -6
- package/src/daemon/conversation-launch.ts +4 -8
- package/src/daemon/conversation-lifecycle.ts +10 -38
- package/src/daemon/conversation-messaging.ts +83 -44
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +174 -116
- package/src/daemon/conversation-runtime-assembly.ts +76 -30
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-store.ts +6 -5
- package/src/daemon/conversation-surfaces.ts +124 -103
- package/src/daemon/conversation-tool-setup.ts +36 -48
- package/src/daemon/conversation.ts +111 -166
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +7 -4
- package/src/daemon/disk-pressure-guard.ts +54 -50
- package/src/daemon/external-plugins-bootstrap.ts +46 -24
- package/src/daemon/first-greeting.ts +53 -13
- package/src/daemon/guardian-action-generators.ts +2 -2
- package/src/daemon/handlers/conversations.ts +6 -22
- package/src/daemon/handlers/shared.ts +10 -1
- package/src/daemon/handlers/skills.ts +15 -14
- package/src/daemon/host-app-control-proxy.ts +54 -1
- package/src/daemon/host-cu-proxy.ts +46 -22
- package/src/daemon/host-file-proxy.ts +25 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -6
- package/src/daemon/lifecycle.ts +40 -67
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-protocol.ts +2 -3
- package/src/daemon/message-provenance.ts +49 -0
- package/src/daemon/message-types/contacts.ts +3 -20
- package/src/daemon/message-types/conversations.ts +25 -125
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +5 -18
- package/src/daemon/message-types/integrations.ts +4 -13
- package/src/daemon/message-types/messages.ts +47 -377
- package/src/daemon/message-types/notifications.ts +2 -32
- package/src/daemon/message-types/settings.ts +3 -8
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/sync.ts +12 -25
- package/src/daemon/message-types/workspace.ts +3 -11
- package/src/daemon/process-message.ts +58 -55
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +28 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/tool-side-effects.ts +10 -7
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +21 -1
- 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 +31 -0
- package/src/heartbeat/heartbeat-run-store.ts +31 -0
- package/src/heartbeat/heartbeat-service.ts +79 -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/feature-gate.ts +22 -0
- package/src/home/feed-types.ts +36 -221
- 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/__tests__/email-ipc.test.ts +0 -9
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
- package/src/ipc/routes/route-adapter.ts +45 -6
- package/src/ipc/skill-routes/__tests__/memory.test.ts +19 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +59 -20
- package/src/ipc/skill-routes/memory.ts +27 -13
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +39 -88
- package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
- package/src/memory/__tests__/conversation-queries.test.ts +192 -8
- package/src/memory/__tests__/db-maintenance.test.ts +128 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +11 -6
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
- package/src/memory/context-search/agent-runner.ts +2 -4
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +386 -115
- package/src/memory/conversation-queries.ts +78 -22
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +12 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/consolidation.ts +8 -11
- package/src/memory/graph/conversation-graph-memory.ts +41 -8
- package/src/memory/graph/extraction.ts +6 -9
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/graph/retriever.ts +20 -26
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/job-handlers/conversation-starters.ts +32 -32
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-store.ts +3 -1
- package/src/memory/jobs-worker.ts +63 -40
- package/src/memory/llm-request-log-source-clickhouse.ts +55 -1
- package/src/memory/llm-request-log-source-local.ts +13 -0
- package/src/memory/llm-request-log-source.ts +21 -6
- package/src/memory/llm-request-log-store.ts +147 -3
- package/src/memory/llm-usage-store.ts +10 -0
- package/src/memory/memory-marker.ts +17 -0
- package/src/memory/memory-retrospective-job.ts +6 -2
- package/src/memory/memory-v2-activation-log-store.ts +13 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
- package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
- package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
- package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
- package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/schema/infrastructure.ts +11 -0
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/migration.test.ts +11 -3
- package/src/memory/v2/__tests__/page-index.test.ts +37 -1
- package/src/memory/v2/__tests__/router.test.ts +14 -4
- package/src/memory/v2/__tests__/sweep-job.test.ts +9 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +89 -9
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v2/migration.ts +5 -3
- package/src/memory/v2/page-index.ts +11 -0
- package/src/memory/v2/router.ts +8 -11
- package/src/memory/v2/sweep-job.ts +8 -11
- package/src/memory/v2/types.ts +1 -0
- package/src/memory/v3/__tests__/assign.test.ts +242 -0
- package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
- package/src/memory/v3/__tests__/core.test.ts +39 -0
- package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
- package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
- package/src/memory/v3/__tests__/health.test.ts +203 -0
- package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
- package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
- package/src/memory/v3/__tests__/needle.test.ts +107 -0
- package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
- package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
- package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
- package/src/memory/v3/__tests__/router.test.ts +260 -0
- package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
- package/src/memory/v3/__tests__/selector.test.ts +404 -0
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
- package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
- package/src/memory/v3/__tests__/tree.test.ts +192 -0
- package/src/memory/v3/__tests__/types.test.ts +54 -0
- package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
- package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
- package/src/memory/v3/assign.ts +268 -0
- package/src/memory/v3/capabilities.ts +124 -0
- package/src/memory/v3/core.ts +26 -0
- package/src/memory/v3/data/README.md +84 -0
- package/src/memory/v3/data/assignments.json +5 -0
- package/src/memory/v3/data/core.json +1 -0
- package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
- package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
- package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +314 -0
- package/src/memory/v3/needle.ts +115 -0
- package/src/memory/v3/orchestrate.ts +114 -0
- package/src/memory/v3/page-content.ts +34 -0
- package/src/memory/v3/provider-blocks.ts +16 -0
- package/src/memory/v3/reconcile.ts +523 -0
- package/src/memory/v3/render-injection.ts +32 -0
- package/src/memory/v3/router.ts +184 -0
- package/src/memory/v3/selection-log-store.ts +84 -0
- package/src/memory/v3/selector.ts +211 -0
- package/src/memory/v3/shadow-plugin.ts +379 -0
- package/src/memory/v3/snapshot.ts +209 -0
- package/src/memory/v3/tree.ts +174 -0
- package/src/memory/v3/types.ts +46 -60
- package/src/memory/v3/working-set.ts +88 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
- package/src/messaging/providers/slack/render-transcript.ts +2 -2
- package/src/messaging/style-analyzer.ts +8 -11
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +30 -8
- package/src/notifications/decision-engine.ts +10 -13
- 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/preference-extractor.ts +11 -14
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/permissions/prompter.ts +42 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugin-api/index.ts +2 -0
- package/src/plugin-api/types.ts +25 -3
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
- package/src/plugins/defaults/circuit-breaker/package.json +15 -0
- package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
- package/src/plugins/defaults/compaction/package.json +15 -0
- package/src/plugins/defaults/compaction/register.ts +35 -0
- package/src/plugins/defaults/compaction/terminal.ts +73 -0
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
- package/src/plugins/defaults/empty-response/package.json +15 -0
- package/src/plugins/defaults/empty-response/register.ts +28 -0
- package/src/plugins/defaults/empty-response/terminal.ts +106 -0
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/history-repair/package.json +15 -0
- package/src/plugins/defaults/history-repair/register.ts +24 -0
- package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
- package/src/plugins/defaults/index.ts +29 -40
- package/src/plugins/defaults/injectors/package.json +15 -0
- package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +16 -46
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
- package/src/plugins/defaults/llm-call/package.json +15 -0
- package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
- package/src/plugins/defaults/memory-retrieval/package.json +15 -0
- package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
- package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
- package/src/plugins/defaults/overflow-reduce/package.json +15 -0
- package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
- package/src/plugins/defaults/persistence/package.json +15 -0
- package/src/plugins/defaults/persistence/register.ts +38 -0
- package/src/plugins/defaults/persistence/terminal.ts +83 -0
- package/src/plugins/defaults/title-generate/package.json +15 -0
- package/src/plugins/defaults/title-generate/register.ts +35 -0
- package/src/plugins/defaults/title-generate/terminal.ts +31 -0
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
- package/src/plugins/defaults/token-estimate/package.json +15 -0
- package/src/plugins/defaults/token-estimate/register.ts +34 -0
- package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
- package/src/plugins/defaults/tool-error/package.json +15 -0
- package/src/plugins/defaults/tool-error/register.ts +35 -0
- package/src/plugins/defaults/tool-error/terminal.ts +47 -0
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
- package/src/plugins/defaults/tool-execute/package.json +15 -0
- package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
- package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
- package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/pipeline.ts +0 -12
- package/src/plugins/types.ts +107 -102
- package/src/plugins/user-loader.ts +4 -3
- package/src/proactive-artifact/aux-message-injector.ts +0 -1
- package/src/proactive-artifact/job.test.ts +21 -8
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
- package/src/prompts/templates/BOOTSTRAP.md +10 -10
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/prompts/templates/system-sections.ts +6 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -25
- package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
- package/src/providers/anthropic/client.ts +24 -5
- package/src/providers/call-site-routing.ts +34 -18
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +176 -37
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -7
- package/src/providers/openai/chat-completions-provider.ts +111 -16
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +54 -57
- package/src/providers/openrouter/client.ts +14 -14
- package/src/providers/provider-send-message.ts +23 -14
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -9
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +20 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +132 -26
- package/src/runtime/__tests__/background-job-runner.test.ts +2 -3
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/agent-wake.ts +93 -18
- package/src/runtime/assistant-event-hub.ts +2 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
- package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
- package/src/runtime/auth/route-policy.ts +42 -1069
- package/src/runtime/background-job-runner.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -1
- package/src/runtime/channel-approvals.ts +3 -14
- package/src/runtime/channel-invite-transport.ts +5 -6
- package/src/runtime/channel-readiness-service.ts +70 -5
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +59 -30
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/conversation-stream-state.ts +294 -0
- package/src/runtime/http-router.ts +19 -22
- package/src/runtime/http-types.ts +12 -6
- package/src/runtime/invite-instruction-generator.ts +3 -3
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +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 +26 -72
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +58 -5
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
- package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/acp-routes.test.ts +97 -75
- package/src/runtime/routes/acp-routes.ts +29 -6
- package/src/runtime/routes/app-management-routes.ts +208 -28
- package/src/runtime/routes/app-routes.ts +25 -5
- package/src/runtime/routes/approval-routes.ts +16 -4
- package/src/runtime/routes/attachment-routes.ts +25 -1
- package/src/runtime/routes/audio-routes.ts +1 -0
- package/src/runtime/routes/audit-routes.ts +5 -0
- package/src/runtime/routes/auth-routes.ts +5 -0
- package/src/runtime/routes/avatar-routes.ts +238 -59
- package/src/runtime/routes/background-tool-routes.ts +9 -0
- package/src/runtime/routes/background-wake-routes.ts +201 -23
- package/src/runtime/routes/backup-routes.ts +45 -0
- package/src/runtime/routes/bookmark-routes.ts +13 -0
- package/src/runtime/routes/brain-graph-routes.ts +9 -0
- package/src/runtime/routes/browser-routes.ts +5 -0
- package/src/runtime/routes/browser-tabs-routes.ts +5 -0
- package/src/runtime/routes/btw-routes.ts +9 -5
- package/src/runtime/routes/cache-routes.ts +13 -0
- package/src/runtime/routes/call-routes.ts +21 -10
- package/src/runtime/routes/channel-availability-routes.ts +5 -1
- package/src/runtime/routes/channel-readiness-routes.ts +37 -4
- package/src/runtime/routes/channel-route-definitions.ts +21 -0
- package/src/runtime/routes/channel-verification-routes.ts +21 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
- package/src/runtime/routes/client-routes.ts +9 -0
- package/src/runtime/routes/consolidation-routes.ts +13 -5
- package/src/runtime/routes/contact-prompt-routes.ts +9 -0
- package/src/runtime/routes/contact-routes.ts +90 -23
- package/src/runtime/routes/content-source-routes.ts +5 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +11 -1
- package/src/runtime/routes/conversation-attention-routes.ts +5 -0
- package/src/runtime/routes/conversation-cli-routes.ts +54 -7
- package/src/runtime/routes/conversation-compaction-routes.ts +292 -0
- package/src/runtime/routes/conversation-list-routes.ts +225 -9
- package/src/runtime/routes/conversation-management-routes.ts +96 -28
- package/src/runtime/routes/conversation-query-routes.ts +148 -51
- package/src/runtime/routes/conversation-routes.ts +259 -158
- package/src/runtime/routes/conversation-starter-routes.ts +22 -13
- package/src/runtime/routes/conversations-import-routes.ts +25 -7
- package/src/runtime/routes/credential-prompt-routes.ts +5 -0
- package/src/runtime/routes/credential-routes.ts +25 -6
- package/src/runtime/routes/debug-bash-routes.ts +5 -0
- package/src/runtime/routes/debug-routes.ts +11 -2
- package/src/runtime/routes/defer-routes.ts +13 -0
- package/src/runtime/routes/diagnostics-routes.ts +37 -46
- package/src/runtime/routes/disk-pressure-routes.ts +17 -31
- package/src/runtime/routes/document-comments-routes.ts +46 -27
- package/src/runtime/routes/documents-routes.ts +31 -11
- package/src/runtime/routes/domain-routes.ts +61 -28
- package/src/runtime/routes/email-routes.ts +33 -0
- package/src/runtime/routes/events-routes.ts +114 -9
- package/src/runtime/routes/filing-routes.ts +9 -4
- package/src/runtime/routes/gateway-log-routes.ts +5 -0
- package/src/runtime/routes/global-search-routes.ts +53 -50
- package/src/runtime/routes/group-routes.ts +32 -5
- package/src/runtime/routes/guardian-action-routes.ts +9 -0
- package/src/runtime/routes/guardian-approval-interception.ts +0 -31
- package/src/runtime/routes/heartbeat-routes.ts +25 -9
- package/src/runtime/routes/home-feed-routes.ts +149 -16
- package/src/runtime/routes/home-state-routes.ts +8 -40
- package/src/runtime/routes/host-app-control-routes.ts +5 -0
- package/src/runtime/routes/host-bash-routes.ts +5 -0
- package/src/runtime/routes/host-browser-routes.ts +13 -0
- package/src/runtime/routes/host-cu-routes.ts +5 -0
- package/src/runtime/routes/host-file-routes.ts +26 -6
- package/src/runtime/routes/host-transfer-routes.ts +13 -2
- package/src/runtime/routes/http-adapter.ts +1 -2
- package/src/runtime/routes/identity-intro-cache.ts +72 -16
- package/src/runtime/routes/identity-routes.ts +42 -11
- package/src/runtime/routes/image-generation-routes.ts +5 -0
- package/src/runtime/routes/inbound-message-handler.ts +15 -11
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +524 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +72 -27
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +26 -31
- package/src/runtime/routes/inference-send-routes.ts +11 -11
- package/src/runtime/routes/integrations/a2a.ts +30 -7
- package/src/runtime/routes/integrations/slack/channel.ts +19 -3
- package/src/runtime/routes/integrations/slack/share.ts +9 -2
- package/src/runtime/routes/integrations/telegram.ts +28 -9
- package/src/runtime/routes/integrations/twilio.ts +35 -7
- package/src/runtime/routes/integrations/vercel.ts +18 -3
- package/src/runtime/routes/internal-oauth-routes.ts +5 -0
- package/src/runtime/routes/internal-twilio-routes.ts +13 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/log-export-routes.ts +28 -10
- package/src/runtime/routes/mcp-auth-routes.ts +25 -0
- package/src/runtime/routes/memory-item-routes.ts +21 -10
- package/src/runtime/routes/memory-v2-routes.ts +90 -36
- package/src/runtime/routes/memory-v3-routes.ts +283 -259
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +49 -13
- package/src/runtime/routes/notification-routes.ts +80 -2
- package/src/runtime/routes/oauth-apps.ts +33 -11
- package/src/runtime/routes/oauth-commands-routes.ts +43 -15
- package/src/runtime/routes/oauth-connect-routes.ts +9 -0
- package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
- package/src/runtime/routes/oauth-providers.ts +35 -10
- package/src/runtime/routes/platform-routes.ts +21 -0
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
- package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +0 -1
- package/src/runtime/routes/playground/inject-failures.ts +13 -8
- package/src/runtime/routes/playground/reset-circuit.ts +14 -9
- package/src/runtime/routes/playground/seed-conversation.ts +1 -1
- package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
- package/src/runtime/routes/playground/state.ts +4 -3
- package/src/runtime/routes/plugins-routes.ts +22 -19
- package/src/runtime/routes/profiler-routes.ts +17 -4
- package/src/runtime/routes/ps-routes.ts +5 -0
- package/src/runtime/routes/publish-routes.ts +13 -3
- package/src/runtime/routes/question-routes.ts +5 -0
- package/src/runtime/routes/recording-routes.ts +25 -12
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/sanity-routes.ts +9 -2
- package/src/runtime/routes/schedule-routes.ts +137 -47
- package/src/runtime/routes/secret-routes.ts +17 -4
- package/src/runtime/routes/sequence-routes.ts +33 -0
- package/src/runtime/routes/settings-routes.ts +65 -19
- package/src/runtime/routes/skills-routes.ts +133 -69
- package/src/runtime/routes/slack-channel-routes.ts +5 -0
- package/src/runtime/routes/stt-routes.ts +13 -6
- package/src/runtime/routes/subagents-routes.ts +24 -18
- package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
- package/src/runtime/routes/surface-action-routes.ts +10 -38
- package/src/runtime/routes/surface-content-routes.ts +21 -6
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/task-routes.ts +37 -0
- package/src/runtime/routes/telemetry-routes.ts +9 -0
- package/src/runtime/routes/trace-event-routes.ts +42 -1
- package/src/runtime/routes/trust-rules-routes.ts +5 -0
- package/src/runtime/routes/tts-routes.ts +13 -6
- package/src/runtime/routes/types.ts +17 -8
- package/src/runtime/routes/ui-request-routes.ts +5 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
- package/src/runtime/routes/usage-routes.ts +71 -3
- package/src/runtime/routes/user-routes-cli.ts +9 -0
- package/src/runtime/routes/user-routes.ts +5 -1
- package/src/runtime/routes/wake-conversation-routes.ts +5 -0
- package/src/runtime/routes/watcher-routes.ts +21 -0
- package/src/runtime/routes/webhook-routes.ts +9 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +8 -0
- package/src/runtime/routes/work-items-routes.ts +47 -19
- package/src/runtime/routes/workspace-commit-routes.ts +5 -0
- package/src/runtime/routes/workspace-routes.test.ts +42 -0
- package/src/runtime/routes/workspace-routes.ts +120 -9
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +4 -4
- package/src/runtime/services/analyze-conversation.ts +3 -6
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +17 -3
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +16 -1
- package/src/schedule/scheduler.ts +114 -16
- 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 +10 -16
- package/src/skills/catalog-files.ts +4 -1
- package/src/skills/clawhub-files.ts +2 -0
- package/src/skills/skillssh-files.ts +2 -0
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +16 -19
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/telemetry/types.ts +26 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
- package/src/telemetry/usage-telemetry-reporter.ts +31 -0
- package/src/tools/acp/spawn.test.ts +88 -38
- package/src/tools/apps/definitions.ts +42 -24
- package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
- package/src/tools/ask-question/ask-question-tool.ts +85 -90
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +295 -289
- package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
- package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
- package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
- package/src/tools/credentials/vault.ts +112 -111
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +3 -6
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/executor.ts +18 -55
- package/src/tools/filesystem/edit.ts +45 -42
- package/src/tools/filesystem/list.ts +33 -30
- package/src/tools/filesystem/read.ts +54 -35
- package/src/tools/filesystem/write.ts +34 -31
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-fetch.ts +49 -46
- package/src/tools/network/web-search.ts +215 -57
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +184 -118
- package/src/tools/schedule/create.ts +12 -1
- package/src/tools/schedule/update.ts +16 -0
- package/src/tools/shared/filesystem/audio-read.ts +122 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/execute.ts +34 -31
- package/src/tools/skills/load.ts +29 -23
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/system/avatar-generator.ts +13 -22
- package/src/tools/system/request-permission.ts +30 -27
- package/src/tools/terminal/shell.ts +190 -61
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +86 -33
- package/src/tools/ui-surface/definitions.ts +166 -94
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/usage/types.ts +10 -0
- package/src/util/browser.ts +7 -2
- package/src/util/errors.ts +2 -2
- package/src/util/map-limit.ts +27 -0
- package/src/util/platform.ts +15 -12
- package/src/work-items/work-item-runner.ts +7 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
- package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
- package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
- package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
- package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +15 -17
- package/tsconfig.json +4 -1
- package/src/__tests__/history-repair-pipeline.test.ts +0 -396
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
- package/src/cli/commands/memory-v3-render.ts +0 -344
- package/src/daemon/message-types/disk-pressure.ts +0 -9
- package/src/email/feature-gate.ts +0 -23
- package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
- package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -468
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -563
- package/src/memory/v3/__tests__/filter.test.ts +0 -512
- package/src/memory/v3/__tests__/gate.test.ts +0 -574
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -233
- package/src/memory/v3/__tests__/loop.test.ts +0 -530
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -440
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -312
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -469
- package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
- package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
- package/src/memory/v3/__tests__/tree-walk.test.ts +0 -707
- package/src/memory/v3/__tests__/validate.test.ts +0 -245
- package/src/memory/v3/auto-edges.ts +0 -223
- package/src/memory/v3/coactivation-store.ts +0 -124
- package/src/memory/v3/consolidation-job.ts +0 -323
- package/src/memory/v3/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -249
- package/src/memory/v3/filter.ts +0 -281
- package/src/memory/v3/gate.ts +0 -334
- package/src/memory/v3/index-composition.ts +0 -113
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -382
- package/src/memory/v3/maintenance.ts +0 -144
- package/src/memory/v3/prompt-context.ts +0 -33
- package/src/memory/v3/prompts/consolidation.ts +0 -458
- package/src/memory/v3/prompts/system-prompts.ts +0 -196
- package/src/memory/v3/retriever.ts +0 -33
- package/src/memory/v3/scouts.ts +0 -420
- package/src/memory/v3/shadow-middleware.ts +0 -305
- package/src/memory/v3/traversal.ts +0 -206
- package/src/memory/v3/tree-index.ts +0 -237
- package/src/memory/v3/tree-store.ts +0 -394
- package/src/memory/v3/tree-walk.ts +0 -351
- package/src/memory/v3/validate.ts +0 -300
- package/src/plugins/defaults/circuit-breaker.ts +0 -141
- package/src/plugins/defaults/compaction.ts +0 -141
- package/src/plugins/defaults/empty-response.ts +0 -124
- package/src/plugins/defaults/history-repair.ts +0 -83
- package/src/plugins/defaults/persistence.ts +0 -127
- package/src/plugins/defaults/title-generate.ts +0 -90
- package/src/plugins/defaults/token-estimate.ts +0 -101
- package/src/plugins/defaults/tool-error.ts +0 -119
- package/src/plugins/defaults/tool-result-truncate.ts +0 -84
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
3
3
|
|
|
4
|
+
import { CompactionCircuit } from "../agent/compaction-circuit.js";
|
|
4
5
|
import type {
|
|
5
6
|
AgentEvent,
|
|
7
|
+
AgentLoopRunResult,
|
|
6
8
|
CheckpointDecision,
|
|
7
9
|
CheckpointInfo,
|
|
10
|
+
ExitReason,
|
|
8
11
|
} from "../agent/loop.js";
|
|
9
12
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
10
13
|
import type { Message, ProviderResponse } from "../providers/types.js";
|
|
@@ -175,7 +178,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
175
178
|
_convId: string,
|
|
176
179
|
role: string,
|
|
177
180
|
content: string,
|
|
178
|
-
metadata?: Record<string, unknown
|
|
181
|
+
options?: { metadata?: Record<string, unknown> },
|
|
179
182
|
) => {
|
|
180
183
|
// Simulate a persist failure for tests that need to exercise the
|
|
181
184
|
// tail-persist-failed path in drainBatch. Triggered by matching any
|
|
@@ -186,13 +189,20 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
const id = `msg-${Date.now()}-${capturedAddMessages.length}`;
|
|
189
|
-
capturedAddMessages.push({
|
|
192
|
+
capturedAddMessages.push({
|
|
193
|
+
id,
|
|
194
|
+
role,
|
|
195
|
+
content,
|
|
196
|
+
metadata: options?.metadata,
|
|
197
|
+
});
|
|
190
198
|
return { id };
|
|
191
199
|
},
|
|
192
200
|
updateConversationUsage: () => {},
|
|
193
201
|
updateConversationTitle: () => {},
|
|
194
202
|
getMessageById: () => null,
|
|
195
203
|
getLastUserTimestampBefore: () => 0,
|
|
204
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
205
|
+
updateMessageContent: mock(() => {}),
|
|
196
206
|
}));
|
|
197
207
|
|
|
198
208
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -315,16 +325,24 @@ interface PendingRun {
|
|
|
315
325
|
resolve: (history: Message[]) => void;
|
|
316
326
|
reject: (err: Error) => void;
|
|
317
327
|
messages: Message[];
|
|
318
|
-
onEvent: (event: AgentEvent) => void
|
|
328
|
+
onEvent: (event: AgentEvent) => void | Promise<void>;
|
|
319
329
|
onCheckpoint?: (
|
|
320
330
|
checkpoint: CheckpointInfo,
|
|
321
331
|
) => CheckpointDecision | Promise<CheckpointDecision>;
|
|
332
|
+
/**
|
|
333
|
+
* Pause-reason recorded from the most recent `onCheckpoint` call, mirroring
|
|
334
|
+
* how the production loop carries it back via {@link AgentLoopRunResult}.
|
|
335
|
+
* `resolve(history)` packages this into the run result so the orchestrator
|
|
336
|
+
* derives its handoff bookkeeping the same way it does against the real loop.
|
|
337
|
+
*/
|
|
338
|
+
exitReason: ExitReason | null;
|
|
322
339
|
}
|
|
323
340
|
|
|
324
341
|
let pendingRuns: PendingRun[] = [];
|
|
325
342
|
|
|
326
343
|
mock.module("../agent/loop.js", () => ({
|
|
327
344
|
AgentLoop: class {
|
|
345
|
+
compactionCircuit = new CompactionCircuit("test-conv");
|
|
328
346
|
constructor() {}
|
|
329
347
|
getToolTokenBudget() {
|
|
330
348
|
return 0;
|
|
@@ -337,15 +355,33 @@ mock.module("../agent/loop.js", () => ({
|
|
|
337
355
|
}
|
|
338
356
|
async run(
|
|
339
357
|
messages: Message[],
|
|
340
|
-
onEvent: (event: AgentEvent) => void
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
): Promise<
|
|
347
|
-
return new Promise<
|
|
348
|
-
|
|
358
|
+
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
359
|
+
options?: {
|
|
360
|
+
onCheckpoint?: (
|
|
361
|
+
checkpoint: CheckpointInfo,
|
|
362
|
+
) => CheckpointDecision | Promise<CheckpointDecision>;
|
|
363
|
+
},
|
|
364
|
+
): Promise<AgentLoopRunResult> {
|
|
365
|
+
return new Promise<AgentLoopRunResult>((resolveResult, reject) => {
|
|
366
|
+
const pending: PendingRun = {
|
|
367
|
+
resolve: (history: Message[]) =>
|
|
368
|
+
resolveResult({
|
|
369
|
+
history,
|
|
370
|
+
exitReason: pending.exitReason,
|
|
371
|
+
}),
|
|
372
|
+
reject,
|
|
373
|
+
messages,
|
|
374
|
+
onEvent,
|
|
375
|
+
exitReason: null,
|
|
376
|
+
onCheckpoint: options?.onCheckpoint
|
|
377
|
+
? async (checkpoint) => {
|
|
378
|
+
const decision = await options.onCheckpoint!(checkpoint);
|
|
379
|
+
pending.exitReason = decision === "continue" ? null : decision;
|
|
380
|
+
return decision;
|
|
381
|
+
}
|
|
382
|
+
: undefined,
|
|
383
|
+
};
|
|
384
|
+
pendingRuns.push(pending);
|
|
349
385
|
});
|
|
350
386
|
}
|
|
351
387
|
},
|
|
@@ -409,9 +445,9 @@ function makeConversation(
|
|
|
409
445
|
"conv-1",
|
|
410
446
|
provider,
|
|
411
447
|
"system prompt",
|
|
412
|
-
4096,
|
|
413
448
|
sendToClient ?? (() => {}),
|
|
414
449
|
"/tmp",
|
|
450
|
+
{ maxTokens: 4096 },
|
|
415
451
|
);
|
|
416
452
|
const conversationWithWorkspaceDeps =
|
|
417
453
|
conversationObj as ConversationWithWorkspaceDeps;
|
|
@@ -471,7 +507,7 @@ async function waitForCondition(
|
|
|
471
507
|
* that `runAgentLoop` expects (usage + message_complete) so the conversation
|
|
472
508
|
* cleanly transitions out of its processing state.
|
|
473
509
|
*/
|
|
474
|
-
function resolveRun(index: number) {
|
|
510
|
+
async function resolveRun(index: number) {
|
|
475
511
|
const run = pendingRuns[index];
|
|
476
512
|
if (!run) throw new Error(`No pending run at index ${index}`);
|
|
477
513
|
// Emit the events runAgentLoop expects
|
|
@@ -479,14 +515,17 @@ function resolveRun(index: number) {
|
|
|
479
515
|
role: "assistant",
|
|
480
516
|
content: [{ type: "text", text: `reply-${index}` }],
|
|
481
517
|
};
|
|
482
|
-
|
|
518
|
+
// Prime the assistant row anchor — production code emits this from
|
|
519
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
520
|
+
await run.onEvent({ type: "llm_call_started" });
|
|
521
|
+
await run.onEvent({
|
|
483
522
|
type: "usage",
|
|
484
523
|
inputTokens: 10,
|
|
485
524
|
outputTokens: 5,
|
|
486
525
|
model: "mock",
|
|
487
526
|
providerDurationMs: 100,
|
|
488
527
|
});
|
|
489
|
-
run.onEvent({ type: "message_complete", message: assistantMsg });
|
|
528
|
+
await run.onEvent({ type: "message_complete", message: assistantMsg });
|
|
490
529
|
// Return updated history with the assistant message appended
|
|
491
530
|
run.resolve([...run.messages, assistantMsg]);
|
|
492
531
|
}
|
|
@@ -519,12 +558,12 @@ describe("Conversation message queue", () => {
|
|
|
519
558
|
const events2: ServerMessage[] = [];
|
|
520
559
|
|
|
521
560
|
// Start first message — this will block on AgentLoop.run
|
|
522
|
-
const p1 = conversation.processMessage(
|
|
523
|
-
"msg-1",
|
|
524
|
-
[],
|
|
525
|
-
(e) => events1.push(e),
|
|
526
|
-
"req-1",
|
|
527
|
-
);
|
|
561
|
+
const p1 = conversation.processMessage({
|
|
562
|
+
content: "msg-1",
|
|
563
|
+
attachments: [],
|
|
564
|
+
onEvent: (e) => events1.push(e),
|
|
565
|
+
requestId: "req-1",
|
|
566
|
+
});
|
|
528
567
|
|
|
529
568
|
// Wait for the first AgentLoop.run to be registered
|
|
530
569
|
await waitForPendingRun(1);
|
|
@@ -533,18 +572,17 @@ describe("Conversation message queue", () => {
|
|
|
533
572
|
expect(conversation.isProcessing()).toBe(true);
|
|
534
573
|
|
|
535
574
|
// Enqueue second message — should NOT throw
|
|
536
|
-
const result = conversation.enqueueMessage(
|
|
537
|
-
"msg-2",
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
);
|
|
575
|
+
const result = conversation.enqueueMessage({
|
|
576
|
+
content: "msg-2",
|
|
577
|
+
onEvent: (e) => events2.push(e),
|
|
578
|
+
requestId: "req-2",
|
|
579
|
+
});
|
|
542
580
|
expect(result.queued).toBe(true);
|
|
543
581
|
expect(result.requestId).toBe("req-2");
|
|
544
582
|
expect(conversation.getQueueDepth()).toBe(1);
|
|
545
583
|
|
|
546
584
|
// Complete the first message
|
|
547
|
-
resolveRun(0);
|
|
585
|
+
await resolveRun(0);
|
|
548
586
|
await p1;
|
|
549
587
|
|
|
550
588
|
// After the first run resolves, the queue drains and triggers a second run.
|
|
@@ -557,7 +595,7 @@ describe("Conversation message queue", () => {
|
|
|
557
595
|
expect(pendingRuns.length).toBe(2);
|
|
558
596
|
|
|
559
597
|
// Complete the second run
|
|
560
|
-
resolveRun(1);
|
|
598
|
+
await resolveRun(1);
|
|
561
599
|
await new Promise((r) => setTimeout(r, 10));
|
|
562
600
|
});
|
|
563
601
|
|
|
@@ -570,21 +608,29 @@ describe("Conversation message queue", () => {
|
|
|
570
608
|
const events3: ServerMessage[] = [];
|
|
571
609
|
|
|
572
610
|
// Start first message
|
|
573
|
-
const p1 = conversation.processMessage(
|
|
574
|
-
"msg-1",
|
|
575
|
-
[],
|
|
576
|
-
(e) => events1.push(e),
|
|
577
|
-
"req-1",
|
|
578
|
-
);
|
|
611
|
+
const p1 = conversation.processMessage({
|
|
612
|
+
content: "msg-1",
|
|
613
|
+
attachments: [],
|
|
614
|
+
onEvent: (e) => events1.push(e),
|
|
615
|
+
requestId: "req-1",
|
|
616
|
+
});
|
|
579
617
|
await waitForPendingRun(1);
|
|
580
618
|
|
|
581
619
|
// Enqueue two more sibling passthrough messages
|
|
582
|
-
conversation.enqueueMessage(
|
|
583
|
-
|
|
620
|
+
conversation.enqueueMessage({
|
|
621
|
+
content: "msg-2",
|
|
622
|
+
onEvent: (e) => events2.push(e),
|
|
623
|
+
requestId: "req-2",
|
|
624
|
+
});
|
|
625
|
+
conversation.enqueueMessage({
|
|
626
|
+
content: "msg-3",
|
|
627
|
+
onEvent: (e) => events3.push(e),
|
|
628
|
+
requestId: "req-3",
|
|
629
|
+
});
|
|
584
630
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
585
631
|
|
|
586
632
|
// Complete run 0 → drain pulls msg-2 and msg-3 into ONE batched run.
|
|
587
|
-
resolveRun(0);
|
|
633
|
+
await resolveRun(0);
|
|
588
634
|
await p1;
|
|
589
635
|
await waitForPendingRun(2);
|
|
590
636
|
|
|
@@ -623,7 +669,7 @@ describe("Conversation message queue", () => {
|
|
|
623
669
|
expect(combinedUserText).toContain("msg-3");
|
|
624
670
|
|
|
625
671
|
// Resolve the batched run; message_complete must fan out to both clients.
|
|
626
|
-
resolveRun(1);
|
|
672
|
+
await resolveRun(1);
|
|
627
673
|
await new Promise((r) => setTimeout(r, 10));
|
|
628
674
|
|
|
629
675
|
expect(events2.some((e) => e.type === "message_complete")).toBe(true);
|
|
@@ -637,20 +683,23 @@ describe("Conversation message queue", () => {
|
|
|
637
683
|
const events2: ServerMessage[] = [];
|
|
638
684
|
|
|
639
685
|
// Start first message
|
|
640
|
-
const p1 = conversation.processMessage(
|
|
686
|
+
const p1 = conversation.processMessage({
|
|
687
|
+
content: "msg-1",
|
|
688
|
+
attachments: [],
|
|
689
|
+
requestId: "req-1",
|
|
690
|
+
});
|
|
641
691
|
await waitForPendingRun(1);
|
|
642
692
|
|
|
643
693
|
// Enqueue second — simulating what handleUserMessage does
|
|
644
|
-
const result = conversation.enqueueMessage(
|
|
645
|
-
"msg-2",
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
);
|
|
694
|
+
const result = conversation.enqueueMessage({
|
|
695
|
+
content: "msg-2",
|
|
696
|
+
onEvent: (e) => events2.push(e),
|
|
697
|
+
requestId: "req-2",
|
|
698
|
+
});
|
|
650
699
|
expect(result.queued).toBe(true);
|
|
651
700
|
|
|
652
701
|
// Complete first
|
|
653
|
-
resolveRun(0);
|
|
702
|
+
await resolveRun(0);
|
|
654
703
|
await p1;
|
|
655
704
|
await waitForPendingRun(2);
|
|
656
705
|
|
|
@@ -664,7 +713,7 @@ describe("Conversation message queue", () => {
|
|
|
664
713
|
});
|
|
665
714
|
|
|
666
715
|
// Complete second run so the conversation finishes cleanly
|
|
667
|
-
resolveRun(1);
|
|
716
|
+
await resolveRun(1);
|
|
668
717
|
await new Promise((r) => setTimeout(r, 10));
|
|
669
718
|
});
|
|
670
719
|
|
|
@@ -676,12 +725,24 @@ describe("Conversation message queue", () => {
|
|
|
676
725
|
const events3: ServerMessage[] = [];
|
|
677
726
|
|
|
678
727
|
// Start first message
|
|
679
|
-
conversation.processMessage(
|
|
728
|
+
conversation.processMessage({
|
|
729
|
+
content: "msg-1",
|
|
730
|
+
attachments: [],
|
|
731
|
+
requestId: "req-1",
|
|
732
|
+
});
|
|
680
733
|
await waitForPendingRun(1);
|
|
681
734
|
|
|
682
735
|
// Enqueue two more
|
|
683
|
-
conversation.enqueueMessage(
|
|
684
|
-
|
|
736
|
+
conversation.enqueueMessage({
|
|
737
|
+
content: "msg-2",
|
|
738
|
+
onEvent: (e) => events2.push(e),
|
|
739
|
+
requestId: "req-2",
|
|
740
|
+
});
|
|
741
|
+
conversation.enqueueMessage({
|
|
742
|
+
content: "msg-3",
|
|
743
|
+
onEvent: (e) => events3.push(e),
|
|
744
|
+
requestId: "req-3",
|
|
745
|
+
});
|
|
685
746
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
686
747
|
|
|
687
748
|
// Abort
|
|
@@ -727,12 +788,12 @@ describe("Conversation message queue", () => {
|
|
|
727
788
|
const events: ServerMessage[] = [];
|
|
728
789
|
|
|
729
790
|
// Start a message — blocks on AgentLoop.run
|
|
730
|
-
const p1 = conversation.processMessage(
|
|
731
|
-
"msg-1",
|
|
732
|
-
[],
|
|
733
|
-
(e) => events.push(e),
|
|
734
|
-
"req-1",
|
|
735
|
-
);
|
|
791
|
+
const p1 = conversation.processMessage({
|
|
792
|
+
content: "msg-1",
|
|
793
|
+
attachments: [],
|
|
794
|
+
onEvent: (e) => events.push(e),
|
|
795
|
+
requestId: "req-1",
|
|
796
|
+
});
|
|
736
797
|
await waitForPendingRun(1);
|
|
737
798
|
|
|
738
799
|
// Reject the AgentLoop.run() with a provider error to trigger the
|
|
@@ -754,23 +815,27 @@ describe("Conversation message queue", () => {
|
|
|
754
815
|
await conversation.loadFromDb();
|
|
755
816
|
|
|
756
817
|
// Start first message
|
|
757
|
-
const p1 = conversation.processMessage(
|
|
818
|
+
const p1 = conversation.processMessage({
|
|
819
|
+
content: "msg-1",
|
|
820
|
+
attachments: [],
|
|
821
|
+
requestId: "req-1",
|
|
822
|
+
});
|
|
758
823
|
await waitForPendingRun(1);
|
|
759
824
|
|
|
760
825
|
expect(conversation.getQueueDepth()).toBe(0);
|
|
761
826
|
|
|
762
|
-
conversation.enqueueMessage("msg-2",
|
|
827
|
+
conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
|
|
763
828
|
expect(conversation.getQueueDepth()).toBe(1);
|
|
764
829
|
|
|
765
|
-
conversation.enqueueMessage("msg-3",
|
|
830
|
+
conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
|
|
766
831
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
767
832
|
|
|
768
|
-
conversation.enqueueMessage("msg-4",
|
|
833
|
+
conversation.enqueueMessage({ content: "msg-4", requestId: "req-4" });
|
|
769
834
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
770
835
|
|
|
771
836
|
// Complete first → drain pulls all three same-interface passthroughs
|
|
772
837
|
// into a single batched run (depth → 0, runs → 2 total).
|
|
773
|
-
resolveRun(0);
|
|
838
|
+
await resolveRun(0);
|
|
774
839
|
await p1;
|
|
775
840
|
await waitForPendingRun(2);
|
|
776
841
|
|
|
@@ -778,7 +843,7 @@ describe("Conversation message queue", () => {
|
|
|
778
843
|
expect(pendingRuns.length).toBe(2);
|
|
779
844
|
|
|
780
845
|
// Complete the batched run; conversation finishes cleanly.
|
|
781
|
-
resolveRun(1);
|
|
846
|
+
await resolveRun(1);
|
|
782
847
|
await new Promise((r) => setTimeout(r, 10));
|
|
783
848
|
});
|
|
784
849
|
|
|
@@ -791,23 +856,31 @@ describe("Conversation message queue", () => {
|
|
|
791
856
|
const events3: ServerMessage[] = [];
|
|
792
857
|
|
|
793
858
|
// Start first message — blocks on AgentLoop.run
|
|
794
|
-
const p1 = conversation.processMessage(
|
|
795
|
-
"msg-1",
|
|
796
|
-
[],
|
|
797
|
-
(e) => events1.push(e),
|
|
798
|
-
"req-1",
|
|
799
|
-
);
|
|
859
|
+
const p1 = conversation.processMessage({
|
|
860
|
+
content: "msg-1",
|
|
861
|
+
attachments: [],
|
|
862
|
+
onEvent: (e) => events1.push(e),
|
|
863
|
+
requestId: "req-1",
|
|
864
|
+
});
|
|
800
865
|
await waitForPendingRun(1);
|
|
801
866
|
|
|
802
867
|
// Enqueue a message with empty content (will fail persistUserMessage)
|
|
803
|
-
conversation.enqueueMessage(
|
|
868
|
+
conversation.enqueueMessage({
|
|
869
|
+
content: "",
|
|
870
|
+
onEvent: (e) => events2.push(e),
|
|
871
|
+
requestId: "req-2",
|
|
872
|
+
});
|
|
804
873
|
// Enqueue a valid message after the bad one
|
|
805
|
-
conversation.enqueueMessage(
|
|
874
|
+
conversation.enqueueMessage({
|
|
875
|
+
content: "msg-3",
|
|
876
|
+
onEvent: (e) => events3.push(e),
|
|
877
|
+
requestId: "req-3",
|
|
878
|
+
});
|
|
806
879
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
807
880
|
|
|
808
881
|
// Complete first message — triggers drain. The empty message should fail
|
|
809
882
|
// to persist, but the drain should continue to msg-3.
|
|
810
|
-
resolveRun(0);
|
|
883
|
+
await resolveRun(0);
|
|
811
884
|
await p1;
|
|
812
885
|
|
|
813
886
|
// msg-3 should have been dequeued and started a new AgentLoop.run
|
|
@@ -824,7 +897,7 @@ describe("Conversation message queue", () => {
|
|
|
824
897
|
expect(events3.some((e) => e.type === "message_dequeued")).toBe(true);
|
|
825
898
|
|
|
826
899
|
// Complete the third message's run
|
|
827
|
-
resolveRun(1);
|
|
900
|
+
await resolveRun(1);
|
|
828
901
|
await new Promise((r) => setTimeout(r, 10));
|
|
829
902
|
|
|
830
903
|
// msg-3 should have completed successfully
|
|
@@ -851,7 +924,11 @@ describe("Batched drain", () => {
|
|
|
851
924
|
const events5: ServerMessage[] = [];
|
|
852
925
|
|
|
853
926
|
// Start in-flight message (msg-1)
|
|
854
|
-
const p1 = conversation.processMessage(
|
|
927
|
+
const p1 = conversation.processMessage({
|
|
928
|
+
content: "msg-1",
|
|
929
|
+
attachments: [],
|
|
930
|
+
requestId: "req-1",
|
|
931
|
+
});
|
|
855
932
|
await waitForPendingRun(1);
|
|
856
933
|
|
|
857
934
|
// Enqueue 4 messages with interfaces [macos, macos, cli, macos].
|
|
@@ -860,46 +937,34 @@ describe("Batched drain", () => {
|
|
|
860
937
|
userMessageInterface: iface,
|
|
861
938
|
assistantMessageInterface: iface,
|
|
862
939
|
});
|
|
863
|
-
conversation.enqueueMessage(
|
|
864
|
-
"msg-2",
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
"
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
"
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
"
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
meta("
|
|
880
|
-
);
|
|
881
|
-
conversation.enqueueMessage(
|
|
882
|
-
"msg-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
"
|
|
886
|
-
|
|
887
|
-
undefined,
|
|
888
|
-
meta("cli"),
|
|
889
|
-
);
|
|
890
|
-
conversation.enqueueMessage(
|
|
891
|
-
"msg-5",
|
|
892
|
-
[],
|
|
893
|
-
(e) => events5.push(e),
|
|
894
|
-
"req-5",
|
|
895
|
-
undefined,
|
|
896
|
-
undefined,
|
|
897
|
-
meta("macos"),
|
|
898
|
-
);
|
|
940
|
+
conversation.enqueueMessage({
|
|
941
|
+
content: "msg-2",
|
|
942
|
+
onEvent: (e) => events2.push(e),
|
|
943
|
+
requestId: "req-2",
|
|
944
|
+
metadata: meta("macos"),
|
|
945
|
+
});
|
|
946
|
+
conversation.enqueueMessage({
|
|
947
|
+
content: "msg-3",
|
|
948
|
+
onEvent: (e) => events3.push(e),
|
|
949
|
+
requestId: "req-3",
|
|
950
|
+
metadata: meta("macos"),
|
|
951
|
+
});
|
|
952
|
+
conversation.enqueueMessage({
|
|
953
|
+
content: "msg-4",
|
|
954
|
+
onEvent: (e) => events4.push(e),
|
|
955
|
+
requestId: "req-4",
|
|
956
|
+
metadata: meta("cli"),
|
|
957
|
+
});
|
|
958
|
+
conversation.enqueueMessage({
|
|
959
|
+
content: "msg-5",
|
|
960
|
+
onEvent: (e) => events5.push(e),
|
|
961
|
+
requestId: "req-5",
|
|
962
|
+
metadata: meta("macos"),
|
|
963
|
+
});
|
|
899
964
|
expect(conversation.getQueueDepth()).toBe(4);
|
|
900
965
|
|
|
901
966
|
// Resolve msg-1 → batched run pulls macos msg-2 + msg-3.
|
|
902
|
-
resolveRun(0);
|
|
967
|
+
await resolveRun(0);
|
|
903
968
|
await p1;
|
|
904
969
|
await waitForPendingRun(2);
|
|
905
970
|
|
|
@@ -927,7 +992,7 @@ describe("Batched drain", () => {
|
|
|
927
992
|
);
|
|
928
993
|
|
|
929
994
|
// Resolve the batched run → drain pulls the cli single-message run.
|
|
930
|
-
resolveRun(1);
|
|
995
|
+
await resolveRun(1);
|
|
931
996
|
await waitForPendingRun(3);
|
|
932
997
|
|
|
933
998
|
// cli run contains msg-4 as a single-message run.
|
|
@@ -942,7 +1007,7 @@ describe("Batched drain", () => {
|
|
|
942
1007
|
);
|
|
943
1008
|
|
|
944
1009
|
// Resolve the cli run → drain pulls the final macos single-message run.
|
|
945
|
-
resolveRun(2);
|
|
1010
|
+
await resolveRun(2);
|
|
946
1011
|
await waitForPendingRun(4);
|
|
947
1012
|
const finalHistory = pendingRuns[3].messages;
|
|
948
1013
|
const finalUserText = finalHistory
|
|
@@ -957,7 +1022,7 @@ describe("Batched drain", () => {
|
|
|
957
1022
|
// Four total runs: msg-1, batched [msg-2, msg-3], msg-4, msg-5.
|
|
958
1023
|
expect(pendingRuns.length).toBe(4);
|
|
959
1024
|
|
|
960
|
-
resolveRun(3);
|
|
1025
|
+
await resolveRun(3);
|
|
961
1026
|
await new Promise((r) => setTimeout(r, 10));
|
|
962
1027
|
});
|
|
963
1028
|
|
|
@@ -970,36 +1035,37 @@ describe("Batched drain", () => {
|
|
|
970
1035
|
const eventsWorld: ServerMessage[] = [];
|
|
971
1036
|
|
|
972
1037
|
// Start in-flight message
|
|
973
|
-
const p1 = conversation.processMessage(
|
|
1038
|
+
const p1 = conversation.processMessage({
|
|
1039
|
+
content: "msg-1",
|
|
1040
|
+
attachments: [],
|
|
1041
|
+
requestId: "req-1",
|
|
1042
|
+
});
|
|
974
1043
|
await waitForPendingRun(1);
|
|
975
1044
|
|
|
976
1045
|
// Enqueue ["hello", "/compact", "world"]. /compact resolves to a non-
|
|
977
1046
|
// passthrough slash, so the batch builder stops at "hello" (length 1),
|
|
978
1047
|
// then /compact takes the single-message /compact short-circuit path
|
|
979
1048
|
// (no new runAgentLoop invocation), then "world" drains as its own run.
|
|
980
|
-
conversation.enqueueMessage(
|
|
981
|
-
"hello",
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
"world",
|
|
994
|
-
|
|
995
|
-
(e) => eventsWorld.push(e),
|
|
996
|
-
"req-world",
|
|
997
|
-
);
|
|
1049
|
+
conversation.enqueueMessage({
|
|
1050
|
+
content: "hello",
|
|
1051
|
+
onEvent: (e) => eventsHello.push(e),
|
|
1052
|
+
requestId: "req-hello",
|
|
1053
|
+
});
|
|
1054
|
+
conversation.enqueueMessage({
|
|
1055
|
+
content: "/compact",
|
|
1056
|
+
onEvent: (e) => eventsSlash.push(e),
|
|
1057
|
+
requestId: "req-slash",
|
|
1058
|
+
});
|
|
1059
|
+
conversation.enqueueMessage({
|
|
1060
|
+
content: "world",
|
|
1061
|
+
onEvent: (e) => eventsWorld.push(e),
|
|
1062
|
+
requestId: "req-world",
|
|
1063
|
+
});
|
|
998
1064
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
999
1065
|
|
|
1000
1066
|
// Resolve msg-1 → drain pulls "hello" as its own run (batch stops at
|
|
1001
1067
|
// /compact boundary).
|
|
1002
|
-
resolveRun(0);
|
|
1068
|
+
await resolveRun(0);
|
|
1003
1069
|
await p1;
|
|
1004
1070
|
await waitForPendingRun(2);
|
|
1005
1071
|
|
|
@@ -1010,7 +1076,7 @@ describe("Batched drain", () => {
|
|
|
1010
1076
|
|
|
1011
1077
|
// Resolve "hello" → drain pops /compact via the builder-rejected path,
|
|
1012
1078
|
// runs its short-circuit (no new runAgentLoop), then drains "world".
|
|
1013
|
-
resolveRun(1);
|
|
1079
|
+
await resolveRun(1);
|
|
1014
1080
|
await waitForPendingRun(3);
|
|
1015
1081
|
|
|
1016
1082
|
// /compact should have emitted its own message_complete via the short-
|
|
@@ -1019,7 +1085,7 @@ describe("Batched drain", () => {
|
|
|
1019
1085
|
expect(eventsWorld.some((e) => e.type === "message_dequeued")).toBe(true);
|
|
1020
1086
|
expect(pendingRuns.length).toBe(3);
|
|
1021
1087
|
|
|
1022
|
-
resolveRun(2);
|
|
1088
|
+
await resolveRun(2);
|
|
1023
1089
|
await new Promise((r) => setTimeout(r, 10));
|
|
1024
1090
|
});
|
|
1025
1091
|
|
|
@@ -1042,7 +1108,11 @@ describe("Batched drain", () => {
|
|
|
1042
1108
|
const eventsPlainB: ServerMessage[] = [];
|
|
1043
1109
|
|
|
1044
1110
|
// Start in-flight message
|
|
1045
|
-
const p1 = conversation.processMessage(
|
|
1111
|
+
const p1 = conversation.processMessage({
|
|
1112
|
+
content: "msg-1",
|
|
1113
|
+
attachments: [],
|
|
1114
|
+
requestId: "req-1",
|
|
1115
|
+
});
|
|
1046
1116
|
await waitForPendingRun(1);
|
|
1047
1117
|
|
|
1048
1118
|
// Enqueue ["plain-a", "/status", "plain-b"]. /status resolves to a non-
|
|
@@ -1051,29 +1121,26 @@ describe("Batched drain", () => {
|
|
|
1051
1121
|
// unknown-slash short-circuit path (no new runAgentLoop invocation — it
|
|
1052
1122
|
// emits assistant_text_delta + message_complete inline), then "plain-b"
|
|
1053
1123
|
// drains as its own run.
|
|
1054
|
-
conversation.enqueueMessage(
|
|
1055
|
-
"plain-a",
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
"plain-b",
|
|
1068
|
-
|
|
1069
|
-
(e) => eventsPlainB.push(e),
|
|
1070
|
-
"req-plain-b",
|
|
1071
|
-
);
|
|
1124
|
+
conversation.enqueueMessage({
|
|
1125
|
+
content: "plain-a",
|
|
1126
|
+
onEvent: (e) => eventsPlainA.push(e),
|
|
1127
|
+
requestId: "req-plain-a",
|
|
1128
|
+
});
|
|
1129
|
+
conversation.enqueueMessage({
|
|
1130
|
+
content: "/status",
|
|
1131
|
+
onEvent: (e) => eventsSlash.push(e),
|
|
1132
|
+
requestId: "req-slash",
|
|
1133
|
+
});
|
|
1134
|
+
conversation.enqueueMessage({
|
|
1135
|
+
content: "plain-b",
|
|
1136
|
+
onEvent: (e) => eventsPlainB.push(e),
|
|
1137
|
+
requestId: "req-plain-b",
|
|
1138
|
+
});
|
|
1072
1139
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
1073
1140
|
|
|
1074
1141
|
// Resolve msg-1 → drain pulls "plain-a" as its own run (batch stops at
|
|
1075
1142
|
// the /status boundary).
|
|
1076
|
-
resolveRun(0);
|
|
1143
|
+
await resolveRun(0);
|
|
1077
1144
|
await p1;
|
|
1078
1145
|
await waitForPendingRun(2);
|
|
1079
1146
|
|
|
@@ -1086,7 +1153,7 @@ describe("Batched drain", () => {
|
|
|
1086
1153
|
// runs its unknown-slash short-circuit (no new runAgentLoop, emits
|
|
1087
1154
|
// assistant_text_delta + message_complete inline), then drains "plain-b"
|
|
1088
1155
|
// as its own run.
|
|
1089
|
-
resolveRun(1);
|
|
1156
|
+
await resolveRun(1);
|
|
1090
1157
|
await waitForPendingRun(3);
|
|
1091
1158
|
|
|
1092
1159
|
// /status should have emitted its own assistant_text_delta + message_complete
|
|
@@ -1100,7 +1167,7 @@ describe("Batched drain", () => {
|
|
|
1100
1167
|
// without a runAgentLoop invocation.
|
|
1101
1168
|
expect(pendingRuns.length).toBe(3);
|
|
1102
1169
|
|
|
1103
|
-
resolveRun(2);
|
|
1170
|
+
await resolveRun(2);
|
|
1104
1171
|
await new Promise((r) => setTimeout(r, 10));
|
|
1105
1172
|
});
|
|
1106
1173
|
|
|
@@ -1110,7 +1177,11 @@ describe("Batched drain", () => {
|
|
|
1110
1177
|
await conversation.loadFromDb();
|
|
1111
1178
|
|
|
1112
1179
|
// Start in-flight message
|
|
1113
|
-
const p1 = conversation.processMessage(
|
|
1180
|
+
const p1 = conversation.processMessage({
|
|
1181
|
+
content: "msg-1",
|
|
1182
|
+
attachments: [],
|
|
1183
|
+
requestId: "req-1",
|
|
1184
|
+
});
|
|
1114
1185
|
await waitForPendingRun(1);
|
|
1115
1186
|
|
|
1116
1187
|
// Two sibling messages, each with a distinct image attachment.
|
|
@@ -1132,11 +1203,19 @@ describe("Batched drain", () => {
|
|
|
1132
1203
|
filePath: "/tmp/b.png",
|
|
1133
1204
|
},
|
|
1134
1205
|
];
|
|
1135
|
-
conversation.enqueueMessage(
|
|
1136
|
-
|
|
1206
|
+
conversation.enqueueMessage({
|
|
1207
|
+
content: "with-A",
|
|
1208
|
+
attachments: attachA,
|
|
1209
|
+
requestId: "req-A",
|
|
1210
|
+
});
|
|
1211
|
+
conversation.enqueueMessage({
|
|
1212
|
+
content: "with-B",
|
|
1213
|
+
attachments: attachB,
|
|
1214
|
+
requestId: "req-B",
|
|
1215
|
+
});
|
|
1137
1216
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
1138
1217
|
|
|
1139
|
-
resolveRun(0);
|
|
1218
|
+
await resolveRun(0);
|
|
1140
1219
|
await p1;
|
|
1141
1220
|
await waitForPendingRun(2);
|
|
1142
1221
|
|
|
@@ -1170,7 +1249,7 @@ describe("Batched drain", () => {
|
|
|
1170
1249
|
expect(allText).toContain("a.png");
|
|
1171
1250
|
expect(allText).toContain("b.png");
|
|
1172
1251
|
|
|
1173
|
-
resolveRun(1);
|
|
1252
|
+
await resolveRun(1);
|
|
1174
1253
|
await new Promise((r) => setTimeout(r, 10));
|
|
1175
1254
|
});
|
|
1176
1255
|
|
|
@@ -1185,34 +1264,33 @@ describe("Batched drain", () => {
|
|
|
1185
1264
|
new MessageQueue(budget);
|
|
1186
1265
|
|
|
1187
1266
|
// Start in-flight so subsequent enqueues are queued (not processed).
|
|
1188
|
-
const p1 = conversation.processMessage(
|
|
1267
|
+
const p1 = conversation.processMessage({
|
|
1268
|
+
content: "msg-1",
|
|
1269
|
+
attachments: [],
|
|
1270
|
+
requestId: "req-1",
|
|
1271
|
+
});
|
|
1189
1272
|
await waitForPendingRun(1);
|
|
1190
1273
|
|
|
1191
1274
|
// Fill to just-under budget: two ~500-char messages (1512+1512 = 3024 bytes).
|
|
1192
|
-
const accepted1 = conversation.enqueueMessage(
|
|
1193
|
-
"x".repeat(500),
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
[],
|
|
1201
|
-
() => {},
|
|
1202
|
-
"req-big-2",
|
|
1203
|
-
);
|
|
1275
|
+
const accepted1 = conversation.enqueueMessage({
|
|
1276
|
+
content: "x".repeat(500),
|
|
1277
|
+
requestId: "req-big-1",
|
|
1278
|
+
});
|
|
1279
|
+
const accepted2 = conversation.enqueueMessage({
|
|
1280
|
+
content: "y".repeat(500),
|
|
1281
|
+
requestId: "req-big-2",
|
|
1282
|
+
});
|
|
1204
1283
|
expect(accepted1.queued).toBe(true);
|
|
1205
1284
|
expect(accepted2.queued).toBe(true);
|
|
1206
1285
|
// A third would push the queue over budget → rejected. Capture its
|
|
1207
1286
|
// onEvent callback so we can verify the queue_full error event reaches
|
|
1208
1287
|
// the rejected caller (not just the synchronous return value).
|
|
1209
1288
|
const rejectedEvents: ServerMessage[] = [];
|
|
1210
|
-
const rejected = conversation.enqueueMessage(
|
|
1211
|
-
"z".repeat(500),
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
);
|
|
1289
|
+
const rejected = conversation.enqueueMessage({
|
|
1290
|
+
content: "z".repeat(500),
|
|
1291
|
+
onEvent: (e) => rejectedEvents.push(e),
|
|
1292
|
+
requestId: "req-over",
|
|
1293
|
+
});
|
|
1216
1294
|
expect(rejected.queued).toBe(false);
|
|
1217
1295
|
expect(rejected.rejected).toBe(true);
|
|
1218
1296
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
@@ -1231,33 +1309,41 @@ describe("Batched drain", () => {
|
|
|
1231
1309
|
}
|
|
1232
1310
|
|
|
1233
1311
|
// Complete in-flight → drain pulls both queued passthroughs as ONE batched run.
|
|
1234
|
-
resolveRun(0);
|
|
1312
|
+
await resolveRun(0);
|
|
1235
1313
|
await p1;
|
|
1236
1314
|
await waitForPendingRun(2);
|
|
1237
1315
|
expect(conversation.getQueueDepth()).toBe(0);
|
|
1238
1316
|
|
|
1239
1317
|
// Resolve the batched run.
|
|
1240
|
-
resolveRun(1);
|
|
1318
|
+
await resolveRun(1);
|
|
1241
1319
|
await new Promise((r) => setTimeout(r, 10));
|
|
1242
1320
|
|
|
1243
1321
|
// After the full drain, the byte budget must be fully reclaimed — a fresh
|
|
1244
1322
|
// round of enqueues up to the budget should succeed again. Spin up another
|
|
1245
1323
|
// in-flight message to reach the queueing state.
|
|
1246
|
-
const p2 = conversation.processMessage(
|
|
1324
|
+
const p2 = conversation.processMessage({
|
|
1325
|
+
content: "msg-2",
|
|
1326
|
+
attachments: [],
|
|
1327
|
+
requestId: "req-2",
|
|
1328
|
+
});
|
|
1247
1329
|
await waitForPendingRun(3);
|
|
1248
1330
|
expect(
|
|
1249
|
-
conversation.enqueueMessage(
|
|
1250
|
-
.
|
|
1331
|
+
conversation.enqueueMessage({
|
|
1332
|
+
content: "a".repeat(500),
|
|
1333
|
+
requestId: "req-a",
|
|
1334
|
+
}).queued,
|
|
1251
1335
|
).toBe(true);
|
|
1252
1336
|
expect(
|
|
1253
|
-
conversation.enqueueMessage(
|
|
1254
|
-
.
|
|
1337
|
+
conversation.enqueueMessage({
|
|
1338
|
+
content: "b".repeat(500),
|
|
1339
|
+
requestId: "req-b",
|
|
1340
|
+
}).queued,
|
|
1255
1341
|
).toBe(true);
|
|
1256
1342
|
|
|
1257
|
-
resolveRun(2);
|
|
1343
|
+
await resolveRun(2);
|
|
1258
1344
|
await p2;
|
|
1259
1345
|
await waitForPendingRun(4);
|
|
1260
|
-
resolveRun(3);
|
|
1346
|
+
await resolveRun(3);
|
|
1261
1347
|
await new Promise((r) => setTimeout(r, 10));
|
|
1262
1348
|
});
|
|
1263
1349
|
});
|
|
@@ -1281,7 +1367,11 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1281
1367
|
const eventsRegular: ServerMessage[] = [];
|
|
1282
1368
|
|
|
1283
1369
|
// Start in-flight message
|
|
1284
|
-
const p1 = conversation.processMessage(
|
|
1370
|
+
const p1 = conversation.processMessage({
|
|
1371
|
+
content: "msg-1",
|
|
1372
|
+
attachments: [],
|
|
1373
|
+
requestId: "req-1",
|
|
1374
|
+
});
|
|
1285
1375
|
await waitForPendingRun(1);
|
|
1286
1376
|
|
|
1287
1377
|
// Enqueue a surface-action message (activeSurfaceId set + tracked in
|
|
@@ -1289,25 +1379,23 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1289
1379
|
// same interface. The batch builder must reject the surface-action head
|
|
1290
1380
|
// so each drains as its own run.
|
|
1291
1381
|
conversation.surfaceActionRequestIds.add("req-surface");
|
|
1292
|
-
conversation.enqueueMessage(
|
|
1293
|
-
"surface action response",
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
"
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
"req-regular",
|
|
1304
|
-
);
|
|
1382
|
+
conversation.enqueueMessage({
|
|
1383
|
+
content: "surface action response",
|
|
1384
|
+
onEvent: (e) => eventsSurface.push(e),
|
|
1385
|
+
requestId: "req-surface",
|
|
1386
|
+
activeSurfaceId: "surface-1",
|
|
1387
|
+
});
|
|
1388
|
+
conversation.enqueueMessage({
|
|
1389
|
+
content: "regular follow-up",
|
|
1390
|
+
onEvent: (e) => eventsRegular.push(e),
|
|
1391
|
+
requestId: "req-regular",
|
|
1392
|
+
});
|
|
1305
1393
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
1306
1394
|
|
|
1307
1395
|
// Complete run 0 → drain must NOT batch the surface-action with the
|
|
1308
1396
|
// regular passthrough. Expect the surface-action to drain as a single
|
|
1309
1397
|
// run first.
|
|
1310
|
-
resolveRun(0);
|
|
1398
|
+
await resolveRun(0);
|
|
1311
1399
|
await p1;
|
|
1312
1400
|
await waitForPendingRun(2);
|
|
1313
1401
|
|
|
@@ -1322,7 +1410,7 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1322
1410
|
|
|
1323
1411
|
// Complete the surface-action run; drain pulls the regular passthrough
|
|
1324
1412
|
// as its own separate run.
|
|
1325
|
-
resolveRun(1);
|
|
1413
|
+
await resolveRun(1);
|
|
1326
1414
|
await waitForPendingRun(3);
|
|
1327
1415
|
expect(pendingRuns.length).toBe(3);
|
|
1328
1416
|
expect(
|
|
@@ -1331,7 +1419,7 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1331
1419
|
|
|
1332
1420
|
// Total runs = 3: msg-1, surface-action, regular — NOT 2 (would mean
|
|
1333
1421
|
// they were batched).
|
|
1334
|
-
resolveRun(2);
|
|
1422
|
+
await resolveRun(2);
|
|
1335
1423
|
await new Promise((r) => setTimeout(r, 10));
|
|
1336
1424
|
});
|
|
1337
1425
|
|
|
@@ -1345,12 +1433,12 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1345
1433
|
const events4: ServerMessage[] = [];
|
|
1346
1434
|
|
|
1347
1435
|
// Start in-flight message
|
|
1348
|
-
const p1 = conversation.processMessage(
|
|
1349
|
-
"msg-1",
|
|
1350
|
-
[],
|
|
1351
|
-
(e) => events1.push(e),
|
|
1352
|
-
"req-1",
|
|
1353
|
-
);
|
|
1436
|
+
const p1 = conversation.processMessage({
|
|
1437
|
+
content: "msg-1",
|
|
1438
|
+
attachments: [],
|
|
1439
|
+
onEvent: (e) => events1.push(e),
|
|
1440
|
+
requestId: "req-1",
|
|
1441
|
+
});
|
|
1354
1442
|
await waitForPendingRun(1);
|
|
1355
1443
|
|
|
1356
1444
|
// Enqueue three sibling passthroughs (msg-2 = head, msg-3 = mid,
|
|
@@ -1360,7 +1448,11 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1360
1448
|
// fresh one). Calling abort() now aborts that fresh controller, and
|
|
1361
1449
|
// the drainBatch loop's abort check after msg-3's persist will break,
|
|
1362
1450
|
// so msg-4 never persists.
|
|
1363
|
-
conversation.enqueueMessage(
|
|
1451
|
+
conversation.enqueueMessage({
|
|
1452
|
+
content: "msg-2",
|
|
1453
|
+
onEvent: (e) => events2.push(e),
|
|
1454
|
+
requestId: "req-2",
|
|
1455
|
+
});
|
|
1364
1456
|
|
|
1365
1457
|
// Install a one-shot abort trigger on msg-3's dequeue event. We do
|
|
1366
1458
|
// this before enqueueing so the wrapped callback is what drainBatch
|
|
@@ -1373,8 +1465,16 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1373
1465
|
conversation.abort();
|
|
1374
1466
|
}
|
|
1375
1467
|
};
|
|
1376
|
-
conversation.enqueueMessage(
|
|
1377
|
-
|
|
1468
|
+
conversation.enqueueMessage({
|
|
1469
|
+
content: "msg-3",
|
|
1470
|
+
onEvent: onMsg3Event,
|
|
1471
|
+
requestId: "req-3",
|
|
1472
|
+
});
|
|
1473
|
+
conversation.enqueueMessage({
|
|
1474
|
+
content: "msg-4",
|
|
1475
|
+
onEvent: (e) => events4.push(e),
|
|
1476
|
+
requestId: "req-4",
|
|
1477
|
+
});
|
|
1378
1478
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
1379
1479
|
|
|
1380
1480
|
const persistedUserRowCountBefore = capturedAddMessages.filter(
|
|
@@ -1382,7 +1482,7 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1382
1482
|
).length;
|
|
1383
1483
|
|
|
1384
1484
|
// Complete run 0 → drain pulls the sibling batch.
|
|
1385
|
-
resolveRun(0);
|
|
1485
|
+
await resolveRun(0);
|
|
1386
1486
|
await p1;
|
|
1387
1487
|
|
|
1388
1488
|
// Give the drain loop a chance to iterate. Abort happens on msg-3's
|
|
@@ -1412,12 +1512,12 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1412
1512
|
const events4: ServerMessage[] = [];
|
|
1413
1513
|
|
|
1414
1514
|
// Start in-flight message
|
|
1415
|
-
const p1 = conversation.processMessage(
|
|
1416
|
-
"msg-1",
|
|
1417
|
-
[],
|
|
1418
|
-
(e) => events1.push(e),
|
|
1419
|
-
"req-1",
|
|
1420
|
-
);
|
|
1515
|
+
const p1 = conversation.processMessage({
|
|
1516
|
+
content: "msg-1",
|
|
1517
|
+
attachments: [],
|
|
1518
|
+
onEvent: (e) => events1.push(e),
|
|
1519
|
+
requestId: "req-1",
|
|
1520
|
+
});
|
|
1421
1521
|
await waitForPendingRun(1);
|
|
1422
1522
|
|
|
1423
1523
|
// Enqueue three siblings. Configure addMessage to throw for the second
|
|
@@ -1426,28 +1526,25 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1426
1526
|
// msg-tail's requestId (the LAST successful persist), not msg-mid's.
|
|
1427
1527
|
addMessageShouldThrowForContent.add("msg-mid-unique-marker");
|
|
1428
1528
|
|
|
1429
|
-
conversation.enqueueMessage(
|
|
1430
|
-
"msg-head",
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
"
|
|
1443
|
-
|
|
1444
|
-
(e) => events4.push(e),
|
|
1445
|
-
"req-tail",
|
|
1446
|
-
);
|
|
1529
|
+
conversation.enqueueMessage({
|
|
1530
|
+
content: "msg-head",
|
|
1531
|
+
onEvent: (e) => events2.push(e),
|
|
1532
|
+
requestId: "req-head",
|
|
1533
|
+
});
|
|
1534
|
+
conversation.enqueueMessage({
|
|
1535
|
+
content: "msg-mid-unique-marker",
|
|
1536
|
+
onEvent: (e) => events3.push(e),
|
|
1537
|
+
requestId: "req-mid",
|
|
1538
|
+
});
|
|
1539
|
+
conversation.enqueueMessage({
|
|
1540
|
+
content: "msg-tail",
|
|
1541
|
+
onEvent: (e) => events4.push(e),
|
|
1542
|
+
requestId: "req-tail",
|
|
1543
|
+
});
|
|
1447
1544
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
1448
1545
|
|
|
1449
1546
|
// Complete run 0 → batched drain.
|
|
1450
|
-
resolveRun(0);
|
|
1547
|
+
await resolveRun(0);
|
|
1451
1548
|
await p1;
|
|
1452
1549
|
await waitForPendingRun(2);
|
|
1453
1550
|
|
|
@@ -1464,7 +1561,7 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1464
1561
|
).toBe("req-tail");
|
|
1465
1562
|
|
|
1466
1563
|
// Cleanup: resolve the batched run.
|
|
1467
|
-
resolveRun(1);
|
|
1564
|
+
await resolveRun(1);
|
|
1468
1565
|
await new Promise((r) => setTimeout(r, 20));
|
|
1469
1566
|
});
|
|
1470
1567
|
|
|
@@ -1477,12 +1574,12 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1477
1574
|
const events3: ServerMessage[] = [];
|
|
1478
1575
|
const events4: ServerMessage[] = [];
|
|
1479
1576
|
|
|
1480
|
-
const p1 = conversation.processMessage(
|
|
1481
|
-
"msg-1",
|
|
1482
|
-
[],
|
|
1483
|
-
(e) => events1.push(e),
|
|
1484
|
-
"req-1",
|
|
1485
|
-
);
|
|
1577
|
+
const p1 = conversation.processMessage({
|
|
1578
|
+
content: "msg-1",
|
|
1579
|
+
attachments: [],
|
|
1580
|
+
onEvent: (e) => events1.push(e),
|
|
1581
|
+
requestId: "req-1",
|
|
1582
|
+
});
|
|
1486
1583
|
await waitForPendingRun(1);
|
|
1487
1584
|
|
|
1488
1585
|
// Mid tail will fail to persist. After the batched run resolves,
|
|
@@ -1492,31 +1589,28 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1492
1589
|
// desync the client.
|
|
1493
1590
|
addMessageShouldThrowForContent.add("fanout-mid-marker");
|
|
1494
1591
|
|
|
1495
|
-
conversation.enqueueMessage(
|
|
1496
|
-
"fanout-head",
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
"fanout-tail",
|
|
1509
|
-
|
|
1510
|
-
(e) => events4.push(e),
|
|
1511
|
-
"req-fanout-tail",
|
|
1512
|
-
);
|
|
1592
|
+
conversation.enqueueMessage({
|
|
1593
|
+
content: "fanout-head",
|
|
1594
|
+
onEvent: (e) => events2.push(e),
|
|
1595
|
+
requestId: "req-fanout-head",
|
|
1596
|
+
});
|
|
1597
|
+
conversation.enqueueMessage({
|
|
1598
|
+
content: "fanout-mid-marker",
|
|
1599
|
+
onEvent: (e) => events3.push(e),
|
|
1600
|
+
requestId: "req-fanout-mid",
|
|
1601
|
+
});
|
|
1602
|
+
conversation.enqueueMessage({
|
|
1603
|
+
content: "fanout-tail",
|
|
1604
|
+
onEvent: (e) => events4.push(e),
|
|
1605
|
+
requestId: "req-fanout-tail",
|
|
1606
|
+
});
|
|
1513
1607
|
|
|
1514
|
-
resolveRun(0);
|
|
1608
|
+
await resolveRun(0);
|
|
1515
1609
|
await p1;
|
|
1516
1610
|
await waitForPendingRun(2);
|
|
1517
1611
|
|
|
1518
1612
|
// Drive the batched run to emit message_complete via fanOutOnEvent.
|
|
1519
|
-
resolveRun(1);
|
|
1613
|
+
await resolveRun(1);
|
|
1520
1614
|
await new Promise((r) => setTimeout(r, 20));
|
|
1521
1615
|
|
|
1522
1616
|
expect(events3.find((e) => e.type === "error")).toBeDefined();
|
|
@@ -1536,7 +1630,11 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1536
1630
|
await conversation.loadFromDb();
|
|
1537
1631
|
|
|
1538
1632
|
// Start in-flight message
|
|
1539
|
-
const p1 = conversation.processMessage(
|
|
1633
|
+
const p1 = conversation.processMessage({
|
|
1634
|
+
content: "msg-1",
|
|
1635
|
+
attachments: [],
|
|
1636
|
+
requestId: "req-1",
|
|
1637
|
+
});
|
|
1540
1638
|
await waitForPendingRun(1);
|
|
1541
1639
|
|
|
1542
1640
|
// Snapshot the count before drain so we only compare batch-emitted
|
|
@@ -1544,12 +1642,12 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1544
1642
|
const baseline = activityStates.length;
|
|
1545
1643
|
|
|
1546
1644
|
// Enqueue three sibling passthroughs.
|
|
1547
|
-
conversation.enqueueMessage("msg-2",
|
|
1548
|
-
conversation.enqueueMessage("msg-3",
|
|
1549
|
-
conversation.enqueueMessage("msg-4",
|
|
1645
|
+
conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
|
|
1646
|
+
conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
|
|
1647
|
+
conversation.enqueueMessage({ content: "msg-4", requestId: "req-4" });
|
|
1550
1648
|
|
|
1551
1649
|
// Complete run 0 → drain pulls the batched siblings as ONE run.
|
|
1552
|
-
resolveRun(0);
|
|
1650
|
+
await resolveRun(0);
|
|
1553
1651
|
await p1;
|
|
1554
1652
|
await waitForPendingRun(2);
|
|
1555
1653
|
|
|
@@ -1569,7 +1667,7 @@ describe("Batched drain correctness fixes", () => {
|
|
|
1569
1667
|
requestId: "req-2", // head's requestId, per the fix
|
|
1570
1668
|
});
|
|
1571
1669
|
|
|
1572
|
-
resolveRun(1);
|
|
1670
|
+
await resolveRun(1);
|
|
1573
1671
|
await new Promise((r) => setTimeout(r, 10));
|
|
1574
1672
|
});
|
|
1575
1673
|
|
|
@@ -1610,17 +1708,21 @@ describe("Conversation queue policy helpers", () => {
|
|
|
1610
1708
|
await conversation.loadFromDb();
|
|
1611
1709
|
|
|
1612
1710
|
// Start processing to make the session busy
|
|
1613
|
-
conversation.processMessage(
|
|
1711
|
+
conversation.processMessage({
|
|
1712
|
+
content: "msg-1",
|
|
1713
|
+
attachments: [],
|
|
1714
|
+
requestId: "req-1",
|
|
1715
|
+
});
|
|
1614
1716
|
await waitForPendingRun(1);
|
|
1615
1717
|
|
|
1616
1718
|
// Enqueue a message while processing
|
|
1617
|
-
conversation.enqueueMessage("msg-2",
|
|
1719
|
+
conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
|
|
1618
1720
|
expect(conversation.hasQueuedMessages()).toBe(true);
|
|
1619
1721
|
|
|
1620
1722
|
// Cleanup: resolve the pending run
|
|
1621
|
-
resolveRun(0);
|
|
1723
|
+
await resolveRun(0);
|
|
1622
1724
|
await waitForPendingRun(2);
|
|
1623
|
-
resolveRun(1);
|
|
1725
|
+
await resolveRun(1);
|
|
1624
1726
|
await new Promise((r) => setTimeout(r, 10));
|
|
1625
1727
|
});
|
|
1626
1728
|
|
|
@@ -1637,7 +1739,11 @@ describe("Conversation queue policy helpers", () => {
|
|
|
1637
1739
|
await conversation.loadFromDb();
|
|
1638
1740
|
|
|
1639
1741
|
// Start processing — but don't enqueue anything
|
|
1640
|
-
conversation.processMessage(
|
|
1742
|
+
conversation.processMessage({
|
|
1743
|
+
content: "msg-1",
|
|
1744
|
+
attachments: [],
|
|
1745
|
+
requestId: "req-1",
|
|
1746
|
+
});
|
|
1641
1747
|
await waitForPendingRun(1);
|
|
1642
1748
|
|
|
1643
1749
|
expect(conversation.isProcessing()).toBe(true);
|
|
@@ -1645,7 +1751,7 @@ describe("Conversation queue policy helpers", () => {
|
|
|
1645
1751
|
expect(conversation.canHandoffAtCheckpoint()).toBe(false);
|
|
1646
1752
|
|
|
1647
1753
|
// Cleanup
|
|
1648
|
-
resolveRun(0);
|
|
1754
|
+
await resolveRun(0);
|
|
1649
1755
|
await new Promise((r) => setTimeout(r, 10));
|
|
1650
1756
|
});
|
|
1651
1757
|
|
|
@@ -1654,20 +1760,24 @@ describe("Conversation queue policy helpers", () => {
|
|
|
1654
1760
|
await conversation.loadFromDb();
|
|
1655
1761
|
|
|
1656
1762
|
// Start processing
|
|
1657
|
-
conversation.processMessage(
|
|
1763
|
+
conversation.processMessage({
|
|
1764
|
+
content: "msg-1",
|
|
1765
|
+
attachments: [],
|
|
1766
|
+
requestId: "req-1",
|
|
1767
|
+
});
|
|
1658
1768
|
await waitForPendingRun(1);
|
|
1659
1769
|
|
|
1660
1770
|
// Enqueue a message
|
|
1661
|
-
conversation.enqueueMessage("msg-2",
|
|
1771
|
+
conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
|
|
1662
1772
|
|
|
1663
1773
|
expect(conversation.isProcessing()).toBe(true);
|
|
1664
1774
|
expect(conversation.hasQueuedMessages()).toBe(true);
|
|
1665
1775
|
expect(conversation.canHandoffAtCheckpoint()).toBe(true);
|
|
1666
1776
|
|
|
1667
1777
|
// Cleanup
|
|
1668
|
-
resolveRun(0);
|
|
1778
|
+
await resolveRun(0);
|
|
1669
1779
|
await waitForPendingRun(2);
|
|
1670
|
-
resolveRun(1);
|
|
1780
|
+
await resolveRun(1);
|
|
1671
1781
|
await new Promise((r) => setTimeout(r, 10));
|
|
1672
1782
|
});
|
|
1673
1783
|
|
|
@@ -1705,16 +1815,16 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1705
1815
|
const events1: ServerMessage[] = [];
|
|
1706
1816
|
|
|
1707
1817
|
// Start processing first message
|
|
1708
|
-
const p1 = conversation.processMessage(
|
|
1709
|
-
"msg-1",
|
|
1710
|
-
[],
|
|
1711
|
-
(e) => events1.push(e),
|
|
1712
|
-
"req-1",
|
|
1713
|
-
);
|
|
1818
|
+
const p1 = conversation.processMessage({
|
|
1819
|
+
content: "msg-1",
|
|
1820
|
+
attachments: [],
|
|
1821
|
+
onEvent: (e) => events1.push(e),
|
|
1822
|
+
requestId: "req-1",
|
|
1823
|
+
});
|
|
1714
1824
|
await waitForPendingRun(1);
|
|
1715
1825
|
|
|
1716
1826
|
// Enqueue a second message while the first is processing
|
|
1717
|
-
conversation.enqueueMessage("msg-2",
|
|
1827
|
+
conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
|
|
1718
1828
|
expect(conversation.hasQueuedMessages()).toBe(true);
|
|
1719
1829
|
|
|
1720
1830
|
// The pending run should have received an onCheckpoint callback.
|
|
@@ -1728,11 +1838,11 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1728
1838
|
history: [],
|
|
1729
1839
|
});
|
|
1730
1840
|
|
|
1731
|
-
// Because there is a queued message, the callback should
|
|
1732
|
-
expect(decision).
|
|
1841
|
+
// Because there is a queued message, the callback should yield for handoff
|
|
1842
|
+
expect(decision).toEqual("handoff");
|
|
1733
1843
|
|
|
1734
1844
|
// Complete the run so the conversation finishes cleanly
|
|
1735
|
-
resolveRun(0);
|
|
1845
|
+
await resolveRun(0);
|
|
1736
1846
|
await p1;
|
|
1737
1847
|
|
|
1738
1848
|
// After yield, the first message should emit generation_handoff
|
|
@@ -1747,7 +1857,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1747
1857
|
|
|
1748
1858
|
// The queued message should now be draining (second run started)
|
|
1749
1859
|
await waitForPendingRun(2);
|
|
1750
|
-
resolveRun(1);
|
|
1860
|
+
await resolveRun(1);
|
|
1751
1861
|
await new Promise((r) => setTimeout(r, 10));
|
|
1752
1862
|
});
|
|
1753
1863
|
|
|
@@ -1756,7 +1866,11 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1756
1866
|
await conversation.loadFromDb();
|
|
1757
1867
|
|
|
1758
1868
|
// Start processing — no enqueued messages
|
|
1759
|
-
const p1 = conversation.processMessage(
|
|
1869
|
+
const p1 = conversation.processMessage({
|
|
1870
|
+
content: "msg-1",
|
|
1871
|
+
attachments: [],
|
|
1872
|
+
requestId: "req-1",
|
|
1873
|
+
});
|
|
1760
1874
|
await waitForPendingRun(1);
|
|
1761
1875
|
|
|
1762
1876
|
expect(conversation.hasQueuedMessages()).toBe(false);
|
|
@@ -1775,7 +1889,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1775
1889
|
expect(decision).toBe("continue");
|
|
1776
1890
|
|
|
1777
1891
|
// Cleanup
|
|
1778
|
-
resolveRun(0);
|
|
1892
|
+
await resolveRun(0);
|
|
1779
1893
|
await p1;
|
|
1780
1894
|
});
|
|
1781
1895
|
|
|
@@ -1789,18 +1903,30 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1789
1903
|
const events4: ServerMessage[] = [];
|
|
1790
1904
|
|
|
1791
1905
|
// Start first message (mid-tool-use — will yield at the next checkpoint)
|
|
1792
|
-
const p1 = conversation.processMessage(
|
|
1793
|
-
"msg-1",
|
|
1794
|
-
[],
|
|
1795
|
-
(e) => events1.push(e),
|
|
1796
|
-
"req-1",
|
|
1797
|
-
);
|
|
1906
|
+
const p1 = conversation.processMessage({
|
|
1907
|
+
content: "msg-1",
|
|
1908
|
+
attachments: [],
|
|
1909
|
+
onEvent: (e) => events1.push(e),
|
|
1910
|
+
requestId: "req-1",
|
|
1911
|
+
});
|
|
1798
1912
|
await waitForPendingRun(1);
|
|
1799
1913
|
|
|
1800
1914
|
// Enqueue three sibling passthroughs while msg-1 is mid-turn
|
|
1801
|
-
conversation.enqueueMessage(
|
|
1802
|
-
|
|
1803
|
-
|
|
1915
|
+
conversation.enqueueMessage({
|
|
1916
|
+
content: "msg-2",
|
|
1917
|
+
onEvent: (e) => events2.push(e),
|
|
1918
|
+
requestId: "req-2",
|
|
1919
|
+
});
|
|
1920
|
+
conversation.enqueueMessage({
|
|
1921
|
+
content: "msg-3",
|
|
1922
|
+
onEvent: (e) => events3.push(e),
|
|
1923
|
+
requestId: "req-3",
|
|
1924
|
+
});
|
|
1925
|
+
conversation.enqueueMessage({
|
|
1926
|
+
content: "msg-4",
|
|
1927
|
+
onEvent: (e) => events4.push(e),
|
|
1928
|
+
requestId: "req-4",
|
|
1929
|
+
});
|
|
1804
1930
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
1805
1931
|
|
|
1806
1932
|
// Simulate the agent loop yielding at the checkpoint (first run is mid-tool-use)
|
|
@@ -1812,10 +1938,10 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1812
1938
|
hasToolUse: true,
|
|
1813
1939
|
history: [],
|
|
1814
1940
|
});
|
|
1815
|
-
expect(decision).
|
|
1941
|
+
expect(decision).toEqual("handoff");
|
|
1816
1942
|
|
|
1817
1943
|
// Complete first run
|
|
1818
|
-
resolveRun(0);
|
|
1944
|
+
await resolveRun(0);
|
|
1819
1945
|
await p1;
|
|
1820
1946
|
|
|
1821
1947
|
// The yielded drain pulls ALL THREE queued siblings as ONE batched run —
|
|
@@ -1829,7 +1955,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1829
1955
|
expect(events4.some((e) => e.type === "message_dequeued")).toBe(true);
|
|
1830
1956
|
|
|
1831
1957
|
// Resolve the batched run — message_complete fans out to all three clients.
|
|
1832
|
-
resolveRun(1);
|
|
1958
|
+
await resolveRun(1);
|
|
1833
1959
|
await new Promise((r) => setTimeout(r, 10));
|
|
1834
1960
|
|
|
1835
1961
|
expect(events2.some((e) => e.type === "message_complete")).toBe(true);
|
|
@@ -1845,20 +1971,24 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1845
1971
|
const events2: ServerMessage[] = [];
|
|
1846
1972
|
|
|
1847
1973
|
// Start processing first message
|
|
1848
|
-
const p1 = conversation.processMessage(
|
|
1849
|
-
"msg-1",
|
|
1850
|
-
[],
|
|
1851
|
-
(e) => events1.push(e),
|
|
1852
|
-
"req-1",
|
|
1853
|
-
);
|
|
1974
|
+
const p1 = conversation.processMessage({
|
|
1975
|
+
content: "msg-1",
|
|
1976
|
+
attachments: [],
|
|
1977
|
+
onEvent: (e) => events1.push(e),
|
|
1978
|
+
requestId: "req-1",
|
|
1979
|
+
});
|
|
1854
1980
|
await waitForPendingRun(1);
|
|
1855
1981
|
|
|
1856
1982
|
// Enqueue a second message while the first is processing
|
|
1857
|
-
conversation.enqueueMessage(
|
|
1983
|
+
conversation.enqueueMessage({
|
|
1984
|
+
content: "msg-2",
|
|
1985
|
+
onEvent: (e) => events2.push(e),
|
|
1986
|
+
requestId: "req-2",
|
|
1987
|
+
});
|
|
1858
1988
|
expect(conversation.hasQueuedMessages()).toBe(true);
|
|
1859
1989
|
|
|
1860
1990
|
// Simulate tool-use turns: the agent loop calls onCheckpoint at each turn boundary.
|
|
1861
|
-
// Because there is a queued message, the callback should
|
|
1991
|
+
// Because there is a queued message, the callback should yield for handoff.
|
|
1862
1992
|
const run = pendingRuns[0];
|
|
1863
1993
|
expect(run.onCheckpoint).toBeDefined();
|
|
1864
1994
|
|
|
@@ -1870,10 +2000,10 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1870
2000
|
hasToolUse: true,
|
|
1871
2001
|
history: [],
|
|
1872
2002
|
});
|
|
1873
|
-
expect(decision).
|
|
2003
|
+
expect(decision).toEqual("handoff");
|
|
1874
2004
|
|
|
1875
2005
|
// Complete the run (AgentLoop resolves after yielding)
|
|
1876
|
-
resolveRun(0);
|
|
2006
|
+
await resolveRun(0);
|
|
1877
2007
|
await p1;
|
|
1878
2008
|
|
|
1879
2009
|
// Verify generation_handoff was emitted (not plain message_complete)
|
|
@@ -1896,7 +2026,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1896
2026
|
expect(events2.some((e) => e.type === "message_dequeued")).toBe(true);
|
|
1897
2027
|
|
|
1898
2028
|
// Complete the second run
|
|
1899
|
-
resolveRun(1);
|
|
2029
|
+
await resolveRun(1);
|
|
1900
2030
|
await new Promise((r) => setTimeout(r, 10));
|
|
1901
2031
|
});
|
|
1902
2032
|
|
|
@@ -1912,12 +2042,12 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1912
2042
|
};
|
|
1913
2043
|
|
|
1914
2044
|
// Start processing message A
|
|
1915
|
-
const pA = conversation.processMessage(
|
|
1916
|
-
"msg-A",
|
|
1917
|
-
[],
|
|
1918
|
-
(e) => eventsA.push(e),
|
|
1919
|
-
"req-A",
|
|
1920
|
-
);
|
|
2045
|
+
const pA = conversation.processMessage({
|
|
2046
|
+
content: "msg-A",
|
|
2047
|
+
attachments: [],
|
|
2048
|
+
onEvent: (e) => eventsA.push(e),
|
|
2049
|
+
requestId: "req-A",
|
|
2050
|
+
});
|
|
1921
2051
|
await waitForPendingRun(1);
|
|
1922
2052
|
|
|
1923
2053
|
// Enqueue messages B, C, D — each on a distinct userMessageInterface so the
|
|
@@ -1926,33 +2056,24 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1926
2056
|
userMessageInterface: iface,
|
|
1927
2057
|
assistantMessageInterface: iface,
|
|
1928
2058
|
});
|
|
1929
|
-
conversation.enqueueMessage(
|
|
1930
|
-
"msg-B",
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
"
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
"
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
"
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
meta("
|
|
1946
|
-
);
|
|
1947
|
-
conversation.enqueueMessage(
|
|
1948
|
-
"msg-D",
|
|
1949
|
-
[],
|
|
1950
|
-
makeHandler("D"),
|
|
1951
|
-
"req-D",
|
|
1952
|
-
undefined,
|
|
1953
|
-
undefined,
|
|
1954
|
-
meta("vellum"),
|
|
1955
|
-
);
|
|
2059
|
+
conversation.enqueueMessage({
|
|
2060
|
+
content: "msg-B",
|
|
2061
|
+
onEvent: makeHandler("B"),
|
|
2062
|
+
requestId: "req-B",
|
|
2063
|
+
metadata: meta("macos"),
|
|
2064
|
+
});
|
|
2065
|
+
conversation.enqueueMessage({
|
|
2066
|
+
content: "msg-C",
|
|
2067
|
+
onEvent: makeHandler("C"),
|
|
2068
|
+
requestId: "req-C",
|
|
2069
|
+
metadata: meta("cli"),
|
|
2070
|
+
});
|
|
2071
|
+
conversation.enqueueMessage({
|
|
2072
|
+
content: "msg-D",
|
|
2073
|
+
onEvent: makeHandler("D"),
|
|
2074
|
+
requestId: "req-D",
|
|
2075
|
+
metadata: meta("vellum"),
|
|
2076
|
+
});
|
|
1956
2077
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
1957
2078
|
|
|
1958
2079
|
// Handoff from A -> B
|
|
@@ -1965,8 +2086,8 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1965
2086
|
hasToolUse: true,
|
|
1966
2087
|
history: [],
|
|
1967
2088
|
}),
|
|
1968
|
-
).
|
|
1969
|
-
resolveRun(0);
|
|
2089
|
+
).toEqual("handoff");
|
|
2090
|
+
await resolveRun(0);
|
|
1970
2091
|
await pA;
|
|
1971
2092
|
|
|
1972
2093
|
// B should be draining
|
|
@@ -1982,8 +2103,8 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1982
2103
|
hasToolUse: true,
|
|
1983
2104
|
history: [],
|
|
1984
2105
|
}),
|
|
1985
|
-
).
|
|
1986
|
-
resolveRun(1);
|
|
2106
|
+
).toEqual("handoff");
|
|
2107
|
+
await resolveRun(1);
|
|
1987
2108
|
await waitForPendingRun(3);
|
|
1988
2109
|
|
|
1989
2110
|
// Handoff from C -> D
|
|
@@ -1997,8 +2118,8 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1997
2118
|
hasToolUse: true,
|
|
1998
2119
|
history: [],
|
|
1999
2120
|
}),
|
|
2000
|
-
).
|
|
2001
|
-
resolveRun(2);
|
|
2121
|
+
).toEqual("handoff");
|
|
2122
|
+
await resolveRun(2);
|
|
2002
2123
|
await waitForPendingRun(4);
|
|
2003
2124
|
|
|
2004
2125
|
// D has no more queued -> checkpoint should return 'continue'
|
|
@@ -2013,7 +2134,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
2013
2134
|
}),
|
|
2014
2135
|
).toBe("continue");
|
|
2015
2136
|
|
|
2016
|
-
resolveRun(3);
|
|
2137
|
+
await resolveRun(3);
|
|
2017
2138
|
await new Promise((r) => setTimeout(r, 10));
|
|
2018
2139
|
|
|
2019
2140
|
// Verify FIFO dequeue order
|
|
@@ -2029,21 +2150,29 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
2029
2150
|
const eventsC: ServerMessage[] = [];
|
|
2030
2151
|
|
|
2031
2152
|
// Start processing message A
|
|
2032
|
-
const pA = conversation.processMessage(
|
|
2033
|
-
"msg-A",
|
|
2034
|
-
[],
|
|
2035
|
-
(e) => eventsA.push(e),
|
|
2036
|
-
"req-A",
|
|
2037
|
-
);
|
|
2153
|
+
const pA = conversation.processMessage({
|
|
2154
|
+
content: "msg-A",
|
|
2155
|
+
attachments: [],
|
|
2156
|
+
onEvent: (e) => eventsA.push(e),
|
|
2157
|
+
requestId: "req-A",
|
|
2158
|
+
});
|
|
2038
2159
|
await waitForPendingRun(1);
|
|
2039
2160
|
|
|
2040
2161
|
// Enqueue B (empty content — will fail to persist) and C (valid)
|
|
2041
|
-
conversation.enqueueMessage(
|
|
2042
|
-
|
|
2162
|
+
conversation.enqueueMessage({
|
|
2163
|
+
content: "",
|
|
2164
|
+
onEvent: (e) => eventsB.push(e),
|
|
2165
|
+
requestId: "req-B",
|
|
2166
|
+
});
|
|
2167
|
+
conversation.enqueueMessage({
|
|
2168
|
+
content: "msg-C",
|
|
2169
|
+
onEvent: (e) => eventsC.push(e),
|
|
2170
|
+
requestId: "req-C",
|
|
2171
|
+
});
|
|
2043
2172
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
2044
2173
|
|
|
2045
2174
|
// Complete message A — triggers drain. B should fail, C should proceed.
|
|
2046
|
-
resolveRun(0);
|
|
2175
|
+
await resolveRun(0);
|
|
2047
2176
|
await pA;
|
|
2048
2177
|
|
|
2049
2178
|
// C should have been dequeued and started a new AgentLoop.run
|
|
@@ -2060,7 +2189,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
2060
2189
|
expect(eventsC.some((e) => e.type === "message_dequeued")).toBe(true);
|
|
2061
2190
|
|
|
2062
2191
|
// Complete C's run
|
|
2063
|
-
resolveRun(1);
|
|
2192
|
+
await resolveRun(1);
|
|
2064
2193
|
await new Promise((r) => setTimeout(r, 10));
|
|
2065
2194
|
|
|
2066
2195
|
// C should have completed successfully
|
|
@@ -2072,7 +2201,11 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
2072
2201
|
await conversation.loadFromDb();
|
|
2073
2202
|
|
|
2074
2203
|
// Start processing
|
|
2075
|
-
const p1 = conversation.processMessage(
|
|
2204
|
+
const p1 = conversation.processMessage({
|
|
2205
|
+
content: "msg-1",
|
|
2206
|
+
attachments: [],
|
|
2207
|
+
requestId: "req-1",
|
|
2208
|
+
});
|
|
2076
2209
|
await waitForPendingRun(1);
|
|
2077
2210
|
|
|
2078
2211
|
// The first run should have onCheckpoint
|
|
@@ -2097,7 +2230,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
2097
2230
|
expect(pendingRuns[1].onCheckpoint).toBeDefined();
|
|
2098
2231
|
|
|
2099
2232
|
// Complete retry cleanly
|
|
2100
|
-
resolveRun(1);
|
|
2233
|
+
await resolveRun(1);
|
|
2101
2234
|
await p1;
|
|
2102
2235
|
});
|
|
2103
2236
|
});
|
|
@@ -2116,11 +2249,15 @@ describe("Conversation usage requestId correlation", () => {
|
|
|
2116
2249
|
const conversation = makeConversation();
|
|
2117
2250
|
await conversation.loadFromDb();
|
|
2118
2251
|
|
|
2119
|
-
const p1 = conversation.processMessage(
|
|
2252
|
+
const p1 = conversation.processMessage({
|
|
2253
|
+
content: "msg-1",
|
|
2254
|
+
attachments: [],
|
|
2255
|
+
requestId: "req-42",
|
|
2256
|
+
});
|
|
2120
2257
|
await waitForPendingRun(1);
|
|
2121
2258
|
|
|
2122
2259
|
// Complete the run — this triggers recordUsage with the request's ID
|
|
2123
|
-
resolveRun(0);
|
|
2260
|
+
await resolveRun(0);
|
|
2124
2261
|
await p1;
|
|
2125
2262
|
|
|
2126
2263
|
// The usage event should carry the request ID, not null
|
|
@@ -2149,16 +2286,20 @@ describe("Terminal trace events on rejection/failure", () => {
|
|
|
2149
2286
|
await conversation.loadFromDb();
|
|
2150
2287
|
|
|
2151
2288
|
// Start first message
|
|
2152
|
-
const p1 = conversation.processMessage(
|
|
2289
|
+
const p1 = conversation.processMessage({
|
|
2290
|
+
content: "msg-1",
|
|
2291
|
+
attachments: [],
|
|
2292
|
+
requestId: "req-1",
|
|
2293
|
+
});
|
|
2153
2294
|
await waitForPendingRun(1);
|
|
2154
2295
|
|
|
2155
2296
|
// Enqueue empty content (will fail persistUserMessage)
|
|
2156
|
-
conversation.enqueueMessage("",
|
|
2297
|
+
conversation.enqueueMessage({ content: "", requestId: "req-bad" });
|
|
2157
2298
|
// Enqueue valid message so drain continues
|
|
2158
|
-
conversation.enqueueMessage("msg-3",
|
|
2299
|
+
conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
|
|
2159
2300
|
|
|
2160
2301
|
// Complete first — triggers drain, empty msg fails persist
|
|
2161
|
-
resolveRun(0);
|
|
2302
|
+
await resolveRun(0);
|
|
2162
2303
|
await p1;
|
|
2163
2304
|
await waitForPendingRun(2);
|
|
2164
2305
|
|
|
@@ -2173,7 +2314,7 @@ describe("Terminal trace events on rejection/failure", () => {
|
|
|
2173
2314
|
expect(errorTrace).toBeDefined();
|
|
2174
2315
|
|
|
2175
2316
|
// Cleanup
|
|
2176
|
-
resolveRun(1);
|
|
2317
|
+
await resolveRun(1);
|
|
2177
2318
|
await new Promise((r) => setTimeout(r, 10));
|
|
2178
2319
|
});
|
|
2179
2320
|
});
|
|
@@ -2197,12 +2338,12 @@ describe("Conversation host attachment directives", () => {
|
|
|
2197
2338
|
const conversation = makeConversation((msg) => clientEvents.push(msg));
|
|
2198
2339
|
await conversation.loadFromDb();
|
|
2199
2340
|
|
|
2200
|
-
const p1 = conversation.processMessage(
|
|
2201
|
-
"msg-1",
|
|
2202
|
-
[],
|
|
2203
|
-
(e) => events.push(e),
|
|
2204
|
-
"req-1",
|
|
2205
|
-
);
|
|
2341
|
+
const p1 = conversation.processMessage({
|
|
2342
|
+
content: "msg-1",
|
|
2343
|
+
attachments: [],
|
|
2344
|
+
onEvent: (e) => events.push(e),
|
|
2345
|
+
requestId: "req-1",
|
|
2346
|
+
});
|
|
2206
2347
|
await waitForPendingRun(1);
|
|
2207
2348
|
|
|
2208
2349
|
const run = pendingRuns[0];
|
|
@@ -2215,6 +2356,7 @@ describe("Conversation host attachment directives", () => {
|
|
|
2215
2356
|
},
|
|
2216
2357
|
],
|
|
2217
2358
|
};
|
|
2359
|
+
await run.onEvent({ type: "llm_call_started" });
|
|
2218
2360
|
run.onEvent({
|
|
2219
2361
|
type: "usage",
|
|
2220
2362
|
inputTokens: 10,
|
|
@@ -2266,12 +2408,12 @@ describe("Conversation host attachment directives", () => {
|
|
|
2266
2408
|
const conversation = makeConversation((msg) => clientEvents.push(msg));
|
|
2267
2409
|
await conversation.loadFromDb();
|
|
2268
2410
|
|
|
2269
|
-
const p1 = conversation.processMessage(
|
|
2270
|
-
"msg-1",
|
|
2271
|
-
[],
|
|
2272
|
-
(e) => events.push(e),
|
|
2273
|
-
"req-1",
|
|
2274
|
-
);
|
|
2411
|
+
const p1 = conversation.processMessage({
|
|
2412
|
+
content: "msg-1",
|
|
2413
|
+
attachments: [],
|
|
2414
|
+
onEvent: (e) => events.push(e),
|
|
2415
|
+
requestId: "req-1",
|
|
2416
|
+
});
|
|
2275
2417
|
await waitForPendingRun(1);
|
|
2276
2418
|
|
|
2277
2419
|
const run = pendingRuns[0];
|
|
@@ -2284,6 +2426,7 @@ describe("Conversation host attachment directives", () => {
|
|
|
2284
2426
|
},
|
|
2285
2427
|
],
|
|
2286
2428
|
};
|
|
2429
|
+
await run.onEvent({ type: "llm_call_started" });
|
|
2287
2430
|
run.onEvent({
|
|
2288
2431
|
type: "usage",
|
|
2289
2432
|
inputTokens: 10,
|
|
@@ -2352,12 +2495,12 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2352
2495
|
const conversation = makeConversation();
|
|
2353
2496
|
await conversation.loadFromDb();
|
|
2354
2497
|
|
|
2355
|
-
const p1 = conversation.processMessage(
|
|
2356
|
-
"msg-1",
|
|
2357
|
-
[],
|
|
2358
|
-
(e) => events.push(e),
|
|
2359
|
-
"req-1",
|
|
2360
|
-
);
|
|
2498
|
+
const p1 = conversation.processMessage({
|
|
2499
|
+
content: "msg-1",
|
|
2500
|
+
attachments: [],
|
|
2501
|
+
onEvent: (e) => events.push(e),
|
|
2502
|
+
requestId: "req-1",
|
|
2503
|
+
});
|
|
2361
2504
|
await waitForPendingRun(1);
|
|
2362
2505
|
|
|
2363
2506
|
const run = pendingRuns[0];
|
|
@@ -2377,6 +2520,7 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2377
2520
|
} as any,
|
|
2378
2521
|
],
|
|
2379
2522
|
});
|
|
2523
|
+
await run.onEvent({ type: "llm_call_started" });
|
|
2380
2524
|
run.onEvent({
|
|
2381
2525
|
type: "usage",
|
|
2382
2526
|
inputTokens: 10,
|
|
@@ -2414,16 +2558,16 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2414
2558
|
const conversation = makeConversation();
|
|
2415
2559
|
await conversation.loadFromDb();
|
|
2416
2560
|
|
|
2417
|
-
const p1 = conversation.processMessage(
|
|
2418
|
-
"msg-1",
|
|
2419
|
-
[],
|
|
2420
|
-
(e) => events1.push(e),
|
|
2421
|
-
"req-1",
|
|
2422
|
-
);
|
|
2561
|
+
const p1 = conversation.processMessage({
|
|
2562
|
+
content: "msg-1",
|
|
2563
|
+
attachments: [],
|
|
2564
|
+
onEvent: (e) => events1.push(e),
|
|
2565
|
+
requestId: "req-1",
|
|
2566
|
+
});
|
|
2423
2567
|
await waitForPendingRun(1);
|
|
2424
2568
|
|
|
2425
2569
|
// Queue a second message so the first run yields via checkpoint handoff.
|
|
2426
|
-
conversation.enqueueMessage("msg-2",
|
|
2570
|
+
conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
|
|
2427
2571
|
|
|
2428
2572
|
const run = pendingRuns[0];
|
|
2429
2573
|
expect(run.onCheckpoint).toBeDefined();
|
|
@@ -2434,7 +2578,7 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2434
2578
|
hasToolUse: true,
|
|
2435
2579
|
history: [],
|
|
2436
2580
|
}),
|
|
2437
|
-
).
|
|
2581
|
+
).toEqual("handoff");
|
|
2438
2582
|
|
|
2439
2583
|
const assistantMsg: Message = {
|
|
2440
2584
|
role: "assistant",
|
|
@@ -2452,6 +2596,7 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2452
2596
|
} as any,
|
|
2453
2597
|
],
|
|
2454
2598
|
});
|
|
2599
|
+
await run.onEvent({ type: "llm_call_started" });
|
|
2455
2600
|
run.onEvent({
|
|
2456
2601
|
type: "usage",
|
|
2457
2602
|
inputTokens: 10,
|
|
@@ -2478,7 +2623,7 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2478
2623
|
expect(attachments[0].data).toBe("iVBORw0K");
|
|
2479
2624
|
|
|
2480
2625
|
await waitForPendingRun(2);
|
|
2481
|
-
resolveRun(1);
|
|
2626
|
+
await resolveRun(1);
|
|
2482
2627
|
await new Promise((r) => setTimeout(r, 10));
|
|
2483
2628
|
});
|
|
2484
2629
|
});
|
|
@@ -2498,19 +2643,19 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2498
2643
|
await conversation.loadFromDb();
|
|
2499
2644
|
|
|
2500
2645
|
// Start processing a message — collect events from the per-message callback
|
|
2501
|
-
const p1 = conversation.processMessage(
|
|
2502
|
-
"msg-1",
|
|
2503
|
-
[],
|
|
2504
|
-
(e) => msgEvents.push(e),
|
|
2505
|
-
"req-1",
|
|
2506
|
-
);
|
|
2646
|
+
const p1 = conversation.processMessage({
|
|
2647
|
+
content: "msg-1",
|
|
2648
|
+
attachments: [],
|
|
2649
|
+
onEvent: (e) => msgEvents.push(e),
|
|
2650
|
+
requestId: "req-1",
|
|
2651
|
+
});
|
|
2507
2652
|
await waitForPendingRun(1);
|
|
2508
2653
|
|
|
2509
2654
|
// User cancels — sets the abort signal
|
|
2510
2655
|
conversation.abort();
|
|
2511
2656
|
|
|
2512
2657
|
// Resolve the pending run so the abort-check path fires
|
|
2513
|
-
resolveRun(0);
|
|
2658
|
+
await resolveRun(0);
|
|
2514
2659
|
await p1;
|
|
2515
2660
|
|
|
2516
2661
|
// generation_cancelled should be emitted via the per-message callback
|
|
@@ -2532,12 +2677,12 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2532
2677
|
await conversation.loadFromDb();
|
|
2533
2678
|
linkAttachmentShouldThrow = true;
|
|
2534
2679
|
|
|
2535
|
-
const p1 = conversation.processMessage(
|
|
2536
|
-
"msg-1",
|
|
2537
|
-
[],
|
|
2538
|
-
(e) => events.push(e),
|
|
2539
|
-
"req-1",
|
|
2540
|
-
);
|
|
2680
|
+
const p1 = conversation.processMessage({
|
|
2681
|
+
content: "msg-1",
|
|
2682
|
+
attachments: [],
|
|
2683
|
+
onEvent: (e) => events.push(e),
|
|
2684
|
+
requestId: "req-1",
|
|
2685
|
+
});
|
|
2541
2686
|
await waitForPendingRun(1);
|
|
2542
2687
|
const run = pendingRuns[0];
|
|
2543
2688
|
const assistantMsg: Message = {
|
|
@@ -2556,6 +2701,7 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2556
2701
|
} as any,
|
|
2557
2702
|
],
|
|
2558
2703
|
});
|
|
2704
|
+
await run.onEvent({ type: "llm_call_started" });
|
|
2559
2705
|
run.onEvent({
|
|
2560
2706
|
type: "usage",
|
|
2561
2707
|
inputTokens: 10,
|
|
@@ -2585,12 +2731,12 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2585
2731
|
const conversation = makeConversation();
|
|
2586
2732
|
await conversation.loadFromDb();
|
|
2587
2733
|
|
|
2588
|
-
const p1 = conversation.processMessage(
|
|
2589
|
-
"msg-1",
|
|
2590
|
-
[],
|
|
2591
|
-
(e) => allEvents.push(e),
|
|
2592
|
-
"req-1",
|
|
2593
|
-
);
|
|
2734
|
+
const p1 = conversation.processMessage({
|
|
2735
|
+
content: "msg-1",
|
|
2736
|
+
attachments: [],
|
|
2737
|
+
onEvent: (e) => allEvents.push(e),
|
|
2738
|
+
requestId: "req-1",
|
|
2739
|
+
});
|
|
2594
2740
|
await waitForPendingRun(1);
|
|
2595
2741
|
|
|
2596
2742
|
// Simulate a provider failure
|
|
@@ -2614,26 +2760,24 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2614
2760
|
|
|
2615
2761
|
const eventsPerMsg: ServerMessage[][] = [[], [], []];
|
|
2616
2762
|
|
|
2617
|
-
conversation.processMessage(
|
|
2618
|
-
"msg-1",
|
|
2619
|
-
[],
|
|
2620
|
-
(e) => eventsPerMsg[0].push(e),
|
|
2621
|
-
"req-1",
|
|
2622
|
-
);
|
|
2763
|
+
conversation.processMessage({
|
|
2764
|
+
content: "msg-1",
|
|
2765
|
+
attachments: [],
|
|
2766
|
+
onEvent: (e) => eventsPerMsg[0].push(e),
|
|
2767
|
+
requestId: "req-1",
|
|
2768
|
+
});
|
|
2623
2769
|
await waitForPendingRun(1);
|
|
2624
2770
|
|
|
2625
|
-
conversation.enqueueMessage(
|
|
2626
|
-
"msg-2",
|
|
2627
|
-
[],
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
"req-3",
|
|
2636
|
-
);
|
|
2771
|
+
conversation.enqueueMessage({
|
|
2772
|
+
content: "msg-2",
|
|
2773
|
+
onEvent: (e) => eventsPerMsg[1].push(e),
|
|
2774
|
+
requestId: "req-2",
|
|
2775
|
+
});
|
|
2776
|
+
conversation.enqueueMessage({
|
|
2777
|
+
content: "msg-3",
|
|
2778
|
+
onEvent: (e) => eventsPerMsg[2].push(e),
|
|
2779
|
+
requestId: "req-3",
|
|
2780
|
+
});
|
|
2637
2781
|
|
|
2638
2782
|
conversation.abort();
|
|
2639
2783
|
|
|
@@ -2668,19 +2812,23 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2668
2812
|
const events2: ServerMessage[] = [];
|
|
2669
2813
|
|
|
2670
2814
|
// Start first message (promise intentionally not awaited — we test queue drain behavior)
|
|
2671
|
-
const _p1 = conversation.processMessage(
|
|
2672
|
-
"msg-1",
|
|
2673
|
-
[],
|
|
2674
|
-
(e) => events1.push(e),
|
|
2675
|
-
"req-1",
|
|
2676
|
-
);
|
|
2815
|
+
const _p1 = conversation.processMessage({
|
|
2816
|
+
content: "msg-1",
|
|
2817
|
+
attachments: [],
|
|
2818
|
+
onEvent: (e) => events1.push(e),
|
|
2819
|
+
requestId: "req-1",
|
|
2820
|
+
});
|
|
2677
2821
|
await waitForPendingRun(1);
|
|
2678
2822
|
|
|
2679
2823
|
// Enqueue a second message while the first is processing
|
|
2680
|
-
conversation.enqueueMessage(
|
|
2824
|
+
conversation.enqueueMessage({
|
|
2825
|
+
content: "msg-2",
|
|
2826
|
+
onEvent: (e) => events2.push(e),
|
|
2827
|
+
requestId: "req-2",
|
|
2828
|
+
});
|
|
2681
2829
|
|
|
2682
2830
|
// Complete the first agent loop run
|
|
2683
|
-
resolveRun(0);
|
|
2831
|
+
await resolveRun(0);
|
|
2684
2832
|
|
|
2685
2833
|
// The turn should still complete (timeout fires) and drain the queue
|
|
2686
2834
|
// even though commitTurnChanges never resolves.
|
|
@@ -2701,7 +2849,7 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2701
2849
|
|
|
2702
2850
|
// Complete the second run so the test can clean up
|
|
2703
2851
|
turnCommitHangForever = false;
|
|
2704
|
-
resolveRun(1);
|
|
2852
|
+
await resolveRun(1);
|
|
2705
2853
|
await new Promise((r) => origSetTimeout(r, 10));
|
|
2706
2854
|
} finally {
|
|
2707
2855
|
turnCommitHangForever = false;
|