@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
|
@@ -88,8 +88,8 @@ describe("attachmentsToReferenceBlocks", () => {
|
|
|
88
88
|
// ---------------------------------------------------------------------------
|
|
89
89
|
|
|
90
90
|
describe("attachmentsToContentBlocks", () => {
|
|
91
|
-
test("creates image content block for image/jpeg", () => {
|
|
92
|
-
const blocks = attachmentsToContentBlocks([
|
|
91
|
+
test("creates image content block for image/jpeg", async () => {
|
|
92
|
+
const blocks = await attachmentsToContentBlocks([
|
|
93
93
|
{ filename: "photo.jpg", mimeType: "image/jpeg", data: "base64data" },
|
|
94
94
|
]);
|
|
95
95
|
|
|
@@ -104,8 +104,8 @@ describe("attachmentsToContentBlocks", () => {
|
|
|
104
104
|
expect(block.source.data).toBe("base64data");
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
test("creates image content block for image/png", () => {
|
|
108
|
-
const blocks = attachmentsToContentBlocks([
|
|
107
|
+
test("creates image content block for image/png", async () => {
|
|
108
|
+
const blocks = await attachmentsToContentBlocks([
|
|
109
109
|
{ filename: "screenshot.png", mimeType: "image/png", data: "pngdata" },
|
|
110
110
|
]);
|
|
111
111
|
|
|
@@ -113,8 +113,8 @@ describe("attachmentsToContentBlocks", () => {
|
|
|
113
113
|
expect(blocks[0].type).toBe("image");
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
test("creates image content block for image/webp", () => {
|
|
117
|
-
const blocks = attachmentsToContentBlocks([
|
|
116
|
+
test("creates image content block for image/webp", async () => {
|
|
117
|
+
const blocks = await attachmentsToContentBlocks([
|
|
118
118
|
{ filename: "sticker.webp", mimeType: "image/webp", data: "webpdata" },
|
|
119
119
|
]);
|
|
120
120
|
|
|
@@ -122,8 +122,8 @@ describe("attachmentsToContentBlocks", () => {
|
|
|
122
122
|
expect(blocks[0].type).toBe("image");
|
|
123
123
|
});
|
|
124
124
|
|
|
125
|
-
test("creates file content block for non-image mime types", () => {
|
|
126
|
-
const blocks = attachmentsToContentBlocks([
|
|
125
|
+
test("creates file content block for non-image mime types", async () => {
|
|
126
|
+
const blocks = await attachmentsToContentBlocks([
|
|
127
127
|
{ filename: "doc.pdf", mimeType: "application/pdf", data: "pdfdata" },
|
|
128
128
|
]);
|
|
129
129
|
|
|
@@ -137,8 +137,8 @@ describe("attachmentsToContentBlocks", () => {
|
|
|
137
137
|
expect(block.source.media_type).toBe("application/pdf");
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
test("handles multiple attachments including mixed types", () => {
|
|
141
|
-
const blocks = attachmentsToContentBlocks([
|
|
140
|
+
test("handles multiple attachments including mixed types", async () => {
|
|
141
|
+
const blocks = await attachmentsToContentBlocks([
|
|
142
142
|
{ filename: "photo.jpg", mimeType: "image/jpeg", data: "imgdata" },
|
|
143
143
|
{ filename: "notes.txt", mimeType: "text/plain", data: "txtdata" },
|
|
144
144
|
]);
|
|
@@ -148,8 +148,8 @@ describe("attachmentsToContentBlocks", () => {
|
|
|
148
148
|
expect(blocks[1].type).toBe("file");
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
-
test("returns empty array for no attachments", () => {
|
|
152
|
-
const blocks = attachmentsToContentBlocks([]);
|
|
151
|
+
test("returns empty array for no attachments", async () => {
|
|
152
|
+
const blocks = await attachmentsToContentBlocks([]);
|
|
153
153
|
expect(blocks).toHaveLength(0);
|
|
154
154
|
});
|
|
155
155
|
});
|
|
@@ -159,8 +159,8 @@ describe("attachmentsToContentBlocks", () => {
|
|
|
159
159
|
// ---------------------------------------------------------------------------
|
|
160
160
|
|
|
161
161
|
describe("createUserMessage with image attachments", () => {
|
|
162
|
-
test("includes both text and image blocks", () => {
|
|
163
|
-
const msg = createUserMessage("what is this?", [
|
|
162
|
+
test("includes both text and image blocks", async () => {
|
|
163
|
+
const msg = await createUserMessage("what is this?", [
|
|
164
164
|
{ filename: "photo.jpg", mimeType: "image/jpeg", data: "base64img" },
|
|
165
165
|
]);
|
|
166
166
|
|
|
@@ -173,8 +173,8 @@ describe("createUserMessage with image attachments", () => {
|
|
|
173
173
|
expect(msg.content[1].type).toBe("image");
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
-
test("includes only image block when text is empty", () => {
|
|
177
|
-
const msg = createUserMessage("", [
|
|
176
|
+
test("includes only image block when text is empty", async () => {
|
|
177
|
+
const msg = await createUserMessage("", [
|
|
178
178
|
{ filename: "photo.jpg", mimeType: "image/jpeg", data: "base64img" },
|
|
179
179
|
]);
|
|
180
180
|
|
|
@@ -183,8 +183,8 @@ describe("createUserMessage with image attachments", () => {
|
|
|
183
183
|
expect(msg.content[0].type).toBe("image");
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
-
test("includes only image block when text is whitespace", () => {
|
|
187
|
-
const msg = createUserMessage(" ", [
|
|
186
|
+
test("includes only image block when text is whitespace", async () => {
|
|
187
|
+
const msg = await createUserMessage(" ", [
|
|
188
188
|
{ filename: "photo.jpg", mimeType: "image/jpeg", data: "base64img" },
|
|
189
189
|
]);
|
|
190
190
|
|
|
@@ -193,8 +193,8 @@ describe("createUserMessage with image attachments", () => {
|
|
|
193
193
|
expect(msg.content[0].type).toBe("image");
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
-
test("includes multiple image blocks", () => {
|
|
197
|
-
const msg = createUserMessage("compare these", [
|
|
196
|
+
test("includes multiple image blocks", async () => {
|
|
197
|
+
const msg = await createUserMessage("compare these", [
|
|
198
198
|
{ filename: "a.jpg", mimeType: "image/jpeg", data: "img1" },
|
|
199
199
|
{ filename: "b.png", mimeType: "image/png", data: "img2" },
|
|
200
200
|
]);
|
|
@@ -206,9 +206,9 @@ describe("createUserMessage with image attachments", () => {
|
|
|
206
206
|
expect(msg.content[2].type).toBe("image");
|
|
207
207
|
});
|
|
208
208
|
|
|
209
|
-
test("preserves base64 data in image content block", () => {
|
|
209
|
+
test("preserves base64 data in image content block", async () => {
|
|
210
210
|
const base64 = "dGVzdC1pbWFnZS1kYXRh";
|
|
211
|
-
const msg = createUserMessage("test", [
|
|
211
|
+
const msg = await createUserMessage("test", [
|
|
212
212
|
{ filename: "photo.jpg", mimeType: "image/jpeg", data: base64 },
|
|
213
213
|
]);
|
|
214
214
|
|
|
@@ -227,7 +227,7 @@ describe("createUserMessage with image attachments", () => {
|
|
|
227
227
|
// ---------------------------------------------------------------------------
|
|
228
228
|
|
|
229
229
|
describe("enrichMessageWithSourcePaths", () => {
|
|
230
|
-
test("appends a source path annotation for images with filePath", () => {
|
|
230
|
+
test("appends a source path annotation for images with filePath", async () => {
|
|
231
231
|
const attachments = [
|
|
232
232
|
{
|
|
233
233
|
filename: "photo.jpg",
|
|
@@ -236,7 +236,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
236
236
|
filePath: "/Users/me/Desktop/photo.jpg",
|
|
237
237
|
},
|
|
238
238
|
];
|
|
239
|
-
const original = createUserMessage("what is this?", attachments);
|
|
239
|
+
const original = await createUserMessage("what is this?", attachments);
|
|
240
240
|
const enriched = enrichMessageWithSourcePaths(original, attachments);
|
|
241
241
|
|
|
242
242
|
expect(enriched).not.toBe(original);
|
|
@@ -249,7 +249,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
249
249
|
);
|
|
250
250
|
});
|
|
251
251
|
|
|
252
|
-
test("returns the original message (same reference) when no images have filePath", () => {
|
|
252
|
+
test("returns the original message (same reference) when no images have filePath", async () => {
|
|
253
253
|
const attachments = [
|
|
254
254
|
{
|
|
255
255
|
filename: "photo.jpg",
|
|
@@ -257,13 +257,13 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
257
257
|
data: "base64img",
|
|
258
258
|
},
|
|
259
259
|
];
|
|
260
|
-
const original = createUserMessage("what is this?", attachments);
|
|
260
|
+
const original = await createUserMessage("what is this?", attachments);
|
|
261
261
|
const result = enrichMessageWithSourcePaths(original, attachments);
|
|
262
262
|
|
|
263
263
|
expect(result).toBe(original);
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
-
test("skips non-image attachments with filePath", () => {
|
|
266
|
+
test("skips non-image attachments with filePath", async () => {
|
|
267
267
|
const attachments = [
|
|
268
268
|
{
|
|
269
269
|
filename: "doc.pdf",
|
|
@@ -272,14 +272,14 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
272
272
|
filePath: "/Users/me/Documents/doc.pdf",
|
|
273
273
|
},
|
|
274
274
|
];
|
|
275
|
-
const original = createUserMessage("review this", attachments);
|
|
275
|
+
const original = await createUserMessage("review this", attachments);
|
|
276
276
|
const result = enrichMessageWithSourcePaths(original, attachments);
|
|
277
277
|
|
|
278
278
|
// Non-image attachments are not annotated, so we get back the same ref
|
|
279
279
|
expect(result).toBe(original);
|
|
280
280
|
});
|
|
281
281
|
|
|
282
|
-
test("handles multiple images with file paths", () => {
|
|
282
|
+
test("handles multiple images with file paths", async () => {
|
|
283
283
|
const attachments = [
|
|
284
284
|
{
|
|
285
285
|
filename: "a.jpg",
|
|
@@ -294,7 +294,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
294
294
|
filePath: "/path/to/b.png",
|
|
295
295
|
},
|
|
296
296
|
];
|
|
297
|
-
const original = createUserMessage("compare", attachments);
|
|
297
|
+
const original = await createUserMessage("compare", attachments);
|
|
298
298
|
const enriched = enrichMessageWithSourcePaths(original, attachments);
|
|
299
299
|
|
|
300
300
|
expect(enriched).not.toBe(original);
|
|
@@ -307,7 +307,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
307
307
|
);
|
|
308
308
|
});
|
|
309
309
|
|
|
310
|
-
test("only annotates images that have filePath, skips those without", () => {
|
|
310
|
+
test("only annotates images that have filePath, skips those without", async () => {
|
|
311
311
|
const attachments = [
|
|
312
312
|
{
|
|
313
313
|
filename: "a.jpg",
|
|
@@ -322,7 +322,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
322
322
|
// no filePath — e.g. pasted screenshot
|
|
323
323
|
},
|
|
324
324
|
];
|
|
325
|
-
const original = createUserMessage("compare", attachments);
|
|
325
|
+
const original = await createUserMessage("compare", attachments);
|
|
326
326
|
const enriched = enrichMessageWithSourcePaths(original, attachments);
|
|
327
327
|
|
|
328
328
|
expect(enriched).not.toBe(original);
|
|
@@ -332,7 +332,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
332
332
|
expect(annotation.text).toBe("[Attached image source: /path/to/a.jpg]");
|
|
333
333
|
});
|
|
334
334
|
|
|
335
|
-
test("annotates non-image attachments that have storedPath", () => {
|
|
335
|
+
test("annotates non-image attachments that have storedPath", async () => {
|
|
336
336
|
const attachments = [
|
|
337
337
|
{
|
|
338
338
|
filename: "report.pdf",
|
|
@@ -342,7 +342,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
342
342
|
storedPath: "/conv/attachments/report-2.pdf",
|
|
343
343
|
},
|
|
344
344
|
];
|
|
345
|
-
const original = createUserMessage("review my edits", attachments);
|
|
345
|
+
const original = await createUserMessage("review my edits", attachments);
|
|
346
346
|
const enriched = enrichMessageWithSourcePaths(original, attachments);
|
|
347
347
|
|
|
348
348
|
expect(enriched).not.toBe(original);
|
|
@@ -355,7 +355,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
355
355
|
);
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
-
test("emits image source lines before stored path lines in one block", () => {
|
|
358
|
+
test("emits image source lines before stored path lines in one block", async () => {
|
|
359
359
|
const attachments = [
|
|
360
360
|
{
|
|
361
361
|
filename: "data.csv",
|
|
@@ -371,7 +371,7 @@ describe("enrichMessageWithSourcePaths", () => {
|
|
|
371
371
|
storedPath: "/conv/attachments/photo.jpg",
|
|
372
372
|
},
|
|
373
373
|
];
|
|
374
|
-
const original = createUserMessage("compare", attachments);
|
|
374
|
+
const original = await createUserMessage("compare", attachments);
|
|
375
375
|
const enriched = enrichMessageWithSourcePaths(original, attachments);
|
|
376
376
|
|
|
377
377
|
const annotation = enriched.content.at(-1) as {
|
|
@@ -61,7 +61,7 @@ mock.module("../persistence/conversation-crud.js", () => ({
|
|
|
61
61
|
getConversationPersistedSeq: () => null,
|
|
62
62
|
addMessage: mock(() => ({ id: "msg-1" })),
|
|
63
63
|
archiveConversation: mock(() => true),
|
|
64
|
-
|
|
64
|
+
batchSetConversationPlacement: mock(() => {}),
|
|
65
65
|
createConversation: (opts: { conversationType: string }) => {
|
|
66
66
|
createdConversations.push(opts);
|
|
67
67
|
return { id: "conv-1", ...opts };
|
|
@@ -40,10 +40,24 @@ const realGatewaySessionsModule = {
|
|
|
40
40
|
};
|
|
41
41
|
mock.module("../channels/gateway-verification-sessions.js", () => ({
|
|
42
42
|
...realGatewaySessionsModule,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
// Honours the caller's filter: the guard asks for the number it is about
|
|
44
|
+
// to dial, so a session for a different number must read as absent.
|
|
45
|
+
findActiveSession: async (
|
|
46
|
+
channel: string,
|
|
47
|
+
filter?: { expectedExternalUserId?: string },
|
|
48
|
+
) => {
|
|
49
|
+
if (!findActiveSessionMockActive) {
|
|
50
|
+
return realGatewaySessionsModule.findActiveSession(channel, filter);
|
|
51
|
+
}
|
|
52
|
+
const wanted = filter?.expectedExternalUserId;
|
|
53
|
+
if (
|
|
54
|
+
wanted !== undefined &&
|
|
55
|
+
activeVoiceSession?.expectedPhoneE164 !== wanted
|
|
56
|
+
) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return activeVoiceSession;
|
|
60
|
+
},
|
|
47
61
|
}));
|
|
48
62
|
|
|
49
63
|
const { executeCallStart } = await import("../tools/calls/call-start.js");
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canned-reply routes (canned first greeting, unknown slash command) hold
|
|
3
|
+
* the processing lock themselves and release it from a deferred callback that
|
|
4
|
+
* also publishes their client event burst. That release is the only one those
|
|
5
|
+
* turns get: no agent loop runs, so a throw escaping the burst would latch the
|
|
6
|
+
* conversation "processing" for the life of the daemon and queue every later
|
|
7
|
+
* send behind an idle conversation.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
|
|
12
|
+
import { scheduleCannedReplyRelease } from "../runtime/routes/canned-reply-release.js";
|
|
13
|
+
|
|
14
|
+
function makeConversation() {
|
|
15
|
+
let processing = true;
|
|
16
|
+
const drainOrigins: string[] = [];
|
|
17
|
+
return {
|
|
18
|
+
isProcessing: () => processing,
|
|
19
|
+
drainOrigins,
|
|
20
|
+
target: {
|
|
21
|
+
setProcessing: (value: boolean) => {
|
|
22
|
+
processing = value;
|
|
23
|
+
},
|
|
24
|
+
kickDrainQueue: (_reason?: QueueDrainReason, origin?: string) => {
|
|
25
|
+
drainOrigins.push(origin ?? "");
|
|
26
|
+
return Promise.resolve();
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Let the scheduled `setTimeout(..., 0)` callback run. */
|
|
33
|
+
function nextTick(): Promise<void> {
|
|
34
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("scheduleCannedReplyRelease", () => {
|
|
38
|
+
test("releases and drains after the deferred event burst", async () => {
|
|
39
|
+
const conversation = makeConversation();
|
|
40
|
+
const order: string[] = [];
|
|
41
|
+
|
|
42
|
+
scheduleCannedReplyRelease({
|
|
43
|
+
conversation: {
|
|
44
|
+
setProcessing: (value: boolean) => {
|
|
45
|
+
order.push("release");
|
|
46
|
+
conversation.target.setProcessing(value);
|
|
47
|
+
},
|
|
48
|
+
kickDrainQueue: conversation.target.kickDrainQueue,
|
|
49
|
+
},
|
|
50
|
+
origin: "canned_greeting",
|
|
51
|
+
emit: () => {
|
|
52
|
+
order.push("emit");
|
|
53
|
+
},
|
|
54
|
+
afterRelease: () => {
|
|
55
|
+
order.push("after");
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Nothing runs synchronously: the HTTP response has to reach the client
|
|
60
|
+
// before its events do.
|
|
61
|
+
expect(order).toEqual([]);
|
|
62
|
+
|
|
63
|
+
await nextTick();
|
|
64
|
+
|
|
65
|
+
expect(order).toEqual(["emit", "release", "after"]);
|
|
66
|
+
expect(conversation.isProcessing()).toBe(false);
|
|
67
|
+
expect(conversation.drainOrigins).toEqual(["canned_greeting"]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("still releases and drains when a broadcast throws", async () => {
|
|
71
|
+
const conversation = makeConversation();
|
|
72
|
+
|
|
73
|
+
scheduleCannedReplyRelease({
|
|
74
|
+
conversation: conversation.target,
|
|
75
|
+
origin: "slash_command",
|
|
76
|
+
emit: () => {
|
|
77
|
+
throw new Error("broadcast exploded");
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await nextTick();
|
|
82
|
+
|
|
83
|
+
expect(conversation.isProcessing()).toBe(false);
|
|
84
|
+
expect(conversation.drainOrigins).toEqual(["slash_command"]);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -137,7 +137,7 @@ import {
|
|
|
137
137
|
startOutbound,
|
|
138
138
|
} from "../runtime/verification-outbound-actions.js";
|
|
139
139
|
import {
|
|
140
|
-
|
|
140
|
+
composeVerificationText,
|
|
141
141
|
GUARDIAN_VERIFY_TEMPLATE_KEYS,
|
|
142
142
|
} from "../runtime/verification-templates.js";
|
|
143
143
|
import { resetDbForTesting } from "./db-test-helpers.js";
|
|
@@ -1640,32 +1640,42 @@ describe("outbound verification sessions", () => {
|
|
|
1640
1640
|
|
|
1641
1641
|
// ── Auto-revoke of prior sessions ──
|
|
1642
1642
|
|
|
1643
|
-
test("
|
|
1643
|
+
test("a second code for the same number revokes the first", async () => {
|
|
1644
1644
|
const { sessionId: firstId } = await createOutboundSession({
|
|
1645
1645
|
channel: "phone",
|
|
1646
|
-
expectedPhoneE164: "+
|
|
1647
|
-
destinationAddress: "+
|
|
1646
|
+
expectedPhoneE164: "+15555550101",
|
|
1647
|
+
destinationAddress: "+15555550101",
|
|
1648
|
+
});
|
|
1649
|
+
const { sessionId: secondId } = await createOutboundSession({
|
|
1650
|
+
channel: "phone",
|
|
1651
|
+
expectedPhoneE164: "+15555550101",
|
|
1652
|
+
destinationAddress: "+15555550101",
|
|
1648
1653
|
});
|
|
1649
1654
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1655
|
+
// Read by id rather than through the unfiltered lookup: two live
|
|
1656
|
+
// sessions minted in the same millisecond have no defined order, and
|
|
1657
|
+
// what matters here is each row's status, not which one is "latest".
|
|
1658
|
+
expect(getSessionById(firstId)!.status).toBe("revoked");
|
|
1659
|
+
expect(getSessionById(secondId)!.status).toBe("awaiting_response");
|
|
1660
|
+
});
|
|
1653
1661
|
|
|
1654
|
-
|
|
1662
|
+
test("a code for a different number leaves the first alone", async () => {
|
|
1663
|
+
// Two numbers are two people. Verifying one must not silently kill the
|
|
1664
|
+
// other's code, which they would learn about only by trying to use it
|
|
1665
|
+
// and being told it is "invalid or has expired".
|
|
1666
|
+
const { sessionId: firstId } = await createOutboundSession({
|
|
1667
|
+
channel: "phone",
|
|
1668
|
+
expectedPhoneE164: "+15555550101",
|
|
1669
|
+
destinationAddress: "+15555550101",
|
|
1670
|
+
});
|
|
1655
1671
|
const { sessionId: secondId } = await createOutboundSession({
|
|
1656
1672
|
channel: "phone",
|
|
1657
|
-
expectedPhoneE164: "+
|
|
1658
|
-
destinationAddress: "+
|
|
1673
|
+
expectedPhoneE164: "+15555550102",
|
|
1674
|
+
destinationAddress: "+15555550102",
|
|
1659
1675
|
});
|
|
1660
1676
|
|
|
1661
|
-
|
|
1662
|
-
expect(
|
|
1663
|
-
expect(second!.id).toBe(secondId);
|
|
1664
|
-
|
|
1665
|
-
// First session should no longer be findable as active
|
|
1666
|
-
const firstRow = getSessionById(firstId);
|
|
1667
|
-
expect(firstRow).not.toBeNull();
|
|
1668
|
-
expect(firstRow!.status).toBe("revoked");
|
|
1677
|
+
expect(getSessionById(firstId)!.status).toBe("awaiting_response");
|
|
1678
|
+
expect(getSessionById(secondId)!.status).toBe("awaiting_response");
|
|
1669
1679
|
});
|
|
1670
1680
|
|
|
1671
1681
|
// ── findActiveSession returns correct session ──
|
|
@@ -1772,26 +1782,46 @@ describe("outbound verification sessions", () => {
|
|
|
1772
1782
|
expect(result.success).toBe(true);
|
|
1773
1783
|
});
|
|
1774
1784
|
|
|
1775
|
-
// ── Voice identity
|
|
1785
|
+
// ── Voice identity is the number ──
|
|
1776
1786
|
|
|
1777
|
-
test("
|
|
1787
|
+
test("a voice session is redeemed by the number it was sent to", async () => {
|
|
1788
|
+
// Voice knows the caller by one thing, so the consume path receives that
|
|
1789
|
+
// number as both actor axes. Every voice mint records it in both
|
|
1790
|
+
// identity columns, and either way the session is bound to the number.
|
|
1778
1791
|
const { secret } = await createOutboundSession({
|
|
1779
1792
|
channel: "phone",
|
|
1780
|
-
expectedExternalUserId: "
|
|
1781
|
-
expectedPhoneE164: "+
|
|
1782
|
-
destinationAddress: "+
|
|
1793
|
+
expectedExternalUserId: "+15555550101",
|
|
1794
|
+
expectedPhoneE164: "+15555550101",
|
|
1795
|
+
destinationAddress: "+15555550101",
|
|
1783
1796
|
});
|
|
1784
1797
|
|
|
1785
|
-
// Actor matches expectedExternalUserId
|
|
1786
1798
|
const result = await validateAndConsumeVerification(
|
|
1787
1799
|
"phone",
|
|
1788
1800
|
secret,
|
|
1789
|
-
"
|
|
1790
|
-
"
|
|
1801
|
+
"+15555550101",
|
|
1802
|
+
"+15555550101",
|
|
1791
1803
|
);
|
|
1792
1804
|
|
|
1793
1805
|
expect(result.success).toBe(true);
|
|
1794
1806
|
});
|
|
1807
|
+
|
|
1808
|
+
test("a voice session refuses a caller on a different number", async () => {
|
|
1809
|
+
const { secret } = await createOutboundSession({
|
|
1810
|
+
channel: "phone",
|
|
1811
|
+
expectedExternalUserId: "+15555550101",
|
|
1812
|
+
expectedPhoneE164: "+15555550101",
|
|
1813
|
+
destinationAddress: "+15555550101",
|
|
1814
|
+
});
|
|
1815
|
+
|
|
1816
|
+
const result = await validateAndConsumeVerification(
|
|
1817
|
+
"phone",
|
|
1818
|
+
secret,
|
|
1819
|
+
"+15555550102",
|
|
1820
|
+
"+15555550102",
|
|
1821
|
+
);
|
|
1822
|
+
|
|
1823
|
+
expect(result.success).toBe(false);
|
|
1824
|
+
});
|
|
1795
1825
|
});
|
|
1796
1826
|
|
|
1797
1827
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -2344,7 +2374,7 @@ describe("outbound Telegram verification", () => {
|
|
|
2344
2374
|
});
|
|
2345
2375
|
|
|
2346
2376
|
test("telegram template does not include verification code in message", async () => {
|
|
2347
|
-
const msg =
|
|
2377
|
+
const msg = composeVerificationText(
|
|
2348
2378
|
GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST,
|
|
2349
2379
|
{ code: "abc123", expiresInMinutes: 10 },
|
|
2350
2380
|
);
|
|
@@ -2353,7 +2383,7 @@ describe("outbound Telegram verification", () => {
|
|
|
2353
2383
|
});
|
|
2354
2384
|
|
|
2355
2385
|
test("telegram resend template does not include code and includes (resent) suffix", async () => {
|
|
2356
|
-
const msg =
|
|
2386
|
+
const msg = composeVerificationText(
|
|
2357
2387
|
GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_RESEND,
|
|
2358
2388
|
{ code: "xyz789", expiresInMinutes: 5 },
|
|
2359
2389
|
);
|
|
@@ -2363,7 +2393,7 @@ describe("outbound Telegram verification", () => {
|
|
|
2363
2393
|
});
|
|
2364
2394
|
|
|
2365
2395
|
test("telegram template includes Vellum assistant prefix", async () => {
|
|
2366
|
-
const msg =
|
|
2396
|
+
const msg = composeVerificationText(
|
|
2367
2397
|
GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST,
|
|
2368
2398
|
{ code: "999999", expiresInMinutes: 10, assistantName: "MyBot" },
|
|
2369
2399
|
);
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Telegram probe's remote check.
|
|
3
|
+
*
|
|
4
|
+
* Local checks establish that credentials exist. That is a different claim
|
|
5
|
+
* from "messages arrive": a registration that never landed leaves every
|
|
6
|
+
* credential in place and the channel dark, which is how the readiness
|
|
7
|
+
* indicator went green on a channel nobody could reach.
|
|
8
|
+
*
|
|
9
|
+
* The classification lives in `checkTelegramWebhookHealth`, which already
|
|
10
|
+
* handles error recency and the managed-versus-self-hosted recovery text.
|
|
11
|
+
* These tests pin how the probe maps its verdicts across three outcomes, not
|
|
12
|
+
* two: verified pass, hard failure, and the no-evidence statuses that are
|
|
13
|
+
* neither. A probe that cannot see the answer must not report a fault, and
|
|
14
|
+
* must not be counted as proof either.
|
|
15
|
+
*/
|
|
16
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
import type { TelegramWebhookHealthResult } from "../telegram/webhook-health.js";
|
|
19
|
+
|
|
20
|
+
let health: TelegramWebhookHealthResult;
|
|
21
|
+
|
|
22
|
+
mock.module("../telegram/webhook-health.js", () => ({
|
|
23
|
+
checkTelegramWebhookHealth: async () => health,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
mock.module("../calls/twilio-rest.js", () => ({
|
|
27
|
+
hasTwilioCredentials: () => false,
|
|
28
|
+
}));
|
|
29
|
+
mock.module("../calls/twilio-config.js", () => ({
|
|
30
|
+
resolveTwilioPhoneNumber: () => undefined,
|
|
31
|
+
}));
|
|
32
|
+
mock.module("../runtime/channel-invite-transports/whatsapp.js", () => ({
|
|
33
|
+
resolveWhatsAppDisplayNumber: () => undefined,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
async function runTelegramRemoteProbe() {
|
|
37
|
+
const { createReadinessService } =
|
|
38
|
+
await import("../runtime/channel-readiness-service.js");
|
|
39
|
+
const service = createReadinessService();
|
|
40
|
+
return service.getReadiness("telegram", true);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function deliveryCheck() {
|
|
44
|
+
const [snapshot] = await runTelegramRemoteProbe();
|
|
45
|
+
const remote = snapshot.remoteChecks ?? [];
|
|
46
|
+
const result = remote.find((c) => c.name === "webhook_delivery");
|
|
47
|
+
if (!result) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`no webhook_delivery check ran; got: ${remote.map((c) => c.name).join(", ") || "(none)"}`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
health = { status: "healthy", detail: "ok" };
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
mock.restore();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("telegram remote probe (webhook delivery)", () => {
|
|
64
|
+
test("passes when Telegram reports a healthy webhook", async () => {
|
|
65
|
+
health = { status: "healthy", detail: "delivering" };
|
|
66
|
+
|
|
67
|
+
expect((await deliveryCheck()).passed).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("fails when no webhook is registered, carrying the recovery text", async () => {
|
|
71
|
+
health = {
|
|
72
|
+
status: "not_registered",
|
|
73
|
+
detail: "Telegram has no webhook registered. Contact support.",
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const result = await deliveryCheck();
|
|
77
|
+
expect(result.passed).toBe(false);
|
|
78
|
+
// The detail carries fixPath()'s managed-versus-self-hosted guidance, so
|
|
79
|
+
// the probe relays it rather than substituting wording of its own.
|
|
80
|
+
expect(result.message).toBe(health.detail);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("fails when Telegram reports a recent delivery error", async () => {
|
|
84
|
+
health = {
|
|
85
|
+
status: "delivery_failing",
|
|
86
|
+
detail: 'Telegram reported "Connection refused"',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const result = await deliveryCheck();
|
|
90
|
+
expect(result.passed).toBe(false);
|
|
91
|
+
expect(result.message).toBe(health.detail);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("fails when Telegram holds a webhook this deployment did not register", async () => {
|
|
95
|
+
// The case last_error_date cannot see: on a quiet channel a stale tunnel
|
|
96
|
+
// address or another deployment's callback never produces a delivery
|
|
97
|
+
// error, so before the URL comparison this read as healthy.
|
|
98
|
+
health = {
|
|
99
|
+
status: "url_mismatch",
|
|
100
|
+
detail: "Telegram is registered at a different address",
|
|
101
|
+
registeredUrl: "https://stale.example/webhooks/telegram",
|
|
102
|
+
expectedUrl: "https://current.example/webhooks/telegram",
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const result = await deliveryCheck();
|
|
106
|
+
expect(result.passed).toBe(false);
|
|
107
|
+
expect(result.message).toBe(health.detail);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// The three indeterminate statuses. Each must satisfy BOTH halves: no fault
|
|
111
|
+
// reported (so a network blip does not paint a working channel broken), and
|
|
112
|
+
// not counted as evidence (so nothing can claim delivery from a check that
|
|
113
|
+
// never established it). Asserting only `passed` would let a regression that
|
|
114
|
+
// drops `indeterminate` through, which is exactly the false-success this
|
|
115
|
+
// contract exists to prevent.
|
|
116
|
+
const indeterminateCases = [
|
|
117
|
+
["skipped", "no bot token stored"],
|
|
118
|
+
["unknown", "could not reach Telegram"],
|
|
119
|
+
["unverified", "no record of registering this webhook"],
|
|
120
|
+
] as const;
|
|
121
|
+
|
|
122
|
+
for (const [status, detail] of indeterminateCases) {
|
|
123
|
+
test(`${status} is indeterminate: no fault reported, but not proof of delivery`, async () => {
|
|
124
|
+
health = { status, detail };
|
|
125
|
+
|
|
126
|
+
const result = await deliveryCheck();
|
|
127
|
+
expect(result.passed).toBe(true);
|
|
128
|
+
expect(result.indeterminate).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
test("an untouched install stays not_configured rather than incomplete", async () => {
|
|
133
|
+
// setupStatus separates not_configured from incomplete by asking whether
|
|
134
|
+
// any check passed. Counting an indeterminate check there reported a
|
|
135
|
+
// workspace with no Telegram credentials at all as half-finished, which is
|
|
136
|
+
// what routes the Channels UI to the "finish setup" prompt instead of the
|
|
137
|
+
// normal setup flow.
|
|
138
|
+
health = { status: "skipped", detail: "no bot token stored" };
|
|
139
|
+
|
|
140
|
+
const [snapshot] = await runTelegramRemoteProbe();
|
|
141
|
+
expect(snapshot.setupStatus).toBe("not_configured");
|
|
142
|
+
expect(snapshot.ready).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("a verified healthy result is not marked indeterminate", async () => {
|
|
146
|
+
// Sensitivity check for the cases above: if `indeterminate` were set
|
|
147
|
+
// unconditionally they would pass while proving nothing.
|
|
148
|
+
health = { status: "healthy", detail: "registered where we set it" };
|
|
149
|
+
|
|
150
|
+
const result = await deliveryCheck();
|
|
151
|
+
expect(result.passed).toBe(true);
|
|
152
|
+
expect(result.indeterminate).toBeFalsy();
|
|
153
|
+
});
|
|
154
|
+
});
|