@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
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
* conversions of the same image (or daemon restarts) skip the sips call.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { execFileSync } from "node:child_process";
|
|
17
16
|
import { createHash } from "node:crypto";
|
|
18
17
|
import {
|
|
19
18
|
closeSync,
|
|
@@ -101,10 +100,10 @@ export interface ConvertToJpegOptions {
|
|
|
101
100
|
quality?: number;
|
|
102
101
|
}
|
|
103
102
|
|
|
104
|
-
function runSips(
|
|
103
|
+
async function runSips(
|
|
105
104
|
inputBytes: Uint8Array,
|
|
106
105
|
options: ConvertToJpegOptions,
|
|
107
|
-
): Buffer | null {
|
|
106
|
+
): Promise<Buffer | null> {
|
|
108
107
|
const stamp = `${Date.now()}-${uuid().slice(0, 8)}`;
|
|
109
108
|
const srcPath = join(tmpdir(), `vellum-img-opt-${stamp}-src`);
|
|
110
109
|
const outPath = join(tmpdir(), `vellum-img-opt-${stamp}-out.jpg`);
|
|
@@ -133,7 +132,15 @@ function runSips(
|
|
|
133
132
|
"--out",
|
|
134
133
|
outPath,
|
|
135
134
|
);
|
|
136
|
-
|
|
135
|
+
const proc = Bun.spawn(["sips", ...args], {
|
|
136
|
+
stdout: "ignore",
|
|
137
|
+
stderr: "ignore",
|
|
138
|
+
timeout: 15_000,
|
|
139
|
+
});
|
|
140
|
+
await proc.exited;
|
|
141
|
+
if (proc.exitCode !== 0) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
137
144
|
return readFileSync(outPath) as Buffer;
|
|
138
145
|
} catch {
|
|
139
146
|
return null;
|
|
@@ -155,10 +162,10 @@ function runSips(
|
|
|
155
162
|
* Convert an image to JPEG, optionally downscaling. Returns null when
|
|
156
163
|
* conversion is unavailable (non-macOS) or fails; callers keep the original.
|
|
157
164
|
*/
|
|
158
|
-
export function convertImageToJpeg(
|
|
165
|
+
export async function convertImageToJpeg(
|
|
159
166
|
bytes: Uint8Array,
|
|
160
167
|
options: ConvertToJpegOptions = {},
|
|
161
|
-
): Buffer | null {
|
|
168
|
+
): Promise<Buffer | null> {
|
|
162
169
|
const hash = createHash("sha256").update(bytes).digest("hex").slice(0, 16);
|
|
163
170
|
// Options qualify the key so full-resolution storage conversions and
|
|
164
171
|
// resized transport conversions of the same source never collide.
|
|
@@ -173,7 +180,7 @@ export function convertImageToJpeg(
|
|
|
173
180
|
return cached;
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
const converted = runSips(bytes, options);
|
|
183
|
+
const converted = await runSips(bytes, options);
|
|
177
184
|
if (!converted) {
|
|
178
185
|
return null;
|
|
179
186
|
}
|
|
@@ -346,12 +353,12 @@ export interface NormalizedImageBytes {
|
|
|
346
353
|
* {@link jpegFilenameFor} when `converted` is true — a MIME-only correction
|
|
347
354
|
* sets `converted: false` and keeps the filename.
|
|
348
355
|
*/
|
|
349
|
-
export function normalizeImageBytes(
|
|
356
|
+
export async function normalizeImageBytes(
|
|
350
357
|
mimeType: string,
|
|
351
358
|
bytes: Uint8Array,
|
|
352
|
-
): NormalizedImageBytes {
|
|
359
|
+
): Promise<NormalizedImageBytes> {
|
|
353
360
|
if (isHeifImage(bytes)) {
|
|
354
|
-
const converted = convertImageToJpeg(bytes, {
|
|
361
|
+
const converted = await convertImageToJpeg(bytes, {
|
|
355
362
|
quality: STORAGE_JPEG_QUALITY,
|
|
356
363
|
});
|
|
357
364
|
if (!converted) {
|
|
@@ -376,14 +383,14 @@ export interface NormalizedImageBase64 {
|
|
|
376
383
|
* Base64 variant of {@link normalizeImageBytes}. Sniffs the decoded head
|
|
377
384
|
* first so non-HEIF payloads skip the full decode.
|
|
378
385
|
*/
|
|
379
|
-
export function normalizeImageBase64(
|
|
386
|
+
export async function normalizeImageBase64(
|
|
380
387
|
mimeType: string,
|
|
381
388
|
dataBase64: string,
|
|
382
|
-
): NormalizedImageBase64 {
|
|
389
|
+
): Promise<NormalizedImageBase64> {
|
|
383
390
|
// 16 base64 chars decode to the 12 bytes the ftyp/signature sniffs need.
|
|
384
391
|
const head = Buffer.from(dataBase64.slice(0, 16), "base64");
|
|
385
392
|
if (isHeifImage(head)) {
|
|
386
|
-
const normalized = normalizeImageBytes(
|
|
393
|
+
const normalized = await normalizeImageBytes(
|
|
387
394
|
mimeType,
|
|
388
395
|
Buffer.from(dataBase64, "base64"),
|
|
389
396
|
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin-declared schedules record their provenance in the `source_key`
|
|
3
|
+
* column as `plugin:<pluginName>/<scheduleName>`. The format is minted by
|
|
4
|
+
* `pluginScheduleSourceKey` in schedule/plugin-schedule-declarations.ts;
|
|
5
|
+
* this leaf parser lets display surfaces (CLI tables, tool output) derive
|
|
6
|
+
* the owning plugin without importing the daemon's schedule modules.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const PLUGIN_SOURCE_KEY_RE = /^plugin:([^/]+)\//;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Display attribution for a schedule row: the owning plugin's name, the raw
|
|
13
|
+
* key when it does not match the format so provenance is never silently
|
|
14
|
+
* dropped, or null when the row is imperative.
|
|
15
|
+
*/
|
|
16
|
+
export function describeScheduleSource(
|
|
17
|
+
sourceKey: string | null | undefined,
|
|
18
|
+
): string | null {
|
|
19
|
+
if (!sourceKey) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return PLUGIN_SOURCE_KEY_RE.exec(sourceKey)?.[1] ?? sourceKey;
|
|
23
|
+
}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
unlinkSync,
|
|
18
18
|
} from "node:fs";
|
|
19
19
|
import { dirname } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
20
21
|
|
|
21
22
|
import { getCurrentLogFilePath } from "./logger.js";
|
|
22
23
|
import { workerMemoryEnv } from "./worker-memory.js";
|
|
@@ -209,8 +210,13 @@ export async function spawnWorkerProcess(args: {
|
|
|
209
210
|
|
|
210
211
|
// Workers are latency-insensitive, so run them in bun's small-heap mode
|
|
211
212
|
// with a RAM-size hint sized to the container — see worker-memory.ts.
|
|
213
|
+
//
|
|
214
|
+
// `fileURLToPath`, not `.pathname`: a URL's pathname is percent-encoded, so
|
|
215
|
+
// an install path containing a space (every macOS desktop install lives
|
|
216
|
+
// under "Application Support") would reach bun as "Application%20Support"
|
|
217
|
+
// and the entry would not be found.
|
|
212
218
|
const child = Bun.spawn({
|
|
213
|
-
cmd: ["bun", "--smol", "run", args.entry
|
|
219
|
+
cmd: ["bun", "--smol", "run", fileURLToPath(args.entry)],
|
|
214
220
|
env: workerMemoryEnv(),
|
|
215
221
|
stdio: ["ignore", "ignore", stderrFd],
|
|
216
222
|
detached,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFile, spawn
|
|
1
|
+
import { execFile, spawn } from "node:child_process";
|
|
2
2
|
import {
|
|
3
3
|
chmodSync,
|
|
4
4
|
existsSync,
|
|
@@ -413,18 +413,19 @@ export class WorkspaceGitService {
|
|
|
413
413
|
* open, we leave it alone. If no process holds it, it's stale (crashed
|
|
414
414
|
* process) and safe to remove.
|
|
415
415
|
*/
|
|
416
|
-
private cleanStaleLockFile(): void {
|
|
416
|
+
private async cleanStaleLockFile(): Promise<void> {
|
|
417
417
|
const lockPath = join(this.workspaceDir, ".git", "index.lock");
|
|
418
418
|
if (!existsSync(lockPath)) {
|
|
419
419
|
return;
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
try {
|
|
423
|
-
|
|
423
|
+
// lsof exits non-zero when no process holds the file, which rejects the
|
|
424
|
+
// promise and falls through to removal below.
|
|
425
|
+
const { stdout } = await execFileAsync("lsof", ["-t", lockPath], {
|
|
424
426
|
timeout: 3000,
|
|
425
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
426
427
|
});
|
|
427
|
-
if (
|
|
428
|
+
if (stdout.length > 0) {
|
|
428
429
|
log.debug("index.lock held by an active process, skipping removal");
|
|
429
430
|
return;
|
|
430
431
|
}
|
|
@@ -490,7 +491,7 @@ export class WorkspaceGitService {
|
|
|
490
491
|
|
|
491
492
|
// Clean up stale lock files before any git operations.
|
|
492
493
|
if (existsSync(gitDir)) {
|
|
493
|
-
this.cleanStaleLockFile();
|
|
494
|
+
await this.cleanStaleLockFile();
|
|
494
495
|
}
|
|
495
496
|
|
|
496
497
|
if (existsSync(gitDir)) {
|
|
@@ -657,7 +658,7 @@ export class WorkspaceGitService {
|
|
|
657
658
|
await this.ensureInitialized();
|
|
658
659
|
|
|
659
660
|
await this.mutex.withLock(async () => {
|
|
660
|
-
this.cleanStaleLockFile();
|
|
661
|
+
await this.cleanStaleLockFile();
|
|
661
662
|
|
|
662
663
|
// Stage all changes (minus oversized files)
|
|
663
664
|
await this.stageAllLocked();
|
|
@@ -734,7 +735,7 @@ export class WorkspaceGitService {
|
|
|
734
735
|
|
|
735
736
|
try {
|
|
736
737
|
const result = await this.mutex.withLock(async () => {
|
|
737
|
-
this.cleanStaleLockFile();
|
|
738
|
+
await this.cleanStaleLockFile();
|
|
738
739
|
|
|
739
740
|
// Re-check breaker under lock: a queued call that started before the
|
|
740
741
|
// breaker opened should not proceed with expensive git work now that
|
|
@@ -1991,6 +1992,29 @@ export class WorkspaceGitService {
|
|
|
1991
1992
|
return this.execGit(args);
|
|
1992
1993
|
}
|
|
1993
1994
|
|
|
1995
|
+
/**
|
|
1996
|
+
* Run a read-only git command WITHOUT initializing the repository.
|
|
1997
|
+
*
|
|
1998
|
+
* `runReadOnlyGit` is read-only in the sense that its git command does not
|
|
1999
|
+
* write, but it still awaits `ensureInitialized()`, which will create the
|
|
2000
|
+
* repository, write `.gitignore` and the hooks directory, make the initial
|
|
2001
|
+
* commit, and schedule a history compaction when the workspace is not yet a
|
|
2002
|
+
* repo. That makes it unusable from an HTTP GET, which
|
|
2003
|
+
* `src/runtime/AGENTS.md` requires to be side-effect-free.
|
|
2004
|
+
*
|
|
2005
|
+
* This variant skips initialization entirely and throws when the workspace
|
|
2006
|
+
* is not a repository, so a caller that merely wants to *observe* history
|
|
2007
|
+
* can degrade instead of bringing a repo into existence.
|
|
2008
|
+
*/
|
|
2009
|
+
async runReadOnlyGitWithoutInit(
|
|
2010
|
+
args: string[],
|
|
2011
|
+
): Promise<{ stdout: string; stderr: string }> {
|
|
2012
|
+
if (!existsSync(join(this.workspaceDir, ".git"))) {
|
|
2013
|
+
throw new Error("Workspace is not a git repository");
|
|
2014
|
+
}
|
|
2015
|
+
return this.execGit(args);
|
|
2016
|
+
}
|
|
2017
|
+
|
|
1994
2018
|
/**
|
|
1995
2019
|
* Run a sequence of git commands atomically under the workspace mutex.
|
|
1996
2020
|
* Use this for write operations that need serialization with other
|
|
@@ -2003,7 +2027,7 @@ export class WorkspaceGitService {
|
|
|
2003
2027
|
): Promise<void> {
|
|
2004
2028
|
await this.ensureInitialized();
|
|
2005
2029
|
await this.mutex.withLock(async () => {
|
|
2006
|
-
this.cleanStaleLockFile();
|
|
2030
|
+
await this.cleanStaleLockFile();
|
|
2007
2031
|
await fn((args) => {
|
|
2008
2032
|
// Intercept commit commands to enforce hook hardening.
|
|
2009
2033
|
if (args[0] === "commit") {
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { Database } from "bun:sqlite";
|
|
4
|
+
|
|
5
|
+
import type { MigrationRunContext, WorkspaceMigration } from "./types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Repair assistants stuck on `memory.v3.live = false` by the first-launch
|
|
9
|
+
* seed race fixed in #39847: the seed used to persist the schema default
|
|
10
|
+
* (`false`) ~200ms before migration 105 ran, and 105's `"live" in v3Config`
|
|
11
|
+
* guard then bailed: a brand-new workspace that should have come up on
|
|
12
|
+
* memory-v3 ran v2 forever, with no signal to the user. 105 is checkpointed
|
|
13
|
+
* as applied and never re-runs, so nothing self-heals the stamped value.
|
|
14
|
+
*
|
|
15
|
+
* The same stamp lands on an EXISTING v3 assistant whose config.json is
|
|
16
|
+
* quarantined (corrupt JSON → renamed to `config.json.corrupt-<ts>.json`)
|
|
17
|
+
* and reseeded: the reseed silently demotes it to v2.
|
|
18
|
+
*
|
|
19
|
+
* Flips `live` to `true` only when the `false` on disk is attributable to
|
|
20
|
+
* the seed, never over a deliberate opt-out:
|
|
21
|
+
*
|
|
22
|
+
* - Quarantine carry-forward: the newest quarantined config next to
|
|
23
|
+
* config.json says `live: true`, meaning the assistant WAS on v3 before the
|
|
24
|
+
* reseed, so the current `false` is the seed's stamp.
|
|
25
|
+
* - Seed-race repair: the workspace was created during the migration-105 era
|
|
26
|
+
* (105 completed in the first-boot sweep, i.e. at effectively the same
|
|
27
|
+
* time as the workspace's earliest checkpoint), the archived hatch-time
|
|
28
|
+
* overlay (`default-config.json`) records no `live: false` opt-out, AND
|
|
29
|
+
* memory-v3 never ran (no `memory_v3_selections` rows in either database,
|
|
30
|
+
* including the mid-drain `__relocating` staging name). A deliberate
|
|
31
|
+
* opt-out on such a workspace requires flipping v3 off before its first
|
|
32
|
+
* selection is logged; a workspace that DID run v3 and now reads `false`
|
|
33
|
+
* is treated as an opt-out and left alone.
|
|
34
|
+
*
|
|
35
|
+
* Everything else is skipped: `live` absent means a pre-105 assistant on the
|
|
36
|
+
* deliberate manual-upgrade path (tier movement stays user-initiated), while
|
|
37
|
+
* new workspaces are migration 105's job, where a post-#39847 hatch-time
|
|
38
|
+
* `memory.v3.live=false` override merges after migrations and must win.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const MIGRATION_105_ID = "105-enable-memory-v3-live-for-new-workspaces";
|
|
42
|
+
|
|
43
|
+
/** How close migration 105's checkpoint must be to the workspace's earliest
|
|
44
|
+
* checkpoint to count as "completed in the first-boot sweep". The sweep
|
|
45
|
+
* itself takes seconds; the margin absorbs a crash-restart mid-sweep. */
|
|
46
|
+
const FIRST_BOOT_SWEEP_TOLERANCE_MS = 60 * 60 * 1000;
|
|
47
|
+
|
|
48
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
49
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** `memory.v3.live` from a parsed config-shaped value, or undefined. */
|
|
53
|
+
function readMemoryV3Live(config: unknown): unknown {
|
|
54
|
+
if (!isPlainObject(config)) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const memory = config.memory;
|
|
58
|
+
if (!isPlainObject(memory)) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const v3 = memory.v3;
|
|
62
|
+
if (!isPlainObject(v3)) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return v3.live;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Whether the newest quarantined config (`config.json.corrupt-*.json`)
|
|
70
|
+
* carried `memory.v3.live: true`. Quarantine filenames embed a
|
|
71
|
+
* filesystem-safe ISO timestamp, so a lexicographic sort is chronological.
|
|
72
|
+
* A quarantined file is usually truncated JSON; every config writer
|
|
73
|
+
* serializes via `JSON.stringify(config, null, 2)` and `memory.v3.live` is
|
|
74
|
+
* the only config key named `"live"`, so when parsing fails a literal
|
|
75
|
+
* `"live": true` match is unambiguous.
|
|
76
|
+
*/
|
|
77
|
+
function quarantinedConfigSaidLiveTrue(workspaceDir: string): boolean {
|
|
78
|
+
try {
|
|
79
|
+
const entries = readdirSync(workspaceDir)
|
|
80
|
+
.filter(
|
|
81
|
+
(name) =>
|
|
82
|
+
name.startsWith("config.json.corrupt-") && name.endsWith(".json"),
|
|
83
|
+
)
|
|
84
|
+
.sort();
|
|
85
|
+
const newest = entries[entries.length - 1];
|
|
86
|
+
if (!newest) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const raw = readFileSync(join(workspaceDir, newest), "utf-8");
|
|
90
|
+
try {
|
|
91
|
+
return readMemoryV3Live(JSON.parse(raw)) === true;
|
|
92
|
+
} catch {
|
|
93
|
+
return /"live"\s*:\s*true/.test(raw);
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Whether migration 105 completed in this workspace's first-boot sweep,
|
|
102
|
+
* i.e. the workspace was CREATED in the era where 105 should have flipped it
|
|
103
|
+
* to v3. On an older workspace 105 arrives in an upgrade sweep long after
|
|
104
|
+
* the earliest checkpoint, so the gap exceeds the tolerance.
|
|
105
|
+
*/
|
|
106
|
+
function wasCreatedInMigration105Era(workspaceDir: string): boolean {
|
|
107
|
+
try {
|
|
108
|
+
const raw = readFileSync(
|
|
109
|
+
join(workspaceDir, "data", ".workspace-migrations.json"),
|
|
110
|
+
"utf-8",
|
|
111
|
+
);
|
|
112
|
+
const parsed: unknown = JSON.parse(raw);
|
|
113
|
+
if (!isPlainObject(parsed) || !isPlainObject(parsed.applied)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
let earliest = Number.POSITIVE_INFINITY;
|
|
117
|
+
let applied105 = Number.NaN;
|
|
118
|
+
for (const [id, entry] of Object.entries(parsed.applied)) {
|
|
119
|
+
if (!isPlainObject(entry) || typeof entry.appliedAt !== "string") {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const at = Date.parse(entry.appliedAt);
|
|
123
|
+
if (!Number.isFinite(at)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
earliest = Math.min(earliest, at);
|
|
127
|
+
if (id === MIGRATION_105_ID) {
|
|
128
|
+
applied105 = at;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return (
|
|
132
|
+
Number.isFinite(applied105) &&
|
|
133
|
+
Number.isFinite(earliest) &&
|
|
134
|
+
applied105 - earliest < FIRST_BOOT_SWEEP_TOLERANCE_MS
|
|
135
|
+
);
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Whether a hatch-time default workspace overlay recorded a deliberate
|
|
143
|
+
* memory-v3 opt-out. `mergeDefaultWorkspaceConfig()` archives the consumed
|
|
144
|
+
* overlay to `default-config.json` next to config.json; an overlay carrying
|
|
145
|
+
* `memory.v3.live: false` merged AFTER workspace migrations, so the
|
|
146
|
+
* persisted `false` is the user's hatch-time choice, not the seed's stamp,
|
|
147
|
+
* even though the workspace otherwise fingerprints as a seed-race victim
|
|
148
|
+
* (born in the 105 era, no v3 selection rows).
|
|
149
|
+
*/
|
|
150
|
+
function hatchOverlayOptedOut(workspaceDir: string): boolean {
|
|
151
|
+
try {
|
|
152
|
+
const raw = readFileSync(
|
|
153
|
+
join(workspaceDir, "default-config.json"),
|
|
154
|
+
"utf-8",
|
|
155
|
+
);
|
|
156
|
+
return readMemoryV3Live(JSON.parse(raw)) === false;
|
|
157
|
+
} catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Whether memory-v3 ever ran a selection on this workspace. The
|
|
164
|
+
* `memory_v3_selections` table lives in `assistant-memory.db` after DB
|
|
165
|
+
* migration 338 and in `assistant.db` before it; both are checked, along
|
|
166
|
+
* with the `__relocating` staging name the relocation renames the source to
|
|
167
|
+
* mid-drain (a crash there leaves the rows only under the staging name). A
|
|
168
|
+
* database file that exists but cannot be read counts as "may have run v3"
|
|
169
|
+
* so an unreadable database never causes an opt-out to be overridden.
|
|
170
|
+
*/
|
|
171
|
+
function memoryV3EverRan(workspaceDir: string): boolean {
|
|
172
|
+
const tables = ["memory_v3_selections", "memory_v3_selections__relocating"];
|
|
173
|
+
for (const file of ["assistant-memory.db", "assistant.db"]) {
|
|
174
|
+
const dbPath = join(workspaceDir, "data", "db", file);
|
|
175
|
+
if (!existsSync(dbPath)) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
let db: Database | null = null;
|
|
179
|
+
try {
|
|
180
|
+
db = new Database(dbPath, { readonly: true });
|
|
181
|
+
for (const table of tables) {
|
|
182
|
+
const exists = db
|
|
183
|
+
.query(
|
|
184
|
+
`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`,
|
|
185
|
+
)
|
|
186
|
+
.get(table);
|
|
187
|
+
if (!exists) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const row = db.query(`SELECT 1 FROM "${table}" LIMIT 1`).get();
|
|
191
|
+
if (row) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
return true;
|
|
197
|
+
} finally {
|
|
198
|
+
db?.close();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const repairSeedPinnedMemoryV3LiveMigration: WorkspaceMigration = {
|
|
205
|
+
id: "140-repair-seed-pinned-memory-v3-live",
|
|
206
|
+
description:
|
|
207
|
+
"Re-flip memory.v3.live=true for assistants the pre-#39847 first-launch seed pinned to false (seed-race and quarantine-reseed victims), leaving deliberate opt-outs alone",
|
|
208
|
+
|
|
209
|
+
run(workspaceDir: string, ctx?: MigrationRunContext): void {
|
|
210
|
+
// New workspaces are migration 105's job, and a post-#39847 hatch-time
|
|
211
|
+
// memory.v3.live=false override (merged after migrations) must win.
|
|
212
|
+
if (ctx?.isNewWorkspace) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const configPath = join(workspaceDir, "config.json");
|
|
217
|
+
if (!existsSync(configPath)) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
let config: Record<string, unknown>;
|
|
221
|
+
try {
|
|
222
|
+
const parsed: unknown = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
223
|
+
if (!isPlainObject(parsed)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
config = parsed;
|
|
227
|
+
} catch {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Only a persisted explicit `false` is repairable: `true` is healthy and
|
|
232
|
+
// an absent leaf is a pre-105 assistant on the manual-upgrade path.
|
|
233
|
+
if (readMemoryV3Live(config) !== false) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!quarantinedConfigSaidLiveTrue(workspaceDir)) {
|
|
238
|
+
if (!wasCreatedInMigration105Era(workspaceDir)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (hatchOverlayOptedOut(workspaceDir)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (memoryV3EverRan(workspaceDir)) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Narrow-write: only the one leaf changes; the shape was validated above.
|
|
250
|
+
const memory = config.memory as Record<string, unknown>;
|
|
251
|
+
const v3 = memory.v3 as Record<string, unknown>;
|
|
252
|
+
v3.live = true;
|
|
253
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
down(_workspaceDir: string): void {
|
|
257
|
+
// Forward-only: cannot distinguish this repair from a user's explicit
|
|
258
|
+
// choice after the fact (mirrors migration 105).
|
|
259
|
+
},
|
|
260
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Put existing assistants on code-switching by writing
|
|
8
|
+
* `services.stt.language: "multi"` on Deepgram and the managed relay,
|
|
9
|
+
* covering both an accepted English default and a language that was never
|
|
10
|
+
* set at all.
|
|
11
|
+
*
|
|
12
|
+
* Normally rewriting a value the user set would be off limits. It is safe
|
|
13
|
+
* here because of when the field appeared: `services.stt.language` did not
|
|
14
|
+
* exist before the language picker shipped (#39546, released 2026-07-31), and
|
|
15
|
+
* on these two providers that picker never offered English and Multilingual
|
|
16
|
+
* as peers. The only English row was the default row, labelled "English
|
|
17
|
+
* (default)". So an `"en"` on Deepgram or the managed relay records "I
|
|
18
|
+
* accepted the default", not "I want English rather than code-switching", and
|
|
19
|
+
* the default it accepted has since changed.
|
|
20
|
+
*
|
|
21
|
+
* Scoped deliberately:
|
|
22
|
+
*
|
|
23
|
+
* - Only `"en"` and absent. Any other language is a real choice from a list
|
|
24
|
+
* where it was the only way to get that language.
|
|
25
|
+
* - Only Deepgram and the managed relay (`mode: "managed"` routes there
|
|
26
|
+
* while `provider` holds the bring-your-own restore value). xAI has
|
|
27
|
+
* offered an explicit English row since the picker shipped, so an `"en"`
|
|
28
|
+
* there is deliberate, and code-switching is a Deepgram mode its adapter
|
|
29
|
+
* drops anyway.
|
|
30
|
+
*
|
|
31
|
+
* Idempotent: an assistant already on `"multi"` (or anything else) is left
|
|
32
|
+
* alone, so re-running changes nothing.
|
|
33
|
+
*/
|
|
34
|
+
const MULTILINGUAL = "multi";
|
|
35
|
+
const ENGLISH = "en";
|
|
36
|
+
const MULTI_DEFAULT_PROVIDERS = new Set(["deepgram", "vellum"]);
|
|
37
|
+
|
|
38
|
+
export const sttEnglishDefaultToMultilingualMigration: WorkspaceMigration = {
|
|
39
|
+
id: "141-stt-english-default-to-multilingual",
|
|
40
|
+
description:
|
|
41
|
+
"Move services.stt.language from the accepted English default to multilingual on Deepgram and managed speech",
|
|
42
|
+
run(workspaceDir: string): void {
|
|
43
|
+
if (process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const configPath = join(workspaceDir, "config.json");
|
|
48
|
+
if (!existsSync(configPath)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let config: Record<string, unknown> = {};
|
|
53
|
+
try {
|
|
54
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
55
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
config = raw as Record<string, unknown>;
|
|
59
|
+
} catch {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const services = readObject(config.services);
|
|
64
|
+
if (services === null) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const stt = readObject(services.stt);
|
|
68
|
+
if (stt === null) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// Two states move, and only these two. An accepted English default, and
|
|
72
|
+
// an absent language.
|
|
73
|
+
//
|
|
74
|
+
// Absent has to be handled here rather than left to the schema default,
|
|
75
|
+
// because the config route serves the RAW config: the parsed default
|
|
76
|
+
// never reaches the web, so the settings UI would keep reading an absent
|
|
77
|
+
// language as English while live transcription used multilingual. Writing
|
|
78
|
+
// it makes the two agree.
|
|
79
|
+
const language = stt.language;
|
|
80
|
+
if (language !== ENGLISH && language !== undefined) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// A legacy managed-mode config routes to the relay while `provider` still
|
|
85
|
+
// holds the bring-your-own value it would restore, so mode wins.
|
|
86
|
+
const provider =
|
|
87
|
+
stt.mode === "managed"
|
|
88
|
+
? "vellum"
|
|
89
|
+
: typeof stt.provider === "string"
|
|
90
|
+
? stt.provider
|
|
91
|
+
: "deepgram";
|
|
92
|
+
if (!MULTI_DEFAULT_PROVIDERS.has(provider)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
stt.language = MULTILINGUAL;
|
|
97
|
+
services.stt = stt;
|
|
98
|
+
config.services = services;
|
|
99
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
100
|
+
},
|
|
101
|
+
down(_workspaceDir: string): void {
|
|
102
|
+
// Forward-only.
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function readObject(value: unknown): Record<string, unknown> | null {
|
|
107
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
return value as Record<string, unknown>;
|
|
111
|
+
}
|