@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- package/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +42 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +134 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +16 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { eq } from "drizzle-orm";
|
|
4
|
+
|
|
5
|
+
import { makeMockLogger } from "./helpers/mock-logger.js";
|
|
6
|
+
|
|
7
|
+
mock.module("../util/logger.js", () => ({
|
|
8
|
+
getLogger: () => makeMockLogger(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
appendCompactionEvent,
|
|
13
|
+
forkCompactionLedger,
|
|
14
|
+
getLatestCompactionEventAtOrBefore,
|
|
15
|
+
} from "../memory/compaction-ledger-store.js";
|
|
16
|
+
import { getDb } from "../memory/db-connection.js";
|
|
17
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
18
|
+
import {
|
|
19
|
+
conversationCompactionEvents,
|
|
20
|
+
conversations,
|
|
21
|
+
} from "../memory/schema.js";
|
|
22
|
+
|
|
23
|
+
await initializeDb();
|
|
24
|
+
|
|
25
|
+
function reset(): void {
|
|
26
|
+
const db = getDb();
|
|
27
|
+
db.delete(conversationCompactionEvents).run();
|
|
28
|
+
db.delete(conversations).run();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function makeConversation(id: string): void {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
getDb()
|
|
34
|
+
.insert(conversations)
|
|
35
|
+
.values({ id, title: id, createdAt: now, updatedAt: now })
|
|
36
|
+
.run();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function forkEventsFor(conversationId: string) {
|
|
40
|
+
return getDb()
|
|
41
|
+
.select()
|
|
42
|
+
.from(conversationCompactionEvents)
|
|
43
|
+
.where(eq(conversationCompactionEvents.conversationId, conversationId))
|
|
44
|
+
.all();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("compaction-ledger-store", () => {
|
|
48
|
+
beforeEach(reset);
|
|
49
|
+
|
|
50
|
+
test("getLatestCompactionEventAtOrBefore returns the newest event at-or-before the cutoff", () => {
|
|
51
|
+
makeConversation("conv");
|
|
52
|
+
appendCompactionEvent("conv", {
|
|
53
|
+
compactedAt: 100,
|
|
54
|
+
summary: "s100",
|
|
55
|
+
compactedMessageCount: 1,
|
|
56
|
+
});
|
|
57
|
+
appendCompactionEvent("conv", {
|
|
58
|
+
compactedAt: 200,
|
|
59
|
+
summary: "s200",
|
|
60
|
+
compactedMessageCount: 3,
|
|
61
|
+
});
|
|
62
|
+
appendCompactionEvent("conv", {
|
|
63
|
+
compactedAt: 300,
|
|
64
|
+
summary: "s300",
|
|
65
|
+
compactedMessageCount: 5,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(getLatestCompactionEventAtOrBefore("conv", 50)).toBeNull();
|
|
69
|
+
expect(
|
|
70
|
+
getLatestCompactionEventAtOrBefore("conv", 100)?.compactedMessageCount,
|
|
71
|
+
).toBe(1);
|
|
72
|
+
expect(
|
|
73
|
+
getLatestCompactionEventAtOrBefore("conv", 250)?.compactedMessageCount,
|
|
74
|
+
).toBe(3);
|
|
75
|
+
expect(
|
|
76
|
+
getLatestCompactionEventAtOrBefore("conv", 999)?.compactedMessageCount,
|
|
77
|
+
).toBe(5);
|
|
78
|
+
expect(getLatestCompactionEventAtOrBefore("conv", null)).toBeNull();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("getLatestCompactionEventAtOrBefore is scoped per conversation", () => {
|
|
82
|
+
makeConversation("a");
|
|
83
|
+
makeConversation("b");
|
|
84
|
+
appendCompactionEvent("a", {
|
|
85
|
+
compactedAt: 100,
|
|
86
|
+
summary: "a",
|
|
87
|
+
compactedMessageCount: 1,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(getLatestCompactionEventAtOrBefore("b", 999)).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("forkCompactionLedger copies only events at-or-before the boundary", () => {
|
|
94
|
+
makeConversation("src");
|
|
95
|
+
makeConversation("fork");
|
|
96
|
+
appendCompactionEvent("src", {
|
|
97
|
+
compactedAt: 100,
|
|
98
|
+
summary: "s100",
|
|
99
|
+
compactedMessageCount: 1,
|
|
100
|
+
});
|
|
101
|
+
appendCompactionEvent("src", {
|
|
102
|
+
compactedAt: 300,
|
|
103
|
+
summary: "s300",
|
|
104
|
+
compactedMessageCount: 5,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
forkCompactionLedger(getDb(), "src", "fork", 200);
|
|
108
|
+
|
|
109
|
+
const copied = forkEventsFor("fork");
|
|
110
|
+
expect(copied).toHaveLength(1);
|
|
111
|
+
expect(copied[0]?.compactedAt).toBe(100);
|
|
112
|
+
expect(copied[0]?.summary).toBe("s100");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("forkCompactionLedger with a null boundary copies nothing", () => {
|
|
116
|
+
makeConversation("src");
|
|
117
|
+
makeConversation("fork");
|
|
118
|
+
appendCompactionEvent("src", {
|
|
119
|
+
compactedAt: 100,
|
|
120
|
+
summary: "s100",
|
|
121
|
+
compactedMessageCount: 1,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
forkCompactionLedger(getDb(), "src", "fork", null);
|
|
125
|
+
|
|
126
|
+
expect(forkEventsFor("fork")).toHaveLength(0);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -17,6 +17,8 @@ mock.module("../util/logger.js", () => ({
|
|
|
17
17
|
// The compactor reads the conversation's image attachments from the DB to
|
|
18
18
|
// build its manifest; with no images these return empty.
|
|
19
19
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
20
|
+
setConversationProcessingStartedAt: () => {},
|
|
21
|
+
isConversationProcessing: () => false,
|
|
20
22
|
getMessages: () => [],
|
|
21
23
|
}));
|
|
22
24
|
mock.module("../memory/attachments-store.js", () => ({
|
|
@@ -30,6 +30,8 @@ mock.module("../util/logger.js", () => ({
|
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
32
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
33
|
+
setConversationProcessingStartedAt: () => {},
|
|
34
|
+
isConversationProcessing: () => false,
|
|
33
35
|
getMessages: () => [],
|
|
34
36
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
35
37
|
}));
|
|
@@ -22,6 +22,8 @@ mock.module("../util/logger.js", () => ({
|
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
24
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
25
|
+
setConversationProcessingStartedAt: () => {},
|
|
26
|
+
isConversationProcessing: () => false,
|
|
25
27
|
getMessages: () => [],
|
|
26
28
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
27
29
|
}));
|
|
@@ -22,6 +22,8 @@ mock.module("../util/logger.js", () => ({
|
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
24
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
25
|
+
setConversationProcessingStartedAt: () => {},
|
|
26
|
+
isConversationProcessing: () => false,
|
|
25
27
|
getMessages: () => [],
|
|
26
28
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
27
29
|
}));
|
|
@@ -98,6 +98,64 @@ function writeConfig(obj: unknown): void {
|
|
|
98
98
|
writeFileSync(CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n");
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function latencySeed(): Record<string, unknown> {
|
|
102
|
+
return {
|
|
103
|
+
model: "claude-haiku-4-5-20251001",
|
|
104
|
+
effort: "low",
|
|
105
|
+
thinking: { enabled: false },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function fullSeededCallSites(): Record<string, Record<string, unknown>> {
|
|
110
|
+
return {
|
|
111
|
+
guardianQuestionCopy: latencySeed(),
|
|
112
|
+
interactionClassifier: latencySeed(),
|
|
113
|
+
skillCategoryInference: latencySeed(),
|
|
114
|
+
inviteInstructionGenerator: latencySeed(),
|
|
115
|
+
notificationDecision: latencySeed(),
|
|
116
|
+
preferenceExtraction: latencySeed(),
|
|
117
|
+
commitMessage: {
|
|
118
|
+
model: "claude-haiku-4-5-20251001",
|
|
119
|
+
maxTokens: 120,
|
|
120
|
+
temperature: 0.2,
|
|
121
|
+
effort: "low",
|
|
122
|
+
thinking: { enabled: false },
|
|
123
|
+
},
|
|
124
|
+
conversationStarters: latencySeed(),
|
|
125
|
+
conversationSummarization: {
|
|
126
|
+
model: "claude-opus-4-7",
|
|
127
|
+
effort: "low",
|
|
128
|
+
thinking: { enabled: false },
|
|
129
|
+
},
|
|
130
|
+
recall: {
|
|
131
|
+
profile: "cost-optimized",
|
|
132
|
+
maxTokens: 4096,
|
|
133
|
+
effort: "low",
|
|
134
|
+
thinking: { enabled: false, streamThinking: false },
|
|
135
|
+
temperature: 0,
|
|
136
|
+
disableCache: true,
|
|
137
|
+
},
|
|
138
|
+
heartbeatAgent: {
|
|
139
|
+
profile: "cost-optimized",
|
|
140
|
+
maxTokens: 2048,
|
|
141
|
+
effort: "low",
|
|
142
|
+
temperature: 0,
|
|
143
|
+
thinking: { enabled: false, streamThinking: false },
|
|
144
|
+
contextWindow: { maxInputTokens: 16000 },
|
|
145
|
+
},
|
|
146
|
+
replySuggestion: {
|
|
147
|
+
model: "claude-haiku-4-5-20251001",
|
|
148
|
+
effort: "low",
|
|
149
|
+
thinking: { enabled: false },
|
|
150
|
+
disableCache: true,
|
|
151
|
+
},
|
|
152
|
+
memoryRouter: {
|
|
153
|
+
profile: "cost-optimized",
|
|
154
|
+
contextWindow: { maxInputTokens: 1_000_000 },
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
101
159
|
function mergeDefaultConfigAndSeedInferenceProfiles(db?: DrizzleDb): void {
|
|
102
160
|
const defaultConfigMerge = mergeDefaultWorkspaceConfig();
|
|
103
161
|
seedInferenceProfiles({
|
|
@@ -372,6 +430,56 @@ describe("loadConfig startup behavior", () => {
|
|
|
372
430
|
expect(config.memory.v2.bm25_b).toBe(0.4);
|
|
373
431
|
});
|
|
374
432
|
|
|
433
|
+
test("default workspace config merge prunes exact seeded call-site defaults", () => {
|
|
434
|
+
const seededCallSites = fullSeededCallSites();
|
|
435
|
+
const overlayPath = join(WORKSPACE_DIR, "hatch-overlay.json");
|
|
436
|
+
writeFileSync(
|
|
437
|
+
overlayPath,
|
|
438
|
+
JSON.stringify(
|
|
439
|
+
{
|
|
440
|
+
gateway: {
|
|
441
|
+
unmappedPolicy: "default",
|
|
442
|
+
defaultAssistantId: "self",
|
|
443
|
+
},
|
|
444
|
+
llm: {
|
|
445
|
+
activeProfile: "balanced",
|
|
446
|
+
advisorProfile: "frontier",
|
|
447
|
+
callSites: {
|
|
448
|
+
...seededCallSites,
|
|
449
|
+
recall: {
|
|
450
|
+
...seededCallSites.recall,
|
|
451
|
+
disableCache: false,
|
|
452
|
+
},
|
|
453
|
+
customSite: { profile: "frontier" },
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
null,
|
|
458
|
+
2,
|
|
459
|
+
) + "\n",
|
|
460
|
+
);
|
|
461
|
+
process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH = overlayPath;
|
|
462
|
+
|
|
463
|
+
const result = mergeDefaultWorkspaceConfig();
|
|
464
|
+
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8")) as Record<
|
|
465
|
+
string,
|
|
466
|
+
Record<string, unknown>
|
|
467
|
+
>;
|
|
468
|
+
const llm = raw.llm as Record<string, unknown>;
|
|
469
|
+
const callSites = llm.callSites as Record<string, Record<string, unknown>>;
|
|
470
|
+
|
|
471
|
+
expect(result.hadOverlay).toBe(true);
|
|
472
|
+
expect(raw.gateway).toEqual({
|
|
473
|
+
unmappedPolicy: "default",
|
|
474
|
+
defaultAssistantId: "self",
|
|
475
|
+
});
|
|
476
|
+
expect(llm.activeProfile).toBe("balanced");
|
|
477
|
+
expect(llm.advisorProfile).toBe("frontier");
|
|
478
|
+
expect(Object.keys(callSites).sort()).toEqual(["customSite", "recall"]);
|
|
479
|
+
expect(callSites.recall?.disableCache).toBe(false);
|
|
480
|
+
expect(callSites.customSite).toEqual({ profile: "frontier" });
|
|
481
|
+
});
|
|
482
|
+
|
|
375
483
|
test("reloads cached config when config.json is updated externally", () => {
|
|
376
484
|
// Models a CLI subprocess writing twilio.accountSid while the assistant
|
|
377
485
|
// process already has an effective config cached in memory.
|
|
@@ -745,7 +853,11 @@ describe("loadConfig startup behavior", () => {
|
|
|
745
853
|
);
|
|
746
854
|
expect(raw.llm.profiles.frontier.provider).toBe("anthropic");
|
|
747
855
|
expect(raw.llm.profiles.frontier.model).toBe("claude-opus-4-8");
|
|
748
|
-
|
|
856
|
+
// Speed is served by DeepSeek V4 Flash on Fireworks.
|
|
857
|
+
expect(raw.llm.profiles["cost-optimized"].provider).toBe("fireworks");
|
|
858
|
+
expect(raw.llm.profiles["cost-optimized"].model).toBe(
|
|
859
|
+
"accounts/fireworks/models/deepseek-v4-flash",
|
|
860
|
+
);
|
|
749
861
|
});
|
|
750
862
|
|
|
751
863
|
test("off-platform managed profiles are overwritten on every boot", () => {
|
|
@@ -1591,8 +1703,8 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
|
|
|
1591
1703
|
// ---------------------------------------------------------------------------
|
|
1592
1704
|
// Tests: OS Beta flag-gated managed profile. The template is defined but
|
|
1593
1705
|
// intentionally NOT part of MANAGED_PROFILE_TEMPLATES, so seedInferenceProfiles
|
|
1594
|
-
// must never create it.
|
|
1595
|
-
// feature flag.
|
|
1706
|
+
// must never create it. The flag-gated reconcile creates or removes it based on
|
|
1707
|
+
// the `os-beta` feature flag.
|
|
1596
1708
|
// ---------------------------------------------------------------------------
|
|
1597
1709
|
|
|
1598
1710
|
describe("OS Beta managed profile template", () => {
|
|
@@ -1639,20 +1751,21 @@ describe("OS Beta managed profile template", () => {
|
|
|
1639
1751
|
expect(MANAGED_PROFILE_NAMES.has("os-beta")).toBe(true);
|
|
1640
1752
|
});
|
|
1641
1753
|
|
|
1642
|
-
test("materializeProfile
|
|
1754
|
+
test("materializeProfile resolves OS Beta to the Balanced model with low effort", () => {
|
|
1643
1755
|
const entry = materializeProfile(
|
|
1644
1756
|
OS_BETA_PROFILE_TEMPLATE,
|
|
1645
|
-
"
|
|
1646
|
-
"
|
|
1757
|
+
"together",
|
|
1758
|
+
"together-managed",
|
|
1647
1759
|
);
|
|
1648
1760
|
|
|
1649
|
-
expect(entry.model).toBe("
|
|
1650
|
-
expect(entry.provider_connection).toBe("
|
|
1651
|
-
expect(entry.provider).toBe("
|
|
1761
|
+
expect(entry.model).toBe("MiniMaxAI/MiniMax-M3");
|
|
1762
|
+
expect(entry.provider_connection).toBe("together-managed");
|
|
1763
|
+
expect(entry.provider).toBe("together");
|
|
1652
1764
|
expect(entry.label).toBe("OS Beta");
|
|
1653
1765
|
expect(entry.source).toBe("managed");
|
|
1654
1766
|
expect(entry.maxTokens).toBe(32000);
|
|
1655
|
-
expect(entry.effort).toBe("
|
|
1767
|
+
expect(entry.effort).toBe("low");
|
|
1656
1768
|
expect(entry.thinking?.enabled).toBe(true);
|
|
1769
|
+
expect(entry.topP).toBe(0.95);
|
|
1657
1770
|
});
|
|
1658
1771
|
});
|
|
@@ -120,6 +120,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
120
120
|
shellDefaultTimeoutSec: 120,
|
|
121
121
|
shellMaxTimeoutSec: 600,
|
|
122
122
|
permissionTimeoutSec: 300,
|
|
123
|
+
questionResponseTimeoutSec: 1800,
|
|
123
124
|
toolExecutionTimeoutSec: 120,
|
|
124
125
|
providerStreamTimeoutSec: 1800,
|
|
125
126
|
backgroundTurnTimeoutSec: 1800,
|
|
@@ -135,11 +135,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
135
135
|
mockOnConversationCreatedCallbacks.length = 0;
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
test("emits guardian.question for trusted-contact sessions", () => {
|
|
138
|
+
test("emits guardian.question for trusted-contact sessions", async () => {
|
|
139
139
|
const canonicalRequest = makeCanonicalRequest();
|
|
140
140
|
const trustContext = makeTrustedContactContext();
|
|
141
141
|
|
|
142
|
-
const result = bridgeConfirmationRequestToGuardian({
|
|
142
|
+
const result = await bridgeConfirmationRequestToGuardian({
|
|
143
143
|
canonicalRequest,
|
|
144
144
|
trustContext,
|
|
145
145
|
conversationId: "conv-1",
|
|
@@ -160,7 +160,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
160
160
|
expect(payload.requesterIdentifier).toBe("@requester");
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
test("skips guardian actor sessions (self-approve)", () => {
|
|
163
|
+
test("skips guardian actor sessions (self-approve)", async () => {
|
|
164
164
|
const canonicalRequest = makeCanonicalRequest();
|
|
165
165
|
const trustContext: TrustContext = {
|
|
166
166
|
sourceChannel: "telegram",
|
|
@@ -168,7 +168,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
168
168
|
guardianExternalUserId: "guardian-1",
|
|
169
169
|
};
|
|
170
170
|
|
|
171
|
-
const result = bridgeConfirmationRequestToGuardian({
|
|
171
|
+
const result = await bridgeConfirmationRequestToGuardian({
|
|
172
172
|
canonicalRequest,
|
|
173
173
|
trustContext,
|
|
174
174
|
conversationId: "conv-1",
|
|
@@ -182,14 +182,14 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
182
182
|
expect(emittedSignals).toHaveLength(0);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
-
test("skips unknown actor sessions", () => {
|
|
185
|
+
test("skips unknown actor sessions", async () => {
|
|
186
186
|
const canonicalRequest = makeCanonicalRequest();
|
|
187
187
|
const trustContext: TrustContext = {
|
|
188
188
|
sourceChannel: "telegram",
|
|
189
189
|
trustClass: "unknown",
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
-
const result = bridgeConfirmationRequestToGuardian({
|
|
192
|
+
const result = await bridgeConfirmationRequestToGuardian({
|
|
193
193
|
canonicalRequest,
|
|
194
194
|
trustContext,
|
|
195
195
|
conversationId: "conv-1",
|
|
@@ -203,13 +203,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
203
203
|
expect(emittedSignals).toHaveLength(0);
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
-
test("skips when guardian identity is missing", () => {
|
|
206
|
+
test("skips when guardian identity is missing", async () => {
|
|
207
207
|
const canonicalRequest = makeCanonicalRequest();
|
|
208
208
|
const trustContext = makeTrustedContactContext({
|
|
209
209
|
guardianExternalUserId: undefined,
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
-
const result = bridgeConfirmationRequestToGuardian({
|
|
212
|
+
const result = await bridgeConfirmationRequestToGuardian({
|
|
213
213
|
canonicalRequest,
|
|
214
214
|
trustContext,
|
|
215
215
|
conversationId: "conv-1",
|
|
@@ -223,13 +223,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
223
223
|
expect(emittedSignals).toHaveLength(0);
|
|
224
224
|
});
|
|
225
225
|
|
|
226
|
-
test("skips when no guardian binding exists for channel", () => {
|
|
226
|
+
test("skips when no guardian binding exists for channel", async () => {
|
|
227
227
|
const canonicalRequest = makeCanonicalRequest({ sourceChannel: "phone" });
|
|
228
228
|
const trustContext = makeTrustedContactContext({
|
|
229
229
|
sourceChannel: "phone",
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
-
const result = bridgeConfirmationRequestToGuardian({
|
|
232
|
+
const result = await bridgeConfirmationRequestToGuardian({
|
|
233
233
|
canonicalRequest,
|
|
234
234
|
trustContext,
|
|
235
235
|
conversationId: "conv-1",
|
|
@@ -243,11 +243,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
243
243
|
expect(emittedSignals).toHaveLength(0);
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
test("sets correct attention hints for urgency", () => {
|
|
246
|
+
test("sets correct attention hints for urgency", async () => {
|
|
247
247
|
const canonicalRequest = makeCanonicalRequest();
|
|
248
248
|
const trustContext = makeTrustedContactContext();
|
|
249
249
|
|
|
250
|
-
bridgeConfirmationRequestToGuardian({
|
|
250
|
+
await bridgeConfirmationRequestToGuardian({
|
|
251
251
|
canonicalRequest,
|
|
252
252
|
trustContext,
|
|
253
253
|
conversationId: "conv-1",
|
|
@@ -261,11 +261,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
261
261
|
expect(hints.visibleInSourceNow).toBe(false);
|
|
262
262
|
});
|
|
263
263
|
|
|
264
|
-
test("uses dedupe key scoped to canonical request ID", () => {
|
|
264
|
+
test("uses dedupe key scoped to canonical request ID", async () => {
|
|
265
265
|
const canonicalRequest = makeCanonicalRequest();
|
|
266
266
|
const trustContext = makeTrustedContactContext();
|
|
267
267
|
|
|
268
|
-
bridgeConfirmationRequestToGuardian({
|
|
268
|
+
await bridgeConfirmationRequestToGuardian({
|
|
269
269
|
canonicalRequest,
|
|
270
270
|
trustContext,
|
|
271
271
|
conversationId: "conv-1",
|
|
@@ -277,11 +277,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
277
277
|
);
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
test("creates vellum delivery row via onConversationCreated callback", () => {
|
|
280
|
+
test("creates vellum delivery row via onConversationCreated callback", async () => {
|
|
281
281
|
const canonicalRequest = makeCanonicalRequest();
|
|
282
282
|
const trustContext = makeTrustedContactContext();
|
|
283
283
|
|
|
284
|
-
bridgeConfirmationRequestToGuardian({
|
|
284
|
+
await bridgeConfirmationRequestToGuardian({
|
|
285
285
|
canonicalRequest,
|
|
286
286
|
trustContext,
|
|
287
287
|
conversationId: "conv-1",
|
|
@@ -298,18 +298,20 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
298
298
|
});
|
|
299
299
|
|
|
300
300
|
const deliveries = listCanonicalGuardianDeliveries(canonicalRequest.id);
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
301
|
+
const vellumDelivery = deliveries.find(
|
|
302
|
+
(d) => d.destinationChannel === "vellum",
|
|
303
|
+
);
|
|
304
|
+
expect(vellumDelivery).toBeDefined();
|
|
305
|
+
expect(vellumDelivery?.destinationConversationId).toBe(
|
|
304
306
|
"guardian-conversation-1",
|
|
305
307
|
);
|
|
306
308
|
});
|
|
307
309
|
|
|
308
|
-
test("uses custom assistantId when provided", () => {
|
|
310
|
+
test("uses custom assistantId when provided", async () => {
|
|
309
311
|
const canonicalRequest = makeCanonicalRequest();
|
|
310
312
|
const trustContext = makeTrustedContactContext();
|
|
311
313
|
|
|
312
|
-
bridgeConfirmationRequestToGuardian({
|
|
314
|
+
await bridgeConfirmationRequestToGuardian({
|
|
313
315
|
canonicalRequest,
|
|
314
316
|
trustContext,
|
|
315
317
|
conversationId: "conv-1",
|
|
@@ -324,13 +326,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
324
326
|
expect(emittedSignals).toHaveLength(0);
|
|
325
327
|
});
|
|
326
328
|
|
|
327
|
-
test("does not pass assistantId to notification signal", () => {
|
|
329
|
+
test("does not pass assistantId to notification signal", async () => {
|
|
328
330
|
const canonicalRequest = makeCanonicalRequest();
|
|
329
331
|
const trustContext = makeTrustedContactContext();
|
|
330
332
|
|
|
331
333
|
// assistantId is used internally for guardian binding lookup but is no
|
|
332
334
|
// longer forwarded to the notification signal after the assistantId removal refactor.
|
|
333
|
-
bridgeConfirmationRequestToGuardian({
|
|
335
|
+
await bridgeConfirmationRequestToGuardian({
|
|
334
336
|
canonicalRequest,
|
|
335
337
|
trustContext,
|
|
336
338
|
conversationId: "conv-1",
|
|
@@ -340,13 +342,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
340
342
|
expect(emittedSignals[0].assistantId).toBeUndefined();
|
|
341
343
|
});
|
|
342
344
|
|
|
343
|
-
test("includes requesterChatId as null when not provided", () => {
|
|
345
|
+
test("includes requesterChatId as null when not provided", async () => {
|
|
344
346
|
const canonicalRequest = makeCanonicalRequest();
|
|
345
347
|
const trustContext = makeTrustedContactContext({
|
|
346
348
|
requesterChatId: undefined,
|
|
347
349
|
});
|
|
348
350
|
|
|
349
|
-
bridgeConfirmationRequestToGuardian({
|
|
351
|
+
await bridgeConfirmationRequestToGuardian({
|
|
350
352
|
canonicalRequest,
|
|
351
353
|
trustContext,
|
|
352
354
|
conversationId: "conv-1",
|
|
@@ -357,7 +359,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
357
359
|
expect(payload.requesterChatId).toBeNull();
|
|
358
360
|
});
|
|
359
361
|
|
|
360
|
-
test("skips when binding guardian identity does not match canonical request guardian", () => {
|
|
362
|
+
test("skips when binding guardian identity does not match canonical request guardian", async () => {
|
|
361
363
|
// Create a canonical request where guardianExternalUserId differs from the
|
|
362
364
|
// binding's guardianExternalUserId ('guardian-1' in the mock).
|
|
363
365
|
const canonicalRequest = makeCanonicalRequest({
|
|
@@ -365,7 +367,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
365
367
|
});
|
|
366
368
|
const trustContext = makeTrustedContactContext();
|
|
367
369
|
|
|
368
|
-
const result = bridgeConfirmationRequestToGuardian({
|
|
370
|
+
const result = await bridgeConfirmationRequestToGuardian({
|
|
369
371
|
canonicalRequest,
|
|
370
372
|
trustContext,
|
|
371
373
|
conversationId: "conv-1",
|
|
@@ -379,7 +381,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
379
381
|
expect(emittedSignals).toHaveLength(0);
|
|
380
382
|
});
|
|
381
383
|
|
|
382
|
-
test("does not skip when canonical request guardian identity is null", () => {
|
|
384
|
+
test("does not skip when canonical request guardian identity is null", async () => {
|
|
383
385
|
// When guardianExternalUserId is null on the canonical request (e.g. desktop
|
|
384
386
|
// flow), the identity check should be skipped and the bridge should proceed.
|
|
385
387
|
const canonicalRequest = makeCanonicalRequest({
|
|
@@ -387,7 +389,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
|
|
|
387
389
|
});
|
|
388
390
|
const trustContext = makeTrustedContactContext();
|
|
389
391
|
|
|
390
|
-
const result = bridgeConfirmationRequestToGuardian({
|
|
392
|
+
const result = await bridgeConfirmationRequestToGuardian({
|
|
391
393
|
canonicalRequest,
|
|
392
394
|
trustContext,
|
|
393
395
|
conversationId: "conv-1",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `createConsultDeadline` — the advisor consult's progress-aware
|
|
3
|
+
* timeout. Uses short real timers with generous (>=3x) margins so a streamed
|
|
4
|
+
* chunk reliably lands inside the idle window without timing flakiness.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, test } from "bun:test";
|
|
7
|
+
|
|
8
|
+
import { createConsultDeadline } from "../tools/subagent/consult-deadline.js";
|
|
9
|
+
|
|
10
|
+
const delay = (ms: number): Promise<void> =>
|
|
11
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
|
|
13
|
+
describe("createConsultDeadline", () => {
|
|
14
|
+
test("streamed progress keeps the consult alive past the idle window", async () => {
|
|
15
|
+
// Record progress every 25ms against a 150ms idle window (6x margin). Each
|
|
16
|
+
// chunk resets the window, so it must never abort over ~200ms of streaming.
|
|
17
|
+
const d = createConsultDeadline({ idleMs: 150, maxMs: 10_000 });
|
|
18
|
+
try {
|
|
19
|
+
for (let i = 0; i < 8; i++) {
|
|
20
|
+
await delay(25);
|
|
21
|
+
expect(d.signal.aborted).toBe(false);
|
|
22
|
+
d.recordProgress();
|
|
23
|
+
}
|
|
24
|
+
} finally {
|
|
25
|
+
d.dispose();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("aborts after the idle window once progress stops", async () => {
|
|
30
|
+
const d = createConsultDeadline({ idleMs: 50, maxMs: 10_000 });
|
|
31
|
+
try {
|
|
32
|
+
expect(d.signal.aborted).toBe(false);
|
|
33
|
+
await delay(250); // 5x the idle window with no progress
|
|
34
|
+
expect(d.signal.aborted).toBe(true);
|
|
35
|
+
} finally {
|
|
36
|
+
d.dispose();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("aborts at the absolute max even under steady progress", async () => {
|
|
41
|
+
// Idle window can't fire (huge); the short max must, despite steady chunks.
|
|
42
|
+
const d = createConsultDeadline({ idleMs: 10_000, maxMs: 70 });
|
|
43
|
+
try {
|
|
44
|
+
for (let i = 0; i < 8; i++) {
|
|
45
|
+
await delay(25);
|
|
46
|
+
d.recordProgress();
|
|
47
|
+
}
|
|
48
|
+
expect(d.signal.aborted).toBe(true); // ~200ms elapsed > 70ms max
|
|
49
|
+
} finally {
|
|
50
|
+
d.dispose();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("dispose() cancels both timers — no late abort", async () => {
|
|
55
|
+
const d = createConsultDeadline({ idleMs: 40, maxMs: 40 });
|
|
56
|
+
d.dispose();
|
|
57
|
+
await delay(150); // well past both windows
|
|
58
|
+
expect(d.signal.aborted).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
});
|