@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
|
@@ -91,11 +91,12 @@ Each policy defines:
|
|
|
91
91
|
|
|
92
92
|
### Conversation Strategy Types
|
|
93
93
|
|
|
94
|
-
| Strategy | Behavior
|
|
95
|
-
| -------------------------------- |
|
|
96
|
-
| `start_new_conversation` |
|
|
97
|
-
| `continue_existing_conversation` |
|
|
98
|
-
| `
|
|
94
|
+
| Strategy | Behavior | Used by |
|
|
95
|
+
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `start_new_conversation` | Pairs the delivery with a new or reused conversation, or appends to the producing one (see Conversation Pairing below). | `vellum` |
|
|
97
|
+
| `continue_existing_conversation` | Resolves the destination chat's home conversation, creating one on first delivery. The delivered post is written there only once the channel acknowledges it. | `telegram`, `slack`, `discord`, and the non-deliverable `whatsapp`, `email`, `a2a`, `plugin` |
|
|
98
|
+
| `push_only` | Pairs nothing. The push deep-links through the vellum delivery's conversation. | `platform` |
|
|
99
|
+
| `not_deliverable` | Channel cannot receive notifications. Pairing returns null IDs. | `phone` |
|
|
99
100
|
|
|
100
101
|
### Helper Functions
|
|
101
102
|
|
|
@@ -106,31 +107,23 @@ Each policy defines:
|
|
|
106
107
|
|
|
107
108
|
### How to Add a New Channel
|
|
108
109
|
|
|
109
|
-
1. Add the channel to `CHANNEL_IDS` in `channels/types.ts
|
|
110
|
-
2. Add
|
|
111
|
-
3.
|
|
112
|
-
4. Add
|
|
113
|
-
5. Add a destination resolver case in `destination-resolver.ts`.
|
|
110
|
+
1. Add the channel to `CHANNEL_IDS` in `packages/service-contracts/src/channels.ts` (re-exported through `channels/types.ts`).
|
|
111
|
+
2. Add its policy to `CHANNEL_POLICIES` in `channels/config.ts`; the compiler requires an entry for every channel. `deliveryEnabled: true` makes it a `NotificationChannel` (`DeliverableChannelId` is derived from these policies), and `conversationStrategy` decides what conversation pairing does for it.
|
|
112
|
+
3. For a deliverable channel, add an adapter in `adapters/` implementing `ChannelAdapter` and register it in `getBroadcaster()` in `emit-signal.ts`.
|
|
113
|
+
4. Add its case to `getConnectedChannels()` in `emit-signal.ts` and to `resolveDestinations()` in `destination-resolver.ts`. Both switch over deliverable channels, so the compiler flags a missing case.
|
|
114
114
|
|
|
115
|
-
## Conversation Pairing
|
|
115
|
+
## Conversation Pairing
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
Before an adapter sends a notification, `pairDeliveryWithConversation()` (in `conversation-pairing.ts`) decides which conversation the delivery belongs to, from the channel's `conversationStrategy` and the decision engine's per-channel conversation action. The module header and the function's inline comments are the source of truth for the details. The rules that hold across them:
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
- **Vellum (`start_new_conversation`)**:
|
|
120
|
+
- A signal that sets `requiresConversation` gets a conversation of its own, created with its seed message before the send (`standard` unless the producer overrides `conversationType`). An explicit `reuse_existing` action appends to a valid target instead, and falls back to a new conversation (`conversationFallbackUsed: true`) when the target is stale.
|
|
121
|
+
- A signal that does not set it creates no conversation, except that with `assistant-initiated-threads` on, an `assistant.share` with no `conversationMetadata.source` of its own is promoted to one when its producing conversation is a background or scheduled run, or does not resolve (`withAssistantInitiatedThread()`). Otherwise its body is appended to the conversation that produced it (resolved from `sourceContextId`). A `chat.assistant_reply` appends nothing, because the full reply is already in that transcript.
|
|
122
|
+
- **External channels (`continue_existing_conversation`)**: only the chat's home conversation is resolved, and nothing is written. The broadcaster records the delivered post as an assistant row once the channel acknowledges it (`recordDeliveredChannelPost`), so a failed or pending delivery never reads as something the assistant said.
|
|
123
|
+
- **`push_only`** (platform) and **`not_deliverable`**: nothing is paired. Platform push deep-links through the vellum delivery's conversation.
|
|
124
|
+
- **Guardian-request deliveries to channels** pair nothing either: they are projections of a canonical request, and only their vellum delivery carries a conversation (see `notifications/AGENTS.md`).
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
1. The pairing function looks up the target conversation.
|
|
124
|
-
2. If the conversation exists and has `source: 'notification'`, the seed message is **appended** to the existing conversation (not a new one). The result has `createdNewConversation: false`.
|
|
125
|
-
3. If the target is invalid (does not exist, or has a different `source`), the function falls back to creating a new conversation and sets `conversationFallbackUsed: true` on the result. A warning is logged with the invalid target details.
|
|
126
|
-
|
|
127
|
-
### New Conversation Path (`start_new` / default)
|
|
128
|
-
|
|
129
|
-
- **`start_new_conversation`**: Creates a new conversation with `conversationType: 'standard'` and `source: 'notification'`, plus an assistant message containing the conversation seed. Memory indexing is skipped on the seed message to prevent notification copy from polluting conversational recall. The result has `createdNewConversation: true`.
|
|
130
|
-
- **`continue_existing_conversation`**: Looks up a previously bound conversation by binding key (`sourceChannel` + `externalChatId` via `getBindingByChannelChat()`). When a valid bound conversation with `source: 'notification'` exists, the seed message is appended to it and the binding timestamp is refreshed. When no binding exists or the bound conversation is stale/invalid, a new conversation is created and the binding is upserted for future reuse. The result has `createdNewConversation: false` on reuse, `true` on fresh creation.
|
|
131
|
-
- **`not_deliverable`**: Returns `{ conversationId: null, messageId: null }`.
|
|
132
|
-
|
|
133
|
-
The pairing function is resilient -- errors are caught and logged. A pairing failure never breaks the delivery pipeline.
|
|
126
|
+
Pairing is resilient: errors are caught and logged, and a pairing failure never breaks the delivery pipeline.
|
|
134
127
|
|
|
135
128
|
## Multi-Surface Copy Architecture
|
|
136
129
|
|
|
@@ -138,7 +131,7 @@ The system produces **three distinct copy outputs** per notification:
|
|
|
138
131
|
|
|
139
132
|
| Output | Purpose | Verbosity |
|
|
140
133
|
| ------------------------- | ------------------------------------------------ | ------------------------ |
|
|
141
|
-
| `title` + `body` |
|
|
134
|
+
| `title` + `body` | OS notification banner (desktop and mobile apps) | Short and glanceable |
|
|
142
135
|
| `deliveryText` | Channel-native chat message text (Telegram) | Natural chat phrasing |
|
|
143
136
|
| Conversation seed message | Opening message in the notification conversation | Richer and context-aware |
|
|
144
137
|
|
|
@@ -146,7 +139,7 @@ The system produces **three distinct copy outputs** per notification:
|
|
|
146
139
|
|
|
147
140
|
1. The **decision engine** can produce `title`/`body` (popup copy), `deliveryText` (chat copy), and `conversationSeedMessage` (richer conversation content) per channel.
|
|
148
141
|
2. **Adapters** use the surface-appropriate field:
|
|
149
|
-
- Vellum
|
|
142
|
+
- Vellum notifications use `title` + `body`.
|
|
150
143
|
- Telegram delivery prefers `deliveryText` and falls back to `conversationSeedMessage`, then `body`, then `title`.
|
|
151
144
|
3. **Conversation pairing** uses the conversation seed as the conversation's opening message:
|
|
152
145
|
- If the LLM produced a valid `conversationSeedMessage`, it is used directly (after a sanity check rejects empty, too-short, JSON dumps, or excessively long values).
|
|
@@ -190,7 +183,7 @@ Reminder. Take out the trash. Action required.
|
|
|
190
183
|
|
|
191
184
|
## Conversation Surfacing via `notification_conversation_created` Event (Creation-Only)
|
|
192
185
|
|
|
193
|
-
The `notification_conversation_created` SSE event is emitted **only when a brand-new conversation is actually created** by the broadcaster. Reused conversations do not trigger
|
|
186
|
+
The `notification_conversation_created` SSE event is emitted **only when a brand-new conversation is actually created** by the broadcaster. Reused conversations do not trigger it.
|
|
194
187
|
|
|
195
188
|
This is enforced in `broadcaster.ts` by gating the event emission on `pairing.createdNewConversation === true`:
|
|
196
189
|
|
|
@@ -206,20 +199,11 @@ if (
|
|
|
206
199
|
}
|
|
207
200
|
```
|
|
208
201
|
|
|
209
|
-
When a vellum notification conversation **is** newly created (strategy `start_new_conversation`), the broadcaster emits the SSE event **immediately**, before
|
|
210
|
-
|
|
211
|
-
The SSE event payload:
|
|
202
|
+
When a vellum notification conversation **is** newly created (strategy `start_new_conversation`), the broadcaster emits the SSE event **immediately**, before slower channel deliveries such as Telegram, so a slow channel send cannot delay it.
|
|
212
203
|
|
|
213
|
-
|
|
214
|
-
{
|
|
215
|
-
type: 'notification_conversation_created',
|
|
216
|
-
conversationId: string,
|
|
217
|
-
title: string,
|
|
218
|
-
sourceEventName: string,
|
|
219
|
-
}
|
|
220
|
-
```
|
|
204
|
+
The payload is `NotificationConversationCreatedEventSchema` in `api/events/notification-conversation-created.ts`.
|
|
221
205
|
|
|
222
|
-
|
|
206
|
+
No first-party client acts on this event: the shared web renderer (browser, desktop app, mobile apps) lists it as a no-op in `use-stream-event-handler.ts`.
|
|
223
207
|
|
|
224
208
|
### Per-Dispatch Conversation Callback
|
|
225
209
|
|
|
@@ -228,7 +212,7 @@ The macOS client listens for this event and surfaces the conversation in the sid
|
|
|
228
212
|
**Important distinction between the two callbacks:**
|
|
229
213
|
|
|
230
214
|
- **Per-dispatch `options.onConversationCreated`**: Fires for **both** new and reused vellum conversation pairings. Callers like `dispatchGuardianQuestion` rely on this to create delivery bookkeeping rows before `emitNotificationSignal()` returns, regardless of whether the conversation was newly created or reused.
|
|
231
|
-
- **Class-level `this.onConversationCreated` (SSE broadcast)**: Fires **only** when a brand-new conversation is created (`createdNewConversation === true && strategy === 'start_new_conversation'`). This emits the `notification_conversation_created` SSE event
|
|
215
|
+
- **Class-level `this.onConversationCreated` (SSE broadcast)**: Fires **only** when a brand-new conversation is created (`createdNewConversation === true && strategy === 'start_new_conversation'`). This emits the `notification_conversation_created` SSE event. Reused conversations do not trigger it.
|
|
232
216
|
|
|
233
217
|
## Schedule Routing Metadata and Trigger-Time Enforcement
|
|
234
218
|
|
|
@@ -255,8 +239,8 @@ The `routing_hints_json` field is free-form JSON metadata passed alongside the r
|
|
|
255
239
|
When a schedule fires in `notify` mode, the routing metadata flows through the notification pipeline with a post-decision enforcement step:
|
|
256
240
|
|
|
257
241
|
```
|
|
258
|
-
Schedule fires (scheduler.ts
|
|
259
|
-
→
|
|
242
|
+
Schedule fires (schedule worker: runDueSchedulesOnce in scheduler.ts, notify mode)
|
|
243
|
+
→ emitScheduleNotifySignal (scheduler.ts)
|
|
260
244
|
→ emitNotificationSignal({ routingIntent, routingHints })
|
|
261
245
|
→ Decision Engine (LLM selects channels)
|
|
262
246
|
→ enforceRoutingIntent() (post-decision guard)
|
|
@@ -264,6 +248,8 @@ Schedule fires (scheduler.ts: notify mode)
|
|
|
264
248
|
→ Broadcaster → Adapters → Delivery
|
|
265
249
|
```
|
|
266
250
|
|
|
251
|
+
`contextPayload.channelAllowlist` is an exclusive channel set. When present, emit-signal intersects it with connected channels, skips urgent vellum/platform force, and skips routing-intent expansion. `--preferred-channels` stays additive and is ignored when an allowlist is present. Access-request signals still force the in-app vellum card.
|
|
252
|
+
|
|
267
253
|
The `enforceRoutingIntent()` function in `decision-engine.ts` runs after the LLM produces its channel selection but before deterministic checks. It overrides the decision's `selectedChannels` based on the routing intent:
|
|
268
254
|
|
|
269
255
|
- **`all_channels`**: Replaces `selectedChannels` with all connected channels (from `getConnectedChannels()`).
|
|
@@ -280,8 +266,8 @@ A key design principle: **one schedule produces one notification signal that fan
|
|
|
280
266
|
|
|
281
267
|
```
|
|
282
268
|
schedule_jobs table (routing_intent, routing_hints_json)
|
|
283
|
-
→
|
|
284
|
-
→
|
|
269
|
+
→ schedule-store.ts: claimDueSchedules() reads routing metadata
|
|
270
|
+
→ scheduler.ts: emitScheduleNotifySignal({ routingIntent, routingHints })
|
|
285
271
|
→ emitNotificationSignal({ routingIntent, routingHints })
|
|
286
272
|
→ signal.ts: NotificationSignal.routingIntent / routingHints
|
|
287
273
|
→ decision-engine.ts: evaluateSignal() → enforceRoutingIntent()
|
|
@@ -290,7 +276,7 @@ schedule_jobs table (routing_intent, routing_hints_json)
|
|
|
290
276
|
|
|
291
277
|
## Channel Delivery Architecture
|
|
292
278
|
|
|
293
|
-
The notification system delivers to
|
|
279
|
+
The notification system delivers to five channel types:
|
|
294
280
|
|
|
295
281
|
### Vellum (always connected)
|
|
296
282
|
|
|
@@ -300,18 +286,33 @@ Local SSE via the assistant's broadcast mechanism. The `VellumAdapter` emits a `
|
|
|
300
286
|
- `title` and `body` -- rendered notification copy
|
|
301
287
|
- `deepLinkMetadata` -- optional metadata for navigating to the relevant context (e.g. `{ conversationId }`)
|
|
302
288
|
|
|
303
|
-
|
|
289
|
+
Every first-party client runs the shared web renderer (browser, desktop app, mobile apps), which posts a local notification from this payload and acks the delivery. When the user taps the notification, the client uses `deepLinkMetadata` to navigate to the relevant conversation.
|
|
290
|
+
|
|
291
|
+
A guardian-sensitive notification (approval requests, access requests, channel activation codes) carries `targetGuardianPrincipalId` and is published with `targetActorPrincipalId`, so the event hub delivers it only to connections authenticated as the guardian. The client shows it when the sending assistant is new enough to apply that targeting (`clients/web/src/lib/backwards-compat/guardian-notification-targeting.ts`) and drops it otherwise.
|
|
292
|
+
|
|
293
|
+
### Platform (always connected)
|
|
304
294
|
|
|
305
|
-
|
|
295
|
+
The `PlatformPushAdapter` posts the notification to the platform's `/v1/assistants/{id}/push/dispatch/` endpoint, which fans it out to the bound user's registered devices for native mobile push. Without platform credentials the delivery is recorded as failed.
|
|
306
296
|
|
|
307
|
-
|
|
297
|
+
### External channels: Telegram, Slack, Discord
|
|
298
|
+
|
|
299
|
+
The assistant sends these itself. Each adapter calls the provider's API through the send module in `../messaging/providers/<channel>/`; nothing goes through the gateway.
|
|
300
|
+
|
|
301
|
+
- **Telegram**: the `TelegramAdapter` sends channel-native text (`deliveryText` when present) to the guardian's chat ID through `telegram-bot/send.ts`, which calls the Telegram Bot API. Approval cards carry inline keyboard buttons and fall back to plain text with typed-reply instructions if the rich send fails.
|
|
302
|
+
- **Slack**: the `SlackAdapter` posts to the guardian's DM through `slack/send.ts`. Approval cards render as a Card block with the approval's action buttons.
|
|
303
|
+
- **Discord**: the `DiscordAdapter` opens the guardian's DM from their user ID through the Discord REST API and sends through `discord/send.ts`. Approval cards render component buttons.
|
|
304
|
+
|
|
305
|
+
The destination for each comes from the guardian delivery list in `destination-resolver.ts`, with deterministic fallback copy when model copy is unavailable.
|
|
308
306
|
|
|
309
307
|
### Channel Connectivity
|
|
310
308
|
|
|
311
|
-
Connected channels are resolved at signal emission time by `getConnectedChannels()` in `emit-signal.ts
|
|
309
|
+
Connected channels are resolved at signal emission time by `getConnectedChannels()` in `emit-signal.ts`, from the guardian delivery list (`getGuardianDelivery()`):
|
|
312
310
|
|
|
313
|
-
- **Vellum** is always considered connected (
|
|
314
|
-
- **
|
|
311
|
+
- **Vellum** is always considered connected (the local transport is always available when the assistant is running)
|
|
312
|
+
- **Platform** is always considered connected; a missing credential surfaces as a failed delivery
|
|
313
|
+
- **Telegram** is connected when the guardian has a chat ID on the channel
|
|
314
|
+
- **Slack** is connected when the guardian's chat ID is a DM channel (`D`-prefixed), so a binding made from a shared channel never receives notifications
|
|
315
|
+
- **Discord** is connected when a guardian binding names a user to DM
|
|
315
316
|
|
|
316
317
|
## Conversation Materialization
|
|
317
318
|
|
|
@@ -323,9 +324,9 @@ The system uses a single conversation materialization path for **all** notificat
|
|
|
323
324
|
|
|
324
325
|
Guardian dispatch follows this same path and uses the optional `onConversationCreated` callback to attach guardian-delivery bookkeeping to the paired vellum conversation.
|
|
325
326
|
|
|
326
|
-
### Conversation
|
|
327
|
+
### Conversation-Before-Event Invariant
|
|
327
328
|
|
|
328
|
-
For notification flows that create conversations, the conversation must be created **before** the SSE event is emitted. This ensures
|
|
329
|
+
For notification flows that create conversations, the conversation must be created **before** the SSE event is emitted. This ensures a client can immediately fetch the conversation contents when it receives the conversation-created event.
|
|
329
330
|
|
|
330
331
|
## Conversation Decision Audit Trail
|
|
331
332
|
|
|
@@ -380,34 +381,23 @@ When the decision engine routes multiple guardian questions to the **same** conv
|
|
|
380
381
|
|
|
381
382
|
Each guardian request is assigned a unique 6-character hex code (e.g. `A1B2C3`) at creation time, generated gateway-side during `guardian_requests_create` (`generateRequestCode()` in `gateway/src/db/guardian-request-store.ts`). The guardian sees the code only where they need to type it: in the plain-text fallback a transport appends when it sends a request without buttons, and in the router's disambiguation reply when several requests are pending.
|
|
382
383
|
|
|
383
|
-
### Disambiguation
|
|
384
|
-
|
|
385
|
-
The disambiguation logic is identical on all channels — mac/vellum (`conversation-process.ts`) and Telegram (`inbound-message-handler.ts`):
|
|
386
|
-
|
|
387
|
-
1. **Single pending delivery in the conversation**: The guardian's reply is matched to the sole pending request automatically. No request code prefix is needed. This is the **single-match fast path**.
|
|
388
|
-
|
|
389
|
-
2. **Multiple pending deliveries in the conversation**: The guardian must prefix their reply with the request code of the question they are answering (e.g. `A1B2C3 yes, allow it`). Matching is case-insensitive.
|
|
384
|
+
### Reply Routing and Disambiguation
|
|
390
385
|
|
|
391
|
-
|
|
386
|
+
Every guardian reply typed in the app, and every reply or button press arriving on a channel, goes through `routeGuardianReply()` in `runtime/guardian-reply-router.ts`: from `conversation-process.ts` and `conversation-routes.ts` for the app, and from `inbound-stages/guardian-reply-intercept.ts` for channels. Buttons on the app's own cards skip the router: `surface-action-routes.ts` and `guardian-action-routes.ts` call `processGuardianDecision()` directly. In priority order the router tries:
|
|
392
387
|
|
|
393
|
-
|
|
388
|
+
1. A channel button callback (`apr:<requestId>:<action>`).
|
|
389
|
+
2. A request-code prefix on the reply. Matching is case-insensitive.
|
|
390
|
+
3. `open invite flow` while an access request is pending, which passes through to the normal assistant turn.
|
|
391
|
+
4. A bare-text answer, when exactly one question is pending in the conversation it was asked in.
|
|
392
|
+
5. An explicit approve or reject phrase, applied when exactly one request is pending.
|
|
393
|
+
6. Natural-language classification, only where the caller passes an `approvalConversationGenerator`: channels do, app sessions do not.
|
|
394
394
|
|
|
395
|
-
|
|
395
|
+
When several requests are pending and the reply names none of them, the router answers with `composeDisambiguationReply()`, listing each request's code and how to reply to it:
|
|
396
396
|
|
|
397
|
-
-
|
|
398
|
-
-
|
|
397
|
+
- for an explicit approve or reject phrase, everywhere;
|
|
398
|
+
- where classification runs, for any reply it does not resolve to one request. `runApprovalConversationTurn()` returns `keep_pending` for indecision and for every failure (generator error, malformed output, a decision with no target), so on those channels any other code-less reply is consumed here.
|
|
399
399
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
### Disambiguation Message Generation
|
|
403
|
-
|
|
404
|
-
All disambiguation messages are generated through `composeGuardianActionMessageGenerative()` in `guardian-action-message-composer.ts`, which uses a 2-tier priority chain (LLM generator with deterministic fallback). Three disambiguation scenarios exist:
|
|
405
|
-
|
|
406
|
-
| Scenario | When triggered |
|
|
407
|
-
| ---------------------------------- | ------------------------------------------------------ |
|
|
408
|
-
| `guardian_disambiguation` | Multiple pending approval requests in a conversation |
|
|
409
|
-
| `guardian_expired_disambiguation` | Multiple expired requests with late replies |
|
|
410
|
-
| `guardian_followup_disambiguation` | Multiple follow-up deliveries awaiting guardian action |
|
|
400
|
+
Where classification does not run, any other code-less reply falls through to the normal message pipeline. With one request pending, a `keep_pending` classification returns the engine's own reply as `nl_keep_pending`: the channel intercept treats it as consumed, while `conversation-routes.ts` lets the message through. Every decision applies through `applyGuardianDecision()`. The end-to-end map is [docs/guardian-request-flow.md](../../docs/guardian-request-flow.md).
|
|
411
401
|
|
|
412
402
|
## Key Files
|
|
413
403
|
|
|
@@ -425,12 +415,15 @@ All disambiguation messages are generated through `composeGuardianActionMessageG
|
|
|
425
415
|
| `broadcaster.ts` | Fan-out to channel adapters with delivery audit trail; emits `notification_conversation_created` SSE event |
|
|
426
416
|
| `copy-composer.ts` | Template-based fallback notification copy when LLM copy is unavailable |
|
|
427
417
|
| `conversation-seed-composer.ts` | Surface-aware conversation seed generation (richer than notification copy) |
|
|
428
|
-
| `destination-resolver.ts` | Resolves per-channel endpoints (
|
|
418
|
+
| `destination-resolver.ts` | Resolves per-channel endpoints (Telegram and Slack chat ID, Discord user ID) |
|
|
429
419
|
| `adapters/macos.ts` | Vellum adapter -- broadcasts `notification_intent` via SSE with deep-link metadata |
|
|
430
|
-
| `adapters/
|
|
420
|
+
| `adapters/platform.ts` | Platform adapter: posts to the platform push-dispatch endpoint for native mobile push |
|
|
421
|
+
| `adapters/telegram.ts` | Telegram adapter: calls the Telegram Bot API through `messaging/providers/telegram-bot/send.ts` |
|
|
422
|
+
| `adapters/slack.ts` | Slack adapter: posts to the guardian's DM through `messaging/providers/slack/send.ts` |
|
|
423
|
+
| `adapters/discord.ts` | Discord adapter: sends to the guardian's DM through `messaging/providers/discord/send.ts` |
|
|
431
424
|
| `preference-extractor.ts` | Detects notification preferences in conversation messages |
|
|
432
425
|
| `preference-summary.ts` | Builds preference context string for the decision engine prompt |
|
|
433
|
-
| `preferences-store.ts` |
|
|
426
|
+
| `preferences-store.ts` | Create and list for the `notification_preferences` table |
|
|
434
427
|
| `events-store.ts` | CRUD for `notification_events` table |
|
|
435
428
|
| `decisions-store.ts` | CRUD for `notification_decisions` table |
|
|
436
429
|
| `deliveries-store.ts` | CRUD for `notification_deliveries` table |
|
|
@@ -479,15 +472,6 @@ Step 1 suppresses on this hint before anything else runs, and nothing downstream
|
|
|
479
472
|
- **Never opt in a `guardian.question` producer.** The card _is_ the prompt, so a `true` suppresses the question's only rendering and the tool hangs until the prompt timeout. `runtime/question-request-guardian-bridge.ts` carries the rationale at its hint block, and three regression pins hold the line: `runtime/__tests__/question-request-guardian-bridge.test.ts`, `__tests__/confirmation-request-guardian-bridge.test.ts`, and `__tests__/notification-guardian-path.test.ts`.
|
|
480
473
|
- **A producer running inside the schedule worker cannot read presence at all.** `schedule/worker.ts` is a standalone OS process and the sole runner of schedule execution, while the resolver reads `assistantEventHub` through `isWebConversationFocused()` and that hub's SSE clients exist only in the assistant process. Every presence read from the worker resolves `false`, whatever the user is watching. The trap is in testing: a test that drives the producer and mocks presence in one process passes while the shipped worker suppresses nothing. Tracked as JARVIS-1711.
|
|
481
474
|
|
|
482
|
-
## How to Add a New Channel
|
|
483
|
-
|
|
484
|
-
1. Add the channel to `CHANNEL_IDS` in `channels/types.ts`.
|
|
485
|
-
2. Create an adapter in `adapters/` implementing the `ChannelAdapter` interface.
|
|
486
|
-
3. Register the adapter in `emit-signal.ts` `getBroadcaster()`.
|
|
487
|
-
4. Add a connectivity check in `getConnectedChannels()` in `emit-signal.ts`.
|
|
488
|
-
5. Add a destination resolver case in `destination-resolver.ts`.
|
|
489
|
-
6. Add the channel to the `NotificationChannel` union in `types.ts`.
|
|
490
|
-
|
|
491
475
|
## Audit Trail
|
|
492
476
|
|
|
493
477
|
Three SQLite tables form the audit chain:
|
|
@@ -498,21 +482,21 @@ Three SQLite tables form the audit chain:
|
|
|
498
482
|
|
|
499
483
|
### Client Delivery Ack
|
|
500
484
|
|
|
501
|
-
For vellum
|
|
485
|
+
For vellum deliveries, the audit trail extends past the SSE broadcast to the OS notification post. The `notification_intent` message carries an optional `deliveryId`; after posting the notification (or deciding not to, for the conversation already on screen), the client reports the outcome to `POST notification-intent-result` through `sendNotificationIntentAck()` in `clients/web/src/runtime/notifications.ts`.
|
|
502
486
|
|
|
503
487
|
The ack populates three columns on `notification_deliveries`:
|
|
504
488
|
|
|
505
|
-
| Column | Type | Description
|
|
506
|
-
| ------------------------ | ------- |
|
|
507
|
-
| `client_delivery_status` | TEXT | `'delivered'` if the
|
|
508
|
-
| `client_delivery_error` | TEXT | Error description when the post failed (e.g. authorization denied)
|
|
509
|
-
| `client_delivery_at` | INTEGER | Epoch ms timestamp of when the client reported the outcome
|
|
489
|
+
| Column | Type | Description |
|
|
490
|
+
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------------- |
|
|
491
|
+
| `client_delivery_status` | TEXT | `'delivered'` if the client handled the intent, `'client_failed'` otherwise. Last writer wins (see below) |
|
|
492
|
+
| `client_delivery_error` | TEXT | Error description when the post failed (e.g. authorization denied) |
|
|
493
|
+
| `client_delivery_at` | INTEGER | Epoch ms timestamp of when the client reported the outcome |
|
|
510
494
|
|
|
511
|
-
|
|
495
|
+
`'delivered'` covers two outcomes the column cannot tell apart: the OS accepted a posted banner, or the client deliberately showed none (the intent was `silent`, the user was already watching that conversation, or the sending assistant predates guardian targeting and the intent was guardian-scoped). The intent goes to every eligible connection, and each one acks the same `deliveryId`. `handleNotificationIntentResult()` in `runtime/routes/notification-routes.ts` overwrites the row with no client key, so the status holds the last client to report, not an aggregate, and `client_delivery_error` is only ever set, so it can outlive a later success. The audit trail answers three questions for each vellum delivery:
|
|
512
496
|
|
|
513
497
|
1. **Was the intent broadcast?** -- existing `status` column (`sent`)
|
|
514
|
-
2. **Did
|
|
515
|
-
3. **Did the
|
|
498
|
+
2. **Did any client report back?** -- `client_delivery_status` is non-null
|
|
499
|
+
3. **Did the last client to report fail, and why?** -- `client_delivery_status = 'client_failed'` + `client_delivery_error`
|
|
516
500
|
|
|
517
501
|
Query examples:
|
|
518
502
|
|
|
@@ -140,7 +140,7 @@ function makeSignal(
|
|
|
140
140
|
createdAt: 1700000000000,
|
|
141
141
|
sourceChannel: "scheduler",
|
|
142
142
|
sourceContextId: "ctx-1",
|
|
143
|
-
sourceEventName: "
|
|
143
|
+
sourceEventName: "assistant.share",
|
|
144
144
|
contextPayload: {},
|
|
145
145
|
attentionHints: {
|
|
146
146
|
requiresAction: false,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
intersectChannelAllowlist,
|
|
5
|
+
readChannelAllowlist,
|
|
6
|
+
} from "../channel-allowlist.js";
|
|
7
|
+
import type { NotificationChannel } from "../types.js";
|
|
8
|
+
|
|
9
|
+
describe("readChannelAllowlist", () => {
|
|
10
|
+
test("returns trimmed non-empty strings", () => {
|
|
11
|
+
expect(
|
|
12
|
+
readChannelAllowlist({
|
|
13
|
+
channelAllowlist: [" telegram ", "", "slack"],
|
|
14
|
+
}),
|
|
15
|
+
).toEqual(["telegram", "slack"]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("returns undefined for a missing or empty list", () => {
|
|
19
|
+
expect(readChannelAllowlist({})).toBeUndefined();
|
|
20
|
+
expect(readChannelAllowlist({ channelAllowlist: [] })).toBeUndefined();
|
|
21
|
+
expect(readChannelAllowlist({ channelAllowlist: ["", " "] })).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("intersectChannelAllowlist", () => {
|
|
26
|
+
test("keeps only available channel ids", () => {
|
|
27
|
+
const available = ["vellum", "telegram"] as NotificationChannel[];
|
|
28
|
+
expect(intersectChannelAllowlist(["telegram", "nope"], available)).toEqual([
|
|
29
|
+
"telegram",
|
|
30
|
+
]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("deduplicates a repeated available channel", () => {
|
|
34
|
+
const available = ["vellum", "telegram"] as NotificationChannel[];
|
|
35
|
+
expect(
|
|
36
|
+
intersectChannelAllowlist(["telegram", "telegram"], available),
|
|
37
|
+
).toEqual(["telegram"]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -21,7 +21,7 @@ function makeSignal(
|
|
|
21
21
|
createdAt: Date.now(),
|
|
22
22
|
sourceChannel: "scheduler",
|
|
23
23
|
sourceContextId: "ctx-1",
|
|
24
|
-
sourceEventName: "
|
|
24
|
+
sourceEventName: "assistant.share",
|
|
25
25
|
contextPayload: {},
|
|
26
26
|
attentionHints: {
|
|
27
27
|
requiresAction: false,
|
|
@@ -503,6 +503,76 @@ describe("composeFallbackCopy plugin schedule templates", () => {
|
|
|
503
503
|
});
|
|
504
504
|
});
|
|
505
505
|
|
|
506
|
+
// ── activity.complete rendering ───────────────────────────────────────
|
|
507
|
+
|
|
508
|
+
describe("activity.complete copy", () => {
|
|
509
|
+
function completeSignal(
|
|
510
|
+
contextPayload: Record<string, unknown>,
|
|
511
|
+
): NotificationSignal {
|
|
512
|
+
return makeSignal({
|
|
513
|
+
sourceEventName: "activity.complete",
|
|
514
|
+
contextPayload,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
test("a producer-authored title survives the fallback path", () => {
|
|
519
|
+
// The home feed reads the payload title first; the popup must say the
|
|
520
|
+
// same thing when the classifier was unreachable.
|
|
521
|
+
const copy = composeFallbackCopy(
|
|
522
|
+
completeSignal({
|
|
523
|
+
title: "Skill updated: Weekly Report Export",
|
|
524
|
+
summary: "Added the retry after an expired session.",
|
|
525
|
+
}),
|
|
526
|
+
CHANNELS,
|
|
527
|
+
);
|
|
528
|
+
expect(copy.vellum?.title).toBe("Skill updated: Weekly Report Export");
|
|
529
|
+
expect(copy.vellum?.body).toBe("Added the retry after an expired session.");
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test("the pass-through's requestedTitle is honored when no title is set", () => {
|
|
533
|
+
const copy = composeFallbackCopy(
|
|
534
|
+
completeSignal({
|
|
535
|
+
requestedTitle: "Nightly export finished",
|
|
536
|
+
summary: "Wrote 12 files to the reports folder.",
|
|
537
|
+
}),
|
|
538
|
+
CHANNELS,
|
|
539
|
+
);
|
|
540
|
+
expect(copy.vellum?.title).toBe("Nightly export finished");
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test("without an authored title the headline is the summary's first sentence", () => {
|
|
544
|
+
const copy = composeFallbackCopy(
|
|
545
|
+
completeSignal({
|
|
546
|
+
summary:
|
|
547
|
+
"Added the retry after an expired session. Dropped the login step.",
|
|
548
|
+
}),
|
|
549
|
+
CHANNELS,
|
|
550
|
+
);
|
|
551
|
+
expect(copy.vellum?.title).toBe(
|
|
552
|
+
"Added the retry after an expired session.",
|
|
553
|
+
);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test("an authored title the normalizer rejects falls back to the derived one", () => {
|
|
557
|
+
const copy = composeFallbackCopy(
|
|
558
|
+
completeSignal({
|
|
559
|
+
title: "Skill\nupdated",
|
|
560
|
+
summary: "Added the retry after an expired session.",
|
|
561
|
+
}),
|
|
562
|
+
CHANNELS,
|
|
563
|
+
);
|
|
564
|
+
expect(copy.vellum?.title).toBe(
|
|
565
|
+
"Added the retry after an expired session.",
|
|
566
|
+
);
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test("neither a title nor a summary still says something happened", () => {
|
|
570
|
+
const copy = composeFallbackCopy(completeSignal({}), CHANNELS);
|
|
571
|
+
expect(copy.vellum?.title).toBe("Activity complete");
|
|
572
|
+
expect(copy.vellum?.body).toBe("An activity has completed");
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
506
576
|
// ── deriveTitle ───────────────────────────────────────────────────────
|
|
507
577
|
|
|
508
578
|
describe("deriveTitle", () => {
|
|
@@ -99,7 +99,7 @@ function makeAssistantToolSignal(
|
|
|
99
99
|
createdAt: Date.now(),
|
|
100
100
|
sourceChannel: "assistant_tool",
|
|
101
101
|
sourceContextId: "tool-call-1",
|
|
102
|
-
sourceEventName: "
|
|
102
|
+
sourceEventName: "assistant.share",
|
|
103
103
|
contextPayload: {
|
|
104
104
|
requestedMessage: "exact verbatim text here",
|
|
105
105
|
requestedTitle: "Custom Title",
|
|
@@ -356,6 +356,81 @@ describe("assistant_tool pass-through in notification decision engine", () => {
|
|
|
356
356
|
expect(decision.selectedChannels).toEqual(["vellum"]);
|
|
357
357
|
expect(decision.renderedCopy.vellum?.body).toBe("fyi");
|
|
358
358
|
});
|
|
359
|
+
|
|
360
|
+
test("channelAllowlist replaces the default channel set", async () => {
|
|
361
|
+
const signal = makeAssistantToolSignal({
|
|
362
|
+
contextPayload: {
|
|
363
|
+
requestedMessage: "telegram only",
|
|
364
|
+
channelAllowlist: ["telegram"],
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
const decision = await evaluateSignal(signal, [
|
|
368
|
+
"vellum",
|
|
369
|
+
"telegram",
|
|
370
|
+
] as NotificationChannel[]);
|
|
371
|
+
|
|
372
|
+
expect(decision.selectedChannels).toEqual(["telegram"]);
|
|
373
|
+
expect(decision.shouldNotify).toBe(true);
|
|
374
|
+
expect(decision.renderedCopy.telegram?.body).toBe("telegram only");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("channelAllowlist wins over preferredChannels", async () => {
|
|
378
|
+
const signal = makeAssistantToolSignal({
|
|
379
|
+
contextPayload: {
|
|
380
|
+
requestedMessage: "exclusive telegram",
|
|
381
|
+
preferredChannels: ["slack"],
|
|
382
|
+
channelAllowlist: ["telegram"],
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
const decision = await evaluateSignal(signal, [
|
|
386
|
+
"vellum",
|
|
387
|
+
"telegram",
|
|
388
|
+
"slack",
|
|
389
|
+
] as NotificationChannel[]);
|
|
390
|
+
|
|
391
|
+
expect(decision.selectedChannels).toEqual(["telegram"]);
|
|
392
|
+
expect(decision.selectedChannels).not.toContain("vellum");
|
|
393
|
+
expect(decision.selectedChannels).not.toContain("slack");
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("urgent + channelAllowlist stays exclusive", async () => {
|
|
397
|
+
const signal = makeAssistantToolSignal({
|
|
398
|
+
contextPayload: {
|
|
399
|
+
requestedMessage: "urgent telegram only",
|
|
400
|
+
channelAllowlist: ["telegram"],
|
|
401
|
+
},
|
|
402
|
+
attentionHints: {
|
|
403
|
+
requiresAction: true,
|
|
404
|
+
urgency: "critical",
|
|
405
|
+
isAsyncBackground: false,
|
|
406
|
+
visibleInSourceNow: false,
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
const decision = await evaluateSignal(signal, [
|
|
410
|
+
"vellum",
|
|
411
|
+
"telegram",
|
|
412
|
+
"slack",
|
|
413
|
+
] as NotificationChannel[]);
|
|
414
|
+
|
|
415
|
+
expect(decision.selectedChannels).toEqual(["telegram"]);
|
|
416
|
+
expect(decision.shouldNotify).toBe(true);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test("channelAllowlist with no overlap yields an empty selection", async () => {
|
|
420
|
+
const signal = makeAssistantToolSignal({
|
|
421
|
+
contextPayload: {
|
|
422
|
+
requestedMessage: "nowhere to send",
|
|
423
|
+
channelAllowlist: ["disconnected_channel"],
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
const decision = await evaluateSignal(signal, [
|
|
427
|
+
"vellum",
|
|
428
|
+
"telegram",
|
|
429
|
+
] as NotificationChannel[]);
|
|
430
|
+
|
|
431
|
+
expect(decision.selectedChannels).toEqual([]);
|
|
432
|
+
expect(decision.shouldNotify).toBe(false);
|
|
433
|
+
});
|
|
359
434
|
});
|
|
360
435
|
|
|
361
436
|
describe("chat.assistant_reply pass-through in notification decision engine", () => {
|
|
@@ -905,7 +980,9 @@ describe("scheduler requested-message pass-through in notification decision engi
|
|
|
905
980
|
expect(decision.reasoningSummary).not.toBe(
|
|
906
981
|
"scheduler requested-message pass-through",
|
|
907
982
|
);
|
|
908
|
-
expect(decision.reasoningSummary).not.toBe(
|
|
983
|
+
expect(decision.reasoningSummary).not.toBe(
|
|
984
|
+
"schedule_result pass-through",
|
|
985
|
+
);
|
|
909
986
|
} finally {
|
|
910
987
|
providerSendMessage = previousSendMessage;
|
|
911
988
|
}
|