@vellumai/assistant 0.11.2 → 0.11.3-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @vellumai/environments
|
|
3
|
-
*
|
|
4
|
-
* blocks). Consumed by the CLI, the assistant daemon, and the local-mode
|
|
5
|
-
* host library so the environment list is defined exactly once on the TS
|
|
6
|
-
* side. The Swift client mirrors the same set in `VellumEnvironment.swift`.
|
|
2
|
+
* @vellumai/environments: the single source of truth for how a Vellum install
|
|
3
|
+
* is laid out and which deployment environment it belongs to.
|
|
7
4
|
*
|
|
8
|
-
*
|
|
5
|
+
* Two things live here:
|
|
6
|
+
*
|
|
7
|
+
* - The known deployment environments (names, platform/web URLs, per-service
|
|
8
|
+
* port blocks). The Swift client mirrors the same set in
|
|
9
|
+
* `VellumEnvironment.swift`.
|
|
10
|
+
* - Install layout: whether a runtime is a repo checkout or an installed
|
|
11
|
+
* package, and where the `assistant` command it ships lives.
|
|
12
|
+
*
|
|
13
|
+
* Consumed by the CLI, the assistant daemon, and the local-mode host library
|
|
14
|
+
* so each answer is defined exactly once on the TS side.
|
|
15
|
+
*
|
|
16
|
+
* This is the lowest layer of the shared-packages hierarchy and stays a leaf:
|
|
17
|
+
* types, constants, and small helpers over node builtins, with no runtime
|
|
18
|
+
* dependencies of its own. See `__tests__/package-boundary.test.ts`.
|
|
9
19
|
*/
|
|
10
20
|
export * from "./types.js";
|
|
11
21
|
export * from "./seeds.js";
|
|
22
|
+
export * from "./install-layout.js";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the layout a Vellum runtime is installed in.
|
|
3
|
+
*
|
|
4
|
+
* The daemon is started either from a developer's repo checkout or from an
|
|
5
|
+
* installed npm package (`<install>/node_modules/@vellumai/assistant/src/
|
|
6
|
+
* index.ts`, which is what the desktop app runs). The CLI needs the layout to
|
|
7
|
+
* decide whether a launch is a dev run and what to put on the daemon's PATH;
|
|
8
|
+
* the daemon needs it to decide what to install as the `assistant` command.
|
|
9
|
+
* Both ask the same two questions, so they ask them here.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { join, sep } from "node:path";
|
|
14
|
+
|
|
15
|
+
const NODE_MODULES_SEGMENT = `${sep}node_modules${sep}`;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whether `path` sits in a developer's repo checkout rather than inside an
|
|
19
|
+
* installed package.
|
|
20
|
+
*/
|
|
21
|
+
export function isRepoCheckoutPath(path: string): boolean {
|
|
22
|
+
return !path.includes(NODE_MODULES_SEGMENT);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The `assistant` command shipped with the install that contains `path`, or
|
|
27
|
+
* null when there is none (a repo checkout, or an install whose dependency
|
|
28
|
+
* graph omits the package declaring the bin).
|
|
29
|
+
*
|
|
30
|
+
* Searches innermost `node_modules` outward, since a nested install shadows a
|
|
31
|
+
* hoisted one.
|
|
32
|
+
*/
|
|
33
|
+
export function findAssistantCommand(path: string): string | null {
|
|
34
|
+
let index = path.lastIndexOf(NODE_MODULES_SEGMENT);
|
|
35
|
+
|
|
36
|
+
while (index !== -1) {
|
|
37
|
+
const nodeModulesDir = path.slice(
|
|
38
|
+
0,
|
|
39
|
+
index + NODE_MODULES_SEGMENT.length - 1,
|
|
40
|
+
);
|
|
41
|
+
const command = join(nodeModulesDir, ".bin", "assistant");
|
|
42
|
+
if (existsSync(command)) {
|
|
43
|
+
return command;
|
|
44
|
+
}
|
|
45
|
+
index = path.lastIndexOf(NODE_MODULES_SEGMENT, index - 1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
@@ -24,6 +24,35 @@ function portBlock(base: number): PortMap {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Base URL of the cloud-hosted assistant SPA (the "hub") for a deployment
|
|
29
|
+
* environment. The Capacitor mobile shell bakes this into native builds as
|
|
30
|
+
* `server.url`, and the CLI's remote-web edge stamps it into the served
|
|
31
|
+
* config as `hubUrl`, so the environment-to-hub mapping lives here exactly
|
|
32
|
+
* once.
|
|
33
|
+
*
|
|
34
|
+
* Only the cloud deployments (production, staging) serve the hosted SPA at
|
|
35
|
+
* their own web origin; every other environment (dev, test, local, unknown)
|
|
36
|
+
* falls back to the dev SPA, whose host serves remote clients that cannot
|
|
37
|
+
* reach a local or per-developer web URL.
|
|
38
|
+
*
|
|
39
|
+
* The `/assistant` suffix is deliberate: booting on the bare host lands on
|
|
40
|
+
* the marketing page, whose CTA redirects to `www.vellum.ai/assistant` and
|
|
41
|
+
* bounces non-prod shells off their own host.
|
|
42
|
+
*
|
|
43
|
+
* NOTE: this module is loaded by Capacitor's single-file TS config loader
|
|
44
|
+
* (via the `@vellumai/environments/seeds` subpath), which cannot follow the
|
|
45
|
+
* package index's `.js`-suffixed runtime re-exports. Keep this file's
|
|
46
|
+
* imports type-only so it stays loadable on its own.
|
|
47
|
+
*/
|
|
48
|
+
export function cloudAssistantHubUrl(envName: string | undefined): string {
|
|
49
|
+
const seed =
|
|
50
|
+
envName === "production" || envName === "staging"
|
|
51
|
+
? SEEDS[envName]
|
|
52
|
+
: SEEDS.dev;
|
|
53
|
+
return `${seed.webUrl}/assistant`;
|
|
54
|
+
}
|
|
55
|
+
|
|
27
56
|
/**
|
|
28
57
|
* Built-in environment definitions and the source of truth for the
|
|
29
58
|
* set of known environment names.
|
|
@@ -40,3 +40,42 @@ export function isChannelId(value: unknown): value is ChannelId {
|
|
|
40
40
|
(CHANNEL_IDS as readonly string[]).includes(value)
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The provider key holding each channel's bot credential.
|
|
46
|
+
*
|
|
47
|
+
* Two senses of "connected" share the provider registry: a provider the user
|
|
48
|
+
* authorized so the assistant can act **as them** (`slack`, `discord`,
|
|
49
|
+
* `google`), and a bot credential letting people reach the assistant **as
|
|
50
|
+
* itself**. Nothing in a provider key says which, and the naming actively
|
|
51
|
+
* misleads: `slack` and `discord` name the user integration while their bots
|
|
52
|
+
* take a `_channel` suffix, yet `telegram` *is* the bot, because Telegram has
|
|
53
|
+
* no user-identity integration.
|
|
54
|
+
*
|
|
55
|
+
* That irregularity is why this is stated rather than derived from the key,
|
|
56
|
+
* and it is stated here because this file already owns what a channel is.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately only the key. What fields each credential requires is declared
|
|
59
|
+
* once already, per service, in the gateway's credential specs; restating it
|
|
60
|
+
* here would be a second copy of a different fact.
|
|
61
|
+
*
|
|
62
|
+
* Channels absent from this map reach the assistant without a bot credential
|
|
63
|
+
* of their own: `phone` through the voice provider, `vellum` and `platform`
|
|
64
|
+
* internally.
|
|
65
|
+
*/
|
|
66
|
+
export const CHANNEL_BOT_PROVIDER = {
|
|
67
|
+
slack: "slack_channel",
|
|
68
|
+
discord: "discord_channel",
|
|
69
|
+
telegram: "telegram",
|
|
70
|
+
} as const satisfies Partial<Record<ChannelId, string>>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether a provider key names a bot the assistant is reached through, rather
|
|
74
|
+
* than a grant letting it act as the user. This is the "which sense of
|
|
75
|
+
* connected" question, for any provider key.
|
|
76
|
+
*/
|
|
77
|
+
export function isChannelBotProvider(providerKey: string): boolean {
|
|
78
|
+
return (Object.values(CHANNEL_BOT_PROVIDER) as readonly string[]).includes(
|
|
79
|
+
providerKey,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -20,6 +20,16 @@ export function normalizePublicBaseUrl(value: unknown): string | undefined {
|
|
|
20
20
|
return normalized.length > 0 ? normalized : undefined;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Application close code the gateway's velay bridge sends to proxied
|
|
25
|
+
* WebSockets when the tunnel itself is lost (velay disconnect, gateway
|
|
26
|
+
* shutdown). A dedicated code because the natural 1001 (going away) cannot
|
|
27
|
+
* be sent through the JS `close()` API (the bridge would remap it to a
|
|
28
|
+
* misleading 4001), and Bun's WebSocket client drops close reasons, so the
|
|
29
|
+
* code is the only signal that survives the relay to the daemon.
|
|
30
|
+
*/
|
|
31
|
+
export const GATEWAY_TUNNEL_LOST_WS_CLOSE_CODE = 4801;
|
|
32
|
+
|
|
23
33
|
export function normalizeHttpPublicBaseUrl(value: unknown): string | undefined {
|
|
24
34
|
if (typeof value !== "string") return undefined;
|
|
25
35
|
const trimmed = value.trim();
|
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
* `Date#toISOString()`.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Pairing-challenge TTL in milliseconds (10 minutes): the gateway's challenge
|
|
26
|
+
* store enforces it and the `vellum pair` CLI renders it in user-facing copy.
|
|
27
|
+
*/
|
|
28
|
+
export const REMOTE_WEB_PAIRING_CODE_TTL_MS = 10 * 60 * 1000;
|
|
29
|
+
|
|
24
30
|
/** `POST /v1/remote-web/pairing-challenge` request body. */
|
|
25
31
|
export interface RemoteWebPairingChallengeRequest {
|
|
26
32
|
/** Public https base URL the scanning device can reach the assistant at. */
|
|
@@ -157,6 +157,8 @@ export {
|
|
|
157
157
|
hashVerificationSecret,
|
|
158
158
|
IdentityBindingStatusSchema,
|
|
159
159
|
ResolveBootstrapSessionIpcParamsSchema,
|
|
160
|
+
bindsSameIdentity,
|
|
161
|
+
boundIdentity,
|
|
160
162
|
RevokePendingSessionsIpcParamsSchema,
|
|
161
163
|
SessionLookupIpcResponseSchema,
|
|
162
164
|
SessionMutationIpcResponseSchema,
|
|
@@ -172,6 +174,7 @@ export {
|
|
|
172
174
|
|
|
173
175
|
export type {
|
|
174
176
|
BindSessionIdentityIpcParams,
|
|
177
|
+
BoundIdentity,
|
|
175
178
|
CountRecentSendsIpcParams,
|
|
176
179
|
CountRecentSendsIpcResponse,
|
|
177
180
|
CreateInboundSessionIpcParams,
|
|
@@ -183,6 +186,7 @@ export type {
|
|
|
183
186
|
FindActiveSessionIpcParams,
|
|
184
187
|
GetPendingSessionIpcParams,
|
|
185
188
|
IdentityBindingStatus,
|
|
189
|
+
IdentityBoundSession,
|
|
186
190
|
ResolveBootstrapSessionIpcParams,
|
|
187
191
|
RevokePendingSessionsIpcParams,
|
|
188
192
|
SessionLookupIpcResponse,
|
|
@@ -113,6 +113,78 @@ export const VerificationSessionSchema = z.object({
|
|
|
113
113
|
|
|
114
114
|
export type VerificationSessionWire = z.infer<typeof VerificationSessionSchema>;
|
|
115
115
|
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// Bound identity (which of the three identity columns decides who may redeem)
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
/** The identity fields the rules read. Callers may pass a full session row. */
|
|
121
|
+
export interface IdentityBoundSession {
|
|
122
|
+
expectedExternalUserId?: string | null;
|
|
123
|
+
expectedChatId?: string | null;
|
|
124
|
+
expectedPhoneE164?: string | null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The identity a session redeems on.
|
|
129
|
+
*
|
|
130
|
+
* `chatId` is compared against the actor's conversation and everything else
|
|
131
|
+
* against the actor themselves, which is why the field travels with the value.
|
|
132
|
+
*/
|
|
133
|
+
export interface BoundIdentity {
|
|
134
|
+
field: "phoneE164" | "externalUserId" | "chatId";
|
|
135
|
+
value: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The one identity a session is bound to, or null when it is bound to none
|
|
140
|
+
* (an inbound challenge, or a bootstrap session before its deep link is
|
|
141
|
+
* redeemed).
|
|
142
|
+
*
|
|
143
|
+
* This lives in the contract because more than one side has to agree on it:
|
|
144
|
+
* the gateway consume path decides who may redeem a code, and the supersede
|
|
145
|
+
* scope decides whose earlier codes a fresh mint kills. Those two disagreeing
|
|
146
|
+
* is a silent one-time-code bug, so the rule is stated once.
|
|
147
|
+
*
|
|
148
|
+
* Precedence, most to least specific:
|
|
149
|
+
*
|
|
150
|
+
* 1. `expectedPhoneE164` (a voice session's subject is the number itself).
|
|
151
|
+
* 2. `expectedExternalUserId` (the person).
|
|
152
|
+
* 3. `expectedChatId` (the conversation), and only when nothing identifies a
|
|
153
|
+
* person. A chat id can be shared (a Slack channel, a Telegram group), so
|
|
154
|
+
* on its own it would let any participant satisfy the binding. It counts
|
|
155
|
+
* only where there is no better answer.
|
|
156
|
+
*/
|
|
157
|
+
export function boundIdentity(
|
|
158
|
+
session: IdentityBoundSession,
|
|
159
|
+
): BoundIdentity | null {
|
|
160
|
+
// Presence, not truthiness. An empty string is a set-but-useless identity,
|
|
161
|
+
// and treating it as absent would report the session unbound, which the
|
|
162
|
+
// consume path reads as "no identity to check" and admits anyone holding
|
|
163
|
+
// the code. Kept as a value it can compare against and fail instead.
|
|
164
|
+
if (session.expectedPhoneE164 != null) {
|
|
165
|
+
return { field: "phoneE164", value: session.expectedPhoneE164 };
|
|
166
|
+
}
|
|
167
|
+
if (session.expectedExternalUserId != null) {
|
|
168
|
+
return { field: "externalUserId", value: session.expectedExternalUserId };
|
|
169
|
+
}
|
|
170
|
+
if (session.expectedChatId != null) {
|
|
171
|
+
return { field: "chatId", value: session.expectedChatId };
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Whether two sessions are bound to the same identity. Two sessions bound to
|
|
178
|
+
* no identity are not the same: bootstrap sessions are claimed by id, so an
|
|
179
|
+
* unbound mint must not sweep up every other unbound row.
|
|
180
|
+
*/
|
|
181
|
+
export function bindsSameIdentity(
|
|
182
|
+
a: BoundIdentity | null,
|
|
183
|
+
b: BoundIdentity | null,
|
|
184
|
+
): boolean {
|
|
185
|
+
return a !== null && b !== null && a.field === b.field && a.value === b.value;
|
|
186
|
+
}
|
|
187
|
+
|
|
116
188
|
// ---------------------------------------------------------------------------
|
|
117
189
|
// IPC methods
|
|
118
190
|
// ---------------------------------------------------------------------------
|
|
@@ -193,9 +265,12 @@ export type CreateInboundSessionIpcResponse = z.infer<
|
|
|
193
265
|
/** Request for `verification_sessions_create_outbound`. */
|
|
194
266
|
export const CreateOutboundSessionIpcParamsSchema = z.object({
|
|
195
267
|
channel: z.string().min(1),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
268
|
+
// An identity field is either absent or a real value. An empty string is
|
|
269
|
+
// neither, and a session bound to one can never be redeemed by anybody,
|
|
270
|
+
// so reject it at the boundary rather than mint a dead code.
|
|
271
|
+
expectedExternalUserId: z.string().min(1).optional(),
|
|
272
|
+
expectedChatId: z.string().min(1).optional(),
|
|
273
|
+
expectedPhoneE164: z.string().min(1).optional(),
|
|
199
274
|
identityBindingStatus: IdentityBindingStatusSchema.optional(),
|
|
200
275
|
destinationAddress: z.string().optional(),
|
|
201
276
|
codeDigits: z.number().int().positive().optional(),
|
|
@@ -211,7 +286,6 @@ export const CreateOutboundSessionIpcParamsSchema = z.object({
|
|
|
211
286
|
requireSourceSessionPending: z.string().min(1).optional(),
|
|
212
287
|
// Mint only if the channel has no active (pending_bootstrap /
|
|
213
288
|
// awaiting_response, non-expired) session.
|
|
214
|
-
ifNoneActive: z.boolean().optional(),
|
|
215
289
|
// Sender-scoped variant: mint unless the channel's active session is bound
|
|
216
290
|
// to this expectedExternalUserId (a different sender may supersede).
|
|
217
291
|
ifNoneActiveForExternalUserId: z.string().min(1).optional(),
|
|
@@ -240,7 +314,8 @@ export type CreateOutboundSessionIpcResponse = z.infer<
|
|
|
240
314
|
|
|
241
315
|
/**
|
|
242
316
|
* Conflict marker returned by `verification_sessions_create_outbound` when a
|
|
243
|
-
* claim guard (`requireSourceSessionPending` /
|
|
317
|
+
* claim guard (`requireSourceSessionPending` /
|
|
318
|
+
* `ifNoneActiveForExternalUserId`) fails. Only
|
|
244
319
|
* reachable when a guard was passed — legacy callers never see this shape.
|
|
245
320
|
*/
|
|
246
321
|
export const CreateOutboundSessionConflictSchema = z.object({
|
|
@@ -277,7 +352,17 @@ export type GetPendingSessionIpcParams = z.infer<
|
|
|
277
352
|
* Request for `verification_sessions_find_active` (statuses
|
|
278
353
|
* `pending_bootstrap`/`awaiting_response`, newest first).
|
|
279
354
|
*/
|
|
280
|
-
export const FindActiveSessionIpcParamsSchema =
|
|
355
|
+
export const FindActiveSessionIpcParamsSchema =
|
|
356
|
+
ChannelOnlyIpcParamsSchema.extend({
|
|
357
|
+
/**
|
|
358
|
+
* Narrows the lookup to one session. A channel can carry several live
|
|
359
|
+
* sessions at once, one per person verifying plus the guardian's own flow,
|
|
360
|
+
* so a caller that means a particular one has to say which. Omitted, the
|
|
361
|
+
* lookup returns whoever started most recently.
|
|
362
|
+
*/
|
|
363
|
+
expectedExternalUserId: z.string().min(1).optional(),
|
|
364
|
+
verificationPurpose: VerificationPurposeSchema.optional(),
|
|
365
|
+
});
|
|
281
366
|
|
|
282
367
|
export type FindActiveSessionIpcParams = z.infer<
|
|
283
368
|
typeof FindActiveSessionIpcParamsSchema
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { removeIpcEndpointFile, resolveIpcEndpoint } from "./endpoint.js";
|
|
6
|
+
const windowsEndpoint = (name: string, workspaceDir: string) => resolveIpcEndpoint(name, { workspaceDir, platform: "win32" });
|
|
7
|
+
describe("resolveIpcEndpoint", () => {
|
|
8
|
+
test("keeps long POSIX fallbacks deterministic and bounded", () => {
|
|
9
|
+
const options = {
|
|
10
|
+
workspaceDir: `/tmp/${"a".repeat(120)}`,
|
|
11
|
+
platform: "darwin" as const,
|
|
12
|
+
};
|
|
13
|
+
const endpoint = resolveIpcEndpoint("assistant", options);
|
|
14
|
+
expect(resolveIpcEndpoint("assistant", options)).toEqual(endpoint);
|
|
15
|
+
expect(Buffer.byteLength(endpoint.path)).toBeLessThanOrEqual(103);
|
|
16
|
+
});
|
|
17
|
+
test("returns normalized, isolated, bounded Windows pipes", () => {
|
|
18
|
+
const first = windowsEndpoint("assistant", "C:\\one");
|
|
19
|
+
expect(resolveIpcEndpoint("assistant", { workspaceDir: "c:\\ONE\\", platform: "win32", env: { ASSISTANT_IPC_SOCKET_DIR: "C:\\ignored" } })).toEqual(first);
|
|
20
|
+
expect(first.path.length).toBeLessThanOrEqual(256);
|
|
21
|
+
expect(windowsEndpoint("assistant", "C:\\two").path).not.toBe(first.path);
|
|
22
|
+
expect(windowsEndpoint("gateway", "C:\\one").path).not.toBe(first.path);
|
|
23
|
+
});
|
|
24
|
+
test("cleans socket files but skips named pipes", () => {
|
|
25
|
+
const dir = mkdtempSync(join(tmpdir(), "vellum-ipc-cleanup-"));
|
|
26
|
+
const socketPath = join(dir, "assistant.sock");
|
|
27
|
+
try {
|
|
28
|
+
writeFileSync(socketPath, "stale");
|
|
29
|
+
removeIpcEndpointFile(socketPath);
|
|
30
|
+
expect(existsSync(socketPath)).toBe(false);
|
|
31
|
+
removeIpcEndpointFile("\\\\.\\pipe\\vellum-assistant-test");
|
|
32
|
+
} finally {
|
|
33
|
+
rmSync(dir, { recursive: true, force: true });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { unlinkSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { posix, win32 } from "node:path";
|
|
5
|
+
|
|
6
|
+
const DARWIN_UNIX_SOCKET_MAX_PATH_BYTES = 103;
|
|
7
|
+
const DEFAULT_UNIX_SOCKET_MAX_PATH_BYTES = 107;
|
|
8
|
+
const IPC_TMP_DIR_NAME = "vellum-ipc";
|
|
9
|
+
const join = posix.join;
|
|
10
|
+
|
|
11
|
+
export type IpcSocketPathSource =
|
|
12
|
+
| "env-override"
|
|
13
|
+
| "workspace"
|
|
14
|
+
| "tmp-hash"
|
|
15
|
+
| "tmp-short-hash"
|
|
16
|
+
| "windows-named-pipe";
|
|
17
|
+
|
|
18
|
+
export interface IpcSocketPathResolution {
|
|
19
|
+
path: string;
|
|
20
|
+
source: IpcSocketPathSource;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface IpcEndpointOptions {
|
|
24
|
+
workspaceDir: string;
|
|
25
|
+
env?: Record<string, string | undefined>;
|
|
26
|
+
platform?: NodeJS.Platform;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getUnixSocketMaxPathBytes(platform: NodeJS.Platform): number {
|
|
30
|
+
return platform === "darwin"
|
|
31
|
+
? DARWIN_UNIX_SOCKET_MAX_PATH_BYTES
|
|
32
|
+
: DEFAULT_UNIX_SOCKET_MAX_PATH_BYTES;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isPathWithinSocketLimit(path: string, maxPathBytes: number): boolean {
|
|
36
|
+
return Buffer.byteLength(path, "utf8") <= maxPathBytes;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Derive the env var name and socket filename from a socket name.
|
|
41
|
+
*
|
|
42
|
+
* Examples (hyphens in the name become underscores in the env var):
|
|
43
|
+
*/
|
|
44
|
+
function deriveSocketNames(socketName: string): {
|
|
45
|
+
envVar: string;
|
|
46
|
+
fileName: string;
|
|
47
|
+
} {
|
|
48
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(socketName)) {
|
|
49
|
+
throw new Error(`Invalid IPC endpoint name: ${socketName}`);
|
|
50
|
+
}
|
|
51
|
+
const envVar = `${socketName.toUpperCase().replace(/-/g, "_")}_IPC_SOCKET_DIR`;
|
|
52
|
+
const fileName = `${socketName}.sock`;
|
|
53
|
+
return { envVar, fileName };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveWindowsNamedPipe(
|
|
57
|
+
socketName: string,
|
|
58
|
+
workspaceDir: string,
|
|
59
|
+
): IpcSocketPathResolution {
|
|
60
|
+
const workspaceIdentity = win32
|
|
61
|
+
.normalize(workspaceDir)
|
|
62
|
+
.replace(/\\+$/, "")
|
|
63
|
+
.toLowerCase();
|
|
64
|
+
const hash = createHash("sha256")
|
|
65
|
+
.update(`${workspaceIdentity}\0${socketName}`)
|
|
66
|
+
.digest("hex")
|
|
67
|
+
.slice(0, 24);
|
|
68
|
+
return {
|
|
69
|
+
path: `\\\\.\\pipe\\vellum-${socketName}-${hash}`,
|
|
70
|
+
source: "windows-named-pipe",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function isNamedPipePath(endpointPath: string): boolean {
|
|
75
|
+
return /^\\\\[.?]\\pipe\\/.test(endpointPath);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function removeIpcEndpointFile(endpointPath: string): void {
|
|
79
|
+
if (isNamedPipePath(endpointPath)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
unlinkSync(endpointPath);
|
|
84
|
+
} catch {
|
|
85
|
+
// Already absent.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the path to an IPC socket file.
|
|
91
|
+
*
|
|
92
|
+
* Resolution order:
|
|
93
|
+
* POSIX uses overrides, workspace paths, then bounded temporary paths.
|
|
94
|
+
* Windows uses deterministic named pipes.
|
|
95
|
+
*/
|
|
96
|
+
export function resolveIpcEndpoint(
|
|
97
|
+
socketName: string,
|
|
98
|
+
options: IpcEndpointOptions,
|
|
99
|
+
): IpcSocketPathResolution {
|
|
100
|
+
const { envVar, fileName } = deriveSocketNames(socketName);
|
|
101
|
+
const platform = options.platform ?? process.platform;
|
|
102
|
+
if (platform === "win32") {
|
|
103
|
+
return resolveWindowsNamedPipe(socketName, options.workspaceDir);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Explicit override via env var.
|
|
107
|
+
const envSocketDir = (options.env ?? process.env)[envVar]?.trim();
|
|
108
|
+
if (envSocketDir) {
|
|
109
|
+
return {
|
|
110
|
+
path: join(envSocketDir, fileName),
|
|
111
|
+
source: "env-override",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const maxPathBytes = getUnixSocketMaxPathBytes(platform);
|
|
116
|
+
const workspacePath = join(options.workspaceDir, fileName);
|
|
117
|
+
|
|
118
|
+
if (isPathWithinSocketLimit(workspacePath, maxPathBytes)) {
|
|
119
|
+
return {
|
|
120
|
+
path: workspacePath,
|
|
121
|
+
source: "workspace",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Workspace path exceeds AF_UNIX limit - fall back to tmpdir.
|
|
126
|
+
const hash = createHash("sha256")
|
|
127
|
+
.update(workspacePath)
|
|
128
|
+
.digest("hex")
|
|
129
|
+
.slice(0, 12);
|
|
130
|
+
const hashedPath = join(tmpdir(), IPC_TMP_DIR_NAME, `${hash}-${fileName}`);
|
|
131
|
+
if (isPathWithinSocketLimit(hashedPath, maxPathBytes)) {
|
|
132
|
+
return {
|
|
133
|
+
path: hashedPath,
|
|
134
|
+
source: "tmp-hash",
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
path: join(tmpdir(), `v-${hash}.sock`),
|
|
140
|
+
source: "tmp-short-hash",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -4,6 +4,8 @@ export {
|
|
|
4
4
|
type SocketWatchdogOptions,
|
|
5
5
|
type SocketWatchdogLogger,
|
|
6
6
|
} from "./socket-watchdog.js";
|
|
7
|
+
export { isNamedPipePath, removeIpcEndpointFile, resolveIpcEndpoint } from "./endpoint.js";
|
|
8
|
+
export { ipcListenOptions } from "./listen-options.js";
|
|
7
9
|
export {
|
|
8
10
|
IpcFrameReader,
|
|
9
11
|
writeLegacyMessage,
|
|
@@ -46,6 +46,7 @@ interface TestHarness {
|
|
|
46
46
|
|
|
47
47
|
interface BuildOptions {
|
|
48
48
|
intervalMs?: number;
|
|
49
|
+
socketPathOverride?: string;
|
|
49
50
|
createServerOverride?: () => Server;
|
|
50
51
|
/** Override `getServer` to simulate races. */
|
|
51
52
|
getServerOverride?: () => Server | null;
|
|
@@ -75,7 +76,7 @@ function buildHarness(opts: BuildOptions): TestHarness {
|
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
const watchdog = new SocketWatchdog({
|
|
78
|
-
socketPath,
|
|
79
|
+
socketPath: opts.socketPathOverride ?? socketPath,
|
|
79
80
|
intervalMs: opts.intervalMs ?? 0,
|
|
80
81
|
getServer: opts.getServerOverride ?? (() => serverRef.current),
|
|
81
82
|
createServer: opts.createServerOverride ?? defaultFactory,
|
|
@@ -190,6 +191,19 @@ describe("SocketWatchdog", () => {
|
|
|
190
191
|
expect(harness.rebinds).toHaveLength(0);
|
|
191
192
|
});
|
|
192
193
|
|
|
194
|
+
test("named pipes skip filesystem watchdog behavior", async () => {
|
|
195
|
+
const server = createServer();
|
|
196
|
+
harness = buildHarness({
|
|
197
|
+
socketPathOverride: "\\\\.\\pipe\\vellum-assistant-test",
|
|
198
|
+
getServerOverride: () => server,
|
|
199
|
+
createServerOverride: () => {
|
|
200
|
+
throw new Error("must not rebind");
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
harness.spawnedServers.push(server);
|
|
204
|
+
expect(await harness.watchdog.rebindIfMissing()).toBe(false);
|
|
205
|
+
});
|
|
206
|
+
|
|
193
207
|
test("rebindIfMissing recreates the listener when the path is gone", async () => {
|
|
194
208
|
harness = buildHarness({});
|
|
195
209
|
const initial = await startInitialServer(harness);
|
|
@@ -16,6 +16,9 @@ import { existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
|
16
16
|
import type { Server } from "node:net";
|
|
17
17
|
import { dirname } from "node:path";
|
|
18
18
|
|
|
19
|
+
import { isNamedPipePath } from "./endpoint.js";
|
|
20
|
+
import { ipcListenOptions } from "./listen-options.js";
|
|
21
|
+
|
|
19
22
|
/**
|
|
20
23
|
* Minimal logger surface (pino-compatible). Each method receives a context
|
|
21
24
|
* object plus an optional human-readable message.
|
|
@@ -66,6 +69,9 @@ const DEFAULT_INTERVAL_MS = 5000;
|
|
|
66
69
|
* `mkdir` only applies the mode to directories it creates.
|
|
67
70
|
*/
|
|
68
71
|
export function ensureSocketDir(socketPath: string): void {
|
|
72
|
+
if (isNamedPipePath(socketPath)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
69
75
|
const socketDir = dirname(socketPath);
|
|
70
76
|
if (!existsSync(socketDir)) {
|
|
71
77
|
mkdirSync(socketDir, { recursive: true, mode: 0o700 });
|
|
@@ -109,7 +115,13 @@ export class SocketWatchdog {
|
|
|
109
115
|
* watchdog is already running.
|
|
110
116
|
*/
|
|
111
117
|
start(): void {
|
|
112
|
-
if (
|
|
118
|
+
if (
|
|
119
|
+
this.intervalMs <= 0 ||
|
|
120
|
+
this.handle !== null ||
|
|
121
|
+
isNamedPipePath(this.socketPath)
|
|
122
|
+
) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
113
125
|
this.handle = setInterval(() => {
|
|
114
126
|
// The async entry path of rebindIfMissing performs filesystem work
|
|
115
127
|
// (ensureSocketDir, createServer) before its inner try/catch, so a
|
|
@@ -143,6 +155,9 @@ export class SocketWatchdog {
|
|
|
143
155
|
* is not running, or a shutdown/restart raced the rebind.
|
|
144
156
|
*/
|
|
145
157
|
async rebindIfMissing(): Promise<boolean> {
|
|
158
|
+
if (isNamedPipePath(this.socketPath)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
146
161
|
const initialServer = this.getServer();
|
|
147
162
|
if (initialServer === null) return false;
|
|
148
163
|
if (existsSync(this.socketPath)) return false;
|
|
@@ -167,7 +182,7 @@ export class SocketWatchdog {
|
|
|
167
182
|
};
|
|
168
183
|
newServer.once("error", onError);
|
|
169
184
|
newServer.once("listening", onListening);
|
|
170
|
-
newServer.listen(this.socketPath);
|
|
185
|
+
newServer.listen(ipcListenOptions(this.socketPath));
|
|
171
186
|
});
|
|
172
187
|
} catch (err) {
|
|
173
188
|
this.log.error(
|
|
@@ -40,3 +40,42 @@ export function isChannelId(value: unknown): value is ChannelId {
|
|
|
40
40
|
(CHANNEL_IDS as readonly string[]).includes(value)
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The provider key holding each channel's bot credential.
|
|
46
|
+
*
|
|
47
|
+
* Two senses of "connected" share the provider registry: a provider the user
|
|
48
|
+
* authorized so the assistant can act **as them** (`slack`, `discord`,
|
|
49
|
+
* `google`), and a bot credential letting people reach the assistant **as
|
|
50
|
+
* itself**. Nothing in a provider key says which, and the naming actively
|
|
51
|
+
* misleads: `slack` and `discord` name the user integration while their bots
|
|
52
|
+
* take a `_channel` suffix, yet `telegram` *is* the bot, because Telegram has
|
|
53
|
+
* no user-identity integration.
|
|
54
|
+
*
|
|
55
|
+
* That irregularity is why this is stated rather than derived from the key,
|
|
56
|
+
* and it is stated here because this file already owns what a channel is.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately only the key. What fields each credential requires is declared
|
|
59
|
+
* once already, per service, in the gateway's credential specs; restating it
|
|
60
|
+
* here would be a second copy of a different fact.
|
|
61
|
+
*
|
|
62
|
+
* Channels absent from this map reach the assistant without a bot credential
|
|
63
|
+
* of their own: `phone` through the voice provider, `vellum` and `platform`
|
|
64
|
+
* internally.
|
|
65
|
+
*/
|
|
66
|
+
export const CHANNEL_BOT_PROVIDER = {
|
|
67
|
+
slack: "slack_channel",
|
|
68
|
+
discord: "discord_channel",
|
|
69
|
+
telegram: "telegram",
|
|
70
|
+
} as const satisfies Partial<Record<ChannelId, string>>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether a provider key names a bot the assistant is reached through, rather
|
|
74
|
+
* than a grant letting it act as the user. This is the "which sense of
|
|
75
|
+
* connected" question, for any provider key.
|
|
76
|
+
*/
|
|
77
|
+
export function isChannelBotProvider(providerKey: string): boolean {
|
|
78
|
+
return (Object.values(CHANNEL_BOT_PROVIDER) as readonly string[]).includes(
|
|
79
|
+
providerKey,
|
|
80
|
+
);
|
|
81
|
+
}
|