@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
package/package.json
CHANGED
|
@@ -115,9 +115,9 @@ describe("upscaleTargetDimensions", () => {
|
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
describe("upscaleImageToMinimum", () => {
|
|
118
|
-
it("returns null when dimensions are unparseable", () => {
|
|
118
|
+
it("returns null when dimensions are unparseable", async () => {
|
|
119
119
|
expect(
|
|
120
|
-
upscaleImageToMinimum(
|
|
120
|
+
await upscaleImageToMinimum(
|
|
121
121
|
Buffer.from("not an image").toString("base64"),
|
|
122
122
|
"image/png",
|
|
123
123
|
),
|
|
@@ -126,12 +126,12 @@ describe("upscaleImageToMinimum", () => {
|
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
describe("optimizeImageForTransport", () => {
|
|
129
|
-
it("passes an undersized image through unchanged
|
|
129
|
+
it("passes an undersized image through unchanged: the minimum floor is rejection-path only", async () => {
|
|
130
130
|
// The floor is undocumented provider behavior, so pre-send transport
|
|
131
131
|
// never enforces it; only the image-recovery plugin reacts to an actual
|
|
132
132
|
// "Could not process image" rejection.
|
|
133
133
|
const tiny = makePngBase64(16, 14);
|
|
134
|
-
expect(optimizeImageForTransport(tiny, "image/png")).toEqual({
|
|
134
|
+
expect(await optimizeImageForTransport(tiny, "image/png")).toEqual({
|
|
135
135
|
data: tiny,
|
|
136
136
|
mediaType: "image/png",
|
|
137
137
|
});
|
|
@@ -1,36 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Verifies
|
|
3
|
-
* `SendMessageOptions.config
|
|
4
|
-
* user message carries a volatile `<memory>` block, so the loop sets
|
|
5
|
-
* `providerConfig.mutableLatestUserMessage` to tell the provider to anchor its
|
|
6
|
-
* long-TTL cache breakpoint on the most recent STABLE message instead.
|
|
2
|
+
* Verifies how the loop derives the `mutableLatestUserMessage` cache-anchor
|
|
3
|
+
* signal on `SendMessageOptions.config`.
|
|
7
4
|
*
|
|
8
|
-
* The
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* The signal means "the latest user message's bytes do not recur next turn", so
|
|
6
|
+
* it is set from the history actually being sent: only when the latest user
|
|
7
|
+
* message carries a memory-v3 `<memory_spotlight>` block, the one injected
|
|
8
|
+
* block that is strip-and-replaced on the tail every turn. Every other injected
|
|
9
|
+
* block is frozen into history and re-renders byte-identically, so turns
|
|
10
|
+
* without a spotlight keep a normal cache anchor even when memory-v3 is live.
|
|
11
|
+
*
|
|
12
|
+
* When no spotlight is present the field is omitted entirely (not `false`) so
|
|
13
|
+
* the wire stays byte-identical for conversations that never see one.
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
// AgentLoop reads the v3-live gate (`config.memory.v3.live`) via
|
|
17
|
-
// `isMemoryV3Live` to decide the cache-anchor signal; drive it per-test.
|
|
18
|
-
let memoryV3LiveSlot = false;
|
|
19
|
-
mock.module("../config/memory-v3-gate.js", () => ({
|
|
20
|
-
isMemoryV3Live: () => memoryV3LiveSlot,
|
|
21
|
-
isV3TierActive: () => memoryV3LiveSlot,
|
|
22
|
-
isMemoryEnabled: (config?: { memory?: { enabled?: boolean } }) =>
|
|
23
|
-
config?.memory?.enabled !== false,
|
|
24
|
-
usesConceptPageMemory: (memory?: {
|
|
25
|
-
enabled?: boolean;
|
|
26
|
-
v2?: { enabled?: boolean };
|
|
27
|
-
v3?: { live?: boolean };
|
|
28
|
-
}) =>
|
|
29
|
-
memory?.enabled !== false &&
|
|
30
|
-
(memory?.v3?.live === true || memory?.v2?.enabled === true),
|
|
31
|
-
}));
|
|
16
|
+
import { describe, expect, test } from "bun:test";
|
|
32
17
|
|
|
33
18
|
import { AgentLoop } from "../agent/loop.js";
|
|
19
|
+
import { wrapMemorySpotlightBlock } from "../plugins/defaults/memory/memory-marker.js";
|
|
34
20
|
import type {
|
|
35
21
|
Message,
|
|
36
22
|
Provider,
|
|
@@ -44,6 +30,15 @@ const userMessage: Message = {
|
|
|
44
30
|
content: [{ type: "text", text: "hi" }],
|
|
45
31
|
};
|
|
46
32
|
|
|
33
|
+
/** A user turn whose tail block is the ephemeral memory-v3 spotlight. */
|
|
34
|
+
const spotlightUserMessage: Message = {
|
|
35
|
+
role: "user",
|
|
36
|
+
content: [
|
|
37
|
+
{ type: "text", text: "hi" },
|
|
38
|
+
{ type: "text", text: wrapMemorySpotlightBlock("recalled: Alice's plan") },
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
|
|
47
42
|
function textResponse(text: string): ProviderResponse {
|
|
48
43
|
return {
|
|
49
44
|
content: [{ type: "text", text }],
|
|
@@ -87,40 +82,52 @@ function makeRecordingProvider(responses: ProviderResponse[]): {
|
|
|
87
82
|
return { provider, configs: () => configs };
|
|
88
83
|
}
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
const echoTool: ToolDefinition = {
|
|
86
|
+
name: "echo",
|
|
87
|
+
description: "Echo back the input",
|
|
88
|
+
input_schema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: { value: { type: "string" } },
|
|
91
|
+
},
|
|
92
|
+
};
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
describe("AgentLoop.run: mutableLatestUserMessage from spotlight presence", () => {
|
|
95
|
+
test("sets mutableLatestUserMessage when the latest user message carries a spotlight block", async () => {
|
|
96
|
+
// GIVEN a provider that records the config of each LLM call
|
|
97
|
+
const { provider, configs } = makeRecordingProvider([textResponse("done")]);
|
|
98
|
+
const loop = new AgentLoop({
|
|
99
|
+
provider,
|
|
100
|
+
systemPrompt: "system",
|
|
101
|
+
conversationId: "test-conversation",
|
|
102
|
+
config: { maxTokens: 1024 },
|
|
103
|
+
});
|
|
98
104
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
// WHEN the loop runs over a history whose tail carries the spotlight
|
|
106
|
+
await loop.run({
|
|
107
|
+
requestId: "test-request",
|
|
108
|
+
messages: [spotlightUserMessage],
|
|
109
|
+
onEvent: () => {},
|
|
110
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
111
|
+
callSite: "mainAgent",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// THEN the send carries the cache-anchor signal
|
|
115
|
+
expect(configs()).toHaveLength(1);
|
|
116
|
+
expect(configs()[0]?.mutableLatestUserMessage).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("omits mutableLatestUserMessage when the latest user message carries no spotlight", async () => {
|
|
120
|
+
// GIVEN a provider that records the config of each LLM call
|
|
121
|
+
const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
|
|
114
122
|
const loop = new AgentLoop({
|
|
115
|
-
provider
|
|
123
|
+
provider,
|
|
116
124
|
systemPrompt: "system",
|
|
117
125
|
conversationId: "test-conversation",
|
|
118
126
|
config: { maxTokens: 1024 },
|
|
119
|
-
tools: dummyTools,
|
|
120
|
-
toolExecutor: async () => ({ content: "ok", isError: false }),
|
|
121
127
|
});
|
|
122
128
|
|
|
123
|
-
// WHEN the loop runs
|
|
129
|
+
// WHEN the loop runs over a spotlight-free history: the shape memory-v3
|
|
130
|
+
// produces whenever it selects nothing to spotlight
|
|
124
131
|
await loop.run({
|
|
125
132
|
requestId: "test-request",
|
|
126
133
|
messages: [userMessage],
|
|
@@ -129,21 +136,16 @@ describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () =>
|
|
|
129
136
|
callSite: "mainAgent",
|
|
130
137
|
});
|
|
131
138
|
|
|
132
|
-
// THEN
|
|
133
|
-
expect(configs()).toHaveLength(
|
|
134
|
-
|
|
135
|
-
expect(cfg?.mutableLatestUserMessage).toBe(true);
|
|
136
|
-
}
|
|
139
|
+
// THEN the field is omitted entirely, not carried as false/undefined
|
|
140
|
+
expect(configs()).toHaveLength(1);
|
|
141
|
+
expect("mutableLatestUserMessage" in (configs()[0] ?? {})).toBe(false);
|
|
137
142
|
});
|
|
138
143
|
|
|
139
|
-
test("
|
|
140
|
-
// GIVEN
|
|
141
|
-
memoryV3LiveSlot = false;
|
|
142
|
-
|
|
143
|
-
// AND a provider that records the config of each LLM call
|
|
144
|
+
test("a text block merely opening with the spotlight tag is not treated as volatile", async () => {
|
|
145
|
+
// GIVEN a user message discussing the marker rather than carrying one
|
|
144
146
|
const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
|
|
145
147
|
const loop = new AgentLoop({
|
|
146
|
-
provider
|
|
148
|
+
provider,
|
|
147
149
|
systemPrompt: "system",
|
|
148
150
|
conversationId: "test-conversation",
|
|
149
151
|
config: { maxTokens: 1024 },
|
|
@@ -152,14 +154,59 @@ describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () =>
|
|
|
152
154
|
// WHEN the loop runs
|
|
153
155
|
await loop.run({
|
|
154
156
|
requestId: "test-request",
|
|
155
|
-
messages: [
|
|
157
|
+
messages: [
|
|
158
|
+
{
|
|
159
|
+
role: "user",
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: "<memory_spotlight>\nwhat does this tag do?",
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
156
168
|
onEvent: () => {},
|
|
157
169
|
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
158
170
|
callSite: "mainAgent",
|
|
159
171
|
});
|
|
160
172
|
|
|
161
|
-
// THEN the
|
|
173
|
+
// THEN the unterminated wrapper does not count: matching the full-wrapper
|
|
174
|
+
// requirement the per-turn strip applies
|
|
162
175
|
expect(configs()).toHaveLength(1);
|
|
163
176
|
expect("mutableLatestUserMessage" in (configs()[0] ?? {})).toBe(false);
|
|
164
177
|
});
|
|
178
|
+
|
|
179
|
+
test("the signal holds for every request in a tool loop", async () => {
|
|
180
|
+
// GIVEN a spotlight-bearing opening message and a tool round-trip
|
|
181
|
+
const { provider, configs } = makeRecordingProvider([
|
|
182
|
+
toolUseResponse("t1", "echo", { value: "first" }),
|
|
183
|
+
textResponse("done"),
|
|
184
|
+
]);
|
|
185
|
+
const loop = new AgentLoop({
|
|
186
|
+
provider,
|
|
187
|
+
systemPrompt: "system",
|
|
188
|
+
conversationId: "test-conversation",
|
|
189
|
+
config: { maxTokens: 1024 },
|
|
190
|
+
tools: [echoTool],
|
|
191
|
+
toolExecutor: async () => ({ content: "ok", isError: false }),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// WHEN the loop runs
|
|
195
|
+
await loop.run({
|
|
196
|
+
requestId: "test-request",
|
|
197
|
+
messages: [spotlightUserMessage],
|
|
198
|
+
onEvent: () => {},
|
|
199
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
200
|
+
callSite: "mainAgent",
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// THEN both calls report the turn as volatile. The signal describes the
|
|
204
|
+
// turn-starting message, not whichever user message happens to be last, so
|
|
205
|
+
// the trailing tool-result turn does not clear it. Holding it steady is
|
|
206
|
+
// what keeps the provider marking that one block at a single TTL for the
|
|
207
|
+
// whole turn instead of writing it twice.
|
|
208
|
+
expect(configs()).toHaveLength(2);
|
|
209
|
+
expect(configs()[0]?.mutableLatestUserMessage).toBe(true);
|
|
210
|
+
expect(configs()[1]?.mutableLatestUserMessage).toBe(true);
|
|
211
|
+
});
|
|
165
212
|
});
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Verifies that the per-turn `overrideProfile` plumbed into `AgentLoop.run()`
|
|
3
3
|
* surfaces on every `SendMessageOptions.config` the loop emits, and that
|
|
4
|
-
* `SubagentManager.spawn()` propagates an
|
|
5
|
-
*
|
|
4
|
+
* `SubagentManager.spawn()` propagates an `overrideProfile` set on its
|
|
5
|
+
* `SubagentConfig` into the subagent's `runAgentLoop()` call.
|
|
6
6
|
*
|
|
7
|
-
* Together these
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Together these establish where a pinned profile does and does not travel: it
|
|
8
|
+
* applies to every LLM call within the pinning conversation's own turn, and it
|
|
9
|
+
* stops at the spawn boundary. `executeSubagentSpawn` sets `overrideProfile`
|
|
10
|
+
* on the `SubagentConfig` only for a profile named explicitly on the spawn, so
|
|
11
|
+
* the propagation above carries a caller's choice rather than a parent's pin.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* With no `overrideProfile` set, the field is omitted from `providerConfig`
|
|
14
|
+
* rather than carrying `undefined`.
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import { describe, expect, mock, test } from "bun:test";
|
|
@@ -183,7 +184,7 @@ describe("AgentLoop.run — overrideProfile plumbing", () => {
|
|
|
183
184
|
});
|
|
184
185
|
});
|
|
185
186
|
|
|
186
|
-
// ── Subagent
|
|
187
|
+
// ── Subagent profile forwarding ──────────────────────────────────────────
|
|
187
188
|
|
|
188
189
|
// Capture the SubagentManager → Conversation handshake so we can verify the
|
|
189
190
|
// `overrideProfile` from `SubagentConfig` is forwarded into the spawned
|
|
@@ -329,7 +330,7 @@ seedLlmConfig();
|
|
|
329
330
|
|
|
330
331
|
import { SubagentManager } from "../subagent/manager.js";
|
|
331
332
|
|
|
332
|
-
describe("SubagentManager.spawn
|
|
333
|
+
describe("SubagentManager.spawn: forwards a configured overrideProfile", () => {
|
|
333
334
|
test("forwards overrideProfile from SubagentConfig into runAgentLoop", async () => {
|
|
334
335
|
capturedRunAgentLoopOptions.length = 0;
|
|
335
336
|
|
|
@@ -344,9 +345,9 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
|
|
|
344
345
|
() => {},
|
|
345
346
|
);
|
|
346
347
|
|
|
347
|
-
// The spawned subagent's runAgentLoop
|
|
348
|
-
//
|
|
349
|
-
// `
|
|
348
|
+
// The spawned subagent's runAgentLoop receives the `subagentSpawn`
|
|
349
|
+
// callSite and whatever `overrideProfile` the config carries, which
|
|
350
|
+
// `executeSubagentSpawn` sets only for an explicitly named profile.
|
|
350
351
|
expect(capturedRunAgentLoopOptions).toHaveLength(1);
|
|
351
352
|
const captured = capturedRunAgentLoopOptions[0];
|
|
352
353
|
expect(captured.callSite).toBe("subagentSpawn");
|
|
@@ -399,21 +400,18 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
|
|
|
399
400
|
});
|
|
400
401
|
});
|
|
401
402
|
|
|
402
|
-
// ── Nested subagent spawn
|
|
403
|
+
// ── Nested subagent spawn: no profile crosses the boundary ───────────────
|
|
403
404
|
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
// inheritance chain breaks at the second nesting level.
|
|
405
|
+
// When a subagent's agent loop is running under a turn profile, the executor
|
|
406
|
+
// closure plumbs that value into `ToolContext.overrideProfile`. That value
|
|
407
|
+
// describes the spawning turn, so `executeSubagentSpawn` leaves it behind: a
|
|
408
|
+
// child resolves the `subagentSpawn` call site on its own, and a forwarded
|
|
409
|
+
// override would both re-price the child and file its spend as a pin nobody
|
|
410
|
+
// set. These tests cover the nesting levels where a profile could otherwise
|
|
411
|
+
// compound down a chain of children.
|
|
412
412
|
|
|
413
413
|
mock.module("../persistence/conversation-crud.js", () => ({
|
|
414
414
|
setConversationProcessingStartedAt: () => {},
|
|
415
|
-
// Always return undefined for the row read so the test fails fast unless
|
|
416
|
-
// executeSubagentSpawn reads from context.overrideProfile first.
|
|
417
415
|
getConversationOverrideProfile: () => undefined,
|
|
418
416
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
419
417
|
}));
|
|
@@ -421,8 +419,8 @@ mock.module("../persistence/conversation-crud.js", () => ({
|
|
|
421
419
|
import { getSubagentManager } from "../subagent/index.js";
|
|
422
420
|
import { executeSubagentSpawn } from "../tools/subagent/spawn.js";
|
|
423
421
|
|
|
424
|
-
describe("executeSubagentSpawn
|
|
425
|
-
test("
|
|
422
|
+
describe("executeSubagentSpawn: the spawn boundary carries no profile", () => {
|
|
423
|
+
test("a subagent spawning a subagent does not pass its own turn profile down", async () => {
|
|
426
424
|
const manager = getSubagentManager();
|
|
427
425
|
const originalSpawn = manager.spawn.bind(manager);
|
|
428
426
|
let capturedConfig: Record<string, unknown> | undefined;
|
|
@@ -432,11 +430,11 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
432
430
|
};
|
|
433
431
|
|
|
434
432
|
try {
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
433
|
+
// The second-level spawn: this tool call happens inside the first
|
|
434
|
+
// subagent's tool context, where `overrideProfile` was populated by
|
|
435
|
+
// `runAgentLoopImpl` from its own turn snapshot. That snapshot describes
|
|
436
|
+
// the parent's turn, so it stops here rather than compounding down a
|
|
437
|
+
// chain of nested children.
|
|
440
438
|
const result = await executeSubagentSpawn(
|
|
441
439
|
{ label: "nested", objective: "do nested work" },
|
|
442
440
|
{
|
|
@@ -450,16 +448,13 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
450
448
|
|
|
451
449
|
expect(result.isError).toBe(false);
|
|
452
450
|
expect(capturedConfig).toBeDefined();
|
|
453
|
-
|
|
454
|
-
// SubagentManager.spawn forwards it into the nested subagent's
|
|
455
|
-
// runAgentLoop options — preserving inheritance across the chain.
|
|
456
|
-
expect(capturedConfig!.overrideProfile).toBe("fast");
|
|
451
|
+
expect("overrideProfile" in capturedConfig!).toBe(false);
|
|
457
452
|
} finally {
|
|
458
453
|
manager.spawn = originalSpawn;
|
|
459
454
|
}
|
|
460
455
|
});
|
|
461
456
|
|
|
462
|
-
test("
|
|
457
|
+
test("no override anywhere leaves the child on the subagentSpawn default", async () => {
|
|
463
458
|
const manager = getSubagentManager();
|
|
464
459
|
const originalSpawn = manager.spawn.bind(manager);
|
|
465
460
|
let capturedConfig: Record<string, unknown> | undefined;
|
|
@@ -481,22 +476,17 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
481
476
|
);
|
|
482
477
|
|
|
483
478
|
expect(capturedConfig).toBeDefined();
|
|
484
|
-
|
|
485
|
-
// the invoking call site's resolved default profile — here `mainAgent`
|
|
486
|
-
// with no active profile or pin, which resolves the catalog `balanced`
|
|
487
|
-
// default — so the subagent matches its invoker's model selection.
|
|
488
|
-
expect(capturedConfig!.overrideProfile).toBe("balanced");
|
|
479
|
+
expect("overrideProfile" in capturedConfig!).toBe(false);
|
|
489
480
|
} finally {
|
|
490
481
|
manager.spawn = originalSpawn;
|
|
491
482
|
}
|
|
492
483
|
});
|
|
493
484
|
|
|
494
|
-
test("
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
// explicit
|
|
498
|
-
//
|
|
499
|
-
// resolution, overriding the user's explicit call-site choice.
|
|
485
|
+
test("a workspace active profile does not reach the child, pinned or not", async () => {
|
|
486
|
+
// The child resolves `llm.callSites.subagentSpawn` on its own, so the
|
|
487
|
+
// spawn must forward nothing either way: a forwarded override would
|
|
488
|
+
// outrank an explicit call-site pin under single-winner resolution and
|
|
489
|
+
// overturn the operator's choice.
|
|
500
490
|
const manager = getSubagentManager();
|
|
501
491
|
const originalSpawn = manager.spawn.bind(manager);
|
|
502
492
|
let capturedConfig: Record<string, unknown> | undefined;
|
|
@@ -517,7 +507,7 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
517
507
|
{ label: "nested", objective: "do nested work" },
|
|
518
508
|
baseContext,
|
|
519
509
|
);
|
|
520
|
-
expect(capturedConfig
|
|
510
|
+
expect("overrideProfile" in capturedConfig!).toBe(false);
|
|
521
511
|
|
|
522
512
|
seedLlmConfig({
|
|
523
513
|
activeProfile: "fast",
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime proxy bills delegated work off `X-Vellum-Subagent-Role` and
|
|
3
|
+
* `X-Vellum-Subagent-Spawn-Mode`. `RetryProvider` resolves both from
|
|
4
|
+
* `config.conversationId`, so the headers only ever materialize in production
|
|
5
|
+
* if `AgentLoop` stamps its own conversation id onto the provider config it
|
|
6
|
+
* sends. These tests drive a real `AgentLoop` through a real `RetryProvider`
|
|
7
|
+
* so a config field dropped at the loop can never pass silently.
|
|
8
|
+
*
|
|
9
|
+
* Subagents run their turns through `ConversationRunner.runAgentLoop` with
|
|
10
|
+
* `callSite: "subagentSpawn"`, which is the shape reproduced here.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
14
|
+
|
|
15
|
+
import { AgentLoop } from "../agent/loop.js";
|
|
16
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
17
|
+
import { rawRun } from "../persistence/raw-query.js";
|
|
18
|
+
import { RetryProvider } from "../providers/retry.js";
|
|
19
|
+
import type {
|
|
20
|
+
Message,
|
|
21
|
+
Provider,
|
|
22
|
+
ProviderResponse,
|
|
23
|
+
SendMessageOptions,
|
|
24
|
+
} from "../providers/types.js";
|
|
25
|
+
import { resetSubagentAttributionCacheForTests } from "../usage/subagent-attribution.js";
|
|
26
|
+
import { setConfig } from "./helpers/set-config.js";
|
|
27
|
+
|
|
28
|
+
const userMessage: Message = {
|
|
29
|
+
role: "user",
|
|
30
|
+
content: [{ type: "text", text: "hi" }],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Stands in for the transport underneath `RetryProvider`, recording the config
|
|
35
|
+
* it is handed. That config is what would go on the wire, so it is also where
|
|
36
|
+
* the resolved billing headers land.
|
|
37
|
+
*/
|
|
38
|
+
function makeRecordingProvider(name: string): {
|
|
39
|
+
provider: Provider;
|
|
40
|
+
configs: () => Array<Record<string, unknown>>;
|
|
41
|
+
} {
|
|
42
|
+
const configs: Array<Record<string, unknown>> = [];
|
|
43
|
+
const provider: Provider = {
|
|
44
|
+
name,
|
|
45
|
+
async sendMessage(
|
|
46
|
+
_messages: Message[],
|
|
47
|
+
options?: SendMessageOptions,
|
|
48
|
+
): Promise<ProviderResponse> {
|
|
49
|
+
configs.push((options?.config ?? {}) as Record<string, unknown>);
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: "text", text: "ok" }],
|
|
52
|
+
model: "gpt-sub",
|
|
53
|
+
usage: { inputTokens: 1, outputTokens: 1 },
|
|
54
|
+
stopReason: "end_turn",
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
return { provider, configs: () => configs };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function seedConversation(
|
|
62
|
+
id: string,
|
|
63
|
+
subagentRole: string | null,
|
|
64
|
+
subagentSpawnMode: string | null,
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
await initializeDb();
|
|
67
|
+
resetSubagentAttributionCacheForTests();
|
|
68
|
+
rawRun(
|
|
69
|
+
"test:seedSubagentConversation",
|
|
70
|
+
`INSERT OR REPLACE INTO conversations (id, conversation_type, created_at, updated_at, subagent_role, subagent_spawn_mode)
|
|
71
|
+
VALUES (?, 'background', 1000, 1000, ?, ?)`,
|
|
72
|
+
id,
|
|
73
|
+
subagentRole,
|
|
74
|
+
subagentSpawnMode,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Runs one loop turn the way a subagent's conversation runner does. */
|
|
79
|
+
async function runSubagentTurn(
|
|
80
|
+
provider: Provider,
|
|
81
|
+
conversationId: string,
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
const loop = new AgentLoop({
|
|
84
|
+
provider,
|
|
85
|
+
systemPrompt: "system",
|
|
86
|
+
conversationId,
|
|
87
|
+
config: { maxTokens: 1024 },
|
|
88
|
+
});
|
|
89
|
+
await loop.run({
|
|
90
|
+
requestId: "test-request",
|
|
91
|
+
messages: [userMessage],
|
|
92
|
+
onEvent: () => {},
|
|
93
|
+
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
94
|
+
callSite: "subagentSpawn",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
describe("AgentLoop subagent billing-header attribution", () => {
|
|
99
|
+
beforeEach(() => {
|
|
100
|
+
setConfig("llm", {
|
|
101
|
+
callSites: { subagentSpawn: { provider: "openai", model: "gpt-sub" } },
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("forwards the spawning role and mode on a subagent conversation's turn", async () => {
|
|
106
|
+
// GIVEN a conversation stamped as an advisor-consult subagent
|
|
107
|
+
await seedConversation("conv-loop-advisor", "advisor", "advisor_consult");
|
|
108
|
+
|
|
109
|
+
// AND a managed-proxy-shaped provider stack (headers forwarded)
|
|
110
|
+
const { provider, configs } = makeRecordingProvider("openai");
|
|
111
|
+
const wrapped = new RetryProvider(provider, {
|
|
112
|
+
forwardUsageAttributionHeaders: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// WHEN that subagent's loop runs a turn
|
|
116
|
+
await runSubagentTurn(wrapped, "conv-loop-advisor");
|
|
117
|
+
|
|
118
|
+
// THEN the transport sees the delegated-work attribution
|
|
119
|
+
expect(configs()).toHaveLength(1);
|
|
120
|
+
const headers = configs()[0]?.usageAttributionHeaders as Record<
|
|
121
|
+
string,
|
|
122
|
+
string
|
|
123
|
+
>;
|
|
124
|
+
expect(headers["X-Vellum-Subagent-Role"]).toBe("advisor");
|
|
125
|
+
expect(headers["X-Vellum-Subagent-Spawn-Mode"]).toBe("advisor_consult");
|
|
126
|
+
expect(headers["X-Vellum-LLM-Call-Site"]).toBe("subagentSpawn");
|
|
127
|
+
|
|
128
|
+
// AND the routing-time id itself never reaches the wire config
|
|
129
|
+
expect(configs()[0]?.conversationId).toBeUndefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("omits the subagent headers on a turn for a plain conversation", async () => {
|
|
133
|
+
// GIVEN a conversation that was not spawned as a subagent
|
|
134
|
+
await seedConversation("conv-loop-plain", null, null);
|
|
135
|
+
|
|
136
|
+
const { provider, configs } = makeRecordingProvider("openai");
|
|
137
|
+
const wrapped = new RetryProvider(provider, {
|
|
138
|
+
forwardUsageAttributionHeaders: true,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// WHEN its loop runs a turn
|
|
142
|
+
await runSubagentTurn(wrapped, "conv-loop-plain");
|
|
143
|
+
|
|
144
|
+
// THEN the base attribution is present without the delegated-work fields
|
|
145
|
+
const headers = configs()[0]?.usageAttributionHeaders as Record<
|
|
146
|
+
string,
|
|
147
|
+
string
|
|
148
|
+
>;
|
|
149
|
+
expect(headers["X-Vellum-LLM-Call-Site"]).toBe("subagentSpawn");
|
|
150
|
+
expect(headers["X-Vellum-Subagent-Role"]).toBeUndefined();
|
|
151
|
+
expect(headers["X-Vellum-Subagent-Spawn-Mode"]).toBeUndefined();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("stamps the conversation id on the provider config it sends", async () => {
|
|
155
|
+
// GIVEN a bare recording provider (no `RetryProvider` to consume the field)
|
|
156
|
+
const { provider, configs } = makeRecordingProvider("openai");
|
|
157
|
+
|
|
158
|
+
// WHEN a loop with a conversation id runs a call-site turn
|
|
159
|
+
await runSubagentTurn(provider, "conv-loop-raw");
|
|
160
|
+
|
|
161
|
+
// THEN the id the header lookup depends on is on the config, alongside the
|
|
162
|
+
// mix-expansion seed
|
|
163
|
+
expect(configs()[0]?.conversationId).toBe("conv-loop-raw");
|
|
164
|
+
expect(configs()[0]?.selectionSeed).toBe("conv-loop-raw");
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -88,6 +88,7 @@ function makeTarget(): Conversation {
|
|
|
88
88
|
getTurnChannelContext: () => null,
|
|
89
89
|
getTurnInterfaceContext: () => null,
|
|
90
90
|
drainQueue: async () => {},
|
|
91
|
+
kickDrainQueue: async () => {},
|
|
91
92
|
// Pre-run auto-compaction gate — no-op for these tests.
|
|
92
93
|
maybeCompact: async () => null,
|
|
93
94
|
};
|
|
@@ -100,6 +100,7 @@ function makeTarget(): {
|
|
|
100
100
|
getTurnChannelContext: () => null,
|
|
101
101
|
getTurnInterfaceContext: () => null,
|
|
102
102
|
drainQueue: async () => {},
|
|
103
|
+
kickDrainQueue: async () => {},
|
|
103
104
|
// Pre-run auto-compaction gate — no-op for these tests.
|
|
104
105
|
maybeCompact: async () => null,
|
|
105
106
|
buildCurrentSystemPrompt: () => "mock-system-prompt",
|