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