@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
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type DurableProfileFields,
|
|
8
|
+
resolveDurableProfile,
|
|
9
|
+
} from "../conversation-crud.js";
|
|
10
|
+
|
|
11
|
+
// The migration resolves through the shared schedule helpers, which read the
|
|
12
|
+
// workspace config. The default is stubbed so both of its outcomes can be
|
|
13
|
+
// driven: a named profile, and the code-owned anchor (null), which has no key
|
|
14
|
+
// to record. The wake seed keeps the real durable-pin reader so the
|
|
15
|
+
// session-backed vs. sticky distinction under test is the production one.
|
|
16
|
+
let resolvedDefault: string | null = "balanced";
|
|
17
|
+
mock.module("../../schedule/inference-profile.js", () => ({
|
|
18
|
+
resolveDefaultScheduleInferenceProfile: () => resolvedDefault,
|
|
19
|
+
resolveWakeScheduleInferenceProfile: (target: DurableProfileFields | null) =>
|
|
20
|
+
resolveDurableProfile(target) ?? resolvedDefault,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
import * as schema from "../schema.js";
|
|
24
|
+
import { migrateBackfillScheduleInferenceProfile } from "./363-backfill-schedule-inference-profile.js";
|
|
25
|
+
|
|
26
|
+
function createTestDb(withProfileColumn = true) {
|
|
27
|
+
const sqlite = new Database(":memory:");
|
|
28
|
+
sqlite.exec(/*sql*/ `
|
|
29
|
+
CREATE TABLE cron_jobs (
|
|
30
|
+
id TEXT PRIMARY KEY,
|
|
31
|
+
name TEXT NOT NULL,
|
|
32
|
+
message TEXT NOT NULL,
|
|
33
|
+
next_run_at INTEGER NOT NULL,
|
|
34
|
+
mode TEXT NOT NULL DEFAULT 'execute',
|
|
35
|
+
wake_conversation_id TEXT${withProfileColumn ? ",\n inference_profile TEXT" : ""}
|
|
36
|
+
);
|
|
37
|
+
CREATE TABLE conversations (
|
|
38
|
+
id TEXT PRIMARY KEY,
|
|
39
|
+
conversation_type TEXT,
|
|
40
|
+
inference_profile TEXT,
|
|
41
|
+
inference_profile_session_id TEXT,
|
|
42
|
+
inference_profile_expires_at INTEGER
|
|
43
|
+
);
|
|
44
|
+
`);
|
|
45
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function insertJob(
|
|
49
|
+
sqlite: Database,
|
|
50
|
+
id: string,
|
|
51
|
+
inferenceProfile: string | null,
|
|
52
|
+
): void {
|
|
53
|
+
sqlite
|
|
54
|
+
.query(
|
|
55
|
+
"INSERT INTO cron_jobs (id, name, message, next_run_at, inference_profile) VALUES (?, ?, ?, ?, ?)",
|
|
56
|
+
)
|
|
57
|
+
.run(id, id, "hi", 1000, inferenceProfile);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function insertWakeJob(
|
|
61
|
+
sqlite: Database,
|
|
62
|
+
id: string,
|
|
63
|
+
wakeConversationId: string,
|
|
64
|
+
): void {
|
|
65
|
+
sqlite
|
|
66
|
+
.query(
|
|
67
|
+
"INSERT INTO cron_jobs (id, name, message, next_run_at, mode, wake_conversation_id, inference_profile) VALUES (?, ?, ?, ?, 'wake', ?, NULL)",
|
|
68
|
+
)
|
|
69
|
+
.run(id, id, "hi", 1000, wakeConversationId);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function insertConversation(
|
|
73
|
+
sqlite: Database,
|
|
74
|
+
id: string,
|
|
75
|
+
fields: {
|
|
76
|
+
inferenceProfile?: string | null;
|
|
77
|
+
sessionId?: string | null;
|
|
78
|
+
expiresAt?: number | null;
|
|
79
|
+
} = {},
|
|
80
|
+
): void {
|
|
81
|
+
sqlite
|
|
82
|
+
.query(
|
|
83
|
+
"INSERT INTO conversations (id, conversation_type, inference_profile, inference_profile_session_id, inference_profile_expires_at) VALUES (?, 'chat', ?, ?, ?)",
|
|
84
|
+
)
|
|
85
|
+
.run(
|
|
86
|
+
id,
|
|
87
|
+
fields.inferenceProfile ?? null,
|
|
88
|
+
fields.sessionId ?? null,
|
|
89
|
+
fields.expiresAt ?? null,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function profileOf(sqlite: Database, id: string): unknown {
|
|
94
|
+
return (
|
|
95
|
+
sqlite
|
|
96
|
+
.query("SELECT inference_profile FROM cron_jobs WHERE id = ?")
|
|
97
|
+
.get(id) as { inference_profile: unknown }
|
|
98
|
+
).inference_profile;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
describe("migration 363: backfill cron_jobs.inference_profile", () => {
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
resolvedDefault = "balanced";
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("pins unpinned rows to the resolved default", () => {
|
|
107
|
+
const { sqlite, db } = createTestDb();
|
|
108
|
+
insertJob(sqlite, "unpinned-1", null);
|
|
109
|
+
insertJob(sqlite, "unpinned-2", null);
|
|
110
|
+
|
|
111
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
112
|
+
|
|
113
|
+
expect(profileOf(sqlite, "unpinned-1")).toBe("balanced");
|
|
114
|
+
expect(profileOf(sqlite, "unpinned-2")).toBe("balanced");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("leaves an existing pin alone", () => {
|
|
118
|
+
const { sqlite, db } = createTestDb();
|
|
119
|
+
insertJob(sqlite, "pinned", "cost-optimized");
|
|
120
|
+
|
|
121
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
122
|
+
|
|
123
|
+
expect(profileOf(sqlite, "pinned")).toBe("cost-optimized");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("is idempotent: a second run changes nothing", () => {
|
|
127
|
+
const { sqlite, db } = createTestDb();
|
|
128
|
+
insertJob(sqlite, "unpinned", null);
|
|
129
|
+
|
|
130
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
131
|
+
resolvedDefault = "cost-optimized";
|
|
132
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
133
|
+
|
|
134
|
+
// The row was pinned by the first run, so the later default does not
|
|
135
|
+
// overwrite it. That is what makes a schedule's cost stable.
|
|
136
|
+
expect(profileOf(sqlite, "unpinned")).toBe("balanced");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("leaves rows null when no named profile resolves", () => {
|
|
140
|
+
const { sqlite, db } = createTestDb();
|
|
141
|
+
insertJob(sqlite, "unpinned", null);
|
|
142
|
+
resolvedDefault = null;
|
|
143
|
+
|
|
144
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
145
|
+
|
|
146
|
+
expect(profileOf(sqlite, "unpinned")).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("skips an install that has no inference_profile column yet", () => {
|
|
150
|
+
const { sqlite, db } = createTestDb(false);
|
|
151
|
+
sqlite
|
|
152
|
+
.query(
|
|
153
|
+
"INSERT INTO cron_jobs (id, name, message, next_run_at) VALUES ('legacy', 'legacy', 'hi', 1000)",
|
|
154
|
+
)
|
|
155
|
+
.run();
|
|
156
|
+
|
|
157
|
+
expect(() => migrateBackfillScheduleInferenceProfile(db)).not.toThrow();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// A pending wake fires with no forced override today, so it resolves its
|
|
161
|
+
// target conversation's pin live. The backfill has to preserve that, or a
|
|
162
|
+
// reminder set inside a pinned conversation silently moves to the global
|
|
163
|
+
// default.
|
|
164
|
+
describe("wake rows seed from their target conversation", () => {
|
|
165
|
+
test("takes the target's durable pin", () => {
|
|
166
|
+
const { sqlite, db } = createTestDb();
|
|
167
|
+
insertConversation(sqlite, "conv-pinned", {
|
|
168
|
+
inferenceProfile: "cost-optimized",
|
|
169
|
+
});
|
|
170
|
+
insertWakeJob(sqlite, "wake-pinned", "conv-pinned");
|
|
171
|
+
|
|
172
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
173
|
+
|
|
174
|
+
expect(profileOf(sqlite, "wake-pinned")).toBe("cost-optimized");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("takes the default when the target has no pin, as does an ordinary schedule", () => {
|
|
178
|
+
const { sqlite, db } = createTestDb();
|
|
179
|
+
insertConversation(sqlite, "conv-unpinned");
|
|
180
|
+
insertWakeJob(sqlite, "wake-unpinned", "conv-unpinned");
|
|
181
|
+
insertJob(sqlite, "ordinary", null);
|
|
182
|
+
|
|
183
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
184
|
+
|
|
185
|
+
expect(profileOf(sqlite, "wake-unpinned")).toBe("balanced");
|
|
186
|
+
expect(profileOf(sqlite, "ordinary")).toBe("balanced");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("ignores a session-backed pin on the target", () => {
|
|
190
|
+
const { sqlite, db } = createTestDb();
|
|
191
|
+
insertConversation(sqlite, "conv-session", {
|
|
192
|
+
inferenceProfile: "cost-optimized",
|
|
193
|
+
sessionId: "session-1",
|
|
194
|
+
expiresAt: Date.now() + 600_000,
|
|
195
|
+
});
|
|
196
|
+
insertWakeJob(sqlite, "wake-session", "conv-session");
|
|
197
|
+
|
|
198
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
199
|
+
|
|
200
|
+
// The session lapses in minutes; the row it seeded would keep billing
|
|
201
|
+
// that model for as long as the wake stays pending.
|
|
202
|
+
expect(profileOf(sqlite, "wake-session")).toBe("balanced");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("takes the default when the target conversation is gone", () => {
|
|
206
|
+
const { sqlite, db } = createTestDb();
|
|
207
|
+
insertWakeJob(sqlite, "wake-orphan", "conv-deleted");
|
|
208
|
+
|
|
209
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
210
|
+
|
|
211
|
+
expect(profileOf(sqlite, "wake-orphan")).toBe("balanced");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("keeps the target's pin even when no default resolves", () => {
|
|
215
|
+
const { sqlite, db } = createTestDb();
|
|
216
|
+
resolvedDefault = null;
|
|
217
|
+
insertConversation(sqlite, "conv-pinned", {
|
|
218
|
+
inferenceProfile: "cost-optimized",
|
|
219
|
+
});
|
|
220
|
+
insertWakeJob(sqlite, "wake-pinned", "conv-pinned");
|
|
221
|
+
insertJob(sqlite, "ordinary", null);
|
|
222
|
+
|
|
223
|
+
migrateBackfillScheduleInferenceProfile(db);
|
|
224
|
+
|
|
225
|
+
expect(profileOf(sqlite, "wake-pinned")).toBe("cost-optimized");
|
|
226
|
+
expect(profileOf(sqlite, "ordinary")).toBeNull();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
resolveDefaultScheduleInferenceProfile,
|
|
5
|
+
resolveWakeScheduleInferenceProfile,
|
|
6
|
+
} from "../../schedule/inference-profile.js";
|
|
7
|
+
import type { DurableProfileFields } from "../conversation-crud.js";
|
|
8
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
9
|
+
|
|
10
|
+
/** Columns a wake row's seed needs on `cron_jobs` and `conversations`. */
|
|
11
|
+
const WAKE_JOB_COLUMNS = ["mode", "wake_conversation_id"] as const;
|
|
12
|
+
const CONVERSATION_PIN_COLUMNS = [
|
|
13
|
+
"conversation_type",
|
|
14
|
+
"inference_profile",
|
|
15
|
+
"inference_profile_session_id",
|
|
16
|
+
"inference_profile_expires_at",
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
function hasColumns(
|
|
20
|
+
raw: Database,
|
|
21
|
+
table: string,
|
|
22
|
+
required: readonly string[],
|
|
23
|
+
): boolean {
|
|
24
|
+
const columns = raw.prepare(`PRAGMA table_info(${table})`).all() as {
|
|
25
|
+
name: string;
|
|
26
|
+
}[];
|
|
27
|
+
const present = new Set(columns.map((c) => c.name));
|
|
28
|
+
return required.every((name) => present.has(name));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Pin every unpinned schedule to a concrete inference profile.
|
|
33
|
+
*
|
|
34
|
+
* A schedule with no pin follows `llm.activeProfile`, so changing the global
|
|
35
|
+
* default silently moves every such schedule onto a different model and a
|
|
36
|
+
* different price. Schedules created from now on snapshot a profile at write
|
|
37
|
+
* time (`insertSchedule`); this backfill gives the rows that predate that the
|
|
38
|
+
* same stability, on the profile they are running under today.
|
|
39
|
+
*
|
|
40
|
+
* "Running under today" is not the global default for every row. An unpinned
|
|
41
|
+
* wake row reaches `buildWakeScheduleOptions` with no forced override, so its
|
|
42
|
+
* firing resolves the target conversation's own pin live. Stamping the global
|
|
43
|
+
* default over it would silently re-point a pending reminder that was created
|
|
44
|
+
* inside a pinned conversation, so wake rows seed through the same
|
|
45
|
+
* {@link resolveWakeScheduleInferenceProfile} the create path uses: the
|
|
46
|
+
* target's durable pin, else the default. Every other row takes the default.
|
|
47
|
+
*
|
|
48
|
+
* Rows keep a NULL pin when no named profile resolves at all (the winner is
|
|
49
|
+
* the code-owned anchor). That is the same value they carry now and resolves
|
|
50
|
+
* identically at run time, through the `mainAgent` call-site configuration.
|
|
51
|
+
*
|
|
52
|
+
* Only `cron_jobs` is written. `conversations` carries a pin the user sets per
|
|
53
|
+
* conversation (read here, never modified), and the telemetry tables
|
|
54
|
+
* (tool_invocations, llm_usage_events, skill_loaded_events) carry an
|
|
55
|
+
* `inference_profile` recording what actually ran, which must keep its
|
|
56
|
+
* historical value.
|
|
57
|
+
*
|
|
58
|
+
* Idempotent: `WHERE inference_profile IS NULL` matches nothing once a row is
|
|
59
|
+
* pinned, and the PRAGMA guards skip an install that has not created the
|
|
60
|
+
* columns yet.
|
|
61
|
+
*/
|
|
62
|
+
export function migrateBackfillScheduleInferenceProfile(
|
|
63
|
+
database: DrizzleDb,
|
|
64
|
+
): void {
|
|
65
|
+
const raw = getSqliteFrom(database);
|
|
66
|
+
if (!hasColumns(raw, "cron_jobs", ["inference_profile"])) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
hasColumns(raw, "cron_jobs", WAKE_JOB_COLUMNS) &&
|
|
72
|
+
hasColumns(raw, "conversations", CONVERSATION_PIN_COLUMNS)
|
|
73
|
+
) {
|
|
74
|
+
backfillWakeRows(raw);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const profile = resolveDefaultScheduleInferenceProfile();
|
|
78
|
+
if (profile === null) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
raw
|
|
82
|
+
.prepare(
|
|
83
|
+
`UPDATE cron_jobs SET inference_profile = ? WHERE inference_profile IS NULL`,
|
|
84
|
+
)
|
|
85
|
+
.run(profile);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function backfillWakeRows(raw: Database): void {
|
|
89
|
+
const rows = raw
|
|
90
|
+
.prepare(
|
|
91
|
+
`SELECT id, wake_conversation_id AS wakeConversationId
|
|
92
|
+
FROM cron_jobs
|
|
93
|
+
WHERE inference_profile IS NULL
|
|
94
|
+
AND mode = 'wake'
|
|
95
|
+
AND wake_conversation_id IS NOT NULL`,
|
|
96
|
+
)
|
|
97
|
+
.all() as { id: string; wakeConversationId: string }[];
|
|
98
|
+
if (rows.length === 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const selectTarget = raw.prepare(
|
|
103
|
+
`SELECT conversation_type AS conversationType,
|
|
104
|
+
inference_profile AS inferenceProfile,
|
|
105
|
+
inference_profile_session_id AS inferenceProfileSessionId,
|
|
106
|
+
inference_profile_expires_at AS inferenceProfileExpiresAt
|
|
107
|
+
FROM conversations
|
|
108
|
+
WHERE id = ?`,
|
|
109
|
+
);
|
|
110
|
+
const pin = raw.prepare(
|
|
111
|
+
`UPDATE cron_jobs SET inference_profile = ? WHERE id = ? AND inference_profile IS NULL`,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
for (const row of rows) {
|
|
115
|
+
// A target deleted since the wake was created reads as null and falls
|
|
116
|
+
// through to the default, the same value the firing would resolve.
|
|
117
|
+
const target =
|
|
118
|
+
(selectTarget.get(
|
|
119
|
+
row.wakeConversationId,
|
|
120
|
+
) as DurableProfileFields | null) ?? null;
|
|
121
|
+
const profile = resolveWakeScheduleInferenceProfile(target);
|
|
122
|
+
if (profile === null) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
pin.run(profile, row.id);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateAddScheduleSourceKey } from "./364-add-schedule-source-key.js";
|
|
8
|
+
|
|
9
|
+
const NEW_COLUMNS = ["source_key", "definition_hash", "user_enabled"];
|
|
10
|
+
|
|
11
|
+
/** Pre-364 shape: the cron_jobs table exactly as core tables created it. */
|
|
12
|
+
function createTestDb() {
|
|
13
|
+
const sqlite = new Database(":memory:");
|
|
14
|
+
sqlite.exec(/*sql*/ `
|
|
15
|
+
CREATE TABLE cron_jobs (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
name TEXT NOT NULL,
|
|
18
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
19
|
+
cron_expression TEXT NOT NULL,
|
|
20
|
+
schedule_syntax TEXT NOT NULL DEFAULT 'cron',
|
|
21
|
+
timezone TEXT,
|
|
22
|
+
message TEXT NOT NULL,
|
|
23
|
+
next_run_at INTEGER NOT NULL,
|
|
24
|
+
last_run_at INTEGER,
|
|
25
|
+
last_status TEXT,
|
|
26
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
created_by TEXT NOT NULL,
|
|
28
|
+
created_at INTEGER NOT NULL,
|
|
29
|
+
updated_at INTEGER NOT NULL
|
|
30
|
+
);
|
|
31
|
+
`);
|
|
32
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function columnInfo(sqlite: Database) {
|
|
36
|
+
return sqlite.query("PRAGMA table_info(cron_jobs)").all() as Array<{
|
|
37
|
+
name: string;
|
|
38
|
+
notnull: number;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function insertJob(
|
|
43
|
+
sqlite: Database,
|
|
44
|
+
id: string,
|
|
45
|
+
sourceKey: string | null,
|
|
46
|
+
): void {
|
|
47
|
+
sqlite
|
|
48
|
+
.query(
|
|
49
|
+
/*sql*/ `INSERT INTO cron_jobs (id, name, cron_expression, message, next_run_at, created_by, created_at, updated_at, source_key)
|
|
50
|
+
VALUES (?, 'Job', '0 9 * * *', 'hello', 1000, 'agent', 1000, 1000, ?)`,
|
|
51
|
+
)
|
|
52
|
+
.run(id, sourceKey);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe("migration 364: cron_jobs declaration provenance", () => {
|
|
56
|
+
test("adds the nullable columns to an existing database", () => {
|
|
57
|
+
const { sqlite, db } = createTestDb();
|
|
58
|
+
const before = columnInfo(sqlite).map((c) => c.name);
|
|
59
|
+
for (const name of NEW_COLUMNS) {
|
|
60
|
+
expect(before).not.toContain(name);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
migrateAddScheduleSourceKey(db);
|
|
64
|
+
|
|
65
|
+
const after = columnInfo(sqlite);
|
|
66
|
+
for (const name of NEW_COLUMNS) {
|
|
67
|
+
const column = after.find((c) => c.name === name);
|
|
68
|
+
expect(column).toBeDefined();
|
|
69
|
+
expect(column?.notnull).toBe(0);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("rows written before the columns existed read back null", () => {
|
|
74
|
+
const { sqlite, db } = createTestDb();
|
|
75
|
+
sqlite.exec(/*sql*/ `
|
|
76
|
+
INSERT INTO cron_jobs (id, name, cron_expression, message, next_run_at, created_by, created_at, updated_at)
|
|
77
|
+
VALUES ('job-old', 'Old', '0 9 * * *', 'hello', 1000, 'agent', 1000, 1000)
|
|
78
|
+
`);
|
|
79
|
+
|
|
80
|
+
migrateAddScheduleSourceKey(db);
|
|
81
|
+
|
|
82
|
+
const row = sqlite
|
|
83
|
+
.query(
|
|
84
|
+
"SELECT source_key, definition_hash, user_enabled FROM cron_jobs WHERE id = 'job-old'",
|
|
85
|
+
)
|
|
86
|
+
.get() as Record<string, unknown>;
|
|
87
|
+
expect(row.source_key).toBeNull();
|
|
88
|
+
expect(row.definition_hash).toBeNull();
|
|
89
|
+
expect(row.user_enabled).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("rejects a second row with the same source_key", () => {
|
|
93
|
+
const { sqlite, db } = createTestDb();
|
|
94
|
+
migrateAddScheduleSourceKey(db);
|
|
95
|
+
|
|
96
|
+
insertJob(sqlite, "job-a", "plugin:example/daily");
|
|
97
|
+
|
|
98
|
+
expect(() => insertJob(sqlite, "job-b", "plugin:example/daily")).toThrow();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("leaves imperative schedules unconstrained", () => {
|
|
102
|
+
const { sqlite, db } = createTestDb();
|
|
103
|
+
migrateAddScheduleSourceKey(db);
|
|
104
|
+
|
|
105
|
+
insertJob(sqlite, "job-a", null);
|
|
106
|
+
expect(() => insertJob(sqlite, "job-b", null)).not.toThrow();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("is idempotent: re-run is a no-op", () => {
|
|
110
|
+
const { sqlite, db } = createTestDb();
|
|
111
|
+
|
|
112
|
+
migrateAddScheduleSourceKey(db);
|
|
113
|
+
expect(() => migrateAddScheduleSourceKey(db)).not.toThrow();
|
|
114
|
+
|
|
115
|
+
const names = columnInfo(sqlite).map((c) => c.name);
|
|
116
|
+
for (const name of NEW_COLUMNS) {
|
|
117
|
+
expect(names.filter((n) => n === name)).toHaveLength(1);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { tableHasColumn } from "./schema-introspection.js";
|
|
3
|
+
|
|
4
|
+
const COLUMNS: ReadonlyArray<{ name: string; definition: string }> = [
|
|
5
|
+
{ name: "source_key", definition: "source_key TEXT" },
|
|
6
|
+
{ name: "definition_hash", definition: "definition_hash TEXT" },
|
|
7
|
+
{ name: "user_enabled", definition: "user_enabled INTEGER" },
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Add plugin-declaration provenance columns to `cron_jobs`.
|
|
12
|
+
*
|
|
13
|
+
* `source_key` links a schedule row to the plugin declaration it was
|
|
14
|
+
* reconciled from (`plugin:<pluginName>/<scheduleName>`). Rows with a NULL
|
|
15
|
+
* `source_key` are imperative schedules and keep their existing behavior.
|
|
16
|
+
* `definition_hash` is the reconciler's change detector over the declaration
|
|
17
|
+
* files. `user_enabled` is the user's enable/disable override for a sourced
|
|
18
|
+
* row; NULL means the declaration's own enabled value applies.
|
|
19
|
+
*
|
|
20
|
+
* The unique index is partial (`WHERE source_key IS NOT NULL`) so the many
|
|
21
|
+
* NULL rows stay unconstrained while a given declaration resolves to exactly
|
|
22
|
+
* one row. The reconciler's upsert depends on that uniqueness.
|
|
23
|
+
*
|
|
24
|
+
* Nullable with no backfill: nothing recorded declaration provenance before
|
|
25
|
+
* these columns existed, so pre-existing rows correctly stay NULL.
|
|
26
|
+
*
|
|
27
|
+
* Idempotent: each column add is guarded with `tableHasColumn` and the index
|
|
28
|
+
* with `IF NOT EXISTS`, so a crash partway through does not break the next
|
|
29
|
+
* boot.
|
|
30
|
+
*/
|
|
31
|
+
export function migrateAddScheduleSourceKey(database: DrizzleDb): void {
|
|
32
|
+
for (const column of COLUMNS) {
|
|
33
|
+
if (!tableHasColumn(database, "cron_jobs", column.name)) {
|
|
34
|
+
database.run(`ALTER TABLE cron_jobs ADD COLUMN ${column.definition}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
database.run(/*sql*/ `
|
|
39
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_cron_jobs_source_key
|
|
40
|
+
ON cron_jobs(source_key)
|
|
41
|
+
WHERE source_key IS NOT NULL
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { tableHasColumn } from "./schema-introspection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Add `fork_strategy` to `conversations`, the discriminator that says whether
|
|
6
|
+
* a fork's inherited history lives on the fork (copied) or on its parent
|
|
7
|
+
* (read through `fork_parent_message_id`).
|
|
8
|
+
*
|
|
9
|
+
* The column is required even though `fork_parent_conversation_id` and
|
|
10
|
+
* `fork_parent_message_id` already exist, because both are set on copied
|
|
11
|
+
* forks too. Without a discriminator, a lineage read cannot tell a fork whose
|
|
12
|
+
* prefix it must fetch from the parent from one that already holds its own
|
|
13
|
+
* copy of that prefix, and would return the copied rows twice.
|
|
14
|
+
*
|
|
15
|
+
* Nullable with no backfill: every fork that exists when this runs was
|
|
16
|
+
* materialized by copying, and `isReferentialFork` reads NULL as `cloning`.
|
|
17
|
+
* A backfill would have to write a value to every historical conversation row
|
|
18
|
+
* to encode what the absence of a value already means.
|
|
19
|
+
*
|
|
20
|
+
* Idempotent: the column add is guarded with `tableHasColumn`, so a crash
|
|
21
|
+
* partway through does not break the next boot.
|
|
22
|
+
*/
|
|
23
|
+
export function migrateAddConversationForkStrategy(database: DrizzleDb): void {
|
|
24
|
+
if (!tableHasColumn(database, "conversations", "fork_strategy")) {
|
|
25
|
+
database.run(`ALTER TABLE conversations ADD COLUMN fork_strategy TEXT`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -37,6 +37,16 @@ export const conversations = sqliteTable(
|
|
|
37
37
|
originInterface: text("origin_interface"),
|
|
38
38
|
forkParentConversationId: text("fork_parent_conversation_id"),
|
|
39
39
|
forkParentMessageId: text("fork_parent_message_id"),
|
|
40
|
+
/**
|
|
41
|
+
* How this conversation's fork was materialized: `reference` means the
|
|
42
|
+
* rows at-or-before `forkParentMessageId` live on the parent and are read
|
|
43
|
+
* through it, `cloning` (and NULL, the value every pre-existing fork
|
|
44
|
+
* carries) means they were physically copied onto this row. Read through
|
|
45
|
+
* `isReferentialFork` in `conversation-lineage.ts`, never directly: the
|
|
46
|
+
* NULL-is-cloning default is what keeps existing forks from being read
|
|
47
|
+
* twice, once from their copies and once through their parent.
|
|
48
|
+
*/
|
|
49
|
+
forkStrategy: text("fork_strategy"),
|
|
40
50
|
/**
|
|
41
51
|
* Id of the conversation that spawned this one (subagent spawns stamp
|
|
42
52
|
* their parent's conversation id). Distinct from
|
|
@@ -45,6 +55,28 @@ export const conversations = sqliteTable(
|
|
|
45
55
|
* conversations not spawned by another conversation.
|
|
46
56
|
*/
|
|
47
57
|
parentConversationId: text("parent_conversation_id"),
|
|
58
|
+
/**
|
|
59
|
+
* Role the subagent that owns this conversation was spawned with, from
|
|
60
|
+
* `SUBAGENT_ROLE_REGISTRY` (`researcher`, `builder`, `advisor`); a spawn
|
|
61
|
+
* that named no role records the default, `builder`. NULL for every
|
|
62
|
+
* conversation that is not a subagent.
|
|
63
|
+
*
|
|
64
|
+
* Denormalized here rather than read from the `subagents` table at
|
|
65
|
+
* telemetry-flush time on purpose: `subagents` rows are deleted on
|
|
66
|
+
* dispose (TTL sweep ~30 minutes after the run goes terminal), while
|
|
67
|
+
* usage telemetry flushes on a watermark that can trail arbitrarily far
|
|
68
|
+
* behind after an ingest outage. Stamping the conversation row, the same
|
|
69
|
+
* rationale that put `parent_conversation_id` here, makes the
|
|
70
|
+
* attribution durable for the life of the usage row.
|
|
71
|
+
*/
|
|
72
|
+
subagentRole: text("subagent_role"),
|
|
73
|
+
/**
|
|
74
|
+
* How the subagent that owns this conversation was spawned: context
|
|
75
|
+
* inheritance and lifecycle, orthogonal to {@link subagentRole}. One of
|
|
76
|
+
* the modes described on `SubagentSpawnMode` in `subagent/types.ts`. NULL
|
|
77
|
+
* for every conversation that is not a subagent.
|
|
78
|
+
*/
|
|
79
|
+
subagentSpawnMode: text("subagent_spawn_mode"),
|
|
48
80
|
isAutoTitle: integer("is_auto_title").notNull().default(1),
|
|
49
81
|
scheduleJobId: text("schedule_job_id"),
|
|
50
82
|
lastMessageAt: integer("last_message_at"),
|
|
@@ -40,6 +40,9 @@ export const cronJobs = sqliteTable("cron_jobs", {
|
|
|
40
40
|
workflowName: text("workflow_name"), // saved workflow to trigger (nullable, only used when mode = 'workflow')
|
|
41
41
|
workflowArgsJson: text("workflow_args_json"), // JSON-encoded args passed to the workflow run (nullable)
|
|
42
42
|
capabilitiesJson: text("capabilities_json"), // JSON-encoded capability manifest for the run (nullable; null = hardcoded read-only manifest)
|
|
43
|
+
sourceKey: text("source_key"), // plugin declaration key ('plugin:<pluginName>/<scheduleName>'); null = imperative schedule
|
|
44
|
+
definitionHash: text("definition_hash"), // reconciler change detector over the declaration files (nullable, only used when source_key is set)
|
|
45
|
+
userEnabled: integer("user_enabled", { mode: "boolean" }), // user override for a sourced row's enabled state; null = declaration's value applies
|
|
43
46
|
createdAt: integer("created_at").notNull(),
|
|
44
47
|
updatedAt: integer("updated_at").notNull(),
|
|
45
48
|
});
|
package/src/persistence/steps.ts
CHANGED
|
@@ -468,6 +468,11 @@ import { migrateMoveMemorySegmentsToMemoryDb } from "./migrations/357-move-memor
|
|
|
468
468
|
import { migrateMoveMemoryEmbeddingsToMemoryDb } from "./migrations/358-move-memory-embeddings-to-memory-db.js";
|
|
469
469
|
import { migrateMoveMemorySummariesToMemoryDb } from "./migrations/359-move-memory-summaries-to-memory-db.js";
|
|
470
470
|
import { migrateAddDocumentWorkspacePath } from "./migrations/360-add-document-workspace-path.js";
|
|
471
|
+
import { migrateNormalizeManagedConnectionRows } from "./migrations/361-normalize-managed-connection-rows.js";
|
|
472
|
+
import { migrateAddConversationSubagentKind } from "./migrations/362-add-conversation-subagent-kind.js";
|
|
473
|
+
import { migrateBackfillScheduleInferenceProfile } from "./migrations/363-backfill-schedule-inference-profile.js";
|
|
474
|
+
import { migrateAddScheduleSourceKey } from "./migrations/364-add-schedule-source-key.js";
|
|
475
|
+
import { migrateAddConversationForkStrategy } from "./migrations/365-add-conversation-fork-strategy.js";
|
|
471
476
|
import type { MigrationStep } from "./migrations/run-migrations.js";
|
|
472
477
|
|
|
473
478
|
export const migrationSteps: MigrationStep[] = [
|
|
@@ -1543,4 +1548,29 @@ export const migrationSteps: MigrationStep[] = [
|
|
|
1543
1548
|
],
|
|
1544
1549
|
},
|
|
1545
1550
|
migrateAddDocumentWorkspacePath,
|
|
1551
|
+
{
|
|
1552
|
+
name: "migrateNormalizeManagedConnectionRows",
|
|
1553
|
+
run: migrateNormalizeManagedConnectionRows,
|
|
1554
|
+
// The table-exists guard treats a missing table as nothing-to-do, so
|
|
1555
|
+
// without this dependency a failed table creation followed by repair
|
|
1556
|
+
// would permanently checkpoint the normalization as a no-op.
|
|
1557
|
+
dependsOn: ["migrateCreateProviderConnections"],
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
name: "migrateAddConversationSubagentKind",
|
|
1561
|
+
run: migrateAddConversationSubagentKind,
|
|
1562
|
+
// The backfill reads the `subagents` table (migration 311), so that table
|
|
1563
|
+
// must exist and be checkpointed first.
|
|
1564
|
+
dependsOn: ["migrateCreateSubagentsTable"],
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
name: "migrateBackfillScheduleInferenceProfile",
|
|
1568
|
+
run: migrateBackfillScheduleInferenceProfile,
|
|
1569
|
+
// The column-exists guard treats a missing column as nothing-to-do, so
|
|
1570
|
+
// without this dependency a failed column add followed by repair would
|
|
1571
|
+
// permanently checkpoint the backfill as a no-op.
|
|
1572
|
+
dependsOn: ["migrateScheduleInferenceProfile"],
|
|
1573
|
+
},
|
|
1574
|
+
migrateAddScheduleSourceKey,
|
|
1575
|
+
migrateAddConversationForkStrategy,
|
|
1546
1576
|
];
|