@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
|
@@ -318,6 +318,8 @@ const deleteMessageByIdMock = mock(() => ({
|
|
|
318
318
|
deletedSummaryIds: [],
|
|
319
319
|
}));
|
|
320
320
|
const reserveMessageMock = mock(async () => ({ id: "msg-reserve" }));
|
|
321
|
+
/** Persisted rows the loop reads back. Empty unless a test seeds one. */
|
|
322
|
+
let mockStoredMessages: unknown[] = [];
|
|
321
323
|
const updateMessageContentMock = mock(() => {});
|
|
322
324
|
const finalizeMessageContentMock = mock(() => {});
|
|
323
325
|
const addMessageMock = mock(() => ({ id: "mock-msg-id" }));
|
|
@@ -328,7 +330,7 @@ mock.module("../persistence/conversation-crud.js", () => ({
|
|
|
328
330
|
updateConversationUsage: () => {},
|
|
329
331
|
updateMessageMetadata: updateMessageMetadataMock,
|
|
330
332
|
setConversationHistoryStrippedAt: setConversationHistoryStrippedAtMock,
|
|
331
|
-
getMessages: () =>
|
|
333
|
+
getMessages: () => mockStoredMessages,
|
|
332
334
|
getConversation: () => mockConversationRow,
|
|
333
335
|
provenanceFromTrustContext: () => ({
|
|
334
336
|
source: "user",
|
|
@@ -673,6 +675,7 @@ import {
|
|
|
673
675
|
applyCompactionResult,
|
|
674
676
|
runAgentLoopImpl,
|
|
675
677
|
} from "../daemon/conversation-agent-loop.js";
|
|
678
|
+
import { settleTurnTail } from "../daemon/turn-tail-chain.js";
|
|
676
679
|
import {
|
|
677
680
|
createMockProvider,
|
|
678
681
|
type ScriptedResponse,
|
|
@@ -780,6 +783,7 @@ function makeCtx(
|
|
|
780
783
|
channelCapabilities: undefined,
|
|
781
784
|
commandIntent: undefined,
|
|
782
785
|
trustContext: undefined,
|
|
786
|
+
toolsDisabledDepth: 0,
|
|
783
787
|
|
|
784
788
|
allowedToolNames: undefined,
|
|
785
789
|
preactivatedSkillIds: undefined,
|
|
@@ -862,6 +866,24 @@ function makeCtx(
|
|
|
862
866
|
return ctx;
|
|
863
867
|
}
|
|
864
868
|
|
|
869
|
+
/**
|
|
870
|
+
* What `classifyConversationError` returns for a daily-credit-limit 402 (see
|
|
871
|
+
* `dailyLimitClassification` in conversation-error.ts). Its `userMessage` is
|
|
872
|
+
* banner voice; the loop swaps in {@link DAILY_LIMIT_ASSISTANT_REPLY} for the
|
|
873
|
+
* transcript row.
|
|
874
|
+
*/
|
|
875
|
+
const DAILY_LIMIT_CLASSIFICATION = {
|
|
876
|
+
code: "PROVIDER_BILLING",
|
|
877
|
+
userMessage:
|
|
878
|
+
"You've hit your daily credit limit. Raise the limit in Billing settings to keep going today.",
|
|
879
|
+
retryable: false,
|
|
880
|
+
errorCategory: "daily_limit_reached",
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
/** Mirrors `DAILY_LIMIT_REACHED_ASSISTANT_REPLY` in conversation-agent-loop.ts. */
|
|
884
|
+
const DAILY_LIMIT_ASSISTANT_REPLY =
|
|
885
|
+
"I had to stop because you hit your daily credit limit. Raise the limit in Settings → Billing and we can pick up where we left off, or I can continue once it resets.";
|
|
886
|
+
|
|
865
887
|
type CompactionResult = Parameters<typeof applyCompactionResult>[1];
|
|
866
888
|
|
|
867
889
|
function makeCompactionResult(
|
|
@@ -931,7 +953,9 @@ beforeEach(() => {
|
|
|
931
953
|
getSlackCompactionWatermarkForPrefixMock.mockClear();
|
|
932
954
|
deleteMessageByIdMock.mockClear();
|
|
933
955
|
reserveMessageMock.mockClear();
|
|
956
|
+
mockStoredMessages = [];
|
|
934
957
|
updateMessageContentMock.mockClear();
|
|
958
|
+
updateMessageContentMock.mockImplementation(() => {});
|
|
935
959
|
finalizeMessageContentMock.mockClear();
|
|
936
960
|
rmSync(inflightDir(), { recursive: true, force: true });
|
|
937
961
|
addMessageMock.mockClear();
|
|
@@ -1183,6 +1207,92 @@ describe("session-agent-loop", () => {
|
|
|
1183
1207
|
runAgentLoopImpl(ctx, "hello", "msg-1", () => {}),
|
|
1184
1208
|
).rejects.toThrow("runAgentLoop called without prior persistUserMessage");
|
|
1185
1209
|
});
|
|
1210
|
+
|
|
1211
|
+
test("defers workspace Git work when tools are disabled", async () => {
|
|
1212
|
+
const ensureInitialized = mock(async () => {});
|
|
1213
|
+
const commitTurnChanges = mock(async () => {});
|
|
1214
|
+
const ctx = makeCtx({
|
|
1215
|
+
toolsDisabledDepth: 1,
|
|
1216
|
+
getWorkspaceGitService: () => ({ ensureInitialized }),
|
|
1217
|
+
commitTurnChanges,
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
1221
|
+
|
|
1222
|
+
expect(ensureInitialized).not.toHaveBeenCalled();
|
|
1223
|
+
expect(commitTurnChanges).not.toHaveBeenCalled();
|
|
1224
|
+
|
|
1225
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
1226
|
+
|
|
1227
|
+
expect(commitTurnChanges).toHaveBeenCalledTimes(1);
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
test("leaves a deferred commit to an immediate follow-up turn", async () => {
|
|
1231
|
+
const commitTurnChanges = mock(async () => {});
|
|
1232
|
+
const ctx = makeCtx({
|
|
1233
|
+
toolsDisabledDepth: 1,
|
|
1234
|
+
commitTurnChanges,
|
|
1235
|
+
});
|
|
1236
|
+
|
|
1237
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
1238
|
+
ctx.setProcessing(true);
|
|
1239
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
1240
|
+
|
|
1241
|
+
expect(commitTurnChanges).not.toHaveBeenCalled();
|
|
1242
|
+
ctx.setProcessing(false);
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
test("initializes workspace Git before hooks when tools can run", async () => {
|
|
1246
|
+
let initialized = false;
|
|
1247
|
+
const ensureInitialized = mock(async () => {
|
|
1248
|
+
initialized = true;
|
|
1249
|
+
});
|
|
1250
|
+
const commitTurnChanges = mock(async () => {});
|
|
1251
|
+
const initializedDuringHook: boolean[] = [];
|
|
1252
|
+
registerPlugin({
|
|
1253
|
+
manifest: {
|
|
1254
|
+
name: "test-workspace-git-ready-before-hook",
|
|
1255
|
+
version: "1.0.0",
|
|
1256
|
+
},
|
|
1257
|
+
hooks: {
|
|
1258
|
+
"user-prompt-submit": async () => {
|
|
1259
|
+
initializedDuringHook.push(initialized);
|
|
1260
|
+
},
|
|
1261
|
+
},
|
|
1262
|
+
});
|
|
1263
|
+
const ctx = makeCtx({
|
|
1264
|
+
getWorkspaceGitService: () => ({ ensureInitialized }),
|
|
1265
|
+
commitTurnChanges,
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
1269
|
+
|
|
1270
|
+
expect(ensureInitialized).toHaveBeenCalledTimes(1);
|
|
1271
|
+
expect(commitTurnChanges).toHaveBeenCalledTimes(1);
|
|
1272
|
+
expect(initializedDuringHook).toEqual([true]);
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
test("continues a tool-capable turn when workspace Git initialization fails", async () => {
|
|
1276
|
+
const ensureInitialized = mock(async () => {
|
|
1277
|
+
throw new Error("simulated Git initialization failure");
|
|
1278
|
+
});
|
|
1279
|
+
const events: AssistantEvent[] = [];
|
|
1280
|
+
const ctx = makeCtx({
|
|
1281
|
+
getWorkspaceGitService: () => ({ ensureInitialized }),
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", (event) =>
|
|
1285
|
+
events.push(event),
|
|
1286
|
+
);
|
|
1287
|
+
|
|
1288
|
+
expect(ensureInitialized).toHaveBeenCalledTimes(1);
|
|
1289
|
+
expect(events.some((event) => event.type === "message_complete")).toBe(
|
|
1290
|
+
true,
|
|
1291
|
+
);
|
|
1292
|
+
expect(events.some((event) => event.type === "conversation_error")).toBe(
|
|
1293
|
+
false,
|
|
1294
|
+
);
|
|
1295
|
+
});
|
|
1186
1296
|
});
|
|
1187
1297
|
|
|
1188
1298
|
describe("disk pressure injection context", () => {
|
|
@@ -1971,6 +2081,76 @@ describe("session-agent-loop", () => {
|
|
|
1971
2081
|
expect(ctx.pendingSurfaceActions.has("stale-form-1")).toBe(false);
|
|
1972
2082
|
});
|
|
1973
2083
|
|
|
2084
|
+
test("withholds the dismissal event and keeps the surface pending when its persisted write fails", async () => {
|
|
2085
|
+
const events: AssistantEvent[] = [];
|
|
2086
|
+
|
|
2087
|
+
const ctx = makeCtx();
|
|
2088
|
+
ctx.pendingSurfaceActions.set("stale-table-2", { surfaceType: "table" });
|
|
2089
|
+
mockStoredMessages = [
|
|
2090
|
+
{
|
|
2091
|
+
id: "msg-with-stale-surface",
|
|
2092
|
+
conversationId: "conv-1",
|
|
2093
|
+
role: "assistant",
|
|
2094
|
+
content: [{ type: "ui_surface", surfaceId: "stale-table-2" }],
|
|
2095
|
+
createdAt: 0,
|
|
2096
|
+
metadata: null,
|
|
2097
|
+
},
|
|
2098
|
+
];
|
|
2099
|
+
updateMessageContentMock.mockImplementationOnce(() => {
|
|
2100
|
+
throw new Error("database is locked");
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg), {
|
|
2104
|
+
isUserMessage: true,
|
|
2105
|
+
});
|
|
2106
|
+
|
|
2107
|
+
// Announcing a dismissal the DB still holds as pending leaves the client
|
|
2108
|
+
// showing a card the next history reseed reverts.
|
|
2109
|
+
expect(
|
|
2110
|
+
events.filter((e) => e.type === "ui_surface_complete"),
|
|
2111
|
+
).toHaveLength(0);
|
|
2112
|
+
// The card stays live on the client, so the daemon must keep treating it
|
|
2113
|
+
// as pending; the sweep retries on the next user message.
|
|
2114
|
+
expect(ctx.pendingSurfaceActions.has("stale-table-2")).toBe(true);
|
|
2115
|
+
});
|
|
2116
|
+
|
|
2117
|
+
test("dismisses the remaining surfaces when one write fails", async () => {
|
|
2118
|
+
const events: AssistantEvent[] = [];
|
|
2119
|
+
|
|
2120
|
+
const ctx = makeCtx();
|
|
2121
|
+
ctx.pendingSurfaceActions.set("stale-table-3", { surfaceType: "table" });
|
|
2122
|
+
ctx.pendingSurfaceActions.set("stale-form-3", { surfaceType: "form" });
|
|
2123
|
+
mockStoredMessages = [
|
|
2124
|
+
{
|
|
2125
|
+
id: "msg-with-stale-surfaces",
|
|
2126
|
+
conversationId: "conv-1",
|
|
2127
|
+
role: "assistant",
|
|
2128
|
+
content: [
|
|
2129
|
+
{ type: "ui_surface", surfaceId: "stale-table-3" },
|
|
2130
|
+
{ type: "ui_surface", surfaceId: "stale-form-3" },
|
|
2131
|
+
],
|
|
2132
|
+
createdAt: 0,
|
|
2133
|
+
metadata: null,
|
|
2134
|
+
},
|
|
2135
|
+
];
|
|
2136
|
+
updateMessageContentMock.mockImplementationOnce(() => {
|
|
2137
|
+
throw new Error("database is locked");
|
|
2138
|
+
});
|
|
2139
|
+
|
|
2140
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg), {
|
|
2141
|
+
isUserMessage: true,
|
|
2142
|
+
});
|
|
2143
|
+
|
|
2144
|
+
// Retaining the failed entry must not stall the sweep: deleting the
|
|
2145
|
+
// current key while iterating the map leaves later keys reachable.
|
|
2146
|
+
const completed = events
|
|
2147
|
+
.filter((e) => e.type === "ui_surface_complete")
|
|
2148
|
+
.map((e) => (e as { surfaceId: string }).surfaceId);
|
|
2149
|
+
expect(completed).toEqual(["stale-form-3"]);
|
|
2150
|
+
expect(ctx.pendingSurfaceActions.has("stale-table-3")).toBe(true);
|
|
2151
|
+
expect(ctx.pendingSurfaceActions.has("stale-form-3")).toBe(false);
|
|
2152
|
+
});
|
|
2153
|
+
|
|
1974
2154
|
test("does not auto-complete surfaces when request is a surface action", async () => {
|
|
1975
2155
|
const events: AssistantEvent[] = [];
|
|
1976
2156
|
|
|
@@ -2277,6 +2457,134 @@ describe("session-agent-loop", () => {
|
|
|
2277
2457
|
});
|
|
2278
2458
|
});
|
|
2279
2459
|
|
|
2460
|
+
test("persists assistant-voice daily-limit copy on a first-call rejection", async () => {
|
|
2461
|
+
mockConversationErrorClassification = DAILY_LIMIT_CLASSIFICATION;
|
|
2462
|
+
|
|
2463
|
+
const ctx = makeCtx({
|
|
2464
|
+
loopProvider: {
|
|
2465
|
+
name: "mock-provider",
|
|
2466
|
+
async sendMessage() {
|
|
2467
|
+
throw new Error("Payment Required");
|
|
2468
|
+
},
|
|
2469
|
+
} as unknown as Provider,
|
|
2470
|
+
});
|
|
2471
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
2472
|
+
|
|
2473
|
+
expect(addMessageMock).toHaveBeenCalledTimes(1);
|
|
2474
|
+
const addCall = addMessageMock.mock.calls[0] as unknown as [
|
|
2475
|
+
string,
|
|
2476
|
+
string,
|
|
2477
|
+
string,
|
|
2478
|
+
{ metadata?: Record<string, unknown> },
|
|
2479
|
+
];
|
|
2480
|
+
expect(addCall[1]).toBe("assistant");
|
|
2481
|
+
// Like the credits-exhausted row, the daily-limit row re-enters LLM
|
|
2482
|
+
// history and renders as assistant speech, so it carries first-person
|
|
2483
|
+
// copy rather than the banner-voice classification text.
|
|
2484
|
+
const persistedBlocks = JSON.parse(addCall[2]) as Array<{
|
|
2485
|
+
type: string;
|
|
2486
|
+
text: string;
|
|
2487
|
+
}>;
|
|
2488
|
+
expect(persistedBlocks[0]?.text).toBe(DAILY_LIMIT_ASSISTANT_REPLY);
|
|
2489
|
+
expect(addCall[3]?.metadata).toMatchObject({
|
|
2490
|
+
messageKind: "provider_error",
|
|
2491
|
+
providerErrorCode: "PROVIDER_BILLING",
|
|
2492
|
+
providerErrorCategory: "daily_limit_reached",
|
|
2493
|
+
});
|
|
2494
|
+
});
|
|
2495
|
+
|
|
2496
|
+
test("persists the synthetic error row when a daily-limit error kills the run mid-turn", async () => {
|
|
2497
|
+
// Mid-turn shape: the run already carries assistant `tool_use` messages
|
|
2498
|
+
// from the completed tool round-trip, and only the failing follow-up
|
|
2499
|
+
// call lacks a reply. The synthetic error row must persist for this
|
|
2500
|
+
// shape too, so a reload explains why the assistant stopped instead of
|
|
2501
|
+
// ending on a bare tool call.
|
|
2502
|
+
reserveMessageMock
|
|
2503
|
+
.mockImplementationOnce(async () => ({ id: "msg-reserve-1" }))
|
|
2504
|
+
.mockImplementationOnce(async () => ({ id: "msg-reserve-2" }))
|
|
2505
|
+
.mockImplementationOnce(async () => ({ id: "msg-reserve-3" }));
|
|
2506
|
+
mockConversationErrorClassification = DAILY_LIMIT_CLASSIFICATION;
|
|
2507
|
+
|
|
2508
|
+
// GIVEN a run whose first call asks for a tool, the tool succeeds, and
|
|
2509
|
+
// the follow-up call is rejected with a daily-limit 402.
|
|
2510
|
+
const ctx = makeCtx({
|
|
2511
|
+
providerResponses: [
|
|
2512
|
+
toolUseResponse("tu-1", "file_read", {}),
|
|
2513
|
+
new Error("Payment Required"),
|
|
2514
|
+
],
|
|
2515
|
+
loopTools: [
|
|
2516
|
+
{
|
|
2517
|
+
name: "file_read",
|
|
2518
|
+
description: "Read a file",
|
|
2519
|
+
input_schema: { type: "object", properties: {} },
|
|
2520
|
+
},
|
|
2521
|
+
],
|
|
2522
|
+
toolExecutor: async () => ({ content: "file content", isError: false }),
|
|
2523
|
+
} as unknown as Partial<Conversation>);
|
|
2524
|
+
|
|
2525
|
+
await runAgentLoopImpl(ctx, "hello", "msg-user-daily", () => {});
|
|
2526
|
+
|
|
2527
|
+
// (a) The synthetic provider-error row is persisted with the
|
|
2528
|
+
// assistant-voice daily-limit copy and the classification metadata.
|
|
2529
|
+
expect(addMessageMock).toHaveBeenCalledTimes(1);
|
|
2530
|
+
const addCall = addMessageMock.mock.calls[0] as unknown as [
|
|
2531
|
+
string,
|
|
2532
|
+
string,
|
|
2533
|
+
string,
|
|
2534
|
+
{ metadata?: Record<string, unknown> },
|
|
2535
|
+
];
|
|
2536
|
+
expect(addCall[1]).toBe("assistant");
|
|
2537
|
+
const persistedBlocks = JSON.parse(addCall[2]) as Array<{
|
|
2538
|
+
type: string;
|
|
2539
|
+
text: string;
|
|
2540
|
+
}>;
|
|
2541
|
+
expect(persistedBlocks[0]?.text).toBe(DAILY_LIMIT_ASSISTANT_REPLY);
|
|
2542
|
+
expect(addCall[3]?.metadata).toMatchObject({
|
|
2543
|
+
messageKind: "provider_error",
|
|
2544
|
+
providerErrorCode: "PROVIDER_BILLING",
|
|
2545
|
+
providerErrorCategory: "daily_limit_reached",
|
|
2546
|
+
});
|
|
2547
|
+
|
|
2548
|
+
// (b) The empty row the failing call reserved at `llm_call_started` is
|
|
2549
|
+
// dropped, so the transcript carries the error row instead of a stranded
|
|
2550
|
+
// blank bubble. The failing call reserves last (after the first call's
|
|
2551
|
+
// assistant row and the grouped tool-result row).
|
|
2552
|
+
expect(deleteMessageByIdMock).toHaveBeenCalledTimes(1);
|
|
2553
|
+
const deleteCall = deleteMessageByIdMock.mock.calls[0] as unknown as [
|
|
2554
|
+
string,
|
|
2555
|
+
];
|
|
2556
|
+
expect(deleteCall[0]).toBe("msg-reserve-3");
|
|
2557
|
+
|
|
2558
|
+
// (c) The turn is stamped failed with the classified code.
|
|
2559
|
+
const outcomeStamps = (
|
|
2560
|
+
updateMessageMetadataMock.mock.calls as unknown as Array<
|
|
2561
|
+
[string, Record<string, unknown>]
|
|
2562
|
+
>
|
|
2563
|
+
).filter((call) => call[1]?.turnOutcome !== undefined);
|
|
2564
|
+
expect(outcomeStamps).toHaveLength(1);
|
|
2565
|
+
expect(outcomeStamps[0]?.[0]).toBe("msg-user-daily");
|
|
2566
|
+
expect(outcomeStamps[0]?.[1]).toMatchObject({
|
|
2567
|
+
turnOutcome: "failed",
|
|
2568
|
+
turnFailureCode: "PROVIDER_BILLING",
|
|
2569
|
+
});
|
|
2570
|
+
|
|
2571
|
+
// (d) History ends tool_use -> tool_result -> error text, so the row
|
|
2572
|
+
// never lands between a tool_use and its tool_result on replay.
|
|
2573
|
+
const tail = ctx.messages.slice(-3) as Array<{
|
|
2574
|
+
role: string;
|
|
2575
|
+
content: Array<{ type: string; text?: string }>;
|
|
2576
|
+
}>;
|
|
2577
|
+
expect(tail[0]?.role).toBe("assistant");
|
|
2578
|
+
expect(tail[0]?.content[0]?.type).toBe("tool_use");
|
|
2579
|
+
expect(tail[1]?.role).toBe("user");
|
|
2580
|
+
expect(tail[1]?.content[0]?.type).toBe("tool_result");
|
|
2581
|
+
expect(tail[2]?.role).toBe("assistant");
|
|
2582
|
+
expect(tail[2]?.content[0]).toEqual({
|
|
2583
|
+
type: "text",
|
|
2584
|
+
text: DAILY_LIMIT_ASSISTANT_REPLY,
|
|
2585
|
+
});
|
|
2586
|
+
});
|
|
2587
|
+
|
|
2280
2588
|
test("does not persist managed credential refresh failures as assistant text", async () => {
|
|
2281
2589
|
mockConversationErrorClassification = {
|
|
2282
2590
|
code: "MANAGED_KEY_INVALID",
|
|
@@ -2435,6 +2743,128 @@ describe("session-agent-loop", () => {
|
|
|
2435
2743
|
});
|
|
2436
2744
|
});
|
|
2437
2745
|
|
|
2746
|
+
describe("detached deferred turn tail", () => {
|
|
2747
|
+
const reservedAssistantRow = {
|
|
2748
|
+
id: "msg-reserve",
|
|
2749
|
+
conversationId: "test-conv",
|
|
2750
|
+
createdAt: 1_700_000_000_001,
|
|
2751
|
+
role: "assistant" as const,
|
|
2752
|
+
content: "[]",
|
|
2753
|
+
metadata: null,
|
|
2754
|
+
};
|
|
2755
|
+
|
|
2756
|
+
test("frees the conversation before the deferred tail finishes, and serializes tails", async () => {
|
|
2757
|
+
mockMessageById = reservedAssistantRow;
|
|
2758
|
+
// A tail effect that never settles on its own stands in for the
|
|
2759
|
+
// production case this guards: memory indexing that takes tens of
|
|
2760
|
+
// seconds against a slow embedding provider.
|
|
2761
|
+
let releaseFirstIndex: (() => void) | undefined;
|
|
2762
|
+
const firstIndexHangs = new Promise<void>((resolve) => {
|
|
2763
|
+
releaseFirstIndex = resolve;
|
|
2764
|
+
});
|
|
2765
|
+
indexMessageNowMock.mockImplementationOnce(async () => {
|
|
2766
|
+
await firstIndexHangs;
|
|
2767
|
+
return { indexedSegments: 0, enqueuedJobs: 0 };
|
|
2768
|
+
});
|
|
2769
|
+
|
|
2770
|
+
const drainReasons: string[] = [];
|
|
2771
|
+
const ctx = makeCtx({
|
|
2772
|
+
providerResponses: [textResponse("first"), textResponse("second")],
|
|
2773
|
+
drainQueue: async (reason?: string) => {
|
|
2774
|
+
drainReasons.push(reason ?? "loop_complete");
|
|
2775
|
+
},
|
|
2776
|
+
});
|
|
2777
|
+
|
|
2778
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
2779
|
+
|
|
2780
|
+
// The tail is still parked inside the indexer…
|
|
2781
|
+
expect(indexMessageNowMock).toHaveBeenCalledTimes(1);
|
|
2782
|
+
// …yet the conversation is already released and the queue already
|
|
2783
|
+
// kicked, so a send arriving now runs instead of being queued.
|
|
2784
|
+
expect(ctx.isProcessing()).toBe(false);
|
|
2785
|
+
expect(drainReasons).toEqual(["loop_complete"]);
|
|
2786
|
+
|
|
2787
|
+
// A follow-up turn runs to completion against the still-hung tail.
|
|
2788
|
+
ctx.abortController = new AbortController();
|
|
2789
|
+
ctx.setProcessing(true);
|
|
2790
|
+
await runAgentLoopImpl(ctx, "again", "msg-2", () => {});
|
|
2791
|
+
expect(ctx.isProcessing()).toBe(false);
|
|
2792
|
+
expect(drainReasons).toEqual(["loop_complete", "loop_complete"]);
|
|
2793
|
+
|
|
2794
|
+
// The second turn's tail is chained behind the first, so it has not run
|
|
2795
|
+
// its own indexing yet.
|
|
2796
|
+
expect(indexMessageNowMock).toHaveBeenCalledTimes(1);
|
|
2797
|
+
|
|
2798
|
+
releaseFirstIndex?.();
|
|
2799
|
+
await settleTurnTail(ctx.conversationId);
|
|
2800
|
+
expect(indexMessageNowMock).toHaveBeenCalledTimes(2);
|
|
2801
|
+
});
|
|
2802
|
+
|
|
2803
|
+
test("a throwing tail effect neither latches the lock nor skips the drain", async () => {
|
|
2804
|
+
mockMessageById = reservedAssistantRow;
|
|
2805
|
+
indexMessageNowMock.mockImplementationOnce(async () => {
|
|
2806
|
+
throw new Error("indexer exploded");
|
|
2807
|
+
});
|
|
2808
|
+
|
|
2809
|
+
const drainReasons: string[] = [];
|
|
2810
|
+
const ctx = makeCtx({
|
|
2811
|
+
providerResponses: [textResponse("first")],
|
|
2812
|
+
drainQueue: async (reason?: string) => {
|
|
2813
|
+
drainReasons.push(reason ?? "loop_complete");
|
|
2814
|
+
},
|
|
2815
|
+
});
|
|
2816
|
+
|
|
2817
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
2818
|
+
// The chain settles rather than rejecting, so the throw can neither
|
|
2819
|
+
// surface as an unhandled rejection nor poison a later turn's tail.
|
|
2820
|
+
await settleTurnTail(ctx.conversationId);
|
|
2821
|
+
|
|
2822
|
+
expect(ctx.isProcessing()).toBe(false);
|
|
2823
|
+
expect(drainReasons).toEqual(["loop_complete"]);
|
|
2824
|
+
// The tail continued past the failure: attention projection still ran.
|
|
2825
|
+
expect(projectAssistantMessageMock).toHaveBeenCalledTimes(1);
|
|
2826
|
+
});
|
|
2827
|
+
|
|
2828
|
+
test("the queue drain waits for the turn-boundary commit", async () => {
|
|
2829
|
+
// `commitTurnChanges` attributes the working tree's changes to the turn
|
|
2830
|
+
// that just finished, so a queued turn must not start writing files
|
|
2831
|
+
// while it runs. The lock release above frees direct sends immediately;
|
|
2832
|
+
// the drain is what has to wait.
|
|
2833
|
+
let commitReached: (() => void) | undefined;
|
|
2834
|
+
const commitStarted = new Promise<void>((resolve) => {
|
|
2835
|
+
commitReached = resolve;
|
|
2836
|
+
});
|
|
2837
|
+
let releaseCommit: (() => void) | undefined;
|
|
2838
|
+
const commitHangs = new Promise<void>((resolve) => {
|
|
2839
|
+
releaseCommit = resolve;
|
|
2840
|
+
});
|
|
2841
|
+
|
|
2842
|
+
const drainReasons: string[] = [];
|
|
2843
|
+
const ctx = makeCtx({
|
|
2844
|
+
providerResponses: [textResponse("first")],
|
|
2845
|
+
commitTurnChanges: async () => {
|
|
2846
|
+
commitReached?.();
|
|
2847
|
+
await commitHangs;
|
|
2848
|
+
},
|
|
2849
|
+
drainQueue: async (reason?: string) => {
|
|
2850
|
+
drainReasons.push(reason ?? "loop_complete");
|
|
2851
|
+
},
|
|
2852
|
+
});
|
|
2853
|
+
|
|
2854
|
+
const loop = runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
2855
|
+
await commitStarted;
|
|
2856
|
+
|
|
2857
|
+
// Released for direct sends…
|
|
2858
|
+
expect(ctx.isProcessing()).toBe(false);
|
|
2859
|
+
// …but the queue is untouched until the commit settles.
|
|
2860
|
+
expect(drainReasons).toEqual([]);
|
|
2861
|
+
|
|
2862
|
+
releaseCommit?.();
|
|
2863
|
+
await loop;
|
|
2864
|
+
expect(drainReasons).toEqual(["loop_complete"]);
|
|
2865
|
+
});
|
|
2866
|
+
});
|
|
2867
|
+
|
|
2438
2868
|
describe("B3 pre-allocation: indexing + cleanup", () => {
|
|
2439
2869
|
test("handleMessageComplete indexes and projects the finalized assistant row", async () => {
|
|
2440
2870
|
// The pre-B3 path inserted assistant rows via `addMessage`, which ran
|
|
@@ -2461,6 +2891,9 @@ describe("session-agent-loop", () => {
|
|
|
2461
2891
|
providerResponses: [textResponse("indexed reply")],
|
|
2462
2892
|
});
|
|
2463
2893
|
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
2894
|
+
// The finalize effects run on the detached tail, so wait for the chain
|
|
2895
|
+
// rather than on the loop's own resolution.
|
|
2896
|
+
await settleTurnTail(ctx.conversationId);
|
|
2464
2897
|
|
|
2465
2898
|
// Indexer fired with the reserved row's id + the finalized content.
|
|
2466
2899
|
expect(indexMessageNowMock).toHaveBeenCalledTimes(1);
|
|
@@ -2537,8 +2970,9 @@ describe("session-agent-loop", () => {
|
|
|
2537
2970
|
providerResponses: [textResponse("indexed reply")],
|
|
2538
2971
|
});
|
|
2539
2972
|
await runAgentLoopImpl(ctx, "hi", "msg-1", (msg) => events.push(msg));
|
|
2973
|
+
await settleTurnTail(ctx.conversationId);
|
|
2540
2974
|
|
|
2541
|
-
// The deferred indexer runs exactly once
|
|
2975
|
+
// The deferred indexer runs exactly once…
|
|
2542
2976
|
expect(indexMessageNowMock).toHaveBeenCalledTimes(1);
|
|
2543
2977
|
// …and only after the terminal SSE that re-enables the composer.
|
|
2544
2978
|
expect(messageCompleteSeenWhenIndexed).toBe(true);
|
|
@@ -2561,6 +2995,7 @@ describe("session-agent-loop", () => {
|
|
|
2561
2995
|
// GIVEN a real loop that answers with a single finalized assistant turn
|
|
2562
2996
|
const ctx = makeCtx({ providerResponses: [textResponse("quiet")] });
|
|
2563
2997
|
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
2998
|
+
await settleTurnTail(ctx.conversationId);
|
|
2564
2999
|
|
|
2565
3000
|
expect(projectAssistantMessageMock).toHaveBeenCalledTimes(1);
|
|
2566
3001
|
// The mock will still receive a `:messages` invalidation from the
|
|
@@ -2950,6 +3385,7 @@ describe("session-agent-loop", () => {
|
|
|
2950
3385
|
|
|
2951
3386
|
// WHEN the orchestrator runs the turn to completion
|
|
2952
3387
|
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3388
|
+
await settleTurnTail(ctx.conversationId);
|
|
2953
3389
|
|
|
2954
3390
|
expect(snapshot).toBeDefined();
|
|
2955
3391
|
// Indexer + projector were both ZERO during the mid-turn partial
|
|
@@ -128,7 +128,11 @@ describe("addMessage + syncMessageToDisk → disk view", () => {
|
|
|
128
128
|
skipIndexing: true,
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
const att = uploadAttachment(
|
|
131
|
+
const att = await uploadAttachment(
|
|
132
|
+
"screenshot.png",
|
|
133
|
+
"image/png",
|
|
134
|
+
"iVBORw0K",
|
|
135
|
+
);
|
|
132
136
|
linkAttachmentToMessage(msg.id, att.id, 0);
|
|
133
137
|
|
|
134
138
|
syncMessageToDisk(conv.id, msg.id, conv.createdAt);
|
|
@@ -434,7 +434,7 @@ describe("syncMessageToDisk", () => {
|
|
|
434
434
|
});
|
|
435
435
|
|
|
436
436
|
// Upload an attachment and link to the message
|
|
437
|
-
const att = uploadAttachment("photo.png", "image/png", "iVBORw0K");
|
|
437
|
+
const att = await uploadAttachment("photo.png", "image/png", "iVBORw0K");
|
|
438
438
|
linkAttachmentToMessage(msg.id, att.id, 0);
|
|
439
439
|
|
|
440
440
|
syncMessageToDisk(conv.id, msg.id, conv.createdAt);
|
|
@@ -498,7 +498,7 @@ describe("syncMessageToDisk", () => {
|
|
|
498
498
|
skipIndexing: true,
|
|
499
499
|
});
|
|
500
500
|
|
|
501
|
-
const att = uploadAttachment("legacy.png", "image/png", "iVBORw0K");
|
|
501
|
+
const att = await uploadAttachment("legacy.png", "image/png", "iVBORw0K");
|
|
502
502
|
linkAttachmentToMessage(msg.id, att.id, 0);
|
|
503
503
|
|
|
504
504
|
syncMessageToDisk(conv.id, msg.id, createdAt);
|
|
@@ -525,7 +525,7 @@ describe("syncMessageToDisk", () => {
|
|
|
525
525
|
const msg = await addMessage(conv.id, "user", "Disk repair", {
|
|
526
526
|
skipIndexing: true,
|
|
527
527
|
});
|
|
528
|
-
const att = uploadAttachment("repair.png", "image/png", "iVBORw0K");
|
|
528
|
+
const att = await uploadAttachment("repair.png", "image/png", "iVBORw0K");
|
|
529
529
|
rawRun(
|
|
530
530
|
"test:linkAttachment",
|
|
531
531
|
`INSERT INTO message_attachments (id, message_id, attachment_id, position, created_at)
|
|
@@ -603,7 +603,7 @@ describe("rebuildConversationDiskViewFromDbState", () => {
|
|
|
603
603
|
{ skipIndexing: true },
|
|
604
604
|
);
|
|
605
605
|
|
|
606
|
-
const att = uploadAttachment("result.txt", "text/plain", "ok");
|
|
606
|
+
const att = await uploadAttachment("result.txt", "text/plain", "ok");
|
|
607
607
|
linkAttachmentToMessage(assistantPart2.id, att.id, 0);
|
|
608
608
|
|
|
609
609
|
// Simulate stale disk view generated before consolidation.
|
|
@@ -1556,7 +1556,7 @@ describe("ConnectionResolutionError classification", () => {
|
|
|
1556
1556
|
expect(result.profileName).toBe("custom-fast");
|
|
1557
1557
|
});
|
|
1558
1558
|
|
|
1559
|
-
it("classifies missing_connection
|
|
1559
|
+
it("classifies missing_connection offering inline recovery and no sentinel name", () => {
|
|
1560
1560
|
const err = new ConnectionResolutionError(
|
|
1561
1561
|
"<llm.default>",
|
|
1562
1562
|
"missing_connection",
|
|
@@ -1566,7 +1566,10 @@ describe("ConnectionResolutionError classification", () => {
|
|
|
1566
1566
|
expect(result.userMessage).toContain(
|
|
1567
1567
|
"No provider connection is configured",
|
|
1568
1568
|
);
|
|
1569
|
-
|
|
1569
|
+
// The user is locked out of chat here, so the copy must offer recovery in
|
|
1570
|
+
// the conversation itself, not only a settings detour.
|
|
1571
|
+
expect(result.userMessage).toContain("Ask me to set one up right here");
|
|
1572
|
+
expect(result.userMessage).toContain("Settings → Models & Services");
|
|
1570
1573
|
expect(result.userMessage).not.toContain("<llm.default>");
|
|
1571
1574
|
expect(result.connectionName).toBeUndefined();
|
|
1572
1575
|
});
|
|
@@ -1610,7 +1613,7 @@ describe("ConnectionResolutionError classification", () => {
|
|
|
1610
1613
|
expect(result.userMessage).toContain('profile "custom-fast"');
|
|
1611
1614
|
});
|
|
1612
1615
|
|
|
1613
|
-
it("classifies platform_unauthenticated with a log-in fix", () => {
|
|
1616
|
+
it("classifies platform_unauthenticated with a log-in fix on a non-platform install", () => {
|
|
1614
1617
|
const err = new ConnectionResolutionError(
|
|
1615
1618
|
"vellum",
|
|
1616
1619
|
"platform_unauthenticated",
|
|
@@ -1621,6 +1624,22 @@ describe("ConnectionResolutionError classification", () => {
|
|
|
1621
1624
|
expect(result.userMessage).toContain("Log in");
|
|
1622
1625
|
});
|
|
1623
1626
|
|
|
1627
|
+
it("classifies platform_unauthenticated as a re-provision hint on a platform-managed assistant", () => {
|
|
1628
|
+
const err = new ConnectionResolutionError(
|
|
1629
|
+
"vellum",
|
|
1630
|
+
"platform_unauthenticated",
|
|
1631
|
+
"unavailable",
|
|
1632
|
+
);
|
|
1633
|
+
process.env.IS_PLATFORM = "true";
|
|
1634
|
+
try {
|
|
1635
|
+
const result = classifyConversationError(err, errCtx);
|
|
1636
|
+
expect(result.userMessage).toContain("re-provisioned");
|
|
1637
|
+
expect(result.userMessage).not.toContain("Log in");
|
|
1638
|
+
} finally {
|
|
1639
|
+
delete process.env.IS_PLATFORM;
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1624
1643
|
it("is a structured VellumError (ConfigError) for logging/monitoring", () => {
|
|
1625
1644
|
const err = new ConnectionResolutionError("x", "not_found", "m");
|
|
1626
1645
|
expect(err).toBeInstanceOf(VellumError);
|
|
@@ -702,7 +702,11 @@ describe("forkConversation", () => {
|
|
|
702
702
|
"Attached the updated mock.",
|
|
703
703
|
{ skipIndexing: true },
|
|
704
704
|
);
|
|
705
|
-
const uploaded = uploadAttachment(
|
|
705
|
+
const uploaded = await uploadAttachment(
|
|
706
|
+
"wireframe.png",
|
|
707
|
+
"image/png",
|
|
708
|
+
"iVBORw0K",
|
|
709
|
+
);
|
|
706
710
|
linkAttachmentToMessage(sourceAssistant.id, uploaded.id, 0);
|
|
707
711
|
|
|
708
712
|
const sourceAttachments = getAttachmentsForMessage(sourceAssistant.id);
|