@vellumai/assistant 0.8.7 → 0.8.8
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/Dockerfile +20 -4
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/plugins.md +75 -79
- package/examples/plugins/echo/README.md +6 -12
- package/examples/plugins/echo/register.ts +0 -41
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/openapi.yaml +3381 -348
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +34 -39
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-pipeline.test.ts +1 -1
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +313 -1136
- package/src/__tests__/conversation-agent-loop.test.ts +596 -1616
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +26 -5
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +170 -229
- package/src/__tests__/conversation-runtime-workspace.test.ts +3 -24
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -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 +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +2 -8
- package/src/__tests__/injector-chain.test.ts +106 -270
- package/src/__tests__/injector-disk-pressure.test.ts +3 -12
- package/src/__tests__/injector-document-comments.test.ts +2 -2
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pipeline-runner.test.ts +29 -39
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-bootstrap.test.ts +13 -28
- package/src/__tests__/plugin-registry.test.ts +0 -27
- package/src/__tests__/plugin-types.test.ts +2 -125
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -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__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/agent/loop.ts +346 -442
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +368 -0
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -20
- 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/document-editor/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +35 -3
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +122 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +3 -6
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +605 -153
- package/src/daemon/conversation-agent-loop.ts +281 -760
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-runtime-assembly.ts +130 -347
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation.ts +32 -14
- package/src/daemon/external-plugins-bootstrap.ts +9 -10
- 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/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +39 -10
- package/src/daemon/lifecycle.ts +4 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- 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/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- 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__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +4 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +31 -4
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- 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/index.ts +2 -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/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -1
- package/src/memory/v2/consolidation-job.ts +1 -1
- package/src/memory/v3/__tests__/health.test.ts +16 -0
- package/src/memory/v3/__tests__/orchestrate.test.ts +45 -9
- package/src/memory/v3/__tests__/provider-blocks.test.ts +13 -0
- package/src/memory/v3/__tests__/router.test.ts +101 -29
- package/src/memory/v3/__tests__/selector.test.ts +93 -27
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +23 -5
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/llm-retry.ts +32 -0
- package/src/memory/v3/orchestrate.ts +26 -14
- package/src/memory/v3/provider-blocks.ts +15 -5
- package/src/memory/v3/router.ts +48 -42
- package/src/memory/v3/selector.ts +57 -42
- package/src/memory/v3/shadow-plugin.ts +47 -15
- package/src/memory/v3/types.ts +8 -0
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +8 -1
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +1 -15
- package/src/plugins/defaults/injectors/register.ts +243 -74
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +91 -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/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 +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/pipeline.ts +6 -18
- package/src/plugins/registry.ts +8 -25
- package/src/plugins/types.ts +43 -474
- package/src/proactive-artifact/aux-message-injector.ts +3 -3
- package/src/proactive-artifact/job.test.ts +7 -12
- package/src/prompts/__tests__/system-prompt.test.ts +36 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +62 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +5 -1
- package/src/runtime/agent-wake.ts +2 -2
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-query-routes.ts +2 -0
- package/src/runtime/routes/conversation-routes.ts +358 -184
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +208 -17
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +50 -28
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- 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/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/executor.ts +1 -53
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +9 -1
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- 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 +93 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- 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 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/package.json +0 -15
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `annotatePersistedAssistantMessage` persisting `_activityMetadata`
|
|
3
|
+
* (web_search / web_fetch) alongside the tool call.
|
|
4
|
+
*
|
|
5
|
+
* Without this annotation the tool activity card (e.g. WebSearchProgressCard)
|
|
6
|
+
* is lost on a history reopen — the snapshot only carries the plain result
|
|
7
|
+
* text. External provider tools (brave/perplexity/tavily, web_fetch) resolve
|
|
8
|
+
* their activity only when the `tool_result` lands, after `message_complete`
|
|
9
|
+
* already persisted the block, so they are stamped here. Native server tools
|
|
10
|
+
* (Anthropic web_search) resolve before `message_complete` and are stamped at
|
|
11
|
+
* persist time in `buildPersistedAssistantContent` (covered separately in
|
|
12
|
+
* build-persisted-content.test.ts).
|
|
13
|
+
*
|
|
14
|
+
* The test exercises the populate → annotate → persist round-trip:
|
|
15
|
+
* handleToolResult(event with activityMetadata)
|
|
16
|
+
* → state.toolActivityMetadata captures it
|
|
17
|
+
* → annotatePersistedAssistantMessage writes _activityMetadata onto the row
|
|
18
|
+
* → updateMessageContent receives the JSON-serialized output
|
|
19
|
+
*
|
|
20
|
+
* Read-side coverage (renderHistoryContent in handlers/shared.ts) lives in
|
|
21
|
+
* server-history-render.test.ts.
|
|
22
|
+
*/
|
|
23
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
24
|
+
|
|
25
|
+
// ── Mock platform (must precede imports that read it) ─────────────────────────
|
|
26
|
+
mock.module("../util/logger.js", () => ({
|
|
27
|
+
getLogger: () =>
|
|
28
|
+
new Proxy({} as Record<string, unknown>, {
|
|
29
|
+
get: () => () => {},
|
|
30
|
+
}),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
mock.module("../config/loader.js", () => ({
|
|
34
|
+
getConfig: () => ({
|
|
35
|
+
skills: {
|
|
36
|
+
entries: {},
|
|
37
|
+
load: { extraDirs: [], watch: false, watchDebounceMs: 0 },
|
|
38
|
+
install: { nodeManager: "npm" },
|
|
39
|
+
allowBundled: null,
|
|
40
|
+
remoteProviders: {
|
|
41
|
+
skillssh: { enabled: true },
|
|
42
|
+
clawhub: { enabled: true },
|
|
43
|
+
},
|
|
44
|
+
remotePolicy: {
|
|
45
|
+
blockSuspicious: true,
|
|
46
|
+
blockMalware: true,
|
|
47
|
+
maxSkillsShRisk: "medium",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
loadConfig: () => ({}),
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
let mockedRowContent = "";
|
|
55
|
+
const updates: Array<{ id: string; content: string }> = [];
|
|
56
|
+
|
|
57
|
+
mock.module("../memory/conversation-crud.js", () => ({
|
|
58
|
+
addMessage: () => ({ id: "mock-msg-id" }),
|
|
59
|
+
getMessageById: (id: string) =>
|
|
60
|
+
mockedRowContent ? { id, content: mockedRowContent } : null,
|
|
61
|
+
updateMessageContent: (id: string, content: string) => {
|
|
62
|
+
updates.push({ id, content });
|
|
63
|
+
},
|
|
64
|
+
provenanceFromTrustContext: () => ({}),
|
|
65
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
mock.module("../memory/llm-request-log-store.js", () => ({
|
|
69
|
+
recordRequestLog: () => {},
|
|
70
|
+
backfillMessageIdOnLogs: () => {},
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
|
74
|
+
import type {
|
|
75
|
+
EventHandlerDeps,
|
|
76
|
+
EventHandlerState,
|
|
77
|
+
} from "../daemon/conversation-agent-loop-handlers.js";
|
|
78
|
+
import {
|
|
79
|
+
createEventHandlerState,
|
|
80
|
+
handleToolResult,
|
|
81
|
+
} from "../daemon/conversation-agent-loop-handlers.js";
|
|
82
|
+
import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
|
|
83
|
+
|
|
84
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
function makeDeps(): EventHandlerDeps {
|
|
87
|
+
return {
|
|
88
|
+
ctx: {
|
|
89
|
+
conversationId: "test-conv",
|
|
90
|
+
provider: { name: "anthropic" },
|
|
91
|
+
traceEmitter: { emit: () => {} },
|
|
92
|
+
streamThinking: false,
|
|
93
|
+
emitActivityState: () => {},
|
|
94
|
+
markWorkspaceTopLevelDirty: () => {},
|
|
95
|
+
currentTurnSurfaces: [],
|
|
96
|
+
} as unknown as EventHandlerDeps["ctx"],
|
|
97
|
+
onEvent: () => {},
|
|
98
|
+
reqId: "test-req",
|
|
99
|
+
isFirstMessage: false,
|
|
100
|
+
shouldGenerateTitle: false,
|
|
101
|
+
rlog: new Proxy({} as Record<string, unknown>, {
|
|
102
|
+
get: () => () => {},
|
|
103
|
+
}) as unknown as EventHandlerDeps["rlog"],
|
|
104
|
+
turnChannelContext: {
|
|
105
|
+
userMessageChannel: "vellum",
|
|
106
|
+
assistantMessageChannel: "vellum",
|
|
107
|
+
} as unknown as EventHandlerDeps["turnChannelContext"],
|
|
108
|
+
turnInterfaceContext: {
|
|
109
|
+
userMessageInterface: "web",
|
|
110
|
+
assistantMessageInterface: "web",
|
|
111
|
+
} as unknown as EventHandlerDeps["turnInterfaceContext"],
|
|
112
|
+
applyCompaction: async () => {},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function setupState(toolUseId: string): EventHandlerState {
|
|
117
|
+
const state = createEventHandlerState();
|
|
118
|
+
state.lastAssistantMessageId = "msg-1";
|
|
119
|
+
state.toolUseIdToName.set(toolUseId, "web_search");
|
|
120
|
+
state.toolCallTimestamps.set(toolUseId, { startedAt: Date.now() });
|
|
121
|
+
state.currentTurnToolUseIds.push(toolUseId);
|
|
122
|
+
return state;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function findBlockById(
|
|
126
|
+
rawContent: string,
|
|
127
|
+
id: string,
|
|
128
|
+
): Record<string, unknown> {
|
|
129
|
+
const parsed = JSON.parse(rawContent) as Array<Record<string, unknown>>;
|
|
130
|
+
const block = parsed.find((b) => b.id === id);
|
|
131
|
+
if (!block) throw new Error(`block ${id} not found`);
|
|
132
|
+
return block;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const webSearchActivity: ToolActivityMetadata = {
|
|
136
|
+
webSearch: {
|
|
137
|
+
query: "vellum docs",
|
|
138
|
+
provider: "brave",
|
|
139
|
+
resultCount: 2,
|
|
140
|
+
durationMs: 142,
|
|
141
|
+
results: [
|
|
142
|
+
{
|
|
143
|
+
rank: 1,
|
|
144
|
+
title: "Vellum",
|
|
145
|
+
url: "https://vellum.ai",
|
|
146
|
+
domain: "vellum.ai",
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
rank: 2,
|
|
150
|
+
title: "Docs",
|
|
151
|
+
url: "https://docs.vellum.ai",
|
|
152
|
+
domain: "docs.vellum.ai",
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
describe("annotatePersistedAssistantMessage — activityMetadata", () => {
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
updates.length = 0;
|
|
163
|
+
mockedRowContent = "";
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("persists activityMetadata from the live tool_result event onto the tool_use block", () => {
|
|
167
|
+
// GIVEN a persisted tool_use block for an external web_search tool
|
|
168
|
+
const toolUseId = "tu_web_search";
|
|
169
|
+
const state = setupState(toolUseId);
|
|
170
|
+
mockedRowContent = JSON.stringify([
|
|
171
|
+
{
|
|
172
|
+
type: "tool_use",
|
|
173
|
+
id: toolUseId,
|
|
174
|
+
name: "web_search",
|
|
175
|
+
input: { query: "vellum docs" },
|
|
176
|
+
},
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
// WHEN the tool result lands carrying activityMetadata
|
|
180
|
+
handleToolResult(state, makeDeps(), {
|
|
181
|
+
type: "tool_result",
|
|
182
|
+
toolUseId,
|
|
183
|
+
content: "results",
|
|
184
|
+
isError: false,
|
|
185
|
+
activityMetadata: webSearchActivity,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// THEN the metadata is stamped on the persisted block verbatim
|
|
189
|
+
expect(updates).toHaveLength(1);
|
|
190
|
+
const block = findBlockById(updates[0].content, toolUseId);
|
|
191
|
+
expect(block._activityMetadata).toEqual(webSearchActivity);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("leaves native server_tool_use blocks untouched (stamped at persist time)", () => {
|
|
195
|
+
// GIVEN an external tool completes (to trigger annotation) AND a native
|
|
196
|
+
// server_tool_use block whose activity was captured at server_tool_complete
|
|
197
|
+
const externalId = "tu_external";
|
|
198
|
+
const nativeId = "srvtu_native_search";
|
|
199
|
+
const state = setupState(externalId);
|
|
200
|
+
state.toolActivityMetadata.set(nativeId, webSearchActivity);
|
|
201
|
+
mockedRowContent = JSON.stringify([
|
|
202
|
+
{
|
|
203
|
+
type: "server_tool_use",
|
|
204
|
+
id: nativeId,
|
|
205
|
+
name: "web_search",
|
|
206
|
+
input: { query: "vellum docs" },
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
type: "tool_use",
|
|
210
|
+
id: externalId,
|
|
211
|
+
name: "bash",
|
|
212
|
+
input: { command: "ls" },
|
|
213
|
+
},
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
// WHEN the external tool result lands (no activity of its own)
|
|
217
|
+
handleToolResult(state, makeDeps(), {
|
|
218
|
+
type: "tool_result",
|
|
219
|
+
toolUseId: externalId,
|
|
220
|
+
content: "ok",
|
|
221
|
+
isError: false,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// THEN the annotate pass does not stamp the server_tool_use block — native
|
|
225
|
+
// activity is stamped earlier by `buildPersistedAssistantContent` (covered
|
|
226
|
+
// in build-persisted-content.test.ts), and the unrelated external tool_use
|
|
227
|
+
// block carries no activity of its own
|
|
228
|
+
expect(updates).toHaveLength(1);
|
|
229
|
+
const nativeBlock = findBlockById(updates[0].content, nativeId);
|
|
230
|
+
expect(nativeBlock._activityMetadata).toBeUndefined();
|
|
231
|
+
const externalBlock = findBlockById(updates[0].content, externalId);
|
|
232
|
+
expect(externalBlock._activityMetadata).toBeUndefined();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("omits activityMetadata when the tool produced none", () => {
|
|
236
|
+
// GIVEN a non-activity tool
|
|
237
|
+
const toolUseId = "tu_plain";
|
|
238
|
+
const state = setupState(toolUseId);
|
|
239
|
+
state.toolUseIdToName.set(toolUseId, "bash");
|
|
240
|
+
mockedRowContent = JSON.stringify([
|
|
241
|
+
{
|
|
242
|
+
type: "tool_use",
|
|
243
|
+
id: toolUseId,
|
|
244
|
+
name: "bash",
|
|
245
|
+
input: { command: "ls" },
|
|
246
|
+
},
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
// WHEN the result lands with no activityMetadata
|
|
250
|
+
handleToolResult(state, makeDeps(), {
|
|
251
|
+
type: "tool_result",
|
|
252
|
+
toolUseId,
|
|
253
|
+
content: "ok",
|
|
254
|
+
isError: false,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// THEN no _activityMetadata is written
|
|
258
|
+
expect(updates).toHaveLength(1);
|
|
259
|
+
const block = findBlockById(updates[0].content, toolUseId);
|
|
260
|
+
expect(block._activityMetadata).toBeUndefined();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
@@ -103,6 +103,7 @@ function makeDeps(): EventHandlerDeps {
|
|
|
103
103
|
userMessageInterface: "web",
|
|
104
104
|
assistantMessageInterface: "web",
|
|
105
105
|
} as unknown as EventHandlerDeps["turnInterfaceContext"],
|
|
106
|
+
applyCompaction: async () => {},
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
|
|
@@ -120,9 +121,7 @@ function findPersistedToolUse(
|
|
|
120
121
|
toolUseId: string,
|
|
121
122
|
): Record<string, unknown> {
|
|
122
123
|
const parsed = JSON.parse(rawContent) as Array<Record<string, unknown>>;
|
|
123
|
-
const block = parsed.find(
|
|
124
|
-
(b) => b.type === "tool_use" && b.id === toolUseId,
|
|
125
|
-
);
|
|
124
|
+
const block = parsed.find((b) => b.type === "tool_use" && b.id === toolUseId);
|
|
126
125
|
if (!block) throw new Error(`tool_use block ${toolUseId} not found`);
|
|
127
126
|
return block;
|
|
128
127
|
}
|
|
@@ -102,12 +102,13 @@ mock.module("@anthropic-ai/sdk", () => ({
|
|
|
102
102
|
}));
|
|
103
103
|
|
|
104
104
|
// Import after mocking
|
|
105
|
+
import { cachedTextBlock } from "../memory/v3/provider-blocks.js";
|
|
106
|
+
import { AnthropicProvider } from "../providers/anthropic/client.js";
|
|
105
107
|
import {
|
|
106
|
-
AnthropicProvider,
|
|
107
108
|
isPlaceholderSentinelText,
|
|
108
109
|
PLACEHOLDER_BLOCKS_OMITTED,
|
|
109
110
|
PLACEHOLDER_EMPTY_TURN,
|
|
110
|
-
} from "../providers/
|
|
111
|
+
} from "../providers/placeholder-sentinels.js";
|
|
111
112
|
|
|
112
113
|
// ---------------------------------------------------------------------------
|
|
113
114
|
// Helpers
|
|
@@ -323,6 +324,98 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
323
324
|
expect(tools[0].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
324
325
|
});
|
|
325
326
|
|
|
327
|
+
test("preserves a caller's 1h block cache_control and sends the extended-cache beta (non-Haiku)", async () => {
|
|
328
|
+
// v3's `cachedTextBlock` stamps a stable prefix block with a 1h TTL; the
|
|
329
|
+
// non-Haiku path must forward it unchanged and send the beta header.
|
|
330
|
+
await provider.sendMessage([
|
|
331
|
+
{
|
|
332
|
+
role: "user",
|
|
333
|
+
content: [
|
|
334
|
+
cachedTextBlock("stable pages block"),
|
|
335
|
+
{ type: "text", text: "volatile current message" },
|
|
336
|
+
],
|
|
337
|
+
},
|
|
338
|
+
]);
|
|
339
|
+
|
|
340
|
+
const messages = lastStreamParams!.messages as Array<{
|
|
341
|
+
content: Array<{ cache_control?: { type: string; ttl?: string } }>;
|
|
342
|
+
}>;
|
|
343
|
+
expect(messages[0].content[0].cache_control).toEqual({
|
|
344
|
+
type: "ephemeral",
|
|
345
|
+
ttl: "1h",
|
|
346
|
+
});
|
|
347
|
+
expect(lastStreamParams!.betas).toContain("extended-cache-ttl-2025-04-11");
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("strips ttl from a caller's block cache_control and omits the beta for Haiku", async () => {
|
|
351
|
+
// Haiku doesn't support the extended-cache-ttl beta, so a caller-stamped
|
|
352
|
+
// 1h ttl (e.g. from `cachedTextBlock`) must be stripped before sending.
|
|
353
|
+
const haiku = new AnthropicProvider(
|
|
354
|
+
"sk-ant-test",
|
|
355
|
+
"claude-haiku-4-5-20251001",
|
|
356
|
+
);
|
|
357
|
+
await haiku.sendMessage([
|
|
358
|
+
{
|
|
359
|
+
role: "user",
|
|
360
|
+
content: [
|
|
361
|
+
cachedTextBlock("stable pages block"),
|
|
362
|
+
{ type: "text", text: "volatile current message" },
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
const messages = lastStreamParams!.messages as Array<{
|
|
368
|
+
content: Array<{ cache_control?: { type: string; ttl?: string } }>;
|
|
369
|
+
}>;
|
|
370
|
+
expect(messages[0].content[0].cache_control).toEqual({ type: "ephemeral" });
|
|
371
|
+
expect(messages[0].content[0].cache_control).not.toHaveProperty("ttl");
|
|
372
|
+
expect(
|
|
373
|
+
(lastStreamParams!.betas as string[] | undefined) ?? [],
|
|
374
|
+
).not.toContain("extended-cache-ttl-2025-04-11");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("v3-shape call (system + tools + cached prefix block) stays within the 4-breakpoint cap", async () => {
|
|
378
|
+
// Mirrors a v3 L2 selector call: a system prompt, a forced tool, and a user
|
|
379
|
+
// message whose stable <pages> block carries a cache_control breakpoint
|
|
380
|
+
// followed by the volatile current-message block. The preserved prefix
|
|
381
|
+
// breakpoint plus the client's system/tools/turn-start anchors must total
|
|
382
|
+
// exactly Anthropic's max of 4.
|
|
383
|
+
await provider.sendMessage(
|
|
384
|
+
[
|
|
385
|
+
{
|
|
386
|
+
role: "user",
|
|
387
|
+
content: [
|
|
388
|
+
cachedTextBlock("stable pages block"),
|
|
389
|
+
{ type: "text", text: "volatile current message" },
|
|
390
|
+
],
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
{ systemPrompt: "Select relevant pages.", tools: [sampleTools[0]] },
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
let breakpoints = 0;
|
|
397
|
+
const system = lastStreamParams!.system as
|
|
398
|
+
| Array<{ cache_control?: unknown }>
|
|
399
|
+
| undefined;
|
|
400
|
+
for (const b of system ?? []) if (b.cache_control) breakpoints++;
|
|
401
|
+
const tools = lastStreamParams!.tools as
|
|
402
|
+
| Array<{ cache_control?: unknown }>
|
|
403
|
+
| undefined;
|
|
404
|
+
for (const t of tools ?? []) if (t.cache_control) breakpoints++;
|
|
405
|
+
const messages = lastStreamParams!.messages as Array<{
|
|
406
|
+
content: Array<{ cache_control?: { type: string; ttl?: string } }>;
|
|
407
|
+
}>;
|
|
408
|
+
for (const m of messages)
|
|
409
|
+
for (const b of m.content) if (b.cache_control) breakpoints++;
|
|
410
|
+
|
|
411
|
+
expect(breakpoints).toBe(4);
|
|
412
|
+
// The stable pages block specifically must hold the preserved breakpoint.
|
|
413
|
+
expect(messages[0].content[0].cache_control).toEqual({
|
|
414
|
+
type: "ephemeral",
|
|
415
|
+
ttl: "1h",
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
326
419
|
test("no tools param when tools are omitted", async () => {
|
|
327
420
|
await provider.sendMessage([userMsg("Hi")]);
|
|
328
421
|
|
|
@@ -189,6 +189,31 @@ describe("AssistantEventHub — unsubscribe cleanup", () => {
|
|
|
189
189
|
expect(s.active).toBe(false);
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
test("assigns a distinct per-connection id to each subscription", () => {
|
|
193
|
+
const hub = new AssistantEventHub();
|
|
194
|
+
|
|
195
|
+
// Two connections sharing one clientId (an old connection and the
|
|
196
|
+
// reconnect that supersedes it) must be distinguishable by connection
|
|
197
|
+
// id so logs can be attributed to a specific connection.
|
|
198
|
+
const first = hub.subscribe({
|
|
199
|
+
type: "client",
|
|
200
|
+
clientId: "client-1",
|
|
201
|
+
interfaceId: "macos",
|
|
202
|
+
capabilities: [],
|
|
203
|
+
callback: () => {},
|
|
204
|
+
});
|
|
205
|
+
const second = hub.subscribe({
|
|
206
|
+
type: "client",
|
|
207
|
+
clientId: "client-1",
|
|
208
|
+
interfaceId: "macos",
|
|
209
|
+
capabilities: [],
|
|
210
|
+
callback: () => {},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expect(first.connectionId).not.toBe(second.connectionId);
|
|
214
|
+
expect(second.connectionId).toMatch(/^conn-/);
|
|
215
|
+
});
|
|
216
|
+
|
|
192
217
|
test("subscriberCount reflects live subscriptions only", () => {
|
|
193
218
|
const hub = new AssistantEventHub();
|
|
194
219
|
|
|
@@ -36,6 +36,7 @@ interface ShedReport {
|
|
|
36
36
|
client_id: string | null;
|
|
37
37
|
interface_id: string | null;
|
|
38
38
|
conversation_key: string | null;
|
|
39
|
+
connection_id: string | null;
|
|
39
40
|
subscription_age_ms: number;
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -54,6 +55,7 @@ function makeReporterCaptor(): {
|
|
|
54
55
|
client_id: inst.clientId,
|
|
55
56
|
interface_id: inst.interfaceId,
|
|
56
57
|
conversation_key: inst.conversationKey,
|
|
58
|
+
connection_id: inst.connectionId,
|
|
57
59
|
subscription_age_ms: Date.now() - inst.subscribedAtMs,
|
|
58
60
|
});
|
|
59
61
|
},
|
|
@@ -181,6 +183,10 @@ describe("SSE route — backpressure shed observability", () => {
|
|
|
181
183
|
expect(reports.length).toBe(1);
|
|
182
184
|
expect(reports[0]?.client_id).toBe("client-abc");
|
|
183
185
|
expect(reports[0]?.interface_id).toBe("macos");
|
|
186
|
+
// The hub-assigned per-connection id is threaded into the shed
|
|
187
|
+
// report so a shed can be attributed to a specific connection even
|
|
188
|
+
// when several share a clientId across reconnects.
|
|
189
|
+
expect(reports[0]?.connection_id).toMatch(/^conn-/);
|
|
184
190
|
|
|
185
191
|
ac.abort();
|
|
186
192
|
});
|
|
@@ -195,6 +201,7 @@ describe("buildSseShedSentryContext", () => {
|
|
|
195
201
|
interfaceId: "macos",
|
|
196
202
|
// Channel-backed conversation key embedding a phone number.
|
|
197
203
|
conversationKey: "asst:self:whatsapp:447123456789",
|
|
204
|
+
connectionId: "conn-1",
|
|
198
205
|
};
|
|
199
206
|
const elDelay = { mean_ms: 1.2, p99_ms: 3.4, max_ms: 5.6 };
|
|
200
207
|
|
|
@@ -224,6 +231,7 @@ describe("buildSseShedSentryContext", () => {
|
|
|
224
231
|
heartbeats_sent: 3,
|
|
225
232
|
client_id: "client-xyz",
|
|
226
233
|
interface_id: "macos",
|
|
234
|
+
connection_id: "conn-1",
|
|
227
235
|
event_loop_delay_mean_ms: 1.2,
|
|
228
236
|
event_loop_delay_p99_ms: 3.4,
|
|
229
237
|
event_loop_delay_max_ms: 5.6,
|