@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
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
installPlugin,
|
|
29
29
|
InvalidPluginNameError,
|
|
30
30
|
PluginAlreadyInstalledError,
|
|
31
|
+
PluginInstallDeclinedError,
|
|
31
32
|
PluginNotFoundError,
|
|
32
33
|
PluginPostinstallError,
|
|
33
34
|
PluginSourceUnavailableError,
|
|
@@ -362,6 +363,84 @@ describe("installPlugin — install lifecycle", () => {
|
|
|
362
363
|
});
|
|
363
364
|
});
|
|
364
365
|
|
|
366
|
+
describe("installPlugin - staged-install consent", () => {
|
|
367
|
+
let ws: string;
|
|
368
|
+
let pluginsDir: string;
|
|
369
|
+
|
|
370
|
+
beforeEach(() => {
|
|
371
|
+
ws = mkdtempSync(join(tmpdir(), "vellum-plugins-consent-"));
|
|
372
|
+
pluginsDir = join(ws, "plugins");
|
|
373
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
afterEach(() => {
|
|
377
|
+
rmSync(ws, { recursive: true, force: true });
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const CLONE_TREE = {
|
|
381
|
+
"package.json": '{"name":"caveman"}',
|
|
382
|
+
"schedules/daily/config.json": '{"expression": "0 9 * * *"}',
|
|
383
|
+
"schedules/daily/index.md": "Do it.\n",
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
test("confirmStaged sees the staged tree before anything is finalized", async () => {
|
|
387
|
+
const fetch = makeContentsFetch({ tree: {}, manifest: CAVEMAN_MANIFEST });
|
|
388
|
+
const runGit = fakeGitRunner({ tree: CLONE_TREE, commit: CAVEMAN_SHA });
|
|
389
|
+
const observed: Array<{ name: string; stagingDir: string }> = [];
|
|
390
|
+
|
|
391
|
+
const result = await installPlugin(
|
|
392
|
+
{ name: "caveman", ref: "main" },
|
|
393
|
+
{
|
|
394
|
+
fetch,
|
|
395
|
+
runGit,
|
|
396
|
+
workspacePluginsDir: pluginsDir,
|
|
397
|
+
confirmStaged: async (staged) => {
|
|
398
|
+
observed.push(staged);
|
|
399
|
+
// The staged tree is fully materialized, but nothing has been
|
|
400
|
+
// swapped into the served plugins dir yet.
|
|
401
|
+
expect(
|
|
402
|
+
existsSync(join(staged.stagingDir, "schedules/daily/index.md")),
|
|
403
|
+
).toBe(true);
|
|
404
|
+
expect(existsSync(join(pluginsDir, "caveman"))).toBe(false);
|
|
405
|
+
return true;
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
expect(observed.length).toBe(1);
|
|
411
|
+
expect(observed[0]!.name).toBe("caveman");
|
|
412
|
+
expect(result.target).toBe(join(pluginsDir, "caveman"));
|
|
413
|
+
expect(existsSync(join(result.target, "schedules/daily/index.md"))).toBe(
|
|
414
|
+
true,
|
|
415
|
+
);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("declining aborts cleanly: no target, staging removed", async () => {
|
|
419
|
+
const fetch = makeContentsFetch({ tree: {}, manifest: CAVEMAN_MANIFEST });
|
|
420
|
+
const runGit = fakeGitRunner({ tree: CLONE_TREE, commit: CAVEMAN_SHA });
|
|
421
|
+
const stagingDirs: string[] = [];
|
|
422
|
+
|
|
423
|
+
await expect(
|
|
424
|
+
installPlugin(
|
|
425
|
+
{ name: "caveman", ref: "main" },
|
|
426
|
+
{
|
|
427
|
+
fetch,
|
|
428
|
+
runGit,
|
|
429
|
+
workspacePluginsDir: pluginsDir,
|
|
430
|
+
confirmStaged: async (staged) => {
|
|
431
|
+
stagingDirs.push(staged.stagingDir);
|
|
432
|
+
return false;
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
),
|
|
436
|
+
).rejects.toBeInstanceOf(PluginInstallDeclinedError);
|
|
437
|
+
|
|
438
|
+
expect(existsSync(join(pluginsDir, "caveman"))).toBe(false);
|
|
439
|
+
expect(stagingDirs.length).toBe(1);
|
|
440
|
+
expect(existsSync(stagingDirs[0]!)).toBe(false);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
365
444
|
describe("installPlugin — marketplace resolution", () => {
|
|
366
445
|
let ws: string;
|
|
367
446
|
let pluginsDir: string;
|
|
@@ -15,7 +15,10 @@ import { gzipSync } from "node:zlib";
|
|
|
15
15
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
16
16
|
|
|
17
17
|
import type { FetchLike } from "../fetch-like.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
PluginInstallDeclinedError,
|
|
20
|
+
readInstallMeta,
|
|
21
|
+
} from "../install-from-github.js";
|
|
19
22
|
import {
|
|
20
23
|
extractPluginTarball,
|
|
21
24
|
installPluginFromPlatform,
|
|
@@ -306,6 +309,49 @@ describe("installPluginFromPlatform — integrity", () => {
|
|
|
306
309
|
});
|
|
307
310
|
});
|
|
308
311
|
|
|
312
|
+
// ─── Staged-install consent ──────────────────────────────────────────────────
|
|
313
|
+
|
|
314
|
+
describe("installPluginFromPlatform - staged-install consent", () => {
|
|
315
|
+
test("confirmStaged runs over the staged tree; declining leaves nothing behind", async () => {
|
|
316
|
+
const fetchFn = makeInstallFetch({
|
|
317
|
+
entries: [
|
|
318
|
+
{ name: "plugin.json", content: '{"name":"reading-pal"}' },
|
|
319
|
+
{
|
|
320
|
+
name: "schedules/daily/config.json",
|
|
321
|
+
content: '{"expression": "0 9 * * *"}',
|
|
322
|
+
},
|
|
323
|
+
{ name: "schedules/daily/index.md", content: "Do it.\n" },
|
|
324
|
+
],
|
|
325
|
+
});
|
|
326
|
+
const stagingDirs: string[] = [];
|
|
327
|
+
|
|
328
|
+
await expect(
|
|
329
|
+
installPluginFromPlatform(
|
|
330
|
+
{ name: "reading-pal" },
|
|
331
|
+
{
|
|
332
|
+
fetch: fetchFn,
|
|
333
|
+
platformBaseUrl: PLATFORM,
|
|
334
|
+
workspacePluginsDir: pluginsDir,
|
|
335
|
+
confirmStaged: async (staged) => {
|
|
336
|
+
stagingDirs.push(staged.stagingDir);
|
|
337
|
+
expect(
|
|
338
|
+
existsSync(
|
|
339
|
+
join(staged.stagingDir, "schedules", "daily", "index.md"),
|
|
340
|
+
),
|
|
341
|
+
).toBe(true);
|
|
342
|
+
expect(existsSync(join(pluginsDir, "reading-pal"))).toBe(false);
|
|
343
|
+
return false;
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
),
|
|
347
|
+
).rejects.toBeInstanceOf(PluginInstallDeclinedError);
|
|
348
|
+
|
|
349
|
+
expect(existsSync(join(pluginsDir, "reading-pal"))).toBe(false);
|
|
350
|
+
expect(stagingDirs.length).toBe(1);
|
|
351
|
+
expect(existsSync(stagingDirs[0]!)).toBe(false);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
309
355
|
// ─── Path traversal ──────────────────────────────────────────────────────────
|
|
310
356
|
|
|
311
357
|
describe("extractPluginTarball — path traversal", () => {
|
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
* Tests for {@link detectPluginSurfaces}.
|
|
3
3
|
*
|
|
4
4
|
* Surface detection is a pure walk of an installed plugin's on-disk tree, so
|
|
5
|
-
* the fixtures materialize the `hooks/`, `tools/`, and `
|
|
6
|
-
* conventions in a real temp dir and assert the derived listing
|
|
7
|
-
* the runtime loader / skills catalog would discover.
|
|
5
|
+
* the fixtures materialize the `hooks/`, `tools/`, `skills/`, and `schedules/`
|
|
6
|
+
* directory conventions in a real temp dir and assert the derived listing
|
|
7
|
+
* matches what the runtime loader / skills catalog would discover.
|
|
8
|
+
*
|
|
9
|
+
* The schedules walk only mirrors the daemon parser's structural refusals;
|
|
10
|
+
* it stays permissive beyond structure. In particular, `expression` validity
|
|
11
|
+
* is not checked CLI-side, so a schedule whose expression the daemon rejects
|
|
12
|
+
* is still listed.
|
|
8
13
|
*/
|
|
9
14
|
|
|
10
15
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -24,11 +29,16 @@ afterEach(() => {
|
|
|
24
29
|
rmSync(pluginDir, { recursive: true, force: true });
|
|
25
30
|
});
|
|
26
31
|
|
|
27
|
-
/** Create `<pluginDir>/<rel>` with
|
|
28
|
-
function touch(rel: string): void {
|
|
32
|
+
/** Create `<pluginDir>/<rel>` with the given contents, making parents as needed. */
|
|
33
|
+
function touch(rel: string, content = ""): void {
|
|
29
34
|
const path = join(pluginDir, rel);
|
|
30
35
|
mkdirSync(join(path, ".."), { recursive: true });
|
|
31
|
-
writeFileSync(path,
|
|
36
|
+
writeFileSync(path, content);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A prompt body carrying YAML frontmatter, which `index.md` must not have. */
|
|
40
|
+
function withFrontmatter(expression: string): string {
|
|
41
|
+
return `---\nexpression: "${expression}"\n---\nDo the thing.\n`;
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
describe("detectPluginSurfaces", () => {
|
|
@@ -106,6 +116,75 @@ describe("detectPluginSurfaces", () => {
|
|
|
106
116
|
const surfaces = detectPluginSurfaces(pluginDir);
|
|
107
117
|
|
|
108
118
|
// THEN every surface type is empty
|
|
109
|
-
expect(surfaces).toEqual({
|
|
119
|
+
expect(surfaces).toEqual({
|
|
120
|
+
skills: [],
|
|
121
|
+
hooks: [],
|
|
122
|
+
tools: [],
|
|
123
|
+
schedules: [],
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("lists declared schedules with cadence and mode for both entrypoints", () => {
|
|
128
|
+
// GIVEN two declaration directories, one per entrypoint kind
|
|
129
|
+
touch(
|
|
130
|
+
"schedules/cleanup/config.json",
|
|
131
|
+
'{"expression": "0 0 * * 0", "timezone": "UTC"}',
|
|
132
|
+
);
|
|
133
|
+
touch("schedules/cleanup/index.sh", "#!/bin/sh\necho hi\n");
|
|
134
|
+
touch("schedules/digest/config.json", '{"expression": "0 8 * * 1"}');
|
|
135
|
+
touch("schedules/digest/index.md", "Summarize the week.\n");
|
|
136
|
+
|
|
137
|
+
// WHEN its surfaces are detected
|
|
138
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
139
|
+
|
|
140
|
+
// THEN each declaration reports its raw expression and entrypoint mode
|
|
141
|
+
expect(surfaces.schedules).toEqual([
|
|
142
|
+
{ name: "cleanup", cadence: "0 0 * * 0", mode: "script" },
|
|
143
|
+
{ name: "digest", cadence: "0 8 * * 1", mode: "execute" },
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("skips a markdown file sitting directly under schedules/", () => {
|
|
148
|
+
// GIVEN a flat declaration file, which is not a declaration form
|
|
149
|
+
touch("schedules/dupe.md", withFrontmatter("0 9 * * *"));
|
|
150
|
+
touch("schedules/keeper/config.json", '{"expression": "30 7 * * *"}');
|
|
151
|
+
touch("schedules/keeper/index.md", "body\n");
|
|
152
|
+
|
|
153
|
+
// WHEN its surfaces are detected
|
|
154
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
155
|
+
|
|
156
|
+
// THEN only the declaration directory is listed
|
|
157
|
+
expect(surfaces.schedules).toEqual([
|
|
158
|
+
{ name: "keeper", cadence: "30 7 * * *", mode: "execute" },
|
|
159
|
+
]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("skips structurally malformed schedule declarations (frontmatter, entrypoint, config, body)", () => {
|
|
163
|
+
// GIVEN a directory with both entrypoints
|
|
164
|
+
touch("schedules/two-entries/config.json", '{"expression": "0 9 * * *"}');
|
|
165
|
+
touch("schedules/two-entries/index.md", "body\n");
|
|
166
|
+
touch("schedules/two-entries/index.sh", "#!/bin/sh\n");
|
|
167
|
+
// AND a directory with an unsupported entrypoint
|
|
168
|
+
touch("schedules/bad-entry/config.json", '{"expression": "0 9 * * *"}');
|
|
169
|
+
touch("schedules/bad-entry/index.py", "print()\n");
|
|
170
|
+
// AND a directory whose index.md carries frontmatter (config belongs in
|
|
171
|
+
// config.json)
|
|
172
|
+
touch(
|
|
173
|
+
"schedules/md-frontmatter/config.json",
|
|
174
|
+
'{"expression": "0 9 * * *"}',
|
|
175
|
+
);
|
|
176
|
+
touch("schedules/md-frontmatter/index.md", withFrontmatter("0 9 * * *"));
|
|
177
|
+
// AND a directory whose index.md prompt body is empty
|
|
178
|
+
touch("schedules/md-empty/config.json", '{"expression": "0 9 * * *"}');
|
|
179
|
+
touch("schedules/md-empty/index.md", "\n \n");
|
|
180
|
+
// AND a directory missing its config.json
|
|
181
|
+
touch("schedules/no-config/index.md", "body\n");
|
|
182
|
+
// AND a directory whose config declares no expression
|
|
183
|
+
touch("schedules/no-expression/config.json", '{"timezone": "UTC"}');
|
|
184
|
+
touch("schedules/no-expression/index.md", "body\n");
|
|
185
|
+
|
|
186
|
+
// WHEN its surfaces are detected
|
|
187
|
+
// THEN none of them are listed
|
|
188
|
+
expect(detectPluginSurfaces(pluginDir).schedules).toEqual([]);
|
|
110
189
|
});
|
|
111
190
|
});
|
|
@@ -24,6 +24,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
|
24
24
|
import type { FetchLike } from "../fetch-like.js";
|
|
25
25
|
import {
|
|
26
26
|
type GitRunner,
|
|
27
|
+
PluginInstallDeclinedError,
|
|
27
28
|
PluginNotFoundError,
|
|
28
29
|
PluginSourceUnavailableError,
|
|
29
30
|
} from "../install-from-github.js";
|
|
@@ -1033,3 +1034,124 @@ describe("upgradePlugin --strategy", () => {
|
|
|
1033
1034
|
expect(installedFile("level-up", "conflict.txt")).toBe("ours\n");
|
|
1034
1035
|
});
|
|
1035
1036
|
});
|
|
1037
|
+
|
|
1038
|
+
describe("upgradePlugin confirmStaged consent gate", () => {
|
|
1039
|
+
test("overwrite: declining aborts and preserves the existing install", async () => {
|
|
1040
|
+
// GIVEN an installed copy pinned to SHA_A with a newer marketplace pin
|
|
1041
|
+
installCopy(pluginsDir, "level-up", { commit: SHA_A });
|
|
1042
|
+
const fetch = makeFetch({ manifest: manifestWith("level-up", SHA_B) });
|
|
1043
|
+
const runGit = fakeGitRunner(SHA_B);
|
|
1044
|
+
const seen: Array<{ name: string; staged: boolean }> = [];
|
|
1045
|
+
|
|
1046
|
+
// WHEN the consent gate declines the staged upgrade
|
|
1047
|
+
await expect(
|
|
1048
|
+
upgradePlugin(
|
|
1049
|
+
{ name: "level-up" },
|
|
1050
|
+
{
|
|
1051
|
+
fetch,
|
|
1052
|
+
runGit,
|
|
1053
|
+
workspacePluginsDir: pluginsDir,
|
|
1054
|
+
confirmStaged: async ({ name, stagingDir }) => {
|
|
1055
|
+
seen.push({
|
|
1056
|
+
name,
|
|
1057
|
+
staged: existsSync(join(stagingDir, "package.json")),
|
|
1058
|
+
});
|
|
1059
|
+
return false;
|
|
1060
|
+
},
|
|
1061
|
+
},
|
|
1062
|
+
),
|
|
1063
|
+
).rejects.toBeInstanceOf(PluginInstallDeclinedError);
|
|
1064
|
+
|
|
1065
|
+
// THEN the gate saw the fully staged tree exactly once
|
|
1066
|
+
expect(seen).toEqual([{ name: "level-up", staged: true }]);
|
|
1067
|
+
// AND the live install still carries the old pin
|
|
1068
|
+
expect(sidecarCommit(pluginsDir, "level-up")).toBe(SHA_A);
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
test("overwrite: an accepting gate lets the upgrade proceed", async () => {
|
|
1072
|
+
installCopy(pluginsDir, "level-up", { commit: SHA_A });
|
|
1073
|
+
const fetch = makeFetch({ manifest: manifestWith("level-up", SHA_B) });
|
|
1074
|
+
const runGit = fakeGitRunner(SHA_B);
|
|
1075
|
+
|
|
1076
|
+
const result = await upgradePlugin(
|
|
1077
|
+
{ name: "level-up" },
|
|
1078
|
+
{
|
|
1079
|
+
fetch,
|
|
1080
|
+
runGit,
|
|
1081
|
+
workspacePluginsDir: pluginsDir,
|
|
1082
|
+
confirmStaged: async () => true,
|
|
1083
|
+
},
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
expect(result.outcome).toBe("upgraded");
|
|
1087
|
+
expect(sidecarCommit(pluginsDir, "level-up")).toBe(SHA_B);
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
test("merge: the gate sees the merged tree; declining keeps the local install", async () => {
|
|
1091
|
+
const PKG = '{"name":"level-up"}\n';
|
|
1092
|
+
const base: Tree = { "package.json": PKG, "note.txt": "base\n" };
|
|
1093
|
+
const ours: Tree = {
|
|
1094
|
+
"package.json": PKG,
|
|
1095
|
+
"note.txt": "base\n",
|
|
1096
|
+
"local.txt": "mine\n",
|
|
1097
|
+
};
|
|
1098
|
+
const theirs: Tree = { "package.json": PKG, "note.txt": "upstream\n" };
|
|
1099
|
+
installMergeCopy("level-up", ours, SHA_A, base);
|
|
1100
|
+
const fetch = makeFetch({ manifest: manifestWith("level-up", SHA_B) });
|
|
1101
|
+
const runGit = treeGitRunner({ [SHA_A]: base, [SHA_B]: theirs });
|
|
1102
|
+
let sawMerged = false;
|
|
1103
|
+
|
|
1104
|
+
await expect(
|
|
1105
|
+
upgradePlugin(
|
|
1106
|
+
{ name: "level-up", strategy: "ours" },
|
|
1107
|
+
{
|
|
1108
|
+
fetch,
|
|
1109
|
+
runGit,
|
|
1110
|
+
workspacePluginsDir: pluginsDir,
|
|
1111
|
+
confirmStaged: async ({ stagingDir }) => {
|
|
1112
|
+
// The staged tree is the merged result: the upstream edit plus
|
|
1113
|
+
// the surviving local addition.
|
|
1114
|
+
sawMerged =
|
|
1115
|
+
readFileSync(join(stagingDir, "note.txt"), "utf-8") ===
|
|
1116
|
+
"upstream\n" && existsSync(join(stagingDir, "local.txt"));
|
|
1117
|
+
return false;
|
|
1118
|
+
},
|
|
1119
|
+
},
|
|
1120
|
+
),
|
|
1121
|
+
).rejects.toBeInstanceOf(PluginInstallDeclinedError);
|
|
1122
|
+
|
|
1123
|
+
expect(sawMerged).toBe(true);
|
|
1124
|
+
// The live install is untouched by the declined merge.
|
|
1125
|
+
expect(installedFile("level-up", "note.txt")).toBe("base\n");
|
|
1126
|
+
expect(sidecarCommit(pluginsDir, "level-up")).toBe(SHA_A);
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
test("dry runs and no-ops never invoke the gate", async () => {
|
|
1130
|
+
installCopy(pluginsDir, "level-up", { commit: SHA_A });
|
|
1131
|
+
const gate = async () => {
|
|
1132
|
+
throw new Error("confirmStaged must not run without staging");
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
const dry = await upgradePlugin(
|
|
1136
|
+
{ name: "level-up", dryRun: true },
|
|
1137
|
+
{
|
|
1138
|
+
fetch: makeFetch({ manifest: manifestWith("level-up", SHA_B) }),
|
|
1139
|
+
runGit: unusedGitRunner,
|
|
1140
|
+
workspacePluginsDir: pluginsDir,
|
|
1141
|
+
confirmStaged: gate,
|
|
1142
|
+
},
|
|
1143
|
+
);
|
|
1144
|
+
expect(dry.outcome).toBe("would-upgrade");
|
|
1145
|
+
|
|
1146
|
+
const noop = await upgradePlugin(
|
|
1147
|
+
{ name: "level-up" },
|
|
1148
|
+
{
|
|
1149
|
+
fetch: makeFetch({ manifest: manifestWith("level-up", SHA_A) }),
|
|
1150
|
+
runGit: unusedGitRunner,
|
|
1151
|
+
workspacePluginsDir: pluginsDir,
|
|
1152
|
+
confirmStaged: gate,
|
|
1153
|
+
},
|
|
1154
|
+
);
|
|
1155
|
+
expect(noop.outcome).toBe("already-up-to-date");
|
|
1156
|
+
});
|
|
1157
|
+
});
|
|
@@ -154,6 +154,20 @@ export interface InstallPluginOptions {
|
|
|
154
154
|
readonly trustedSource?: PluginFetchSource;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Consent gate invoked after the plugin tree is fully staged and before it is
|
|
159
|
+
* finalized (dependencies installed, provenance recorded, swapped live). The
|
|
160
|
+
* caller inspects the staged tree to see what the plugin declares (e.g.
|
|
161
|
+
* schedules) and asks the user. Returning `false` aborts the install: the
|
|
162
|
+
* staging directory is removed, nothing is swapped, and
|
|
163
|
+
* {@link PluginInstallDeclinedError} is thrown.
|
|
164
|
+
*/
|
|
165
|
+
export type ConfirmStagedInstall = (staged: {
|
|
166
|
+
readonly name: string;
|
|
167
|
+
/** Absolute path of the fully materialized staging directory. */
|
|
168
|
+
readonly stagingDir: string;
|
|
169
|
+
}) => Promise<boolean>;
|
|
170
|
+
|
|
157
171
|
/** Dependencies injected by the caller. */
|
|
158
172
|
export interface InstallPluginDeps {
|
|
159
173
|
/** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
|
|
@@ -168,6 +182,8 @@ export interface InstallPluginDeps {
|
|
|
168
182
|
readonly runInstallDeps?: DependencyInstaller;
|
|
169
183
|
/** Forwarded to {@link finalizeStagedInstall}; see {@link FinalizeStagedInstallParams.beforeSwap}. */
|
|
170
184
|
readonly beforeSwap?: () => Promise<void>;
|
|
185
|
+
/** Consent gate between staging and finalize; see {@link ConfirmStagedInstall}. */
|
|
186
|
+
readonly confirmStaged?: ConfirmStagedInstall;
|
|
171
187
|
}
|
|
172
188
|
|
|
173
189
|
/** Successful install result. */
|
|
@@ -214,6 +230,19 @@ export class PluginPostinstallError extends Error {
|
|
|
214
230
|
}
|
|
215
231
|
}
|
|
216
232
|
|
|
233
|
+
/**
|
|
234
|
+
* The caller's {@link ConfirmStagedInstall} gate declined the staged install.
|
|
235
|
+
* Nothing was installed and the staging directory was removed. The caller that
|
|
236
|
+
* supplied the gate owns the user-facing messaging, so this error signals the
|
|
237
|
+
* outcome rather than something to report as a failure.
|
|
238
|
+
*/
|
|
239
|
+
export class PluginInstallDeclinedError extends Error {
|
|
240
|
+
constructor(pluginName: string) {
|
|
241
|
+
super(`Install of "${pluginName}" was declined.`);
|
|
242
|
+
this.name = "PluginInstallDeclinedError";
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
217
246
|
/** A plugin with the same name is already installed and `--force` was not passed. */
|
|
218
247
|
export class PluginAlreadyInstalledError extends Error {
|
|
219
248
|
constructor(
|
|
@@ -503,6 +532,8 @@ export async function installPlugin(
|
|
|
503
532
|
throw new PluginNotFoundError(name, ref, sourceLabel(effectiveSource));
|
|
504
533
|
}
|
|
505
534
|
|
|
535
|
+
await confirmStagedOrAbort(name, stagingDir, deps.confirmStaged);
|
|
536
|
+
|
|
506
537
|
await finalizeStagedInstall(stagingDir, {
|
|
507
538
|
name,
|
|
508
539
|
source: effectiveSource,
|
|
@@ -517,6 +548,36 @@ export async function installPlugin(
|
|
|
517
548
|
return { name, target, fileCount, ref, commit, committedAt };
|
|
518
549
|
}
|
|
519
550
|
|
|
551
|
+
/**
|
|
552
|
+
* Run the caller's {@link ConfirmStagedInstall} gate over the staged tree. A
|
|
553
|
+
* decline (or a gate that throws) removes the staging directory so no partial
|
|
554
|
+
* install is left behind; a decline then surfaces as
|
|
555
|
+
* {@link PluginInstallDeclinedError}. A no-op when no gate was supplied.
|
|
556
|
+
*
|
|
557
|
+
* Shared by {@link installPlugin} and the platform-endpoint install so both
|
|
558
|
+
* abort a declined install the same way.
|
|
559
|
+
*/
|
|
560
|
+
export async function confirmStagedOrAbort(
|
|
561
|
+
name: string,
|
|
562
|
+
stagingDir: string,
|
|
563
|
+
confirmStaged: ConfirmStagedInstall | undefined,
|
|
564
|
+
): Promise<void> {
|
|
565
|
+
if (!confirmStaged) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
let confirmed = false;
|
|
569
|
+
try {
|
|
570
|
+
confirmed = await confirmStaged({ name, stagingDir });
|
|
571
|
+
} finally {
|
|
572
|
+
if (!confirmed) {
|
|
573
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (!confirmed) {
|
|
577
|
+
throw new PluginInstallDeclinedError(name);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
520
581
|
/** Inputs for {@link finalizeStagedInstall}. */
|
|
521
582
|
export interface FinalizeStagedInstallParams {
|
|
522
583
|
readonly name: string;
|
|
@@ -33,6 +33,8 @@ import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
|
33
33
|
import { APP_VERSION } from "../../version.js";
|
|
34
34
|
import type { FetchLike } from "./fetch-like.js";
|
|
35
35
|
import {
|
|
36
|
+
type ConfirmStagedInstall,
|
|
37
|
+
confirmStagedOrAbort,
|
|
36
38
|
finalizeStagedInstall,
|
|
37
39
|
type InstallPluginResult,
|
|
38
40
|
PluginAlreadyInstalledError,
|
|
@@ -123,6 +125,8 @@ export interface InstallFromPlatformDeps {
|
|
|
123
125
|
readonly maxAttempts?: number;
|
|
124
126
|
/** Sleep between retries. Injected so tests don't wait real time. */
|
|
125
127
|
readonly sleep?: (ms: number) => Promise<void>;
|
|
128
|
+
/** Consent gate between staging and finalize; see {@link ConfirmStagedInstall}. */
|
|
129
|
+
readonly confirmStaged?: ConfirmStagedInstall;
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
/** Total attempts (initial + retries) for a transient upstream status. */
|
|
@@ -194,6 +198,8 @@ export async function installPluginFromPlatform(
|
|
|
194
198
|
throw new PluginNotFoundError(name, meta.ref ?? "", meta.repo ?? name);
|
|
195
199
|
}
|
|
196
200
|
|
|
201
|
+
await confirmStagedOrAbort(name, stagingDir, deps.confirmStaged);
|
|
202
|
+
|
|
197
203
|
// Record provenance for future update/version UX. The endpoint re-roots the
|
|
198
204
|
// tarball to the plugin root, so the recorded ref is the pinned commit and
|
|
199
205
|
// the ETag documents exactly which bytes were verified.
|
|
@@ -223,7 +229,7 @@ export async function installPluginFromPlatform(
|
|
|
223
229
|
*/
|
|
224
230
|
export async function installPluginViaPlatform(
|
|
225
231
|
opts: { name: string; force?: boolean; conversationId?: string },
|
|
226
|
-
deps: { fetch: FetchLike },
|
|
232
|
+
deps: { fetch: FetchLike; confirmStaged?: ConfirmStagedInstall },
|
|
227
233
|
): Promise<InstallPluginResult> {
|
|
228
234
|
const platformBaseUrl = getPlatformBaseUrl();
|
|
229
235
|
const apiKey = await resolveAssistantApiKey();
|
|
@@ -237,7 +243,12 @@ export async function installPluginViaPlatform(
|
|
|
237
243
|
...(opts.conversationId ? { conversationId: opts.conversationId } : {}),
|
|
238
244
|
assistantVersion: APP_VERSION,
|
|
239
245
|
},
|
|
240
|
-
{
|
|
246
|
+
{
|
|
247
|
+
fetch: deps.fetch,
|
|
248
|
+
platformBaseUrl,
|
|
249
|
+
...(apiKey ? { apiKey } : {}),
|
|
250
|
+
...(deps.confirmStaged ? { confirmStaged: deps.confirmStaged } : {}),
|
|
251
|
+
},
|
|
241
252
|
);
|
|
242
253
|
}
|
|
243
254
|
|