@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,605 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Level-based reconciler converging plugin `schedules/` declarations into
|
|
3
|
+
* `cron_jobs` rows.
|
|
4
|
+
*
|
|
5
|
+
* Each pass enumerates the installed, enabled plugins, parses their
|
|
6
|
+
* declarations (see `./plugin-schedule-declarations.ts`), and diffs the
|
|
7
|
+
* desired set against the sourced rows in the store: new declarations are
|
|
8
|
+
* inserted, changed ones (by `definition_hash`) updated, and rows whose
|
|
9
|
+
* declaration is gone (or, for a script row, no longer parses) are disarmed
|
|
10
|
+
* in place. The reconciler owns only the definition columns; the engine owns
|
|
11
|
+
* the runtime columns and the user owns `user_enabled` (both enforced in
|
|
12
|
+
* `./schedule-store.ts`). Rows with a null `source_key` are imperative
|
|
13
|
+
* schedules and are never touched.
|
|
14
|
+
*
|
|
15
|
+
* Triggers: daemon startup (`daemon/lifecycle.ts`), plugin-set convergence
|
|
16
|
+
* (`plugins/mtime-cache.ts` after an imperative source reconcile), the plugin
|
|
17
|
+
* enable/disable routes (`runtime/routes/plugins-routes.ts`), and a periodic
|
|
18
|
+
* backstop sweep (`runtime/http-server.ts`) that covers the sentinel writers
|
|
19
|
+
* outside the daemon, notably the CLI's own enable/disable.
|
|
20
|
+
*
|
|
21
|
+
* Rows this pass has not caught up with are still safe to leave armed: the
|
|
22
|
+
* scheduler re-reads the `.disabled` sentinel at fire time and the run-now
|
|
23
|
+
* route re-probes the declaration, so reconcile lag delays bookkeeping rather
|
|
24
|
+
* than letting a disabled plugin execute.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { existsSync } from "node:fs";
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
|
|
30
|
+
import { getDbMigrationReadiness } from "../daemon/daemon-readiness.js";
|
|
31
|
+
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
32
|
+
import type { AttentionHints } from "../notifications/signal.js";
|
|
33
|
+
import { isPluginDisabled } from "../plugins/disabled-state.js";
|
|
34
|
+
import { parsePluginManifest } from "../plugins/external-plugin-loader.js";
|
|
35
|
+
import { listInstalledPluginDirs } from "../plugins/installed-plugin-dirs.js";
|
|
36
|
+
import { getLogger } from "../util/logger.js";
|
|
37
|
+
import {
|
|
38
|
+
type DeclarationError,
|
|
39
|
+
parsePluginScheduleDeclarations,
|
|
40
|
+
type ScheduleDeclaration,
|
|
41
|
+
} from "./plugin-schedule-declarations.js";
|
|
42
|
+
import { isPluginSchedulesEnabled } from "./plugin-schedules-gate.js";
|
|
43
|
+
import {
|
|
44
|
+
type DeclaredScheduleDefinition,
|
|
45
|
+
disarmDeclaredSchedule,
|
|
46
|
+
listDeclaredSchedules,
|
|
47
|
+
type ScheduleJob,
|
|
48
|
+
upsertDeclaredSchedule,
|
|
49
|
+
} from "./schedule-store.js";
|
|
50
|
+
|
|
51
|
+
const log = getLogger("plugin-schedule-reconciler");
|
|
52
|
+
|
|
53
|
+
/** In-flight pass; concurrent triggers await it rather than racing. */
|
|
54
|
+
let reconcileInFlight: Promise<void> | null = null;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Converge plugin-declared schedules against the current on-disk plugin set.
|
|
58
|
+
*
|
|
59
|
+
* Single-flight: concurrent triggers serialize through the in-flight latch,
|
|
60
|
+
* so two passes never interleave their list/diff/write sequences. Never
|
|
61
|
+
* throws: a failed pass is logged and the next trigger retries from disk.
|
|
62
|
+
* No-ops while DB migrations are unready.
|
|
63
|
+
*/
|
|
64
|
+
export async function reconcilePluginSchedules(): Promise<void> {
|
|
65
|
+
while (reconcileInFlight !== null) {
|
|
66
|
+
await reconcileInFlight;
|
|
67
|
+
}
|
|
68
|
+
reconcileInFlight = (async () => {
|
|
69
|
+
try {
|
|
70
|
+
await runReconcilePass();
|
|
71
|
+
} catch (err) {
|
|
72
|
+
log.error({ err }, "Plugin schedule reconcile failed");
|
|
73
|
+
}
|
|
74
|
+
})().finally(() => {
|
|
75
|
+
reconcileInFlight = null;
|
|
76
|
+
});
|
|
77
|
+
await reconcileInFlight;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface DesiredEntry {
|
|
81
|
+
pluginName: string;
|
|
82
|
+
declaration: ScheduleDeclaration;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface CollectedDeclarations {
|
|
86
|
+
desired: Map<string, DesiredEntry>;
|
|
87
|
+
errors: DeclarationError[];
|
|
88
|
+
manifestFailures: DeclarationError[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function runReconcilePass(): Promise<void> {
|
|
92
|
+
// The pass is pure DB reads/writes over cron_jobs; refuse to touch a
|
|
93
|
+
// partially-migrated schema (see assistant/CLAUDE.md, DB migration
|
|
94
|
+
// readiness gating). A skipped pass is retried by the periodic sweep.
|
|
95
|
+
if (!getDbMigrationReadiness().ready) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// The feature flag is a kill switch, not just a launch gate. While it is
|
|
100
|
+
// off nothing is parsed and nothing is emitted, and the desired set is
|
|
101
|
+
// empty, so the disarm branch below turns every declared row off on the next
|
|
102
|
+
// pass. Turning the flag back on re-arms them from their declarations.
|
|
103
|
+
const { desired, errors, manifestFailures }: CollectedDeclarations =
|
|
104
|
+
isPluginSchedulesEnabled()
|
|
105
|
+
? await collectDesiredDeclarations()
|
|
106
|
+
: { desired: new Map(), errors: [], manifestFailures: [] };
|
|
107
|
+
|
|
108
|
+
const existingByKey = new Map<string, ScheduleJob>();
|
|
109
|
+
for (const row of listDeclaredSchedules()) {
|
|
110
|
+
if (row.sourceKey !== null) {
|
|
111
|
+
existingByKey.set(row.sourceKey, row);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (const [sourceKey, { pluginName, declaration }] of desired) {
|
|
116
|
+
const row = existingByKey.get(sourceKey);
|
|
117
|
+
const definition = toDefinition(declaration);
|
|
118
|
+
const definitionChanged =
|
|
119
|
+
row !== undefined && row.definitionHash !== definition.definitionHash;
|
|
120
|
+
const changesArmedRow = definitionChanged && row.enabled;
|
|
121
|
+
// A definition change can also arm a row that was disarmed (e.g. an
|
|
122
|
+
// upgrade flipping the declared `enabled` from false to true). That
|
|
123
|
+
// transition gets the arrival notification below; a plain re-enable or
|
|
124
|
+
// reinstall re-link (hash unchanged) stays quiet.
|
|
125
|
+
const armsDisarmedRow = definitionChanged && !row.enabled;
|
|
126
|
+
// The change notice fires once, and by the next pass the new hash is
|
|
127
|
+
// already stored, so `changesArmedRow` can never bring it back. A pending
|
|
128
|
+
// entry means the emit never reached a pipeline verdict; it is retried
|
|
129
|
+
// while the row is still there and still armed for this definition.
|
|
130
|
+
const pendingChanged =
|
|
131
|
+
row !== undefined &&
|
|
132
|
+
row.enabled &&
|
|
133
|
+
isEmitPending("changed", sourceKey, definition.definitionHash);
|
|
134
|
+
let applied: ScheduleJob;
|
|
135
|
+
try {
|
|
136
|
+
applied = await upsertDeclaredSchedule(sourceKey, definition);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
log.error(
|
|
139
|
+
{ err, sourceKey },
|
|
140
|
+
"Failed to apply declared schedule, skipping",
|
|
141
|
+
);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (changesArmedRow || pendingChanged) {
|
|
145
|
+
emitDefinitionChanged(pluginName, declaration);
|
|
146
|
+
}
|
|
147
|
+
// A row arming without consent must never be silent: a daemon-route
|
|
148
|
+
// install/upgrade has no interactive consent prompt, and even a CLI
|
|
149
|
+
// upgrade only confirms what it staged. The arrival notification is the
|
|
150
|
+
// consent surface for those paths; a CLI install that already prompted
|
|
151
|
+
// gets one redundant, hash-deduped notification. A re-linked row (its
|
|
152
|
+
// `source_key` already existed, e.g. reinstall or re-enable) is not new
|
|
153
|
+
// unless its definition change is what armed it. A pending entry means a
|
|
154
|
+
// prior emit for this exact definition never reached a pipeline verdict,
|
|
155
|
+
// so the notification is re-attempted even though the row already exists.
|
|
156
|
+
const pendingDeclared = isEmitPending(
|
|
157
|
+
"declared",
|
|
158
|
+
sourceKey,
|
|
159
|
+
definition.definitionHash,
|
|
160
|
+
);
|
|
161
|
+
if (
|
|
162
|
+
applied.enabled &&
|
|
163
|
+
(row === undefined || armsDisarmedRow || pendingDeclared)
|
|
164
|
+
) {
|
|
165
|
+
emitScheduleDeclared(pluginName, declaration);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// A key with a declaration error keeps its last-good row untouched: fail
|
|
170
|
+
// closed means the broken declaration does not load, not that a previously
|
|
171
|
+
// healthy schedule is torn down by its own typo. An execute row can be held
|
|
172
|
+
// that way because it fires the message stored on the row, which is the one
|
|
173
|
+
// that last validated.
|
|
174
|
+
//
|
|
175
|
+
// A script row cannot: it fires its entrypoint by absolute path, so holding
|
|
176
|
+
// it armed through an invalid declaration runs whatever `index.sh` now
|
|
177
|
+
// holds, which is exactly the content that failed to validate. Those rows
|
|
178
|
+
// disarm through the same path as an absent declaration and re-arm on the
|
|
179
|
+
// pass after the declaration parses again. An `ended` recurrence is not a
|
|
180
|
+
// rewrite, so it keeps the last-good handling in both modes.
|
|
181
|
+
const erroredKeys = new Set<string>();
|
|
182
|
+
for (const error of errors) {
|
|
183
|
+
const row = existingByKey.get(error.sourceKey);
|
|
184
|
+
const disarmsScriptRow = error.kind === "invalid" && row?.mode === "script";
|
|
185
|
+
if (!disarmsScriptRow) {
|
|
186
|
+
erroredKeys.add(error.sourceKey);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Disarm rows whose declaration is absent from the desired set (plugin
|
|
191
|
+
// uninstalled or disabled, or the schedule file removed), plus the script
|
|
192
|
+
// rows an invalid declaration left out of `erroredKeys` above. Teardown
|
|
193
|
+
// deliberately does not ride plugin shutdown hooks: uninstalling a disabled
|
|
194
|
+
// plugin skips them entirely (`cli/lib/uninstall-plugin.ts`), so
|
|
195
|
+
// directory-absence diffing here is the only reliable reap. The row and its
|
|
196
|
+
// runs are kept so a reinstall re-links by `source_key`.
|
|
197
|
+
//
|
|
198
|
+
// `pausedKeys` collects what this pass actually turned off, which is what
|
|
199
|
+
// the notification below reports. A row the user or the engine had already
|
|
200
|
+
// disabled is left alone, and a failed write leaves the row armed, so
|
|
201
|
+
// neither claims a pause that did not happen.
|
|
202
|
+
const pausedKeys = new Set<string>();
|
|
203
|
+
for (const [sourceKey, row] of existingByKey) {
|
|
204
|
+
if (desired.has(sourceKey) || erroredKeys.has(sourceKey)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
if (await disarmDeclaredSchedule(row.id)) {
|
|
209
|
+
pausedKeys.add(sourceKey);
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
log.error(
|
|
213
|
+
{ err, sourceKey, scheduleId: row.id },
|
|
214
|
+
"Failed to disarm declared schedule, skipping",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Manifest failures ride the same emit path as declaration errors but are
|
|
220
|
+
// kept out of `erroredKeys` above: an errored key preserves its last-good
|
|
221
|
+
// row, while a manifest failure must let the disarm loop pause the rows.
|
|
222
|
+
for (const error of [...errors, ...manifestFailures]) {
|
|
223
|
+
if (isCompletedRecurrence(error, existingByKey.get(error.sourceKey))) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
emitDefinitionError(error, pausedKeys.has(error.sourceKey));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* True when an `ended` recurrence is the expected end of a bounded schedule
|
|
232
|
+
* rather than something to tell the user about. Three rows qualify: one the
|
|
233
|
+
* engine latched on its last occurrence, which zeroes `next_run_at` with a
|
|
234
|
+
* run behind it; one the user turned off; and one that shipped disabled and
|
|
235
|
+
* was never armed, which has `next_run_at` zeroed with no run behind it and
|
|
236
|
+
* no user choice recorded. None of them lost a firing the user expected.
|
|
237
|
+
*
|
|
238
|
+
* A row this reconciler disarmed does not qualify, and cannot be mistaken
|
|
239
|
+
* for the never-armed one because it keeps the stale non-zero `next_run_at`
|
|
240
|
+
* it was armed with. `enabled = false` there means the plugin is disabled or
|
|
241
|
+
* its declaration was briefly absent, and once the plugin comes back the
|
|
242
|
+
* ended recurrence is a dead schedule the user has to hear about. An ended
|
|
243
|
+
* declaration whose row is still armed loses firings, and one with no row at
|
|
244
|
+
* all arrives dead; both surface.
|
|
245
|
+
*/
|
|
246
|
+
function isCompletedRecurrence(
|
|
247
|
+
error: DeclarationError,
|
|
248
|
+
row: ScheduleJob | undefined,
|
|
249
|
+
): boolean {
|
|
250
|
+
if (error.kind !== "ended" || row === undefined) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
const engineLatched = row.nextRunAt === 0 && row.lastRunAt != null;
|
|
254
|
+
const insertedDisabled =
|
|
255
|
+
!row.enabled &&
|
|
256
|
+
row.nextRunAt === 0 &&
|
|
257
|
+
row.lastRunAt == null &&
|
|
258
|
+
row.userEnabled == null;
|
|
259
|
+
return engineLatched || row.userEnabled === false || insertedDisabled;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Enumerate installed plugins ({@link listInstalledPluginDirs}, the same walk
|
|
264
|
+
* as the plugin source collector) and gather their schedule declarations.
|
|
265
|
+
* Identity = the directory basename, mirroring `parsePluginManifest`.
|
|
266
|
+
*
|
|
267
|
+
* Disabled plugins and plugins without a `schedules/` directory are skipped
|
|
268
|
+
* entirely; in particular, only schedule-declaring plugins pay the manifest
|
|
269
|
+
* parse each pass. A schedule-declaring plugin whose manifest fails
|
|
270
|
+
* `parsePluginManifest` (unreadable or schema-invalid `package.json`) also
|
|
271
|
+
* contributes nothing to the desired set: the runtime loader refuses to
|
|
272
|
+
* bring such a plugin up, so its schedules must not stay armed either. Each
|
|
273
|
+
* of its declared schedules comes back in `manifestFailures` so the pause is
|
|
274
|
+
* surfaced with the same visibility as a bad declaration.
|
|
275
|
+
*/
|
|
276
|
+
async function collectDesiredDeclarations(): Promise<CollectedDeclarations> {
|
|
277
|
+
const desired = new Map<string, DesiredEntry>();
|
|
278
|
+
const errors: DeclarationError[] = [];
|
|
279
|
+
const manifestFailures: DeclarationError[] = [];
|
|
280
|
+
|
|
281
|
+
for (const { name, dir } of listInstalledPluginDirs()) {
|
|
282
|
+
if (isPluginDisabled(name)) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (!existsSync(join(dir, "schedules"))) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const parsed = parsePluginScheduleDeclarations(dir, name);
|
|
289
|
+
if ((await parsePluginManifest(dir, { quiet: true })) === undefined) {
|
|
290
|
+
// The reason states the fault only. Whether a row went off over it is
|
|
291
|
+
// the `paused` field's job, which is derived from the disarm outcome.
|
|
292
|
+
const reason = "the plugin's package.json could not be read or validated";
|
|
293
|
+
for (const declared of [
|
|
294
|
+
...parsed.declarations.map((d) => ({
|
|
295
|
+
scheduleName: d.name,
|
|
296
|
+
sourceKey: d.sourceKey,
|
|
297
|
+
})),
|
|
298
|
+
...parsed.errors,
|
|
299
|
+
]) {
|
|
300
|
+
manifestFailures.push({
|
|
301
|
+
pluginName: name,
|
|
302
|
+
scheduleName: declared.scheduleName,
|
|
303
|
+
sourceKey: declared.sourceKey,
|
|
304
|
+
reason,
|
|
305
|
+
kind: "invalid",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
for (const declaration of parsed.declarations) {
|
|
311
|
+
desired.set(declaration.sourceKey, { pluginName: name, declaration });
|
|
312
|
+
}
|
|
313
|
+
errors.push(...parsed.errors);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return { desired, errors, manifestFailures };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function toDefinition(
|
|
320
|
+
declaration: ScheduleDeclaration,
|
|
321
|
+
): DeclaredScheduleDefinition {
|
|
322
|
+
const { config } = declaration;
|
|
323
|
+
return {
|
|
324
|
+
name: declaration.name,
|
|
325
|
+
description: config.description ?? undefined,
|
|
326
|
+
syntax: config.syntax,
|
|
327
|
+
expression: config.expression,
|
|
328
|
+
timezone: config.timezone,
|
|
329
|
+
// Script-mode runs ignore the message column (the engine executes
|
|
330
|
+
// `script`); it is non-null in the schema, so store an empty string.
|
|
331
|
+
message: declaration.message ?? "",
|
|
332
|
+
script: declaration.scriptInvocation,
|
|
333
|
+
mode: declaration.mode,
|
|
334
|
+
maxRetries: config.maxRetries ?? undefined,
|
|
335
|
+
retryBackoffMs: config.retryBackoffMs ?? undefined,
|
|
336
|
+
quiet: config.quiet ?? undefined,
|
|
337
|
+
inferenceProfile: config.inferenceProfile,
|
|
338
|
+
timeoutMs: config.timeoutMs,
|
|
339
|
+
enabled: config.enabled,
|
|
340
|
+
definitionHash: declaration.definitionHash,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Same shape as the background-job failure notification: passive home-feed
|
|
345
|
+
// surfacing, no action required.
|
|
346
|
+
const DEFINITION_NOTIFICATION_HINTS: AttentionHints = {
|
|
347
|
+
requiresAction: false,
|
|
348
|
+
urgency: "medium",
|
|
349
|
+
isAsyncBackground: true,
|
|
350
|
+
visibleInSourceNow: false,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Emit one definition-lifecycle signal. Resolves `true` when the pipeline
|
|
355
|
+
* reached a verdict (dispatched, deduplicated, or suppressed) and `false`
|
|
356
|
+
* when it failed outright, so a caller latching a dedupe guard can retry a
|
|
357
|
+
* transient failure on a later pass.
|
|
358
|
+
*/
|
|
359
|
+
async function emitDefinitionSignal(
|
|
360
|
+
sourceEventName: string,
|
|
361
|
+
sourceKey: string,
|
|
362
|
+
dedupeKey: string,
|
|
363
|
+
contextPayload: Record<string, unknown>,
|
|
364
|
+
): Promise<boolean> {
|
|
365
|
+
try {
|
|
366
|
+
const result = await emitNotificationSignal({
|
|
367
|
+
sourceChannel: "scheduler",
|
|
368
|
+
sourceContextId: sourceKey,
|
|
369
|
+
sourceEventName,
|
|
370
|
+
dedupeKey,
|
|
371
|
+
contextPayload,
|
|
372
|
+
attentionHints: DEFINITION_NOTIFICATION_HINTS,
|
|
373
|
+
});
|
|
374
|
+
// emitNotificationSignal resolves (never rejects) and flags a pipeline
|
|
375
|
+
// error as `pipelineFailed`; every other outcome, including dedupe and
|
|
376
|
+
// suppression, is a verdict.
|
|
377
|
+
return !result.pipelineFailed;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
log.warn(
|
|
380
|
+
{ err, sourceKey, sourceEventName },
|
|
381
|
+
"Failed to emit schedule definition notification",
|
|
382
|
+
);
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* UTC day of the last completed definition-error emit per `sourceKey`. A
|
|
389
|
+
* persistently broken declaration re-surfaces on every pass (60s sweep); the
|
|
390
|
+
* downstream daily dedupe key would drop the repeats, but this guard skips
|
|
391
|
+
* the emit call entirely so steady-state passes do no notification work.
|
|
392
|
+
*/
|
|
393
|
+
const definitionErrorEmittedDay = new Map<string, string>();
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* The definition-lifecycle events whose emit is retried until a pipeline
|
|
397
|
+
* verdict. `declared` is the consent surface for unattended arming
|
|
398
|
+
* (daemon-route install/upgrade) and `changed` is the only notice that an
|
|
399
|
+
* armed schedule's definition was rewritten; both are one-shot per definition
|
|
400
|
+
* hash. `error` is one-shot per UTC day and carries that day in place of a
|
|
401
|
+
* hash. Whatever triggered each is stored before the emit, so a transient
|
|
402
|
+
* pipeline failure leaves a marker the next pass acts on rather than losing
|
|
403
|
+
* the notification.
|
|
404
|
+
*/
|
|
405
|
+
type TrackedEmitKind = "declared" | "changed" | "error";
|
|
406
|
+
|
|
407
|
+
function trackedEmitKey(kind: TrackedEmitKind, sourceKey: string): string {
|
|
408
|
+
return `${kind}:${sourceKey}`;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Token of a tracked emit that has not yet reached a pipeline verdict, keyed
|
|
413
|
+
* by {@link trackedEmitKey}: the definition hash the emit describes, or the
|
|
414
|
+
* UTC day for an `error`. Declared entries are kept across disarm so a
|
|
415
|
+
* reinstall re-delivers a never-delivered consent notification; the map is
|
|
416
|
+
* bounded by distinct declared schedules seen in this process. In-memory only:
|
|
417
|
+
* a daemon restart inside the failure window loses the retry, a residual
|
|
418
|
+
* accepted over persisting a marker.
|
|
419
|
+
*/
|
|
420
|
+
const pendingEmits = new Map<string, string>();
|
|
421
|
+
|
|
422
|
+
/** Keys whose emit is awaiting its pipeline result. */
|
|
423
|
+
const inFlightEmits = new Set<string>();
|
|
424
|
+
|
|
425
|
+
function isEmitPending(
|
|
426
|
+
kind: TrackedEmitKind,
|
|
427
|
+
sourceKey: string,
|
|
428
|
+
definitionHash: string,
|
|
429
|
+
): boolean {
|
|
430
|
+
return pendingEmits.get(trackedEmitKey(kind, sourceKey)) === definitionHash;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Emit one tracked event, holding a pending marker until the pipeline reaches
|
|
435
|
+
* a verdict. Emits for a key are serialized: while one is still evaluating, a
|
|
436
|
+
* later pass does not start a second. A duplicate would hit event-store dedupe
|
|
437
|
+
* and resolve as a verdict of its own, clearing the marker the original still
|
|
438
|
+
* needs if it goes on to fail and release the dedupe key. Skipping leaves the
|
|
439
|
+
* marker in place, so a pass after the in-flight attempt settles retries it.
|
|
440
|
+
*/
|
|
441
|
+
function trackedEmit(
|
|
442
|
+
kind: TrackedEmitKind,
|
|
443
|
+
sourceKey: string,
|
|
444
|
+
token: string,
|
|
445
|
+
emit: () => Promise<boolean>,
|
|
446
|
+
): void {
|
|
447
|
+
const key = trackedEmitKey(kind, sourceKey);
|
|
448
|
+
pendingEmits.set(key, token);
|
|
449
|
+
if (inFlightEmits.has(key)) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
inFlightEmits.add(key);
|
|
453
|
+
void emit()
|
|
454
|
+
.then((verdict) => {
|
|
455
|
+
if (verdict && pendingEmits.get(key) === token) {
|
|
456
|
+
pendingEmits.delete(key);
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
.finally(() => {
|
|
460
|
+
inFlightEmits.delete(key);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Test-only: forget emit-guard state (error day-latch and tracked emits). */
|
|
465
|
+
export function resetDefinitionErrorEmitGuardForTests(): void {
|
|
466
|
+
definitionErrorEmittedDay.clear();
|
|
467
|
+
pendingEmits.clear();
|
|
468
|
+
inFlightEmits.clear();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Surface a malformed declaration (or a manifest failure pausing one), with
|
|
473
|
+
* `paused` set when this pass disarmed the row over it. Deduped per schedule
|
|
474
|
+
* per UTC day so a broken file doesn't spam on every pass. The day guard
|
|
475
|
+
* latches only after the emit resolves with a pipeline verdict; a transient
|
|
476
|
+
* pipeline failure is retried on the next pass instead of being silenced for
|
|
477
|
+
* the rest of the day. Attempts are serialized like the other tracked emits,
|
|
478
|
+
* so a second pass during an in-flight first one cannot latch the day on a
|
|
479
|
+
* deduped verdict while the original goes on to fail.
|
|
480
|
+
*/
|
|
481
|
+
function emitDefinitionError(error: DeclarationError, paused: boolean): void {
|
|
482
|
+
const day = new Date().toISOString().slice(0, 10);
|
|
483
|
+
if (definitionErrorEmittedDay.get(error.sourceKey) === day) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
trackedEmit("error", error.sourceKey, day, () =>
|
|
487
|
+
emitDefinitionSignal(
|
|
488
|
+
"schedule.definition_error",
|
|
489
|
+
error.sourceKey,
|
|
490
|
+
`schedule-definition-error:${error.sourceKey}:${day}`,
|
|
491
|
+
{
|
|
492
|
+
pluginName: error.pluginName,
|
|
493
|
+
scheduleName: error.scheduleName,
|
|
494
|
+
sourceKey: error.sourceKey,
|
|
495
|
+
reason: error.reason,
|
|
496
|
+
paused,
|
|
497
|
+
},
|
|
498
|
+
).then((completed) => {
|
|
499
|
+
if (completed) {
|
|
500
|
+
definitionErrorEmittedDay.set(error.sourceKey, day);
|
|
501
|
+
}
|
|
502
|
+
return completed;
|
|
503
|
+
}),
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Surface a plugin upgrade rewriting an armed schedule's definition, so the
|
|
509
|
+
* user learns the thing firing on their behalf changed. Deduped by the new
|
|
510
|
+
* hash so concurrent triggers emit once per change, and retried while the
|
|
511
|
+
* pipeline has not reached a verdict.
|
|
512
|
+
*/
|
|
513
|
+
function emitDefinitionChanged(
|
|
514
|
+
pluginName: string,
|
|
515
|
+
declaration: ScheduleDeclaration,
|
|
516
|
+
): void {
|
|
517
|
+
trackedEmit(
|
|
518
|
+
"changed",
|
|
519
|
+
declaration.sourceKey,
|
|
520
|
+
declaration.definitionHash,
|
|
521
|
+
() =>
|
|
522
|
+
emitDefinitionSignal(
|
|
523
|
+
"schedule.definition_changed",
|
|
524
|
+
declaration.sourceKey,
|
|
525
|
+
`schedule-definition-changed:${declaration.sourceKey}:${declaration.definitionHash}`,
|
|
526
|
+
{
|
|
527
|
+
pluginName,
|
|
528
|
+
scheduleName: declaration.name,
|
|
529
|
+
sourceKey: declaration.sourceKey,
|
|
530
|
+
},
|
|
531
|
+
),
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Surface a declared schedule arming (its row was created by this pass, or a
|
|
537
|
+
* definition change armed a disarmed row). Deduped by definition hash, so
|
|
538
|
+
* the CLI-install case (which already prompted for consent) collapses to a
|
|
539
|
+
* single notification and concurrent triggers emit once.
|
|
540
|
+
*/
|
|
541
|
+
function emitScheduleDeclared(
|
|
542
|
+
pluginName: string,
|
|
543
|
+
declaration: ScheduleDeclaration,
|
|
544
|
+
): void {
|
|
545
|
+
trackedEmit(
|
|
546
|
+
"declared",
|
|
547
|
+
declaration.sourceKey,
|
|
548
|
+
declaration.definitionHash,
|
|
549
|
+
() =>
|
|
550
|
+
emitDefinitionSignal(
|
|
551
|
+
"schedule.declared",
|
|
552
|
+
declaration.sourceKey,
|
|
553
|
+
`schedule-declared:${declaration.sourceKey}:${declaration.definitionHash}`,
|
|
554
|
+
{
|
|
555
|
+
pluginName,
|
|
556
|
+
scheduleName: declaration.name,
|
|
557
|
+
sourceKey: declaration.sourceKey,
|
|
558
|
+
cadence: declaration.config.expression,
|
|
559
|
+
},
|
|
560
|
+
),
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// ── Periodic backstop sweep ─────────────────────────────────────────────
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Backstop interval. Enable/disable flows write the `.disabled` sentinel
|
|
568
|
+
* without poking the plugin source reconcile, so this bounds how long a
|
|
569
|
+
* toggle can go unreflected in the rows.
|
|
570
|
+
*/
|
|
571
|
+
const SWEEP_INTERVAL_MS = 60_000;
|
|
572
|
+
|
|
573
|
+
let sweepTimer: ReturnType<typeof setInterval> | null = null;
|
|
574
|
+
|
|
575
|
+
/** Guard against a slow pass stacking further passes behind the latch. */
|
|
576
|
+
let sweepInProgress = false;
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Start the periodic reconcile sweep. Idempotent: repeat calls reuse the
|
|
580
|
+
* timer. The DB-readiness guard lives inside {@link reconcilePluginSchedules},
|
|
581
|
+
* which no-ops while migrations are unready.
|
|
582
|
+
*/
|
|
583
|
+
export function startPluginScheduleReconcileSweep(): void {
|
|
584
|
+
if (sweepTimer) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
sweepTimer = setInterval(() => {
|
|
588
|
+
if (sweepInProgress) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
sweepInProgress = true;
|
|
592
|
+
void reconcilePluginSchedules().finally(() => {
|
|
593
|
+
sweepInProgress = false;
|
|
594
|
+
});
|
|
595
|
+
}, SWEEP_INTERVAL_MS);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** Stop the periodic reconcile sweep. Used in tests and shutdown. */
|
|
599
|
+
export function stopPluginScheduleReconcileSweep(): void {
|
|
600
|
+
if (sweepTimer) {
|
|
601
|
+
clearInterval(sweepTimer);
|
|
602
|
+
sweepTimer = null;
|
|
603
|
+
}
|
|
604
|
+
sweepInProgress = false;
|
|
605
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
2
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
3
|
+
|
|
4
|
+
const PLUGIN_SCHEDULES_FLAG_KEY = "plugin-schedules" as const;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Whether plugins may declare schedules that register and run automatically.
|
|
8
|
+
*
|
|
9
|
+
* The flag is a kill switch, not just a launch gate. `reconcilePluginSchedules`
|
|
10
|
+
* treats an off flag as an empty desired set and still runs its disarm branch,
|
|
11
|
+
* so the next pass after turning the flag off disarms every declared row and
|
|
12
|
+
* the next pass after turning it back on re-arms them. The fire-time guard in
|
|
13
|
+
* `./scheduler.ts` and the run-now guard in
|
|
14
|
+
* `../runtime/routes/schedule-routes.ts` close the window between the toggle
|
|
15
|
+
* and that pass.
|
|
16
|
+
*
|
|
17
|
+
* The CLI's install-consent listing (`../cli/lib/plugin-surfaces.ts`) is not
|
|
18
|
+
* gated: it only reports what a plugin's files declare, and the reconciler is
|
|
19
|
+
* the point where a declaration becomes something that runs.
|
|
20
|
+
*/
|
|
21
|
+
export function isPluginSchedulesEnabled(config?: AssistantConfig): boolean {
|
|
22
|
+
return isAssistantFeatureFlagEnabled(PLUGIN_SCHEDULES_FLAG_KEY, config);
|
|
23
|
+
}
|