@vellumai/assistant 0.12.3-staging.1 → 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/ARCHITECTURE.md +43 -29
- package/Dockerfile +63 -0
- package/docs/architecture/security.md +10 -5
- package/docs/architecture/turn-actor.md +2 -0
- package/docs/desktop-browser-cli.md +45 -4
- 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/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/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- 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/src/gateway-ipc-contracts.ts +20 -0
- 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/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
- package/openapi.yaml +665 -14
- package/package.json +1 -1
- package/scripts/smoke-desktop-browser-cli.ts +10 -26
- package/scripts/smoke-desktop-window-theme.ts +2 -3
- 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__/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__/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__/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-retry-sweep.test.ts +9 -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-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.test.ts +546 -16
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
- package/src/__tests__/conversation-evictor.test.ts +62 -37
- 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-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 +6 -6
- package/src/__tests__/credential-prompt-route.test.ts +35 -3
- package/src/__tests__/cu-unified-flow.test.ts +82 -1
- 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 +25 -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__/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 +1 -1
- package/src/__tests__/interrupt-on-send.test.ts +57 -43
- package/src/__tests__/interrupt-turn-note.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +2 -2
- 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-provider-serializer.test.ts +12 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -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 -0
- 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__/runtime-events-sse-reconnect.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__/secure-keys.test.ts +43 -1
- package/src/__tests__/send-endpoint-busy.test.ts +163 -1
- package/src/__tests__/server-history-render.test.ts +91 -1
- package/src/__tests__/settings-routes.test.ts +50 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
- 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__/surface-completion-in-flight-snapshot.test.ts +2 -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-metadata-plumbing.test.ts +3 -1
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/twilio-routes.test.ts +1 -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 +99 -41
- package/src/api/events/assistant-turn-start.ts +3 -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/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 +31 -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 +6 -0
- package/src/approvals/AGENTS.md +4 -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 -8
- 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__/notifications.test.ts +35 -10
- package/src/cli/commands/browser.help.ts +4 -5
- 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 +6 -1
- package/src/cli/commands/notifications.help.ts +16 -2
- package/src/cli/commands/notifications.ts +30 -8
- package/src/cli/commands/roadmap.help.ts +18 -21
- package/src/cli/commands/roadmap.ts +4 -21
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
- package/src/cli/lib/bundled-marketplace.json +84 -34
- package/src/cli/lib/plugin-marketplace.ts +8 -0
- package/src/cli/lib/publish-plugin.ts +1 -2
- package/src/config/__tests__/default-provider.test.ts +3 -1
- 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/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/profile-text-generation.test.ts +2 -2
- 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 +3 -1
- package/src/config/schemas/mcp.ts +2 -1
- package/src/config/schemas/memory-retrospective.ts +11 -2
- 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 +1 -11
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
- package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
- package/src/daemon/browser-mode-session.test.ts +402 -0
- package/src/daemon/browser-mode-session.ts +186 -0
- 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 +58 -10
- package/src/daemon/conversation-agent-loop.ts +219 -25
- 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 +8 -3
- package/src/daemon/conversation.ts +143 -41
- 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/message-protocol.ts +0 -1
- package/src/daemon/message-provenance.ts +28 -4
- package/src/daemon/mode-session-startup-recovery.ts +23 -0
- package/src/daemon/mode-session-tracking.ts +54 -0
- package/src/daemon/process-message.ts +13 -4
- package/src/daemon/virtual-desktop-context.ts +21 -0
- 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 +76 -68
- package/src/desktop/desktop-automation-lease.ts +41 -50
- 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-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 +11 -122
- package/src/desktop/desktop-stream-bridge.ts +6 -8
- package/src/desktop/desktop-window-theme.ts +5 -25
- package/src/desktop/virtual-desktop-feature.ts +52 -2
- 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 +63 -18
- package/src/mcp/__tests__/tool-caps.test.ts +28 -1
- package/src/mcp/manager.ts +3 -0
- package/src/mcp/tool-caps.ts +12 -4
- 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/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 +1 -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 +0 -23
- 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/signal.ts +4 -19
- package/src/notifications/types.ts +15 -0
- package/src/oauth/connection-resolver.ts +1 -2
- package/src/oauth/oauth-store.ts +1 -3
- package/src/oauth/provider-serializer.ts +7 -0
- package/src/oauth/seed-providers.ts +30 -0
- package/src/onboarding/onboarding-events-store.ts +99 -12
- package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
- package/src/persistence/client-connection-events-store.ts +377 -0
- package/src/persistence/conversation-crud.ts +55 -6
- package/src/persistence/conversation-mode-sessions.test.ts +424 -0
- package/src/persistence/conversation-mode-sessions.ts +460 -0
- package/src/persistence/conversation-queries.ts +6 -19
- package/src/persistence/llm-usage-store.ts +3 -9
- package/src/persistence/message-metadata.ts +25 -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/conversations.ts +67 -0
- package/src/persistence/schema/infrastructure.ts +29 -0
- package/src/persistence/steps.ts +17 -0
- package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
- package/src/plugin-api/effective-context-window.ts +33 -0
- package/src/plugin-api/index.ts +24 -5
- 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/__tests__/archive-removal-framing.test.ts +64 -0
- package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
- package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
- package/src/plugins/defaults/memory/context-search/format.ts +51 -7
- 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/types.ts +1 -11
- package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
- package/src/plugins/defaults/memory/graph/tools.ts +35 -64
- package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
- package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
- package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
- package/src/plugins/defaults/memory/tools.test.ts +124 -9
- package/src/plugins/defaults/memory/tools.ts +38 -11
- package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
- package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
- package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
- package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
- 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/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 +1 -1
- package/src/providers/jev/client.test.ts +10 -0
- package/src/providers/jev/client.ts +20 -4
- package/src/providers/model-catalog.ts +4 -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/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 +4 -2
- package/src/runtime/actor-trust-resolver.ts +3 -3
- package/src/runtime/agent-wake.ts +1 -1
- 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-reply-router.ts +28 -32
- 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/vbundle-builder.ts +24 -1
- package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
- 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 +1 -1
- 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/client-routes.ts +108 -0
- package/src/runtime/routes/conversation-management-routes.ts +4 -1
- package/src/runtime/routes/conversation-routes.ts +113 -27
- 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 +4 -6
- 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/identity-routes.ts +1 -5
- package/src/runtime/routes/image-generation-routes.ts +8 -14
- package/src/runtime/routes/inbound-message-handler.ts +35 -38
- 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 +38 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -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-providers.ts +7 -0
- package/src/runtime/routes/plugins-routes.ts +6 -0
- package/src/runtime/routes/roadmap-routes.ts +47 -8
- package/src/runtime/routes/secret-routes.ts +1 -1
- 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/worker-control.ts +15 -5
- package/src/security/secure-keys.ts +53 -103
- 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.ts +6 -4
- package/src/tools/browser/browser-execution.ts +22 -59
- 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 +4 -1
- 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/skills/load.ts +2 -1
- package/src/tools/skills/sandbox-runner.ts +11 -2
- 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 +11 -0
- package/src/tools/terminal/safe-env.ts +4 -0
- package/src/tools/terminal/shell.ts +2 -0
- 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/canonicalize-identity.test.ts +21 -0
- package/src/util/canonicalize-identity.ts +16 -0
- package/src/util/errors.ts +13 -3
- package/src/util/provider-error-patterns.ts +3 -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
|
@@ -9,10 +9,16 @@ import {
|
|
|
9
9
|
setCesReconnect,
|
|
10
10
|
} from "../security/secure-keys.js";
|
|
11
11
|
import { getLogger } from "../util/logger.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
openCesRpcSession,
|
|
14
|
+
reconnectCesRpcSession,
|
|
15
|
+
} from "./ces-connect.js";
|
|
16
|
+
import {
|
|
17
|
+
type CesClient,
|
|
18
|
+
type CesClientHandshakeOptions,
|
|
19
|
+
} from "./client.js";
|
|
13
20
|
import {
|
|
14
21
|
type CesProcessManager,
|
|
15
|
-
CesUnavailableError,
|
|
16
22
|
createCesProcessManager,
|
|
17
23
|
} from "./process-manager.js";
|
|
18
24
|
import {
|
|
@@ -45,70 +51,69 @@ interface CesStartupResult {
|
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
/**
|
|
48
|
-
*
|
|
49
|
-
* handles to the in-flight initialization
|
|
50
|
-
* for startup to continue.
|
|
54
|
+
* Open the assistant's CES RPC client and perform the handshake. Returns
|
|
55
|
+
* immediately with handles to the in-flight initialization: callers don't
|
|
56
|
+
* need to await this for startup to continue.
|
|
51
57
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
58
|
+
* Claims reconnect ownership before any credential read so boot identity
|
|
59
|
+
* loading cannot open a second, identity-less session through the child
|
|
60
|
+
* entry point. CES serves a multi-connection Unix socket, so child
|
|
61
|
+
* processes in other address spaces still open their own connections.
|
|
54
62
|
*/
|
|
55
63
|
function startCesProcess(config: AssistantConfig): CesStartupResult {
|
|
56
64
|
const pm = createCesProcessManager({ assistantConfig: config });
|
|
57
65
|
const abortController = new AbortController();
|
|
58
66
|
let currentClient: CesClient | undefined;
|
|
67
|
+
let handshake: CesClientHandshakeOptions = {};
|
|
68
|
+
|
|
69
|
+
// Own this process's CES session before resolveManagedProxyContext()
|
|
70
|
+
// reads the API key. That read must not take the child open path.
|
|
71
|
+
setCesReconnect(async () => {
|
|
72
|
+
const client = await reconnectCesRpcSession(pm, handshake);
|
|
73
|
+
if (client) {
|
|
74
|
+
log.info("CES reconnection handshake accepted");
|
|
75
|
+
}
|
|
76
|
+
return client;
|
|
77
|
+
});
|
|
59
78
|
|
|
60
79
|
const handshakePromise = (async (): Promise<CesClient | undefined> => {
|
|
61
80
|
try {
|
|
62
|
-
const transport = await pm.start();
|
|
63
|
-
if (abortController.signal.aborted) {
|
|
64
|
-
throw new Error("CES initialization aborted during shutdown");
|
|
65
|
-
}
|
|
66
|
-
const client = createCesClient(transport);
|
|
67
|
-
currentClient = client;
|
|
68
81
|
// Resolve the assistant API key so CES can use it for platform
|
|
69
82
|
// credential materialisation. In managed mode the key is provisioned
|
|
70
|
-
// after hatch and stored in the credential store
|
|
71
|
-
// the env var, so we pass it via the handshake.
|
|
83
|
+
// after hatch and stored in the credential store. CES can't read
|
|
84
|
+
// the env var, so we pass it via the handshake. Reconnect ownership
|
|
85
|
+
// is already claimed, so this read uses HTTP or the encrypted store,
|
|
86
|
+
// not a second RPC session.
|
|
72
87
|
const proxyCtx = await resolveManagedProxyContext();
|
|
88
|
+
if (abortController.signal.aborted) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
73
91
|
const assistantId = getPlatformAssistantId();
|
|
74
|
-
|
|
92
|
+
handshake = {
|
|
75
93
|
...(proxyCtx.assistantApiKey
|
|
76
94
|
? { assistantApiKey: proxyCtx.assistantApiKey }
|
|
77
95
|
: {}),
|
|
78
96
|
...(assistantId ? { assistantId } : {}),
|
|
97
|
+
};
|
|
98
|
+
const session = await openCesRpcSession({
|
|
99
|
+
processManager: pm,
|
|
100
|
+
signal: abortController.signal,
|
|
101
|
+
handshake,
|
|
79
102
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
throw new Error("CES initialization aborted during shutdown");
|
|
83
|
-
}
|
|
84
|
-
if (accepted) {
|
|
103
|
+
currentClient = session?.client;
|
|
104
|
+
if (session) {
|
|
85
105
|
log.info(
|
|
86
106
|
"CES client initialized and handshake accepted (server-level)",
|
|
87
107
|
);
|
|
88
|
-
return client;
|
|
89
108
|
}
|
|
109
|
+
return session?.client;
|
|
110
|
+
} catch (err) {
|
|
90
111
|
log.warn(
|
|
91
|
-
{
|
|
92
|
-
"
|
|
112
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
113
|
+
"Failed to initialize CES client",
|
|
93
114
|
);
|
|
94
|
-
client.close();
|
|
95
115
|
currentClient = undefined;
|
|
96
|
-
await pm.stop();
|
|
97
|
-
return undefined;
|
|
98
|
-
} catch (err) {
|
|
99
|
-
if (err instanceof CesUnavailableError) {
|
|
100
|
-
log.info(
|
|
101
|
-
{ reason: err.message },
|
|
102
|
-
"CES is not available — CES tools will be unavailable",
|
|
103
|
-
);
|
|
104
|
-
} else {
|
|
105
|
-
log.warn(
|
|
106
|
-
{ error: err instanceof Error ? err.message : String(err) },
|
|
107
|
-
"Failed to initialize CES client — CES tools will be unavailable",
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
116
|
await pm.stop().catch(() => {});
|
|
111
|
-
currentClient = undefined;
|
|
112
117
|
return undefined;
|
|
113
118
|
}
|
|
114
119
|
})();
|
|
@@ -154,11 +159,11 @@ function updateClientRef(client: CesClient | undefined): void {
|
|
|
154
159
|
}
|
|
155
160
|
|
|
156
161
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* direct credential store.
|
|
162
|
+
* Open the assistant's CES RPC client: handshake (blocking up to a 20s
|
|
163
|
+
* timeout so credential reads can route through CES before provider init)
|
|
164
|
+
* and keep the live client reference in sync. Reconnect ownership is
|
|
165
|
+
* claimed before the identity read. Non-fatal: on failure the assistant
|
|
166
|
+
* falls back to the direct credential store.
|
|
162
167
|
*/
|
|
163
168
|
export async function startCes(config: AssistantConfig): Promise<void> {
|
|
164
169
|
const cesResult = startCesProcess(config);
|
|
@@ -171,7 +176,7 @@ export async function startCes(config: AssistantConfig): Promise<void> {
|
|
|
171
176
|
timeoutMs: DEFAULT_CES_STARTUP_TIMEOUT_MS,
|
|
172
177
|
onTimeout: () => {
|
|
173
178
|
log.warn(
|
|
174
|
-
"CES handshake timed out after 20s
|
|
179
|
+
"CES handshake timed out after 20s, falling back to direct credential store",
|
|
175
180
|
);
|
|
176
181
|
},
|
|
177
182
|
});
|
|
@@ -193,56 +198,13 @@ export async function startCes(config: AssistantConfig): Promise<void> {
|
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
200
|
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
201
|
+
// Reconnect ownership is claimed inside startCesProcess before the
|
|
202
|
+
// identity read. Snapshotting the API key there (not here) avoids a
|
|
203
|
+
// second resolveManagedProxyContext() after setCesClient, which would
|
|
204
|
+
// read the key through CES while reconnecting.
|
|
199
205
|
if (cesResult.processManager) {
|
|
200
206
|
const pm = cesResult.processManager;
|
|
201
207
|
|
|
202
|
-
// Snapshot the managed-proxy context and assistant ID at CES startup so the
|
|
203
|
-
// reconnect closure below never calls back into `resolveManagedProxyContext()`.
|
|
204
|
-
// That function reads the assistant API key via `getSecureKeyAsync()`, which
|
|
205
|
-
// — once `setCesClient()` has resolved the backend to CES RPC — routes the
|
|
206
|
-
// read through CES itself. During a reconnect the old transport is dead and
|
|
207
|
-
// a new one is being set up by this very closure, so the nested credential
|
|
208
|
-
// read recursively awaits its own in-flight reconnection and deadlocks until
|
|
209
|
-
// `CREDENTIAL_OP_TIMEOUT_MS` (45s) fires. That 45-second stall delays every
|
|
210
|
-
// CES restart and causes dependent credential reads (e.g. Meet's STT
|
|
211
|
-
// provider resolution) to return `undefined` during the window. API key
|
|
212
|
-
// rotation uses the `updateAssistantApiKey` RPC on the live client, not a
|
|
213
|
-
// reconnect, so caching at startup is safe.
|
|
214
|
-
const startupProxyCtx = await resolveManagedProxyContext();
|
|
215
|
-
const startupAssistantId = getPlatformAssistantId();
|
|
216
|
-
|
|
217
|
-
setCesReconnect(async () => {
|
|
218
|
-
try {
|
|
219
|
-
await pm.stop();
|
|
220
|
-
const transport = await pm.start();
|
|
221
|
-
const newClient = createCesClient(transport);
|
|
222
|
-
const { accepted, reason } = await newClient.handshake({
|
|
223
|
-
...(startupProxyCtx.assistantApiKey
|
|
224
|
-
? { assistantApiKey: startupProxyCtx.assistantApiKey }
|
|
225
|
-
: {}),
|
|
226
|
-
...(startupAssistantId ? { assistantId: startupAssistantId } : {}),
|
|
227
|
-
});
|
|
228
|
-
if (accepted) {
|
|
229
|
-
log.info("CES reconnection handshake accepted");
|
|
230
|
-
return newClient;
|
|
231
|
-
}
|
|
232
|
-
log.warn({ reason }, "CES reconnection handshake rejected");
|
|
233
|
-
newClient.close();
|
|
234
|
-
await pm.stop().catch(() => {});
|
|
235
|
-
return undefined;
|
|
236
|
-
} catch (err) {
|
|
237
|
-
log.warn(
|
|
238
|
-
{ error: err instanceof Error ? err.message : String(err) },
|
|
239
|
-
"CES reconnection attempt failed",
|
|
240
|
-
);
|
|
241
|
-
await pm.stop().catch(() => {});
|
|
242
|
-
return undefined;
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
|
|
246
208
|
// Proactive reconnect: when the transport dies (socket close, process
|
|
247
209
|
// exit), start a retry-with-backoff loop immediately instead of waiting
|
|
248
210
|
// for the next credential operation to trigger the lazy reconnect path.
|
|
@@ -53,9 +53,7 @@ export type DiscoveryResult = ManagedDiscoverySuccess | DiscoveryFailure;
|
|
|
53
53
|
* processes can each connect. The actual connection is made later by
|
|
54
54
|
* `CesProcessManager.start()`.
|
|
55
55
|
*/
|
|
56
|
-
export function
|
|
57
|
-
| ManagedDiscoverySuccess
|
|
58
|
-
| DiscoveryFailure {
|
|
56
|
+
export function discoverCes(): DiscoveryResult {
|
|
59
57
|
const socketPath = getCesSocketPath();
|
|
60
58
|
|
|
61
59
|
if (!isNamedPipePath(socketPath) && !existsSync(socketPath)) {
|
|
@@ -68,14 +66,6 @@ export function discoverManagedCes():
|
|
|
68
66
|
return { mode: "managed", socketPath };
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
/**
|
|
72
|
-
* Discover CES for the current process. Local and managed topologies use
|
|
73
|
-
* the same bootstrap socket path.
|
|
74
|
-
*/
|
|
75
|
-
export function discoverCes(): DiscoveryResult {
|
|
76
|
-
return discoverManagedCes();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
69
|
/** How long to poll for the CES socket before giving up. */
|
|
80
70
|
const MANAGED_DISCOVERY_TIMEOUT_MS = 3_000;
|
|
81
71
|
|
|
@@ -440,7 +440,12 @@ describe("handleSurfaceAction — launch_conversation dispatch", () => {
|
|
|
440
440
|
// claimed to eliminate that round-trip; this test enforces it.
|
|
441
441
|
nextKeyStoreResult = { conversationId: "conv-pending-set" };
|
|
442
442
|
|
|
443
|
-
const
|
|
443
|
+
const settleStructuralWait = mock(() => true);
|
|
444
|
+
const ctx = makeContext({
|
|
445
|
+
modeSessions: {
|
|
446
|
+
settleStructuralWait,
|
|
447
|
+
} as unknown as Conversation["modeSessions"],
|
|
448
|
+
});
|
|
444
449
|
registerCardSurface(ctx, "surface-pending");
|
|
445
450
|
// Simulate `ui_show` having stamped a pending entry for this surface
|
|
446
451
|
// (which it does for any interactive card, including persistent ones).
|
|
@@ -473,6 +478,10 @@ describe("handleSurfaceAction — launch_conversation dispatch", () => {
|
|
|
473
478
|
// Pending entry was deleted so subsequent sibling clicks on the same
|
|
474
479
|
// persistent card aren't blocked behind a stale "owes-an-answer" flag.
|
|
475
480
|
expect(ctx.pendingSurfaceActions.has("surface-pending")).toBe(false);
|
|
481
|
+
expect(settleStructuralWait).toHaveBeenCalledWith(
|
|
482
|
+
{ kind: "surface", responseId: "surface-pending" },
|
|
483
|
+
{ status: "completed", endReason: "surface_launch_settled" },
|
|
484
|
+
);
|
|
476
485
|
|
|
477
486
|
await processStartedPromise;
|
|
478
487
|
resolveProcess();
|
|
@@ -47,6 +47,7 @@ mock.module("../../plugins/defaults/memory/v2/activation-log-store.js", () => ({
|
|
|
47
47
|
}));
|
|
48
48
|
|
|
49
49
|
// ── Imports (after mocks) ────────────────────────────────────────────────────
|
|
50
|
+
import { mockUnownedModeSessions } from "../../__tests__/helpers/mock-conversation.js";
|
|
50
51
|
import type { AgentEvent } from "../../agent/loop.js";
|
|
51
52
|
import type { Message } from "../../providers/types.js";
|
|
52
53
|
import type {
|
|
@@ -68,6 +69,7 @@ function makeDeps(): EventHandlerDeps {
|
|
|
68
69
|
provider: { name: "mock-provider" },
|
|
69
70
|
currentTurnSurfaces: [],
|
|
70
71
|
trustContext: undefined,
|
|
72
|
+
modeSessions: mockUnownedModeSessions(),
|
|
71
73
|
} as unknown as EventHandlerDeps["ctx"],
|
|
72
74
|
onEvent: () => {},
|
|
73
75
|
reqId: "req-profile-notify",
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { setOverridesForTesting } from "../__tests__/feature-flag-test-helpers.js";
|
|
4
|
+
import type { ModeSession } from "../api/mode-session.js";
|
|
5
|
+
import { isSessionGroupsEnabled } from "../config/session-groups-gate.js";
|
|
6
|
+
import { BrowserModeSessionProducer } from "./browser-mode-session.js";
|
|
7
|
+
import type {
|
|
8
|
+
ModeSessionSourceHandle,
|
|
9
|
+
ModeSessionTerminalDisposition,
|
|
10
|
+
} from "./conversation-mode-session.js";
|
|
11
|
+
|
|
12
|
+
function createCoordinator() {
|
|
13
|
+
let owner: ModeSession | undefined;
|
|
14
|
+
let activation = 0;
|
|
15
|
+
const activated: ModeSessionSourceHandle[] = [];
|
|
16
|
+
const retired: Array<{
|
|
17
|
+
handle: ModeSessionSourceHandle;
|
|
18
|
+
disposition?: ModeSessionTerminalDisposition;
|
|
19
|
+
}> = [];
|
|
20
|
+
const activity: Array<{ turnId: string; at: number }> = [];
|
|
21
|
+
|
|
22
|
+
const coordinator = {
|
|
23
|
+
activateSource(input: {
|
|
24
|
+
sourceId: string;
|
|
25
|
+
generation: number;
|
|
26
|
+
mode: "browser";
|
|
27
|
+
sourceStartedAt: number;
|
|
28
|
+
}): ModeSessionSourceHandle {
|
|
29
|
+
activation += 1;
|
|
30
|
+
const handle = {
|
|
31
|
+
sourceId: input.sourceId,
|
|
32
|
+
generation: input.generation,
|
|
33
|
+
activation,
|
|
34
|
+
id: `browser-${activation}`,
|
|
35
|
+
mode: input.mode,
|
|
36
|
+
} as const;
|
|
37
|
+
activated.push(handle);
|
|
38
|
+
return handle;
|
|
39
|
+
},
|
|
40
|
+
claimTurn(
|
|
41
|
+
_turnId: string,
|
|
42
|
+
handle: ModeSessionSourceHandle,
|
|
43
|
+
_at: number,
|
|
44
|
+
): ModeSession {
|
|
45
|
+
owner ??= { id: handle.id, mode: handle.mode };
|
|
46
|
+
return owner;
|
|
47
|
+
},
|
|
48
|
+
getTurnOwner: () => owner,
|
|
49
|
+
recordActivity: (turnId: string, at: number) => {
|
|
50
|
+
activity.push({ turnId, at });
|
|
51
|
+
return true;
|
|
52
|
+
},
|
|
53
|
+
retireSource: (
|
|
54
|
+
handle: ModeSessionSourceHandle,
|
|
55
|
+
disposition?: ModeSessionTerminalDisposition,
|
|
56
|
+
) => {
|
|
57
|
+
retired.push({ handle, disposition });
|
|
58
|
+
return true;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
coordinator,
|
|
64
|
+
activated,
|
|
65
|
+
retired,
|
|
66
|
+
activity,
|
|
67
|
+
clearOwner: () => {
|
|
68
|
+
owner = undefined;
|
|
69
|
+
},
|
|
70
|
+
setOwner: (next: ModeSession) => {
|
|
71
|
+
owner = next;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe("BrowserModeSessionProducer", () => {
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
setOverridesForTesting({});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test.each([false, true])(
|
|
82
|
+
"retires failed admission while preserving a partial turn owner (%s)",
|
|
83
|
+
(partial) => {
|
|
84
|
+
const state = createCoordinator();
|
|
85
|
+
const claim = state.coordinator.claimTurn;
|
|
86
|
+
state.coordinator.claimTurn = (...args) => {
|
|
87
|
+
if (partial) {
|
|
88
|
+
claim(...args);
|
|
89
|
+
}
|
|
90
|
+
throw new Error("tracking unavailable");
|
|
91
|
+
};
|
|
92
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
93
|
+
expect(() =>
|
|
94
|
+
producer.beginOperation({
|
|
95
|
+
turnId: "turn-123",
|
|
96
|
+
lifecycle: "action",
|
|
97
|
+
at: 100,
|
|
98
|
+
}),
|
|
99
|
+
).toThrow("tracking unavailable");
|
|
100
|
+
expect(state.retired).toHaveLength(1);
|
|
101
|
+
expect(state.retired[0]?.disposition).toMatchObject({
|
|
102
|
+
status: "interrupted",
|
|
103
|
+
endReason: "tracking_failed",
|
|
104
|
+
});
|
|
105
|
+
if (partial) {
|
|
106
|
+
expect(state.coordinator.getTurnOwner()).toBeDefined();
|
|
107
|
+
} else {
|
|
108
|
+
expect(state.coordinator.getTurnOwner()).toBeUndefined();
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
test("admits new tracking only while the shared feature flag is enabled", () => {
|
|
114
|
+
const state = createCoordinator();
|
|
115
|
+
const producer = new BrowserModeSessionProducer(
|
|
116
|
+
state.coordinator,
|
|
117
|
+
1,
|
|
118
|
+
isSessionGroupsEnabled,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
setOverridesForTesting({});
|
|
122
|
+
expect(isSessionGroupsEnabled()).toBe(false);
|
|
123
|
+
expect(
|
|
124
|
+
producer.beginOperation({
|
|
125
|
+
turnId: "turn-disabled",
|
|
126
|
+
lifecycle: "action",
|
|
127
|
+
at: 100,
|
|
128
|
+
}),
|
|
129
|
+
).toBeUndefined();
|
|
130
|
+
expect(state.activated).toEqual([]);
|
|
131
|
+
|
|
132
|
+
setOverridesForTesting({ "session-groups": true });
|
|
133
|
+
const admitted = producer.beginOperation({
|
|
134
|
+
turnId: "turn-enabled",
|
|
135
|
+
lifecycle: "action",
|
|
136
|
+
at: 110,
|
|
137
|
+
});
|
|
138
|
+
expect(admitted?.owner).toEqual({ id: "browser-1", mode: "browser" });
|
|
139
|
+
|
|
140
|
+
setOverridesForTesting({ "session-groups": false });
|
|
141
|
+
state.clearOwner();
|
|
142
|
+
const close = producer.beginOperation({
|
|
143
|
+
turnId: "turn-close",
|
|
144
|
+
lifecycle: "terminal",
|
|
145
|
+
at: 120,
|
|
146
|
+
});
|
|
147
|
+
expect(close?.owner).toEqual({ id: "browser-1", mode: "browser" });
|
|
148
|
+
expect(
|
|
149
|
+
producer.finishOperation(close, {
|
|
150
|
+
at: 130,
|
|
151
|
+
isError: false,
|
|
152
|
+
cancelled: false,
|
|
153
|
+
terminalReason: "browser_closed",
|
|
154
|
+
}),
|
|
155
|
+
).toBe(true);
|
|
156
|
+
expect(state.retired).toHaveLength(1);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("claims the first action and records repeated successful activity", () => {
|
|
160
|
+
const state = createCoordinator();
|
|
161
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
162
|
+
|
|
163
|
+
const first = producer.beginOperation({
|
|
164
|
+
turnId: "turn-1",
|
|
165
|
+
lifecycle: "action",
|
|
166
|
+
at: 100,
|
|
167
|
+
});
|
|
168
|
+
expect(first?.owner).toEqual({ id: "browser-1", mode: "browser" });
|
|
169
|
+
expect(state.activated).toHaveLength(1);
|
|
170
|
+
expect(
|
|
171
|
+
producer.finishOperation(first, {
|
|
172
|
+
at: 120,
|
|
173
|
+
isError: false,
|
|
174
|
+
cancelled: false,
|
|
175
|
+
}),
|
|
176
|
+
).toBe(true);
|
|
177
|
+
expect(state.activity).toEqual([{ turnId: "turn-1", at: 120 }]);
|
|
178
|
+
|
|
179
|
+
const repeated = producer.beginOperation({
|
|
180
|
+
turnId: "turn-1",
|
|
181
|
+
lifecycle: "action",
|
|
182
|
+
at: 130,
|
|
183
|
+
});
|
|
184
|
+
expect(repeated?.owner.id).toBe("browser-1");
|
|
185
|
+
expect(state.activated).toHaveLength(1);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("status and a terminal operation without an active run are inert", () => {
|
|
189
|
+
const state = createCoordinator();
|
|
190
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
191
|
+
|
|
192
|
+
expect(
|
|
193
|
+
producer.beginOperation({
|
|
194
|
+
turnId: "turn-1",
|
|
195
|
+
lifecycle: "status",
|
|
196
|
+
at: 100,
|
|
197
|
+
}),
|
|
198
|
+
).toBeUndefined();
|
|
199
|
+
expect(
|
|
200
|
+
producer.beginOperation({
|
|
201
|
+
turnId: "turn-1",
|
|
202
|
+
lifecycle: "terminal",
|
|
203
|
+
at: 110,
|
|
204
|
+
}),
|
|
205
|
+
).toBeUndefined();
|
|
206
|
+
expect(state.activated).toEqual([]);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("keeps an existing non-browser owner without activating a nested run", () => {
|
|
210
|
+
const state = createCoordinator();
|
|
211
|
+
state.setOwner({ id: "live-1", mode: "live_vision" });
|
|
212
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
213
|
+
|
|
214
|
+
const token = producer.beginOperation({
|
|
215
|
+
turnId: "turn-1",
|
|
216
|
+
lifecycle: "action",
|
|
217
|
+
at: 100,
|
|
218
|
+
});
|
|
219
|
+
expect(token).toMatchObject({ owner: { id: "live-1" } });
|
|
220
|
+
expect(token?.handle).toBeUndefined();
|
|
221
|
+
expect(state.activated).toEqual([]);
|
|
222
|
+
expect(
|
|
223
|
+
producer.finishOperation(token, {
|
|
224
|
+
at: 120,
|
|
225
|
+
isError: false,
|
|
226
|
+
cancelled: false,
|
|
227
|
+
}),
|
|
228
|
+
).toBe(true);
|
|
229
|
+
expect(state.activity).toEqual([{ turnId: "turn-1", at: 120 }]);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("successful close retires the active run with a completed disposition", () => {
|
|
233
|
+
const state = createCoordinator();
|
|
234
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
235
|
+
producer.beginOperation({
|
|
236
|
+
turnId: "turn-1",
|
|
237
|
+
lifecycle: "action",
|
|
238
|
+
at: 100,
|
|
239
|
+
});
|
|
240
|
+
state.clearOwner();
|
|
241
|
+
|
|
242
|
+
const close = producer.beginOperation({
|
|
243
|
+
turnId: "turn-close",
|
|
244
|
+
lifecycle: "terminal",
|
|
245
|
+
at: 200,
|
|
246
|
+
});
|
|
247
|
+
expect(close?.owner.id).toBe("browser-1");
|
|
248
|
+
expect(
|
|
249
|
+
producer.finishOperation(close, {
|
|
250
|
+
at: 220,
|
|
251
|
+
isError: false,
|
|
252
|
+
cancelled: false,
|
|
253
|
+
terminalReason: "browser_closed",
|
|
254
|
+
}),
|
|
255
|
+
).toBe(true);
|
|
256
|
+
expect(state.retired).toEqual([
|
|
257
|
+
{
|
|
258
|
+
handle: expect.objectContaining({ id: "browser-1", activation: 1 }),
|
|
259
|
+
disposition: {
|
|
260
|
+
status: "completed",
|
|
261
|
+
endReason: "browser_closed",
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("a foreign-owned terminal operation retires only the captured browser run", () => {
|
|
268
|
+
const state = createCoordinator();
|
|
269
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
270
|
+
producer.beginOperation({
|
|
271
|
+
turnId: "turn-browser",
|
|
272
|
+
lifecycle: "action",
|
|
273
|
+
at: 100,
|
|
274
|
+
});
|
|
275
|
+
state.setOwner({ id: "live-1", mode: "live_vision" });
|
|
276
|
+
|
|
277
|
+
const close = producer.beginOperation({
|
|
278
|
+
turnId: "turn-live",
|
|
279
|
+
lifecycle: "terminal",
|
|
280
|
+
at: 200,
|
|
281
|
+
});
|
|
282
|
+
expect(close).toMatchObject({
|
|
283
|
+
owner: { id: "live-1", mode: "live_vision" },
|
|
284
|
+
handle: { id: "browser-1", activation: 1 },
|
|
285
|
+
});
|
|
286
|
+
expect(
|
|
287
|
+
producer.finishOperation(close, {
|
|
288
|
+
at: 220,
|
|
289
|
+
isError: false,
|
|
290
|
+
cancelled: false,
|
|
291
|
+
terminalReason: "browser_detached",
|
|
292
|
+
}),
|
|
293
|
+
).toBe(true);
|
|
294
|
+
expect(state.retired.at(-1)).toEqual({
|
|
295
|
+
handle: expect.objectContaining({ id: "browser-1", activation: 1 }),
|
|
296
|
+
disposition: {
|
|
297
|
+
status: "completed",
|
|
298
|
+
endReason: "browser_detached",
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
state.clearOwner();
|
|
303
|
+
producer.beginOperation({
|
|
304
|
+
turnId: "turn-browser-2",
|
|
305
|
+
lifecycle: "action",
|
|
306
|
+
at: 300,
|
|
307
|
+
});
|
|
308
|
+
expect(
|
|
309
|
+
producer.finishOperation(close, {
|
|
310
|
+
at: 320,
|
|
311
|
+
isError: true,
|
|
312
|
+
cancelled: false,
|
|
313
|
+
}),
|
|
314
|
+
).toBe(false);
|
|
315
|
+
expect(state.retired).toHaveLength(1);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("a failed close keeps the session active until a successful retry", () => {
|
|
319
|
+
const state = createCoordinator();
|
|
320
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
321
|
+
const action = producer.beginOperation({
|
|
322
|
+
turnId: "turn-1",
|
|
323
|
+
lifecycle: "action",
|
|
324
|
+
at: 100,
|
|
325
|
+
});
|
|
326
|
+
const close = producer.beginOperation({
|
|
327
|
+
turnId: "turn-1",
|
|
328
|
+
lifecycle: "terminal",
|
|
329
|
+
at: 110,
|
|
330
|
+
});
|
|
331
|
+
expect(
|
|
332
|
+
producer.finishOperation(close, {
|
|
333
|
+
at: 120,
|
|
334
|
+
isError: true,
|
|
335
|
+
cancelled: false,
|
|
336
|
+
}),
|
|
337
|
+
).toBe(true);
|
|
338
|
+
expect(state.retired).toHaveLength(0);
|
|
339
|
+
expect(state.activity).toEqual([{ turnId: "turn-1", at: 120 }]);
|
|
340
|
+
|
|
341
|
+
const retry = producer.beginOperation({
|
|
342
|
+
turnId: "turn-1",
|
|
343
|
+
lifecycle: "terminal",
|
|
344
|
+
at: 130,
|
|
345
|
+
});
|
|
346
|
+
expect(retry?.handle).toEqual(action?.handle);
|
|
347
|
+
producer.finishOperation(retry, {
|
|
348
|
+
at: 140,
|
|
349
|
+
isError: false,
|
|
350
|
+
cancelled: false,
|
|
351
|
+
});
|
|
352
|
+
expect(state.retired[0]?.disposition).toEqual({
|
|
353
|
+
status: "completed",
|
|
354
|
+
endReason: "browser_closed",
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("cancellation retires the exact run while accepted output remains on its captured turn", () => {
|
|
359
|
+
const state = createCoordinator();
|
|
360
|
+
const producer = new BrowserModeSessionProducer(state.coordinator);
|
|
361
|
+
const stale = producer.beginOperation({
|
|
362
|
+
turnId: "turn-1",
|
|
363
|
+
lifecycle: "action",
|
|
364
|
+
at: 100,
|
|
365
|
+
});
|
|
366
|
+
expect(
|
|
367
|
+
producer.finishOperation(stale, {
|
|
368
|
+
at: 120,
|
|
369
|
+
isError: true,
|
|
370
|
+
cancelled: true,
|
|
371
|
+
}),
|
|
372
|
+
).toBe(true);
|
|
373
|
+
expect(state.retired[0]?.disposition).toEqual({
|
|
374
|
+
status: "interrupted",
|
|
375
|
+
endReason: "browser_operation_cancelled",
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
state.clearOwner();
|
|
379
|
+
const current = producer.beginOperation({
|
|
380
|
+
turnId: "turn-2",
|
|
381
|
+
lifecycle: "action",
|
|
382
|
+
at: 200,
|
|
383
|
+
});
|
|
384
|
+
expect(current?.handle?.activation).toBe(2);
|
|
385
|
+
expect(
|
|
386
|
+
producer.finishOperation(stale, {
|
|
387
|
+
at: 210,
|
|
388
|
+
isError: true,
|
|
389
|
+
cancelled: true,
|
|
390
|
+
}),
|
|
391
|
+
).toBe(false);
|
|
392
|
+
expect(
|
|
393
|
+
producer.finishOperation(stale, {
|
|
394
|
+
at: 220,
|
|
395
|
+
isError: false,
|
|
396
|
+
cancelled: false,
|
|
397
|
+
}),
|
|
398
|
+
).toBe(true);
|
|
399
|
+
expect(state.activity).toEqual([{ turnId: "turn-1", at: 220 }]);
|
|
400
|
+
expect(state.retired).toHaveLength(1);
|
|
401
|
+
});
|
|
402
|
+
});
|