@vellumai/assistant 0.11.2 → 0.11.3-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue-cancellation and steer-recovery contracts on the shared handlers.
|
|
3
|
+
*
|
|
4
|
+
* `deleteQueuedMessage` closes out the queued row on the sender's event sink,
|
|
5
|
+
* pairing that terminal event with the same visibility predicate the enqueue
|
|
6
|
+
* ack used.
|
|
7
|
+
*
|
|
8
|
+
* `steerToMessage` promotes a queued message and then aborts the in-flight
|
|
9
|
+
* turn. When the flag is latched with no live controller there is nothing to
|
|
10
|
+
* abort and no agent-loop release coming, so the handler force-clears and
|
|
11
|
+
* drains itself instead of leaving the promoted message at the head of a queue
|
|
12
|
+
* that never runs.
|
|
13
|
+
*/
|
|
14
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
15
|
+
|
|
16
|
+
import type { AssistantEvent } from "../api/index.js";
|
|
17
|
+
|
|
18
|
+
const broadcasts: AssistantEvent[] = [];
|
|
19
|
+
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
20
|
+
broadcastMessage: (msg: AssistantEvent) => {
|
|
21
|
+
broadcasts.push(msg);
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
import type { Conversation } from "../daemon/conversation.js";
|
|
26
|
+
import type { QueuedMessage } from "../daemon/conversation-queue-manager.js";
|
|
27
|
+
import {
|
|
28
|
+
deleteConversation,
|
|
29
|
+
setConversation,
|
|
30
|
+
} from "../daemon/conversation-registry.js";
|
|
31
|
+
import {
|
|
32
|
+
deleteQueuedMessage,
|
|
33
|
+
steerToMessage,
|
|
34
|
+
} from "../daemon/handlers/conversations.js";
|
|
35
|
+
|
|
36
|
+
const CONV = "queued-cancel-conv";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register a conversation holding one queued message, and hand back the events
|
|
40
|
+
* the handler delivered to that message's sender sink.
|
|
41
|
+
*/
|
|
42
|
+
function registerWithQueuedMessage(
|
|
43
|
+
queued: Partial<QueuedMessage> | undefined,
|
|
44
|
+
): AssistantEvent[] {
|
|
45
|
+
const sent: AssistantEvent[] = [];
|
|
46
|
+
const item = queued
|
|
47
|
+
? { ...queued, onEvent: (msg: AssistantEvent) => sent.push(msg) }
|
|
48
|
+
: undefined;
|
|
49
|
+
const fake = {
|
|
50
|
+
conversationId: CONV,
|
|
51
|
+
isProcessing: () => false,
|
|
52
|
+
queue: {
|
|
53
|
+
findByRequestId: (requestId: string) =>
|
|
54
|
+
item?.requestId === requestId ? item : undefined,
|
|
55
|
+
},
|
|
56
|
+
removeQueuedMessage: () => item,
|
|
57
|
+
};
|
|
58
|
+
setConversation(CONV, fake as unknown as Conversation);
|
|
59
|
+
return sent;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface LatchedTurn {
|
|
63
|
+
cleared: () => boolean;
|
|
64
|
+
drainReasons: string[];
|
|
65
|
+
pendingSteerRepair: () => boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Register a conversation whose processing flag is latched with no live
|
|
70
|
+
* controller: the shape a turn leaves behind when it tore its controller down
|
|
71
|
+
* (or died) without any release running.
|
|
72
|
+
*/
|
|
73
|
+
function registerLatchedTurn(): LatchedTurn {
|
|
74
|
+
let processing = true;
|
|
75
|
+
const drainReasons: string[] = [];
|
|
76
|
+
const fake = {
|
|
77
|
+
conversationId: CONV,
|
|
78
|
+
isProcessing: () => processing,
|
|
79
|
+
setProcessing: (value: boolean) => {
|
|
80
|
+
processing = value;
|
|
81
|
+
},
|
|
82
|
+
abortController: null,
|
|
83
|
+
queue: {
|
|
84
|
+
findByRequestId: (requestId: string) => ({ requestId }),
|
|
85
|
+
promoteToHead: (requestId: string) => ({ requestId }),
|
|
86
|
+
},
|
|
87
|
+
pendingSteerRepair: false,
|
|
88
|
+
kickDrainQueue: (reason: string) => {
|
|
89
|
+
drainReasons.push(reason);
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
},
|
|
92
|
+
denyAllPendingConfirmations: () => {},
|
|
93
|
+
};
|
|
94
|
+
setConversation(CONV, fake as unknown as Conversation);
|
|
95
|
+
return {
|
|
96
|
+
cleared: () => !processing,
|
|
97
|
+
drainReasons,
|
|
98
|
+
pendingSteerRepair: () => fake.pendingSteerRepair,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
describe("deleteQueuedMessage", () => {
|
|
103
|
+
afterEach(() => {
|
|
104
|
+
broadcasts.length = 0;
|
|
105
|
+
deleteConversation(CONV);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("closes out the queued row so clients retire the pending indicator", () => {
|
|
109
|
+
const sent = registerWithQueuedMessage({
|
|
110
|
+
requestId: "req-1",
|
|
111
|
+
clientMessageId: "client-1",
|
|
112
|
+
metadata: {},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(deleteQueuedMessage(CONV, "req-1")).toEqual({ removed: true });
|
|
116
|
+
expect(sent).toEqual([
|
|
117
|
+
{
|
|
118
|
+
type: "message_queued_deleted",
|
|
119
|
+
conversationId: CONV,
|
|
120
|
+
requestId: "req-1",
|
|
121
|
+
clientMessageId: "client-1",
|
|
122
|
+
},
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("omits clientMessageId when the sender minted none", () => {
|
|
127
|
+
const sent = registerWithQueuedMessage({
|
|
128
|
+
requestId: "req-2",
|
|
129
|
+
metadata: {},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(deleteQueuedMessage(CONV, "req-2")).toEqual({ removed: true });
|
|
133
|
+
expect(sent).toEqual([
|
|
134
|
+
{
|
|
135
|
+
type: "message_queued_deleted",
|
|
136
|
+
conversationId: CONV,
|
|
137
|
+
requestId: "req-2",
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("stays silent for an echo-suppressed entry", () => {
|
|
143
|
+
// A daemon-injected notification never produced a `message_queued` ack, so
|
|
144
|
+
// a delete event would be an unpaired retire for a row no client renders.
|
|
145
|
+
const sent = registerWithQueuedMessage({
|
|
146
|
+
requestId: "req-3",
|
|
147
|
+
metadata: {
|
|
148
|
+
subagentNotification: { subagentId: "child-1", label: "research" },
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expect(deleteQueuedMessage(CONV, "req-3")).toEqual({ removed: true });
|
|
153
|
+
expect(sent).toEqual([]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("stays silent when nothing matched the request id", () => {
|
|
157
|
+
const sent = registerWithQueuedMessage(undefined);
|
|
158
|
+
|
|
159
|
+
expect(deleteQueuedMessage(CONV, "req-missing")).toEqual({
|
|
160
|
+
removed: false,
|
|
161
|
+
reason: "message_not_found",
|
|
162
|
+
});
|
|
163
|
+
expect(sent).toEqual([]);
|
|
164
|
+
expect(broadcasts).toEqual([]);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe("steerToMessage with a latched processing flag", () => {
|
|
169
|
+
afterEach(() => {
|
|
170
|
+
broadcasts.length = 0;
|
|
171
|
+
deleteConversation(CONV);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("force-clears and drains when no live controller can be aborted", () => {
|
|
175
|
+
const turn = registerLatchedTurn();
|
|
176
|
+
|
|
177
|
+
expect(steerToMessage(CONV, "req-steer")).toEqual({ steered: true });
|
|
178
|
+
|
|
179
|
+
// Without the force-clear the promoted message would sit behind a flag
|
|
180
|
+
// nothing is going to release.
|
|
181
|
+
expect(turn.cleared()).toBe(true);
|
|
182
|
+
expect(turn.drainReasons).toEqual(["loop_complete"]);
|
|
183
|
+
// The drain consumes the repair flag, so it must survive the force-clear.
|
|
184
|
+
expect(turn.pendingSteerRepair()).toBe(true);
|
|
185
|
+
expect(broadcasts).toEqual([
|
|
186
|
+
{ type: "message_steered", conversationId: CONV, requestId: "req-steer" },
|
|
187
|
+
]);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorization for steering to a queued message.
|
|
3
|
+
*
|
|
4
|
+
* Steering aborts the in-flight generation and promotes the named queued
|
|
5
|
+
* message to the head of the queue, so it is scoped to the actor principal
|
|
6
|
+
* that enqueued the message the same way deletion is: every subscriber sees
|
|
7
|
+
* every `message_queued` ack, so requestIds are not secrets and cannot stand
|
|
8
|
+
* in for authorization. The caller identity is normalized exactly as the send
|
|
9
|
+
* path normalizes it before recording `sourceActorPrincipalId`.
|
|
10
|
+
*/
|
|
11
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
MessageQueue,
|
|
15
|
+
type QueuedMessage,
|
|
16
|
+
} from "../daemon/conversation-queue-manager.js";
|
|
17
|
+
import {
|
|
18
|
+
deleteConversation,
|
|
19
|
+
setConversation,
|
|
20
|
+
} from "../daemon/conversation-registry.js";
|
|
21
|
+
import { ROUTES } from "../runtime/routes/conversation-query-routes.js";
|
|
22
|
+
|
|
23
|
+
const steerRoute = ROUTES.find(
|
|
24
|
+
(route) => route.operationId === "messages_queued_steer",
|
|
25
|
+
)!;
|
|
26
|
+
|
|
27
|
+
const registered: string[] = [];
|
|
28
|
+
|
|
29
|
+
interface QueuedFixture {
|
|
30
|
+
requestId: string;
|
|
31
|
+
sourceActorPrincipalId?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Register a live, mid-turn conversation holding `items`. The steer path
|
|
36
|
+
* reads the in-memory registry and touches no persistence, so a queue-only
|
|
37
|
+
* stand-in exercises the real code.
|
|
38
|
+
*/
|
|
39
|
+
function seedProcessingConversation(
|
|
40
|
+
conversationId: string,
|
|
41
|
+
items: QueuedFixture[],
|
|
42
|
+
): { queue: MessageQueue; aborted: () => boolean } {
|
|
43
|
+
const queue = new MessageQueue();
|
|
44
|
+
for (const item of items) {
|
|
45
|
+
queue.push({
|
|
46
|
+
content: `content for ${item.requestId}`,
|
|
47
|
+
attachments: [],
|
|
48
|
+
requestId: item.requestId,
|
|
49
|
+
onEvent: () => {},
|
|
50
|
+
sentAt: Date.now(),
|
|
51
|
+
sourceActorPrincipalId: item.sourceActorPrincipalId,
|
|
52
|
+
} as QueuedMessage);
|
|
53
|
+
}
|
|
54
|
+
const abortController = new AbortController();
|
|
55
|
+
const conversation = {
|
|
56
|
+
conversationId,
|
|
57
|
+
queue,
|
|
58
|
+
isProcessing: () => true,
|
|
59
|
+
pendingSteerRepair: false,
|
|
60
|
+
abortController,
|
|
61
|
+
denyAllPendingConfirmations: () => {},
|
|
62
|
+
};
|
|
63
|
+
setConversation(conversationId, conversation as never);
|
|
64
|
+
registered.push(conversationId);
|
|
65
|
+
return { queue, aborted: () => abortController.signal.aborted };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function dispatchSteer(args: {
|
|
69
|
+
conversationId: string;
|
|
70
|
+
requestId: string;
|
|
71
|
+
actorPrincipalId?: string;
|
|
72
|
+
}) {
|
|
73
|
+
return Promise.resolve(
|
|
74
|
+
steerRoute.handler({
|
|
75
|
+
pathParams: { id: args.requestId },
|
|
76
|
+
queryParams: { conversationId: args.conversationId },
|
|
77
|
+
headers:
|
|
78
|
+
args.actorPrincipalId !== undefined
|
|
79
|
+
? { "x-vellum-actor-principal-id": args.actorPrincipalId }
|
|
80
|
+
: {},
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
for (const conversationId of registered.splice(0)) {
|
|
87
|
+
deleteConversation(conversationId);
|
|
88
|
+
}
|
|
89
|
+
delete process.env.DISABLE_HTTP_AUTH;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("queued message steer: actor scoping", () => {
|
|
93
|
+
test("a different actor principal cannot steer to someone else's queued message", async () => {
|
|
94
|
+
const { queue, aborted } = seedProcessingConversation("conv-steer-1", [
|
|
95
|
+
{ requestId: "r-head", sourceActorPrincipalId: "actor-owner" },
|
|
96
|
+
{ requestId: "r-target", sourceActorPrincipalId: "actor-owner" },
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
await expect(
|
|
100
|
+
dispatchSteer({
|
|
101
|
+
conversationId: "conv-steer-1",
|
|
102
|
+
requestId: "r-target",
|
|
103
|
+
actorPrincipalId: "actor-bystander",
|
|
104
|
+
}),
|
|
105
|
+
).rejects.toThrow("sent by a different user");
|
|
106
|
+
|
|
107
|
+
// The queue order is untouched and the live generation kept running.
|
|
108
|
+
expect(queue.peek(0)?.requestId).toBe("r-head");
|
|
109
|
+
expect(aborted()).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("the enqueuing actor principal can steer to its own queued message", async () => {
|
|
113
|
+
const { queue, aborted } = seedProcessingConversation("conv-steer-2", [
|
|
114
|
+
{ requestId: "r-head", sourceActorPrincipalId: "actor-owner" },
|
|
115
|
+
{ requestId: "r-target", sourceActorPrincipalId: "actor-owner" },
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
expect(
|
|
119
|
+
await dispatchSteer({
|
|
120
|
+
conversationId: "conv-steer-2",
|
|
121
|
+
requestId: "r-target",
|
|
122
|
+
actorPrincipalId: "actor-owner",
|
|
123
|
+
}),
|
|
124
|
+
).toEqual({
|
|
125
|
+
ok: true,
|
|
126
|
+
conversationId: "conv-steer-2",
|
|
127
|
+
requestId: "r-target",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(queue.peek(0)?.requestId).toBe("r-target");
|
|
131
|
+
expect(aborted()).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("a caller with no actor principal is the guardian by construction", async () => {
|
|
135
|
+
const { queue } = seedProcessingConversation("conv-steer-3", [
|
|
136
|
+
{ requestId: "r-head", sourceActorPrincipalId: "actor-owner" },
|
|
137
|
+
{ requestId: "r-target", sourceActorPrincipalId: "actor-owner" },
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
// Local/IPC and service principals carry no actorPrincipalId; the CLI
|
|
141
|
+
// must keep being able to steer.
|
|
142
|
+
await dispatchSteer({
|
|
143
|
+
conversationId: "conv-steer-3",
|
|
144
|
+
requestId: "r-target",
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(queue.peek(0)?.requestId).toBe("r-target");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("a daemon-internal enqueue with no recorded requester stays steerable", async () => {
|
|
151
|
+
const { queue } = seedProcessingConversation("conv-steer-4", [
|
|
152
|
+
{ requestId: "r-head" },
|
|
153
|
+
{ requestId: "r-target" },
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
await dispatchSteer({
|
|
157
|
+
conversationId: "conv-steer-4",
|
|
158
|
+
requestId: "r-target",
|
|
159
|
+
actorPrincipalId: "actor-anyone",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(queue.peek(0)?.requestId).toBe("r-target");
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -78,6 +78,26 @@ describe("mapChatTypeToConversationType", () => {
|
|
|
78
78
|
test("maps DM-shaped chat types to dm", () => {
|
|
79
79
|
expect(mapChatTypeToConversationType("im")).toBe("dm"); // Slack DM
|
|
80
80
|
expect(mapChatTypeToConversationType("private")).toBe("dm"); // Telegram DM
|
|
81
|
+
expect(mapChatTypeToConversationType("dm")).toBe("dm"); // Discord DM
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("every DM-capable channel's own word for a DM lands on the axis", () => {
|
|
85
|
+
// A missing arm is silent rather than loud: the permission cell stays
|
|
86
|
+
// settable, because the adapter validates against the channel registry,
|
|
87
|
+
// and simply never matches. The guardian sets a rule for DMs on that
|
|
88
|
+
// channel and it does nothing. Stated as a sweep so adding a DM-capable
|
|
89
|
+
// channel without its arm fails here.
|
|
90
|
+
const dmChatTypeByChannel = {
|
|
91
|
+
slack: "im",
|
|
92
|
+
telegram: "private",
|
|
93
|
+
whatsapp: "private",
|
|
94
|
+
discord: "dm",
|
|
95
|
+
};
|
|
96
|
+
for (const [channel, chatType] of Object.entries(dmChatTypeByChannel)) {
|
|
97
|
+
expect(`${channel}:${mapChatTypeToConversationType(chatType)}`).toBe(
|
|
98
|
+
`${channel}:dm`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
81
101
|
});
|
|
82
102
|
|
|
83
103
|
test("maps closed-group chat types to private", () => {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* sequences.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
8
10
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
11
|
|
|
10
12
|
mock.module("../background-wake/publisher.js", () => ({
|
|
@@ -58,8 +60,11 @@ import { initializeDb } from "../persistence/db-init.js";
|
|
|
58
60
|
import {
|
|
59
61
|
createSchedule,
|
|
60
62
|
deferClaimedSchedule,
|
|
63
|
+
upsertDeclaredSchedule,
|
|
61
64
|
} from "../schedule/schedule-store.js";
|
|
62
65
|
import { runDueSchedulesOnce } from "../schedule/scheduler.js";
|
|
66
|
+
import { getWorkspacePluginsDir } from "../util/platform.js";
|
|
67
|
+
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
63
68
|
|
|
64
69
|
await initializeDb();
|
|
65
70
|
|
|
@@ -217,3 +222,172 @@ describe("runDueSchedulesOnce (the schedule worker's tick)", () => {
|
|
|
217
222
|
expect(row.next_run_at).toBeGreaterThan(Date.now());
|
|
218
223
|
});
|
|
219
224
|
});
|
|
225
|
+
|
|
226
|
+
describe("fire-time source-availability gate", () => {
|
|
227
|
+
const PLUGIN_NAME = "example-plugin";
|
|
228
|
+
const SCHEDULE_NAME = "daily-digest";
|
|
229
|
+
const SOURCE_KEY = `plugin:${PLUGIN_NAME}/${SCHEDULE_NAME}`;
|
|
230
|
+
const pluginDir = join(getWorkspacePluginsDir(), PLUGIN_NAME);
|
|
231
|
+
const declarationDir = join(pluginDir, "schedules", SCHEDULE_NAME);
|
|
232
|
+
const manifest = join(pluginDir, "package.json");
|
|
233
|
+
// The marker lives outside the plugin directory so the uninstall case still
|
|
234
|
+
// proves the script never ran: a `touch` into a deleted tree would fail on
|
|
235
|
+
// its own and hide a guard that let the row through.
|
|
236
|
+
const marker = join(getWorkspacePluginsDir(), "ran.txt");
|
|
237
|
+
|
|
238
|
+
// The gate probes the declaration's on-disk presence and its plugin's
|
|
239
|
+
// manifest, so the healthy fixture is a full plugin: a valid package.json
|
|
240
|
+
// and a schedules/<name>/ declaration directory.
|
|
241
|
+
beforeEach(() => {
|
|
242
|
+
rmSync(pluginDir, { recursive: true, force: true });
|
|
243
|
+
rmSync(marker, { force: true });
|
|
244
|
+
mkdirSync(declarationDir, { recursive: true });
|
|
245
|
+
writeFileSync(
|
|
246
|
+
join(declarationDir, "config.json"),
|
|
247
|
+
JSON.stringify({ expression: "* * * * *" }),
|
|
248
|
+
);
|
|
249
|
+
writeFileSync(join(declarationDir, "index.sh"), "#!/bin/sh\ntrue\n");
|
|
250
|
+
writeFileSync(
|
|
251
|
+
manifest,
|
|
252
|
+
JSON.stringify({ name: PLUGIN_NAME, version: "1.0.0" }),
|
|
253
|
+
);
|
|
254
|
+
// The feature ships off, so the healthy-plugin cases below need it on.
|
|
255
|
+
setOverridesForTesting({ "plugin-schedules": true });
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
function skipRunsFor(jobId: string): Array<{
|
|
259
|
+
status: string;
|
|
260
|
+
error: string | null;
|
|
261
|
+
}> {
|
|
262
|
+
return rawDb()
|
|
263
|
+
.query("SELECT status, error FROM cron_runs WHERE job_id = ?")
|
|
264
|
+
.all(jobId) as Array<{ status: string; error: string | null }>;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** A due plugin-sourced script row whose script leaves a marker when it runs. */
|
|
268
|
+
async function seedDueSourcedScript() {
|
|
269
|
+
const job = await upsertDeclaredSchedule(SOURCE_KEY, {
|
|
270
|
+
name: "Daily digest",
|
|
271
|
+
syntax: "cron",
|
|
272
|
+
expression: "* * * * *",
|
|
273
|
+
timezone: null,
|
|
274
|
+
message: "",
|
|
275
|
+
script: `touch ${marker}`,
|
|
276
|
+
mode: "script",
|
|
277
|
+
inferenceProfile: null,
|
|
278
|
+
timeoutMs: null,
|
|
279
|
+
enabled: true,
|
|
280
|
+
definitionHash: "hash-1",
|
|
281
|
+
});
|
|
282
|
+
rawDb().run("UPDATE cron_jobs SET next_run_at = ? WHERE id = ?", [
|
|
283
|
+
Date.now() - 1000,
|
|
284
|
+
job.id,
|
|
285
|
+
]);
|
|
286
|
+
return job;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
test("does not execute a due sourced row whose plugin is disabled", async () => {
|
|
290
|
+
const job = await seedDueSourcedScript();
|
|
291
|
+
writeFileSync(join(pluginDir, ".disabled"), "");
|
|
292
|
+
|
|
293
|
+
const result = await runDueSchedulesOnce();
|
|
294
|
+
|
|
295
|
+
expect(result.claimed).toBe(1);
|
|
296
|
+
expect(result.skipped).toBe(1);
|
|
297
|
+
expect(result.completed).toBe(0);
|
|
298
|
+
expect(existsSync(marker)).toBe(false);
|
|
299
|
+
// The skip is recorded so it stays visible in the schedule's run history.
|
|
300
|
+
const runs = skipRunsFor(job.id);
|
|
301
|
+
expect(runs).toHaveLength(1);
|
|
302
|
+
expect(runs[0].status).toBe("error");
|
|
303
|
+
expect(runs[0].error).toContain("no longer declares this schedule");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("does not execute a due sourced row whose plugin directory is gone", async () => {
|
|
307
|
+
const job = await seedDueSourcedScript();
|
|
308
|
+
// A local CLI uninstall removes the directory outright, with no daemon
|
|
309
|
+
// call to disarm the row first.
|
|
310
|
+
rmSync(pluginDir, { recursive: true, force: true });
|
|
311
|
+
|
|
312
|
+
const result = await runDueSchedulesOnce();
|
|
313
|
+
|
|
314
|
+
expect(result.claimed).toBe(1);
|
|
315
|
+
expect(result.skipped).toBe(1);
|
|
316
|
+
expect(result.completed).toBe(0);
|
|
317
|
+
expect(existsSync(marker)).toBe(false);
|
|
318
|
+
expect(skipRunsFor(job.id)).toHaveLength(1);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("does not execute a due sourced row whose manifest no longer parses", async () => {
|
|
322
|
+
const job = await seedDueSourcedScript();
|
|
323
|
+
writeFileSync(manifest, "{ broken");
|
|
324
|
+
|
|
325
|
+
const result = await runDueSchedulesOnce();
|
|
326
|
+
|
|
327
|
+
expect(result.claimed).toBe(1);
|
|
328
|
+
expect(result.skipped).toBe(1);
|
|
329
|
+
expect(result.completed).toBe(0);
|
|
330
|
+
expect(existsSync(marker)).toBe(false);
|
|
331
|
+
expect(skipRunsFor(job.id)).toHaveLength(1);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test("does not execute a due sourced row whose declaration is gone", async () => {
|
|
335
|
+
const job = await seedDueSourcedScript();
|
|
336
|
+
rmSync(declarationDir, { recursive: true, force: true });
|
|
337
|
+
|
|
338
|
+
const result = await runDueSchedulesOnce();
|
|
339
|
+
|
|
340
|
+
expect(result.skipped).toBe(1);
|
|
341
|
+
expect(existsSync(marker)).toBe(false);
|
|
342
|
+
expect(skipRunsFor(job.id)).toHaveLength(1);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("does not execute a due sourced row while the feature flag is off", async () => {
|
|
346
|
+
const job = await seedDueSourcedScript();
|
|
347
|
+
setOverridesForTesting({ "plugin-schedules": false });
|
|
348
|
+
|
|
349
|
+
const result = await runDueSchedulesOnce();
|
|
350
|
+
|
|
351
|
+
expect(result.claimed).toBe(1);
|
|
352
|
+
expect(result.skipped).toBe(1);
|
|
353
|
+
expect(result.completed).toBe(0);
|
|
354
|
+
expect(existsSync(marker)).toBe(false);
|
|
355
|
+
const runs = skipRunsFor(job.id);
|
|
356
|
+
expect(runs).toHaveLength(1);
|
|
357
|
+
expect(runs[0].status).toBe("error");
|
|
358
|
+
expect(runs[0].error).toContain("no longer declares this schedule");
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("a skip spends no retry budget and keeps the row on its normal cadence", async () => {
|
|
362
|
+
const job = await seedDueSourcedScript();
|
|
363
|
+
// A non-zero starting budget proves the counter is left alone rather than
|
|
364
|
+
// merely still reading zero.
|
|
365
|
+
rawDb().run("UPDATE cron_jobs SET retry_count = 2 WHERE id = ?", [job.id]);
|
|
366
|
+
writeFileSync(join(pluginDir, ".disabled"), "");
|
|
367
|
+
|
|
368
|
+
await runDueSchedulesOnce();
|
|
369
|
+
|
|
370
|
+
const row = rawDb()
|
|
371
|
+
.query("SELECT retry_count, next_run_at FROM cron_jobs WHERE id = ?")
|
|
372
|
+
.get(job.id) as { retry_count: number; next_run_at: number };
|
|
373
|
+
expect(row.retry_count).toBe(2);
|
|
374
|
+
// The next occurrence of `* * * * *` is at most a minute out. A retry at
|
|
375
|
+
// this budget would have pushed it minutes further on backoff.
|
|
376
|
+
expect(row.next_run_at).toBeGreaterThan(Date.now());
|
|
377
|
+
expect(row.next_run_at).toBeLessThanOrEqual(Date.now() + 61_000);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test("executes a due sourced row whose plugin is enabled", async () => {
|
|
381
|
+
const job = await seedDueSourcedScript();
|
|
382
|
+
|
|
383
|
+
const result = await runDueSchedulesOnce();
|
|
384
|
+
|
|
385
|
+
expect(result.completed).toBe(1);
|
|
386
|
+
expect(existsSync(marker)).toBe(true);
|
|
387
|
+
const runs = rawDb()
|
|
388
|
+
.query("SELECT status FROM cron_runs WHERE job_id = ?")
|
|
389
|
+
.all(job.id) as Array<{ status: string }>;
|
|
390
|
+
expect(runs).toHaveLength(1);
|
|
391
|
+
expect(runs[0].status).toBe("ok");
|
|
392
|
+
});
|
|
393
|
+
});
|
|
@@ -97,7 +97,7 @@ describe("Runtime attachment metadata", () => {
|
|
|
97
97
|
);
|
|
98
98
|
|
|
99
99
|
// Upload and link an attachment using "self" as the assistantId
|
|
100
|
-
const stored = uploadAttachment("chart.png", "image/png", "iVBORw==");
|
|
100
|
+
const stored = await uploadAttachment("chart.png", "image/png", "iVBORw==");
|
|
101
101
|
linkAttachmentToMessage(assistantMsg.id, stored.id, 0);
|
|
102
102
|
|
|
103
103
|
const res = await fetch(
|
|
@@ -162,7 +162,7 @@ describe("Runtime attachment metadata", () => {
|
|
|
162
162
|
});
|
|
163
163
|
|
|
164
164
|
test("GET /attachments/:id returns attachment with payload", async () => {
|
|
165
|
-
const stored = uploadAttachment(
|
|
165
|
+
const stored = await uploadAttachment(
|
|
166
166
|
"report.pdf",
|
|
167
167
|
"application/pdf",
|
|
168
168
|
"JVBERA==",
|
|
@@ -191,7 +191,11 @@ describe("Runtime attachment metadata", () => {
|
|
|
191
191
|
});
|
|
192
192
|
|
|
193
193
|
test('GET /attachments/:id returns attachment stored under "self"', async () => {
|
|
194
|
-
const stored = uploadAttachment(
|
|
194
|
+
const stored = await uploadAttachment(
|
|
195
|
+
"shared.txt",
|
|
196
|
+
"text/plain",
|
|
197
|
+
"c2hhcmVk",
|
|
198
|
+
);
|
|
195
199
|
|
|
196
200
|
const res = await fetch(
|
|
197
201
|
`http://127.0.0.1:${port}/v1/attachments/${stored.id}`,
|
|
@@ -303,12 +307,16 @@ describe("WhatsApp channel ingress attachment resolution", () => {
|
|
|
303
307
|
test("inbound handler accepts request with valid gateway-uploaded attachment IDs", async () => {
|
|
304
308
|
// Simulate what the gateway does: upload attachments then forward the
|
|
305
309
|
// inbound event with attachmentIds. The handler must resolve them.
|
|
306
|
-
const img = uploadAttachment(
|
|
310
|
+
const img = await uploadAttachment(
|
|
307
311
|
"whatsapp-photo.jpg",
|
|
308
312
|
"image/jpeg",
|
|
309
313
|
"/9j/4AAQ",
|
|
310
314
|
);
|
|
311
|
-
const doc = uploadAttachment(
|
|
315
|
+
const doc = await uploadAttachment(
|
|
316
|
+
"receipt.pdf",
|
|
317
|
+
"application/pdf",
|
|
318
|
+
"JVBERi0x",
|
|
319
|
+
);
|
|
312
320
|
|
|
313
321
|
const res = await fetch(
|
|
314
322
|
`http://127.0.0.1:${ingressPort}/v1/channels/inbound`,
|
|
@@ -329,7 +337,7 @@ describe("WhatsApp channel ingress attachment resolution", () => {
|
|
|
329
337
|
test("inbound handler rejects request when some attachment IDs are missing", async () => {
|
|
330
338
|
// When the gateway fails to upload one attachment, the handler detects
|
|
331
339
|
// the missing ID and returns a 400.
|
|
332
|
-
const valid = uploadAttachment("ok.jpg", "image/jpeg", "base64ok");
|
|
340
|
+
const valid = await uploadAttachment("ok.jpg", "image/jpeg", "base64ok");
|
|
333
341
|
|
|
334
342
|
const res = await fetch(
|
|
335
343
|
`http://127.0.0.1:${ingressPort}/v1/channels/inbound`,
|
|
@@ -355,7 +363,7 @@ describe("WhatsApp channel ingress attachment resolution", () => {
|
|
|
355
363
|
|
|
356
364
|
test("inbound handler accepts attachment-only message with no text content", async () => {
|
|
357
365
|
// WhatsApp allows sending images/documents without caption text.
|
|
358
|
-
const img = uploadAttachment("photo.jpg", "image/jpeg", "/9j/4AAQ");
|
|
366
|
+
const img = await uploadAttachment("photo.jpg", "image/jpeg", "/9j/4AAQ");
|
|
359
367
|
|
|
360
368
|
const res = await fetch(
|
|
361
369
|
`http://127.0.0.1:${ingressPort}/v1/channels/inbound`,
|