@vellumai/assistant 0.11.2 → 0.11.3-staging.1
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-queue.test.ts +265 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -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-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-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-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 +216 -132
- 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/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 +296 -145
- 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 +85 -42
- package/src/daemon/conversation-process.ts +76 -28
- 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 +105 -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.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/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 +56 -8
- 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 +350 -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 +295 -42
- 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/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/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
|
@@ -110,10 +110,10 @@ export function upscaleTargetDimensions(dims: {
|
|
|
110
110
|
* Reactive only: called by the image-recovery plugin after an actual
|
|
111
111
|
* "Could not process image" rejection, never on the pre-send path.
|
|
112
112
|
*/
|
|
113
|
-
export function upscaleImageToMinimum(
|
|
113
|
+
export async function upscaleImageToMinimum(
|
|
114
114
|
base64Data: string,
|
|
115
115
|
mediaType: string,
|
|
116
|
-
): { data: string; mediaType: string } | null {
|
|
116
|
+
): Promise<{ data: string; mediaType: string } | null> {
|
|
117
117
|
const dims = parseImageDimensions(base64Data, mediaType);
|
|
118
118
|
if (!dims) {
|
|
119
119
|
return null;
|
|
@@ -122,7 +122,7 @@ export function upscaleImageToMinimum(
|
|
|
122
122
|
if (!target) {
|
|
123
123
|
return null;
|
|
124
124
|
}
|
|
125
|
-
const upscaled = convertImageToJpeg(Buffer.from(base64Data, "base64"), {
|
|
125
|
+
const upscaled = await convertImageToJpeg(Buffer.from(base64Data, "base64"), {
|
|
126
126
|
resizeToPx: target,
|
|
127
127
|
quality: JPEG_QUALITY,
|
|
128
128
|
});
|
|
@@ -144,10 +144,10 @@ export function upscaleImageToMinimum(
|
|
|
144
144
|
* Conversion (and its content-addressed disk cache) is shared with attachment
|
|
145
145
|
* storage normalization — see `util/image-conversion.ts`.
|
|
146
146
|
*/
|
|
147
|
-
export function optimizeImageForTransport(
|
|
147
|
+
export async function optimizeImageForTransport(
|
|
148
148
|
base64Data: string,
|
|
149
149
|
mediaType: string,
|
|
150
|
-
): { data: string; mediaType: string } {
|
|
150
|
+
): Promise<{ data: string; mediaType: string }> {
|
|
151
151
|
const rawBytes = Buffer.from(base64Data, "base64");
|
|
152
152
|
const dims = parseImageDimensions(base64Data, mediaType);
|
|
153
153
|
|
|
@@ -155,7 +155,7 @@ export function optimizeImageForTransport(
|
|
|
155
155
|
return { data: base64Data, mediaType };
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const optimized = convertImageToJpeg(rawBytes, {
|
|
158
|
+
const optimized = await convertImageToJpeg(rawBytes, {
|
|
159
159
|
maxDimensionPx: MAX_DIMENSION,
|
|
160
160
|
quality: JPEG_QUALITY,
|
|
161
161
|
});
|
package/src/agent/loop.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isMemoryV3Live } from "../config/memory-v3-gate.js";
|
|
1
|
+
import type { AnsweredQuestion } from "../api/events/question-answered.js";
|
|
3
2
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
4
3
|
import { recordEstimate } from "../context/estimator-calibration.js";
|
|
5
4
|
import { preModelCallSanitize } from "../context/outbound-sanitize.js";
|
|
5
|
+
import { turnStartUserMessageHasSpotlight } from "../context/strip-injections.js";
|
|
6
6
|
import {
|
|
7
7
|
estimatePromptTokensRaw,
|
|
8
8
|
estimatePromptTokensWithTools,
|
|
@@ -116,9 +116,9 @@ export interface AgentLoopConfig {
|
|
|
116
116
|
* This is a SERVER tool the provider runs itself, distinct from the client
|
|
117
117
|
* tool list (`tools` / `resolveTools`): it is never executed by
|
|
118
118
|
* {@link AgentLoopConstructorOptions.toolExecutor} and does not require any
|
|
119
|
-
* client-tool allowlist entry. Used by the
|
|
120
|
-
*
|
|
121
|
-
*
|
|
119
|
+
* client-tool allowlist entry. Used by the advisor consult to ground its
|
|
120
|
+
* guidance with live web access on top of its read-only client tools.
|
|
121
|
+
* Defaults to false.
|
|
122
122
|
*/
|
|
123
123
|
enableNativeWebSearch?: boolean;
|
|
124
124
|
}
|
|
@@ -289,6 +289,8 @@ export type AgentEvent =
|
|
|
289
289
|
approvalReason?: string;
|
|
290
290
|
riskThreshold?: string;
|
|
291
291
|
activityMetadata?: ToolActivityMetadata;
|
|
292
|
+
/** Answered `ask_question` record (see `ToolExecutionResult.answeredQuestion`). */
|
|
293
|
+
answeredQuestion?: AnsweredQuestion;
|
|
292
294
|
/** Stable machine-readable error classification (see `ToolExecutionResult.errorCode`). */
|
|
293
295
|
errorCode?: string;
|
|
294
296
|
/**
|
|
@@ -698,6 +700,7 @@ export type LoopToolExecutor = (
|
|
|
698
700
|
approvalReason?: string;
|
|
699
701
|
riskThreshold?: string;
|
|
700
702
|
activityMetadata?: ToolActivityMetadata;
|
|
703
|
+
answeredQuestion?: AnsweredQuestion;
|
|
701
704
|
errorCode?: string;
|
|
702
705
|
}>;
|
|
703
706
|
|
|
@@ -1355,9 +1358,9 @@ export class AgentLoop {
|
|
|
1355
1358
|
// `this.provider.supportsNativeWebSearch` snapshot; providers without
|
|
1356
1359
|
// the routing-aware probe fall back to the static flag. This is a SERVER
|
|
1357
1360
|
// tool — it bypasses the client allowlist and the tool executor — so the
|
|
1358
|
-
//
|
|
1359
|
-
//
|
|
1360
|
-
//
|
|
1361
|
+
// advisor consult can ground its guidance with live web access on top of
|
|
1362
|
+
// its read-only client tools. Skip when a `web_search` tool is already
|
|
1363
|
+
// present so we never duplicate the name.
|
|
1361
1364
|
const supportsRoutedNativeWebSearch = this.provider
|
|
1362
1365
|
.supportsNativeWebSearchFor
|
|
1363
1366
|
? this.provider.supportsNativeWebSearchFor(
|
|
@@ -1420,9 +1423,7 @@ export class AgentLoop {
|
|
|
1420
1423
|
if (this.config.toolChoice) {
|
|
1421
1424
|
providerConfig.tool_choice = this.config.toolChoice;
|
|
1422
1425
|
} else if (attachNativeWebSearch) {
|
|
1423
|
-
//
|
|
1424
|
-
// advisor consult is otherwise tool-less). Let the model decide
|
|
1425
|
-
// whether to search rather than forcing it.
|
|
1426
|
+
// Let the model decide whether to search rather than forcing it.
|
|
1426
1427
|
providerConfig.tool_choice = { type: "auto" };
|
|
1427
1428
|
}
|
|
1428
1429
|
|
|
@@ -1430,14 +1431,18 @@ export class AgentLoop {
|
|
|
1430
1431
|
providerConfig.cacheTtl = this.config.cacheTtl;
|
|
1431
1432
|
}
|
|
1432
1433
|
|
|
1433
|
-
// Cache-anchor signal for
|
|
1434
|
-
// memory-v3
|
|
1435
|
-
//
|
|
1436
|
-
//
|
|
1437
|
-
//
|
|
1438
|
-
//
|
|
1439
|
-
//
|
|
1440
|
-
|
|
1434
|
+
// Cache-anchor signal for turns whose opening message is volatile. The
|
|
1435
|
+
// memory-v3 `<memory_spotlight>` block is the only injected block that
|
|
1436
|
+
// is strip-and-replaced from every user message each turn, so when it
|
|
1437
|
+
// is present that message's bytes do not recur next turn and a
|
|
1438
|
+
// long-TTL breakpoint on it could never be read back. The provider
|
|
1439
|
+
// marks it at the short TTL instead. Derived from the history actually
|
|
1440
|
+
// being sent rather than from configuration, so turns where memory
|
|
1441
|
+
// contributed no spotlight keep a normal anchor. Read off the
|
|
1442
|
+
// turn-starting message, so the signal holds for every request in the
|
|
1443
|
+
// turn rather than flipping once tool results arrive. Only set when
|
|
1444
|
+
// true so the wire/config stays byte-identical when absent.
|
|
1445
|
+
if (turnStartUserMessageHasSpotlight(history)) {
|
|
1441
1446
|
providerConfig.mutableLatestUserMessage = true;
|
|
1442
1447
|
}
|
|
1443
1448
|
|
|
@@ -1459,8 +1464,17 @@ export class AgentLoop {
|
|
|
1459
1464
|
// conversation id). Absent for standalone `AgentLoop` instances
|
|
1460
1465
|
// (unit tests constructed without a conversation id) — those fall
|
|
1461
1466
|
// back to per-call random mix selection.
|
|
1467
|
+
//
|
|
1468
|
+
// The same id also travels as `conversationId`, which
|
|
1469
|
+
// `RetryProvider.normalizeSendMessageOptions` uses to resolve the
|
|
1470
|
+
// conversation's subagent role and spawn mode for the runtime
|
|
1471
|
+
// proxy's billing-attribution headers. Both are stripped before any
|
|
1472
|
+
// provider wire request; they are carried separately because
|
|
1473
|
+
// `selectionSeed` is an opaque hashing input that callers may set to
|
|
1474
|
+
// something other than a conversation id.
|
|
1462
1475
|
if (this.conversationId) {
|
|
1463
1476
|
providerConfig.selectionSeed = this.conversationId;
|
|
1477
|
+
providerConfig.conversationId = this.conversationId;
|
|
1464
1478
|
}
|
|
1465
1479
|
}
|
|
1466
1480
|
|
|
@@ -2201,13 +2215,16 @@ export class AgentLoop {
|
|
|
2201
2215
|
// message between calls would invalidate the prompt-cache prefix on
|
|
2202
2216
|
// every iteration).
|
|
2203
2217
|
if (conversationDir) {
|
|
2204
|
-
const
|
|
2205
|
-
toolUseBlocks.map((tu) => [
|
|
2218
|
+
const toolCallByUseId = new Map(
|
|
2219
|
+
toolUseBlocks.map((tu) => [
|
|
2220
|
+
tu.id,
|
|
2221
|
+
{ name: tu.name, input: tu.input },
|
|
2222
|
+
]),
|
|
2206
2223
|
);
|
|
2207
2224
|
try {
|
|
2208
2225
|
spoolAndStubOversizedToolResults(rawResultBlocks, {
|
|
2209
2226
|
conversationDir,
|
|
2210
|
-
|
|
2227
|
+
toolCallById: (id) => toolCallByUseId.get(id),
|
|
2211
2228
|
});
|
|
2212
2229
|
} catch (err) {
|
|
2213
2230
|
rlog.warn(
|
|
@@ -2286,6 +2303,7 @@ export class AgentLoop {
|
|
|
2286
2303
|
approvalReason: result.approvalReason,
|
|
2287
2304
|
riskThreshold: result.riskThreshold,
|
|
2288
2305
|
activityMetadata: result.activityMetadata,
|
|
2306
|
+
answeredQuestion: result.answeredQuestion,
|
|
2289
2307
|
errorCode: result.errorCode,
|
|
2290
2308
|
});
|
|
2291
2309
|
}
|
|
@@ -4,15 +4,15 @@ import {
|
|
|
4
4
|
type MessageAttachmentInput,
|
|
5
5
|
} from "./attachments.js";
|
|
6
6
|
|
|
7
|
-
export function createUserMessage(
|
|
7
|
+
export async function createUserMessage(
|
|
8
8
|
text: string,
|
|
9
9
|
attachments: MessageAttachmentInput[] = [],
|
|
10
|
-
): Message {
|
|
10
|
+
): Promise<Message> {
|
|
11
11
|
const content = [] as Message["content"];
|
|
12
12
|
if (text.trim().length > 0) {
|
|
13
13
|
content.push({ type: "text", text });
|
|
14
14
|
}
|
|
15
|
-
content.push(...attachmentsToContentBlocks(attachments));
|
|
15
|
+
content.push(...(await attachmentsToContentBlocks(attachments)));
|
|
16
16
|
return { role: "user", content };
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document tools that mutate the stored document set, and what each one
|
|
3
|
+
* leaves behind. This is the single declaration both the daemon and the web
|
|
4
|
+
* client derive their tool sets from, so a new mutating document tool is added
|
|
5
|
+
* here once and every consumer picks it up.
|
|
6
|
+
*
|
|
7
|
+
* Tool names are the ones the runner sees. Calls arriving through the bundled
|
|
8
|
+
* `document-editor` skill reach the daemon already unwrapped to the inner tool
|
|
9
|
+
* name, and the web client unwraps the `skill_execute` envelope itself.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface DocumentMutationTool {
|
|
13
|
+
/** The executed tool name. */
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the document is still openable once the tool has run. A deleted
|
|
17
|
+
* document has nothing to open, so it cannot anchor a link back to itself.
|
|
18
|
+
*/
|
|
19
|
+
readonly reopenable: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the tool writes into an already-created document, as opposed to
|
|
22
|
+
* the create that opened it.
|
|
23
|
+
*/
|
|
24
|
+
readonly edit: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const DOCUMENT_MUTATION_TOOLS: readonly DocumentMutationTool[] = [
|
|
28
|
+
{ name: "document_create", reopenable: true, edit: false },
|
|
29
|
+
{ name: "document_update", reopenable: true, edit: true },
|
|
30
|
+
{ name: "document_replace_text", reopenable: true, edit: true },
|
|
31
|
+
{ name: "document_delete", reopenable: false, edit: false },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Every tool that changes the document list. The daemon hooks these to publish
|
|
36
|
+
* a documents-changed broadcast, because they carry no list-level change signal
|
|
37
|
+
* of their own and client asset lists would otherwise keep serving a cached
|
|
38
|
+
* list after an edit and a deleted document would linger as a ghost row.
|
|
39
|
+
*/
|
|
40
|
+
export const DOCUMENT_MUTATION_TOOL_NAMES: readonly string[] =
|
|
41
|
+
DOCUMENT_MUTATION_TOOLS.map((tool) => tool.name);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The mutating tools that leave the document openable. The web transcript
|
|
45
|
+
* anchors its changed-document chips on these, so a delete never produces a
|
|
46
|
+
* chip pointing at a document that is gone.
|
|
47
|
+
*/
|
|
48
|
+
export const REOPENABLE_DOCUMENT_MUTATION_TOOL_NAMES: readonly string[] =
|
|
49
|
+
DOCUMENT_MUTATION_TOOLS.filter((tool) => tool.reopenable).map(
|
|
50
|
+
(tool) => tool.name,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/** The mutating tools that write into an already-created document. */
|
|
54
|
+
export const DOCUMENT_EDIT_TOOL_NAMES: readonly string[] =
|
|
55
|
+
DOCUMENT_MUTATION_TOOLS.filter((tool) => tool.edit).map((tool) => tool.name);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable record of an `ask_question` prompt the user has answered.
|
|
3
|
+
*
|
|
4
|
+
* The pending counterpart (`question_request` / `PendingToolQuestion`) is live
|
|
5
|
+
* registry state: it exists only while the prompt is outstanding and vanishes
|
|
6
|
+
* the moment the interaction resolves. This record is the opposite. It is
|
|
7
|
+
* produced by the `ask_question` executor once the prompt settles, rides the
|
|
8
|
+
* `tool_result` event, and is persisted on the tool_use block, so the answered
|
|
9
|
+
* question and the user's choice stay readable in the transcript across
|
|
10
|
+
* conversation switches, reloads, and history reopens.
|
|
11
|
+
*
|
|
12
|
+
* `questions` mirrors the `question_request` event's `questions[]` (same
|
|
13
|
+
* daemon-assigned `q1`, `q2`, ... ids) so the answered card renders from the
|
|
14
|
+
* same shape the interactive card did. `responses` is ordered to match, one
|
|
15
|
+
* entry per question.
|
|
16
|
+
*
|
|
17
|
+
* Only user-driven outcomes are recorded: a completed batch, or a card the
|
|
18
|
+
* user closed without answering (every question `skipped`). A prompt that
|
|
19
|
+
* timed out or was aborted produced no user decision, so it carries no record
|
|
20
|
+
* and the tool's own error result stands on its own.
|
|
21
|
+
*
|
|
22
|
+
* Canonical wire-contract source. Daemon code imports the type directly from
|
|
23
|
+
* this file; external consumers import via `@vellumai/assistant-api`.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
|
|
28
|
+
import { QuestionEntrySchema } from "./question-request.js";
|
|
29
|
+
|
|
30
|
+
export const AnsweredQuestionResponseSchema = z.object({
|
|
31
|
+
/** Daemon-assigned question id (`q1`, `q2`, ...) this answer belongs to. */
|
|
32
|
+
questionId: z.string(),
|
|
33
|
+
decision: z.enum(["option", "free_text", "skipped"]),
|
|
34
|
+
/** Chosen option id. Present only when `decision` is `"option"`. */
|
|
35
|
+
optionId: z.string().optional(),
|
|
36
|
+
/** The user's typed answer. Present only when `decision` is `"free_text"`. */
|
|
37
|
+
text: z.string().optional(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export type AnsweredQuestionResponse = z.infer<
|
|
41
|
+
typeof AnsweredQuestionResponseSchema
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
export const AnsweredQuestionSchema = z.object({
|
|
45
|
+
/** The resolved interaction's request id, matching the `question_request` that raised it. */
|
|
46
|
+
requestId: z.string(),
|
|
47
|
+
questions: z.array(QuestionEntrySchema),
|
|
48
|
+
responses: z.array(AnsweredQuestionResponseSchema),
|
|
49
|
+
/** `"completed"` when the user submitted a batch, `"closed"` when they dismissed the card. */
|
|
50
|
+
overall: z.enum(["completed", "closed"]),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type AnsweredQuestion = z.infer<typeof AnsweredQuestionSchema>;
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
ConfirmationDiffSchema,
|
|
35
35
|
DirectoryScopeOptionSchema,
|
|
36
36
|
} from "./confirmation-request.js";
|
|
37
|
+
import { AnsweredQuestionSchema } from "./question-answered.js";
|
|
37
38
|
|
|
38
39
|
export const RiskScopeOptionSchema = z.object({
|
|
39
40
|
pattern: z.string(),
|
|
@@ -131,6 +132,12 @@ export const ToolResultEventSchema = z.object({
|
|
|
131
132
|
approvalReason: z.string().optional(),
|
|
132
133
|
riskThreshold: z.string().optional(),
|
|
133
134
|
activityMetadata: ToolActivityMetadataSchema.optional(),
|
|
135
|
+
/**
|
|
136
|
+
* Set only by `ask_question`: the questions asked and the answers the user
|
|
137
|
+
* gave. Carried here so the answered card renders the instant the prompt
|
|
138
|
+
* resolves, from the same record the daemon persists on the tool_use block.
|
|
139
|
+
*/
|
|
140
|
+
answeredQuestion: AnsweredQuestionSchema.optional(),
|
|
134
141
|
/**
|
|
135
142
|
* Stable, machine-readable classification for an error result (only set when
|
|
136
143
|
* `isError`). Lets a client branch on a known failure — e.g.
|
package/src/api/index.ts
CHANGED
|
@@ -137,6 +137,13 @@ export {
|
|
|
137
137
|
CALL_SITE_COMPACTION_AGENT,
|
|
138
138
|
CALL_SITE_SYNTHETIC_AGENT_ERROR_MESSAGE,
|
|
139
139
|
} from "./constants/call-sites.js";
|
|
140
|
+
export {
|
|
141
|
+
DOCUMENT_EDIT_TOOL_NAMES,
|
|
142
|
+
DOCUMENT_MUTATION_TOOL_NAMES,
|
|
143
|
+
DOCUMENT_MUTATION_TOOLS,
|
|
144
|
+
type DocumentMutationTool,
|
|
145
|
+
REOPENABLE_DOCUMENT_MUTATION_TOOL_NAMES,
|
|
146
|
+
} from "./constants/document-tools.js";
|
|
140
147
|
export {
|
|
141
148
|
SSE_REPLAY_RING_AGE_LIMIT_MS,
|
|
142
149
|
SSE_REPLAY_RING_COUNT_LIMIT,
|
|
@@ -493,6 +500,12 @@ export {
|
|
|
493
500
|
type PlatformDisconnectedEvent,
|
|
494
501
|
PlatformDisconnectedEventSchema,
|
|
495
502
|
} from "./events/platform-disconnected.js";
|
|
503
|
+
export {
|
|
504
|
+
type AnsweredQuestion,
|
|
505
|
+
type AnsweredQuestionResponse,
|
|
506
|
+
AnsweredQuestionResponseSchema,
|
|
507
|
+
AnsweredQuestionSchema,
|
|
508
|
+
} from "./events/question-answered.js";
|
|
496
509
|
export {
|
|
497
510
|
type QuestionEntry,
|
|
498
511
|
QuestionEntrySchema,
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
DirectoryScopeOptionSchema,
|
|
30
30
|
ScopeOptionSchema,
|
|
31
31
|
} from "../events/confirmation-request.js";
|
|
32
|
+
import { AnsweredQuestionSchema } from "../events/question-answered.js";
|
|
32
33
|
import { QuestionEntrySchema } from "../events/question-request.js";
|
|
33
34
|
import { ToolActivityMetadataSchema } from "../events/tool-result.js";
|
|
34
35
|
|
|
@@ -242,6 +243,16 @@ export const ConversationMessageToolCallSchema = z.object({
|
|
|
242
243
|
* render time). Lets a cold reconnect restore the inline question card.
|
|
243
244
|
*/
|
|
244
245
|
pendingQuestion: PendingToolQuestionSchema.optional(),
|
|
246
|
+
/**
|
|
247
|
+
* The answered counterpart to `pendingQuestion`: the questions an
|
|
248
|
+
* `ask_question` call asked and the answers the user gave. Persisted on the
|
|
249
|
+
* tool_use block rather than stamped from the live registry, so it survives a
|
|
250
|
+
* conversation switch, a reload, and a history reopen. A tool call carries at
|
|
251
|
+
* most one of the two: the pending prompt while it is outstanding, the
|
|
252
|
+
* answered record once it settles. Absent on history rows written before the
|
|
253
|
+
* daemon recorded it.
|
|
254
|
+
*/
|
|
255
|
+
answeredQuestion: AnsweredQuestionSchema.optional(),
|
|
245
256
|
});
|
|
246
257
|
export type ConversationMessageToolCall = z.infer<
|
|
247
258
|
typeof ConversationMessageToolCallSchema
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type OAuthConnectSurfaceData,
|
|
5
|
+
OAuthConnectSurfaceDataSchema,
|
|
6
|
+
} from "./surfaces.js";
|
|
7
|
+
|
|
8
|
+
describe("OAuthConnectSurfaceDataSchema requestedScopes", () => {
|
|
9
|
+
test("parses a valid scopes array and preserves order", () => {
|
|
10
|
+
const parsed = OAuthConnectSurfaceDataSchema.parse({
|
|
11
|
+
providerKey: "google",
|
|
12
|
+
requestedScopes: [
|
|
13
|
+
"https://www.googleapis.com/auth/tasks",
|
|
14
|
+
"https://www.googleapis.com/auth/calendar",
|
|
15
|
+
"openid",
|
|
16
|
+
],
|
|
17
|
+
});
|
|
18
|
+
expect(parsed.requestedScopes).toEqual([
|
|
19
|
+
"https://www.googleapis.com/auth/tasks",
|
|
20
|
+
"https://www.googleapis.com/auth/calendar",
|
|
21
|
+
"openid",
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("absent requestedScopes parses to undefined", () => {
|
|
26
|
+
const parsed = OAuthConnectSurfaceDataSchema.parse({
|
|
27
|
+
providerKey: "google",
|
|
28
|
+
});
|
|
29
|
+
expect(parsed.requestedScopes).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("an empty array collapses to undefined", () => {
|
|
33
|
+
const parsed = OAuthConnectSurfaceDataSchema.parse({
|
|
34
|
+
providerKey: "google",
|
|
35
|
+
requestedScopes: [],
|
|
36
|
+
});
|
|
37
|
+
expect(parsed.requestedScopes).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("non-array values collapse to undefined instead of rejecting", () => {
|
|
41
|
+
for (const value of [
|
|
42
|
+
"https://www.googleapis.com/auth/tasks, openid",
|
|
43
|
+
42,
|
|
44
|
+
true,
|
|
45
|
+
{ scope: "openid" },
|
|
46
|
+
null,
|
|
47
|
+
]) {
|
|
48
|
+
const parsed = OAuthConnectSurfaceDataSchema.parse({
|
|
49
|
+
providerKey: "google",
|
|
50
|
+
requestedScopes: value,
|
|
51
|
+
});
|
|
52
|
+
expect(parsed.requestedScopes).toBeUndefined();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("an array of blanks collapses to undefined", () => {
|
|
57
|
+
const parsed = OAuthConnectSurfaceDataSchema.parse({
|
|
58
|
+
providerKey: "google",
|
|
59
|
+
requestedScopes: ["", " ", "\t"],
|
|
60
|
+
});
|
|
61
|
+
expect(parsed.requestedScopes).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("entries are trimmed and blank or non-string junk is dropped", () => {
|
|
65
|
+
const parsed = OAuthConnectSurfaceDataSchema.parse({
|
|
66
|
+
providerKey: "google",
|
|
67
|
+
requestedScopes: [
|
|
68
|
+
" https://www.googleapis.com/auth/tasks ",
|
|
69
|
+
"",
|
|
70
|
+
null,
|
|
71
|
+
{ nope: true },
|
|
72
|
+
"openid",
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
expect(parsed.requestedScopes).toEqual([
|
|
76
|
+
"https://www.googleapis.com/auth/tasks",
|
|
77
|
+
"openid",
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("a bare string is not split on commas (scopes are opaque URIs)", () => {
|
|
82
|
+
const parsed = OAuthConnectSurfaceDataSchema.parse({
|
|
83
|
+
providerKey: "google",
|
|
84
|
+
requestedScopes: "a,b,c",
|
|
85
|
+
});
|
|
86
|
+
expect(parsed.requestedScopes).toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("legacy payload { providerKey: 'google' } still parses (skew guard)", () => {
|
|
90
|
+
const parsed: OAuthConnectSurfaceData = OAuthConnectSurfaceDataSchema.parse(
|
|
91
|
+
{ providerKey: "google" },
|
|
92
|
+
);
|
|
93
|
+
expect(parsed.providerKey).toBe("google");
|
|
94
|
+
expect(parsed.requestedScopes).toBeUndefined();
|
|
95
|
+
});
|
|
96
|
+
});
|
package/src/api/surfaces.ts
CHANGED
|
@@ -120,6 +120,21 @@ export const CardSurfaceDataSchema = z.object({
|
|
|
120
120
|
});
|
|
121
121
|
export type CardSurfaceData = z.infer<typeof CardSurfaceDataSchema>;
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Stringify+trim entries, drop blanks and non-scalars, and collapse an
|
|
125
|
+
* empty result to `undefined` so a schema treats it as "field absent".
|
|
126
|
+
*/
|
|
127
|
+
function cleanedStringEntries(items: unknown[]): string[] | undefined {
|
|
128
|
+
const cleaned = items
|
|
129
|
+
.map((item) =>
|
|
130
|
+
typeof item === "string" || typeof item === "number"
|
|
131
|
+
? String(item).trim()
|
|
132
|
+
: "",
|
|
133
|
+
)
|
|
134
|
+
.filter((item) => item.length > 0);
|
|
135
|
+
return cleaned.length > 0 ? cleaned : undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
123
138
|
/**
|
|
124
139
|
* Accepted MIME-type / extension patterns for a `file_upload` surface.
|
|
125
140
|
*
|
|
@@ -137,14 +152,7 @@ const FileUploadAcceptedTypesSchema = z.preprocess((value) => {
|
|
|
137
152
|
: Array.isArray(value)
|
|
138
153
|
? value
|
|
139
154
|
: [];
|
|
140
|
-
|
|
141
|
-
.map((item) =>
|
|
142
|
-
typeof item === "string" || typeof item === "number"
|
|
143
|
-
? String(item).trim()
|
|
144
|
-
: "",
|
|
145
|
-
)
|
|
146
|
-
.filter((item) => item.length > 0);
|
|
147
|
-
return cleaned.length > 0 ? cleaned : undefined;
|
|
155
|
+
return cleanedStringEntries(items);
|
|
148
156
|
}, z.array(z.string()).optional());
|
|
149
157
|
|
|
150
158
|
export const FileUploadSurfaceDataSchema = z.object({
|
|
@@ -238,6 +246,19 @@ export const ChoiceSurfaceDataSchema = z.object({
|
|
|
238
246
|
});
|
|
239
247
|
export type ChoiceSurfaceData = z.infer<typeof ChoiceSurfaceDataSchema>;
|
|
240
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Requested OAuth scopes for an `oauth_connect` surface.
|
|
251
|
+
*
|
|
252
|
+
* Unlike `FileUploadAcceptedTypesSchema`, a bare string is NOT split on
|
|
253
|
+
* commas: OAuth scopes are opaque URIs, so only arrays are accepted.
|
|
254
|
+
* Entries are stringified and trimmed, blanks are dropped, and an empty
|
|
255
|
+
* result or any non-array value collapses to `undefined` (use defaults).
|
|
256
|
+
*/
|
|
257
|
+
const RequestedScopesSchema = z.preprocess(
|
|
258
|
+
(value) => (Array.isArray(value) ? cleanedStringEntries(value) : undefined),
|
|
259
|
+
z.array(z.string()).optional(),
|
|
260
|
+
);
|
|
261
|
+
|
|
241
262
|
export const OAuthConnectSurfaceDataSchema = z.object({
|
|
242
263
|
/** OAuth provider key from the managed provider catalog, e.g. "google". */
|
|
243
264
|
providerKey: z
|
|
@@ -246,6 +267,13 @@ export const OAuthConnectSurfaceDataSchema = z.object({
|
|
|
246
267
|
z.string(),
|
|
247
268
|
)
|
|
248
269
|
.catch(""),
|
|
270
|
+
/**
|
|
271
|
+
* Optional OAuth scopes to request for the managed connection. A full
|
|
272
|
+
* replacement set: when present, the platform uses exactly these scopes
|
|
273
|
+
* instead of its defaults (it does not merge). Omit to use the platform's
|
|
274
|
+
* default scopes for the provider.
|
|
275
|
+
*/
|
|
276
|
+
requestedScopes: RequestedScopesSchema,
|
|
249
277
|
/** Optional display label. The client falls back to the provider catalog. */
|
|
250
278
|
displayName: tolerantString(),
|
|
251
279
|
/** Optional helper text. The client falls back to the provider catalog. */
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
* guardian decided off-channel (desktop), or the expiry sweep fired on a timer
|
|
16
16
|
* with no originating request in hand. Returns null for channels that have no
|
|
17
17
|
* deliverable route (e.g. email, the in-app vellum surface).
|
|
18
|
+
*
|
|
19
|
+
* Discord carries `dm=1`, which tells its transport that the `chatId` it is
|
|
20
|
+
* handed names a person to open a DM with rather than a channel to post in.
|
|
21
|
+
* Pair it with {@link resolveRequesterDeliveryTarget}, which supplies that
|
|
22
|
+
* person.
|
|
18
23
|
*/
|
|
19
24
|
export function resolveDeliverCallbackUrlForChannel(
|
|
20
25
|
channel: string,
|
|
@@ -24,7 +29,85 @@ export function resolveDeliverCallbackUrlForChannel(
|
|
|
24
29
|
case "whatsapp":
|
|
25
30
|
case "slack":
|
|
26
31
|
return `/deliver/${channel}`;
|
|
32
|
+
case "discord":
|
|
33
|
+
return "/deliver/discord?dm=1";
|
|
27
34
|
default:
|
|
28
35
|
return null;
|
|
29
36
|
}
|
|
30
37
|
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether a message posted back in-band on this channel can be kept to one
|
|
41
|
+
* reader.
|
|
42
|
+
*
|
|
43
|
+
* Slack can, with `chat.postEphemeral`. Discord cannot: it has no ephemeral
|
|
44
|
+
* message outside an interaction response, so anything posted into the room a
|
|
45
|
+
* guardian replied in, or the room a request came from, is readable by every
|
|
46
|
+
* member of the server. That applies to a decision notice and to a
|
|
47
|
+
* verification code alike, which is why the callers treat an in-band reply
|
|
48
|
+
* context as unusable for Discord and fall through to the DM route instead.
|
|
49
|
+
*
|
|
50
|
+
* Telegram and WhatsApp are trivially true: their conversation already has one
|
|
51
|
+
* reader.
|
|
52
|
+
*/
|
|
53
|
+
export function channelCanAddressOneReaderInBand(channel: string): boolean {
|
|
54
|
+
return channel !== "discord";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Whether a notice can be addressed to one person by their user id rather than
|
|
59
|
+
* to the conversation the request arrived in.
|
|
60
|
+
*
|
|
61
|
+
* Slack opens a 1:1 DM when a `U…` id is posted as the channel. Discord
|
|
62
|
+
* resolves a user snowflake to a DM channel on its `dm`-marked route. This is
|
|
63
|
+
* an OUTBOUND question only: it says a message can be put in front of one
|
|
64
|
+
* person, and nothing about whether they can answer it.
|
|
65
|
+
*/
|
|
66
|
+
export function channelDeliversToUserId(channel: string): boolean {
|
|
67
|
+
return channel === "slack" || channel === "discord";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Whether a verification code can be sent straight to the requester, rather
|
|
72
|
+
* than handed to the guardian to relay out of band.
|
|
73
|
+
*
|
|
74
|
+
* Deliberately NOT the same set as {@link channelDeliversToUserId}, though it
|
|
75
|
+
* looks like it. That one asks whether a message reaches one person; this asks
|
|
76
|
+
* whether a code handshake can COMPLETE there, which additionally needs the
|
|
77
|
+
* reply to be heard. The copy this gates says "reply with it here", so a
|
|
78
|
+
* channel that can send into a DM but not receive from one would strand the
|
|
79
|
+
* requester holding a code they can never spend.
|
|
80
|
+
*
|
|
81
|
+
* Discord qualifies on both halves: its `dm`-marked route reaches the person,
|
|
82
|
+
* and `gateway/src/discord/admit.ts` admits DMs so the reply is heard.
|
|
83
|
+
*/
|
|
84
|
+
export function channelCanCompleteCodeHandshakeInDm(channel: string): boolean {
|
|
85
|
+
return channel === "slack" || channel === "discord";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve who a requester notice is addressed to on the callback-less route.
|
|
90
|
+
*
|
|
91
|
+
* A request's `requesterChatId` is wherever the request came from, and where
|
|
92
|
+
* the channel has a private route to a user id that room is one other people
|
|
93
|
+
* can read. "Your request was denied" posted into a community channel is worse
|
|
94
|
+
* than not sending it, so those channels address the requester's own user id
|
|
95
|
+
* instead and let their transport turn it into a DM.
|
|
96
|
+
*
|
|
97
|
+
* Telegram and WhatsApp fall through to the chat id because theirs already is
|
|
98
|
+
* the private one-to-one conversation. So does a request with no actor
|
|
99
|
+
* identity: there is nobody to open a DM with, and on the channels that need
|
|
100
|
+
* one the transport reports that as a delivery failure rather than posting to
|
|
101
|
+
* the room.
|
|
102
|
+
*/
|
|
103
|
+
export function resolveRequesterDeliveryTarget(params: {
|
|
104
|
+
channel: string;
|
|
105
|
+
requesterChatId: string;
|
|
106
|
+
requesterExternalUserId: string;
|
|
107
|
+
}): string {
|
|
108
|
+
const { channel, requesterChatId, requesterExternalUserId } = params;
|
|
109
|
+
if (channelDeliversToUserId(channel) && requesterExternalUserId) {
|
|
110
|
+
return requesterExternalUserId;
|
|
111
|
+
}
|
|
112
|
+
return requesterChatId;
|
|
113
|
+
}
|