@vellumai/assistant 0.11.2 → 0.11.3-staging.1
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-queue.test.ts +265 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -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-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-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-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 +216 -132
- 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/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 +296 -145
- 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 +85 -42
- package/src/daemon/conversation-process.ts +76 -28
- 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 +105 -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.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/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 +56 -8
- 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 +350 -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 +295 -42
- 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/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/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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdirSync,
|
|
3
|
+
mkdtempSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
import { sttEnglishDefaultToMultilingualMigration as MIG } from "../141-stt-english-default-to-multilingual.js";
|
|
13
|
+
import { WORKSPACE_MIGRATIONS } from "../registry.js";
|
|
14
|
+
|
|
15
|
+
let workspaceDir: string;
|
|
16
|
+
let configPath: string;
|
|
17
|
+
|
|
18
|
+
function write(stt: Record<string, unknown>): void {
|
|
19
|
+
writeFileSync(
|
|
20
|
+
configPath,
|
|
21
|
+
JSON.stringify({ services: { stt } }, null, 2) + "\n",
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
function language(): unknown {
|
|
25
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
26
|
+
return config.services.stt.language;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
workspaceDir = mkdtempSync(join(tmpdir(), "stt-en-to-multi-"));
|
|
31
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
32
|
+
configPath = join(workspaceDir, "config.json");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("141-stt-english-default-to-multilingual", () => {
|
|
40
|
+
test("is registered last, so it defines the registry ceiling", () => {
|
|
41
|
+
// getLastWorkspaceMigrationId() reads the final entry, and the runner
|
|
42
|
+
// executes the array in order, so a new migration belongs at the end.
|
|
43
|
+
expect(MIG.id).toBe("141-stt-english-default-to-multilingual");
|
|
44
|
+
expect(WORKSPACE_MIGRATIONS[WORKSPACE_MIGRATIONS.length - 1]?.id).toBe(
|
|
45
|
+
MIG.id,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("moves an accepted English default to multilingual on deepgram", () => {
|
|
50
|
+
write({ provider: "deepgram", language: "en" });
|
|
51
|
+
MIG.run(workspaceDir);
|
|
52
|
+
expect(language()).toBe("multi");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("moves it on the managed relay", () => {
|
|
56
|
+
write({ provider: "vellum", language: "en" });
|
|
57
|
+
MIG.run(workspaceDir);
|
|
58
|
+
expect(language()).toBe("multi");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("treats legacy managed mode as the relay, not as its restore provider", () => {
|
|
62
|
+
// `mode: "managed"` routes to Vellum while `provider` holds the value a
|
|
63
|
+
// switch back to bring-your-own would restore, so mode has to win.
|
|
64
|
+
write({ mode: "managed", provider: "deepgram", language: "en" });
|
|
65
|
+
MIG.run(workspaceDir);
|
|
66
|
+
expect(language()).toBe("multi");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("leaves a real language alone", () => {
|
|
70
|
+
// Any language other than English could only have come from picking it
|
|
71
|
+
// out of the list, which is a choice rather than an accepted default.
|
|
72
|
+
write({ provider: "deepgram", language: "hi" });
|
|
73
|
+
MIG.run(workspaceDir);
|
|
74
|
+
expect(language()).toBe("hi");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("leaves English alone under xai, where it was a real option", () => {
|
|
78
|
+
// xai has offered an explicit English row since the picker shipped, so
|
|
79
|
+
// "en" there is deliberate.
|
|
80
|
+
write({ provider: "xai", language: "en" });
|
|
81
|
+
MIG.run(workspaceDir);
|
|
82
|
+
expect(language()).toBe("en");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("leaves an assistant already on multilingual untouched", () => {
|
|
86
|
+
write({ provider: "deepgram", language: "multi" });
|
|
87
|
+
MIG.run(workspaceDir);
|
|
88
|
+
expect(language()).toBe("multi");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("is idempotent", () => {
|
|
92
|
+
write({ provider: "deepgram", language: "en" });
|
|
93
|
+
MIG.run(workspaceDir);
|
|
94
|
+
MIG.run(workspaceDir);
|
|
95
|
+
expect(language()).toBe("multi");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("fills a language that was never set", () => {
|
|
99
|
+
// The config route serves the RAW config, so a schema default never
|
|
100
|
+
// reaches the web. Without writing it here the settings UI would read an
|
|
101
|
+
// absent language as English while live transcription used multilingual.
|
|
102
|
+
write({ provider: "deepgram" });
|
|
103
|
+
MIG.run(workspaceDir);
|
|
104
|
+
expect(language()).toBe("multi");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("does not fill an absent language under xai", () => {
|
|
108
|
+
// Unset means native detection there, which is broader than the ten
|
|
109
|
+
// languages code-switching follows.
|
|
110
|
+
write({ provider: "xai" });
|
|
111
|
+
MIG.run(workspaceDir);
|
|
112
|
+
expect(language()).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("survives a config with no services block", () => {
|
|
116
|
+
writeFileSync(configPath, JSON.stringify({}, null, 2) + "\n");
|
|
117
|
+
expect(() => MIG.run(workspaceDir)).not.toThrow();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("survives a missing or unreadable config", () => {
|
|
121
|
+
rmSync(configPath, { force: true });
|
|
122
|
+
expect(() => MIG.run(workspaceDir)).not.toThrow();
|
|
123
|
+
writeFileSync(configPath, "{ not json");
|
|
124
|
+
expect(() => MIG.run(workspaceDir)).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -137,6 +137,8 @@ import { repairStaleFireworksKimiModelIdMigration } from "./136-repair-stale-fir
|
|
|
137
137
|
import { repairRetiredFireworksMinimaxModelIdMigration } from "./137-repair-retired-fireworks-minimax-model-id.js";
|
|
138
138
|
import { backfillHomeFeedTitlesMigration } from "./138-backfill-home-feed-titles.js";
|
|
139
139
|
import { clearRenamedCostProfileLabelMigration } from "./139-clear-renamed-cost-profile-label.js";
|
|
140
|
+
import { repairSeedPinnedMemoryV3LiveMigration } from "./140-repair-seed-pinned-memory-v3-live.js";
|
|
141
|
+
import { sttEnglishDefaultToMultilingualMigration } from "./141-stt-english-default-to-multilingual.js";
|
|
140
142
|
import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
|
|
141
143
|
import type { WorkspaceMigration } from "./types.js";
|
|
142
144
|
|
|
@@ -289,4 +291,6 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
|
|
|
289
291
|
repairRetiredFireworksMinimaxModelIdMigration,
|
|
290
292
|
backfillHomeFeedTitlesMigration,
|
|
291
293
|
clearRenamedCostProfileLabelMigration,
|
|
294
|
+
repairSeedPinnedMemoryV3LiveMigration,
|
|
295
|
+
sttEnglishDefaultToMultilingualMigration,
|
|
292
296
|
];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir, userInfo } from "node:os";
|
|
2
2
|
|
|
3
|
+
import { getIsContainerized } from "../config/env-registry.js";
|
|
3
4
|
import type { TopLevelSnapshot } from "./top-level-scanner.js";
|
|
4
5
|
|
|
5
6
|
// `os.userInfo()` throws `SystemError` when the current UID has no passwd
|
|
@@ -52,10 +53,22 @@ export function renderWorkspaceTopLevelContext(
|
|
|
52
53
|
if (snapshot.truncated) {
|
|
53
54
|
lines.push("(list truncated — more entries exist)");
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
+
// The daemon's own `homedir()` / `userInfo()` describe the HOST only when
|
|
57
|
+
// the daemon runs on it. In a container they describe the container, and
|
|
58
|
+
// labelling them "Host …" invites host-side tools (`host_bash`,
|
|
59
|
+
// `host_file_*`, computer use) to be handed a path that exists nowhere on
|
|
60
|
+
// the user's machine. Fall back only when the two are the same machine;
|
|
61
|
+
// otherwise say the value is unknown and name the way to find it, which is
|
|
62
|
+
// cheaper than a failed write on the user's Mac.
|
|
63
|
+
const hostHomeDir =
|
|
64
|
+
options.hostHomeDir ?? (getIsContainerized() ? undefined : homedir());
|
|
65
|
+
const hostUsername =
|
|
66
|
+
options.hostUsername ??
|
|
67
|
+
(getIsContainerized() ? undefined : safeUserInfoUsername());
|
|
56
68
|
lines.push(
|
|
57
|
-
`Host
|
|
69
|
+
`Host home directory: ${hostHomeDir ?? "unknown (ask the host shell: `host_bash` with `echo $HOME`)"}`,
|
|
58
70
|
);
|
|
71
|
+
lines.push(`Host username: ${hostUsername ?? "unknown"}`);
|
|
59
72
|
lines.push("</workspace>");
|
|
60
73
|
return lines.join("\n");
|
|
61
74
|
}
|