@vellumai/assistant 0.11.2 → 0.11.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 +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
enforceSameActorOrErrorResult,
|
|
43
43
|
pickSameUserAutoResolve,
|
|
44
44
|
} from "../runtime/auth/same-actor.js";
|
|
45
|
-
import
|
|
45
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
46
46
|
import type {
|
|
47
47
|
InteractiveUiRequest,
|
|
48
48
|
InteractiveUiResult,
|
|
@@ -57,10 +57,9 @@ import { resolveAppId } from "../tools/apps/resolve-app-id.js";
|
|
|
57
57
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
58
58
|
import { getLogger } from "../util/logger.js";
|
|
59
59
|
import { isPlainObject } from "../util/object.js";
|
|
60
|
+
import type { Conversation } from "./conversation.js";
|
|
60
61
|
import { buildConversationErrorMessage } from "./conversation-error.js";
|
|
61
62
|
import { launchConversation } from "./conversation-launch.js";
|
|
62
|
-
import type { EnqueueMessageOptions } from "./conversation-messaging.js";
|
|
63
|
-
import type { ProcessMessageOptions } from "./conversation-process.js";
|
|
64
63
|
import {
|
|
65
64
|
buildSurfaceShowPair,
|
|
66
65
|
type CurrentTurnSurface,
|
|
@@ -68,7 +67,6 @@ import {
|
|
|
68
67
|
type SurfaceShowPair,
|
|
69
68
|
type SurfaceStateEntry,
|
|
70
69
|
} from "./conversation-surface-state.js";
|
|
71
|
-
import type { HostAppControlProxy } from "./host-app-control-proxy.js";
|
|
72
70
|
import type { HostCuProxy } from "./host-cu-proxy.js";
|
|
73
71
|
import type {
|
|
74
72
|
AnySurfaceData,
|
|
@@ -86,6 +84,7 @@ import type {
|
|
|
86
84
|
UiSurfaceShow,
|
|
87
85
|
} from "./message-protocol.js";
|
|
88
86
|
import { INTERACTIVE_SURFACE_TYPES } from "./message-protocol.js";
|
|
87
|
+
import { isRowVisibleToUntrustedActor } from "./message-provenance.js";
|
|
89
88
|
export {
|
|
90
89
|
buildSurfaceShowPair,
|
|
91
90
|
type CurrentTurnSurface,
|
|
@@ -97,7 +96,6 @@ export {
|
|
|
97
96
|
} from "./conversation-surface-state.js";
|
|
98
97
|
import type { HostAppControlInput } from "./message-types/host-app-control.js";
|
|
99
98
|
import type { UserMessageAttachment } from "./message-types/shared.js";
|
|
100
|
-
import type { TrustContext } from "./trust-context-types.js";
|
|
101
99
|
|
|
102
100
|
/**
|
|
103
101
|
* Prefix of the synthetic user-message text this module writes when a surface
|
|
@@ -175,11 +173,70 @@ const pendingSurfacePersists = new Map<
|
|
|
175
173
|
}
|
|
176
174
|
>();
|
|
177
175
|
|
|
176
|
+
/** A located `ui_surface` block plus everything needed to write it back. */
|
|
177
|
+
type PersistedSurfaceHit = {
|
|
178
|
+
rowId: string;
|
|
179
|
+
blocks: unknown[];
|
|
180
|
+
block: Record<string, unknown>;
|
|
181
|
+
blockIndex: number;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Locate the persisted `ui_surface` content block for `surfaceId`.
|
|
186
|
+
*
|
|
187
|
+
* The single owner of how a persisted surface block is resolved *in this
|
|
188
|
+
* module*: newest message row first, first matching block within that row,
|
|
189
|
+
* stopping at the first hit. Every persisted read and write here goes through
|
|
190
|
+
* it so a change to storage resolution, ordering, or matching can never make
|
|
191
|
+
* them target different blocks. `findPersistedSurfaceState`
|
|
192
|
+
* (`runtime/routes/surface-conversation-resolver.ts`) is the other persisted
|
|
193
|
+
* `ui_surface` reader and deliberately does not: it runs its own SQL scan,
|
|
194
|
+
* bounded by the compaction boundary, for the read-only content route.
|
|
195
|
+
*
|
|
196
|
+
* `filterByProvenance` applies {@link isRowVisibleToUntrustedActor} per row,
|
|
197
|
+
* the identical predicate `loadFromDb` applies when building `messages`. It
|
|
198
|
+
* covers the persisted type/data read ({@link findPersistedSurfaceInfo})
|
|
199
|
+
* only. Writes scan unfiltered, so a completion write can mutate a row the
|
|
200
|
+
* requester cannot see and push requester-controlled summary text into it.
|
|
201
|
+
* The live-state path (`ctx.surfaceState` / `ctx.pendingSurfaceActions`) is
|
|
202
|
+
* unfiltered too: it is scoped to the trust the conversation was loaded
|
|
203
|
+
* under, not the requester's.
|
|
204
|
+
*
|
|
205
|
+
* The scan is deliberately unbounded by compaction (see
|
|
206
|
+
* {@link findPersistedSurfaceInfo}), and it throws rather than swallowing DB
|
|
207
|
+
* errors so each caller keeps its own logging.
|
|
208
|
+
*/
|
|
209
|
+
function findPersistedSurfaceBlock(
|
|
210
|
+
conversationId: string,
|
|
211
|
+
surfaceId: string,
|
|
212
|
+
opts: { filterByProvenance: boolean },
|
|
213
|
+
): PersistedSurfaceHit | undefined {
|
|
214
|
+
const { filterByProvenance } = opts;
|
|
215
|
+
const rows = getMessages(conversationId);
|
|
216
|
+
for (let r = rows.length - 1; r >= 0; r--) {
|
|
217
|
+
if (filterByProvenance && !isRowVisibleToUntrustedActor(rows[r].metadata)) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const blocks: unknown[] = rows[r].content;
|
|
221
|
+
const blockIndex = blocks.findIndex((pb) => {
|
|
222
|
+
const rb = pb as Record<string, unknown>;
|
|
223
|
+
return rb.type === "ui_surface" && rb.surfaceId === surfaceId;
|
|
224
|
+
});
|
|
225
|
+
if (blockIndex !== -1) {
|
|
226
|
+
return {
|
|
227
|
+
rowId: rows[r].id,
|
|
228
|
+
blocks,
|
|
229
|
+
block: blocks[blockIndex] as Record<string, unknown>,
|
|
230
|
+
blockIndex,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
178
237
|
/**
|
|
179
|
-
* Persist the latest `data` for a `ui_surface` content block by
|
|
180
|
-
*
|
|
181
|
-
* `surfaceId` and patching its `data` field. Mirrors the scan-and-patch
|
|
182
|
-
* pattern in `markSurfaceCompleted`.
|
|
238
|
+
* Persist the latest `data` for a `ui_surface` content block by locating it
|
|
239
|
+
* with {@link findPersistedSurfaceBlock} and patching its `data` field.
|
|
183
240
|
*
|
|
184
241
|
* Safe to call before the assistant message has been persisted (mid-stream):
|
|
185
242
|
* the scan simply finds nothing and bails. The next update after
|
|
@@ -191,23 +248,14 @@ function persistSurfaceData(
|
|
|
191
248
|
data: SurfaceData,
|
|
192
249
|
): void {
|
|
193
250
|
try {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const rb = pb as Record<string, unknown>;
|
|
200
|
-
if (rb.type === "ui_surface" && rb.surfaceId === surfaceId) {
|
|
201
|
-
rb.data = data;
|
|
202
|
-
found = true;
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (found) {
|
|
207
|
-
updateMessageContent(rows[r].id, JSON.stringify(parsed));
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
251
|
+
const hit = findPersistedSurfaceBlock(conversationId, surfaceId, {
|
|
252
|
+
filterByProvenance: false,
|
|
253
|
+
});
|
|
254
|
+
if (!hit) {
|
|
255
|
+
return;
|
|
210
256
|
}
|
|
257
|
+
hit.block.data = data;
|
|
258
|
+
updateMessageContent(hit.rowId, JSON.stringify(hit.blocks));
|
|
211
259
|
} catch (err) {
|
|
212
260
|
log.debug(
|
|
213
261
|
{ err, surfaceId, conversationId },
|
|
@@ -306,22 +354,72 @@ export function flushPendingSurfaceDataPersists(conversationId?: string): void {
|
|
|
306
354
|
|
|
307
355
|
/**
|
|
308
356
|
* Mark a `ui_surface` content block as completed in the database so that
|
|
309
|
-
* history reconstruction preserves the completion state.
|
|
357
|
+
* history reconstruction preserves the completion state. Also updates
|
|
310
358
|
* in-memory messages when available.
|
|
359
|
+
*
|
|
360
|
+
* Never throws: a persistence hiccup must not fail the surface action that
|
|
361
|
+
* triggered it. Returns whether the completion is safe to announce to
|
|
362
|
+
* clients, so a caller about to broadcast `ui_surface_complete` can withhold
|
|
363
|
+
* it. A write that threw is not safe: it leaves a persisted block that reverts
|
|
364
|
+
* to pending on the next history reseed. Finding no block to write IS safe: a
|
|
365
|
+
* standalone surface owns none, so nothing can revert.
|
|
366
|
+
*
|
|
367
|
+
* A surface still in `ctx.currentTurnSurfaces` owns no persisted block either,
|
|
368
|
+
* but one is coming: the turn-finalization appenders build it from that
|
|
369
|
+
* snapshot. The completion is stamped onto the snapshot so the appended block
|
|
370
|
+
* carries it, instead of landing as a fresh pending card the next reseed
|
|
371
|
+
* reactivates.
|
|
311
372
|
*/
|
|
312
373
|
export function markSurfaceCompleted(
|
|
313
|
-
ctx: {
|
|
374
|
+
ctx: {
|
|
375
|
+
conversationId: string;
|
|
376
|
+
messages?: Array<{ content: unknown }>;
|
|
377
|
+
currentTurnSurfaces?: CurrentTurnSurface[];
|
|
378
|
+
},
|
|
314
379
|
surfaceId: string,
|
|
315
380
|
summary: string,
|
|
316
|
-
):
|
|
381
|
+
): boolean {
|
|
317
382
|
// Force-flush any pending debounced data persist so the completion
|
|
318
383
|
// patch lands on top of the latest data instead of racing with it.
|
|
319
384
|
flushSurfaceDataPersist(surfaceId);
|
|
320
385
|
|
|
386
|
+
try {
|
|
387
|
+
const hit = findPersistedSurfaceBlock(ctx.conversationId, surfaceId, {
|
|
388
|
+
filterByProvenance: false,
|
|
389
|
+
});
|
|
390
|
+
if (hit) {
|
|
391
|
+
hit.block.completed = true;
|
|
392
|
+
hit.block.completionSummary = summary;
|
|
393
|
+
updateMessageContent(hit.rowId, JSON.stringify(hit.blocks));
|
|
394
|
+
} else {
|
|
395
|
+
// Mutated in place so a later `ui_update` that respreads the entry
|
|
396
|
+
// carries the completion forward.
|
|
397
|
+
const pendingSnapshot = ctx.currentTurnSurfaces?.find(
|
|
398
|
+
(s) => s.surfaceId === surfaceId,
|
|
399
|
+
);
|
|
400
|
+
if (pendingSnapshot) {
|
|
401
|
+
pendingSnapshot.completed = true;
|
|
402
|
+
pendingSnapshot.completionSummary = summary;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
} catch (err) {
|
|
406
|
+
// Error, not warn: a silent failure here presents as the user's answer being discarded.
|
|
407
|
+
log.error(
|
|
408
|
+
{ err, conversationId: ctx.conversationId, surfaceId },
|
|
409
|
+
"Failed to persist surface completion to DB",
|
|
410
|
+
);
|
|
411
|
+
// In-memory messages stay untouched so this process's history cannot
|
|
412
|
+
// claim an answered card the database still holds as pending.
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
|
|
321
416
|
// Update in-memory messages when available so subsequent reads within
|
|
322
|
-
// this session see the change without waiting for DB.
|
|
417
|
+
// this session see the change without waiting for DB. Newest match only,
|
|
418
|
+
// matching the persisted patch above: marking every copy of a duplicated
|
|
419
|
+
// surfaceId would make memory and history disagree, and a reseed would
|
|
420
|
+
// revert the extras.
|
|
323
421
|
if (ctx.messages) {
|
|
324
|
-
for (let i = ctx.messages.length - 1; i >= 0; i--) {
|
|
422
|
+
outer: for (let i = ctx.messages.length - 1; i >= 0; i--) {
|
|
325
423
|
const msg = ctx.messages[i];
|
|
326
424
|
if (!Array.isArray(msg.content)) {
|
|
327
425
|
continue;
|
|
@@ -331,48 +429,94 @@ export function markSurfaceCompleted(
|
|
|
331
429
|
if (b.type === "ui_surface" && b.surfaceId === surfaceId) {
|
|
332
430
|
b.completed = true;
|
|
333
431
|
b.completionSummary = summary;
|
|
334
|
-
break;
|
|
432
|
+
break outer;
|
|
335
433
|
}
|
|
336
434
|
}
|
|
337
435
|
}
|
|
338
436
|
}
|
|
339
437
|
|
|
340
|
-
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** What a persisted `ui_surface` block can still tell us once it is cold. */
|
|
442
|
+
type PersistedSurfaceInfo = {
|
|
443
|
+
/** The block's `surfaceType`, absent when it carries no string type. */
|
|
444
|
+
surfaceType: string | undefined;
|
|
445
|
+
/** The block's `data`, absent when it carries no plain-object data. */
|
|
446
|
+
data: Record<string, unknown> | undefined;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Read a `ui_surface` block's `surfaceType` and `data` out of persisted
|
|
451
|
+
* history. Both travel together because every caller that has lost the live
|
|
452
|
+
* entry needs both: the type to decide completion, the data for the labels a
|
|
453
|
+
* completion summary quotes.
|
|
454
|
+
*
|
|
455
|
+
* `requesterCanAccessMemory` is the REQUESTER's memory capability, not the
|
|
456
|
+
* trust class whatever conversation happens to be loaded under. The live path
|
|
457
|
+
* this falls back from hides guardian-provenance rows from an untrusted actor
|
|
458
|
+
* (`loadFromDb` → `restoreSurfaceStateFromHistory`), so the live entry is
|
|
459
|
+
* missing precisely when the row was filtered out. The scan therefore applies
|
|
460
|
+
* the same per-row predicate rather than handing back what the filter dropped.
|
|
461
|
+
*
|
|
462
|
+
* This must NOT be routed through `findPersistedSurfaceState`
|
|
463
|
+
* (`runtime/routes/surface-conversation-resolver.ts`): that helper is bounded
|
|
464
|
+
* by `liveHistoryStartRow` / `contextCompactedMessageCount` and by design will
|
|
465
|
+
* not see a surface behind the compaction boundary, which is exactly the case
|
|
466
|
+
* this lookup exists to serve. {@link findPersistedSurfaceBlock}, which also
|
|
467
|
+
* backs the write paths, is unbounded, so reads and writes stay consistent.
|
|
468
|
+
*
|
|
469
|
+
* Hits are deliberately not memoized into `conversation.surfaceState`; see
|
|
470
|
+
* `runtime/routes/surface-content-routes.ts` for why that shared map must not
|
|
471
|
+
* absorb scan results.
|
|
472
|
+
*
|
|
473
|
+
* Exported for its unit test only, and not a supported read API. In-repo callers
|
|
474
|
+
* go through `handleSurfaceAction`.
|
|
475
|
+
*/
|
|
476
|
+
export function findPersistedSurfaceInfo(
|
|
477
|
+
conversationId: string,
|
|
478
|
+
surfaceId: string,
|
|
479
|
+
opts: { requesterCanAccessMemory: boolean },
|
|
480
|
+
): PersistedSurfaceInfo | undefined {
|
|
341
481
|
try {
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const rb = pb as Record<string, unknown>;
|
|
348
|
-
if (rb.type === "ui_surface" && rb.surfaceId === surfaceId) {
|
|
349
|
-
rb.completed = true;
|
|
350
|
-
rb.completionSummary = summary;
|
|
351
|
-
found = true;
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
if (found) {
|
|
356
|
-
updateMessageContent(rows[r].id, JSON.stringify(parsed));
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
482
|
+
const hit = findPersistedSurfaceBlock(conversationId, surfaceId, {
|
|
483
|
+
filterByProvenance: !opts.requesterCanAccessMemory,
|
|
484
|
+
});
|
|
485
|
+
if (!hit) {
|
|
486
|
+
return undefined;
|
|
359
487
|
}
|
|
488
|
+
const data = hit.block.data;
|
|
489
|
+
return {
|
|
490
|
+
surfaceType:
|
|
491
|
+
typeof hit.block.surfaceType === "string"
|
|
492
|
+
? hit.block.surfaceType
|
|
493
|
+
: undefined,
|
|
494
|
+
data: isPlainObject(data) ? data : undefined,
|
|
495
|
+
};
|
|
360
496
|
} catch (err) {
|
|
361
|
-
log.warn(
|
|
497
|
+
log.warn(
|
|
498
|
+
{ err, conversationId, surfaceId },
|
|
499
|
+
"Failed to read persisted surface info from DB",
|
|
500
|
+
);
|
|
362
501
|
}
|
|
502
|
+
return undefined;
|
|
363
503
|
}
|
|
364
504
|
|
|
365
505
|
/**
|
|
366
506
|
* Complete a `ui_surface` card and notify live clients, addressed only by
|
|
367
507
|
* conversation + surface id.
|
|
368
508
|
*
|
|
369
|
-
* Unlike {@link
|
|
509
|
+
* Unlike {@link maybeCompleteSurfaceAfterAction}, this needs no live `Conversation`
|
|
370
510
|
* instance, so it can run from flows that don't own one — projecting a
|
|
371
511
|
* terminal guardian-request status onto its in-app approval card when the
|
|
372
512
|
* request was resolved on another surface (or by the expiry sweep). Persists
|
|
373
|
-
* the completion (reload-safe)
|
|
374
|
-
* connected client of this guardian converges.
|
|
375
|
-
*
|
|
513
|
+
* the completion (reload-safe) first, then broadcasts `ui_surface_complete` so
|
|
514
|
+
* every connected client of this guardian converges.
|
|
515
|
+
*
|
|
516
|
+
* The broadcast is deliberately NOT gated on the persist, unlike the
|
|
517
|
+
* user-action completion paths: the underlying request is already resolved, so
|
|
518
|
+
* withholding the announcement on a transient write failure strands a live,
|
|
519
|
+
* clickable approval card for a decision that has already been made.
|
|
376
520
|
*/
|
|
377
521
|
export function completeSurfaceAndNotify(
|
|
378
522
|
conversationId: string,
|
|
@@ -419,18 +563,12 @@ export function removeSurfaceBlock(
|
|
|
419
563
|
}
|
|
420
564
|
|
|
421
565
|
try {
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
});
|
|
429
|
-
if (idx !== -1) {
|
|
430
|
-
parsed.splice(idx, 1);
|
|
431
|
-
updateMessageContent(rows[r].id, JSON.stringify(parsed));
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
566
|
+
const hit = findPersistedSurfaceBlock(ctx.conversationId, surfaceId, {
|
|
567
|
+
filterByProvenance: false,
|
|
568
|
+
});
|
|
569
|
+
if (hit) {
|
|
570
|
+
hit.blocks.splice(hit.blockIndex, 1);
|
|
571
|
+
updateMessageContent(hit.rowId, JSON.stringify(hit.blocks));
|
|
434
572
|
}
|
|
435
573
|
} catch (err) {
|
|
436
574
|
log.warn({ err, surfaceId }, "Failed to remove dismissed surface from DB");
|
|
@@ -798,7 +936,7 @@ function isTaskProgressCardData(data: SurfaceData | Record<string, unknown>) {
|
|
|
798
936
|
}
|
|
799
937
|
|
|
800
938
|
function isSlackTaskProgressUiException(
|
|
801
|
-
ctx:
|
|
939
|
+
ctx: Conversation,
|
|
802
940
|
toolName: string,
|
|
803
941
|
input: Record<string, unknown>,
|
|
804
942
|
): boolean {
|
|
@@ -834,89 +972,6 @@ function isSlackTaskProgressUiException(
|
|
|
834
972
|
return false;
|
|
835
973
|
}
|
|
836
974
|
|
|
837
|
-
/**
|
|
838
|
-
* Subset of Conversation state that surface helpers need access to.
|
|
839
|
-
* The Conversation class implements this interface so its instances can be
|
|
840
|
-
* passed directly to the extracted functions.
|
|
841
|
-
*/
|
|
842
|
-
|
|
843
|
-
export interface SurfaceConversationContext {
|
|
844
|
-
readonly conversationId: string;
|
|
845
|
-
/** Assistant id (if known) — used when publishing launch-triggered events. */
|
|
846
|
-
readonly assistantId?: string;
|
|
847
|
-
/** Inherited to spawned conversations in the `launch_conversation` action path. */
|
|
848
|
-
readonly trustContext?: TrustContext;
|
|
849
|
-
/** Verified requester auth context for the active turn. */
|
|
850
|
-
readonly authContext?: AuthContext;
|
|
851
|
-
/** Per-turn auth snapshot, preferred for tool dispatch authorization. */
|
|
852
|
-
readonly currentTurnAuthContext?: AuthContext;
|
|
853
|
-
/** JWT-verified requester principal for the active turn. */
|
|
854
|
-
readonly currentTurnSourceActorPrincipalId?: string;
|
|
855
|
-
readonly channelCapabilities?: {
|
|
856
|
-
channel: string;
|
|
857
|
-
supportsDynamicUi: boolean;
|
|
858
|
-
};
|
|
859
|
-
sendToClient(msg: AssistantEvent): void;
|
|
860
|
-
pendingSurfaceActions: Map<string, { surfaceType: SurfaceType }>;
|
|
861
|
-
lastSurfaceAction: Map<
|
|
862
|
-
string,
|
|
863
|
-
{ actionId: string; data?: Record<string, unknown> }
|
|
864
|
-
>;
|
|
865
|
-
surfaceState: Map<string, SurfaceStateEntry>;
|
|
866
|
-
surfaceUndoStacks: Map<string, string[]>;
|
|
867
|
-
accumulatedSurfaceState: Map<string, Record<string, unknown>>;
|
|
868
|
-
/** Request IDs that originated from surface action button clicks (not regular user messages). */
|
|
869
|
-
surfaceActionRequestIds: Set<string>;
|
|
870
|
-
/**
|
|
871
|
-
* Pending standalone UI requests keyed by surfaceId.
|
|
872
|
-
* These are daemon-driven surfaces (not LLM tool invocations) that block
|
|
873
|
-
* the caller until the user submits, cancels, or the timeout elapses.
|
|
874
|
-
* Optional: only present on conversations that support standalone surfaces.
|
|
875
|
-
*/
|
|
876
|
-
pendingStandaloneSurfaces?: Map<
|
|
877
|
-
string,
|
|
878
|
-
{
|
|
879
|
-
resolve: (result: InteractiveUiResult) => void;
|
|
880
|
-
timer: ReturnType<typeof setTimeout>;
|
|
881
|
-
surfaceType: SurfaceType;
|
|
882
|
-
}
|
|
883
|
-
>;
|
|
884
|
-
/**
|
|
885
|
-
* Short-lived tombstone set of recently-completed standalone surface IDs.
|
|
886
|
-
* Prevents late client actions (arriving after timeout/resolution) from
|
|
887
|
-
* falling through to the history-restored path and triggering an
|
|
888
|
-
* unintended LLM turn. Entries are auto-removed after a TTL.
|
|
889
|
-
*/
|
|
890
|
-
recentlyCompletedStandaloneSurfaces?: Map<
|
|
891
|
-
string,
|
|
892
|
-
ReturnType<typeof setTimeout>
|
|
893
|
-
>;
|
|
894
|
-
currentTurnSurfaces: CurrentTurnSurface[];
|
|
895
|
-
/** Optional proxy for delegating computer-use actions to a connected desktop client. */
|
|
896
|
-
hostCuProxy?: HostCuProxy;
|
|
897
|
-
/** Optional proxy for delegating per-app app-control actions to a connected desktop client. */
|
|
898
|
-
hostAppControlProxy?: HostAppControlProxy;
|
|
899
|
-
/**
|
|
900
|
-
* Setter that lets the resolver detach the conversation's app-control proxy
|
|
901
|
-
* after `app_control_stop`. Disposes the existing proxy when transitioning
|
|
902
|
-
* to undefined so subsequent tool calls cleanly fail with "unavailable"
|
|
903
|
-
* rather than dispatching to a torn-down proxy.
|
|
904
|
-
*/
|
|
905
|
-
setHostAppControlProxy?(proxy: HostAppControlProxy | undefined): void;
|
|
906
|
-
/** True when no interactive client is connected (headless / channel-only). */
|
|
907
|
-
readonly hasNoClient?: boolean;
|
|
908
|
-
isProcessing(): boolean;
|
|
909
|
-
enqueueMessage(options: EnqueueMessageOptions): {
|
|
910
|
-
queued: boolean;
|
|
911
|
-
requestId: string;
|
|
912
|
-
rejected?: boolean;
|
|
913
|
-
};
|
|
914
|
-
getQueueDepth(): number;
|
|
915
|
-
processMessage(options: ProcessMessageOptions): Promise<string>;
|
|
916
|
-
/** Serialize operations on a given surface to prevent read-modify-write races. */
|
|
917
|
-
withSurface<T>(surfaceId: string, fn: () => T | Promise<T>): Promise<T>;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
975
|
export type SurfaceMutex = {
|
|
921
976
|
<T>(surfaceId: string, fn: () => T | Promise<T>): Promise<T>;
|
|
922
977
|
/** Number of surfaces with an active chain — exposed for tests. */
|
|
@@ -977,7 +1032,7 @@ const STANDALONE_TOMBSTONE_TTL_MS = 30_000; // 30 seconds
|
|
|
977
1032
|
* support dynamic UI.
|
|
978
1033
|
*/
|
|
979
1034
|
export function canShowInteractiveUi(
|
|
980
|
-
ctx: Pick<
|
|
1035
|
+
ctx: Pick<Conversation, "hasNoClient" | "channelCapabilities">,
|
|
981
1036
|
): boolean {
|
|
982
1037
|
if (ctx.hasNoClient) {
|
|
983
1038
|
return false;
|
|
@@ -998,7 +1053,7 @@ export function canShowInteractiveUi(
|
|
|
998
1053
|
* so that `handleSurfaceAction` can intercept the callback.
|
|
999
1054
|
*/
|
|
1000
1055
|
export function showStandaloneSurface(
|
|
1001
|
-
ctx:
|
|
1056
|
+
ctx: Conversation,
|
|
1002
1057
|
request: InteractiveUiRequest,
|
|
1003
1058
|
surfaceId: string,
|
|
1004
1059
|
): Promise<InteractiveUiResult> {
|
|
@@ -1168,7 +1223,7 @@ function buildStandaloneSurfaceData(
|
|
|
1168
1223
|
*/
|
|
1169
1224
|
export function cleanupStandaloneSurface(
|
|
1170
1225
|
ctx: Pick<
|
|
1171
|
-
|
|
1226
|
+
Conversation,
|
|
1172
1227
|
| "pendingStandaloneSurfaces"
|
|
1173
1228
|
| "recentlyCompletedStandaloneSurfaces"
|
|
1174
1229
|
| "surfaceState"
|
|
@@ -1233,7 +1288,7 @@ const OPEN_PANEL_ACK_TIMEOUT_MS = 10_000;
|
|
|
1233
1288
|
* removes it on every outcome.
|
|
1234
1289
|
*/
|
|
1235
1290
|
export function openChannelSetupPanel(
|
|
1236
|
-
ctx:
|
|
1291
|
+
ctx: Conversation,
|
|
1237
1292
|
surfaceId: string,
|
|
1238
1293
|
data: Record<string, unknown>,
|
|
1239
1294
|
options?: { signal?: AbortSignal; timeoutMs?: number },
|
|
@@ -1318,7 +1373,7 @@ export function openChannelSetupPanel(
|
|
|
1318
1373
|
* Auto-saves the document content to the app store.
|
|
1319
1374
|
*/
|
|
1320
1375
|
function handleDocumentContentChanged(
|
|
1321
|
-
ctx:
|
|
1376
|
+
ctx: Conversation,
|
|
1322
1377
|
surfaceId: string,
|
|
1323
1378
|
data?: Record<string, unknown>,
|
|
1324
1379
|
): void {
|
|
@@ -1394,7 +1449,7 @@ function handleDocumentContentChanged(
|
|
|
1394
1449
|
* Accumulates state via shallow merge without triggering an LLM turn.
|
|
1395
1450
|
*/
|
|
1396
1451
|
function handleStateUpdate(
|
|
1397
|
-
ctx:
|
|
1452
|
+
ctx: Conversation,
|
|
1398
1453
|
surfaceId: string,
|
|
1399
1454
|
data?: Record<string, unknown>,
|
|
1400
1455
|
): void {
|
|
@@ -1438,10 +1493,7 @@ function pushUndoState(
|
|
|
1438
1493
|
}
|
|
1439
1494
|
}
|
|
1440
1495
|
|
|
1441
|
-
export function handleSurfaceUndo(
|
|
1442
|
-
ctx: SurfaceConversationContext,
|
|
1443
|
-
surfaceId: string,
|
|
1444
|
-
): void {
|
|
1496
|
+
export function handleSurfaceUndo(ctx: Conversation, surfaceId: string): void {
|
|
1445
1497
|
const stack = ctx.surfaceUndoStacks.get(surfaceId);
|
|
1446
1498
|
if (!stack || stack.length === 0) {
|
|
1447
1499
|
ctx.sendToClient({
|
|
@@ -1666,7 +1718,7 @@ function getRequestedSurfaceCompletionSummary(
|
|
|
1666
1718
|
* recorded when the user commits the surface).
|
|
1667
1719
|
*/
|
|
1668
1720
|
function recordActivationMoment(
|
|
1669
|
-
ctx:
|
|
1721
|
+
ctx: Conversation,
|
|
1670
1722
|
moment: ActivationMomentParam,
|
|
1671
1723
|
): void {
|
|
1672
1724
|
try {
|
|
@@ -1700,10 +1752,7 @@ function recordActivationMoment(
|
|
|
1700
1752
|
* submitted / selected-and-committed), NOT from intermediate non-terminal
|
|
1701
1753
|
* events (`selection_changed` / `content_changed` / `state_update`).
|
|
1702
1754
|
*/
|
|
1703
|
-
function maybeEmitActivationMoment(
|
|
1704
|
-
ctx: SurfaceConversationContext,
|
|
1705
|
-
surfaceId: string,
|
|
1706
|
-
): void {
|
|
1755
|
+
function maybeEmitActivationMoment(ctx: Conversation, surfaceId: string): void {
|
|
1707
1756
|
const stored = ctx.surfaceState.get(surfaceId);
|
|
1708
1757
|
const moment = stored?.activationMoment;
|
|
1709
1758
|
if (!moment) {
|
|
@@ -1715,18 +1764,30 @@ function maybeEmitActivationMoment(
|
|
|
1715
1764
|
recordActivationMoment(ctx, moment);
|
|
1716
1765
|
}
|
|
1717
1766
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1767
|
+
/**
|
|
1768
|
+
* Record a surface action the message queue refused.
|
|
1769
|
+
*
|
|
1770
|
+
* The route answers `{ ok: true }` for a rejected enqueue and the client
|
|
1771
|
+
* optimistically completes the card on that, so the user sees an answered card
|
|
1772
|
+
* that the next history reseed reverts. Nothing else on this path logs, so
|
|
1773
|
+
* without this the only user-visible cause of a reverted card leaves no trace.
|
|
1774
|
+
*/
|
|
1775
|
+
function logSurfaceActionRejected(
|
|
1776
|
+
ctx: Conversation,
|
|
1720
1777
|
surfaceId: string,
|
|
1721
|
-
|
|
1778
|
+
actionId: string,
|
|
1779
|
+
surfaceType: string | undefined,
|
|
1722
1780
|
): void {
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1781
|
+
log.warn(
|
|
1782
|
+
{
|
|
1783
|
+
conversationId: ctx.conversationId,
|
|
1784
|
+
surfaceId,
|
|
1785
|
+
actionId,
|
|
1786
|
+
surfaceType,
|
|
1787
|
+
queueDepth: ctx.getQueueDepth(),
|
|
1788
|
+
},
|
|
1789
|
+
"Surface action rejected by the message queue",
|
|
1790
|
+
);
|
|
1730
1791
|
}
|
|
1731
1792
|
|
|
1732
1793
|
// One-shot interactive surfaces auto-complete once their action message is
|
|
@@ -1740,8 +1801,78 @@ const ONE_SHOT_SURFACE_TYPES = [
|
|
|
1740
1801
|
"task_preferences",
|
|
1741
1802
|
];
|
|
1742
1803
|
|
|
1804
|
+
/**
|
|
1805
|
+
* The completion rule for an accepted surface action: complete when the client
|
|
1806
|
+
* asked for it explicitly (`_completeSurface`), or when the surface is a
|
|
1807
|
+
* one-shot type. No-op otherwise. Owns the whole rule, including reading the
|
|
1808
|
+
* `_completeSurface` request out of the raw action data.
|
|
1809
|
+
*
|
|
1810
|
+
* Called from both `handleSurfaceAction` branches, one holding a pending entry
|
|
1811
|
+
* and one with none, always after `enqueueMessage` accepted the turn so a
|
|
1812
|
+
* rejected enqueue leaves the surface answerable. Persists first and broadcasts
|
|
1813
|
+
* `ui_surface_complete` only once that write is known not to have failed: a
|
|
1814
|
+
* client must not render a card the next history reseed reverts.
|
|
1815
|
+
*
|
|
1816
|
+
* "No pending entry" is broader than "restored from history", and with
|
|
1817
|
+
* `pendingSurfaceActions` empty the two cannot be told apart: a surface shown
|
|
1818
|
+
* with `await_action: false` never registered a pending entry, and a daemon
|
|
1819
|
+
* restart drops the entry of one that did. So a one-shot surface that never
|
|
1820
|
+
* awaited an action does complete on its first action.
|
|
1821
|
+
*
|
|
1822
|
+
* `liveSurfaceType` / `liveSurfaceData` come from in-memory state. When the
|
|
1823
|
+
* type is missing the persisted block supplies both, read at most once per
|
|
1824
|
+
* action and only once the decision actually needs it.
|
|
1825
|
+
*/
|
|
1826
|
+
function maybeCompleteSurfaceAfterAction(
|
|
1827
|
+
ctx: Conversation,
|
|
1828
|
+
surfaceId: string,
|
|
1829
|
+
actionId: string,
|
|
1830
|
+
actionData: Record<string, unknown> | undefined,
|
|
1831
|
+
opts: {
|
|
1832
|
+
liveSurfaceType: string | undefined;
|
|
1833
|
+
liveSurfaceData: Record<string, unknown> | undefined;
|
|
1834
|
+
requesterCanAccessMemory: boolean;
|
|
1835
|
+
submittedData?: Record<string, unknown>;
|
|
1836
|
+
},
|
|
1837
|
+
): void {
|
|
1838
|
+
const requestedSummary = getRequestedSurfaceCompletionSummary(actionData);
|
|
1839
|
+
|
|
1840
|
+
let surfaceType = opts.liveSurfaceType;
|
|
1841
|
+
let surfaceData = opts.liveSurfaceData;
|
|
1842
|
+
// Only the type-driven one-shot rule needs history: an explicit request
|
|
1843
|
+
// carries its own summary and decides on its own. Type and labels come from
|
|
1844
|
+
// the same read so the decision and the summary can never disagree.
|
|
1845
|
+
if (surfaceType === undefined && !requestedSummary) {
|
|
1846
|
+
const persisted = findPersistedSurfaceInfo(ctx.conversationId, surfaceId, {
|
|
1847
|
+
requesterCanAccessMemory: opts.requesterCanAccessMemory,
|
|
1848
|
+
});
|
|
1849
|
+
surfaceType = persisted?.surfaceType;
|
|
1850
|
+
surfaceData = persisted?.data ?? surfaceData;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
const isOneShot =
|
|
1854
|
+
surfaceType !== undefined && ONE_SHOT_SURFACE_TYPES.includes(surfaceType);
|
|
1855
|
+
if (!requestedSummary && !isOneShot) {
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
const summary =
|
|
1860
|
+
requestedSummary ??
|
|
1861
|
+
buildCompletionSummary(surfaceType, actionId, actionData, surfaceData);
|
|
1862
|
+
if (!markSurfaceCompleted(ctx, surfaceId, summary)) {
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
broadcastMessage({
|
|
1866
|
+
type: "ui_surface_complete",
|
|
1867
|
+
conversationId: ctx.conversationId,
|
|
1868
|
+
surfaceId,
|
|
1869
|
+
summary,
|
|
1870
|
+
...(opts.submittedData ? { submittedData: opts.submittedData } : {}),
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1743
1874
|
export async function handleSurfaceAction(
|
|
1744
|
-
ctx:
|
|
1875
|
+
ctx: Conversation,
|
|
1745
1876
|
surfaceId: string,
|
|
1746
1877
|
actionId: string,
|
|
1747
1878
|
data?: Record<string, unknown>,
|
|
@@ -1802,6 +1933,9 @@ export async function handleSurfaceAction(
|
|
|
1802
1933
|
return { accepted: true, conversationId: ctx.conversationId };
|
|
1803
1934
|
}
|
|
1804
1935
|
|
|
1936
|
+
// Broadcast unconditionally: `showStandaloneSurface` renders straight to
|
|
1937
|
+
// the client without appending a `ui_surface` block, so there is no
|
|
1938
|
+
// persisted state a failed completion write could contradict.
|
|
1805
1939
|
broadcastMessage({
|
|
1806
1940
|
type: "ui_surface_complete",
|
|
1807
1941
|
conversationId: ctx.conversationId,
|
|
@@ -1911,6 +2045,16 @@ export async function handleSurfaceAction(
|
|
|
1911
2045
|
return { accepted: true, conversationId };
|
|
1912
2046
|
}
|
|
1913
2047
|
|
|
2048
|
+
// Trust of the actor committing THIS action. `POST /v1/surface-actions`
|
|
2049
|
+
// stamps the verified requester's trust onto the conversation before
|
|
2050
|
+
// dispatching here, and `loadFromDb` derives the live history filter from
|
|
2051
|
+
// the same value, so the completion path's persisted read stays scoped to
|
|
2052
|
+
// exactly what this actor's live view would have held. Unresolvable trust
|
|
2053
|
+
// fails closed to the untrusted filter.
|
|
2054
|
+
const requesterCanAccessMemory = resolveCapabilities(
|
|
2055
|
+
ctx.trustContext?.trustClass,
|
|
2056
|
+
).canAccessMemory;
|
|
2057
|
+
|
|
1914
2058
|
const pending = ctx.pendingSurfaceActions.get(surfaceId);
|
|
1915
2059
|
|
|
1916
2060
|
// When surfaces are restored from history (e.g. onboarding cards), there is
|
|
@@ -2019,8 +2163,12 @@ export async function handleSurfaceAction(
|
|
|
2019
2163
|
|
|
2020
2164
|
log.info(
|
|
2021
2165
|
{
|
|
2166
|
+
conversationId: ctx.conversationId,
|
|
2022
2167
|
surfaceId,
|
|
2023
2168
|
actionId,
|
|
2169
|
+
// False means the surface type and the labels its completion summary
|
|
2170
|
+
// quotes can only come from persisted history.
|
|
2171
|
+
hasLiveState: stored !== undefined,
|
|
2024
2172
|
contentLength: content.length,
|
|
2025
2173
|
contentPreview: content.slice(0, 200),
|
|
2026
2174
|
attachmentCount: attachments.length,
|
|
@@ -2057,6 +2205,7 @@ export async function handleSurfaceAction(
|
|
|
2057
2205
|
|
|
2058
2206
|
if (result.rejected) {
|
|
2059
2207
|
ctx.surfaceActionRequestIds.delete(requestId);
|
|
2208
|
+
logSurfaceActionRejected(ctx, surfaceId, actionId, stored?.surfaceType);
|
|
2060
2209
|
return;
|
|
2061
2210
|
}
|
|
2062
2211
|
|
|
@@ -2066,11 +2215,12 @@ export async function handleSurfaceAction(
|
|
|
2066
2215
|
// (and the one-shot tag stays intact for the user's retry).
|
|
2067
2216
|
maybeEmitActivationMoment(ctx, surfaceId);
|
|
2068
2217
|
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2218
|
+
maybeCompleteSurfaceAfterAction(ctx, surfaceId, actionId, mergedData, {
|
|
2219
|
+
liveSurfaceType: stored?.surfaceType,
|
|
2220
|
+
liveSurfaceData: stored?.data as Record<string, unknown> | undefined,
|
|
2221
|
+
requesterCanAccessMemory,
|
|
2222
|
+
submittedData: actionDataForText,
|
|
2223
|
+
});
|
|
2074
2224
|
|
|
2075
2225
|
// One-shot: clear accumulated state now that the message has been accepted.
|
|
2076
2226
|
// Deferred until after rejection check so state is preserved for retry on rejection.
|
|
@@ -2302,6 +2452,7 @@ export async function handleSurfaceAction(
|
|
|
2302
2452
|
});
|
|
2303
2453
|
if (result.rejected) {
|
|
2304
2454
|
ctx.surfaceActionRequestIds.delete(requestId);
|
|
2455
|
+
logSurfaceActionRejected(ctx, surfaceId, actionId, pending.surfaceType);
|
|
2305
2456
|
return;
|
|
2306
2457
|
}
|
|
2307
2458
|
|
|
@@ -2311,26 +2462,12 @@ export async function handleSurfaceAction(
|
|
|
2311
2462
|
// one-shot tag stays intact for the user's retry).
|
|
2312
2463
|
maybeEmitActivationMoment(ctx, surfaceId);
|
|
2313
2464
|
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
if (
|
|
2321
|
-
requestedCompletionSummary ||
|
|
2322
|
-
ONE_SHOT_SURFACE_TYPES.includes(pending.surfaceType)
|
|
2323
|
-
) {
|
|
2324
|
-
const completionSummary = requestedCompletionSummary ?? summary;
|
|
2325
|
-
broadcastMessage({
|
|
2326
|
-
type: "ui_surface_complete",
|
|
2327
|
-
conversationId: ctx.conversationId,
|
|
2328
|
-
surfaceId,
|
|
2329
|
-
summary: completionSummary,
|
|
2330
|
-
submittedData: mergedDataForText,
|
|
2331
|
-
});
|
|
2332
|
-
markSurfaceCompleted(ctx, surfaceId, completionSummary);
|
|
2333
|
-
}
|
|
2465
|
+
maybeCompleteSurfaceAfterAction(ctx, surfaceId, actionId, mergedData, {
|
|
2466
|
+
liveSurfaceType: pending.surfaceType,
|
|
2467
|
+
liveSurfaceData: surfaceData,
|
|
2468
|
+
requesterCanAccessMemory,
|
|
2469
|
+
submittedData: mergedDataForText,
|
|
2470
|
+
});
|
|
2334
2471
|
|
|
2335
2472
|
// One-shot: clear accumulated state now that the message has been accepted.
|
|
2336
2473
|
// Deferred until after rejection check so state is preserved for retry on rejection.
|
|
@@ -2401,7 +2538,7 @@ export async function handleSurfaceAction(
|
|
|
2401
2538
|
* After an app_refresh, refresh any active surface that displays the updated app.
|
|
2402
2539
|
*/
|
|
2403
2540
|
export function refreshSurfacesForApp(
|
|
2404
|
-
ctx:
|
|
2541
|
+
ctx: Conversation,
|
|
2405
2542
|
appId: string,
|
|
2406
2543
|
opts?: { fileChange?: boolean; status?: string },
|
|
2407
2544
|
): boolean {
|
|
@@ -2707,12 +2844,56 @@ export function buildAppOpenPreview(
|
|
|
2707
2844
|
};
|
|
2708
2845
|
}
|
|
2709
2846
|
|
|
2847
|
+
/**
|
|
2848
|
+
* Explain why a `computer_use_*` call cannot be dispatched, after
|
|
2849
|
+
* {@link ensureHostCuProxy} has already failed to attach one.
|
|
2850
|
+
*
|
|
2851
|
+
* "No desktop client connected" is only one of the reasons, and stating it
|
|
2852
|
+
* unconditionally contradicts `assistant clients list` whenever a desktop is
|
|
2853
|
+
* plainly connected. Only counts are reported — never client ids or actor
|
|
2854
|
+
* principals — since this string reaches the model and the transcript.
|
|
2855
|
+
*/
|
|
2856
|
+
function describeComputerUseUnavailable(ctx: Conversation): string {
|
|
2857
|
+
const capable = assistantEventHub.listClientsByCapability("host_cu");
|
|
2858
|
+
if (capable.length === 0) {
|
|
2859
|
+
return "Computer use is not available — no desktop client connected. Open the Vellum desktop app on the machine you want to control, then retry.";
|
|
2860
|
+
}
|
|
2861
|
+
return `Computer use is not available for this conversation — ${capable.length} desktop client(s) advertise host_cu, but none of them can be driven from this conversation's interface (${ctx.transportInterface ?? "unknown"}) as its current user.`;
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
/**
|
|
2865
|
+
* Return the conversation's CU proxy, attaching one first when the
|
|
2866
|
+
* conversation has none.
|
|
2867
|
+
*
|
|
2868
|
+
* Host-proxy attachment is decided at turn boundaries — message create and
|
|
2869
|
+
* queue drain — and a `computer_use_*` call can arrive well after that
|
|
2870
|
+
* decision was made. A conversation whose gate failed at turn start (the
|
|
2871
|
+
* desktop had not connected yet, or the actor principal was not yet
|
|
2872
|
+
* resolvable) therefore stayed permanently without a CU proxy for the rest
|
|
2873
|
+
* of the turn, and every computer-use call reported "no desktop client
|
|
2874
|
+
* connected" while `assistant clients list` showed the desktop connected and
|
|
2875
|
+
* usable.
|
|
2876
|
+
*
|
|
2877
|
+
* Re-running the same gate here — `Conversation.ensureHostProxiesForTurn`,
|
|
2878
|
+
* the very function both turn-boundary paths call — makes the decision track
|
|
2879
|
+
* the live state instead of a stale snapshot. It grants nothing on its own:
|
|
2880
|
+
* the gate is unchanged, and every dispatch below still binds to the calling
|
|
2881
|
+
* actor through the same-actor checks.
|
|
2882
|
+
*/
|
|
2883
|
+
function ensureHostCuProxy(ctx: Conversation): HostCuProxy | undefined {
|
|
2884
|
+
if (ctx.hostCuProxy) {
|
|
2885
|
+
return ctx.hostCuProxy;
|
|
2886
|
+
}
|
|
2887
|
+
ctx.ensureHostProxiesForTurn?.(ctx.transportInterface);
|
|
2888
|
+
return ctx.hostCuProxy;
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2710
2891
|
/**
|
|
2711
2892
|
* Resolve a proxy tool call that targets a UI surface.
|
|
2712
2893
|
* Handles ui_show, ui_update, ui_dismiss, computer_use_* proxy tools, and app_open.
|
|
2713
2894
|
*/
|
|
2714
2895
|
export async function surfaceProxyResolver(
|
|
2715
|
-
ctx:
|
|
2896
|
+
ctx: Conversation,
|
|
2716
2897
|
toolName: string,
|
|
2717
2898
|
input: Record<string, unknown>,
|
|
2718
2899
|
signal?: AbortSignal,
|
|
@@ -2720,9 +2901,10 @@ export async function surfaceProxyResolver(
|
|
|
2720
2901
|
): Promise<ToolExecutionResult> {
|
|
2721
2902
|
// Route CU proxy tools (all computer_use_* action tools)
|
|
2722
2903
|
if (toolName.startsWith("computer_use_")) {
|
|
2723
|
-
|
|
2904
|
+
const hostCuProxy = ensureHostCuProxy(ctx);
|
|
2905
|
+
if (!hostCuProxy || !hostCuProxy.isAvailable()) {
|
|
2724
2906
|
return {
|
|
2725
|
-
content:
|
|
2907
|
+
content: describeComputerUseUnavailable(ctx),
|
|
2726
2908
|
isError: true,
|
|
2727
2909
|
};
|
|
2728
2910
|
}
|
|
@@ -2738,7 +2920,7 @@ export async function surfaceProxyResolver(
|
|
|
2738
2920
|
: typeof input.answer === "string"
|
|
2739
2921
|
? input.answer
|
|
2740
2922
|
: "Task complete";
|
|
2741
|
-
|
|
2923
|
+
hostCuProxy.reset();
|
|
2742
2924
|
return { content: summary, isError: false };
|
|
2743
2925
|
}
|
|
2744
2926
|
|
|
@@ -2811,12 +2993,12 @@ export async function surfaceProxyResolver(
|
|
|
2811
2993
|
}
|
|
2812
2994
|
}
|
|
2813
2995
|
|
|
2814
|
-
|
|
2815
|
-
return
|
|
2996
|
+
hostCuProxy.recordAction(toolName, input, reasoning);
|
|
2997
|
+
return hostCuProxy.request(
|
|
2816
2998
|
toolName,
|
|
2817
2999
|
input,
|
|
2818
3000
|
ctx.conversationId,
|
|
2819
|
-
|
|
3001
|
+
hostCuProxy.stepCount,
|
|
2820
3002
|
reasoning,
|
|
2821
3003
|
signal,
|
|
2822
3004
|
targetClientId,
|
|
@@ -3394,6 +3576,18 @@ export async function surfaceProxyResolver(
|
|
|
3394
3576
|
lastAction.data,
|
|
3395
3577
|
stored?.data,
|
|
3396
3578
|
);
|
|
3579
|
+
// A `ui_show` surface owns a persisted block, so the completion has to
|
|
3580
|
+
// land before the client is told the card is answered.
|
|
3581
|
+
if (!markSurfaceCompleted(ctx, surfaceId, summary)) {
|
|
3582
|
+
// Tearing the live entries down here would strand the card that the
|
|
3583
|
+
// next reseed restores, so leave them for a retry and tell the model
|
|
3584
|
+
// the dismissal did not land.
|
|
3585
|
+
return {
|
|
3586
|
+
content:
|
|
3587
|
+
"Could not dismiss the surface: persisting its completion failed. The card is still showing, so try ui_dismiss again.",
|
|
3588
|
+
isError: true,
|
|
3589
|
+
};
|
|
3590
|
+
}
|
|
3397
3591
|
ctx.sendToClient({
|
|
3398
3592
|
type: "ui_surface_complete",
|
|
3399
3593
|
conversationId: ctx.conversationId,
|
|
@@ -3401,7 +3595,6 @@ export async function surfaceProxyResolver(
|
|
|
3401
3595
|
summary,
|
|
3402
3596
|
submittedData: lastAction.data,
|
|
3403
3597
|
});
|
|
3404
|
-
markSurfaceCompleted(ctx, surfaceId, summary);
|
|
3405
3598
|
} else {
|
|
3406
3599
|
ctx.sendToClient({
|
|
3407
3600
|
type: "ui_surface_dismiss",
|