@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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for persisting an answered `ask_question` prompt onto its tool_use
|
|
3
|
+
* block, so the question and the user's answer stay in the transcript instead
|
|
4
|
+
* of disappearing with the interactive card.
|
|
5
|
+
*
|
|
6
|
+
* Covers the write half of the round-trip:
|
|
7
|
+
* handleToolResult(event carrying answeredQuestion)
|
|
8
|
+
* → immediate `_answeredQuestion` stamp on the persisted row
|
|
9
|
+
* → state.toolAnsweredQuestions captures it
|
|
10
|
+
* → annotatePersistedAssistantMessage re-stamps at end of turn
|
|
11
|
+
*
|
|
12
|
+
* The read half (renderHistoryContent in handlers/shared.ts) lives in
|
|
13
|
+
* server-history-render.test.ts.
|
|
14
|
+
*/
|
|
15
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
let mockedRowContent = "";
|
|
18
|
+
const updates: Array<{ id: string; content: string }> = [];
|
|
19
|
+
|
|
20
|
+
mock.module("../persistence/conversation-crud.js", () => ({
|
|
21
|
+
setConversationProcessingStartedAt: () => {},
|
|
22
|
+
isConversationProcessing: () => false,
|
|
23
|
+
addMessage: () => ({ id: "mock-msg-id" }),
|
|
24
|
+
getMessageById: (id: string) =>
|
|
25
|
+
mockedRowContent ? { id, content: JSON.parse(mockedRowContent) } : null,
|
|
26
|
+
updateMessageContent: (id: string, content: string) => {
|
|
27
|
+
updates.push({ id, content });
|
|
28
|
+
mockedRowContent = content;
|
|
29
|
+
},
|
|
30
|
+
provenanceFromTrustContext: () => ({}),
|
|
31
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
mock.module("../persistence/llm-request-log-store.js", () => ({
|
|
35
|
+
recordRequestLog: () => {},
|
|
36
|
+
backfillMessageIdOnLogs: () => {},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
|
40
|
+
import type { AnsweredQuestion } from "../api/events/question-answered.js";
|
|
41
|
+
import type {
|
|
42
|
+
EventHandlerDeps,
|
|
43
|
+
EventHandlerState,
|
|
44
|
+
} from "../daemon/conversation-agent-loop-handlers.js";
|
|
45
|
+
import {
|
|
46
|
+
createEventHandlerState,
|
|
47
|
+
handleToolResult,
|
|
48
|
+
} from "../daemon/conversation-agent-loop-handlers.js";
|
|
49
|
+
|
|
50
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
const emitted: Array<Record<string, unknown>> = [];
|
|
53
|
+
|
|
54
|
+
function makeDeps(): EventHandlerDeps {
|
|
55
|
+
return {
|
|
56
|
+
ctx: {
|
|
57
|
+
conversationId: "test-conv",
|
|
58
|
+
provider: { name: "anthropic" },
|
|
59
|
+
streamThinking: false,
|
|
60
|
+
emitActivityState: () => {},
|
|
61
|
+
markWorkspaceTopLevelDirty: () => {},
|
|
62
|
+
currentTurnSurfaces: [],
|
|
63
|
+
} as unknown as EventHandlerDeps["ctx"],
|
|
64
|
+
onEvent: (event: unknown) => {
|
|
65
|
+
emitted.push(event as Record<string, unknown>);
|
|
66
|
+
},
|
|
67
|
+
reqId: "test-req",
|
|
68
|
+
isFirstMessage: false,
|
|
69
|
+
shouldGenerateTitle: false,
|
|
70
|
+
rlog: new Proxy({} as Record<string, unknown>, {
|
|
71
|
+
get: () => () => {},
|
|
72
|
+
}) as unknown as EventHandlerDeps["rlog"],
|
|
73
|
+
turnChannelContext: {
|
|
74
|
+
userMessageChannel: "vellum",
|
|
75
|
+
assistantMessageChannel: "vellum",
|
|
76
|
+
} as unknown as EventHandlerDeps["turnChannelContext"],
|
|
77
|
+
turnInterfaceContext: {
|
|
78
|
+
userMessageInterface: "web",
|
|
79
|
+
assistantMessageInterface: "web",
|
|
80
|
+
} as unknown as EventHandlerDeps["turnInterfaceContext"],
|
|
81
|
+
applyCompaction: async () => {},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Seed a turn whose assistant row holds `toolUseIds` tool_use blocks, all of
|
|
87
|
+
* them `ask_question` unless stated otherwise. Every id is registered as
|
|
88
|
+
* in-flight, so end-of-turn annotation only runs once each has a result.
|
|
89
|
+
*/
|
|
90
|
+
function setupState(toolUseIds: string[]): EventHandlerState {
|
|
91
|
+
const state = createEventHandlerState();
|
|
92
|
+
state.lastAssistantMessageId = "msg-1";
|
|
93
|
+
for (const id of toolUseIds) {
|
|
94
|
+
state.toolUseIdToName.set(id, "ask_question");
|
|
95
|
+
state.toolCallTimestamps.set(id, { startedAt: Date.now() });
|
|
96
|
+
state.currentTurnToolUseIds.push(id);
|
|
97
|
+
}
|
|
98
|
+
mockedRowContent = JSON.stringify(
|
|
99
|
+
toolUseIds.map((id) => ({
|
|
100
|
+
type: "tool_use",
|
|
101
|
+
id,
|
|
102
|
+
name: "ask_question",
|
|
103
|
+
input: { questions: [{ question: "Which Alice?", options: [] }] },
|
|
104
|
+
})),
|
|
105
|
+
);
|
|
106
|
+
return state;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function persistedToolUse(toolUseId: string): Record<string, unknown> {
|
|
110
|
+
const parsed = JSON.parse(mockedRowContent) as Array<Record<string, unknown>>;
|
|
111
|
+
const block = parsed.find((b) => b.type === "tool_use" && b.id === toolUseId);
|
|
112
|
+
if (!block) {
|
|
113
|
+
throw new Error(`tool_use block ${toolUseId} not found`);
|
|
114
|
+
}
|
|
115
|
+
return block;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const ANSWERED: AnsweredQuestion = {
|
|
119
|
+
requestId: "req-1",
|
|
120
|
+
questions: [
|
|
121
|
+
{
|
|
122
|
+
id: "q1",
|
|
123
|
+
question: "Which Alice?",
|
|
124
|
+
options: [
|
|
125
|
+
{ id: "alice_work", label: "Alice (work)" },
|
|
126
|
+
{ id: "alice_personal", label: "Alice (personal)" },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
responses: [{ questionId: "q1", decision: "option", optionId: "alice_work" }],
|
|
131
|
+
overall: "completed",
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
describe("answered ask_question persistence", () => {
|
|
137
|
+
beforeEach(() => {
|
|
138
|
+
updates.length = 0;
|
|
139
|
+
emitted.length = 0;
|
|
140
|
+
mockedRowContent = "";
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("stamps the answered record on the tool_use block and forwards it to clients", () => {
|
|
144
|
+
const state = setupState(["tu_q"]);
|
|
145
|
+
|
|
146
|
+
handleToolResult(state, makeDeps(), {
|
|
147
|
+
type: "tool_result",
|
|
148
|
+
toolUseId: "tu_q",
|
|
149
|
+
content: 'Question "Which Alice?" → Option: alice_work (Alice (work))',
|
|
150
|
+
isError: false,
|
|
151
|
+
answeredQuestion: ANSWERED,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(persistedToolUse("tu_q")._answeredQuestion).toEqual(ANSWERED);
|
|
155
|
+
const toolResult = emitted.find((e) => e.type === "tool_result");
|
|
156
|
+
expect(toolResult?.answeredQuestion).toEqual(ANSWERED);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("stamps immediately, before the turn's other tools have finished", () => {
|
|
160
|
+
// A batch where `ask_question` settles first: the user can switch away
|
|
161
|
+
// (forcing a history refetch) while the sibling tool still runs, so the
|
|
162
|
+
// answer must already be durable rather than waiting for end-of-turn
|
|
163
|
+
// annotation.
|
|
164
|
+
const state = setupState(["tu_q", "tu_slow"]);
|
|
165
|
+
|
|
166
|
+
handleToolResult(state, makeDeps(), {
|
|
167
|
+
type: "tool_result",
|
|
168
|
+
toolUseId: "tu_q",
|
|
169
|
+
content: "answered",
|
|
170
|
+
isError: false,
|
|
171
|
+
answeredQuestion: ANSWERED,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
expect(
|
|
175
|
+
state.toolCallTimestamps.get("tu_slow")?.completedAt,
|
|
176
|
+
).toBeUndefined();
|
|
177
|
+
expect(persistedToolUse("tu_q")._answeredQuestion).toEqual(ANSWERED);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("survives the end-of-turn annotation that re-stamps the row", () => {
|
|
181
|
+
const state = setupState(["tu_q"]);
|
|
182
|
+
const deps = makeDeps();
|
|
183
|
+
|
|
184
|
+
handleToolResult(state, deps, {
|
|
185
|
+
type: "tool_result",
|
|
186
|
+
toolUseId: "tu_q",
|
|
187
|
+
content: "answered",
|
|
188
|
+
isError: false,
|
|
189
|
+
answeredQuestion: ANSWERED,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// The result above completes the turn's only tool, so annotation has
|
|
193
|
+
// already run. It writes timing metadata alongside, and must not drop the
|
|
194
|
+
// answered record it did not author.
|
|
195
|
+
const block = persistedToolUse("tu_q");
|
|
196
|
+
expect(block._answeredQuestion).toEqual(ANSWERED);
|
|
197
|
+
expect(block._startedAt).toBeNumber();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("re-stamping the same record does not rewrite the row", () => {
|
|
201
|
+
const state = setupState(["tu_q"]);
|
|
202
|
+
const deps = makeDeps();
|
|
203
|
+
|
|
204
|
+
handleToolResult(state, deps, {
|
|
205
|
+
type: "tool_result",
|
|
206
|
+
toolUseId: "tu_q",
|
|
207
|
+
content: "answered",
|
|
208
|
+
isError: false,
|
|
209
|
+
answeredQuestion: ANSWERED,
|
|
210
|
+
});
|
|
211
|
+
// Early stamp + end-of-turn annotation.
|
|
212
|
+
expect(updates).toHaveLength(2);
|
|
213
|
+
|
|
214
|
+
handleToolResult(state, deps, {
|
|
215
|
+
type: "tool_result",
|
|
216
|
+
toolUseId: "tu_q",
|
|
217
|
+
content: "answered",
|
|
218
|
+
isError: false,
|
|
219
|
+
answeredQuestion: ANSWERED,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// The second result re-runs the early stamp; the identical requestId makes
|
|
223
|
+
// it a no-op, so the row is not rewritten.
|
|
224
|
+
expect(updates).toHaveLength(2);
|
|
225
|
+
expect(persistedToolUse("tu_q")._answeredQuestion).toEqual(ANSWERED);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("leaves the block unstamped when the result carries no answered record", () => {
|
|
229
|
+
const state = setupState(["tu_q"]);
|
|
230
|
+
|
|
231
|
+
handleToolResult(state, makeDeps(), {
|
|
232
|
+
type: "tool_result",
|
|
233
|
+
toolUseId: "tu_q",
|
|
234
|
+
content: "User did not respond within timeout",
|
|
235
|
+
isError: true,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(persistedToolUse("tu_q")._answeredQuestion).toBeUndefined();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
@@ -781,10 +781,11 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
781
781
|
expect(prevLast.cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
782
782
|
});
|
|
783
783
|
|
|
784
|
-
test("mutableLatestUserMessage:
|
|
785
|
-
//
|
|
786
|
-
//
|
|
787
|
-
//
|
|
784
|
+
test("mutableLatestUserMessage: a volatile turn start keeps the 5m TTL through its tool-use loop", async () => {
|
|
785
|
+
// The turn start is fixed for the rest of the turn, so it stays markable;
|
|
786
|
+
// but it is volatile ACROSS turns, so the long TTL would buy a write that
|
|
787
|
+
// can never be read back. Upgrading it here would also mark the same block
|
|
788
|
+
// at a second TTL, billing two writes for one reusable prefix.
|
|
788
789
|
const messages: Message[] = [
|
|
789
790
|
userMsg("Turn 1"),
|
|
790
791
|
assistantMsg("Response 1"),
|
|
@@ -804,14 +805,12 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
804
805
|
}>;
|
|
805
806
|
}>;
|
|
806
807
|
|
|
807
|
-
// Turn-start (Turn 2, index 2) still gets the 1h anchor — within a tool-use
|
|
808
|
-
// loop the block is fixed, so the volatile-latest flag must not move it.
|
|
809
808
|
const turn2 = sent[2];
|
|
810
809
|
const turn2Last = turn2.content[turn2.content.length - 1];
|
|
811
|
-
expect(turn2Last.cache_control).toEqual({ type: "ephemeral", ttl: "
|
|
810
|
+
expect(turn2Last.cache_control).toEqual({ type: "ephemeral", ttl: "5m" });
|
|
812
811
|
});
|
|
813
812
|
|
|
814
|
-
test("mutableLatestUserMessage: first turn
|
|
813
|
+
test("mutableLatestUserMessage: volatile first turn carries the 5m tail but no long-TTL anchor", async () => {
|
|
815
814
|
await provider.sendMessage([userMsg("First ever turn (volatile)")], {
|
|
816
815
|
config: { mutableLatestUserMessage: true },
|
|
817
816
|
});
|
|
@@ -823,9 +822,28 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
823
822
|
cache_control?: { type: string; ttl?: string };
|
|
824
823
|
}>;
|
|
825
824
|
}>;
|
|
826
|
-
//
|
|
827
|
-
//
|
|
828
|
-
//
|
|
825
|
+
// The only user message is volatile, so it gets no 1h anchor: those bytes
|
|
826
|
+
// do not recur next turn. It does get the 5m tail; the message is fixed
|
|
827
|
+
// within its own turn, so the tool-loop calls that follow read the system
|
|
828
|
+
// prompt, tools, and opening message back instead of re-writing them.
|
|
829
|
+
const lastBlock = sent[0].content[sent[0].content.length - 1];
|
|
830
|
+
expect(lastBlock.cache_control).toEqual({ type: "ephemeral", ttl: "5m" });
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
test("mutableLatestUserMessage: volatile first turn with disableTurnStartCache places no message breakpoint", async () => {
|
|
834
|
+
// One-shot call sites opt out of paying for a turn-start write; the 5m
|
|
835
|
+
// bridge lands on that same block, so it honors the same opt-out.
|
|
836
|
+
await provider.sendMessage([userMsg("One-shot (volatile)")], {
|
|
837
|
+
config: { mutableLatestUserMessage: true, disableTurnStartCache: true },
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
841
|
+
role: string;
|
|
842
|
+
content: Array<{
|
|
843
|
+
type: string;
|
|
844
|
+
cache_control?: { type: string; ttl?: string };
|
|
845
|
+
}>;
|
|
846
|
+
}>;
|
|
829
847
|
const lastBlock = sent[0].content[sent[0].content.length - 1];
|
|
830
848
|
expect(lastBlock.cache_control).toBeUndefined();
|
|
831
849
|
});
|
|
@@ -1410,9 +1428,11 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1410
1428
|
expect(sent[2].content[0].type).toBe("text");
|
|
1411
1429
|
});
|
|
1412
1430
|
|
|
1413
|
-
test("
|
|
1414
|
-
//
|
|
1415
|
-
//
|
|
1431
|
+
test("unanswered server_tool_use in the final assistant message is left intact for deferred execution", async () => {
|
|
1432
|
+
// When the final assistant message ends with an unanswered
|
|
1433
|
+
// server_tool_use, the API executes the search on this request (the
|
|
1434
|
+
// pause_turn / deferred-execution contract). The tail must go back
|
|
1435
|
+
// verbatim with no synthetic result, or the search is cancelled.
|
|
1416
1436
|
const messages: Message[] = [
|
|
1417
1437
|
userMsg("Search something"),
|
|
1418
1438
|
{
|
|
@@ -1434,12 +1454,11 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1434
1454
|
content: Array<{ type: string; tool_use_id?: string }>;
|
|
1435
1455
|
}>;
|
|
1436
1456
|
|
|
1437
|
-
// Original 2 messages,
|
|
1457
|
+
// Original 2 messages, assistant tail verbatim: no synthetic result
|
|
1438
1458
|
expect(sent).toHaveLength(2);
|
|
1439
1459
|
expect(sent[1].role).toBe("assistant");
|
|
1460
|
+
expect(sent[1].content).toHaveLength(1);
|
|
1440
1461
|
expect(sent[1].content[0].type).toBe("server_tool_use");
|
|
1441
|
-
expect(sent[1].content[1].type).toBe("web_search_tool_result");
|
|
1442
|
-
expect(sent[1].content[1].tool_use_id).toBe("srvtoolu_end");
|
|
1443
1462
|
});
|
|
1444
1463
|
|
|
1445
1464
|
test("server_tool_use with matching web_search_tool_result passes through unchanged", async () => {
|
|
@@ -1568,7 +1587,11 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1568
1587
|
expect(webSearchResults[0].tool_use_id).toBe("srvtoolu_search");
|
|
1569
1588
|
});
|
|
1570
1589
|
|
|
1571
|
-
test("mixed tool_use and server_tool_use
|
|
1590
|
+
test("mixed tool_use and deferred server_tool_use tail goes out verbatim", async () => {
|
|
1591
|
+
// The mixed parallel group is the deferred-execution trigger: the API
|
|
1592
|
+
// returns stop_reason tool_use without running the search, and runs it on
|
|
1593
|
+
// the next request when the client tool results come back with the
|
|
1594
|
+
// assistant message unchanged. No split, no synthetic result.
|
|
1572
1595
|
const messages: Message[] = [
|
|
1573
1596
|
userMsg("Do things"),
|
|
1574
1597
|
{
|
|
@@ -1583,7 +1606,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1583
1606
|
},
|
|
1584
1607
|
],
|
|
1585
1608
|
},
|
|
1586
|
-
// Only tu_a has a result
|
|
1609
|
+
// Only tu_a has a result; the deferred search has none yet
|
|
1587
1610
|
toolResultMsg("tu_a", "result A"),
|
|
1588
1611
|
];
|
|
1589
1612
|
await provider.sendMessage(messages);
|
|
@@ -1597,27 +1620,139 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1597
1620
|
}>;
|
|
1598
1621
|
}>;
|
|
1599
1622
|
|
|
1600
|
-
//
|
|
1601
|
-
|
|
1602
|
-
// Result: assistant(tool_use) → user(tool_result) → assistant(server_tool_use) → user(synthetic_continuation)
|
|
1623
|
+
// assistant(tool_use, server_tool_use) → user(tool_result), verbatim
|
|
1624
|
+
expect(sent).toHaveLength(3);
|
|
1603
1625
|
const assistantMsg = sent[1];
|
|
1604
1626
|
expect(assistantMsg.role).toBe("assistant");
|
|
1605
|
-
expect(assistantMsg.content
|
|
1627
|
+
expect(assistantMsg.content.map((b) => b.type)).toEqual([
|
|
1628
|
+
"tool_use",
|
|
1629
|
+
"server_tool_use",
|
|
1630
|
+
]);
|
|
1606
1631
|
|
|
1607
1632
|
const userAfterAssistant = sent[2];
|
|
1608
1633
|
expect(userAfterAssistant.role).toBe("user");
|
|
1609
|
-
// Only tool_result for tu_a — no synthetic web_search_tool_result
|
|
1610
1634
|
expect(userAfterAssistant.content[0]).toMatchObject({
|
|
1611
1635
|
type: "tool_result",
|
|
1612
1636
|
tool_use_id: "tu_a",
|
|
1613
1637
|
});
|
|
1614
1638
|
|
|
1615
|
-
//
|
|
1616
|
-
const
|
|
1617
|
-
expect(
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1639
|
+
// No synthetic web_search_tool_result anywhere; the API runs the search
|
|
1640
|
+
const allBlocks = sent.flatMap((m) => m.content);
|
|
1641
|
+
expect(
|
|
1642
|
+
allBlocks.filter((b) => b.type === "web_search_tool_result"),
|
|
1643
|
+
).toHaveLength(0);
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
test("deferred mixed heartbeat shape with text and multiple searches goes out verbatim", async () => {
|
|
1647
|
+
const messages: Message[] = [
|
|
1648
|
+
userMsg("Heartbeat: check the file and the news"),
|
|
1649
|
+
{
|
|
1650
|
+
role: "assistant",
|
|
1651
|
+
content: [
|
|
1652
|
+
{ type: "text", text: "Checking both." },
|
|
1653
|
+
{ type: "tool_use", id: "tu_read", name: "file_read", input: {} },
|
|
1654
|
+
{
|
|
1655
|
+
type: "server_tool_use",
|
|
1656
|
+
id: "srvtoolu_s1",
|
|
1657
|
+
name: "web_search",
|
|
1658
|
+
input: { query: "news one" },
|
|
1659
|
+
},
|
|
1660
|
+
{
|
|
1661
|
+
type: "server_tool_use",
|
|
1662
|
+
id: "srvtoolu_s2",
|
|
1663
|
+
name: "web_search",
|
|
1664
|
+
input: { query: "news two" },
|
|
1665
|
+
},
|
|
1666
|
+
],
|
|
1667
|
+
},
|
|
1668
|
+
toolResultMsg("tu_read", "file contents"),
|
|
1669
|
+
];
|
|
1670
|
+
await provider.sendMessage(messages);
|
|
1671
|
+
|
|
1672
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
1673
|
+
role: string;
|
|
1674
|
+
content: Array<{ type: string; id?: string; tool_use_id?: string }>;
|
|
1675
|
+
}>;
|
|
1676
|
+
|
|
1677
|
+
expect(sent).toHaveLength(3);
|
|
1678
|
+
expect(sent[1].content.map((b) => b.type)).toEqual([
|
|
1679
|
+
"text",
|
|
1680
|
+
"tool_use",
|
|
1681
|
+
"server_tool_use",
|
|
1682
|
+
"server_tool_use",
|
|
1683
|
+
]);
|
|
1684
|
+
const allBlocks = sent.flatMap((m) => m.content);
|
|
1685
|
+
expect(
|
|
1686
|
+
allBlocks.filter((b) => b.type === "web_search_tool_result"),
|
|
1687
|
+
).toHaveLength(0);
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1690
|
+
test("cross-message server tool pair from a completed deferred execution is preserved verbatim", async () => {
|
|
1691
|
+
// After a deferred search executes, history carries the split pair: the
|
|
1692
|
+
// server_tool_use in one assistant message and its web_search_tool_result
|
|
1693
|
+
// heading the next assistant message. Both sides must pass through
|
|
1694
|
+
// untouched: no synthetic result on the use, no text downgrade on the
|
|
1695
|
+
// result.
|
|
1696
|
+
const messages: Message[] = [
|
|
1697
|
+
userMsg("Check the file and the news"),
|
|
1698
|
+
{
|
|
1699
|
+
role: "assistant",
|
|
1700
|
+
content: [
|
|
1701
|
+
{ type: "text", text: "On it." },
|
|
1702
|
+
{ type: "tool_use", id: "tu_a", name: "file_read", input: {} },
|
|
1703
|
+
{
|
|
1704
|
+
type: "server_tool_use",
|
|
1705
|
+
id: "srvtoolu_deferred",
|
|
1706
|
+
name: "web_search",
|
|
1707
|
+
input: { query: "news" },
|
|
1708
|
+
},
|
|
1709
|
+
],
|
|
1710
|
+
},
|
|
1711
|
+
toolResultMsg("tu_a", "file contents"),
|
|
1712
|
+
{
|
|
1713
|
+
role: "assistant",
|
|
1714
|
+
content: [
|
|
1715
|
+
{
|
|
1716
|
+
type: "web_search_tool_result",
|
|
1717
|
+
tool_use_id: "srvtoolu_deferred",
|
|
1718
|
+
content: [
|
|
1719
|
+
{
|
|
1720
|
+
type: "web_search_result",
|
|
1721
|
+
url: "https://example.com",
|
|
1722
|
+
title: "Example",
|
|
1723
|
+
encrypted_content: "enc_deferred",
|
|
1724
|
+
},
|
|
1725
|
+
],
|
|
1726
|
+
},
|
|
1727
|
+
{ type: "text", text: "Here is the news." },
|
|
1728
|
+
],
|
|
1729
|
+
},
|
|
1730
|
+
userMsg("thanks, anything else?"),
|
|
1731
|
+
];
|
|
1732
|
+
await provider.sendMessage(messages);
|
|
1733
|
+
|
|
1734
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
1735
|
+
role: string;
|
|
1736
|
+
content: Array<{ type: string; id?: string; tool_use_id?: string }>;
|
|
1737
|
+
}>;
|
|
1738
|
+
|
|
1739
|
+
expect(sent).toHaveLength(5);
|
|
1740
|
+
expect(sent[1].content.map((b) => b.type)).toEqual([
|
|
1741
|
+
"text",
|
|
1742
|
+
"tool_use",
|
|
1743
|
+
"server_tool_use",
|
|
1744
|
+
]);
|
|
1745
|
+
expect(sent[3].content.map((b) => b.type)).toEqual([
|
|
1746
|
+
"web_search_tool_result",
|
|
1747
|
+
"text",
|
|
1748
|
+
]);
|
|
1749
|
+
// Exactly the original result block, still a result block
|
|
1750
|
+
const allBlocks = sent.flatMap((m) => m.content);
|
|
1751
|
+
const results = allBlocks.filter(
|
|
1752
|
+
(b) => b.type === "web_search_tool_result",
|
|
1753
|
+
);
|
|
1754
|
+
expect(results).toHaveLength(1);
|
|
1755
|
+
expect(results[0].tool_use_id).toBe("srvtoolu_deferred");
|
|
1621
1756
|
});
|
|
1622
1757
|
|
|
1623
1758
|
test("orphaned server_tool_use from interrupted stream gets repaired in multi-turn conversation", async () => {
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
21
21
|
|
|
22
|
+
import { asConversation } from "./helpers/mock-conversation.js";
|
|
23
|
+
|
|
22
24
|
// ---------------------------------------------------------------------------
|
|
23
25
|
// Module mocks (must be installed before importing the units under test)
|
|
24
26
|
// ---------------------------------------------------------------------------
|
|
@@ -102,12 +104,11 @@ const {
|
|
|
102
104
|
_setActiveAppControlSession,
|
|
103
105
|
} = await import("../daemon/host-app-control-proxy.js");
|
|
104
106
|
const { ROUTES } = await import("../runtime/routes/host-app-control-routes.js");
|
|
105
|
-
const { surfaceProxyResolver } =
|
|
107
|
+
const { createSurfaceMutex, surfaceProxyResolver } =
|
|
106
108
|
await import("../daemon/conversation-surfaces.js");
|
|
107
109
|
const { setConversation, clearConversations } =
|
|
108
110
|
await import("../daemon/conversation-registry.js");
|
|
109
|
-
type
|
|
110
|
-
import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
|
|
111
|
+
type Conversation = import("../daemon/conversation.js").Conversation;
|
|
111
112
|
|
|
112
113
|
const handleHostAppControlResult = ROUTES.find(
|
|
113
114
|
(r) => r.endpoint === "host-app-control-result",
|
|
@@ -123,15 +124,15 @@ const TINY_PNG_B64 = "iVBORw0KGgoAAAA";
|
|
|
123
124
|
// ---------------------------------------------------------------------------
|
|
124
125
|
|
|
125
126
|
/**
|
|
126
|
-
* Build a
|
|
127
|
+
* Build a Conversation with a real proxy attached. Only the
|
|
127
128
|
* fields the app-control branch reads are populated — see
|
|
128
129
|
* `cu-unified-flow.test.ts` for the analogous shape.
|
|
129
130
|
*/
|
|
130
131
|
function buildContext(
|
|
131
132
|
proxy: InstanceType<typeof HostAppControlProxy>,
|
|
132
133
|
conversationId = "test-conv",
|
|
133
|
-
):
|
|
134
|
-
return {
|
|
134
|
+
): Conversation {
|
|
135
|
+
return asConversation({
|
|
135
136
|
conversationId,
|
|
136
137
|
trustContext: {
|
|
137
138
|
sourceChannel: "vellum",
|
|
@@ -151,8 +152,8 @@ function buildContext(
|
|
|
151
152
|
enqueueMessage: () => ({ queued: false, requestId: "r1" }),
|
|
152
153
|
getQueueDepth: () => 0,
|
|
153
154
|
processMessage: async () => "",
|
|
154
|
-
withSurface:
|
|
155
|
-
};
|
|
155
|
+
withSurface: createSurfaceMutex(),
|
|
156
|
+
});
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
/**
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getReplayWindow,
|
|
12
12
|
} from "../runtime/assistant-stream-state.js";
|
|
13
13
|
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
14
|
+
import { registerHubClient } from "./helpers/hub-clients.js";
|
|
14
15
|
|
|
15
16
|
function makeEvent(
|
|
16
17
|
overrides: Partial<AssistantEventEnvelope> = {},
|
|
@@ -435,6 +436,53 @@ describe("AssistantEventHub — actorPrincipalId on ClientEntry", () => {
|
|
|
435
436
|
});
|
|
436
437
|
});
|
|
437
438
|
|
|
439
|
+
// ── ClientEntry desktop presence ─────────────────────────────────────────────
|
|
440
|
+
|
|
441
|
+
describe("AssistantEventHub client presence", () => {
|
|
442
|
+
test("records presence on a matching client", () => {
|
|
443
|
+
const hub = new AssistantEventHub();
|
|
444
|
+
registerHubClient({ hub, clientId: "mac-1" });
|
|
445
|
+
|
|
446
|
+
expect(hub.setClientPresence("mac-1", "active")).toBe(true);
|
|
447
|
+
|
|
448
|
+
const presence = hub.getClientById("mac-1")!.presence!;
|
|
449
|
+
expect(presence.state).toBe("active");
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
test("reportedAt advances on every report", async () => {
|
|
453
|
+
const hub = new AssistantEventHub();
|
|
454
|
+
registerHubClient({ hub, clientId: "mac-1" });
|
|
455
|
+
|
|
456
|
+
hub.setClientPresence("mac-1", "idle");
|
|
457
|
+
const first = hub.getClientById("mac-1")!.presence!;
|
|
458
|
+
|
|
459
|
+
await Bun.sleep(5);
|
|
460
|
+
hub.setClientPresence("mac-1", "idle");
|
|
461
|
+
const second = hub.getClientById("mac-1")!.presence!;
|
|
462
|
+
|
|
463
|
+
expect(second.reportedAt.getTime()).toBeGreaterThan(
|
|
464
|
+
first.reportedAt.getTime(),
|
|
465
|
+
);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("returns false for an unknown clientId", () => {
|
|
469
|
+
const hub = new AssistantEventHub();
|
|
470
|
+
registerHubClient({ hub, clientId: "mac-1" });
|
|
471
|
+
|
|
472
|
+
expect(hub.setClientPresence("does-not-exist", "active")).toBe(false);
|
|
473
|
+
expect(hub.getClientById("mac-1")?.presence).toBeUndefined();
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
test("a disposed client no longer accepts presence", () => {
|
|
477
|
+
const hub = new AssistantEventHub();
|
|
478
|
+
registerHubClient({ hub, clientId: "mac-1" });
|
|
479
|
+
|
|
480
|
+
expect(hub.disposeClient("mac-1")).toBe(1);
|
|
481
|
+
expect(hub.setClientPresence("mac-1", "active")).toBe(false);
|
|
482
|
+
expect(hub.getClientById("mac-1")).toBeUndefined();
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
438
486
|
// ── capabilityForMessageType — host-prefix routing ───────────────────────────
|
|
439
487
|
|
|
440
488
|
describe("capabilityForMessageType — host-prefix routing", () => {
|