@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
|
@@ -75,9 +75,9 @@ export const UNSENDABLE_IMAGE_NOTE =
|
|
|
75
75
|
* in context, so without it the original rejected block rehydrates and
|
|
76
76
|
* re-rejects on every later turn.
|
|
77
77
|
*/
|
|
78
|
-
export function unsendableImageReplacement(
|
|
78
|
+
export async function unsendableImageReplacement(
|
|
79
79
|
block: Extract<ContentBlock, { type: "image" }>,
|
|
80
|
-
): ContentBlock | null {
|
|
80
|
+
): Promise<ContentBlock | null> {
|
|
81
81
|
// Resolve reference sources to their bytes so a reloaded (referenced) image
|
|
82
82
|
// is gated on the same payload/dimension caps as an inline one. When the
|
|
83
83
|
// attachment can no longer be read, leave the block untouched.
|
|
@@ -118,8 +118,8 @@ export function unsendableImageReplacement(
|
|
|
118
118
|
// pre-send — the upscale runs only here, in response to an actual
|
|
119
119
|
// provider rejection. Oversized images reuse the transport downscale.
|
|
120
120
|
const optimized = belowMinDimension
|
|
121
|
-
? upscaleImageToMinimum(resolved.data, effectiveMediaType)
|
|
122
|
-
: optimizeImageForTransport(resolved.data, effectiveMediaType);
|
|
121
|
+
? await upscaleImageToMinimum(resolved.data, effectiveMediaType)
|
|
122
|
+
: await optimizeImageForTransport(resolved.data, effectiveMediaType);
|
|
123
123
|
if (optimized && optimized.data !== resolved.data) {
|
|
124
124
|
return {
|
|
125
125
|
type: "image",
|
|
@@ -145,9 +145,9 @@ export function unsendableImageReplacement(
|
|
|
145
145
|
* image, so neither matches on a second run. Returns the number of rewritten
|
|
146
146
|
* messages.
|
|
147
147
|
*/
|
|
148
|
-
export function persistUnsendableImageDowngrades(
|
|
148
|
+
export async function persistUnsendableImageDowngrades(
|
|
149
149
|
conversationId: string,
|
|
150
|
-
): number {
|
|
150
|
+
): Promise<number> {
|
|
151
151
|
let rewritten = 0;
|
|
152
152
|
for (const row of getMessages(conversationId)) {
|
|
153
153
|
const hasImageBlock = row.content.some(
|
|
@@ -163,39 +163,47 @@ export function persistUnsendableImageDowngrades(
|
|
|
163
163
|
const parsed = row.content;
|
|
164
164
|
|
|
165
165
|
let changed = false;
|
|
166
|
-
const next
|
|
166
|
+
const next: ContentBlock[] = [];
|
|
167
|
+
for (const block of parsed as ContentBlock[]) {
|
|
167
168
|
if (block.type === "image") {
|
|
168
|
-
const replacement = unsendableImageReplacement(block);
|
|
169
|
+
const replacement = await unsendableImageReplacement(block);
|
|
169
170
|
if (!replacement) {
|
|
170
|
-
|
|
171
|
+
next.push(block);
|
|
172
|
+
continue;
|
|
171
173
|
}
|
|
172
174
|
changed = true;
|
|
173
|
-
|
|
175
|
+
next.push(replacement);
|
|
176
|
+
continue;
|
|
174
177
|
}
|
|
175
178
|
// Images returned by a tool (e.g. browser_screenshot) live inside the
|
|
176
179
|
// tool_result's contentBlocks, not as top-level blocks. Downgrade them
|
|
177
180
|
// in place so the tool_use/tool_result pairing stays intact.
|
|
178
181
|
if (block.type === "tool_result" && block.contentBlocks?.length) {
|
|
179
182
|
let nestedChanged = false;
|
|
180
|
-
const contentBlocks
|
|
183
|
+
const contentBlocks: ContentBlock[] = [];
|
|
184
|
+
for (const cb of block.contentBlocks) {
|
|
181
185
|
if (cb.type !== "image") {
|
|
182
|
-
|
|
186
|
+
contentBlocks.push(cb);
|
|
187
|
+
continue;
|
|
183
188
|
}
|
|
184
|
-
const replacement = unsendableImageReplacement(cb);
|
|
189
|
+
const replacement = await unsendableImageReplacement(cb);
|
|
185
190
|
if (!replacement) {
|
|
186
|
-
|
|
191
|
+
contentBlocks.push(cb);
|
|
192
|
+
continue;
|
|
187
193
|
}
|
|
188
194
|
nestedChanged = true;
|
|
189
|
-
|
|
190
|
-
}
|
|
195
|
+
contentBlocks.push(replacement);
|
|
196
|
+
}
|
|
191
197
|
if (!nestedChanged) {
|
|
192
|
-
|
|
198
|
+
next.push(block);
|
|
199
|
+
continue;
|
|
193
200
|
}
|
|
194
201
|
changed = true;
|
|
195
|
-
|
|
202
|
+
next.push({ ...block, contentBlocks });
|
|
203
|
+
continue;
|
|
196
204
|
}
|
|
197
|
-
|
|
198
|
-
}
|
|
205
|
+
next.push(block);
|
|
206
|
+
}
|
|
199
207
|
if (!changed) {
|
|
200
208
|
continue;
|
|
201
209
|
}
|
|
@@ -240,50 +248,52 @@ function messageHasImageBlock(content: ReadonlyArray<ContentBlock>): boolean {
|
|
|
240
248
|
* caller must not retry an unchanged history: it would resend the identical
|
|
241
249
|
* rejected image and falsely report a correction.
|
|
242
250
|
*/
|
|
243
|
-
export function recoverUnsendableImages(
|
|
251
|
+
export async function recoverUnsendableImages(
|
|
252
|
+
messages: ReadonlyArray<Message>,
|
|
253
|
+
): Promise<{
|
|
244
254
|
messages: Message[];
|
|
245
255
|
changed: boolean;
|
|
246
|
-
} {
|
|
256
|
+
}> {
|
|
247
257
|
let changed = false;
|
|
248
|
-
const recovered =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return msg;
|
|
258
|
+
const recovered: Message[] = [];
|
|
259
|
+
for (const msg of messages) {
|
|
260
|
+
if (!Array.isArray(msg.content) || !messageHasImageBlock(msg.content)) {
|
|
261
|
+
recovered.push(msg);
|
|
262
|
+
continue;
|
|
254
263
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
264
|
+
const content: ContentBlock[] = [];
|
|
265
|
+
for (const b of msg.content) {
|
|
266
|
+
if (b.type === "image") {
|
|
267
|
+
const replacement = await unsendableImageReplacement(b);
|
|
268
|
+
if (replacement) {
|
|
269
|
+
changed = true;
|
|
270
|
+
content.push(replacement);
|
|
271
|
+
} else {
|
|
272
|
+
content.push(b);
|
|
273
|
+
}
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (b.type === "tool_result" && b.contentBlocks?.length) {
|
|
277
|
+
const contentBlocks: ContentBlock[] = [];
|
|
278
|
+
for (const cb of b.contentBlocks) {
|
|
279
|
+
if (cb.type !== "image") {
|
|
280
|
+
contentBlocks.push(cb);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const replacement = await unsendableImageReplacement(cb);
|
|
260
284
|
if (replacement) {
|
|
261
285
|
changed = true;
|
|
262
|
-
|
|
286
|
+
contentBlocks.push(replacement);
|
|
287
|
+
} else {
|
|
288
|
+
contentBlocks.push(cb);
|
|
263
289
|
}
|
|
264
|
-
return [b];
|
|
265
290
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
const replacement = unsendableImageReplacement(cb);
|
|
275
|
-
if (replacement) {
|
|
276
|
-
changed = true;
|
|
277
|
-
return replacement;
|
|
278
|
-
}
|
|
279
|
-
return cb;
|
|
280
|
-
}),
|
|
281
|
-
},
|
|
282
|
-
];
|
|
283
|
-
}
|
|
284
|
-
return [b];
|
|
285
|
-
}),
|
|
286
|
-
};
|
|
287
|
-
});
|
|
291
|
+
content.push({ ...b, contentBlocks });
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
content.push(b);
|
|
295
|
+
}
|
|
296
|
+
recovered.push({ ...msg, content });
|
|
297
|
+
}
|
|
288
298
|
return { messages: recovered, changed };
|
|
289
299
|
}
|
|
@@ -356,7 +356,7 @@ export const defaultToolErrorPlugin: Plugin = {
|
|
|
356
356
|
* file_list) with no user-facing text, or (on loop-prone models such as Kimi
|
|
357
357
|
* K2.6 and MiniMax M3) the model re-issuing a byte-identical exploration call — and nudges
|
|
358
358
|
* the model via `additionalContext` to summarize progress for the user and
|
|
359
|
-
* delegate the remaining investigation to
|
|
359
|
+
* delegate the remaining investigation to a `researcher` subagent rather
|
|
360
360
|
* than continuing inline.
|
|
361
361
|
*/
|
|
362
362
|
export const defaultExplorationDriftPlugin: Plugin = {
|
|
@@ -396,7 +396,7 @@ describe("countConversations", () => {
|
|
|
396
396
|
const priv = createConversation("private-1");
|
|
397
397
|
setConversationType(priv.id, "private");
|
|
398
398
|
|
|
399
|
-
expect(countConversations("background")).toBe(2);
|
|
399
|
+
expect(countConversations({ conversationType: "background" })).toBe(2);
|
|
400
400
|
});
|
|
401
401
|
|
|
402
402
|
test("includes standard conversations with group_id system:background in background count", () => {
|
|
@@ -411,13 +411,13 @@ describe("countConversations", () => {
|
|
|
411
411
|
createConversation("foreground-1");
|
|
412
412
|
|
|
413
413
|
// WHEN counting background conversations
|
|
414
|
-
const bgCount = countConversations("background");
|
|
414
|
+
const bgCount = countConversations({ conversationType: "background" });
|
|
415
415
|
|
|
416
416
|
// THEN the heartbeat conversation is included
|
|
417
417
|
expect(bgCount).toBe(1);
|
|
418
418
|
|
|
419
419
|
// AND excluded from the foreground count
|
|
420
|
-
expect(countConversations("standard")).toBe(1);
|
|
420
|
+
expect(countConversations({ conversationType: "standard" })).toBe(1);
|
|
421
421
|
});
|
|
422
422
|
|
|
423
423
|
test("excludes standard conversations with group_id system:background from foreground count", () => {
|
|
@@ -432,7 +432,7 @@ describe("countConversations", () => {
|
|
|
432
432
|
|
|
433
433
|
// WHEN counting foreground conversations
|
|
434
434
|
// THEN the heartbeat is excluded
|
|
435
|
-
expect(countConversations("standard")).toBe(2);
|
|
435
|
+
expect(countConversations({ conversationType: "standard" })).toBe(2);
|
|
436
436
|
});
|
|
437
437
|
|
|
438
438
|
test('"scheduled" count returns only scheduled rows', () => {
|
|
@@ -443,7 +443,7 @@ describe("countConversations", () => {
|
|
|
443
443
|
|
|
444
444
|
// WHEN counting scheduled conversations
|
|
445
445
|
// THEN only the scheduled row is counted (background is excluded)
|
|
446
|
-
expect(countConversations("scheduled")).toBe(1);
|
|
446
|
+
expect(countConversations({ conversationType: "scheduled" })).toBe(1);
|
|
447
447
|
});
|
|
448
448
|
|
|
449
449
|
describe("archiveStatus", () => {
|
|
@@ -473,7 +473,12 @@ describe("countConversations", () => {
|
|
|
473
473
|
a2.id,
|
|
474
474
|
);
|
|
475
475
|
|
|
476
|
-
expect(
|
|
476
|
+
expect(
|
|
477
|
+
countConversations({
|
|
478
|
+
conversationType: "standard",
|
|
479
|
+
archiveStatus: "archived",
|
|
480
|
+
}),
|
|
481
|
+
).toBe(2);
|
|
477
482
|
});
|
|
478
483
|
|
|
479
484
|
test('archiveStatus "all" returns both', () => {
|
|
@@ -486,7 +491,12 @@ describe("countConversations", () => {
|
|
|
486
491
|
archived.id,
|
|
487
492
|
);
|
|
488
493
|
|
|
489
|
-
expect(
|
|
494
|
+
expect(
|
|
495
|
+
countConversations({
|
|
496
|
+
conversationType: "standard",
|
|
497
|
+
archiveStatus: "all",
|
|
498
|
+
}),
|
|
499
|
+
).toBe(2);
|
|
490
500
|
});
|
|
491
501
|
});
|
|
492
502
|
});
|
|
@@ -511,7 +521,10 @@ describe("listConversations", () => {
|
|
|
511
521
|
createConversation("foreground-1");
|
|
512
522
|
|
|
513
523
|
// WHEN listing background conversations
|
|
514
|
-
const bgList = listConversations(
|
|
524
|
+
const bgList = listConversations({
|
|
525
|
+
limit: 100,
|
|
526
|
+
conversationType: "background",
|
|
527
|
+
});
|
|
515
528
|
|
|
516
529
|
// THEN both background and heartbeat conversations are returned
|
|
517
530
|
expect(bgList).toHaveLength(2);
|
|
@@ -532,7 +545,10 @@ describe("listConversations", () => {
|
|
|
532
545
|
createConversation("foreground-1");
|
|
533
546
|
|
|
534
547
|
// WHEN listing foreground conversations
|
|
535
|
-
const fgList = listConversations(
|
|
548
|
+
const fgList = listConversations({
|
|
549
|
+
limit: 100,
|
|
550
|
+
conversationType: "standard",
|
|
551
|
+
});
|
|
536
552
|
|
|
537
553
|
// THEN only the foreground conversation is returned
|
|
538
554
|
expect(fgList).toHaveLength(1);
|
|
@@ -549,14 +565,20 @@ describe("listConversations", () => {
|
|
|
549
565
|
);
|
|
550
566
|
|
|
551
567
|
// WHEN listing background conversations
|
|
552
|
-
const bgList = listConversations(
|
|
568
|
+
const bgList = listConversations({
|
|
569
|
+
limit: 100,
|
|
570
|
+
conversationType: "background",
|
|
571
|
+
});
|
|
553
572
|
|
|
554
573
|
// THEN it appears in the background list
|
|
555
574
|
expect(bgList).toHaveLength(1);
|
|
556
575
|
expect(bgList[0]!.title).toBe("schedule-routed");
|
|
557
576
|
|
|
558
577
|
// AND not in the foreground list
|
|
559
|
-
const fgList = listConversations(
|
|
578
|
+
const fgList = listConversations({
|
|
579
|
+
limit: 100,
|
|
580
|
+
conversationType: "standard",
|
|
581
|
+
});
|
|
560
582
|
expect(fgList).toHaveLength(0);
|
|
561
583
|
});
|
|
562
584
|
|
|
@@ -567,7 +589,10 @@ describe("listConversations", () => {
|
|
|
567
589
|
createConversation("foreground-1");
|
|
568
590
|
|
|
569
591
|
// WHEN listing scheduled conversations
|
|
570
|
-
const scheduledList = listConversations(
|
|
592
|
+
const scheduledList = listConversations({
|
|
593
|
+
limit: 100,
|
|
594
|
+
conversationType: "scheduled",
|
|
595
|
+
});
|
|
571
596
|
|
|
572
597
|
// THEN only the scheduled conversation is returned
|
|
573
598
|
expect(scheduledList).toHaveLength(1);
|
|
@@ -592,7 +617,10 @@ describe("listConversations", () => {
|
|
|
592
617
|
);
|
|
593
618
|
|
|
594
619
|
// WHEN listing scheduled conversations
|
|
595
|
-
const scheduledList = listConversations(
|
|
620
|
+
const scheduledList = listConversations({
|
|
621
|
+
limit: 100,
|
|
622
|
+
conversationType: "scheduled",
|
|
623
|
+
});
|
|
596
624
|
|
|
597
625
|
// THEN only the system:scheduled row appears
|
|
598
626
|
expect(scheduledList).toHaveLength(1);
|
|
@@ -608,7 +636,10 @@ describe("listConversations", () => {
|
|
|
608
636
|
});
|
|
609
637
|
|
|
610
638
|
// WHEN listing scheduled conversations
|
|
611
|
-
const scheduledList = listConversations(
|
|
639
|
+
const scheduledList = listConversations({
|
|
640
|
+
limit: 100,
|
|
641
|
+
conversationType: "scheduled",
|
|
642
|
+
});
|
|
612
643
|
|
|
613
644
|
// THEN the subagent run is excluded
|
|
614
645
|
expect(scheduledList).toHaveLength(0);
|
|
@@ -627,7 +658,10 @@ describe("listConversations", () => {
|
|
|
627
658
|
);
|
|
628
659
|
|
|
629
660
|
// WHEN listing without an explicit archiveStatus
|
|
630
|
-
const rows = listConversations(
|
|
661
|
+
const rows = listConversations({
|
|
662
|
+
limit: 100,
|
|
663
|
+
conversationType: "standard",
|
|
664
|
+
});
|
|
631
665
|
|
|
632
666
|
// THEN only the live conversation appears
|
|
633
667
|
expect(rows).toHaveLength(1);
|
|
@@ -646,7 +680,11 @@ describe("listConversations", () => {
|
|
|
646
680
|
);
|
|
647
681
|
|
|
648
682
|
// WHEN listing with archiveStatus "archived"
|
|
649
|
-
const rows = listConversations(
|
|
683
|
+
const rows = listConversations({
|
|
684
|
+
limit: 100,
|
|
685
|
+
conversationType: "standard",
|
|
686
|
+
archiveStatus: "archived",
|
|
687
|
+
});
|
|
650
688
|
|
|
651
689
|
// THEN only the archived conversation appears
|
|
652
690
|
expect(rows).toHaveLength(1);
|
|
@@ -665,7 +703,11 @@ describe("listConversations", () => {
|
|
|
665
703
|
);
|
|
666
704
|
|
|
667
705
|
// WHEN listing with archiveStatus "all"
|
|
668
|
-
const rows = listConversations(
|
|
706
|
+
const rows = listConversations({
|
|
707
|
+
limit: 100,
|
|
708
|
+
conversationType: "standard",
|
|
709
|
+
archiveStatus: "all",
|
|
710
|
+
});
|
|
669
711
|
|
|
670
712
|
// THEN both conversations appear
|
|
671
713
|
expect(rows).toHaveLength(2);
|
|
@@ -690,7 +732,11 @@ describe("listConversations", () => {
|
|
|
690
732
|
);
|
|
691
733
|
|
|
692
734
|
// WHEN listing archived background conversations
|
|
693
|
-
const rows = listConversations(
|
|
735
|
+
const rows = listConversations({
|
|
736
|
+
limit: 100,
|
|
737
|
+
conversationType: "background",
|
|
738
|
+
archiveStatus: "archived",
|
|
739
|
+
});
|
|
694
740
|
|
|
695
741
|
// THEN only the archived background row appears
|
|
696
742
|
expect(rows).toHaveLength(1);
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outcome-truthfulness contract between job handlers and the jobs worker.
|
|
3
|
+
*
|
|
4
|
+
* Handlers that report failure through a RETURNED domain outcome (the
|
|
5
|
+
* retrospective's `wake_failed` / `no_usable_output` / `source_processing`,
|
|
6
|
+
* the consolidation's `run_failed`) must not leave their job rows reading
|
|
7
|
+
* `completed` with a null `last_error`. The registration-site adapters in
|
|
8
|
+
* `job-handlers.ts` translate domain outcomes into `JobQueueResolution`s and
|
|
9
|
+
* the worker applies them, so the persisted `memory_jobs.status` reflects the
|
|
10
|
+
* handler's actual outcome.
|
|
11
|
+
*
|
|
12
|
+
* Also covers the store-level late-transition guards: once the stalled-job
|
|
13
|
+
* watchdog fails a row, a late `completeMemoryJob` from the abandoned
|
|
14
|
+
* execution must not flip it back to `completed`.
|
|
15
|
+
*/
|
|
16
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import {
|
|
20
|
+
afterAll,
|
|
21
|
+
beforeAll,
|
|
22
|
+
beforeEach,
|
|
23
|
+
describe,
|
|
24
|
+
expect,
|
|
25
|
+
mock,
|
|
26
|
+
test,
|
|
27
|
+
} from "bun:test";
|
|
28
|
+
|
|
29
|
+
import { eq } from "drizzle-orm";
|
|
30
|
+
|
|
31
|
+
import type { MemoryRetrospectiveOutcome } from "../memory-retrospective-job.js";
|
|
32
|
+
import type { ConsolidationOutcome } from "../substrate/consolidation-job.js";
|
|
33
|
+
|
|
34
|
+
// Scripted handler outcomes, set per-test.
|
|
35
|
+
let retrospectiveOutcome: MemoryRetrospectiveOutcome = {
|
|
36
|
+
kind: "no_new_messages",
|
|
37
|
+
};
|
|
38
|
+
let consolidationOutcome: ConsolidationOutcome = { kind: "empty_buffer" };
|
|
39
|
+
|
|
40
|
+
mock.module("../memory-retrospective-job.js", () => ({
|
|
41
|
+
memoryRetrospectiveJob: async () => retrospectiveOutcome,
|
|
42
|
+
STALE_SOURCE_PROCESSING_OVERRIDE_MS: 6 * 60 * 60 * 1000,
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
mock.module("../substrate/consolidation-job.js", () => ({
|
|
46
|
+
memoryV2ConsolidateJob: async () => consolidationOutcome,
|
|
47
|
+
// Scheduler helpers the worker reads on every pass.
|
|
48
|
+
countBufferLines: () => 0,
|
|
49
|
+
readConsolidationFailureState: () => null,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
mock.module("../graph/graph-search.js", () => ({
|
|
53
|
+
embedGraphNodeDirect: async () => {},
|
|
54
|
+
embedGraphNodeJob: async (): Promise<void> => {},
|
|
55
|
+
enqueueGraphNodeEmbed: () => {},
|
|
56
|
+
embedGraphTriggerJob: async (): Promise<void> => {},
|
|
57
|
+
enqueueGraphTriggerEmbed: () => {},
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
mock.module("../v1/graph/graph-search.js", () => ({
|
|
61
|
+
searchGraphNodes: async () => [],
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
mock.module("../../../../persistence/db-maintenance.js", () => ({
|
|
65
|
+
maybeRunDbMaintenance: async () => {},
|
|
66
|
+
maybeRunPassiveWalCheckpoint: async () => {},
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
const tmpWorkspace = mkdtempSync(
|
|
70
|
+
join(tmpdir(), "jobs-worker-outcome-contract-"),
|
|
71
|
+
);
|
|
72
|
+
const previousWorkspaceEnv = process.env.VELLUM_WORKSPACE_DIR;
|
|
73
|
+
process.env.VELLUM_WORKSPACE_DIR = tmpWorkspace;
|
|
74
|
+
|
|
75
|
+
import { getMemoryDb } from "../../../../persistence/db-connection.js";
|
|
76
|
+
import { initializeDb } from "../../../../persistence/db-init.js";
|
|
77
|
+
import { _resetQdrantBreaker } from "../../../../persistence/embeddings/qdrant-circuit-breaker.js";
|
|
78
|
+
import {
|
|
79
|
+
completeMemoryJob,
|
|
80
|
+
enqueueMemoryJob,
|
|
81
|
+
failStalledJobs,
|
|
82
|
+
} from "../../../../persistence/jobs-store.js";
|
|
83
|
+
import { memoryJobs } from "../../../../persistence/schema/index.js";
|
|
84
|
+
import { registerMemoryPluginJobHandlers } from "../job-handler-registration.js";
|
|
85
|
+
import { runMemoryJobsOnce } from "../jobs-worker.js";
|
|
86
|
+
|
|
87
|
+
function jobRow(jobId: string) {
|
|
88
|
+
return getMemoryDb()!
|
|
89
|
+
.select()
|
|
90
|
+
.from(memoryJobs)
|
|
91
|
+
.where(eq(memoryJobs.id, jobId))
|
|
92
|
+
.get();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
describe("job outcome truthfulness", () => {
|
|
96
|
+
beforeAll(async () => {
|
|
97
|
+
registerMemoryPluginJobHandlers();
|
|
98
|
+
await initializeDb();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
afterAll(() => {
|
|
102
|
+
if (previousWorkspaceEnv === undefined) {
|
|
103
|
+
delete process.env.VELLUM_WORKSPACE_DIR;
|
|
104
|
+
} else {
|
|
105
|
+
process.env.VELLUM_WORKSPACE_DIR = previousWorkspaceEnv;
|
|
106
|
+
}
|
|
107
|
+
rmSync(tmpWorkspace, { recursive: true, force: true });
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
getMemoryDb()!.run("DELETE FROM memory_jobs");
|
|
112
|
+
_resetQdrantBreaker();
|
|
113
|
+
retrospectiveOutcome = { kind: "no_new_messages" };
|
|
114
|
+
consolidationOutcome = { kind: "empty_buffer" };
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("retrospective wake_failed dead-letters the row with an honest last_error", async () => {
|
|
118
|
+
retrospectiveOutcome = { kind: "wake_failed", reason: "run_error" };
|
|
119
|
+
const jobId = enqueueMemoryJob("memory_retrospective", {
|
|
120
|
+
conversationId: "conv-1",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await runMemoryJobsOnce();
|
|
124
|
+
|
|
125
|
+
const row = jobRow(jobId);
|
|
126
|
+
expect(row?.status).toBe("failed");
|
|
127
|
+
expect(row?.lastError).toContain("retrospective wake failed");
|
|
128
|
+
expect(row?.lastError).toContain("run_error");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("retrospective no_usable_output dead-letters the row with the failure detail", async () => {
|
|
132
|
+
retrospectiveOutcome = {
|
|
133
|
+
kind: "no_usable_output",
|
|
134
|
+
reason: "run persisted no memory-writing tool call",
|
|
135
|
+
};
|
|
136
|
+
const jobId = enqueueMemoryJob("memory_retrospective", {
|
|
137
|
+
conversationId: "conv-1",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await runMemoryJobsOnce();
|
|
141
|
+
|
|
142
|
+
const row = jobRow(jobId);
|
|
143
|
+
expect(row?.status).toBe("failed");
|
|
144
|
+
expect(row?.lastError).toContain("no usable output");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("retrospective source_processing defers the SAME row on the deferral counter", async () => {
|
|
148
|
+
retrospectiveOutcome = { kind: "source_processing" };
|
|
149
|
+
const jobId = enqueueMemoryJob("memory_retrospective", {
|
|
150
|
+
conversationId: "conv-1",
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const before = Date.now();
|
|
154
|
+
await runMemoryJobsOnce();
|
|
155
|
+
|
|
156
|
+
const row = jobRow(jobId);
|
|
157
|
+
expect(row?.status).toBe("pending");
|
|
158
|
+
expect(row?.deferrals).toBe(1);
|
|
159
|
+
expect(row?.attempts).toBe(0);
|
|
160
|
+
expect(row!.runAfter).toBeGreaterThan(before);
|
|
161
|
+
// Exactly one row for the conversation: no fresh row minted per attempt.
|
|
162
|
+
const rows = getMemoryDb()!
|
|
163
|
+
.select()
|
|
164
|
+
.from(memoryJobs)
|
|
165
|
+
.where(eq(memoryJobs.type, "memory_retrospective"))
|
|
166
|
+
.all();
|
|
167
|
+
expect(rows).toHaveLength(1);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("source_processing deferral budget exhausts into an honest terminal failure", async () => {
|
|
171
|
+
retrospectiveOutcome = { kind: "source_processing" };
|
|
172
|
+
const jobId = enqueueMemoryJob("memory_retrospective", {
|
|
173
|
+
conversationId: "conv-1",
|
|
174
|
+
});
|
|
175
|
+
// Spend 49 of the 50 deferrals up front; the next pass is the last one.
|
|
176
|
+
getMemoryDb()!
|
|
177
|
+
.update(memoryJobs)
|
|
178
|
+
.set({ deferrals: 49 })
|
|
179
|
+
.where(eq(memoryJobs.id, jobId))
|
|
180
|
+
.run();
|
|
181
|
+
|
|
182
|
+
await runMemoryJobsOnce();
|
|
183
|
+
|
|
184
|
+
const row = jobRow(jobId);
|
|
185
|
+
expect(row?.status).toBe("failed");
|
|
186
|
+
expect(row?.lastError).toContain("mid-turn");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("retrospective success and benign no-ops still complete", async () => {
|
|
190
|
+
retrospectiveOutcome = { kind: "no_new_messages" };
|
|
191
|
+
const jobId = enqueueMemoryJob("memory_retrospective", {
|
|
192
|
+
conversationId: "conv-1",
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await runMemoryJobsOnce();
|
|
196
|
+
|
|
197
|
+
expect(jobRow(jobId)?.status).toBe("completed");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("consolidation run_failed dead-letters the row with the failure reason", async () => {
|
|
201
|
+
consolidationOutcome = { kind: "run_failed", reason: "timeout" };
|
|
202
|
+
const jobId = enqueueMemoryJob("memory_v2_consolidate", {});
|
|
203
|
+
|
|
204
|
+
await runMemoryJobsOnce();
|
|
205
|
+
|
|
206
|
+
const row = jobRow(jobId);
|
|
207
|
+
expect(row?.status).toBe("failed");
|
|
208
|
+
expect(row?.lastError).toContain("consolidation run failed");
|
|
209
|
+
expect(row?.lastError).toContain("timeout");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("consolidation skips (locked/empty) still complete", async () => {
|
|
213
|
+
consolidationOutcome = { kind: "locked", holder: "1234 0 consolidation" };
|
|
214
|
+
const jobId = enqueueMemoryJob("memory_v2_consolidate", {});
|
|
215
|
+
|
|
216
|
+
await runMemoryJobsOnce();
|
|
217
|
+
|
|
218
|
+
expect(jobRow(jobId)?.status).toBe("completed");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("late completion cannot overwrite a watchdog-failed row", async () => {
|
|
222
|
+
const jobId = enqueueMemoryJob("memory_retrospective", {
|
|
223
|
+
conversationId: "conv-1",
|
|
224
|
+
});
|
|
225
|
+
// Simulate a claimed row whose execution went silent long enough for the
|
|
226
|
+
// stalled-job watchdog to fail it.
|
|
227
|
+
getMemoryDb()!
|
|
228
|
+
.update(memoryJobs)
|
|
229
|
+
.set({ status: "running", startedAt: Date.now() - 60 * 60 * 1000 })
|
|
230
|
+
.where(eq(memoryJobs.id, jobId))
|
|
231
|
+
.run();
|
|
232
|
+
expect(failStalledJobs(30 * 60 * 1000)).toBe(1);
|
|
233
|
+
expect(jobRow(jobId)?.status).toBe("failed");
|
|
234
|
+
|
|
235
|
+
// The abandoned execution finally returns and reports success.
|
|
236
|
+
completeMemoryJob(jobId);
|
|
237
|
+
|
|
238
|
+
const row = jobRow(jobId);
|
|
239
|
+
expect(row?.status).toBe("failed");
|
|
240
|
+
expect(row?.lastError).toContain("timed out");
|
|
241
|
+
});
|
|
242
|
+
});
|
|
@@ -39,12 +39,15 @@ getMemoryDb();
|
|
|
39
39
|
const SWEEP_INTERVAL_MS = 8 * 60 * 60 * 1000; // 8h default
|
|
40
40
|
|
|
41
41
|
function buildConfig(
|
|
42
|
-
overrides: { memoryEnabled?: boolean } = {},
|
|
42
|
+
overrides: { memoryEnabled?: boolean; retrospectiveEnabled?: boolean } = {},
|
|
43
43
|
): AssistantConfig {
|
|
44
44
|
const config = applyNestedDefaults({}) as unknown as AssistantConfig;
|
|
45
45
|
if (overrides.memoryEnabled !== undefined) {
|
|
46
46
|
config.memory.enabled = overrides.memoryEnabled;
|
|
47
47
|
}
|
|
48
|
+
if (overrides.retrospectiveEnabled !== undefined) {
|
|
49
|
+
config.memory.retrospective.enabled = overrides.retrospectiveEnabled;
|
|
50
|
+
}
|
|
48
51
|
return config;
|
|
49
52
|
}
|
|
50
53
|
|
|
@@ -118,6 +121,26 @@ describe("maybeEnqueueRetrospectiveSweepJob", () => {
|
|
|
118
121
|
expect(countSweepJobs()).toBe(0);
|
|
119
122
|
});
|
|
120
123
|
|
|
124
|
+
test("does not enqueue when retrospectives are disabled, and leaves the checkpoint alone", () => {
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
const stale = staleCheckpoint(now);
|
|
127
|
+
setMemoryCheckpoint(RETROSPECTIVE_SWEEP_CHECKPOINT, stale);
|
|
128
|
+
|
|
129
|
+
const enqueued = maybeEnqueueRetrospectiveSweepJob(
|
|
130
|
+
buildConfig({ retrospectiveEnabled: false }),
|
|
131
|
+
now,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(enqueued).toBe(false);
|
|
135
|
+
expect(countSweepJobs()).toBe(0);
|
|
136
|
+
// Checkpoint untouched, so re-enabling sweeps on the next tick rather
|
|
137
|
+
// than waiting out another full interval.
|
|
138
|
+
expect(getMemoryCheckpoint(RETROSPECTIVE_SWEEP_CHECKPOINT)).toBe(stale);
|
|
139
|
+
|
|
140
|
+
expect(maybeEnqueueRetrospectiveSweepJob(buildConfig(), now)).toBe(true);
|
|
141
|
+
expect(countSweepJobs()).toBe(1);
|
|
142
|
+
});
|
|
143
|
+
|
|
121
144
|
test("does not stack a second sweep while one is still in flight, but advances the checkpoint", () => {
|
|
122
145
|
const now = Date.now();
|
|
123
146
|
// A prior sweep is already pending.
|