@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
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable client connection history.
|
|
3
|
+
*
|
|
4
|
+
* The event hub writes one row per client subscribe/dispose. Readers
|
|
5
|
+
* coalesce short reconnects into flaps so a watchdog cycle is not an
|
|
6
|
+
* outage. Writes are best-effort: a missing or unready database never
|
|
7
|
+
* throws back into the hub.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { and, asc, eq, lt, lte, type SQL } from "drizzle-orm";
|
|
11
|
+
import { v4 as uuid } from "uuid";
|
|
12
|
+
|
|
13
|
+
import type { InterfaceId } from "../channels/types.js";
|
|
14
|
+
import { getLogger } from "../util/logger.js";
|
|
15
|
+
import type { DrizzleDb } from "./db-connection.js";
|
|
16
|
+
import * as dbConnection from "./db-connection.js";
|
|
17
|
+
import { clientConnectionEvents } from "./schema/index.js";
|
|
18
|
+
|
|
19
|
+
const log = getLogger("client-connection-events-store");
|
|
20
|
+
|
|
21
|
+
/** How long a reconnect gap can be and still count as a flap, not an outage. */
|
|
22
|
+
export const CLIENT_CONNECTION_FLAP_WINDOW_MS = 60_000;
|
|
23
|
+
|
|
24
|
+
/** Rows older than this are deleted on each successful write. */
|
|
25
|
+
export const CLIENT_CONNECTION_EVENT_RETENTION_MS = 90 * 24 * 60 * 60 * 1000;
|
|
26
|
+
|
|
27
|
+
export const CLIENT_CONNECTION_EVENT_REASONS = [
|
|
28
|
+
"sse_open",
|
|
29
|
+
"sse_close",
|
|
30
|
+
"stale_replaced",
|
|
31
|
+
"shed_backpressure",
|
|
32
|
+
"force_disconnect",
|
|
33
|
+
"cap_evicted",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
export type ClientConnectionEventReason =
|
|
37
|
+
(typeof CLIENT_CONNECTION_EVENT_REASONS)[number];
|
|
38
|
+
|
|
39
|
+
/** Session-only close when a later open arrives with no recorded close. */
|
|
40
|
+
export type ClientConnectionSessionCloseReason =
|
|
41
|
+
| ClientConnectionEventReason
|
|
42
|
+
| "implicit_replaced";
|
|
43
|
+
|
|
44
|
+
const CLOSE_REASONS = new Set<ClientConnectionEventReason>([
|
|
45
|
+
"sse_close",
|
|
46
|
+
"stale_replaced",
|
|
47
|
+
"shed_backpressure",
|
|
48
|
+
"force_disconnect",
|
|
49
|
+
"cap_evicted",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
export interface ClientConnectionEventInput {
|
|
53
|
+
clientId: string;
|
|
54
|
+
interfaceId: InterfaceId | string;
|
|
55
|
+
connectionId: string;
|
|
56
|
+
reason: ClientConnectionEventReason;
|
|
57
|
+
occurredAt?: number;
|
|
58
|
+
actorPrincipalId?: string;
|
|
59
|
+
clientVersion?: string;
|
|
60
|
+
sseWatchdog?: boolean;
|
|
61
|
+
machineName?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ClientConnectionEvent {
|
|
65
|
+
id: string;
|
|
66
|
+
clientId: string;
|
|
67
|
+
interfaceId: string;
|
|
68
|
+
connectionId: string;
|
|
69
|
+
reason: ClientConnectionEventReason;
|
|
70
|
+
occurredAt: number;
|
|
71
|
+
actorPrincipalId: string | null;
|
|
72
|
+
clientVersion: string | null;
|
|
73
|
+
sseWatchdog: boolean | null;
|
|
74
|
+
machineName: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ClientConnectionSession {
|
|
78
|
+
clientId: string;
|
|
79
|
+
interfaceId: string;
|
|
80
|
+
startedAt: number;
|
|
81
|
+
endedAt: number | null;
|
|
82
|
+
durationMs: number | null;
|
|
83
|
+
flapCount: number;
|
|
84
|
+
openReason: ClientConnectionEventReason;
|
|
85
|
+
closeReason: ClientConnectionSessionCloseReason | null;
|
|
86
|
+
clientVersion: string | null;
|
|
87
|
+
sseWatchdog: boolean | null;
|
|
88
|
+
machineName: string | null;
|
|
89
|
+
connectionIds: string[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ListClientConnectionHistoryParams {
|
|
93
|
+
clientId?: string;
|
|
94
|
+
interfaceId?: string;
|
|
95
|
+
actorPrincipalId?: string;
|
|
96
|
+
since?: number;
|
|
97
|
+
until?: number;
|
|
98
|
+
/** Max sessions to return after coalescing. Clamped to 1-500. */
|
|
99
|
+
limit?: number;
|
|
100
|
+
now?: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ClientConnectionHistory {
|
|
104
|
+
events: ClientConnectionEvent[];
|
|
105
|
+
sessions: ClientConnectionSession[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const DEFAULT_SESSION_LIMIT = 50;
|
|
109
|
+
const MAX_SESSION_LIMIT = 500;
|
|
110
|
+
|
|
111
|
+
function resolveDb(database?: DrizzleDb): DrizzleDb | null {
|
|
112
|
+
if (database) {
|
|
113
|
+
return database;
|
|
114
|
+
}
|
|
115
|
+
// Namespace import so a test mock of db-connection that omits `isDbOpen`
|
|
116
|
+
// still loads. Incomplete mocks skip the probe and try `getDb()`.
|
|
117
|
+
if (
|
|
118
|
+
typeof dbConnection.isDbOpen === "function" &&
|
|
119
|
+
!dbConnection.isDbOpen()
|
|
120
|
+
) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
return dbConnection.getDb();
|
|
125
|
+
} catch (err) {
|
|
126
|
+
log.debug({ err }, "client connection history skipped: database unready");
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function rowToEvent(
|
|
132
|
+
row: typeof clientConnectionEvents.$inferSelect,
|
|
133
|
+
): ClientConnectionEvent {
|
|
134
|
+
return {
|
|
135
|
+
id: row.id,
|
|
136
|
+
clientId: row.clientId,
|
|
137
|
+
interfaceId: row.interfaceId,
|
|
138
|
+
connectionId: row.connectionId,
|
|
139
|
+
reason: row.reason as ClientConnectionEventReason,
|
|
140
|
+
occurredAt: row.occurredAt,
|
|
141
|
+
actorPrincipalId: row.actorPrincipalId ?? null,
|
|
142
|
+
clientVersion: row.clientVersion ?? null,
|
|
143
|
+
sseWatchdog: row.sseWatchdog ?? null,
|
|
144
|
+
machineName: row.machineName ?? null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function pruneExpired(db: DrizzleDb, now: number): void {
|
|
149
|
+
const cutoff = now - CLIENT_CONNECTION_EVENT_RETENTION_MS;
|
|
150
|
+
db.delete(clientConnectionEvents)
|
|
151
|
+
.where(lt(clientConnectionEvents.occurredAt, cutoff))
|
|
152
|
+
.run();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Append one hub lifecycle event. Returns null when the main database is
|
|
157
|
+
* not open or the write fails. Never throws.
|
|
158
|
+
*/
|
|
159
|
+
export function recordClientConnectionEvent(
|
|
160
|
+
input: ClientConnectionEventInput,
|
|
161
|
+
database?: DrizzleDb,
|
|
162
|
+
): ClientConnectionEvent | null {
|
|
163
|
+
const db = resolveDb(database);
|
|
164
|
+
if (!db) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const now = input.occurredAt ?? Date.now();
|
|
168
|
+
const row = {
|
|
169
|
+
id: uuid(),
|
|
170
|
+
clientId: input.clientId,
|
|
171
|
+
interfaceId: input.interfaceId,
|
|
172
|
+
connectionId: input.connectionId,
|
|
173
|
+
reason: input.reason,
|
|
174
|
+
occurredAt: now,
|
|
175
|
+
actorPrincipalId: input.actorPrincipalId ?? null,
|
|
176
|
+
clientVersion: input.clientVersion ?? null,
|
|
177
|
+
sseWatchdog: input.sseWatchdog ?? null,
|
|
178
|
+
machineName: input.machineName ?? null,
|
|
179
|
+
};
|
|
180
|
+
try {
|
|
181
|
+
db.insert(clientConnectionEvents).values(row).run();
|
|
182
|
+
pruneExpired(db, now);
|
|
183
|
+
return rowToEvent(row);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
log.debug({ err }, "failed to record client connection event");
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function clampLimit(limit: number | undefined): number {
|
|
191
|
+
if (limit == null || !Number.isFinite(limit)) {
|
|
192
|
+
return DEFAULT_SESSION_LIMIT;
|
|
193
|
+
}
|
|
194
|
+
return Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(limit)));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function startSession(event: ClientConnectionEvent): ClientConnectionSession {
|
|
198
|
+
return {
|
|
199
|
+
clientId: event.clientId,
|
|
200
|
+
interfaceId: event.interfaceId,
|
|
201
|
+
startedAt: event.occurredAt,
|
|
202
|
+
endedAt: event.reason === "sse_open" ? null : event.occurredAt,
|
|
203
|
+
durationMs: event.reason === "sse_open" ? null : 0,
|
|
204
|
+
flapCount: 0,
|
|
205
|
+
openReason: event.reason,
|
|
206
|
+
closeReason: event.reason === "sse_open" ? null : event.reason,
|
|
207
|
+
clientVersion: event.clientVersion,
|
|
208
|
+
sseWatchdog: event.sseWatchdog,
|
|
209
|
+
machineName: event.machineName,
|
|
210
|
+
connectionIds: [event.connectionId],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Coalesce raw events into sessions. A close followed by an open within
|
|
216
|
+
* {@link CLIENT_CONNECTION_FLAP_WINDOW_MS} continues the same session and
|
|
217
|
+
* increments `flapCount`.
|
|
218
|
+
*/
|
|
219
|
+
export function coalesceClientConnectionSessions(
|
|
220
|
+
events: ClientConnectionEvent[],
|
|
221
|
+
now: number = Date.now(),
|
|
222
|
+
): ClientConnectionSession[] {
|
|
223
|
+
const byClient = new Map<string, ClientConnectionEvent[]>();
|
|
224
|
+
for (const event of events) {
|
|
225
|
+
const list = byClient.get(event.clientId) ?? [];
|
|
226
|
+
list.push(event);
|
|
227
|
+
byClient.set(event.clientId, list);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const sessions: ClientConnectionSession[] = [];
|
|
231
|
+
for (const clientEvents of byClient.values()) {
|
|
232
|
+
clientEvents.sort((a, b) => a.occurredAt - b.occurredAt);
|
|
233
|
+
let current: ClientConnectionSession | null = null;
|
|
234
|
+
|
|
235
|
+
const finalize = () => {
|
|
236
|
+
if (!current) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (current.endedAt == null) {
|
|
240
|
+
current.durationMs = Math.max(0, now - current.startedAt);
|
|
241
|
+
} else {
|
|
242
|
+
current.durationMs = Math.max(0, current.endedAt - current.startedAt);
|
|
243
|
+
}
|
|
244
|
+
sessions.push(current);
|
|
245
|
+
current = null;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
for (const event of clientEvents) {
|
|
249
|
+
if (event.reason === "sse_open") {
|
|
250
|
+
if (
|
|
251
|
+
current &&
|
|
252
|
+
current.endedAt != null &&
|
|
253
|
+
event.occurredAt - current.endedAt < CLIENT_CONNECTION_FLAP_WINDOW_MS
|
|
254
|
+
) {
|
|
255
|
+
current.endedAt = null;
|
|
256
|
+
current.closeReason = null;
|
|
257
|
+
current.flapCount += 1;
|
|
258
|
+
current.connectionIds.push(event.connectionId);
|
|
259
|
+
current.clientVersion = event.clientVersion ?? current.clientVersion;
|
|
260
|
+
current.sseWatchdog = event.sseWatchdog ?? current.sseWatchdog;
|
|
261
|
+
current.machineName = event.machineName ?? current.machineName;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (current && current.endedAt == null) {
|
|
265
|
+
current.endedAt = event.occurredAt;
|
|
266
|
+
current.closeReason = "implicit_replaced";
|
|
267
|
+
}
|
|
268
|
+
finalize();
|
|
269
|
+
current = startSession(event);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!CLOSE_REASONS.has(event.reason)) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!current) {
|
|
278
|
+
current = startSession(event);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
current.endedAt = event.occurredAt;
|
|
283
|
+
current.closeReason = event.reason;
|
|
284
|
+
if (!current.connectionIds.includes(event.connectionId)) {
|
|
285
|
+
current.connectionIds.push(event.connectionId);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
finalize();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
sessions.sort((a, b) => b.startedAt - a.startedAt);
|
|
292
|
+
return sessions;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Read raw events and coalesced sessions. Returns empty lists when the
|
|
297
|
+
* database is not open.
|
|
298
|
+
*/
|
|
299
|
+
export function listClientConnectionHistory(
|
|
300
|
+
params: ListClientConnectionHistoryParams = {},
|
|
301
|
+
database?: DrizzleDb,
|
|
302
|
+
): ClientConnectionHistory {
|
|
303
|
+
const db = resolveDb(database);
|
|
304
|
+
if (!db) {
|
|
305
|
+
return { events: [], sessions: [] };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const now = params.now ?? Date.now();
|
|
309
|
+
const limit = clampLimit(params.limit);
|
|
310
|
+
const filters: SQL[] = [];
|
|
311
|
+
if (params.clientId) {
|
|
312
|
+
filters.push(eq(clientConnectionEvents.clientId, params.clientId));
|
|
313
|
+
}
|
|
314
|
+
if (params.interfaceId) {
|
|
315
|
+
filters.push(eq(clientConnectionEvents.interfaceId, params.interfaceId));
|
|
316
|
+
}
|
|
317
|
+
if (params.actorPrincipalId) {
|
|
318
|
+
filters.push(
|
|
319
|
+
eq(clientConnectionEvents.actorPrincipalId, params.actorPrincipalId),
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
if (params.until != null) {
|
|
323
|
+
filters.push(lte(clientConnectionEvents.occurredAt, params.until));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const query = db
|
|
328
|
+
.select()
|
|
329
|
+
.from(clientConnectionEvents)
|
|
330
|
+
.orderBy(asc(clientConnectionEvents.occurredAt));
|
|
331
|
+
const rows =
|
|
332
|
+
filters.length > 0 ? query.where(and(...filters)).all() : query.all();
|
|
333
|
+
const events = rows.map(rowToEvent);
|
|
334
|
+
const sessions = coalesceClientConnectionSessions(events, now)
|
|
335
|
+
.filter((session) => {
|
|
336
|
+
return sessionOverlapsWindow(session, params.since, params.until);
|
|
337
|
+
})
|
|
338
|
+
.slice(0, limit);
|
|
339
|
+
return {
|
|
340
|
+
events: events.filter((event) => {
|
|
341
|
+
return eventInWindow(event, params.since, params.until);
|
|
342
|
+
}),
|
|
343
|
+
sessions,
|
|
344
|
+
};
|
|
345
|
+
} catch (err) {
|
|
346
|
+
log.debug({ err }, "failed to list client connection history");
|
|
347
|
+
return { events: [], sessions: [] };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function sessionOverlapsWindow(
|
|
352
|
+
session: ClientConnectionSession,
|
|
353
|
+
since?: number,
|
|
354
|
+
until?: number,
|
|
355
|
+
): boolean {
|
|
356
|
+
if (since != null && session.endedAt != null && session.endedAt < since) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
if (until != null && session.startedAt > until) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function eventInWindow(
|
|
366
|
+
event: ClientConnectionEvent,
|
|
367
|
+
since?: number,
|
|
368
|
+
until?: number,
|
|
369
|
+
): boolean {
|
|
370
|
+
if (since != null && event.occurredAt < since) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
if (until != null && event.occurredAt > until) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, rmSync } from "node:fs";
|
|
2
2
|
|
|
3
|
+
import { TrustClassSchema } from "@vellumai/gateway-client";
|
|
3
4
|
import {
|
|
4
5
|
and,
|
|
5
6
|
asc,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
forgetActivationConversation,
|
|
27
28
|
forgetAllActivationConversations,
|
|
28
29
|
} from "../activation/progress-store.js";
|
|
30
|
+
import { TolerantModeSessionSchema } from "../api/mode-session.js";
|
|
29
31
|
import type { ChannelId, InterfaceId } from "../channels/types.js";
|
|
30
32
|
import { parseChannelId, parseInterfaceId } from "../channels/types.js";
|
|
31
33
|
import { CHANNEL_IDS, isChannelId } from "../channels/types.js";
|
|
@@ -47,7 +49,6 @@ import { runHook } from "../plugins/pipeline.js";
|
|
|
47
49
|
import type { ContentBlock } from "../providers/types.js";
|
|
48
50
|
import { getCurrentSeq } from "../runtime/assistant-stream-state.js";
|
|
49
51
|
import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
|
|
50
|
-
import { trustClassSchema } from "../runtime/trust-class.js";
|
|
51
52
|
import { UserError } from "../util/errors.js";
|
|
52
53
|
import { getLogger } from "../util/logger.js";
|
|
53
54
|
import { getLogsDbPath } from "../util/logs-db-path.js";
|
|
@@ -91,6 +92,7 @@ import {
|
|
|
91
92
|
REFERENTIAL_FORK_STRATEGY,
|
|
92
93
|
resolveConversationLineage,
|
|
93
94
|
} from "./conversation-lineage.js";
|
|
95
|
+
import { repairConversationModeSessionBoundaries } from "./conversation-mode-sessions.js";
|
|
94
96
|
import { deleteConversationRowsInBatches } from "./conversation-row-batch-delete.js";
|
|
95
97
|
import {
|
|
96
98
|
BACKGROUND_CONVERSATION_TYPES,
|
|
@@ -333,6 +335,8 @@ const backgroundToolCompletionMetadataSchema = z.object({
|
|
|
333
335
|
|
|
334
336
|
export const messageMetadataSchema = z
|
|
335
337
|
.object({
|
|
338
|
+
/** Immutable ownership of this transcript row by a recorded mode session. */
|
|
339
|
+
modeSession: TolerantModeSessionSchema,
|
|
336
340
|
/**
|
|
337
341
|
* Epoch ms the content actually happened, when that differs from when
|
|
338
342
|
* the row was written. Set wherever persistence lags the event: a queued
|
|
@@ -383,7 +387,7 @@ export const messageMetadataSchema = z
|
|
|
383
387
|
* trust status changes later. Used by the memory write gate (indexer)
|
|
384
388
|
* and read gate (conversation history loading) to enforce trust-aware access.
|
|
385
389
|
*/
|
|
386
|
-
provenanceTrustClass:
|
|
390
|
+
provenanceTrustClass: TrustClassSchema.optional(),
|
|
387
391
|
/**
|
|
388
392
|
* Model that actually served this assistant row, carried on the agent
|
|
389
393
|
* loop's `message_complete` event (the provider's `response.model`, the
|
|
@@ -398,6 +402,22 @@ export const messageMetadataSchema = z
|
|
|
398
402
|
provenanceSourceChannel: channelIdSchema.optional(),
|
|
399
403
|
provenanceGuardianExternalUserId: z.string().optional(),
|
|
400
404
|
provenanceRequesterIdentifier: z.string().optional(),
|
|
405
|
+
/**
|
|
406
|
+
* Contact id of the person who wrote this row, from the gateway trust
|
|
407
|
+
* verdict at persist time. Stamped only on a person's own message or
|
|
408
|
+
* reaction (`actorAuthorProvenance`), never on rows the assistant writes
|
|
409
|
+
* during their turn: the other `provenance*` fields describe the turn,
|
|
410
|
+
* this one the author. Absent when the author resolved to no contact.
|
|
411
|
+
*/
|
|
412
|
+
provenanceContactId: z.string().optional(),
|
|
413
|
+
/**
|
|
414
|
+
* Set on a backfilled row whose sender was looked up but no usable gateway
|
|
415
|
+
* verdict came back, so its trust class is the guardian-address fallback
|
|
416
|
+
* and it names no author. Distinguishes that row from a sender the gateway
|
|
417
|
+
* resolved as a stranger, so the lookup can be re-run later. Live ingress
|
|
418
|
+
* never persists such a row: it denies a turn whose verdict failed.
|
|
419
|
+
*/
|
|
420
|
+
provenanceLookupFailed: z.boolean().optional(),
|
|
401
421
|
automated: z.boolean().optional(),
|
|
402
422
|
/**
|
|
403
423
|
* Transcript-suppression flag: the row is a machine signal (e.g. the
|
|
@@ -469,10 +489,10 @@ export const messageMetadataSchema = z
|
|
|
469
489
|
*/
|
|
470
490
|
attachmentStoredPaths: z.record(z.string(), z.string()).optional(),
|
|
471
491
|
/**
|
|
472
|
-
* Marks a role-`"user"` row whose arrival interrupted a turn
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
492
|
+
* Marks a role-`"user"` row whose arrival interrupted a running turn.
|
|
493
|
+
* `loadFromDb` rebuilds the LLM-facing `<interrupted_turn>` note from it;
|
|
494
|
+
* the row's own content is exactly what the user sent, so clients render
|
|
495
|
+
* nothing extra.
|
|
476
496
|
*/
|
|
477
497
|
interruptedPriorTurn: z.boolean().optional(),
|
|
478
498
|
memoryInjectedBlock: z.string().optional(),
|
|
@@ -577,6 +597,12 @@ export function isProviderErrorMetadata(
|
|
|
577
597
|
* assistant rows, and turn grouping closes on them, so display merging and
|
|
578
598
|
* the turn resolver agree on boundaries. Takes the raw persisted `metadata`
|
|
579
599
|
* JSON string; malformed JSON and non-assistant roles are never standalone.
|
|
600
|
+
*
|
|
601
|
+
* The web folds adjacent assistant rows again after pagination and reads the
|
|
602
|
+
* same rule off the wire projection in its own `isStandaloneAssistantMessage`
|
|
603
|
+
* (clients/web/src/domains/chat/utils/is-standalone-assistant-message.ts). A
|
|
604
|
+
* kind added here without a matching flag and check there merges on the
|
|
605
|
+
* client anyway.
|
|
580
606
|
*/
|
|
581
607
|
export function isStandaloneAssistantMessage(
|
|
582
608
|
role: string,
|
|
@@ -4337,6 +4363,24 @@ export function purgeConversationSegments(
|
|
|
4337
4363
|
return segmentIds;
|
|
4338
4364
|
}
|
|
4339
4365
|
|
|
4366
|
+
/** Repair derived mode-session boundaries without changing delete success. */
|
|
4367
|
+
function repairModeSessionBoundariesAfterDelete(conversationId: string): void {
|
|
4368
|
+
const maxAttempts = 2;
|
|
4369
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
4370
|
+
try {
|
|
4371
|
+
repairConversationModeSessionBoundaries(conversationId);
|
|
4372
|
+
return;
|
|
4373
|
+
} catch (err) {
|
|
4374
|
+
if (attempt === maxAttempts) {
|
|
4375
|
+
log.warn(
|
|
4376
|
+
{ err, conversationId, attempts: maxAttempts },
|
|
4377
|
+
"Failed to repair mode session boundaries after message deletion; continuing",
|
|
4378
|
+
);
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
|
|
4340
4384
|
export function deleteLastExchange(conversationId: string): number {
|
|
4341
4385
|
const db = getDb();
|
|
4342
4386
|
|
|
@@ -4441,6 +4485,10 @@ export function deleteLastExchange(conversationId: string): number {
|
|
|
4441
4485
|
} satisfies MessageDeletedInputContext);
|
|
4442
4486
|
}
|
|
4443
4487
|
|
|
4488
|
+
if (deleted > 0) {
|
|
4489
|
+
repairModeSessionBoundariesAfterDelete(conversationId);
|
|
4490
|
+
}
|
|
4491
|
+
|
|
4444
4492
|
return deleted;
|
|
4445
4493
|
}
|
|
4446
4494
|
|
|
@@ -4751,6 +4799,7 @@ export function deleteMessageById(
|
|
|
4751
4799
|
messageId,
|
|
4752
4800
|
createdAt: msgRow.createdAt,
|
|
4753
4801
|
} satisfies MessageDeletedInputContext);
|
|
4802
|
+
repairModeSessionBoundariesAfterDelete(msgRow.conversationId);
|
|
4754
4803
|
}
|
|
4755
4804
|
|
|
4756
4805
|
return result;
|