@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
|
@@ -1,16 +1,77 @@
|
|
|
1
1
|
import { validateInferenceProfileKey } from "../config/inference-profile-validation.js";
|
|
2
|
+
import { resolveDefaultProfileKey } from "../config/llm-resolver.js";
|
|
3
|
+
import { getConfig } from "../config/loader.js";
|
|
4
|
+
import {
|
|
5
|
+
type DurableProfileFields,
|
|
6
|
+
resolveDurableProfile,
|
|
7
|
+
} from "../persistence/conversation-crud.js";
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Validate a schedule's inference-profile key against the configured
|
|
5
11
|
* `llm.profiles` catalog. Returns a user-facing error message when the key is
|
|
6
12
|
* empty or unknown, or `null` when valid.
|
|
7
13
|
*
|
|
8
|
-
* Only create/update paths validate
|
|
9
|
-
*
|
|
10
|
-
* missing `overrideProfile` reference and falls through to the defaults
|
|
14
|
+
* Only create/update paths validate. A profile deleted after a schedule was
|
|
15
|
+
* pinned to it degrades gracefully at run time: the resolver silently drops a
|
|
16
|
+
* missing `overrideProfile` reference and falls through to the defaults, so
|
|
17
|
+
* the schedule keeps firing on the call site's own model selection.
|
|
11
18
|
*/
|
|
12
19
|
export function validateScheduleInferenceProfile(
|
|
13
20
|
profile: string,
|
|
14
21
|
): string | null {
|
|
15
22
|
return validateInferenceProfileKey(profile);
|
|
16
23
|
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The inference-profile key a schedule is pinned to when its creator names
|
|
27
|
+
* none, and the key a `null` update re-snapshots to.
|
|
28
|
+
*
|
|
29
|
+
* Every schedule carries a concrete profile so its cost is stable: an unpinned
|
|
30
|
+
* schedule would follow `llm.activeProfile`, silently moving to a different
|
|
31
|
+
* model (and a different price) whenever the user changes their global
|
|
32
|
+
* default. Snapshotting the resolved default at write time keeps the schedule
|
|
33
|
+
* on the model it was created under until someone re-pins it.
|
|
34
|
+
*
|
|
35
|
+
* `mainAgent` is the call site scheduled turns run under (see `scheduler.ts`).
|
|
36
|
+
* Returns `null` when the resolved winner is the code-owned anchor rather than
|
|
37
|
+
* a named profile: there is no key to record, and the run resolves through the
|
|
38
|
+
* `mainAgent` call-site configuration exactly as an unpinned schedule did.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveDefaultScheduleInferenceProfile(): string | null {
|
|
41
|
+
return resolveDefaultProfileKey("mainAgent", getConfig().llm) ?? null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The profile a wake schedule pins when its creator names none.
|
|
46
|
+
*
|
|
47
|
+
* A wake resumes an existing conversation and forces the row's pin as the
|
|
48
|
+
* woken turn's override, so the target's own choice, not the global default,
|
|
49
|
+
* is what the user expects it to fire on. Only a *durable* pin seeds the row
|
|
50
|
+
* ({@link resolveDurableProfile}): a TTL-limited profile session is a
|
|
51
|
+
* deliberately temporary choice, and freezing one into a row that fires next
|
|
52
|
+
* week would keep billing that model long after the session lapsed. A target
|
|
53
|
+
* with no durable pin falls through to the same default every other schedule
|
|
54
|
+
* snapshots.
|
|
55
|
+
*
|
|
56
|
+
* Both the create path and the backfill of rows that predate it resolve
|
|
57
|
+
* through here, so a pending wake keeps firing on the profile it would have
|
|
58
|
+
* resolved live.
|
|
59
|
+
*
|
|
60
|
+
* @param target The wake's target conversation, or null when it has none.
|
|
61
|
+
*/
|
|
62
|
+
export function resolveWakeScheduleInferenceProfile(
|
|
63
|
+
target: DurableProfileFields | null,
|
|
64
|
+
): string | null {
|
|
65
|
+
return (
|
|
66
|
+
resolveDurableProfile(target) ?? resolveDefaultScheduleInferenceProfile()
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Render a schedule's pinned profile for CLI and tool output. A `null` pin has
|
|
72
|
+
* no named profile behind it, so it is reported as the call site's own
|
|
73
|
+
* selection rather than as a profile key.
|
|
74
|
+
*/
|
|
75
|
+
export function formatScheduleInferenceProfile(profile: string | null): string {
|
|
76
|
+
return profile ?? "none (mainAgent call-site default)";
|
|
77
|
+
}
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for plugin-declared schedules under `<pluginDir>/schedules/`.
|
|
3
|
+
*
|
|
4
|
+
* A declaration is a directory: `<name>/` containing `config.json` (the
|
|
5
|
+
* schedule config) plus exactly one entrypoint, `index.md` (mode `execute`,
|
|
6
|
+
* body is the prompt) or `index.sh` (mode `script`, invoked by absolute path
|
|
7
|
+
* via its shebang interpreter, or `sh` when it has none). A file sitting
|
|
8
|
+
* directly under `schedules/` is not a declaration; it yields a
|
|
9
|
+
* {@link DeclarationError} of its own so the author is told rather than left
|
|
10
|
+
* wondering why nothing loaded.
|
|
11
|
+
*
|
|
12
|
+
* Ambiguity fails closed, never resolves by precedence: a directory with zero
|
|
13
|
+
* or multiple entrypoints, or an unsupported entrypoint, yields a
|
|
14
|
+
* {@link DeclarationError} and the schedule does not load. Errors are
|
|
15
|
+
* per-schedule; one bad declaration never blocks siblings.
|
|
16
|
+
*
|
|
17
|
+
* Declared schedules are recurring only: `expression` is required, a
|
|
18
|
+
* single-fire RRULE (COUNT=1) is rejected, and there is no one-shot form.
|
|
19
|
+
*
|
|
20
|
+
* Pure filesystem + parsing. This module must not import from persistence or
|
|
21
|
+
* schedule-store; the reconciler owns all database interaction.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
|
|
30
|
+
import { isPluginDisabled } from "../plugins/disabled-state.js";
|
|
31
|
+
import { parsePluginManifest } from "../plugins/external-plugin-loader.js";
|
|
32
|
+
import { isInsidePluginRoot } from "../plugins/installed-plugin-dirs.js";
|
|
33
|
+
import { walkPluginTree } from "../plugins/plugin-tree-walk.js";
|
|
34
|
+
import { FRONTMATTER_REGEX } from "../skills/frontmatter.js";
|
|
35
|
+
import { getWorkspacePluginsDir } from "../util/platform.js";
|
|
36
|
+
import {
|
|
37
|
+
computeNextRunAt,
|
|
38
|
+
isSingleFireRRule,
|
|
39
|
+
isValidScheduleExpression,
|
|
40
|
+
validateRruleSetLines,
|
|
41
|
+
} from "./recurrence-engine.js";
|
|
42
|
+
import type { ScheduleSyntax } from "./recurrence-types.js";
|
|
43
|
+
import { normalizeScheduleSyntax } from "./recurrence-types.js";
|
|
44
|
+
import { validateScriptTimeoutMs } from "./run-script.js";
|
|
45
|
+
|
|
46
|
+
export type PluginScheduleMode = "execute" | "script";
|
|
47
|
+
|
|
48
|
+
/** Normalized schedule config parsed from a declaration's config.json. */
|
|
49
|
+
export interface PluginScheduleConfig {
|
|
50
|
+
expression: string;
|
|
51
|
+
syntax: ScheduleSyntax;
|
|
52
|
+
timezone: string | null;
|
|
53
|
+
description: string | null;
|
|
54
|
+
maxRetries: number | null;
|
|
55
|
+
retryBackoffMs: number | null;
|
|
56
|
+
quiet: boolean | null;
|
|
57
|
+
inferenceProfile: string | null;
|
|
58
|
+
timeoutMs: number | null;
|
|
59
|
+
/** Declared enabled state; defaults to true when omitted. */
|
|
60
|
+
enabled: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ScheduleDeclaration {
|
|
64
|
+
/** `plugin:<pluginName>/<scheduleName>`; the reconciler's identity key. */
|
|
65
|
+
sourceKey: string;
|
|
66
|
+
name: string;
|
|
67
|
+
mode: PluginScheduleMode;
|
|
68
|
+
/** Prompt body for `execute` mode; null for `script` mode. */
|
|
69
|
+
message: string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Shell invocation of the declaration's `index.sh`: its shebang
|
|
72
|
+
* interpreter (or `sh` when it has none) followed by the quoted absolute
|
|
73
|
+
* path, rather than the file's content. The explicit interpreter keeps
|
|
74
|
+
* the entrypoint runnable when an install path drops its exec bit. Null
|
|
75
|
+
* for `execute` mode.
|
|
76
|
+
*/
|
|
77
|
+
scriptInvocation: string | null;
|
|
78
|
+
config: PluginScheduleConfig;
|
|
79
|
+
/**
|
|
80
|
+
* Stable sha256 over the declaration's file contents and their paths
|
|
81
|
+
* relative to `schedules/`. Any edit, addition, removal, or rename of a
|
|
82
|
+
* declaration file changes the hash.
|
|
83
|
+
*/
|
|
84
|
+
definitionHash: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Why a declaration did not load.
|
|
89
|
+
*
|
|
90
|
+
* `invalid` is a broken declaration and needs a fix in the plugin. `ended` is
|
|
91
|
+
* a well-formed recurrence with nothing left to fire, its COUNT consumed or
|
|
92
|
+
* its UNTIL past. A bounded schedule that ran to completion reaches that
|
|
93
|
+
* state on its own, so the reconciler surfaces it only when the row it
|
|
94
|
+
* belongs to is still armed, or when there is no row at all.
|
|
95
|
+
*/
|
|
96
|
+
export type DeclarationErrorKind = "invalid" | "ended";
|
|
97
|
+
|
|
98
|
+
export interface DeclarationError {
|
|
99
|
+
pluginName: string;
|
|
100
|
+
scheduleName: string;
|
|
101
|
+
sourceKey: string;
|
|
102
|
+
reason: string;
|
|
103
|
+
kind: DeclarationErrorKind;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** A declaration that did not load, before it is keyed to a plugin. */
|
|
107
|
+
interface DeclarationFailure {
|
|
108
|
+
error: string;
|
|
109
|
+
kind?: DeclarationErrorKind;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ParsedPluginSchedules {
|
|
113
|
+
declarations: ScheduleDeclaration[];
|
|
114
|
+
errors: DeclarationError[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function pluginScheduleSourceKey(
|
|
118
|
+
pluginName: string,
|
|
119
|
+
scheduleName: string,
|
|
120
|
+
): string {
|
|
121
|
+
return `plugin:${pluginName}/${scheduleName}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* True when the declaration behind `sourceKey`
|
|
126
|
+
* (`plugin:<pluginName>/<scheduleName>`) is available to arm a row: its
|
|
127
|
+
* `schedules/<scheduleName>/` directory is present on disk and contained, the
|
|
128
|
+
* plugin is not disabled, and the plugin's manifest parses. Each of the latter
|
|
129
|
+
* two counts as absent for the same reason: the reconciler disarms the
|
|
130
|
+
* schedules of a disabled plugin and of one whose `package.json` the loader
|
|
131
|
+
* rejects, so neither may be re-armed from a stale row while its files sit on
|
|
132
|
+
* disk. Presence and sourceability only; the declaration's own validity is the
|
|
133
|
+
* reconciler's business.
|
|
134
|
+
*
|
|
135
|
+
* Containment is checked on both directories through
|
|
136
|
+
* {@link isInsidePluginRoot}, the same boundary the plugin loader applies: the
|
|
137
|
+
* plugin root must resolve inside the plugins directory and the declaration
|
|
138
|
+
* directory inside its plugin root. A row's stored script invocation names an
|
|
139
|
+
* absolute entrypoint path, so a link swapped in under either one would
|
|
140
|
+
* otherwise let this probe answer for one tree while the schedule executes
|
|
141
|
+
* code from another. A link that stays inside its root is a normal install
|
|
142
|
+
* layout and passes.
|
|
143
|
+
*/
|
|
144
|
+
export async function declarationExistsOnDisk(
|
|
145
|
+
sourceKey: string,
|
|
146
|
+
): Promise<boolean> {
|
|
147
|
+
const match = /^plugin:([^/]+)\/(.+)$/.exec(sourceKey);
|
|
148
|
+
if (!match) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const [, pluginName, scheduleName] = match;
|
|
152
|
+
if (isPluginDisabled(pluginName!)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const pluginsDir = getWorkspacePluginsDir();
|
|
156
|
+
const pluginDir = join(pluginsDir, pluginName!);
|
|
157
|
+
if (!isInsidePluginRoot(pluginDir, pluginsDir)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const declarationDir = join(pluginDir, "schedules", scheduleName!);
|
|
161
|
+
if (!statSync(declarationDir, { throwIfNoEntry: false })?.isDirectory()) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (!isInsidePluginRoot(declarationDir, pluginDir)) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return (await parsePluginManifest(pluginDir, { quiet: true })) !== undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const scheduleConfigSchema = z
|
|
171
|
+
.object({
|
|
172
|
+
expression: z.string().min(1),
|
|
173
|
+
expression_syntax: z.enum(["cron", "rrule"]).optional(),
|
|
174
|
+
timezone: z.string().min(1).optional(),
|
|
175
|
+
description: z.string().optional(),
|
|
176
|
+
max_retries: z.number().int().min(0).optional(),
|
|
177
|
+
retry_backoff_ms: z.number().int().min(0).optional(),
|
|
178
|
+
quiet: z.boolean().optional(),
|
|
179
|
+
inference_profile: z.string().min(1).optional(),
|
|
180
|
+
timeout_ms: z.number().int().positive().optional(),
|
|
181
|
+
enabled: z.boolean().optional(),
|
|
182
|
+
})
|
|
183
|
+
.strict();
|
|
184
|
+
|
|
185
|
+
function parseScheduleConfig(
|
|
186
|
+
raw: unknown,
|
|
187
|
+
): { config: PluginScheduleConfig } | DeclarationFailure {
|
|
188
|
+
const result = scheduleConfigSchema.safeParse(raw);
|
|
189
|
+
if (!result.success) {
|
|
190
|
+
const detail = result.error.issues
|
|
191
|
+
.map((i) => `${i.path.join(".") || "config"}: ${i.message}`)
|
|
192
|
+
.join("; ");
|
|
193
|
+
return { error: `invalid config: ${detail}` };
|
|
194
|
+
}
|
|
195
|
+
const fields = result.data;
|
|
196
|
+
|
|
197
|
+
if (fields.timeout_ms !== undefined) {
|
|
198
|
+
const timeoutError = validateScriptTimeoutMs(fields.timeout_ms);
|
|
199
|
+
if (timeoutError) {
|
|
200
|
+
return { error: timeoutError };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const resolved = normalizeScheduleSyntax({
|
|
205
|
+
syntax: fields.expression_syntax,
|
|
206
|
+
expression: fields.expression,
|
|
207
|
+
});
|
|
208
|
+
if (!resolved) {
|
|
209
|
+
return {
|
|
210
|
+
error: `could not determine syntax for expression "${fields.expression}"; set expression_syntax to "cron" or "rrule"`,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
if (resolved.syntax === "rrule") {
|
|
214
|
+
const setError = validateRruleSetLines(resolved.expression);
|
|
215
|
+
if (setError) {
|
|
216
|
+
return { error: setError };
|
|
217
|
+
}
|
|
218
|
+
if (isSingleFireRRule(resolved.expression)) {
|
|
219
|
+
return {
|
|
220
|
+
error:
|
|
221
|
+
"expression fires exactly once (COUNT=1): declared schedules must be recurring",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (
|
|
226
|
+
!isValidScheduleExpression({
|
|
227
|
+
syntax: resolved.syntax,
|
|
228
|
+
expression: resolved.expression,
|
|
229
|
+
timezone: fields.timezone ?? null,
|
|
230
|
+
})
|
|
231
|
+
) {
|
|
232
|
+
return {
|
|
233
|
+
error: `invalid ${resolved.syntax} expression "${fields.expression}"${
|
|
234
|
+
fields.timezone ? ` (timezone "${fields.timezone}")` : ""
|
|
235
|
+
}`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (resolved.syntax === "rrule") {
|
|
239
|
+
// A syntactically valid recurrence can still be exhausted (UNTIL in the
|
|
240
|
+
// past, COUNT consumed). Such a schedule has no firing left, so it fails
|
|
241
|
+
// closed here rather than surviving to throw on every reconcile upsert.
|
|
242
|
+
// The `ended` kind separates it from a broken declaration, since a
|
|
243
|
+
// bounded recurrence that simply ran its course is not something the
|
|
244
|
+
// plugin author has to fix.
|
|
245
|
+
try {
|
|
246
|
+
computeNextRunAt({
|
|
247
|
+
syntax: resolved.syntax,
|
|
248
|
+
expression: resolved.expression,
|
|
249
|
+
timezone: fields.timezone ?? null,
|
|
250
|
+
});
|
|
251
|
+
} catch {
|
|
252
|
+
return {
|
|
253
|
+
error:
|
|
254
|
+
"expression has no upcoming occurrences: the recurrence has already ended",
|
|
255
|
+
kind: "ended",
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
config: {
|
|
262
|
+
expression: resolved.expression,
|
|
263
|
+
syntax: resolved.syntax,
|
|
264
|
+
timezone: fields.timezone ?? null,
|
|
265
|
+
description: fields.description ?? null,
|
|
266
|
+
maxRetries: fields.max_retries ?? null,
|
|
267
|
+
retryBackoffMs: fields.retry_backoff_ms ?? null,
|
|
268
|
+
quiet: fields.quiet ?? null,
|
|
269
|
+
inferenceProfile: fields.inference_profile ?? null,
|
|
270
|
+
timeoutMs: fields.timeout_ms ?? null,
|
|
271
|
+
enabled: fields.enabled ?? true,
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
interface DeclarationFile {
|
|
277
|
+
relPath: string;
|
|
278
|
+
content: Buffer;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function hashDeclarationFiles(files: DeclarationFile[]): string {
|
|
282
|
+
const sorted = [...files].sort((a, b) =>
|
|
283
|
+
a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0,
|
|
284
|
+
);
|
|
285
|
+
const hash = createHash("sha256");
|
|
286
|
+
for (const file of sorted) {
|
|
287
|
+
hash.update(file.relPath);
|
|
288
|
+
hash.update("\0");
|
|
289
|
+
hash.update(file.content);
|
|
290
|
+
hash.update("\0");
|
|
291
|
+
}
|
|
292
|
+
return hash.digest("hex");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Collect every regular file under `dir` (recursively, dot entries and
|
|
297
|
+
* symlinks skipped) with paths relative to the schedules/ root.
|
|
298
|
+
*/
|
|
299
|
+
function collectDeclarationFiles(
|
|
300
|
+
dir: string,
|
|
301
|
+
relPrefix: string,
|
|
302
|
+
): DeclarationFile[] {
|
|
303
|
+
const files: DeclarationFile[] = [];
|
|
304
|
+
walkPluginTree(dir, { excludeDotEntries: true }, (rel, abs) => {
|
|
305
|
+
files.push({ relPath: `${relPrefix}${rel}`, content: readFileSync(abs) });
|
|
306
|
+
});
|
|
307
|
+
return files;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function shellQuote(value: string): string {
|
|
311
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Shell invocation for a declaration's `index.sh`. The script always runs
|
|
316
|
+
* through an explicit interpreter rather than being executed directly, which
|
|
317
|
+
* keeps the entrypoint runnable when an install path drops its exec bit. A
|
|
318
|
+
* `#!` first line names that interpreter plus at most one argument (the
|
|
319
|
+
* kernel's own shebang contract, covering both `#!/bin/bash` and
|
|
320
|
+
* `#!/usr/bin/env bash`); a script without one is parsed by `sh`.
|
|
321
|
+
*/
|
|
322
|
+
function buildScriptInvocation(scriptPath: string, content: string): string {
|
|
323
|
+
const firstLine = content.split(/\r?\n/, 1)[0] ?? "";
|
|
324
|
+
if (firstLine.startsWith("#!")) {
|
|
325
|
+
const rest = firstLine.slice(2).trim();
|
|
326
|
+
const spaceIdx = rest.search(/\s/);
|
|
327
|
+
const interpreter = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx);
|
|
328
|
+
const argument = spaceIdx === -1 ? "" : rest.slice(spaceIdx).trim();
|
|
329
|
+
if (interpreter) {
|
|
330
|
+
return [interpreter, ...(argument ? [argument] : []), scriptPath]
|
|
331
|
+
.map(shellQuote)
|
|
332
|
+
.join(" ");
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return `sh ${shellQuote(scriptPath)}`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const SUPPORTED_ENTRYPOINTS = ["index.md", "index.sh"];
|
|
339
|
+
|
|
340
|
+
interface ParsedDeclarationBody {
|
|
341
|
+
mode: PluginScheduleMode;
|
|
342
|
+
message: string | null;
|
|
343
|
+
scriptInvocation: string | null;
|
|
344
|
+
config: PluginScheduleConfig;
|
|
345
|
+
files: DeclarationFile[];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function parseDirectoryDeclaration(
|
|
349
|
+
schedulesDir: string,
|
|
350
|
+
dirName: string,
|
|
351
|
+
): ParsedDeclarationBody | DeclarationFailure {
|
|
352
|
+
const dirPath = join(schedulesDir, dirName);
|
|
353
|
+
const children = readdirSync(dirPath, { withFileTypes: true });
|
|
354
|
+
|
|
355
|
+
const entrypoints = children
|
|
356
|
+
.filter((c) => c.isFile() && c.name.startsWith("index."))
|
|
357
|
+
.map((c) => c.name)
|
|
358
|
+
.sort();
|
|
359
|
+
if (entrypoints.length === 0) {
|
|
360
|
+
return {
|
|
361
|
+
error: "no entrypoint: expected exactly one of index.md or index.sh",
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (entrypoints.length > 1) {
|
|
365
|
+
return {
|
|
366
|
+
error: `multiple entrypoints (${entrypoints.join(", ")}): expected exactly one of index.md or index.sh`,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const entrypoint = entrypoints[0]!;
|
|
370
|
+
if (!SUPPORTED_ENTRYPOINTS.includes(entrypoint)) {
|
|
371
|
+
return {
|
|
372
|
+
error: `unsupported entrypoint "${entrypoint}": expected index.md or index.sh`,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let rawConfigText: string;
|
|
377
|
+
try {
|
|
378
|
+
rawConfigText = readFileSync(join(dirPath, "config.json"), "utf8");
|
|
379
|
+
} catch {
|
|
380
|
+
return { error: "missing config.json" };
|
|
381
|
+
}
|
|
382
|
+
let rawConfig: unknown;
|
|
383
|
+
try {
|
|
384
|
+
rawConfig = JSON.parse(rawConfigText);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
return {
|
|
387
|
+
error: `config.json is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
const configResult = parseScheduleConfig(rawConfig);
|
|
391
|
+
if ("error" in configResult) {
|
|
392
|
+
return configResult;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let message: string | null = null;
|
|
396
|
+
let scriptInvocation: string | null = null;
|
|
397
|
+
let mode: PluginScheduleMode;
|
|
398
|
+
if (entrypoint === "index.md") {
|
|
399
|
+
const content = readFileSync(join(dirPath, "index.md"), "utf8");
|
|
400
|
+
if (FRONTMATTER_REGEX.test(content)) {
|
|
401
|
+
return {
|
|
402
|
+
error:
|
|
403
|
+
"index.md must not contain frontmatter: directory-form config belongs in config.json",
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
message = content.trim();
|
|
407
|
+
if (!message) {
|
|
408
|
+
return { error: "prompt body is empty" };
|
|
409
|
+
}
|
|
410
|
+
mode = "execute";
|
|
411
|
+
} else {
|
|
412
|
+
const scriptPath = join(dirPath, "index.sh");
|
|
413
|
+
scriptInvocation = buildScriptInvocation(
|
|
414
|
+
scriptPath,
|
|
415
|
+
readFileSync(scriptPath, "utf8"),
|
|
416
|
+
);
|
|
417
|
+
mode = "script";
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
mode,
|
|
422
|
+
message,
|
|
423
|
+
scriptInvocation,
|
|
424
|
+
config: configResult.config,
|
|
425
|
+
files: collectDeclarationFiles(dirPath, `${dirName}/`),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Parse every schedule declaration under `<pluginDir>/schedules/`. A missing
|
|
431
|
+
* or unreadable schedules/ directory yields no declarations and no errors.
|
|
432
|
+
* Every malformed or ambiguous declaration yields a per-schedule
|
|
433
|
+
* {@link DeclarationError} without affecting siblings.
|
|
434
|
+
*/
|
|
435
|
+
export function parsePluginScheduleDeclarations(
|
|
436
|
+
pluginDir: string,
|
|
437
|
+
pluginName: string,
|
|
438
|
+
): ParsedPluginSchedules {
|
|
439
|
+
const schedulesDir = join(pluginDir, "schedules");
|
|
440
|
+
let entries;
|
|
441
|
+
try {
|
|
442
|
+
entries = readdirSync(schedulesDir, { withFileTypes: true });
|
|
443
|
+
} catch {
|
|
444
|
+
return { declarations: [], errors: [] };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const declarations: ScheduleDeclaration[] = [];
|
|
448
|
+
const errors: DeclarationError[] = [];
|
|
449
|
+
const fail = (
|
|
450
|
+
scheduleName: string,
|
|
451
|
+
reason: string,
|
|
452
|
+
kind: DeclarationErrorKind = "invalid",
|
|
453
|
+
): void => {
|
|
454
|
+
errors.push({
|
|
455
|
+
pluginName,
|
|
456
|
+
scheduleName,
|
|
457
|
+
sourceKey: pluginScheduleSourceKey(pluginName, scheduleName),
|
|
458
|
+
reason,
|
|
459
|
+
kind,
|
|
460
|
+
});
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
const dirNames: string[] = [];
|
|
464
|
+
for (const entry of entries) {
|
|
465
|
+
if (entry.name.startsWith(".")) {
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (entry.isDirectory()) {
|
|
469
|
+
dirNames.push(entry.name);
|
|
470
|
+
} else {
|
|
471
|
+
fail(
|
|
472
|
+
entry.name,
|
|
473
|
+
`schedule declarations must be directories (schedules/<name>/ holding config.json and one index.md or index.sh): "${entry.name}" is a file`,
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const parseOne = (
|
|
479
|
+
name: string,
|
|
480
|
+
parse: () => ParsedDeclarationBody | DeclarationFailure,
|
|
481
|
+
): void => {
|
|
482
|
+
let result: ParsedDeclarationBody | DeclarationFailure;
|
|
483
|
+
try {
|
|
484
|
+
result = parse();
|
|
485
|
+
} catch (err) {
|
|
486
|
+
result = {
|
|
487
|
+
error: `failed to read declaration: ${err instanceof Error ? err.message : String(err)}`,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
if ("error" in result) {
|
|
491
|
+
fail(name, result.error, result.kind);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
declarations.push({
|
|
495
|
+
sourceKey: pluginScheduleSourceKey(pluginName, name),
|
|
496
|
+
name,
|
|
497
|
+
mode: result.mode,
|
|
498
|
+
message: result.message,
|
|
499
|
+
scriptInvocation: result.scriptInvocation,
|
|
500
|
+
config: result.config,
|
|
501
|
+
definitionHash: hashDeclarationFiles(result.files),
|
|
502
|
+
});
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
for (const name of dirNames) {
|
|
506
|
+
parseOne(name, () => parseDirectoryDeclaration(schedulesDir, name));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
declarations.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
510
|
+
errors.sort((a, b) =>
|
|
511
|
+
a.scheduleName < b.scheduleName
|
|
512
|
+
? -1
|
|
513
|
+
: a.scheduleName > b.scheduleName
|
|
514
|
+
? 1
|
|
515
|
+
: 0,
|
|
516
|
+
);
|
|
517
|
+
return { declarations, errors };
|
|
518
|
+
}
|