@vellumai/assistant 0.10.1 → 0.10.2-dev.202606241651.2d2b40d
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/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/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/openapi.yaml +74 -1
- package/package.json +1 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- 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__/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-guardian.test.ts +94 -58
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/compaction-events.test.ts +2 -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__/computer-use-tools.test.ts +13 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/contacts-relay-reads.test.ts +13 -15
- 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 +7 -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-confirmation-signals.test.ts +2 -0
- 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-action-delivery.test.ts +65 -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 +0 -1
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- 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-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +10 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +75 -10
- package/src/__tests__/guardian-dispatch.test.ts +95 -1
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- 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 +1 -7
- 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-proxy.test.ts +299 -0
- 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 +167 -8
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +43 -0
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- 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__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +189 -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__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-bootstrap.test.ts +3 -73
- package/src/__tests__/plugin-route-contribution.test.ts +4 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -18
- package/src/__tests__/plugin-types.test.ts +0 -2
- 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-usage-tracking.test.ts +39 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +3 -0
- package/src/__tests__/relay-server.test.ts +694 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- 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 +102 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +44 -13
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +2 -0
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- 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-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/agent/loop-exclusive-tool.test.ts +150 -0
- package/src/agent/loop.ts +56 -0
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/llm-request-log-entry.ts +25 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +262 -4
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +10 -8
- package/src/calls/inbound-trust-reader.ts +17 -1
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-server.ts +167 -50
- package/src/calls/relay-setup-router.ts +37 -7
- 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/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/cache.ts +194 -181
- 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 +302 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +175 -0
- package/src/cli/commands/plugins.ts +75 -3
- 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/list-installed-plugins.ts +179 -1
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +6 -1
- 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/feature-flag-registry.json +0 -8
- package/src/config/loader.ts +36 -5
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- 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 +14 -5
- package/src/config/skills.ts +27 -5
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/contacts-write.ts +3 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/daemon/conversation-agent-loop.ts +9 -0
- package/src/daemon/conversation-process.ts +39 -17
- package/src/daemon/conversation-surfaces.ts +8 -0
- package/src/daemon/conversation-tool-setup.ts +49 -16
- package/src/daemon/conversation.ts +21 -2
- 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 +4 -34
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -0
- package/src/daemon/handlers/__tests__/config-channels.test.ts +225 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +78 -22
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/host-cu-proxy.ts +102 -11
- package/src/daemon/lifecycle.ts +4 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- 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/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- 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/conversation-crud.ts +38 -0
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +36 -502
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +58 -0
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +9 -8
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +13 -3
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -27
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +130 -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/__tests__/014-backfill-inbox-thread-state.test.ts +108 -0
- package/src/memory/migrations/__tests__/136-drop-assistant-id-columns.test.ts +82 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +224 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +90 -6
- package/src/memory/migrations/schema-introspection.ts +14 -0
- package/src/memory/migrations/validate-migration-state.ts +101 -66
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +573 -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 +118 -0
- package/src/memory/worker-process.ts +72 -0
- package/src/notifications/__tests__/broadcaster.test.ts +16 -8
- package/src/notifications/__tests__/connected-channels.test.ts +114 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +256 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +68 -24
- package/src/notifications/emit-signal.ts +39 -14
- package/src/onboarding/checkin-event.test.ts +220 -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 +6 -6
- package/src/plugin-api/types.ts +3 -5
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +161 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +106 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +60 -0
- package/src/plugins/defaults/advisor/consult.ts +110 -6
- package/src/plugins/defaults/advisor/context-pack.ts +288 -0
- package/src/plugins/defaults/advisor/steering.ts +14 -2
- package/src/plugins/defaults/advisor/tools/advisor.ts +32 -5
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +10 -11
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +12 -20
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- 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 +29 -1
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +8 -1
- package/src/plugins/mtime-cache.ts +7 -2
- package/src/plugins/types.ts +0 -2
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +4 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/responses-provider.ts +5 -0
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +4 -0
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/retry.ts +4 -0
- package/src/providers/types.ts +9 -0
- package/src/providers/usage-tracking.ts +4 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +181 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +164 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +335 -3
- package/src/runtime/access-request-helper.ts +19 -39
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/anchored-guardian.test.ts +156 -0
- package/src/runtime/anchored-guardian.ts +135 -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 +66 -7
- package/src/runtime/invite-redemption-service.ts +50 -18
- package/src/runtime/local-actor-identity.ts +76 -11
- package/src/runtime/local-principal-trust.ts +52 -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 +212 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +93 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +215 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +8 -32
- 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-routes.ts +74 -81
- 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 +1 -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 -49
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +16 -4
- 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/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +71 -0
- package/src/runtime/routes/mcp-auth-routes.ts +38 -15
- 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/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +51 -55
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +7 -9
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +85 -9
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/manager.ts +9 -0
- 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/computer-use/definitions.ts +8 -2
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +18 -0
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/types.ts +17 -2
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/platform.ts +5 -0
- package/src/util/telemetry-db-path.ts +24 -0
- 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/__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
|
@@ -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
|
}));
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
computerUseDoneTool,
|
|
7
7
|
computerUseDragTool,
|
|
8
8
|
computerUseKeyTool,
|
|
9
|
+
computerUseObserveTool,
|
|
9
10
|
computerUseOpenAppTool,
|
|
10
11
|
computerUseRespondTool,
|
|
11
12
|
computerUseRunAppleScriptTool,
|
|
@@ -58,6 +59,18 @@ describe("computer-use tool definitions", () => {
|
|
|
58
59
|
});
|
|
59
60
|
});
|
|
60
61
|
|
|
62
|
+
// ── observe ─────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
describe("computer_use_observe", () => {
|
|
65
|
+
test("supports target_client_id", () => {
|
|
66
|
+
const props = schema(computerUseObserveTool).properties as Record<
|
|
67
|
+
string,
|
|
68
|
+
{ type: string }
|
|
69
|
+
>;
|
|
70
|
+
expect(props.target_client_id.type).toBe("string");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
61
74
|
// ── Unified click tool ──────────────────────────────────────────────
|
|
62
75
|
|
|
63
76
|
describe("computer_use_click (unified)", () => {
|
|
@@ -745,7 +745,11 @@ describe("loadConfig startup behavior", () => {
|
|
|
745
745
|
);
|
|
746
746
|
expect(raw.llm.profiles.frontier.provider).toBe("anthropic");
|
|
747
747
|
expect(raw.llm.profiles.frontier.model).toBe("claude-opus-4-8");
|
|
748
|
-
|
|
748
|
+
// Speed is served by DeepSeek V4 Flash on Fireworks.
|
|
749
|
+
expect(raw.llm.profiles["cost-optimized"].provider).toBe("fireworks");
|
|
750
|
+
expect(raw.llm.profiles["cost-optimized"].model).toBe(
|
|
751
|
+
"accounts/fireworks/models/deepseek-v4-flash",
|
|
752
|
+
);
|
|
749
753
|
});
|
|
750
754
|
|
|
751
755
|
test("off-platform managed profiles are overwritten on every boot", () => {
|
|
@@ -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",
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* handleListContacts (non-search) and handleGetContact relay to the gateway
|
|
5
5
|
* via `ipcCallPersistent`. On the happy path they serve gateway-sourced data
|
|
6
|
-
* and do NOT read the assistant DB. On IPC failure they
|
|
7
|
-
*
|
|
6
|
+
* and do NOT read the assistant DB. On IPC failure they FAIL CLOSED — the relay
|
|
7
|
+
* error propagates rather than falling back to the assistant DB. getContact
|
|
8
|
+
* surfaces a clean gateway not-found as a 404.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -213,16 +214,15 @@ describe("handleListContacts relay", () => {
|
|
|
213
214
|
expect(result.contacts[0].displayName).toBe("Your Guardian");
|
|
214
215
|
});
|
|
215
216
|
|
|
216
|
-
test("
|
|
217
|
+
test("fails closed on IPC failure (no assistant-DB fallback)", async () => {
|
|
217
218
|
ipcStub = () => {
|
|
218
219
|
throw new Error("gateway down");
|
|
219
220
|
};
|
|
220
221
|
|
|
221
|
-
|
|
222
|
+
await expect(handleListContacts({})).rejects.toThrow("gateway down");
|
|
222
223
|
|
|
223
224
|
expect(ipcCalls.map((c) => c.method)).toEqual(["contacts_list_rich"]);
|
|
224
|
-
expect(localCalls).
|
|
225
|
-
expect(result.contacts[0].id).toBe("local-1");
|
|
225
|
+
expect(localCalls).toEqual([]);
|
|
226
226
|
});
|
|
227
227
|
|
|
228
228
|
test("search params stay daemon-native and log the boundary note", async () => {
|
|
@@ -384,26 +384,24 @@ describe("handleGetContact relay", () => {
|
|
|
384
384
|
expect(localCalls).toEqual([]);
|
|
385
385
|
});
|
|
386
386
|
|
|
387
|
-
test("
|
|
387
|
+
test("fails closed on IPC transport failure (no assistant-DB fallback)", async () => {
|
|
388
388
|
ipcStub = () => {
|
|
389
389
|
throw new Error("gateway down");
|
|
390
390
|
};
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
await expect(handleGetContact("gw-1")).rejects.toThrow("gateway down");
|
|
393
393
|
|
|
394
394
|
expect(ipcCalls.map((c) => c.method)).toEqual(["contacts_get_rich"]);
|
|
395
|
-
expect(localCalls).
|
|
396
|
-
expect(result.contact.id).toBe("gw-1");
|
|
395
|
+
expect(localCalls).toEqual([]);
|
|
397
396
|
});
|
|
398
397
|
|
|
399
|
-
test("
|
|
400
|
-
ipcStub = () =>
|
|
401
|
-
throw new Error("gateway down");
|
|
402
|
-
};
|
|
398
|
+
test("clean gateway not-found surfaces as a 404 for unknown ids", async () => {
|
|
399
|
+
ipcStub = () => null;
|
|
403
400
|
|
|
404
401
|
await expect(handleGetContact("missing")).rejects.toThrow(
|
|
405
402
|
'Contact "missing" not found',
|
|
406
403
|
);
|
|
407
|
-
expect(
|
|
404
|
+
expect(ipcCalls.map((c) => c.method)).toEqual(["contacts_get_rich"]);
|
|
405
|
+
expect(localCalls).toEqual([]);
|
|
408
406
|
});
|
|
409
407
|
});
|
|
@@ -112,6 +112,8 @@ let reservedRowContent: Map<string, string> = new Map();
|
|
|
112
112
|
let reserveCounter = 0;
|
|
113
113
|
|
|
114
114
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
115
|
+
setConversationProcessingStartedAt: () => {},
|
|
116
|
+
isConversationProcessing: () => false,
|
|
115
117
|
setConversationOriginChannelIfUnset: () => {},
|
|
116
118
|
updateConversationContextWindow: () => {},
|
|
117
119
|
deleteMessageById: () => {},
|
|
@@ -87,6 +87,8 @@ mock.module("../daemon/disk-pressure-guard.js", () => ({
|
|
|
87
87
|
}));
|
|
88
88
|
|
|
89
89
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
90
|
+
setConversationProcessingStartedAt: () => {},
|
|
91
|
+
isConversationProcessing: () => false,
|
|
90
92
|
getConversation: () => ({
|
|
91
93
|
id: "conv-123",
|
|
92
94
|
conversationType: "background",
|
|
@@ -147,6 +147,8 @@ let mockConversationRow: {
|
|
|
147
147
|
};
|
|
148
148
|
|
|
149
149
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
150
|
+
setConversationProcessingStartedAt: () => {},
|
|
151
|
+
isConversationProcessing: () => false,
|
|
150
152
|
setConversationOriginChannelIfUnset: () => {},
|
|
151
153
|
setConversationHistoryStrippedAt: () => {},
|
|
152
154
|
updateConversationUsage: () => {},
|
|
@@ -257,6 +257,8 @@ mock.module("../plugins/defaults/compaction/overflow-policy.js", () => ({
|
|
|
257
257
|
}));
|
|
258
258
|
|
|
259
259
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
260
|
+
setConversationProcessingStartedAt: () => {},
|
|
261
|
+
isConversationProcessing: () => false,
|
|
260
262
|
setConversationOriginChannelIfUnset: () => {},
|
|
261
263
|
setConversationHistoryStrippedAt: () => {},
|
|
262
264
|
updateConversationUsage: () => {},
|
|
@@ -273,6 +273,8 @@ const deleteMessageByIdMock = mock(() => ({
|
|
|
273
273
|
const reserveMessageMock = mock(async () => ({ id: "msg-reserve" }));
|
|
274
274
|
const updateMessageContentMock = mock(() => {});
|
|
275
275
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
276
|
+
setConversationProcessingStartedAt: () => {},
|
|
277
|
+
isConversationProcessing: () => false,
|
|
276
278
|
setConversationOriginChannelIfUnset: () => {},
|
|
277
279
|
updateConversationUsage: () => {},
|
|
278
280
|
updateMessageMetadata: updateMessageMetadataMock,
|
|
@@ -700,6 +702,7 @@ function makeCtx(
|
|
|
700
702
|
mockConversationRow?.slackContextCompactionWatermarkTs ?? null,
|
|
701
703
|
lastNotifiedInferenceProfile:
|
|
702
704
|
mockConversationRow?.lastNotifiedInferenceProfile ?? null,
|
|
705
|
+
processingStartedAt: mockConversationRow?.processingStartedAt ?? null,
|
|
703
706
|
|
|
704
707
|
memoryPolicy: { scopeId: "default", includeDefaultFallback: true },
|
|
705
708
|
|
|
@@ -1705,6 +1708,10 @@ describe("session-agent-loop", () => {
|
|
|
1705
1708
|
expect(ctx.abortController).toBeNull();
|
|
1706
1709
|
expect(ctx.currentRequestId).toBeUndefined();
|
|
1707
1710
|
expect(ctx.commandIntent).toBeUndefined();
|
|
1711
|
+
// Turn-scoped interactivity is stamped during the run and must be cleared
|
|
1712
|
+
// so paths that bypass this loop (e.g. opportunity wakes) don't inherit a
|
|
1713
|
+
// stale value instead of falling back to live client state.
|
|
1714
|
+
expect(ctx.currentTurnIsNonInteractive).toBeUndefined();
|
|
1708
1715
|
});
|
|
1709
1716
|
|
|
1710
1717
|
test("clears state and surfaces a processing error when the provider call fails", async () => {
|
|
@@ -22,6 +22,8 @@ mock.module("../memory/conversation-key-store.js", () => ({
|
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
24
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
25
|
+
setConversationProcessingStartedAt: () => {},
|
|
26
|
+
isConversationProcessing: () => false,
|
|
25
27
|
getConversation: mockGetConversation,
|
|
26
28
|
getMessages: mockGetMessages,
|
|
27
29
|
createConversation: mockCreateConversation,
|
|
@@ -76,6 +76,8 @@ mock.module("../security/secret-allowlist.js", () => ({
|
|
|
76
76
|
}));
|
|
77
77
|
|
|
78
78
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
79
|
+
setConversationProcessingStartedAt: () => {},
|
|
80
|
+
isConversationProcessing: () => false,
|
|
79
81
|
updateConversationContextWindow: () => {},
|
|
80
82
|
deleteMessageById: () => {},
|
|
81
83
|
updateConversationTitle: () => {},
|
|
@@ -112,6 +112,8 @@ mock.module("../security/secret-allowlist.js", () => ({
|
|
|
112
112
|
}));
|
|
113
113
|
|
|
114
114
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
115
|
+
setConversationProcessingStartedAt: () => {},
|
|
116
|
+
isConversationProcessing: () => false,
|
|
115
117
|
setConversationOriginChannelIfUnset: () => {},
|
|
116
118
|
updateConversationContextWindow: () => {},
|
|
117
119
|
deleteMessageById: () => {},
|
|
@@ -36,6 +36,8 @@ let deletedMessageIds: string[] = [];
|
|
|
36
36
|
let updatedMessages: Array<{ id: string; content: string }> = [];
|
|
37
37
|
|
|
38
38
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
39
|
+
setConversationProcessingStartedAt: () => {},
|
|
40
|
+
isConversationProcessing: () => false,
|
|
39
41
|
getMessages: (conversationId: string) =>
|
|
40
42
|
dbMessages.filter((m) => m.conversationId === conversationId),
|
|
41
43
|
deleteMessageById: (messageId: string) => {
|
|
@@ -74,6 +74,8 @@ let mockConversation: Record<string, unknown> | null = null;
|
|
|
74
74
|
let nextMockMessageId = 1;
|
|
75
75
|
|
|
76
76
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
77
|
+
setConversationProcessingStartedAt: () => {},
|
|
78
|
+
isConversationProcessing: () => false,
|
|
77
79
|
updateConversationContextWindow: () => {},
|
|
78
80
|
deleteMessageById: () => {},
|
|
79
81
|
updateConversationTitle: () => {},
|
|
@@ -70,6 +70,8 @@ let mockDbMessages: Array<{
|
|
|
70
70
|
let mockConversation: Record<string, unknown> | null = null;
|
|
71
71
|
|
|
72
72
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
73
|
+
setConversationProcessingStartedAt: () => {},
|
|
74
|
+
isConversationProcessing: () => false,
|
|
73
75
|
updateConversationContextWindow: () => {},
|
|
74
76
|
deleteMessageById: () => {},
|
|
75
77
|
updateConversationTitle: () => {},
|
|
@@ -51,6 +51,8 @@ const getConversationMock = mock((id: string) => {
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
54
|
+
setConversationProcessingStartedAt: () => {},
|
|
55
|
+
isConversationProcessing: () => false,
|
|
54
56
|
setConversationOriginChannelIfUnset: () => {},
|
|
55
57
|
updateConversationContextWindow: () => {},
|
|
56
58
|
deleteMessageById: () => {},
|
|
@@ -46,6 +46,8 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
|
46
46
|
}));
|
|
47
47
|
|
|
48
48
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
49
|
+
setConversationProcessingStartedAt: () => {},
|
|
50
|
+
isConversationProcessing: () => false,
|
|
49
51
|
setConversationOriginChannelIfUnset: () => {},
|
|
50
52
|
setConversationOriginInterfaceIfUnset: () => {},
|
|
51
53
|
provenanceFromTrustContext: () => ({
|
|
@@ -157,6 +157,8 @@ let mockDbMessages: Array<{ id: string; role: string; content: string }> = [];
|
|
|
157
157
|
let mockConversation: Record<string, unknown> | null = null;
|
|
158
158
|
|
|
159
159
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
160
|
+
setConversationProcessingStartedAt: () => {},
|
|
161
|
+
isConversationProcessing: () => false,
|
|
160
162
|
setConversationOriginChannelIfUnset: () => {},
|
|
161
163
|
setConversationOriginInterfaceIfUnset: () => {},
|
|
162
164
|
updateConversationContextWindow: () => {},
|
|
@@ -98,6 +98,8 @@ mock.module("../security/secret-allowlist.js", () => ({
|
|
|
98
98
|
}));
|
|
99
99
|
|
|
100
100
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
101
|
+
setConversationProcessingStartedAt: () => {},
|
|
102
|
+
isConversationProcessing: () => false,
|
|
101
103
|
setConversationOriginChannelIfUnset: () => {},
|
|
102
104
|
deleteMessageById: () => {},
|
|
103
105
|
getMessages: () => [],
|
|
@@ -149,6 +149,8 @@ mock.module("../security/secret-allowlist.js", () => ({
|
|
|
149
149
|
}));
|
|
150
150
|
|
|
151
151
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
152
|
+
setConversationProcessingStartedAt: () => {},
|
|
153
|
+
isConversationProcessing: () => false,
|
|
152
154
|
setConversationOriginChannelIfUnset: () => {},
|
|
153
155
|
setConversationOriginInterfaceIfUnset: () => {},
|
|
154
156
|
updateConversationContextWindow: () => {},
|
|
@@ -2911,3 +2913,92 @@ describe("MessageQueue byte budget", () => {
|
|
|
2911
2913
|
).toBe(false);
|
|
2912
2914
|
});
|
|
2913
2915
|
});
|
|
2916
|
+
|
|
2917
|
+
describe("subagent notification user_message_echo suppression", () => {
|
|
2918
|
+
beforeEach(() => {
|
|
2919
|
+
pendingRuns = [];
|
|
2920
|
+
capturedAddMessages.length = 0;
|
|
2921
|
+
});
|
|
2922
|
+
|
|
2923
|
+
test("drained subagent-notification message persists and wakes the agent but emits no user_message_echo", async () => {
|
|
2924
|
+
const conversation = makeConversation();
|
|
2925
|
+
await conversation.loadFromDb();
|
|
2926
|
+
|
|
2927
|
+
const events1: ServerMessage[] = [];
|
|
2928
|
+
const eventsNotif: ServerMessage[] = [];
|
|
2929
|
+
|
|
2930
|
+
// Occupy the conversation so the injected notification queues.
|
|
2931
|
+
const p1 = conversation.processMessage({
|
|
2932
|
+
content: "msg-1",
|
|
2933
|
+
attachments: [],
|
|
2934
|
+
onEvent: (e) => events1.push(e),
|
|
2935
|
+
requestId: "req-1",
|
|
2936
|
+
});
|
|
2937
|
+
await waitForPendingRun(1);
|
|
2938
|
+
expect(conversation.isProcessing()).toBe(true);
|
|
2939
|
+
|
|
2940
|
+
// A daemon-injected subagent completion notification carries
|
|
2941
|
+
// `subagentNotification` metadata.
|
|
2942
|
+
conversation.enqueueMessage({
|
|
2943
|
+
content: '[Subagent "research" completed]',
|
|
2944
|
+
onEvent: (e) => eventsNotif.push(e),
|
|
2945
|
+
requestId: "req-notif",
|
|
2946
|
+
metadata: {
|
|
2947
|
+
subagentNotification: {
|
|
2948
|
+
subagentId: "sub-1",
|
|
2949
|
+
label: "research",
|
|
2950
|
+
status: "completed",
|
|
2951
|
+
},
|
|
2952
|
+
},
|
|
2953
|
+
});
|
|
2954
|
+
|
|
2955
|
+
// Resolving the first run drains the queued notification.
|
|
2956
|
+
await resolveRun(0);
|
|
2957
|
+
await p1;
|
|
2958
|
+
await waitForPendingRun(2);
|
|
2959
|
+
|
|
2960
|
+
// It is still persisted (so the orchestrator LLM sees it in the transcript)
|
|
2961
|
+
// and still wakes the agent (a run was created for the drained message)...
|
|
2962
|
+
expect(
|
|
2963
|
+
capturedAddMessages.some((m) => m.content.includes("Subagent")),
|
|
2964
|
+
).toBe(true);
|
|
2965
|
+
expect(pendingRuns.length).toBe(2);
|
|
2966
|
+
// ...but no user_message_echo is broadcast, so the client never renders it
|
|
2967
|
+
// as a live user bubble.
|
|
2968
|
+
expect(eventsNotif.some((e) => e.type === "user_message_echo")).toBe(false);
|
|
2969
|
+
|
|
2970
|
+
await resolveRun(1);
|
|
2971
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
2972
|
+
});
|
|
2973
|
+
|
|
2974
|
+
test("drained ordinary message still emits user_message_echo", async () => {
|
|
2975
|
+
const conversation = makeConversation();
|
|
2976
|
+
await conversation.loadFromDb();
|
|
2977
|
+
|
|
2978
|
+
const events1: ServerMessage[] = [];
|
|
2979
|
+
const eventsNormal: ServerMessage[] = [];
|
|
2980
|
+
|
|
2981
|
+
const p1 = conversation.processMessage({
|
|
2982
|
+
content: "msg-1",
|
|
2983
|
+
attachments: [],
|
|
2984
|
+
onEvent: (e) => events1.push(e),
|
|
2985
|
+
requestId: "req-1",
|
|
2986
|
+
});
|
|
2987
|
+
await waitForPendingRun(1);
|
|
2988
|
+
|
|
2989
|
+
conversation.enqueueMessage({
|
|
2990
|
+
content: "ordinary message",
|
|
2991
|
+
onEvent: (e) => eventsNormal.push(e),
|
|
2992
|
+
requestId: "req-normal",
|
|
2993
|
+
});
|
|
2994
|
+
|
|
2995
|
+
await resolveRun(0);
|
|
2996
|
+
await p1;
|
|
2997
|
+
await waitForPendingRun(2);
|
|
2998
|
+
|
|
2999
|
+
expect(eventsNormal.some((e) => e.type === "user_message_echo")).toBe(true);
|
|
3000
|
+
|
|
3001
|
+
await resolveRun(1);
|
|
3002
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
3003
|
+
});
|
|
3004
|
+
});
|