@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
|
@@ -38,6 +38,9 @@
|
|
|
38
38
|
* `plugins:list` tag via the canonical resource-sync publisher (enable and
|
|
39
39
|
* disable emit the SAME invalidation)
|
|
40
40
|
* - Threads `x-vellum-client-id` into the published event's `originClientId`
|
|
41
|
+
* - Pokes the plugin-declared schedule reconcile on a successful toggle (and
|
|
42
|
+
* not on a failed one), so the plugin's rows disarm / re-arm immediately
|
|
43
|
+
* instead of at the reconciler's next backstop sweep
|
|
41
44
|
* - A broadcast failure does not fail a successful toggle (the publisher
|
|
42
45
|
* swallows hub errors)
|
|
43
46
|
* - Maps `InvalidPluginNameError` → BadRequestError (400)
|
|
@@ -311,6 +314,15 @@ mock.module("../../../cli/lib/toggle-plugin.js", () => ({
|
|
|
311
314
|
enablePlugin: enablePluginSpy,
|
|
312
315
|
}));
|
|
313
316
|
|
|
317
|
+
// Spy on the plugin-schedule reconcile the toggle handlers poke. The handlers
|
|
318
|
+
// import it lazily at call time, so this mock intercepts the dynamic import
|
|
319
|
+
// and keeps the reconciler's persistence graph out of the test.
|
|
320
|
+
const reconcilePluginSchedulesSpy = mock(() => Promise.resolve());
|
|
321
|
+
|
|
322
|
+
mock.module("../../../schedule/plugin-schedule-reconciler.js", () => ({
|
|
323
|
+
reconcilePluginSchedules: reconcilePluginSchedulesSpy,
|
|
324
|
+
}));
|
|
325
|
+
|
|
314
326
|
// Spy on broadcastMessage so we can assert the sync_changed invalidation the
|
|
315
327
|
// enable/disable handlers emit. The handlers publish through the canonical
|
|
316
328
|
// `publishPluginsChanged` → `publishSyncInvalidation` path (both left real), so
|
|
@@ -1883,7 +1895,14 @@ function inspection(
|
|
|
1883
1895
|
remoteError: overrides.remoteError ?? null,
|
|
1884
1896
|
surfaces:
|
|
1885
1897
|
overrides.surfaces === undefined
|
|
1886
|
-
? {
|
|
1898
|
+
? {
|
|
1899
|
+
skills: [],
|
|
1900
|
+
hooks: ["post-model-call"],
|
|
1901
|
+
tools: [],
|
|
1902
|
+
schedules: [
|
|
1903
|
+
{ name: "digest", cadence: "0 9 * * *", mode: "execute" },
|
|
1904
|
+
],
|
|
1905
|
+
}
|
|
1887
1906
|
: overrides.surfaces,
|
|
1888
1907
|
};
|
|
1889
1908
|
}
|
|
@@ -1913,6 +1932,26 @@ describe("GET /v1/plugins/:name/inspect", () => {
|
|
|
1913
1932
|
expect(inspectSpy.mock.calls[0]?.[0]).toEqual({ name: "level-up" });
|
|
1914
1933
|
});
|
|
1915
1934
|
|
|
1935
|
+
test("the inspect wire schema carries the schedules surface", async () => {
|
|
1936
|
+
// GIVEN an inspection whose surfaces declare a schedule
|
|
1937
|
+
inspectSpy.mockImplementation(async () => inspection());
|
|
1938
|
+
const result = await invokeInspect({ pathParams: { name: "level-up" } });
|
|
1939
|
+
|
|
1940
|
+
// WHEN the handler result is parsed through the route's response schema
|
|
1941
|
+
// (zod strips undeclared keys, so an omission would drop the field)
|
|
1942
|
+
const route = PLUGINS_ROUTES.find(
|
|
1943
|
+
(r) => r.operationId === "plugins_inspect",
|
|
1944
|
+
)!;
|
|
1945
|
+
const parsed = (
|
|
1946
|
+
route.responseBody as { parse: (v: unknown) => PluginInspection }
|
|
1947
|
+
).parse(result);
|
|
1948
|
+
|
|
1949
|
+
// THEN the declared schedules survive the wire contract
|
|
1950
|
+
expect(parsed.surfaces?.schedules).toEqual([
|
|
1951
|
+
{ name: "digest", cadence: "0 9 * * *", mode: "execute" },
|
|
1952
|
+
]);
|
|
1953
|
+
});
|
|
1954
|
+
|
|
1916
1955
|
test("a captured marketplace error is returned as 200 with remote-unavailable, not thrown", async () => {
|
|
1917
1956
|
// GIVEN the catalog was unreachable but a local copy exists, so the lib
|
|
1918
1957
|
// returns a status rather than throwing
|
|
@@ -2139,6 +2178,90 @@ describe("POST /v1/plugins/:name/upgrade", () => {
|
|
|
2139
2178
|
});
|
|
2140
2179
|
});
|
|
2141
2180
|
|
|
2181
|
+
test("refuses a concurrent upgrade of the same plugin with ConflictError (409)", async () => {
|
|
2182
|
+
// Two upgrades of one plugin derive the same staging path in this
|
|
2183
|
+
// process, so the second would delete the first's tree mid-swap. It is
|
|
2184
|
+
// refused instead \u2014 the monitor's auto-update sweep and a user clicking
|
|
2185
|
+
// Upgrade can now land at the same moment.
|
|
2186
|
+
let releaseFirst: () => void = () => {};
|
|
2187
|
+
const firstStarted = Promise.withResolvers<void>();
|
|
2188
|
+
upgradeSpy.mockImplementation(async () => {
|
|
2189
|
+
firstStarted.resolve();
|
|
2190
|
+
await new Promise<void>((resolve) => {
|
|
2191
|
+
releaseFirst = resolve;
|
|
2192
|
+
});
|
|
2193
|
+
return upgradeResult();
|
|
2194
|
+
});
|
|
2195
|
+
|
|
2196
|
+
const first = invokeUpgrade({ pathParams: { name: "level-up" } });
|
|
2197
|
+
await firstStarted.promise;
|
|
2198
|
+
|
|
2199
|
+
await expect(
|
|
2200
|
+
invokeUpgrade({ pathParams: { name: "level-up" } }),
|
|
2201
|
+
).rejects.toBeInstanceOf(ConflictError);
|
|
2202
|
+
|
|
2203
|
+
releaseFirst();
|
|
2204
|
+
await expect(first).resolves.toMatchObject({ outcome: "upgraded" });
|
|
2205
|
+
|
|
2206
|
+
// The guard is released once the first settles, so the next one runs.
|
|
2207
|
+
upgradeSpy.mockImplementation(async () => upgradeResult());
|
|
2208
|
+
await expect(
|
|
2209
|
+
invokeUpgrade({ pathParams: { name: "level-up" } }),
|
|
2210
|
+
).resolves.toMatchObject({ outcome: "upgraded" });
|
|
2211
|
+
});
|
|
2212
|
+
|
|
2213
|
+
test("a different plugin is not blocked by an in-flight upgrade", async () => {
|
|
2214
|
+
let releaseFirst: () => void = () => {};
|
|
2215
|
+
const firstStarted = Promise.withResolvers<void>();
|
|
2216
|
+
upgradeSpy.mockImplementation(async (opts) => {
|
|
2217
|
+
if (opts.name === "level-up") {
|
|
2218
|
+
firstStarted.resolve();
|
|
2219
|
+
await new Promise<void>((resolve) => {
|
|
2220
|
+
releaseFirst = resolve;
|
|
2221
|
+
});
|
|
2222
|
+
}
|
|
2223
|
+
return upgradeResult({ name: opts.name });
|
|
2224
|
+
});
|
|
2225
|
+
|
|
2226
|
+
const first = invokeUpgrade({ pathParams: { name: "level-up" } });
|
|
2227
|
+
await firstStarted.promise;
|
|
2228
|
+
|
|
2229
|
+
await expect(
|
|
2230
|
+
invokeUpgrade({ pathParams: { name: "other-plugin" } }),
|
|
2231
|
+
).resolves.toMatchObject({ name: "other-plugin" });
|
|
2232
|
+
|
|
2233
|
+
releaseFirst();
|
|
2234
|
+
await first;
|
|
2235
|
+
});
|
|
2236
|
+
|
|
2237
|
+
test("a dry run neither takes nor respects the in-flight guard", async () => {
|
|
2238
|
+
// Dry runs return before the swap boundary, so they never stage.
|
|
2239
|
+
let releaseFirst: () => void = () => {};
|
|
2240
|
+
const firstStarted = Promise.withResolvers<void>();
|
|
2241
|
+
upgradeSpy.mockImplementation(async (opts) => {
|
|
2242
|
+
if (!opts.dryRun) {
|
|
2243
|
+
firstStarted.resolve();
|
|
2244
|
+
await new Promise<void>((resolve) => {
|
|
2245
|
+
releaseFirst = resolve;
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
return upgradeResult();
|
|
2249
|
+
});
|
|
2250
|
+
|
|
2251
|
+
const first = invokeUpgrade({ pathParams: { name: "level-up" } });
|
|
2252
|
+
await firstStarted.promise;
|
|
2253
|
+
|
|
2254
|
+
await expect(
|
|
2255
|
+
invokeUpgrade({
|
|
2256
|
+
pathParams: { name: "level-up" },
|
|
2257
|
+
body: { dryRun: true },
|
|
2258
|
+
}),
|
|
2259
|
+
).resolves.toMatchObject({ outcome: "upgraded" });
|
|
2260
|
+
|
|
2261
|
+
releaseFirst();
|
|
2262
|
+
await first;
|
|
2263
|
+
});
|
|
2264
|
+
|
|
2142
2265
|
test("PluginMergeBaselineError \u2192 ConflictError (409)", async () => {
|
|
2143
2266
|
// A merge strategy whose install-time baseline can't be reconstructed: a
|
|
2144
2267
|
// well-formed request that isn't actionable in the current state.
|
|
@@ -2414,6 +2537,21 @@ function invokeDisable(args: RouteHandlerArgs = {}): { ok: boolean } {
|
|
|
2414
2537
|
return disableHandler(args) as { ok: boolean };
|
|
2415
2538
|
}
|
|
2416
2539
|
|
|
2540
|
+
/**
|
|
2541
|
+
* Wait for the toggle handlers' fire-and-forget reconcile poke to land. The
|
|
2542
|
+
* lazy `import()` resolves off the microtask queue, so the assertion cannot
|
|
2543
|
+
* run on the handler's synchronous return.
|
|
2544
|
+
*/
|
|
2545
|
+
async function waitForScheduleReconcile(): Promise<void> {
|
|
2546
|
+
for (let i = 0; i < 50; i++) {
|
|
2547
|
+
if (reconcilePluginSchedulesSpy.mock.calls.length > 0) {
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
await new Promise((resolve) => setTimeout(resolve, 2));
|
|
2551
|
+
}
|
|
2552
|
+
throw new Error("plugin schedule reconcile was never triggered");
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2417
2555
|
/** Assert the spy received exactly one sync_changed carrying `plugins:list`. */
|
|
2418
2556
|
function expectPluginsListBroadcast(): void {
|
|
2419
2557
|
expect(broadcastMessageSpy.mock.calls).toHaveLength(1);
|
|
@@ -2426,6 +2564,15 @@ describe("POST /v1/plugins/:name/enable", () => {
|
|
|
2426
2564
|
beforeEach(() => {
|
|
2427
2565
|
enablePluginSpy.mockReset();
|
|
2428
2566
|
broadcastMessageSpy.mockReset();
|
|
2567
|
+
reconcilePluginSchedulesSpy.mockClear();
|
|
2568
|
+
});
|
|
2569
|
+
|
|
2570
|
+
test("reconciles plugin-declared schedules so the plugin's rows re-arm now", async () => {
|
|
2571
|
+
enablePluginSpy.mockImplementation((name) => toggleResult(name, "enable"));
|
|
2572
|
+
|
|
2573
|
+
invokeEnable({ pathParams: { name: "simple-memory" } });
|
|
2574
|
+
|
|
2575
|
+
await waitForScheduleReconcile();
|
|
2429
2576
|
});
|
|
2430
2577
|
|
|
2431
2578
|
test("enables the plugin and broadcasts sync_changed(plugins:list)", () => {
|
|
@@ -2504,6 +2651,31 @@ describe("POST /v1/plugins/:name/disable", () => {
|
|
|
2504
2651
|
beforeEach(() => {
|
|
2505
2652
|
disablePluginSpy.mockReset();
|
|
2506
2653
|
broadcastMessageSpy.mockReset();
|
|
2654
|
+
reconcilePluginSchedulesSpy.mockClear();
|
|
2655
|
+
});
|
|
2656
|
+
|
|
2657
|
+
test("reconciles plugin-declared schedules so the plugin's rows disarm now", async () => {
|
|
2658
|
+
disablePluginSpy.mockImplementation((name) =>
|
|
2659
|
+
toggleResult(name, "disable"),
|
|
2660
|
+
);
|
|
2661
|
+
|
|
2662
|
+
invokeDisable({ pathParams: { name: "simple-memory" } });
|
|
2663
|
+
|
|
2664
|
+
// Without this poke the rows stay armed until the reconciler's next
|
|
2665
|
+
// backstop sweep.
|
|
2666
|
+
await waitForScheduleReconcile();
|
|
2667
|
+
});
|
|
2668
|
+
|
|
2669
|
+
test("a failed toggle does not poke the schedule reconcile", async () => {
|
|
2670
|
+
disablePluginSpy.mockImplementation((name) => {
|
|
2671
|
+
throw new PluginDirectoryNotFoundError(name);
|
|
2672
|
+
});
|
|
2673
|
+
|
|
2674
|
+
expect(() => invokeDisable({ pathParams: { name: "ghost" } })).toThrow(
|
|
2675
|
+
NotFoundError,
|
|
2676
|
+
);
|
|
2677
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
2678
|
+
expect(reconcilePluginSchedulesSpy).not.toHaveBeenCalled();
|
|
2507
2679
|
});
|
|
2508
2680
|
|
|
2509
2681
|
test("disables the plugin and broadcasts sync_changed(plugins:list)", () => {
|
|
@@ -394,4 +394,68 @@ describe("POST /v1/question-response", () => {
|
|
|
394
394
|
// Pending interaction left in place.
|
|
395
395
|
expect(pendingInteractions.get("req-legacy-multi")).toBeDefined();
|
|
396
396
|
});
|
|
397
|
+
|
|
398
|
+
// A gone interaction must read as 404 for every body shape. Clients treat
|
|
399
|
+
// 404 as the terminal signal that retires a stale card; a 400 leaves the
|
|
400
|
+
// card on screen and surfaces a body-validation string the user cannot act
|
|
401
|
+
// on. The legacy shapes are the ones that matter here: the web client sends
|
|
402
|
+
// them for every single-question answer, which is the common prompt.
|
|
403
|
+
test("legacy option against a gone interaction is 404, not a validation 400", async () => {
|
|
404
|
+
let thrown: unknown;
|
|
405
|
+
try {
|
|
406
|
+
await call({
|
|
407
|
+
body: { requestId: "req-gone", kind: "option", optionId: "yes" },
|
|
408
|
+
});
|
|
409
|
+
} catch (err) {
|
|
410
|
+
thrown = err;
|
|
411
|
+
}
|
|
412
|
+
expect(thrown).toBeInstanceOf(NotFoundError);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test("legacy free-text against a gone interaction is 404, not a validation 400", async () => {
|
|
416
|
+
let thrown: unknown;
|
|
417
|
+
try {
|
|
418
|
+
await call({
|
|
419
|
+
body: { requestId: "req-gone", kind: "free_text", text: "maybe" },
|
|
420
|
+
});
|
|
421
|
+
} catch (err) {
|
|
422
|
+
thrown = err;
|
|
423
|
+
}
|
|
424
|
+
expect(thrown).toBeInstanceOf(NotFoundError);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("close against a gone interaction is 404", async () => {
|
|
428
|
+
let thrown: unknown;
|
|
429
|
+
try {
|
|
430
|
+
await call({ body: { requestId: "req-gone", kind: "close" } });
|
|
431
|
+
} catch (err) {
|
|
432
|
+
thrown = err;
|
|
433
|
+
}
|
|
434
|
+
expect(thrown).toBeInstanceOf(NotFoundError);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test("legacy option against a confirmation requestId is 404, not a validation 400", async () => {
|
|
438
|
+
// Cross-talk safety has to hold for the legacy shapes too: the wrong-kind
|
|
439
|
+
// interaction carries no question metadata for the shim to read.
|
|
440
|
+
pendingInteractions.register("req-confirm-legacy", {
|
|
441
|
+
conversationId: "conv-1",
|
|
442
|
+
kind: "confirmation",
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
let thrown: unknown;
|
|
446
|
+
try {
|
|
447
|
+
await call({
|
|
448
|
+
body: {
|
|
449
|
+
requestId: "req-confirm-legacy",
|
|
450
|
+
kind: "option",
|
|
451
|
+
optionId: "y",
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
} catch (err) {
|
|
455
|
+
thrown = err;
|
|
456
|
+
}
|
|
457
|
+
expect(thrown).toBeInstanceOf(NotFoundError);
|
|
458
|
+
// The confirmation is left intact rather than consumed.
|
|
459
|
+
expect(pendingInteractions.get("req-confirm-legacy")).toBeDefined();
|
|
460
|
+
});
|
|
397
461
|
});
|
|
@@ -413,6 +413,26 @@ describe("getRetrospectiveConfig handler", () => {
|
|
|
413
413
|
expect(result.success).toBe(true);
|
|
414
414
|
});
|
|
415
415
|
|
|
416
|
+
test("available but not enabled when only the retrospective switch is off", async () => {
|
|
417
|
+
writeFileSync(
|
|
418
|
+
configPath,
|
|
419
|
+
JSON.stringify(
|
|
420
|
+
{ memory: { retrospective: { enabled: false } } },
|
|
421
|
+
null,
|
|
422
|
+
2,
|
|
423
|
+
) + "\n",
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
const handler = findHandler("getRetrospectiveConfig");
|
|
427
|
+
const result = (await handler({})) as ConfigResponse;
|
|
428
|
+
|
|
429
|
+
// `available` stays true so the Settings row still renders. It reads
|
|
430
|
+
// "Paused" rather than disappearing as it would with memory off.
|
|
431
|
+
expect(result.available).toBe(true);
|
|
432
|
+
expect(result.enabled).toBe(false);
|
|
433
|
+
expect(result.success).toBe(true);
|
|
434
|
+
});
|
|
435
|
+
|
|
416
436
|
test("stays available when memory v2 is disabled (retrospectives do not gate on v2)", async () => {
|
|
417
437
|
writeFileSync(
|
|
418
438
|
configPath,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the `getSkillHistory` route's current-resource boundary.
|
|
3
|
+
*
|
|
4
|
+
* The service layer reads retained git history, which outlives the skill it
|
|
5
|
+
* describes: a deleted skill's commits stay in the workspace repository
|
|
6
|
+
* forever. The route is therefore the only place that can decide whether an id
|
|
7
|
+
* still names something, and it must reach the same answer as the sibling file
|
|
8
|
+
* routes rather than serving history for a resource the rest of the API
|
|
9
|
+
* reports as gone.
|
|
10
|
+
*
|
|
11
|
+
* Both collaborators are mocked, because what is under test is the ordering
|
|
12
|
+
* between them: the existence check has to gate the history read, not run
|
|
13
|
+
* alongside it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
const skillExistsLocallyMock = mock((_id: string): boolean => true);
|
|
19
|
+
const getSkillHistoryMock = mock(async (_id: string, _opts?: unknown) => ({
|
|
20
|
+
skillId: _id,
|
|
21
|
+
revisions: [{ id: "abc1234", changedAt: "", files: ["SKILL.md"], diff: "" }],
|
|
22
|
+
truncatedByCompaction: false,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
mock.module("../../../daemon/handlers/skills.js", () => ({
|
|
26
|
+
skillExistsLocally: skillExistsLocallyMock,
|
|
27
|
+
// The route module imports the whole handler surface; the rest is unused
|
|
28
|
+
// here and only needs to exist.
|
|
29
|
+
checkSkillUpdates: mock(),
|
|
30
|
+
configureSkill: mock(),
|
|
31
|
+
createSkill: mock(),
|
|
32
|
+
disableSkill: mock(),
|
|
33
|
+
draftSkill: mock(),
|
|
34
|
+
enableSkill: mock(),
|
|
35
|
+
getSkill: mock(),
|
|
36
|
+
getSkillFileContent: mock(),
|
|
37
|
+
getSkillFiles: mock(),
|
|
38
|
+
getSkillLocalDetail: mock(),
|
|
39
|
+
inspectSkill: mock(),
|
|
40
|
+
installSkill: mock(),
|
|
41
|
+
listSkills: mock(),
|
|
42
|
+
listSkillsFiltered: mock(),
|
|
43
|
+
searchSkills: mock(),
|
|
44
|
+
uninstallSkill: mock(),
|
|
45
|
+
updateSkill: mock(),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
mock.module("../../../skills/skill-history.js", () => ({
|
|
49
|
+
getSkillHistory: getSkillHistoryMock,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
const { ROUTES } = await import("../skills-routes.js");
|
|
53
|
+
|
|
54
|
+
const handler = ROUTES.find(
|
|
55
|
+
(r) => r.operationId === "getSkillHistory",
|
|
56
|
+
)!.handler;
|
|
57
|
+
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
skillExistsLocallyMock.mockClear();
|
|
60
|
+
getSkillHistoryMock.mockClear();
|
|
61
|
+
skillExistsLocallyMock.mockImplementation(() => true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("getSkillHistory route", () => {
|
|
65
|
+
test("returns history for a skill that currently exists", async () => {
|
|
66
|
+
const result = (await handler({
|
|
67
|
+
pathParams: { id: "release-triage" },
|
|
68
|
+
})) as {
|
|
69
|
+
revisions: unknown[];
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
expect(result.revisions).toHaveLength(1);
|
|
73
|
+
expect(getSkillHistoryMock).toHaveBeenCalledTimes(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("404s for a deleted skill whose commits are still in the repository", async () => {
|
|
77
|
+
// The skill is gone from the resolver, but git would still answer.
|
|
78
|
+
skillExistsLocallyMock.mockImplementation(() => false);
|
|
79
|
+
|
|
80
|
+
await expect(
|
|
81
|
+
handler({ pathParams: { id: "release-triage" } }),
|
|
82
|
+
).rejects.toThrow(/not found/i);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("does not read history at all when the skill is gone", async () => {
|
|
86
|
+
skillExistsLocallyMock.mockImplementation(() => false);
|
|
87
|
+
|
|
88
|
+
await Promise.resolve(
|
|
89
|
+
handler({ pathParams: { id: "release-triage" } }),
|
|
90
|
+
).catch(() => {});
|
|
91
|
+
|
|
92
|
+
// Ordering is the point: a check that ran after the read would still
|
|
93
|
+
// reject, but would have spent the git traversal to do it.
|
|
94
|
+
expect(getSkillHistoryMock).not.toHaveBeenCalled();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("rejects a malformed id as the caller's error, not a server fault", async () => {
|
|
98
|
+
getSkillHistoryMock.mockImplementation(() => {
|
|
99
|
+
throw new Error("Invalid skill id: contains a path separator");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await expect(handler({ pathParams: { id: "bad" } })).rejects.toMatchObject({
|
|
103
|
+
statusCode: 400,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("surfaces an unexpected service failure as a server error", async () => {
|
|
108
|
+
getSkillHistoryMock.mockImplementation(() => {
|
|
109
|
+
throw new Error("git exploded");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await expect(handler({ pathParams: { id: "ok" } })).rejects.toMatchObject({
|
|
113
|
+
statusCode: 500,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* Asserts the `wake_conversation` route elevates trust from the caller's
|
|
3
3
|
* verified principal type, never the request body: a `local` (CLI/IPC) caller
|
|
4
4
|
* runs the woken turn as a non-interactive guardian (clientless + guardian
|
|
5
|
-
* trustContext) and may attribute cost to a body-supplied `cronRunId
|
|
6
|
-
*
|
|
5
|
+
* trustContext) and may attribute cost to a body-supplied `cronRunId` and run
|
|
6
|
+
* on a body-supplied schedule's pinned inference profile; a remote `actor`
|
|
7
|
+
* stays `unknown`/interactive and its body `cronRunId` and `scheduleId` are
|
|
8
|
+
* ignored.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import { describe, expect, mock, test } from "bun:test";
|
|
@@ -13,6 +15,7 @@ interface CapturedWake {
|
|
|
13
15
|
hint: string;
|
|
14
16
|
trustContext?: { sourceChannel: string; trustClass: string };
|
|
15
17
|
cronRunId?: string;
|
|
18
|
+
forceOverrideProfile?: string;
|
|
16
19
|
clientless?: boolean;
|
|
17
20
|
persistTriggerAsEvent?: boolean;
|
|
18
21
|
untrustedOutput?: { content: string; source: string };
|
|
@@ -25,6 +28,14 @@ mock.module("../../agent-wake.js", () => ({
|
|
|
25
28
|
},
|
|
26
29
|
}));
|
|
27
30
|
|
|
31
|
+
const schedules = new Map<string, { inferenceProfile: string | null }>([
|
|
32
|
+
["sched-pinned", { inferenceProfile: "profile-cheap" }],
|
|
33
|
+
["sched-unpinned", { inferenceProfile: null }],
|
|
34
|
+
]);
|
|
35
|
+
mock.module("../../../schedule/schedule-store.js", () => ({
|
|
36
|
+
getSchedule: (id: string) => schedules.get(id) ?? null,
|
|
37
|
+
}));
|
|
38
|
+
|
|
28
39
|
import { createConversation } from "../../../persistence/conversation-crud.js";
|
|
29
40
|
import { initializeDb } from "../../../persistence/db-init.js";
|
|
30
41
|
import { ROUTES } from "../wake-conversation-routes.js";
|
|
@@ -85,6 +96,53 @@ describe("wake_conversation principal-gated elevation", () => {
|
|
|
85
96
|
});
|
|
86
97
|
});
|
|
87
98
|
|
|
99
|
+
describe("wake_conversation schedule profile pin", () => {
|
|
100
|
+
test("local principal → woken turn runs on the schedule's pinned profile", async () => {
|
|
101
|
+
wakeCalls.length = 0;
|
|
102
|
+
const conversationId = makeConversation();
|
|
103
|
+
await handler({
|
|
104
|
+
body: { conversationId, hint: "poll result", scheduleId: "sched-pinned" },
|
|
105
|
+
headers: { "x-vellum-principal-type": "local" },
|
|
106
|
+
});
|
|
107
|
+
expect(wakeCalls[0].forceOverrideProfile).toBe("profile-cheap");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("schedule with no pinned profile leaves resolution unchanged", async () => {
|
|
111
|
+
wakeCalls.length = 0;
|
|
112
|
+
const conversationId = makeConversation();
|
|
113
|
+
await handler({
|
|
114
|
+
body: {
|
|
115
|
+
conversationId,
|
|
116
|
+
hint: "poll result",
|
|
117
|
+
scheduleId: "sched-unpinned",
|
|
118
|
+
},
|
|
119
|
+
headers: { "x-vellum-principal-type": "local" },
|
|
120
|
+
});
|
|
121
|
+
expect(wakeCalls[0].forceOverrideProfile).toBeUndefined();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("unknown scheduleId resolves as an ordinary wake", async () => {
|
|
125
|
+
wakeCalls.length = 0;
|
|
126
|
+
const conversationId = makeConversation();
|
|
127
|
+
const result = await handler({
|
|
128
|
+
body: { conversationId, hint: "poll result", scheduleId: "sched-gone" },
|
|
129
|
+
headers: { "x-vellum-principal-type": "local" },
|
|
130
|
+
});
|
|
131
|
+
expect(result).toEqual({ invoked: true, producedToolCalls: false });
|
|
132
|
+
expect(wakeCalls[0].forceOverrideProfile).toBeUndefined();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("actor principal → body scheduleId is ignored", async () => {
|
|
136
|
+
wakeCalls.length = 0;
|
|
137
|
+
const conversationId = makeConversation();
|
|
138
|
+
await handler({
|
|
139
|
+
body: { conversationId, hint: "poll result", scheduleId: "sched-pinned" },
|
|
140
|
+
headers: { "x-vellum-principal-type": "actor" },
|
|
141
|
+
});
|
|
142
|
+
expect(wakeCalls[0].forceOverrideProfile).toBeUndefined();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
88
146
|
describe("wake_conversation fencing", () => {
|
|
89
147
|
test("persist + externalContent fence untrusted data, keep hint trusted", async () => {
|
|
90
148
|
wakeCalls.length = 0;
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* log export and profiler export routes.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
import { promisify } from "node:util";
|
|
8
|
+
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
7
10
|
|
|
8
11
|
/** Maximum compressed archive size (50 MB). */
|
|
9
12
|
export const MAX_ARCHIVE_BYTES = 50 * 1024 * 1024;
|
|
@@ -13,19 +16,20 @@ export const MAX_ARCHIVE_BYTES = 50 * 1024 * 1024;
|
|
|
13
16
|
* Returns the Buffer on success, or `undefined` if the archive exceeds
|
|
14
17
|
* the size limit or tar otherwise fails.
|
|
15
18
|
*/
|
|
16
|
-
export function createTarGz(
|
|
19
|
+
export async function createTarGz(
|
|
17
20
|
staging: string,
|
|
18
21
|
maxBytes: number = MAX_ARCHIVE_BYTES,
|
|
19
|
-
): ArrayBuffer | undefined {
|
|
20
|
-
|
|
21
|
-
maxBuffer
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
): Promise<ArrayBuffer | undefined> {
|
|
23
|
+
try {
|
|
24
|
+
// Exceeding maxBuffer rejects, which maps to the undefined failure return.
|
|
25
|
+
const { stdout } = await execFileAsync(
|
|
26
|
+
"tar",
|
|
27
|
+
["czf", "-", "-C", staging, "."],
|
|
28
|
+
{ maxBuffer: maxBytes, timeout: 30_000, encoding: "buffer" },
|
|
29
|
+
);
|
|
30
|
+
const buf = Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);
|
|
31
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
32
|
+
} catch {
|
|
25
33
|
return undefined;
|
|
26
34
|
}
|
|
27
|
-
const buf = Buffer.isBuffer(proc.stdout)
|
|
28
|
-
? proc.stdout
|
|
29
|
-
: Buffer.from(proc.stdout);
|
|
30
|
-
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
31
35
|
}
|
|
@@ -238,7 +238,7 @@ async function handleMultipartUpload(
|
|
|
238
238
|
|
|
239
239
|
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
240
240
|
|
|
241
|
-
const attachment = uploadAttachmentFromBytes(filename, mimeType, bytes);
|
|
241
|
+
const attachment = await uploadAttachmentFromBytes(filename, mimeType, bytes);
|
|
242
242
|
return attachmentPayload(attachment);
|
|
243
243
|
}
|
|
244
244
|
|
|
@@ -248,7 +248,7 @@ async function handleMultipartUpload(
|
|
|
248
248
|
*
|
|
249
249
|
* See `handleMultipartUpload` for `gatewayTrustedSource` semantics.
|
|
250
250
|
*/
|
|
251
|
-
function handleOctetStreamUpload(
|
|
251
|
+
async function handleOctetStreamUpload(
|
|
252
252
|
rawBody: Uint8Array,
|
|
253
253
|
headers: Record<string, string>,
|
|
254
254
|
queryParams: Record<string, string>,
|
|
@@ -287,7 +287,11 @@ function handleOctetStreamUpload(
|
|
|
287
287
|
throw new UnsupportedMediaTypeError(validation.error);
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
const attachment = uploadAttachmentFromBytes(
|
|
290
|
+
const attachment = await uploadAttachmentFromBytes(
|
|
291
|
+
filename,
|
|
292
|
+
mimeType,
|
|
293
|
+
rawBody,
|
|
294
|
+
);
|
|
291
295
|
return attachmentPayload(attachment);
|
|
292
296
|
}
|
|
293
297
|
|
|
@@ -296,7 +300,7 @@ function handleOctetStreamUpload(
|
|
|
296
300
|
*
|
|
297
301
|
* See `handleMultipartUpload` for `gatewayTrustedSource` semantics.
|
|
298
302
|
*/
|
|
299
|
-
function handleJsonUpload(
|
|
303
|
+
async function handleJsonUpload(
|
|
300
304
|
body: Record<string, unknown>,
|
|
301
305
|
rawBody: Uint8Array | undefined,
|
|
302
306
|
gatewayTrustedSource: boolean,
|
|
@@ -369,7 +373,10 @@ function handleJsonUpload(
|
|
|
369
373
|
// non-HEIF files (e.g. large videos) from being read into memory.
|
|
370
374
|
try {
|
|
371
375
|
if (isHeifImage(readFileHead(destPath, 12))) {
|
|
372
|
-
const norm = normalizeImageBytes(
|
|
376
|
+
const norm = await normalizeImageBytes(
|
|
377
|
+
mimeType,
|
|
378
|
+
readFileSync(destPath),
|
|
379
|
+
);
|
|
373
380
|
if (
|
|
374
381
|
norm.converted &&
|
|
375
382
|
norm.bytes.length <= MAX_FILE_BACKED_UPLOAD_BYTES
|
|
@@ -407,7 +414,7 @@ function handleJsonUpload(
|
|
|
407
414
|
}
|
|
408
415
|
|
|
409
416
|
try {
|
|
410
|
-
attachment = uploadAttachment(
|
|
417
|
+
attachment = await uploadAttachment(
|
|
411
418
|
filename,
|
|
412
419
|
mimeType,
|
|
413
420
|
data,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deferred release for the canned-reply routes (canned first greeting, unknown
|
|
3
|
+
* slash command).
|
|
4
|
+
*
|
|
5
|
+
* These routes answer without an agent loop, so they hold the processing lock
|
|
6
|
+
* themselves and are the only thing that will ever clear it. They publish their
|
|
7
|
+
* client events on the next tick so the HTTP 202 reaches the client first and
|
|
8
|
+
* its `serverToLocalConversationMap` is populated before any SSE arrives, and
|
|
9
|
+
* they release only after that burst so the next queued message cannot start
|
|
10
|
+
* processing ahead of the reply the client is still being told about.
|
|
11
|
+
*
|
|
12
|
+
* The release therefore has to be unconditional: a throw from any broadcast in
|
|
13
|
+
* the burst would otherwise latch the conversation "processing" with no agent
|
|
14
|
+
* loop, no `finally`, and no later path left to clear it, so every subsequent
|
|
15
|
+
* send is queued behind an idle conversation for the life of the daemon.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { Conversation } from "../../daemon/conversation.js";
|
|
19
|
+
import { getLogger } from "../../util/logger.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("canned-reply-release");
|
|
22
|
+
|
|
23
|
+
/** The conversation surface {@link scheduleCannedReplyRelease} touches. */
|
|
24
|
+
export type CannedReplyReleaseTarget = Pick<
|
|
25
|
+
Conversation,
|
|
26
|
+
"setProcessing" | "kickDrainQueue"
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
export function scheduleCannedReplyRelease(params: {
|
|
30
|
+
conversation: CannedReplyReleaseTarget;
|
|
31
|
+
/** Names the release site in drain logs (e.g. `"canned_greeting"`). */
|
|
32
|
+
origin: string;
|
|
33
|
+
/** The deferred client event burst. Runs before the release. */
|
|
34
|
+
emit: () => void;
|
|
35
|
+
/** Optional work that only makes sense once the lock is free. */
|
|
36
|
+
afterRelease?: () => void;
|
|
37
|
+
}): void {
|
|
38
|
+
const { conversation, origin, emit, afterRelease } = params;
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
try {
|
|
41
|
+
emit();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
// Swallowed rather than rethrown: this runs from a timer with no caller
|
|
44
|
+
// left to handle it, so an escaping error is an uncaught exception in
|
|
45
|
+
// the daemon process. The reply is already persisted, and clients
|
|
46
|
+
// reconcile a missed event burst on their next fetch.
|
|
47
|
+
log.error({ err, origin }, "Canned reply event burst failed");
|
|
48
|
+
} finally {
|
|
49
|
+
conversation.setProcessing(false);
|
|
50
|
+
void conversation.kickDrainQueue("loop_complete", origin);
|
|
51
|
+
}
|
|
52
|
+
afterRelease?.();
|
|
53
|
+
}, 0);
|
|
54
|
+
}
|