@vellumai/assistant 0.12.2 → 0.12.3-staging.2
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/.env.example +2 -0
- package/AGENTS.md +1 -1
- package/ARCHITECTURE.md +43 -29
- package/Dockerfile +63 -0
- package/docs/architecture/integrations.md +7 -0
- package/docs/architecture/memory.md +17 -2
- package/docs/architecture/security.md +10 -5
- package/docs/architecture/turn-actor.md +2 -0
- package/docs/credential-execution-service.md +1 -1
- package/docs/desktop-browser-cli.md +50 -5
- package/docs/live-voice-task-outcomes.md +100 -0
- package/docs/mode-sessions.md +183 -0
- package/docs/voice-input-diagnostics.md +58 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
- package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
- package/node_modules/@vellumai/environments/src/shell.ts +24 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
- package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
- package/openapi.yaml +716 -16
- package/package.json +1 -1
- package/scripts/smoke-desktop-browser-cli.ts +11 -26
- package/scripts/smoke-desktop-window-theme.ts +2 -3
- package/scripts/sync-llm-catalog.ts +3 -0
- package/scripts/voice-ttft-spike.ts +2 -2
- package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
- package/src/__tests__/agent-loop.test.ts +259 -0
- package/src/__tests__/agent-wake-delegation-prompt.test.ts +64 -2
- package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/answered-question-persistence.test.ts +2 -0
- package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
- package/src/__tests__/anthropic-provider.test.ts +55 -0
- package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
- package/src/__tests__/approval-message-composer.test.ts +1 -118
- package/src/__tests__/approval-routes-http.test.ts +3 -0
- package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
- package/src/__tests__/assistant-event-targeting.test.ts +186 -0
- package/src/__tests__/assistant-stream-state.test.ts +29 -9
- package/src/__tests__/attachments-store.test.ts +22 -3
- package/src/__tests__/browser-fill-credential.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +4 -1
- package/src/__tests__/build-persisted-content.test.ts +1 -1
- package/src/__tests__/call-conversation-messages.test.ts +5 -0
- package/src/__tests__/call-recovery.test.ts +7 -2
- package/src/__tests__/call-routes-http.test.ts +12 -0
- package/src/__tests__/call-store.test.ts +7 -2
- package/src/__tests__/channel-approval.test.ts +9 -14
- package/src/__tests__/channel-reply-delivery.test.ts +56 -0
- package/src/__tests__/channel-retry-sweep.test.ts +9 -0
- package/src/__tests__/chat-credential-redaction.test.ts +23 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
- package/src/__tests__/checker.test.ts +11 -3
- package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
- package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
- package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
- package/src/__tests__/computer-use-tools.test.ts +1 -233
- package/src/__tests__/config-model-image-provider.test.ts +39 -0
- package/src/__tests__/config-schema.test.ts +16 -0
- package/src/__tests__/config-watcher.test.ts +24 -0
- package/src/__tests__/context-search-agent-runner.test.ts +260 -1
- package/src/__tests__/context-search-fanout.test.ts +1 -1
- package/src/__tests__/context-search-types.test.ts +7 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.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 +797 -16
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
- package/src/__tests__/conversation-attachments.test.ts +106 -0
- package/src/__tests__/conversation-evictor.test.ts +62 -37
- package/src/__tests__/conversation-fork-crud.test.ts +191 -0
- package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-rendered-delegation-state.test.ts +276 -0
- package/src/__tests__/conversation-retry-route.test.ts +22 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
- package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
- package/src/__tests__/conversation-undo.test.ts +61 -0
- package/src/__tests__/credential-execution-client.test.ts +72 -55
- package/src/__tests__/credential-prompt-route.test.ts +35 -3
- package/src/__tests__/cu-unified-flow.test.ts +82 -1
- package/src/__tests__/db-conversation-tool-surface.test.ts +221 -0
- package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
- package/src/__tests__/dm-backfill.test.ts +91 -1
- package/src/__tests__/document-create-route.test.ts +88 -0
- package/src/__tests__/document-sync-tags.test.ts +27 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
- package/src/__tests__/events-tail-route.test.ts +58 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
- package/src/__tests__/guardian-action-sweep.test.ts +16 -0
- package/src/__tests__/guardian-dispatch.test.ts +13 -0
- package/src/__tests__/headless-browser-interactions.test.ts +64 -20
- package/src/__tests__/heartbeat-service.test.ts +2 -59
- package/src/__tests__/helpers/mock-conversation.ts +21 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
- package/src/__tests__/history-repair.test.ts +53 -0
- package/src/__tests__/host-cu-proxy.test.ts +46 -2
- package/src/__tests__/http-user-message-parity.test.ts +2 -0
- package/src/__tests__/image-credentials.test.ts +49 -0
- package/src/__tests__/image-service-dispatcher.test.ts +45 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +26 -0
- package/src/__tests__/interrupt-on-send.test.ts +57 -43
- package/src/__tests__/interrupt-turn-note.test.ts +5 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
- package/src/__tests__/llm-catalog-parity.test.ts +27 -4
- package/src/__tests__/media-generate-image.test.ts +49 -1
- package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
- package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
- package/src/__tests__/mode-session-history.test.ts +245 -0
- package/src/__tests__/mode-session-recovery.test.ts +222 -0
- package/src/__tests__/mode-session-wire.test.ts +141 -0
- package/src/__tests__/notification-guardian-path.test.ts +7 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
- package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-commands-routes.test.ts +114 -101
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +13 -0
- package/src/__tests__/oauth-providers-routes.test.ts +3 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/persist-media-references.test.ts +50 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-api-provider.test.ts +5 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -1
- package/src/__tests__/process-message-display-content.test.ts +56 -0
- package/src/__tests__/reaction-persistence.test.ts +5 -0
- package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
- package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
- package/src/__tests__/schedule-routes.test.ts +20 -0
- package/src/__tests__/scheduler-result-notification.test.ts +23 -4
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
- package/src/__tests__/secret-routes-scrub.test.ts +22 -27
- package/src/__tests__/secret-scanner.test.ts +20 -0
- package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
- package/src/__tests__/secure-keys.test.ts +50 -4
- package/src/__tests__/send-endpoint-busy.test.ts +163 -1
- package/src/__tests__/server-history-render.test.ts +91 -1
- package/src/__tests__/server-tool-pairing.test.ts +107 -0
- package/src/__tests__/settings-routes.test.ts +50 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
- package/src/__tests__/skills.test.ts +5 -4
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
- package/src/__tests__/subagent-manager-notify.test.ts +1 -2
- package/src/__tests__/subagent-notify-parent.test.ts +146 -39
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
- package/src/__tests__/subagent-tool-gate-mode.test.ts +226 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/thread-backfill.test.ts +53 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-follow-up.test.ts +99 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +66 -1
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/twilio-routes.test.ts +1 -0
- package/src/__tests__/unicode.test.ts +36 -0
- package/src/__tests__/web-search-backend-failure.test.ts +1 -1
- package/src/activation/progress-store.test.ts +12 -7
- package/src/agent/loop.ts +136 -53
- package/src/agent/tool-result-follow-up.ts +70 -0
- package/src/api/attachment-provenance.test.ts +68 -0
- package/src/api/computer-use-tool.test.ts +55 -0
- package/src/api/computer-use-tool.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +3 -0
- package/src/api/events/assistant-turn-start.ts +3 -0
- package/src/api/events/desktop-activity-changed.ts +10 -0
- package/src/api/events/generation-handoff.ts +2 -0
- package/src/api/events/message-complete.ts +2 -0
- package/src/api/events/notification-conversation-created.ts +14 -16
- package/src/api/events/notification-intent.ts +4 -2
- package/src/api/events/question-request.ts +1 -0
- package/src/api/events/tool-result.ts +85 -0
- package/src/api/events/tool-use-start.ts +3 -0
- package/src/api/events/user-message-echo.ts +3 -0
- package/src/api/index.ts +42 -6
- package/src/api/mode-session.test.ts +131 -0
- package/src/api/mode-session.ts +148 -0
- package/src/api/remember-tool.ts +15 -0
- package/src/api/responses/conversation-message.ts +9 -0
- package/src/approvals/AGENTS.md +4 -2
- package/src/approvals/approval-primitive.ts +5 -2
- package/src/approvals/scoped-approval-grants.ts +6 -2
- package/src/browser/__tests__/operations.test.ts +29 -0
- package/src/browser/operation-meta.ts +2 -1
- package/src/browser/operations.ts +29 -0
- package/src/browser/virtual-desktop-target.ts +2 -9
- package/src/bundler/bundle-signer.ts +1 -3
- package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
- package/src/calls/__tests__/call-controller.test.ts +94 -0
- package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
- package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
- package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
- package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
- package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
- package/src/calls/barge-in-guard.ts +5 -0
- package/src/calls/call-controller.ts +196 -1
- package/src/calls/call-domain.ts +4 -0
- package/src/calls/call-store.ts +87 -2
- package/src/calls/guardian-action-sweep.ts +22 -8
- package/src/calls/media-stream-audio-transcode.ts +1 -3
- package/src/calls/typesafe-noul.ts +156 -0
- package/src/calls/voice-control-protocol.ts +6 -0
- package/src/calls/voice-escalation-judge.ts +145 -0
- package/src/calls/voice-escalation-target.ts +10 -0
- package/src/calls/voice-leg-coordinator.ts +121 -3
- package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
- package/src/calls/voice-session-bridge.ts +256 -112
- package/src/calls/voice-triage-escalate.ts +7 -0
- package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
- package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
- package/src/cli/commands/__tests__/notifications.test.ts +35 -10
- package/src/cli/commands/__tests__/plugins.test.ts +20 -1
- package/src/cli/commands/__tests__/schedules.test.ts +14 -0
- package/src/cli/commands/bash.help.ts +4 -3
- package/src/cli/commands/browser.help.ts +9 -6
- package/src/cli/commands/browser.ts +0 -1
- package/src/cli/commands/clients.help.ts +46 -0
- package/src/cli/commands/clients.ts +116 -0
- package/src/cli/commands/credentials.help.ts +9 -4
- package/src/cli/commands/notifications.help.ts +16 -2
- package/src/cli/commands/notifications.ts +30 -8
- package/src/cli/commands/oauth/index.help.ts +10 -0
- package/src/cli/commands/oauth/providers.ts +12 -0
- package/src/cli/commands/oauth/request.test.ts +179 -2
- package/src/cli/commands/oauth/request.ts +41 -10
- package/src/cli/commands/plugins.ts +12 -5
- package/src/cli/commands/roadmap.help.ts +18 -21
- package/src/cli/commands/roadmap.ts +4 -21
- package/src/cli/commands/schedules.ts +2 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +41 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
- package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +133 -51
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +8 -2
- package/src/cli/lib/bundled-marketplace.json +906 -1
- package/src/cli/lib/bundled-plugin-packages.json +783 -1
- package/src/cli/lib/inspect-plugin.ts +11 -4
- package/src/cli/lib/plugin-marketplace.ts +8 -0
- package/src/cli/lib/publish-plugin.ts +1 -2
- package/src/cli/lib/upgrade-plugin.ts +19 -11
- package/src/cli/output.ts +18 -4
- package/src/config/__tests__/default-provider.test.ts +6 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
- package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
- package/src/config/bundled-skills/schedule/SKILL.md +6 -6
- package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
- package/src/config/call-site-defaults.ts +9 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +9 -17
- package/src/config/platform-identity.ts +8 -8
- package/src/config/profile-text-generation.test.ts +51 -0
- package/src/config/profile-text-generation.ts +51 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +12 -0
- package/src/config/schemas/__tests__/tools.test.ts +31 -0
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -18
- package/src/config/schemas/llm.ts +9 -2
- package/src/config/schemas/mcp.ts +6 -1
- package/src/config/schemas/memory-retrospective.ts +11 -2
- package/src/config/schemas/memory-v3.ts +12 -0
- package/src/config/schemas/services.ts +15 -6
- package/src/config/schemas/tools.ts +10 -0
- package/src/config/session-groups-gate.ts +21 -0
- package/src/config/skills.ts +2 -6
- package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
- package/src/conversations/message-consolidation.ts +102 -5
- package/src/credential-execution/ces-connect.test.ts +69 -0
- package/src/credential-execution/ces-connect.ts +115 -0
- package/src/credential-execution/ces-runtime.ts +55 -93
- package/src/credential-execution/executable-discovery.ts +34 -142
- package/src/credential-execution/process-manager.test.ts +20 -24
- package/src/credential-execution/process-manager.ts +24 -19
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +43 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
- package/src/daemon/assistant-attachments.ts +20 -12
- package/src/daemon/browser-mode-session.test.ts +402 -0
- package/src/daemon/browser-mode-session.ts +186 -0
- package/src/daemon/chat-credential-redaction.ts +16 -1
- package/src/daemon/computer-use-mode-session.test.ts +210 -0
- package/src/daemon/computer-use-mode-session.ts +105 -0
- package/src/daemon/config-watcher.ts +10 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +132 -13
- package/src/daemon/conversation-agent-loop.ts +231 -25
- package/src/daemon/conversation-attachments.ts +93 -12
- package/src/daemon/conversation-evictor.ts +5 -24
- package/src/daemon/conversation-history.ts +10 -0
- package/src/daemon/conversation-interrupt-repair.ts +4 -43
- package/src/daemon/conversation-interrupt.ts +7 -11
- package/src/daemon/conversation-messaging.ts +58 -5
- package/src/daemon/conversation-mode-session.test.ts +1667 -0
- package/src/daemon/conversation-mode-session.ts +1008 -0
- package/src/daemon/conversation-process.ts +5 -0
- package/src/daemon/conversation-runtime-assembly.ts +10 -20
- package/src/daemon/conversation-skill-tools.ts +1 -0
- package/src/daemon/conversation-store.ts +4 -4
- package/src/daemon/conversation-surface-snapshots.ts +135 -0
- package/src/daemon/conversation-surfaces.ts +113 -5
- package/src/daemon/conversation-tool-setup.ts +79 -6
- package/src/daemon/conversation-turn-finalize.ts +26 -13
- package/src/daemon/conversation.ts +215 -58
- package/src/daemon/daemon-control.ts +3 -7
- package/src/daemon/handlers/config-model.ts +7 -5
- package/src/daemon/handlers/shared.ts +55 -16
- package/src/daemon/host-cu-proxy.ts +51 -22
- package/src/daemon/lifecycle.ts +30 -13
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/message-protocol.ts +0 -1
- package/src/daemon/message-provenance.ts +28 -4
- package/src/daemon/message-types/shared.ts +1 -0
- package/src/daemon/mode-session-startup-recovery.ts +23 -0
- package/src/daemon/mode-session-tracking.ts +54 -0
- package/src/daemon/orphan-reaper.ts +4 -3
- package/src/daemon/persist-media-references.ts +20 -3
- package/src/daemon/process-message.ts +13 -4
- package/src/daemon/tool-setup-types.ts +6 -0
- package/src/daemon/virtual-desktop-context.ts +21 -0
- package/src/daemon/wake-conversation-ops.ts +50 -15
- package/src/desktop/__tests__/fake-desktop.ts +1 -0
- package/src/desktop/desktop-accessibility-script.ts +88 -0
- package/src/desktop/desktop-accessibility.test.ts +160 -0
- package/src/desktop/desktop-accessibility.ts +169 -0
- package/src/desktop/desktop-automation-lease.test.ts +302 -64
- package/src/desktop/desktop-automation-lease.ts +129 -54
- package/src/desktop/desktop-browser-endpoint.ts +4 -0
- package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
- package/src/desktop/desktop-computer-use.test.ts +449 -0
- package/src/desktop/desktop-computer-use.ts +502 -0
- package/src/desktop/desktop-dependencies.ts +47 -293
- package/src/desktop/desktop-display.ts +2 -2
- package/src/desktop/desktop-help.ts +15 -0
- package/src/desktop/desktop-panel-config.test.ts +13 -3
- package/src/desktop/desktop-panel-config.ts +109 -15
- package/src/desktop/desktop-session-manager.test.ts +69 -21
- package/src/desktop/desktop-session-manager.ts +76 -22
- package/src/desktop/desktop-stream-bridge.test.ts +76 -132
- package/src/desktop/desktop-stream-bridge.ts +9 -11
- package/src/desktop/desktop-wallpaper-renderer.ts +132 -0
- package/src/desktop/desktop-wallpaper-worker.ts +17 -0
- package/src/desktop/desktop-wallpaper.test.ts +48 -7
- package/src/desktop/desktop-wallpaper.ts +34 -102
- package/src/desktop/desktop-window-theme.ts +5 -25
- package/src/desktop/virtual-desktop-feature.ts +53 -3
- package/src/desktop/virtual-desktop-platform.test.ts +5 -9
- package/src/documents/document-store.ts +23 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
- package/src/heartbeat/heartbeat-service.ts +5 -33
- package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
- package/src/ipc/assistant-server.ts +2 -0
- package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
- package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
- package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
- package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
- package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
- package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
- package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
- package/src/live-voice/__tests__/session-controls.test.ts +23 -0
- package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
- package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
- package/src/live-voice/camera-mode-session.test.ts +265 -0
- package/src/live-voice/camera-mode-session.ts +277 -0
- package/src/live-voice/continuation-judge.ts +123 -0
- package/src/live-voice/live-activity-reporter.ts +115 -6
- package/src/live-voice/live-voice-manager.ts +36 -1
- package/src/live-voice/live-voice-photo.ts +88 -7
- package/src/live-voice/live-voice-session-manager.ts +15 -0
- package/src/live-voice/live-voice-session.ts +1443 -747
- package/src/live-voice/live-voice-tts.ts +22 -4
- package/src/live-voice/microphone-idle-gate.ts +56 -0
- package/src/live-voice/protocol.ts +171 -4
- package/src/live-voice/session-controls.ts +11 -3
- package/src/live-voice/task-outcomes.ts +131 -0
- package/src/live-voice/voice-input-diagnostics.ts +117 -0
- package/src/mcp/__tests__/manager-tool-caps.test.ts +156 -0
- package/src/mcp/__tests__/startup.test.ts +30 -7
- package/src/mcp/__tests__/tool-caps.test.ts +134 -0
- package/src/mcp/manager.ts +171 -101
- package/src/mcp/startup.ts +33 -13
- package/src/mcp/tool-caps.ts +184 -0
- package/src/media/__tests__/image-models.test.ts +58 -0
- package/src/media/image-credentials.ts +5 -0
- package/src/media/image-models.ts +52 -0
- package/src/media/image-service.ts +8 -0
- package/src/media/openrouter-image-service.test.ts +133 -0
- package/src/media/openrouter-image-service.ts +153 -0
- package/src/media/types.ts +1 -1
- package/src/messaging/provider-message-metadata.ts +3 -3
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
- package/src/messaging/providers/discord/send.ts +4 -17
- package/src/messaging/providers/discord/transport.ts +2 -2
- package/src/messaging/providers/outlook/client.ts +16 -22
- package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
- package/src/messaging/providers/telegram-bot/send.ts +10 -7
- package/src/messaging/providers/telegram-bot/transport.ts +3 -10
- package/src/messaging/providers/whatsapp/send.test.ts +169 -4
- package/src/monitoring/__tests__/control.test.ts +17 -5
- package/src/monitoring/control.ts +15 -5
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/README.md +83 -99
- package/src/notifications/__tests__/broadcaster.test.ts +1 -1
- package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
- package/src/notifications/__tests__/copy-composer.test.ts +71 -1
- package/src/notifications/__tests__/decision-engine.test.ts +79 -2
- package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
- package/src/notifications/__tests__/notification-utils.test.ts +18 -0
- package/src/notifications/adapters/macos.ts +37 -46
- package/src/notifications/broadcaster.ts +5 -6
- package/src/notifications/channel-allowlist.ts +44 -0
- package/src/notifications/copy-composer.ts +11 -26
- package/src/notifications/decision-engine.ts +24 -14
- package/src/notifications/deterministic-checks.ts +1 -1
- package/src/notifications/emit-signal.ts +123 -37
- package/src/notifications/home-feed-side-effect.ts +9 -7
- package/src/notifications/notification-utils.ts +19 -0
- package/src/notifications/schedule-result-producer.ts +4 -6
- package/src/notifications/signal.ts +4 -19
- package/src/notifications/types.ts +15 -0
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
- package/src/oauth/connection-resolver.ts +1 -2
- package/src/oauth/identity-verifier.ts +24 -0
- package/src/oauth/oauth-store.ts +12 -4
- package/src/oauth/provider-serializer.ts +8 -0
- package/src/oauth/seed-providers.ts +42 -0
- package/src/onboarding/onboarding-events-store.ts +99 -12
- package/src/permissions/confirmation-guardian-request.test.ts +16 -0
- package/src/permissions/confirmation-guardian-request.ts +2 -3
- package/src/permissions/question-prompter.test.ts +31 -0
- package/src/permissions/question-prompter.ts +2 -0
- package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
- package/src/persistence/attachments-store.ts +84 -44
- package/src/persistence/client-connection-events-store.ts +377 -0
- package/src/persistence/conversation-crud.ts +166 -26
- package/src/persistence/conversation-mode-sessions.test.ts +424 -0
- package/src/persistence/conversation-mode-sessions.ts +460 -0
- package/src/persistence/conversation-plugin-facade.ts +30 -0
- package/src/persistence/conversation-queries.ts +6 -19
- package/src/persistence/conversation-tool-surface.ts +135 -0
- package/src/persistence/conversation-types.test.ts +32 -0
- package/src/persistence/conversation-types.ts +26 -4
- package/src/persistence/llm-usage-store.ts +3 -9
- package/src/persistence/message-metadata.ts +25 -0
- package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +78 -0
- package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
- package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
- package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
- package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.test.ts +90 -0
- package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.ts +34 -0
- package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
- package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
- package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
- package/src/persistence/schema/conversation-tool-surfaces.ts +32 -0
- package/src/persistence/schema/conversations.ts +67 -0
- package/src/persistence/schema/index.ts +1 -0
- package/src/persistence/schema/infrastructure.ts +29 -0
- package/src/persistence/schema/oauth.ts +1 -0
- package/src/persistence/steps.ts +41 -3
- package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
- package/src/plugin-api/conversation-turn.ts +31 -7
- package/src/plugin-api/effective-context-window.ts +33 -0
- package/src/plugin-api/index.ts +35 -6
- package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
- package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
- package/src/plugin-api/plugin-name-env.ts +125 -0
- package/src/plugin-api/resolve-credential.ts +79 -20
- package/src/plugin-api/vision-support.test.ts +22 -0
- package/src/plugin-api/vision-support.ts +17 -5
- package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
- package/src/plugins/active-plugin-env.ts +51 -0
- package/src/plugins/defaults/index.ts +24 -0
- package/src/plugins/defaults/memory/AGENTS.md +39 -5
- package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
- package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
- package/src/plugins/defaults/memory/__tests__/conversation-memory-purge.test.ts +1 -0
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +74 -0
- package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +161 -0
- package/src/plugins/defaults/memory/__tests__/relocated-memory-test-rows.ts +10 -0
- package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
- package/src/plugins/defaults/memory/buffer-file.ts +354 -0
- package/src/plugins/defaults/memory/buffer-format.ts +40 -0
- package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +77 -22
- package/src/plugins/defaults/memory/context-search/format.ts +53 -8
- package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
- package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
- package/src/plugins/defaults/memory/context-search/search.ts +8 -14
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -1
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +2 -1
- package/src/plugins/defaults/memory/context-search/types.ts +1 -11
- package/src/plugins/defaults/memory/conversation-memory-purge.ts +4 -0
- package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +1 -2
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +13 -63
- package/src/plugins/defaults/memory/graph/tools.ts +35 -64
- package/src/plugins/defaults/memory/host-utils.ts +0 -10
- package/src/plugins/defaults/memory/injectors.ts +4 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +72 -183
- package/src/plugins/defaults/memory/memory-run-evidence.ts +213 -0
- package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
- package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +6 -1
- package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
- package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +2 -1
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +91 -51
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
- package/src/plugins/defaults/memory/tools.test.ts +124 -9
- package/src/plugins/defaults/memory/tools.ts +38 -11
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +2 -1
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
- package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
- package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
- package/src/plugins/defaults/memory/v2/reranker.ts +2 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -0
- package/src/plugins/defaults/memory/v3/__tests__/plugin-schema.test.ts +12 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +332 -15
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +148 -28
- package/src/plugins/defaults/memory/v3/card.ts +2 -1
- package/src/plugins/defaults/memory/v3/injector.ts +212 -178
- package/src/plugins/defaults/memory/v3/orchestrate.ts +159 -32
- package/src/plugins/defaults/memory/v3/plugin-schema.ts +54 -4
- package/src/plugins/defaults/memory/v3/pool-log-store.ts +275 -37
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +47 -5
- package/src/plugins/defaults/memory/v3/pool-select.ts +710 -59
- package/src/plugins/defaults/memory/v3/sections.ts +2 -1
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +41 -14
- package/src/plugins/defaults/memory/worker-control.ts +15 -5
- package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
- package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
- package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
- package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
- package/src/plugins/defaults/task-progress/package.json +14 -0
- package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
- package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +4 -1
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
- package/src/prompts/__tests__/parallel-tasks-section.test.ts +25 -0
- package/src/prompts/delegation-gate.ts +57 -0
- package/src/prompts/system-prompt.ts +14 -32
- package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
- package/src/providers/anthropic/client.ts +10 -1
- package/src/providers/anthropic-gateway-shared.ts +6 -0
- package/src/providers/fetch-provider-catalog.ts +11 -0
- package/src/providers/gemini/client.ts +25 -8
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/jev/client.test.ts +270 -0
- package/src/providers/jev/client.ts +534 -0
- package/src/providers/model-catalog.ts +59 -3
- package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
- package/src/providers/openai/chat-completions-provider.ts +100 -29
- package/src/providers/openai/google-thought-signature.ts +0 -6
- package/src/providers/openai/responses-provider.ts +23 -1
- package/src/providers/ratelimit.ts +1 -3
- package/src/providers/search-provider-catalog.ts +24 -0
- package/src/providers/server-tool-pairing.ts +16 -7
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
- package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
- package/src/providers/types.ts +5 -0
- package/src/routes/control.ts +7 -5
- package/src/runtime/AGENTS.md +6 -4
- package/src/runtime/__tests__/agent-wake.test.ts +94 -1
- package/src/runtime/actor-trust-resolver.ts +3 -3
- package/src/runtime/agent-wake.ts +32 -5
- package/src/runtime/approval-message-composer.ts +3 -69
- package/src/runtime/assistant-event-hub.ts +73 -75
- package/src/runtime/assistant-event-publish-options.ts +7 -0
- package/src/runtime/assistant-event-targeting.ts +79 -0
- package/src/runtime/assistant-stream-state.ts +13 -100
- package/src/runtime/background-job-runner.ts +5 -5
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/guardian-action-message-composer.ts +8 -161
- package/src/runtime/guardian-action-service.ts +2 -17
- package/src/runtime/guardian-reply-router.ts +29 -40
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +6 -0
- package/src/runtime/message-composer-types.ts +3 -61
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
- package/src/runtime/migrations/vbundle-builder.ts +24 -1
- package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +43 -11
- package/src/runtime/migrations/vbundle-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-validator.ts +1 -0
- package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
- package/src/runtime/pending-interactions.ts +29 -0
- package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
- package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +24 -2
- package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
- package/src/runtime/routes/browser-context.ts +31 -11
- package/src/runtime/routes/browser-routes.ts +43 -4
- package/src/runtime/routes/btw-routes.ts +9 -4
- package/src/runtime/routes/channel-route-shared.ts +1 -9
- package/src/runtime/routes/client-routes.ts +108 -0
- package/src/runtime/routes/conversation-management-routes.ts +4 -1
- package/src/runtime/routes/conversation-routes.ts +125 -14
- package/src/runtime/routes/credential-prompt-routes.ts +4 -3
- package/src/runtime/routes/credential-routes.ts +1 -2
- package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
- package/src/runtime/routes/desktop-setup-routes.ts +8 -7
- package/src/runtime/routes/documents-routes.ts +51 -0
- package/src/runtime/routes/email-routes.ts +3 -7
- package/src/runtime/routes/events-routes.ts +75 -54
- package/src/runtime/routes/guardian-approval-interception.ts +24 -0
- package/src/runtime/routes/identity-routes.ts +1 -5
- package/src/runtime/routes/image-generation-routes.ts +8 -14
- package/src/runtime/routes/inbound-message-handler.ts +37 -41
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +39 -10
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +9 -9
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
- package/src/runtime/routes/inference-profiles-routes.ts +9 -0
- package/src/runtime/routes/llm-context-normalization.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +73 -2
- package/src/runtime/routes/notification-routes.ts +10 -0
- package/src/runtime/routes/oauth-commands-routes.ts +57 -54
- package/src/runtime/routes/oauth-providers.ts +11 -0
- package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
- package/src/runtime/routes/oauth-request-hints.ts +149 -0
- package/src/runtime/routes/plugins-routes.ts +6 -0
- package/src/runtime/routes/roadmap-routes.ts +47 -8
- package/src/runtime/routes/schedule-routes.ts +2 -0
- package/src/runtime/routes/secret-routes.ts +99 -119
- package/src/runtime/routes/settings-routes.ts +10 -4
- package/src/runtime/routes/watcher-routes.ts +1 -3
- package/src/runtime/trust-class.test.ts +7 -6
- package/src/runtime/trust-class.ts +9 -17
- package/src/schedule/__tests__/worker-control.test.ts +36 -5
- package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
- package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
- package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
- package/src/schedule/run-script.ts +2 -2
- package/src/schedule/scheduler.ts +47 -11
- package/src/schedule/tool-surface-readiness.ts +65 -0
- package/src/schedule/worker-control.ts +15 -5
- package/src/schedule/worker-mcp.ts +85 -0
- package/src/schedule/worker.ts +9 -31
- package/src/security/secure-keys.ts +74 -112
- package/src/skills/clawhub.ts +3 -8
- package/src/skills/managed-store.ts +1 -5
- package/src/stt/types.ts +4 -0
- package/src/subagent/notify.ts +54 -34
- package/src/subagent/parent-notification.ts +6 -0
- package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
- package/src/telemetry/phone-call-funnel.ts +144 -0
- package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
- package/src/tools/ask-question/ask-question-tool.test.ts +167 -5
- package/src/tools/ask-question/ask-question-tool.ts +120 -22
- package/src/tools/browser/browser-execution.ts +29 -60
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
- package/src/tools/client-os.ts +15 -0
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
- package/src/tools/computer-use/target.ts +42 -0
- package/src/tools/document/document-tool.ts +13 -18
- package/src/tools/execution-target.ts +14 -16
- package/src/tools/host-terminal/host-shell.ts +16 -7
- package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
- package/src/tools/network/__tests__/web-search.test.ts +203 -0
- package/src/tools/network/web-fetch.ts +274 -7
- package/src/tools/network/web-search.ts +468 -6
- package/src/tools/permission-checker.ts +3 -3
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
- package/src/tools/shared/shell-output.test.ts +10 -0
- package/src/tools/shared/shell-output.ts +14 -2
- package/src/tools/skills/load.ts +2 -1
- package/src/tools/skills/sandbox-runner.ts +24 -4
- package/src/tools/skills/scaffold-managed.ts +2 -1
- package/src/tools/skills/skill-script-runner.ts +7 -0
- package/src/tools/skills/skill-tool-factory.ts +15 -1
- package/src/tools/terminal/__tests__/safe-env.test.ts +44 -4
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -14
- package/src/tools/terminal/safe-env.ts +39 -24
- package/src/tools/terminal/sanitized-bash.ts +15 -2
- package/src/tools/terminal/shell-launch.test.ts +162 -0
- package/src/tools/terminal/shell.test.ts +29 -0
- package/src/tools/terminal/shell.ts +15 -7
- package/src/tools/tool-approval-handler.ts +5 -1
- package/src/tools/tool-types.ts +3 -3
- package/src/tools/types.ts +8 -2
- package/src/util/__tests__/worker-process-command.test.ts +5 -4
- package/src/util/abort-reasons.ts +18 -26
- package/src/util/browser-human-verification.ts +14 -0
- package/src/util/canonicalize-identity.test.ts +21 -0
- package/src/util/canonicalize-identity.ts +16 -0
- package/src/util/errors.ts +13 -3
- package/src/util/host-process.test.ts +17 -1
- package/src/util/host-process.ts +24 -0
- package/src/util/provider-error-patterns.ts +3 -0
- package/src/util/unicode.ts +29 -0
- package/src/util/worker-process.ts +77 -35
- package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
- package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/computer-use-window-schema.test.ts +0 -24
- package/src/api/events/heartbeat-alert.ts +0 -20
- package/src/daemon/message-types/web-activity.ts +0 -65
- package/src/desktop/desktop-dependencies.test.ts +0 -131
- package/src/tools/computer-use/definitions.ts +0 -607
|
@@ -33,6 +33,10 @@ import {
|
|
|
33
33
|
import { AnsweredQuestionSchema } from "../events/question-answered.js";
|
|
34
34
|
import { QuestionEntrySchema } from "../events/question-request.js";
|
|
35
35
|
import { ToolActivityMetadataSchema } from "../events/tool-result.js";
|
|
36
|
+
import {
|
|
37
|
+
ModeSessionActivitySchema,
|
|
38
|
+
ModeSessionSchema,
|
|
39
|
+
} from "../mode-session.js";
|
|
36
40
|
|
|
37
41
|
// ---------------------------------------------------------------------------
|
|
38
42
|
// Attachment metadata
|
|
@@ -51,6 +55,9 @@ export const ConversationMessageAttachmentSchema = z.object({
|
|
|
51
55
|
thumbnailData: z.string().optional(),
|
|
52
56
|
/** True when the attachment bytes are backed by a file on disk. */
|
|
53
57
|
fileBacked: z.boolean().optional(),
|
|
58
|
+
/** True when this computer-use screenshot was placed on the reply
|
|
59
|
+
* automatically. Missing or false identifies legacy or explicit placement. */
|
|
60
|
+
computerUseScreenshot: z.boolean().optional(),
|
|
54
61
|
});
|
|
55
62
|
export type ConversationMessageAttachment = z.infer<
|
|
56
63
|
typeof ConversationMessageAttachmentSchema
|
|
@@ -489,6 +496,8 @@ export type ConversationContentBlock = z.infer<
|
|
|
489
496
|
*/
|
|
490
497
|
export const ConversationMessageSchema = z.object({
|
|
491
498
|
id: z.string(),
|
|
499
|
+
modeSession: ModeSessionSchema.optional(),
|
|
500
|
+
modeSessionActivity: ModeSessionActivitySchema.optional(),
|
|
492
501
|
/**
|
|
493
502
|
* Server message ids folded into this display row when consecutive
|
|
494
503
|
* assistant messages were consolidated for history rendering.
|
package/src/approvals/AGENTS.md
CHANGED
|
@@ -11,7 +11,9 @@ The end-to-end lifecycle of interactive guardian requests (approvals, questions)
|
|
|
11
11
|
|
|
12
12
|
## Single-Guardian Invariant
|
|
13
13
|
|
|
14
|
-
Each assistant instance serves exactly one guardian. Multi-guardian is not supported and will never be.
|
|
14
|
+
Each assistant instance serves exactly one guardian. Multi-guardian is not supported and will never be. Only that guardian's principal decides guardian requests: `applyGuardianDecision()` requires it exactly. Do not introduce guardian-keyed maps, per-guardian routing logic, or multi-guardian multiplexing; they add complexity without a real use case and create the false impression that cross-guardian isolation is required.
|
|
15
|
+
|
|
16
|
+
One guardian does not mean one person connected. The event stream (`GET /v1/events`) admits any actor principal with `chat.read`, so a non-guardian actor can hold a valid connection. Routes that must be guardian-only set `requireGuardian: true`, which `routes/http-adapter.ts` enforces with `requireBoundGuardian` (`runtime/auth/require-bound-guardian.ts`). Content meant only for the guardian is scoped to the guardian's principal at delivery (`targetActorPrincipalId` on the event hub), never assumed safe because it reached a connection.
|
|
15
17
|
|
|
16
18
|
## Guardian Verification Invariant
|
|
17
19
|
|
|
@@ -21,7 +23,7 @@ Conversational guardian verification control-plane invocation is guardian-only.
|
|
|
21
23
|
|
|
22
24
|
## Memory Provenance Invariant
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
Memory access follows the acting actor's capabilities: `resolveCapabilities(trustClass).canAccessMemory`, which only `guardian` holds. An actor without it neither reads nor writes the guardian's memory. The memory plugin's `injectors.ts` admits personal-memory content only for a turn whose trust context passes `isPersonalMemoryAllowed`; the `recall`, `remember`, and `delete_memory_page` tools refuse such a turn (`plugins/defaults/memory/tools.ts`); and extraction from conversation messages runs only for guardian-provenance rows, or legacy rows with none (`plugins/defaults/memory/indexer.ts`).
|
|
25
27
|
|
|
26
28
|
## Guardian Privilege Isolation Invariant
|
|
27
29
|
|
|
@@ -213,7 +213,10 @@ function consumeGrantSync(params: ConsumeGrantParams): ConsumeGrantResult {
|
|
|
213
213
|
return { ok: true, grant: reqResult.grant };
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
// Debug, not info: `consumeGrantForInvocation` and the inline grant wait
|
|
217
|
+
// poll this function, so a miss is the expected steady state while a
|
|
218
|
+
// guardian decides. Hits and the wait's outcome are logged at info.
|
|
219
|
+
log.debug(
|
|
217
220
|
{
|
|
218
221
|
event: "approval_primitive_consume_miss",
|
|
219
222
|
mode: "request_id",
|
|
@@ -255,7 +258,7 @@ function consumeGrantSync(params: ConsumeGrantParams): ConsumeGrantResult {
|
|
|
255
258
|
return { ok: true, grant: sigResult.grant };
|
|
256
259
|
}
|
|
257
260
|
|
|
258
|
-
log.
|
|
261
|
+
log.debug(
|
|
259
262
|
{
|
|
260
263
|
event: "approval_primitive_consume_miss",
|
|
261
264
|
mode: "tool_signature",
|
|
@@ -193,7 +193,10 @@ function consumeScopedApprovalGrantByRequestId(
|
|
|
193
193
|
.get();
|
|
194
194
|
|
|
195
195
|
if (!candidate) {
|
|
196
|
-
|
|
196
|
+
// Debug, not info: the inline grant wait and the voice retry loop call
|
|
197
|
+
// this every poll, so a miss is the expected steady state while a
|
|
198
|
+
// guardian decides. The caller logs the outcome of the wait.
|
|
199
|
+
log.debug(
|
|
197
200
|
{
|
|
198
201
|
event: "scoped_grant_consume_miss",
|
|
199
202
|
requestId,
|
|
@@ -364,7 +367,8 @@ function consumeScopedApprovalGrantByToolSignature(
|
|
|
364
367
|
.get();
|
|
365
368
|
|
|
366
369
|
if (!candidate) {
|
|
367
|
-
|
|
370
|
+
// Debug for the same reason as the request-id miss above: polled.
|
|
371
|
+
log.debug(
|
|
368
372
|
{
|
|
369
373
|
event: "scoped_grant_consume_miss",
|
|
370
374
|
toolName: params.toolName,
|
|
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
BROWSER_OPERATION_META,
|
|
5
|
+
browserOperationLifecycle,
|
|
5
6
|
executeBrowserOperation,
|
|
6
7
|
} from "../operations.js";
|
|
7
8
|
import { BROWSER_OPERATIONS, type BrowserOperation } from "../types.js";
|
|
@@ -19,6 +20,34 @@ describe("browser operations contract", () => {
|
|
|
19
20
|
expect(BROWSER_OPERATION_META).toHaveLength(BROWSER_OPERATIONS.length);
|
|
20
21
|
});
|
|
21
22
|
|
|
23
|
+
test("classifies every typed operation for session lifecycle", () => {
|
|
24
|
+
const classifications = Object.fromEntries(
|
|
25
|
+
BROWSER_OPERATIONS.map((operation) => [
|
|
26
|
+
operation,
|
|
27
|
+
browserOperationLifecycle(operation),
|
|
28
|
+
]),
|
|
29
|
+
);
|
|
30
|
+
expect(classifications).toEqual({
|
|
31
|
+
navigate: "action",
|
|
32
|
+
snapshot: "action",
|
|
33
|
+
screenshot: "action",
|
|
34
|
+
close: "terminal",
|
|
35
|
+
attach: "action",
|
|
36
|
+
detach: "terminal",
|
|
37
|
+
click: "action",
|
|
38
|
+
type: "action",
|
|
39
|
+
press_key: "action",
|
|
40
|
+
scroll: "action",
|
|
41
|
+
select_option: "action",
|
|
42
|
+
hover: "action",
|
|
43
|
+
wait_for: "action",
|
|
44
|
+
extract: "action",
|
|
45
|
+
wait_for_download: "action",
|
|
46
|
+
fill_credential: "action",
|
|
47
|
+
status: "status",
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
22
51
|
// ── Every operation has a dispatch handler ─────────────────────────
|
|
23
52
|
|
|
24
53
|
test("every operation has a dispatch handler (rejects unknown)", async () => {
|
|
@@ -216,7 +216,7 @@ Examples:
|
|
|
216
216
|
name: "key",
|
|
217
217
|
type: "string",
|
|
218
218
|
description:
|
|
219
|
-
'The key to press (e.g. "Enter", "
|
|
219
|
+
'The key or modifier chord to press (e.g. "Enter", "Meta+v", "Control+a").',
|
|
220
220
|
required: true,
|
|
221
221
|
},
|
|
222
222
|
{
|
|
@@ -238,6 +238,7 @@ the key is dispatched to that element; otherwise to the focused element.
|
|
|
238
238
|
Examples:
|
|
239
239
|
$ assistant browser press-key --key Enter
|
|
240
240
|
$ assistant browser press-key --key Tab --element-id e5
|
|
241
|
+
$ assistant browser press-key --key "Meta+v"
|
|
241
242
|
$ assistant browser press-key --key Escape`,
|
|
242
243
|
},
|
|
243
244
|
{
|
|
@@ -36,6 +36,35 @@ import type { ToolExecutionResult } from "../tools/types.js";
|
|
|
36
36
|
import type { BrowserOperationContext as ToolContext } from "./types.js";
|
|
37
37
|
import type { BrowserOperation } from "./types.js";
|
|
38
38
|
|
|
39
|
+
export type BrowserOperationLifecycle = "action" | "terminal" | "status";
|
|
40
|
+
|
|
41
|
+
const OPERATION_LIFECYCLE: Record<BrowserOperation, BrowserOperationLifecycle> =
|
|
42
|
+
{
|
|
43
|
+
navigate: "action",
|
|
44
|
+
snapshot: "action",
|
|
45
|
+
screenshot: "action",
|
|
46
|
+
close: "terminal",
|
|
47
|
+
attach: "action",
|
|
48
|
+
detach: "terminal",
|
|
49
|
+
click: "action",
|
|
50
|
+
type: "action",
|
|
51
|
+
press_key: "action",
|
|
52
|
+
scroll: "action",
|
|
53
|
+
select_option: "action",
|
|
54
|
+
hover: "action",
|
|
55
|
+
wait_for: "action",
|
|
56
|
+
extract: "action",
|
|
57
|
+
wait_for_download: "action",
|
|
58
|
+
fill_credential: "action",
|
|
59
|
+
status: "status",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export function browserOperationLifecycle(
|
|
63
|
+
operation: BrowserOperation,
|
|
64
|
+
): BrowserOperationLifecycle {
|
|
65
|
+
return OPERATION_LIFECYCLE[operation];
|
|
66
|
+
}
|
|
67
|
+
|
|
39
68
|
// ── Dispatch handlers ────────────────────────────────────────────────
|
|
40
69
|
|
|
41
70
|
/**
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isVirtualDesktopEnabled } from "../desktop/virtual-desktop-feature.js";
|
|
1
|
+
import { shouldUseVirtualDesktop } from "../desktop/virtual-desktop-feature.js";
|
|
3
2
|
import { browserManager } from "../tools/browser/browser-manager.js";
|
|
4
3
|
import { normalizeBrowserMode } from "../tools/browser/browser-mode.js";
|
|
5
4
|
import { getPinnedTab } from "../tools/browser/pinned-tabs.js";
|
|
@@ -19,16 +18,10 @@ export function shouldUseVirtualDesktopBrowser(
|
|
|
19
18
|
mode.mode !== "auto" ||
|
|
20
19
|
input.target_client_id ||
|
|
21
20
|
input.use_active_tab ||
|
|
22
|
-
context.transportInterface !== "web" ||
|
|
23
|
-
context.clientOs === "macos" ||
|
|
24
|
-
context.clientOs === "windows" ||
|
|
25
|
-
context.clientOs === "linux" ||
|
|
26
|
-
context.trustClass !== "guardian" ||
|
|
27
|
-
!context.sourceActorPrincipalId ||
|
|
28
21
|
browserManager.getPreferredBackendKind(context.conversationId) !== null ||
|
|
29
22
|
getPinnedTab(context.conversationId)
|
|
30
23
|
) {
|
|
31
24
|
return false;
|
|
32
25
|
}
|
|
33
|
-
return
|
|
26
|
+
return shouldUseVirtualDesktop(context);
|
|
34
27
|
}
|
|
@@ -115,7 +115,7 @@ export async function signBundle(
|
|
|
115
115
|
const { signature, keyId } = await requestSignature(canonicalPayload);
|
|
116
116
|
|
|
117
117
|
// 5. Build SignatureJson
|
|
118
|
-
|
|
118
|
+
return {
|
|
119
119
|
algorithm: "ed25519",
|
|
120
120
|
signer: {
|
|
121
121
|
key_id: keyId,
|
|
@@ -124,6 +124,4 @@ export async function signBundle(
|
|
|
124
124
|
content_hashes: contentHashes,
|
|
125
125
|
signature,
|
|
126
126
|
};
|
|
127
|
-
|
|
128
|
-
return signatureJson;
|
|
129
127
|
}
|
|
@@ -12,13 +12,16 @@ describe("createBargeInGuard", () => {
|
|
|
12
12
|
expect(guard.track("speech", 100)).toBe("pending");
|
|
13
13
|
expect(guard.track("speech", 100)).toBe("pending");
|
|
14
14
|
expect(guard.speechMs).toBe(200);
|
|
15
|
+
expect(guard.fired).toBe(false);
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
test("sustained speech reaching the threshold fires once and stays fired", () => {
|
|
18
19
|
const guard = createBargeInGuard(250);
|
|
19
20
|
guard.track("speech", 200);
|
|
20
21
|
expect(guard.track("speech", 50)).toBe("fired");
|
|
22
|
+
expect(guard.fired).toBe(true);
|
|
21
23
|
expect(guard.track("silence", 1_000)).toBe("fired");
|
|
24
|
+
expect(guard.fired).toBe(true);
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
test("a brief sub-threshold gap does not reset the run", () => {
|
|
@@ -421,6 +421,7 @@ function setupController(
|
|
|
421
421
|
) {
|
|
422
422
|
ensureConversation("conv-ctrl-test");
|
|
423
423
|
const session = createCallSession({
|
|
424
|
+
direction: "inbound",
|
|
424
425
|
conversationId: "conv-ctrl-test",
|
|
425
426
|
provider: "twilio",
|
|
426
427
|
fromNumber: "+15551111111",
|
|
@@ -471,6 +472,7 @@ function setupControllerWithOrigin(task?: string) {
|
|
|
471
472
|
ensureConversation("conv-ctrl-voice");
|
|
472
473
|
ensureConversation("conv-ctrl-origin");
|
|
473
474
|
const session = createCallSession({
|
|
475
|
+
direction: "inbound",
|
|
474
476
|
conversationId: "conv-ctrl-voice",
|
|
475
477
|
provider: "twilio",
|
|
476
478
|
fromNumber: "+15551111111",
|
|
@@ -5028,4 +5030,96 @@ describe("call-controller", () => {
|
|
|
5028
5030
|
await turnPromise.catch(() => {});
|
|
5029
5031
|
});
|
|
5030
5032
|
});
|
|
5033
|
+
describe("turn metrics", () => {
|
|
5034
|
+
test("a caller turn records the marks the latency numbers are built from", async () => {
|
|
5035
|
+
const { controller } = setupController();
|
|
5036
|
+
|
|
5037
|
+
await controller.handleCallerUtterance("What is the weather?");
|
|
5038
|
+
|
|
5039
|
+
const snapshot = controller.getMetricsSnapshot();
|
|
5040
|
+
expect(snapshot.recentTurns.length).toBe(1);
|
|
5041
|
+
const turn = snapshot.recentTurns[0];
|
|
5042
|
+
expect(turn.status).toBe("completed");
|
|
5043
|
+
// The transcript mark is seeded from when the caller stopped talking,
|
|
5044
|
+
// the dispatch mark from when the leg went out, and the delta mark from
|
|
5045
|
+
// the first token back: the three anchors every phone latency number is
|
|
5046
|
+
// subtracted from.
|
|
5047
|
+
expect(turn.timestamps.finalTranscriptAtMs).not.toBeNull();
|
|
5048
|
+
expect(turn.timestamps.assistantDispatchAtMs).not.toBeNull();
|
|
5049
|
+
expect(turn.timestamps.firstAssistantDeltaAtMs).not.toBeNull();
|
|
5050
|
+
expect(turn.durations.dispatchToFirstAssistantDeltaMs).not.toBeNull();
|
|
5051
|
+
expect(turn.durations.totalTurnDurationMs).not.toBeNull();
|
|
5052
|
+
// The provider's final doubles as the speech-end anchor, so the turn's
|
|
5053
|
+
// round trip is measurable on phone as it is in live voice.
|
|
5054
|
+
expect(turn.timestamps.utteranceEndAtMs).not.toBeNull();
|
|
5055
|
+
expect(turn.durations.roundTripMs).not.toBeNull();
|
|
5056
|
+
|
|
5057
|
+
controller.destroy();
|
|
5058
|
+
});
|
|
5059
|
+
|
|
5060
|
+
test("the transcript mark predates the dispatch it triggered", async () => {
|
|
5061
|
+
// Seeding backdates the turn to the caller's own boundary; a turn that
|
|
5062
|
+
// opened at dispatch time would report a felt latency that starts after
|
|
5063
|
+
// the caller already finished speaking.
|
|
5064
|
+
const { controller } = setupController();
|
|
5065
|
+
|
|
5066
|
+
await controller.handleCallerUtterance("Hello there");
|
|
5067
|
+
|
|
5068
|
+
const turn = controller.getMetricsSnapshot().recentTurns[0];
|
|
5069
|
+
const finalTranscriptAtMs = turn.timestamps.finalTranscriptAtMs;
|
|
5070
|
+
const assistantDispatchAtMs = turn.timestamps.assistantDispatchAtMs;
|
|
5071
|
+
expect(finalTranscriptAtMs).not.toBeNull();
|
|
5072
|
+
expect(assistantDispatchAtMs).not.toBeNull();
|
|
5073
|
+
expect(finalTranscriptAtMs!).toBeLessThanOrEqual(assistantDispatchAtMs!);
|
|
5074
|
+
expect(turn.timestamps.startedAtMs).toBeLessThanOrEqual(
|
|
5075
|
+
finalTranscriptAtMs!,
|
|
5076
|
+
);
|
|
5077
|
+
|
|
5078
|
+
controller.destroy();
|
|
5079
|
+
});
|
|
5080
|
+
|
|
5081
|
+
test("a barge-in ends the turn once, as cancelled", async () => {
|
|
5082
|
+
// One interruption is one cancelled turn: the count downstream reads as
|
|
5083
|
+
// "turns the caller cut off", so a second settle would double it.
|
|
5084
|
+
// A turn that never emits or completes holds the controller in the
|
|
5085
|
+
// pre-speech phase, which a sustained barge-in can still cut off.
|
|
5086
|
+
mockStartVoiceTurn.mockImplementation(
|
|
5087
|
+
async (opts: { onComplete: () => void }) => ({
|
|
5088
|
+
turnId: "run-metrics-slow",
|
|
5089
|
+
abort: () => opts.onComplete(),
|
|
5090
|
+
}),
|
|
5091
|
+
);
|
|
5092
|
+
const { controller } = setupController();
|
|
5093
|
+
const turnPromise = controller.handleCallerUtterance("Tell me a story");
|
|
5094
|
+
for (let i = 0; i < 5; i++) {
|
|
5095
|
+
await Promise.resolve();
|
|
5096
|
+
}
|
|
5097
|
+
expect(controller.getState()).toBe("processing");
|
|
5098
|
+
|
|
5099
|
+
expect(controller.handleBargeIn()).toBe(true);
|
|
5100
|
+
await turnPromise.catch(() => {});
|
|
5101
|
+
controller.destroy();
|
|
5102
|
+
|
|
5103
|
+
const turns = controller.getMetricsSnapshot().recentTurns;
|
|
5104
|
+
expect(turns.length).toBe(1);
|
|
5105
|
+
expect(turns[0].status).toBe("cancelled");
|
|
5106
|
+
expect(turns[0].cancellationReason).toBe("interrupted");
|
|
5107
|
+
expect(turns[0].timestamps.bargeInAtMs).not.toBeNull();
|
|
5108
|
+
});
|
|
5109
|
+
|
|
5110
|
+
test("no turn is invented between turns", async () => {
|
|
5111
|
+
// Every mark opens a turn when none is active, so a barge-in on an idle
|
|
5112
|
+
// controller must leave the collector empty rather than start a turn
|
|
5113
|
+
// that never settles.
|
|
5114
|
+
const { controller } = setupController();
|
|
5115
|
+
|
|
5116
|
+
expect(controller.handleBargeIn()).toBe(false);
|
|
5117
|
+
|
|
5118
|
+
const snapshot = controller.getMetricsSnapshot();
|
|
5119
|
+
expect(snapshot.recentTurns.length).toBe(0);
|
|
5120
|
+
expect(snapshot.activeTurn).toBeNull();
|
|
5121
|
+
|
|
5122
|
+
controller.destroy();
|
|
5123
|
+
});
|
|
5124
|
+
});
|
|
5031
5125
|
});
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phone-call funnel rows: which ones the call store records and what they say.
|
|
3
|
+
*
|
|
4
|
+
* The recorder is mocked, as in `live-voice-session-telemetry.test.ts`: the
|
|
5
|
+
* outbox and its consent gate are covered by `telemetry-events-outbox.test.ts`.
|
|
6
|
+
* What these tests pin is the wiring that decides whether a row exists at all,
|
|
7
|
+
* which is where the measurement silently goes wrong: a call that never
|
|
8
|
+
* connects still has to count, a terminal status has to emit exactly one end
|
|
9
|
+
* row however many writes follow it, and a call nobody spoke on has to say so.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
13
|
+
|
|
14
|
+
const recordPhoneCallStarted = mock(() => null);
|
|
15
|
+
const recordPhoneCallEnded = mock(() => null);
|
|
16
|
+
|
|
17
|
+
// Spread the real module rather than replacing it: the call store pulls other
|
|
18
|
+
// recorders out of it, and a bare replacement makes those imports fail.
|
|
19
|
+
const onboardingEventsStore =
|
|
20
|
+
await import("../../onboarding/onboarding-events-store.js");
|
|
21
|
+
mock.module("../../onboarding/onboarding-events-store.js", () => ({
|
|
22
|
+
...onboardingEventsStore,
|
|
23
|
+
recordPhoneCallStarted,
|
|
24
|
+
recordPhoneCallEnded,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
import { getDb } from "../../persistence/db-connection.js";
|
|
28
|
+
import { initializeDb } from "../../persistence/db-init.js";
|
|
29
|
+
import { conversations } from "../../persistence/schema/index.js";
|
|
30
|
+
|
|
31
|
+
await initializeDb();
|
|
32
|
+
|
|
33
|
+
// Values load after `mock.module` so the store picks up the mocked recorder.
|
|
34
|
+
const { createCallSession, recordCallEvent, updateCallSession } =
|
|
35
|
+
await import("../call-store.js");
|
|
36
|
+
|
|
37
|
+
let conversationSeq = 0;
|
|
38
|
+
|
|
39
|
+
function makeConversation(): string {
|
|
40
|
+
const id = `conv-funnel-${++conversationSeq}`;
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
getDb()
|
|
43
|
+
.insert(conversations)
|
|
44
|
+
.values({
|
|
45
|
+
id,
|
|
46
|
+
title: `Test conversation ${id}`,
|
|
47
|
+
createdAt: now,
|
|
48
|
+
updatedAt: now,
|
|
49
|
+
})
|
|
50
|
+
.run();
|
|
51
|
+
return id;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function startCall(opts?: {
|
|
55
|
+
direction?: "inbound" | "outbound";
|
|
56
|
+
callMode?: string;
|
|
57
|
+
}): string {
|
|
58
|
+
const session = createCallSession({
|
|
59
|
+
conversationId: makeConversation(),
|
|
60
|
+
provider: "twilio",
|
|
61
|
+
direction: opts?.direction ?? "inbound",
|
|
62
|
+
fromNumber: "+15550100",
|
|
63
|
+
toNumber: "+15550101",
|
|
64
|
+
...(opts?.callMode !== undefined ? { callMode: opts.callMode } : {}),
|
|
65
|
+
});
|
|
66
|
+
return session.id;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
recordPhoneCallStarted.mockClear();
|
|
71
|
+
recordPhoneCallEnded.mockClear();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("call funnel start rows", () => {
|
|
75
|
+
test("an inbound call counts the moment its session row exists", () => {
|
|
76
|
+
// Before the provider has dialled and before any preflight can reject it:
|
|
77
|
+
// a call that never connects is the one the funnel most needs to count.
|
|
78
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
79
|
+
|
|
80
|
+
expect(recordPhoneCallStarted).toHaveBeenCalledTimes(1);
|
|
81
|
+
expect(recordPhoneCallStarted).toHaveBeenCalledWith({
|
|
82
|
+
callSessionId,
|
|
83
|
+
screen: "started_inbound:normal",
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("an outbound call is stamped by the direction it states", () => {
|
|
88
|
+
// Stated, not inferred: a verification or invite call dials out and
|
|
89
|
+
// carries no task, so task presence would misread as inbound.
|
|
90
|
+
startCall({ direction: "outbound", callMode: "verification" });
|
|
91
|
+
|
|
92
|
+
expect(recordPhoneCallStarted).toHaveBeenCalledWith(
|
|
93
|
+
expect.objectContaining({ screen: "started_outbound:verification" }),
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("an ordinary call stamps the normal mode it never writes", () => {
|
|
98
|
+
// createInboundVoiceSession and startCall leave callMode null; only the
|
|
99
|
+
// verification and invite flows set one.
|
|
100
|
+
startCall();
|
|
101
|
+
|
|
102
|
+
expect(recordPhoneCallStarted).toHaveBeenCalledWith(
|
|
103
|
+
expect.objectContaining({ screen: "started_inbound:normal" }),
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("call funnel end rows", () => {
|
|
109
|
+
test("a completed call that took a turn reports no silence", () => {
|
|
110
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
111
|
+
updateCallSession(callSessionId, { status: "in_progress" });
|
|
112
|
+
recordCallEvent(callSessionId, "call_connected");
|
|
113
|
+
recordCallEvent(callSessionId, "caller_spoke", {
|
|
114
|
+
transcript: "hello",
|
|
115
|
+
transport: "media-stream",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
updateCallSession(callSessionId, { status: "completed" });
|
|
119
|
+
|
|
120
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledTimes(1);
|
|
121
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledWith({
|
|
122
|
+
callSessionId,
|
|
123
|
+
screen: "ended_completed",
|
|
124
|
+
outcome: "completed",
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("a call that never connected is silent for that reason", () => {
|
|
129
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
130
|
+
|
|
131
|
+
updateCallSession(callSessionId, { status: "failed" });
|
|
132
|
+
|
|
133
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledWith({
|
|
134
|
+
callSessionId,
|
|
135
|
+
screen: "ended_failed:silent_no_connect",
|
|
136
|
+
outcome: "failed",
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("a connected call nobody spoke on is silent on the line", () => {
|
|
141
|
+
// The distinction the rate lives on: the telephony leg worked and the
|
|
142
|
+
// conversation still never started.
|
|
143
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
144
|
+
updateCallSession(callSessionId, { status: "in_progress" });
|
|
145
|
+
recordCallEvent(callSessionId, "call_connected");
|
|
146
|
+
|
|
147
|
+
updateCallSession(callSessionId, { status: "completed" });
|
|
148
|
+
|
|
149
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledWith(
|
|
150
|
+
expect.objectContaining({ screen: "ended_completed:silent_no_turn" }),
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("a call where only digits were pressed took no turn", () => {
|
|
155
|
+
// DTMF digits ride `caller_spoke` too; only a transcript is a turn.
|
|
156
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
157
|
+
updateCallSession(callSessionId, { status: "in_progress" });
|
|
158
|
+
recordCallEvent(callSessionId, "call_connected");
|
|
159
|
+
recordCallEvent(callSessionId, "caller_spoke", {
|
|
160
|
+
dtmfDigit: "4",
|
|
161
|
+
transport: "media-stream",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
updateCallSession(callSessionId, { status: "completed" });
|
|
165
|
+
|
|
166
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledWith(
|
|
167
|
+
expect.objectContaining({ screen: "ended_completed:silent_no_turn" }),
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("a cancelled call completed its course", () => {
|
|
172
|
+
// Only an error is a failure; a call cancelled before it connected ran
|
|
173
|
+
// exactly as far as it was asked to.
|
|
174
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
175
|
+
|
|
176
|
+
updateCallSession(callSessionId, { status: "cancelled" });
|
|
177
|
+
|
|
178
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledWith(
|
|
179
|
+
expect.objectContaining({ outcome: "completed" }),
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("only the first terminal write records an end", () => {
|
|
184
|
+
// Terminal states are immutable, so the rejected second write must not
|
|
185
|
+
// produce a second row to pair with the one start.
|
|
186
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
187
|
+
updateCallSession(callSessionId, { status: "completed" });
|
|
188
|
+
|
|
189
|
+
updateCallSession(callSessionId, { status: "failed" });
|
|
190
|
+
|
|
191
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledTimes(1);
|
|
192
|
+
expect(recordPhoneCallEnded).toHaveBeenCalledWith(
|
|
193
|
+
expect.objectContaining({ screen: "ended_completed:silent_no_connect" }),
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("a non-status update records nothing", () => {
|
|
198
|
+
const callSessionId = startCall({ callMode: "normal" });
|
|
199
|
+
|
|
200
|
+
updateCallSession(callSessionId, { providerCallSid: "CA-test" });
|
|
201
|
+
|
|
202
|
+
expect(recordPhoneCallEnded).not.toHaveBeenCalled();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type SessionControlRequest,
|
|
11
11
|
stripInternalSpeechMarkers,
|
|
12
12
|
TASK_STOP_MARKER,
|
|
13
|
+
TASK_UPDATE_SILENT_MARKER,
|
|
13
14
|
terminalControlMarkerLength,
|
|
14
15
|
} from "../voice-control-protocol.js";
|
|
15
16
|
|
|
@@ -227,3 +228,17 @@ describe("session control markers", () => {
|
|
|
227
228
|
expect(terminalControlMarkerLength("The array [-1] sorts")).toBe(0);
|
|
228
229
|
});
|
|
229
230
|
});
|
|
231
|
+
|
|
232
|
+
test("silent task-update marker is held across every split and removed from the transcript", () => {
|
|
233
|
+
for (let split = 1; split < TASK_UPDATE_SILENT_MARKER.length; split += 1) {
|
|
234
|
+
const emitted: string[] = [];
|
|
235
|
+
const flush = createControlMarkerHoldback((text) => emitted.push(text));
|
|
236
|
+
flush(TASK_UPDATE_SILENT_MARKER.slice(0, split));
|
|
237
|
+
flush(TASK_UPDATE_SILENT_MARKER, { force: true });
|
|
238
|
+
expect(emitted.join("")).toBe("");
|
|
239
|
+
}
|
|
240
|
+
expect(terminalControlMarkerLength(TASK_UPDATE_SILENT_MARKER)).toBe(
|
|
241
|
+
TASK_UPDATE_SILENT_MARKER.length,
|
|
242
|
+
);
|
|
243
|
+
expect(parseTerminalSessionControl(TASK_UPDATE_SILENT_MARKER)).toBeNull();
|
|
244
|
+
});
|