@vellumai/assistant 0.11.4 → 0.11.5-staging.1
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/AGENTS.md +2 -2
- package/ARCHITECTURE.md +29 -4
- package/README.md +1 -1
- package/docs/architecture/memory.md +17 -5
- package/docs/architecture/security.md +96 -152
- package/docs/guardian-request-flow.md +13 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +7 -14
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +29 -31
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +193 -3
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +10 -3
- package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +4 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -6
- package/src/__tests__/channel-reply-delivery.test.ts +26 -13
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/client-os-metadata-persistence.test.ts +16 -0
- package/src/__tests__/compactor-retained-image-validation.test.ts +142 -0
- package/src/__tests__/container-cpu-sampler.test.ts +153 -0
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +17 -0
- package/src/__tests__/conversation-lifecycle.test.ts +20 -26
- package/src/__tests__/conversation-pairing.test.ts +186 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +21 -2
- package/src/__tests__/credential-broker-server-use.test.ts +24 -5
- package/src/__tests__/credential-routes.test.ts +22 -3
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/daemon-credential-client.test.ts +88 -0
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/dm-backfill.test.ts +63 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -4
- package/src/__tests__/events-client-registration.test.ts +28 -0
- package/src/__tests__/file-write-tool.test.ts +4 -2
- package/src/__tests__/guardian-prompt-notice-privacy.test.ts +133 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +143 -97
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/host-proxy-interface.test.ts +11 -1
- package/src/__tests__/host-shell-tool.test.ts +23 -4
- package/src/__tests__/image-conversion.test.ts +143 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/input-repairs.test.ts +207 -0
- package/src/__tests__/live-workspace-guard.test.ts +75 -0
- package/src/__tests__/mcp-abort-signal.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -1
- package/src/__tests__/media-resolve-image-validation.test.ts +310 -0
- package/src/__tests__/mtime-cache.test.ts +2 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +124 -1
- package/src/__tests__/openai-provider.test.ts +22 -0
- package/src/__tests__/personal-memory-auth-bypass.test.ts +22 -6
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/platform.test.ts +16 -1
- package/src/__tests__/plugin-api-resolve-credential.test.ts +22 -13
- package/src/__tests__/plugin-api-shim.test.ts +5 -0
- package/src/__tests__/plugin-api-store-credential.test.ts +268 -0
- package/src/__tests__/plugin-config-data-migration.test.ts +8 -1
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/resource-pressure-guard.test.ts +428 -0
- package/src/__tests__/resource-pressure-routes.test.ts +113 -0
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +27 -1
- package/src/__tests__/secret-routes-platform-proxy.test.ts +7 -0
- package/src/__tests__/skill-tool-factory.test.ts +161 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +63 -38
- package/src/__tests__/tool-policy.test.ts +97 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +7 -4
- package/src/__tests__/user-plugin-loader.test.ts +2 -0
- package/src/__tests__/validate-input.test.ts +243 -7
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/acp/__tests__/acp-claude-oauth.test.ts +97 -29
- package/src/acp/__tests__/prepare-agent-env.test.ts +376 -153
- package/src/acp/acp-claude-oauth.ts +38 -21
- package/src/acp/prepare-agent-env.ts +146 -55
- package/src/agent/loop-tool-dedup.test.ts +161 -0
- package/src/agent/loop.ts +68 -22
- package/src/api/constants/app-tools.ts +34 -0
- package/src/api/events/resource-pressure-status-changed.ts +53 -0
- package/src/api/index.ts +19 -0
- package/src/api/responses/resource-pressure-status.ts +25 -0
- package/src/approvals/guardian-channel-delivery.ts +70 -6
- package/src/approvals/guardian-request-resolvers.ts +32 -66
- package/src/channels/__tests__/message-audience.test.ts +49 -0
- package/src/channels/__tests__/types.test.ts +17 -3
- package/src/channels/message-audience.ts +40 -0
- package/src/channels/types.ts +16 -12
- package/src/cli/__tests__/catalog-search-help.test.ts +50 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +31 -0
- package/src/cli/commands/__tests__/conversations-search.test.ts +289 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/__tests__/inference-providers.test.ts +68 -0
- package/src/cli/commands/__tests__/keys.test.ts +89 -8
- package/src/cli/commands/__tests__/plugins.test.ts +57 -1
- package/src/cli/commands/channel-verification-sessions.help.ts +11 -9
- package/src/cli/commands/channel-verification-sessions.ts +11 -21
- package/src/cli/commands/conversations.help.ts +34 -0
- package/src/cli/commands/conversations.ts +125 -0
- package/src/cli/commands/inference-providers.ts +9 -4
- package/src/cli/commands/inference.help.ts +9 -4
- package/src/cli/commands/keys.help.ts +13 -1
- package/src/cli/commands/keys.ts +26 -15
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/index.help.ts +41 -27
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/monitoring.ts +1 -0
- package/src/cli/commands/plugins.help.ts +28 -31
- package/src/cli/commands/plugins.ts +10 -0
- package/src/cli/commands/skills.help.ts +13 -16
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/install-from-github.test.ts +38 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +34 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +32 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +2 -1
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +64 -0
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/daemon-credential-client.ts +25 -3
- package/src/cli/lib/install-from-github.ts +35 -24
- package/src/cli/lib/merge-plugin-tree.ts +22 -4
- package/src/cli/lib/plugin-surfaces.ts +27 -0
- package/src/config/feature-flag-registry.json +35 -19
- package/src/config/webhook-routing.ts +8 -0
- package/src/context/compactor.ts +31 -2
- package/src/daemon/__tests__/provider-rejection-log-fields.test.ts +272 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +4 -1
- package/src/daemon/conversation-error.ts +13 -0
- package/src/daemon/conversation-messaging.ts +22 -2
- package/src/daemon/conversation-process.ts +11 -0
- package/src/daemon/conversation-store.ts +10 -0
- package/src/daemon/conversation-surfaces.ts +21 -8
- package/src/daemon/conversation.ts +12 -10
- package/src/daemon/lifecycle.ts +6 -2
- package/src/daemon/message-types/conversations.ts +5 -7
- package/src/daemon/provider-rejection-log-fields.ts +124 -0
- package/src/daemon/resource-pressure-guard-lifecycle.ts +66 -0
- package/src/daemon/resource-pressure-guard.ts +387 -0
- package/src/daemon/shutdown-handlers.ts +2 -0
- package/src/daemon/startup-error.ts +16 -0
- package/src/daemon/trust-context.ts +14 -10
- package/src/daemon/unsendable-image-notice.ts +76 -0
- package/src/hooks/registry.ts +65 -51
- package/src/ipc/__tests__/socket-path.test.ts +15 -7
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/socket-cleanup.ts +2 -11
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -1
- package/src/messaging/provider-message-metadata.ts +128 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +116 -68
- package/src/messaging/providers/channel-transport.ts +90 -13
- package/src/messaging/providers/discord/send.ts +16 -0
- package/src/messaging/providers/discord/transport.ts +11 -1
- package/src/messaging/providers/index.ts +109 -16
- package/src/messaging/providers/slack/message-metadata.ts +45 -0
- package/src/messaging/providers/slack/render-transcript.ts +1 -4
- package/src/messaging/providers/slack/send.test.ts +6 -9
- package/src/messaging/providers/slack/send.ts +40 -28
- package/src/messaging/providers/slack/transport.ts +22 -26
- package/src/messaging/providers/telegram-bot/send.test.ts +2 -8
- package/src/messaging/providers/telegram-bot/transport.ts +3 -6
- package/src/messaging/read-provider-metadata.test.ts +157 -0
- package/src/messaging/read-provider-metadata.ts +51 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +94 -0
- package/src/notifications/__tests__/edit-notification.test.ts +174 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +536 -4
- package/src/notifications/adapters/macos.ts +55 -0
- package/src/notifications/adapters/slack.ts +10 -5
- package/src/notifications/assistant-reply-producer.ts +37 -4
- package/src/notifications/conversation-pairing.ts +111 -21
- package/src/notifications/edit-notification.ts +38 -8
- package/src/notifications/emit-signal.ts +12 -14
- package/src/notifications/home-feed-side-effect.ts +218 -15
- package/src/notifications/types.ts +5 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +28 -3
- package/src/permissions/confirmation-guardian-request.ts +5 -1
- package/src/persistence/conversation-crud.ts +23 -20
- package/src/persistence/conversation-queries.ts +14 -1
- package/src/persistence/conversation-types.ts +22 -0
- package/src/persistence/delivery-crud.ts +122 -15
- package/src/plugin-api/__tests__/import-graph-partial-mock.test.ts +67 -0
- package/src/plugin-api/conversation-turn.ts +27 -0
- package/src/plugin-api/credential-scope.test.ts +15 -0
- package/src/plugin-api/credential-scope.ts +14 -0
- package/src/plugin-api/index.ts +19 -1
- package/src/plugin-api/resolve-credential.ts +15 -10
- package/src/plugin-api/store-credential.ts +148 -0
- package/src/plugin-api/system-card.ts +36 -0
- package/src/plugin-api/vision-support.test.ts +82 -0
- package/src/plugin-api/vision-support.ts +14 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +361 -110
- package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +4 -0
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +58 -0
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +64 -23
- package/src/plugins/defaults/image-recovery/detect.ts +24 -1
- package/src/plugins/defaults/main.ts +15 -0
- package/src/plugins/defaults/memory/AGENTS.md +11 -3
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +9 -12
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +114 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +1 -61
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +68 -6
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/static-context.ts +0 -29
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +3 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/mtime-cache.ts +1 -1
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/plugin-tree-walk.ts +63 -4
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/connection-resolution.ts +2 -1
- package/src/providers/content-blocks.ts +22 -0
- package/src/providers/gemini/client.ts +5 -2
- package/src/providers/inference/__tests__/adapter-factory-litellm.test.ts +47 -1
- package/src/providers/inference/__tests__/adapter-factory-ollama.test.ts +83 -0
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +98 -1
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +38 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +10 -0
- package/src/providers/inference/__tests__/connections-ollama.test.ts +90 -0
- package/src/providers/inference/__tests__/missing-credential-guard.test.ts +117 -0
- package/src/providers/inference/adapter-factory.ts +33 -2
- package/src/providers/inference/auth.ts +13 -2
- package/src/providers/inference/credential-usage.ts +37 -0
- package/src/providers/inference/missing-credential-guard.ts +110 -0
- package/src/providers/inference/resolve-auth.ts +7 -7
- package/src/providers/media-resolve.ts +176 -15
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +576 -46
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +322 -42
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +31 -10
- package/src/runtime/AGENTS.md +35 -0
- package/src/runtime/__tests__/runtime-http-port-collision.test.ts +131 -0
- package/src/runtime/__tests__/web-presence.test.ts +234 -0
- package/src/runtime/assistant-event-hub.ts +38 -0
- package/src/runtime/channel-reply-delivery.ts +54 -31
- package/src/runtime/channel-retry-sweep.ts +6 -4
- package/src/runtime/effective-capabilities.test.ts +19 -0
- package/src/runtime/effective-capabilities.ts +25 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/http-errors.ts +1 -0
- package/src/runtime/http-server.ts +20 -5
- package/src/runtime/routes/__tests__/client-routes.test.ts +150 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +133 -0
- package/src/runtime/routes/__tests__/credential-delete-in-use.test.ts +155 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +52 -1
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +175 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +88 -35
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/system-card-turn-termination.test.ts +107 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +75 -4
- package/src/runtime/routes/approval-routes.ts +37 -4
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +16 -22
- package/src/runtime/routes/canned-message-complete.ts +68 -29
- package/src/runtime/routes/client-routes.ts +113 -0
- package/src/runtime/routes/conversation-list-routes.ts +22 -7
- package/src/runtime/routes/conversation-management-routes.ts +15 -9
- package/src/runtime/routes/conversation-routes.ts +36 -22
- package/src/runtime/routes/credential-in-use.ts +85 -0
- package/src/runtime/routes/credential-routes.ts +55 -85
- package/src/runtime/routes/guardian-approval-interception.ts +22 -22
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +14 -12
- package/src/runtime/routes/identity-routes.ts +5 -121
- package/src/runtime/routes/inbound-message-handler.ts +71 -27
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -17
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +77 -50
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -97
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -77
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +17 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -70
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +9 -7
- package/src/runtime/routes/monitoring-routes.ts +9 -1
- package/src/runtime/routes/plugins-routes.ts +17 -5
- package/src/runtime/routes/resource-pressure-routes.ts +22 -0
- package/src/runtime/routes/secret-routes.ts +39 -8
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/user-route-dispatcher.ts +31 -2
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/slack-reply-session.test.ts +23 -13
- package/src/runtime/slack-reply-session.ts +38 -55
- package/src/runtime/web-presence.ts +90 -0
- package/src/skills/validate-input.ts +177 -40
- package/src/subagent/__tests__/consult-context-gating.test.ts +6 -10
- package/src/subagent/consult-context.ts +7 -23
- package/src/tools/credentials/broker.ts +24 -79
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/credentials/tool-policy.ts +67 -0
- package/src/tools/executor.ts +53 -15
- package/src/tools/network/__tests__/web-search.test.ts +41 -1
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/network/web-search.ts +21 -3
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/schema-transforms.ts +40 -5
- package/src/tools/shared/input-repairs.ts +160 -0
- package/src/tools/skills/skill-tool-factory.ts +18 -4
- package/src/tools/subagent/spawn.ts +0 -1
- package/src/tools/tool-approval-handler.ts +16 -10
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/util/__tests__/cgroup-memory.test.ts +3 -0
- package/src/util/cgroup-memory.ts +3 -0
- package/src/util/container-cpu-sampler.ts +250 -0
- package/src/util/image-conversion.ts +178 -14
- package/src/util/platform.ts +93 -10
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app tools that mutate the stored app set, and what each one leaves
|
|
3
|
+
* behind. Mirrors `document-tools.ts`: a single declaration both the daemon and
|
|
4
|
+
* the web client derive their tool sets from, so a new mutating app tool is
|
|
5
|
+
* added here once and every consumer picks it up.
|
|
6
|
+
*
|
|
7
|
+
* Tool names are the ones the runner sees. Calls arriving through the bundled
|
|
8
|
+
* `app-builder` skill reach the daemon already unwrapped to the inner tool
|
|
9
|
+
* name, and the web client unwraps the `skill_execute` envelope itself.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface AppMutationTool {
|
|
13
|
+
/** The executed tool name. */
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the app is still openable once the tool has run. A deleted app has
|
|
17
|
+
* nothing to open, so it cannot anchor a card back to itself.
|
|
18
|
+
*/
|
|
19
|
+
readonly reopenable: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const APP_MUTATION_TOOLS: readonly AppMutationTool[] = [
|
|
23
|
+
{ name: "app_create", reopenable: true },
|
|
24
|
+
{ name: "app_update", reopenable: true },
|
|
25
|
+
{ name: "app_delete", reopenable: false },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The mutating app tools that leave the app openable. The web transcript
|
|
30
|
+
* anchors its end-of-response app cards on these, so a delete never produces a
|
|
31
|
+
* card pointing at an app that is gone.
|
|
32
|
+
*/
|
|
33
|
+
export const APP_MUTATION_TOOL_NAMES: readonly string[] =
|
|
34
|
+
APP_MUTATION_TOOLS.filter((tool) => tool.reopenable).map((tool) => tool.name);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `resource_pressure_status_changed` SSE event.
|
|
3
|
+
*
|
|
4
|
+
* Global broadcast pushed when the daemon's resource-pressure snapshot
|
|
5
|
+
* changes. Mirrors the REST `GET /resource-pressure/status` shape so the
|
|
6
|
+
* client's monitor can apply stream updates and polled fetches through
|
|
7
|
+
* the same code path. Carries no `conversationId`: it is workspace-wide.
|
|
8
|
+
*
|
|
9
|
+
* Canonical wire-contract source. Daemon code imports the type
|
|
10
|
+
* directly from this file; external consumers import via
|
|
11
|
+
* `@vellumai/assistant-api`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
|
|
16
|
+
export const ResourcePressureStateSchema = z.enum([
|
|
17
|
+
"disabled",
|
|
18
|
+
"ok",
|
|
19
|
+
"elevated",
|
|
20
|
+
"unknown",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export type ResourcePressureState = z.infer<typeof ResourcePressureStateSchema>;
|
|
24
|
+
|
|
25
|
+
export const ResourcePressureStatusSchema = z.object({
|
|
26
|
+
enabled: z.boolean(),
|
|
27
|
+
state: ResourcePressureStateSchema,
|
|
28
|
+
/** Latest CPU sample as a percent of the CPU allocation. */
|
|
29
|
+
cpuPercent: z.number().nullable(),
|
|
30
|
+
/** Working set as a percent of the memory limit. */
|
|
31
|
+
memoryPercent: z.number().nullable(),
|
|
32
|
+
/** Whether the CPU signal is holding the `elevated` state. */
|
|
33
|
+
cpuElevated: z.boolean(),
|
|
34
|
+
/** Whether the memory signal is holding the `elevated` state. */
|
|
35
|
+
memoryElevated: z.boolean(),
|
|
36
|
+
cpuThresholdPercent: z.number(),
|
|
37
|
+
memoryThresholdPercent: z.number(),
|
|
38
|
+
lastCheckedAt: z.string().nullable(),
|
|
39
|
+
error: z.string().nullable(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export type ResourcePressureStatus = z.infer<
|
|
43
|
+
typeof ResourcePressureStatusSchema
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
export const ResourcePressureStatusChangedEventSchema = z.object({
|
|
47
|
+
type: z.literal("resource_pressure_status_changed"),
|
|
48
|
+
status: ResourcePressureStatusSchema,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export type ResourcePressureStatusChangedEvent = z.infer<
|
|
52
|
+
typeof ResourcePressureStatusChangedEventSchema
|
|
53
|
+
>;
|
package/src/api/index.ts
CHANGED
|
@@ -102,6 +102,7 @@ import {
|
|
|
102
102
|
RecordingStopEventSchema,
|
|
103
103
|
} from "./events/recording.js";
|
|
104
104
|
import { RelationshipStateUpdatedEventSchema } from "./events/relationship-state-updated.js";
|
|
105
|
+
import { ResourcePressureStatusChangedEventSchema } from "./events/resource-pressure-status-changed.js";
|
|
105
106
|
import { ScheduleConversationCreatedEventSchema } from "./events/schedule-conversation-created.js";
|
|
106
107
|
import { SecretRequestEventSchema } from "./events/secret-request.js";
|
|
107
108
|
import { ServiceGroupUpdateCompleteEventSchema } from "./events/service-group-update-complete.js";
|
|
@@ -134,6 +135,11 @@ import { WorkflowLeafStartedEventSchema } from "./events/workflow-leaf-started.j
|
|
|
134
135
|
import { WorkflowProgressEventSchema } from "./events/workflow-progress.js";
|
|
135
136
|
import { WorkflowStartedEventSchema } from "./events/workflow-started.js";
|
|
136
137
|
|
|
138
|
+
export {
|
|
139
|
+
APP_MUTATION_TOOL_NAMES,
|
|
140
|
+
APP_MUTATION_TOOLS,
|
|
141
|
+
type AppMutationTool,
|
|
142
|
+
} from "./constants/app-tools.js";
|
|
137
143
|
export {
|
|
138
144
|
CALL_SITE_COMPACTION_AGENT,
|
|
139
145
|
CALL_SITE_SYNTHETIC_AGENT_ERROR_MESSAGE,
|
|
@@ -536,6 +542,14 @@ export {
|
|
|
536
542
|
type RelationshipStateUpdatedEvent,
|
|
537
543
|
RelationshipStateUpdatedEventSchema,
|
|
538
544
|
} from "./events/relationship-state-updated.js";
|
|
545
|
+
export {
|
|
546
|
+
type ResourcePressureState,
|
|
547
|
+
ResourcePressureStateSchema,
|
|
548
|
+
type ResourcePressureStatus,
|
|
549
|
+
type ResourcePressureStatusChangedEvent,
|
|
550
|
+
ResourcePressureStatusChangedEventSchema,
|
|
551
|
+
ResourcePressureStatusSchema,
|
|
552
|
+
} from "./events/resource-pressure-status-changed.js";
|
|
539
553
|
export {
|
|
540
554
|
type ScheduleConversationCreatedEvent,
|
|
541
555
|
ScheduleConversationCreatedEventSchema,
|
|
@@ -802,6 +816,10 @@ export {
|
|
|
802
816
|
type MemoryV3SelectionRow,
|
|
803
817
|
MemoryV3SelectionRowSchema,
|
|
804
818
|
} from "./responses/memory-v3-selection-log.js";
|
|
819
|
+
export {
|
|
820
|
+
type ResourcePressureStatusResponse,
|
|
821
|
+
ResourcePressureStatusResponseSchema,
|
|
822
|
+
} from "./responses/resource-pressure-status.js";
|
|
805
823
|
export {
|
|
806
824
|
type SubagentDetailEvent,
|
|
807
825
|
SubagentDetailEventSchema,
|
|
@@ -987,6 +1005,7 @@ export const AssistantEventSchema = z.discriminatedUnion("type", [
|
|
|
987
1005
|
RecordingStartEventSchema,
|
|
988
1006
|
RecordingStopEventSchema,
|
|
989
1007
|
RelationshipStateUpdatedEventSchema,
|
|
1008
|
+
ResourcePressureStatusChangedEventSchema,
|
|
990
1009
|
ScheduleConversationCreatedEventSchema,
|
|
991
1010
|
SecretRequestEventSchema,
|
|
992
1011
|
ServiceGroupUpdateCompleteEventSchema,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contract for the resource-pressure REST endpoint
|
|
3
|
+
* (`GET /resource-pressure/status`). Returns the current snapshot
|
|
4
|
+
* wrapped as `{ status }`.
|
|
5
|
+
*
|
|
6
|
+
* Reuses the canonical `ResourcePressureStatusSchema` defined alongside
|
|
7
|
+
* the `resource_pressure_status_changed` SSE event so the polled REST
|
|
8
|
+
* fetch and the streamed update share a single shape.
|
|
9
|
+
*
|
|
10
|
+
* Canonical wire-contract source. Assistant code imports the types
|
|
11
|
+
* directly from this file via relative paths; external consumers
|
|
12
|
+
* (web client, gateway, evals) import via `@vellumai/assistant-api`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
|
|
17
|
+
import { ResourcePressureStatusSchema } from "../events/resource-pressure-status-changed.js";
|
|
18
|
+
|
|
19
|
+
export const ResourcePressureStatusResponseSchema = z.object({
|
|
20
|
+
status: ResourcePressureStatusSchema,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type ResourcePressureStatusResponse = z.infer<
|
|
24
|
+
typeof ResourcePressureStatusResponseSchema
|
|
25
|
+
>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared addressing helpers for guardian
|
|
2
|
+
* Shared addressing helpers for guardian-flow channel notices.
|
|
3
3
|
*
|
|
4
|
-
* Requester notices (approval, denial, expiry)
|
|
5
|
-
*
|
|
6
|
-
* guardian-facing notification pipeline. Centralizing the
|
|
7
|
-
* keeps the decision resolvers
|
|
8
|
-
* apart on
|
|
4
|
+
* Requester notices (approval, denial, expiry) and the guardian's own approval
|
|
5
|
+
* prompt are delivered straight to a chat via `deliverChannelReply` -
|
|
6
|
+
* independent of the guardian-facing notification pipeline. Centralizing the
|
|
7
|
+
* addressing rules here keeps the decision resolvers, the timer-driven expiry
|
|
8
|
+
* sweep and the in-turn approval prompt from drifting apart on who a message
|
|
9
|
+
* is put in front of.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -111,3 +112,66 @@ export function resolveRequesterDeliveryTarget(params: {
|
|
|
111
112
|
}
|
|
112
113
|
return requesterChatId;
|
|
113
114
|
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Reduce a reply callback to its channel route, dropping every query param.
|
|
118
|
+
*
|
|
119
|
+
* A callback addresses the turn it came from. The gateway hangs the turn's
|
|
120
|
+
* coordinates on it as params, and each transport reads a different one:
|
|
121
|
+
* Slack a `threadTs`, Telegram a forum topic `threadId`, Discord a `threadId`
|
|
122
|
+
* that replaces the destination outright rather than narrowing it. Carried
|
|
123
|
+
* onto a delivery aimed elsewhere they either fail the send or quietly win
|
|
124
|
+
* over the new address, and a redirect that leaves one behind is
|
|
125
|
+
* indistinguishable from no redirect at all.
|
|
126
|
+
*
|
|
127
|
+
* Everything is dropped rather than a named set, so a channel added later, or
|
|
128
|
+
* a param a transport starts reading later, cannot silently escape this. The
|
|
129
|
+
* channel itself survives because it is the path, which is also what
|
|
130
|
+
* `isDirectDelivery` resolves a transport from, and nothing the gateway hangs
|
|
131
|
+
* on a deliver callback is needed to authorize or route the send.
|
|
132
|
+
*
|
|
133
|
+
* Relative or malformed URLs are returned as-is; they carry no params.
|
|
134
|
+
*/
|
|
135
|
+
export function stripTurnDestination(replyCallbackUrl: string): string {
|
|
136
|
+
try {
|
|
137
|
+
const url = new URL(replyCallbackUrl);
|
|
138
|
+
url.search = "";
|
|
139
|
+
return url.toString();
|
|
140
|
+
} catch {
|
|
141
|
+
return replyCallbackUrl;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Resolve where a guardian's own approval prompt is delivered.
|
|
147
|
+
*
|
|
148
|
+
* The prompt is raised by a turn the guardian is having, and that turn can be
|
|
149
|
+
* running in a room: a shared Slack channel, a Telegram group. The card
|
|
150
|
+
* carries the tool name and a preview of the command, so delivering it where
|
|
151
|
+
* the turn is shows both to everyone there.
|
|
152
|
+
*
|
|
153
|
+
* It goes to the guardian's own bound chat instead, the address they
|
|
154
|
+
* nominated when they verified and the one the notification pipeline already
|
|
155
|
+
* sends guardian cards to. No channel is named in this rule and none can be
|
|
156
|
+
* forgotten by it: a bound chat is the guardian's by definition, on any
|
|
157
|
+
* channel, including ones not built yet.
|
|
158
|
+
*
|
|
159
|
+
* A bound chat equal to the turn's chat means the turn is already there, so
|
|
160
|
+
* nothing moves. That also covers a turn where no binding resolved, since the
|
|
161
|
+
* trust context falls back to the turn's own chat, leaving delivery exactly as
|
|
162
|
+
* it is rather than dropping it.
|
|
163
|
+
*/
|
|
164
|
+
export function resolveGuardianPromptDelivery(params: {
|
|
165
|
+
turnChatId: string;
|
|
166
|
+
turnCallbackUrl: string;
|
|
167
|
+
guardianChatId: string | undefined;
|
|
168
|
+
}): { chatId: string; callbackUrl: string } {
|
|
169
|
+
const { turnChatId, turnCallbackUrl, guardianChatId } = params;
|
|
170
|
+
if (!guardianChatId || guardianChatId === turnChatId) {
|
|
171
|
+
return { chatId: turnChatId, callbackUrl: turnCallbackUrl };
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
chatId: guardianChatId,
|
|
175
|
+
callbackUrl: stripTurnDestination(turnCallbackUrl),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -23,8 +23,10 @@ import type {
|
|
|
23
23
|
GuardianRequestWire,
|
|
24
24
|
} from "../channels/gateway-guardian-requests.js";
|
|
25
25
|
import { getGuardianRequestOrNull } from "../channels/gateway-guardian-requests.js";
|
|
26
|
+
import { audienceForReader } from "../channels/message-audience.js";
|
|
26
27
|
import { findContactChannel } from "../contacts/contact-store.js";
|
|
27
28
|
import { findConversation } from "../daemon/conversation-registry.js";
|
|
29
|
+
import { isSlackDmConversation } from "../messaging/providers/slack/message-metadata.js";
|
|
28
30
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
29
31
|
import { parseQuestionAnswerActionId } from "../notifications/guardian-question-mode.js";
|
|
30
32
|
import {
|
|
@@ -62,6 +64,7 @@ import {
|
|
|
62
64
|
channelDeliversToUserId,
|
|
63
65
|
resolveDeliverCallbackUrlForChannel,
|
|
64
66
|
resolveRequesterDeliveryTarget,
|
|
67
|
+
stripTurnDestination,
|
|
65
68
|
} from "./guardian-channel-delivery.js";
|
|
66
69
|
|
|
67
70
|
const log = getLogger("guardian-request-resolvers");
|
|
@@ -70,38 +73,6 @@ const log = getLogger("guardian-request-resolvers");
|
|
|
70
73
|
// Helpers
|
|
71
74
|
// ---------------------------------------------------------------------------
|
|
72
75
|
|
|
73
|
-
/**
|
|
74
|
-
* Determines whether a Slack delivery should use ephemeral mode.
|
|
75
|
-
*
|
|
76
|
-
* Ephemeral messages (`chat.postEphemeral`) require a real channel ID
|
|
77
|
-
* (starts with `C` for public/private channels, or `D` for DM conversations).
|
|
78
|
-
* When the chat ID is a user ID (starts with `U`), `chat.postEphemeral` fails
|
|
79
|
-
* with `channel_not_found`. In that case the message is already going to a DM
|
|
80
|
-
* opened by `chat.postMessage`, so ephemeral isn't needed.
|
|
81
|
-
*
|
|
82
|
-
* Returns `true` only when the source channel is Slack AND the chatId is a
|
|
83
|
-
* shared channel (starts with `C`), meaning other users could see the message.
|
|
84
|
-
*/
|
|
85
|
-
function shouldUseEphemeral(sourceChannel: string, chatId: string): boolean {
|
|
86
|
-
return sourceChannel === "slack" && chatId.startsWith("C");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Strip the `threadTs` query param from a reply callback URL. The param
|
|
91
|
-
* addresses the guardian's channel thread; reusing it for a DM delivery
|
|
92
|
-
* raises `thread_not_found`. Relative or malformed URLs are returned as-is —
|
|
93
|
-
* they carry no threadTs to strip.
|
|
94
|
-
*/
|
|
95
|
-
function stripThreadTsParam(replyCallbackUrl: string): string {
|
|
96
|
-
try {
|
|
97
|
-
const url = new URL(replyCallbackUrl);
|
|
98
|
-
url.searchParams.delete("threadTs");
|
|
99
|
-
return url.toString();
|
|
100
|
-
} catch {
|
|
101
|
-
return replyCallbackUrl;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
76
|
/**
|
|
106
77
|
* Deliver the verification code straight to the requester's DM so the
|
|
107
78
|
* guardian is never an out-of-band courier for the secret.
|
|
@@ -125,7 +96,7 @@ async function deliverVerificationCodeToRequester(params: {
|
|
|
125
96
|
verificationCode: string;
|
|
126
97
|
assistantId: string;
|
|
127
98
|
}): Promise<boolean> {
|
|
128
|
-
const callbackUrl =
|
|
99
|
+
const callbackUrl = stripTurnDestination(params.replyCallbackUrl);
|
|
129
100
|
|
|
130
101
|
try {
|
|
131
102
|
await deliverChannelReply(callbackUrl, {
|
|
@@ -194,13 +165,13 @@ function buildRequesterChannelNotice(params: {
|
|
|
194
165
|
text: params.text,
|
|
195
166
|
assistantId: params.assistantId,
|
|
196
167
|
};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
168
|
+
payload.audience = audienceForReader(
|
|
169
|
+
params.channel,
|
|
170
|
+
|
|
171
|
+
params.requesterChatId,
|
|
172
|
+
|
|
173
|
+
params.requesterExternalUserId,
|
|
174
|
+
);
|
|
204
175
|
return payload;
|
|
205
176
|
}
|
|
206
177
|
|
|
@@ -1309,16 +1280,11 @@ const accessRequestResolver: GuardianRequestResolver = {
|
|
|
1309
1280
|
};
|
|
1310
1281
|
// On Slack shared channels, deliver the verification code as ephemeral
|
|
1311
1282
|
// so only the guardian sees the secret — not all channel members.
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
ctx.actor.actorExternalUserId
|
|
1318
|
-
) {
|
|
1319
|
-
codePayload.ephemeral = true;
|
|
1320
|
-
codePayload.user = ctx.actor.actorExternalUserId;
|
|
1321
|
-
}
|
|
1283
|
+
codePayload.audience = audienceForReader(
|
|
1284
|
+
ctx.actor.channel,
|
|
1285
|
+
guardianInBandContext.guardianChatId,
|
|
1286
|
+
ctx.actor.actorExternalUserId,
|
|
1287
|
+
);
|
|
1322
1288
|
await deliverChannelReply(
|
|
1323
1289
|
guardianInBandContext.replyCallbackUrl,
|
|
1324
1290
|
codePayload,
|
|
@@ -1339,9 +1305,9 @@ const accessRequestResolver: GuardianRequestResolver = {
|
|
|
1339
1305
|
codeDelivered &&
|
|
1340
1306
|
ctx.actor.channel === "slack" &&
|
|
1341
1307
|
guardianUserId &&
|
|
1342
|
-
!guardianInBandContext.guardianChatId
|
|
1308
|
+
!isSlackDmConversation(guardianInBandContext.guardianChatId)
|
|
1343
1309
|
) {
|
|
1344
|
-
const dmCallbackUrl =
|
|
1310
|
+
const dmCallbackUrl = stripTurnDestination(
|
|
1345
1311
|
guardianInBandContext.replyCallbackUrl,
|
|
1346
1312
|
);
|
|
1347
1313
|
|
|
@@ -1543,13 +1509,13 @@ const toolGrantRequestResolver: GuardianRequestResolver = {
|
|
|
1543
1509
|
text: `Your request to use "${request.toolName}" has been denied by the guardian.`,
|
|
1544
1510
|
assistantId,
|
|
1545
1511
|
};
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1512
|
+
grantDenialPayload.audience = audienceForReader(
|
|
1513
|
+
request.sourceChannel,
|
|
1514
|
+
|
|
1515
|
+
requesterChatId,
|
|
1516
|
+
|
|
1517
|
+
request.requesterExternalUserId,
|
|
1518
|
+
);
|
|
1553
1519
|
await deliverChannelReply(
|
|
1554
1520
|
channelDeliveryContext.replyCallbackUrl,
|
|
1555
1521
|
grantDenialPayload,
|
|
@@ -1642,13 +1608,13 @@ const toolGrantRequestResolver: GuardianRequestResolver = {
|
|
|
1642
1608
|
text: `Your request to use "${request.toolName}" has been approved. Please retry your request.`,
|
|
1643
1609
|
assistantId,
|
|
1644
1610
|
};
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1611
|
+
grantApprovalPayload.audience = audienceForReader(
|
|
1612
|
+
request.sourceChannel,
|
|
1613
|
+
|
|
1614
|
+
requesterChatId,
|
|
1615
|
+
|
|
1616
|
+
request.requesterExternalUserId,
|
|
1617
|
+
);
|
|
1652
1618
|
await deliverChannelReply(
|
|
1653
1619
|
channelDeliveryContext.replyCallbackUrl,
|
|
1654
1620
|
grantApprovalPayload,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { audienceForReader } from "../message-audience.js";
|
|
4
|
+
|
|
5
|
+
describe("audienceForReader", () => {
|
|
6
|
+
test("restricts a reply in a Slack channel to the named reader", () => {
|
|
7
|
+
expect(audienceForReader("slack", "C0ROOM", "U0READER")).toEqual({
|
|
8
|
+
kind: "oneReader",
|
|
9
|
+
userId: "U0READER",
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("leaves a Slack DM unrestricted", () => {
|
|
14
|
+
// Nobody else can read a DM, so restricting buys no privacy and costs
|
|
15
|
+
// durability: an ephemeral message does not survive a reload.
|
|
16
|
+
expect(audienceForReader("slack", "D0DIRECT", "U0READER")).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("restricts a private group, which several readers share", () => {
|
|
20
|
+
// A `G` id is a shared room like a `C` one. Testing for a DM rather than
|
|
21
|
+
// for a channel id is what keeps this from leaking into the group.
|
|
22
|
+
expect(audienceForReader("slack", "G0PRIVATE", "U0READER")).toEqual({
|
|
23
|
+
kind: "oneReader",
|
|
24
|
+
userId: "U0READER",
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("leaves every other channel unrestricted", () => {
|
|
29
|
+
for (const channel of ["telegram", "discord", "whatsapp", "email"]) {
|
|
30
|
+
expect(
|
|
31
|
+
audienceForReader(channel, "C0ROOM", "U0READER"),
|
|
32
|
+
`${channel} has no way to show one reader a message in a shared room`,
|
|
33
|
+
).toBeUndefined();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("stays unrestricted when there is no reader to name", () => {
|
|
38
|
+
expect(audienceForReader("slack", "C0ROOM", undefined)).toBeUndefined();
|
|
39
|
+
expect(audienceForReader("slack", "C0ROOM", null)).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("tolerates an absent channel or room rather than throwing", () => {
|
|
43
|
+
// Several callers hold values that were never narrowed, so the answer for
|
|
44
|
+
// an unknown channel has to be the safe one rather than an exception.
|
|
45
|
+
expect(audienceForReader(undefined, "C0ROOM", "U0READER")).toBeUndefined();
|
|
46
|
+
expect(audienceForReader(null, "C0ROOM", "U0READER")).toBeUndefined();
|
|
47
|
+
expect(audienceForReader("slack", null, "U0READER")).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -17,9 +17,10 @@ describe("INTERFACE_IDS", () => {
|
|
|
17
17
|
).toBe(true);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
test("
|
|
20
|
+
test("includes desktop and existing interfaces", () => {
|
|
21
21
|
for (const id of [
|
|
22
22
|
"macos",
|
|
23
|
+
"windows",
|
|
23
24
|
"ios",
|
|
24
25
|
"cli",
|
|
25
26
|
"telegram",
|
|
@@ -41,8 +42,9 @@ describe("INTERACTIVE_INTERFACES", () => {
|
|
|
41
42
|
expect(INTERACTIVE_INTERFACES.has("chrome-extension" as never)).toBe(false);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
test("
|
|
45
|
+
test("includes desktop clients", () => {
|
|
45
46
|
expect(INTERACTIVE_INTERFACES.has("macos")).toBe(true);
|
|
47
|
+
expect(INTERACTIVE_INTERFACES.has("windows")).toBe(true);
|
|
46
48
|
});
|
|
47
49
|
});
|
|
48
50
|
|
|
@@ -51,8 +53,9 @@ describe("isInterfaceId", () => {
|
|
|
51
53
|
expect(isInterfaceId("chrome-extension")).toBe(true);
|
|
52
54
|
});
|
|
53
55
|
|
|
54
|
-
test("returns true for
|
|
56
|
+
test("returns true for desktop interfaces", () => {
|
|
55
57
|
expect(isInterfaceId("macos")).toBe(true);
|
|
58
|
+
expect(isInterfaceId("windows")).toBe(true);
|
|
56
59
|
});
|
|
57
60
|
|
|
58
61
|
test("returns false for unknown interface", () => {
|
|
@@ -68,6 +71,7 @@ describe("parseInterfaceId", () => {
|
|
|
68
71
|
test("returns canonical ID for valid interface", () => {
|
|
69
72
|
expect(parseInterfaceId("web")).toBe("web");
|
|
70
73
|
expect(parseInterfaceId("macos")).toBe("macos");
|
|
74
|
+
expect(parseInterfaceId("windows")).toBe("windows");
|
|
71
75
|
});
|
|
72
76
|
|
|
73
77
|
test("normalizes legacy 'vellum' alias to 'web'", () => {
|
|
@@ -103,6 +107,16 @@ describe("supportsHostProxy", () => {
|
|
|
103
107
|
expect(supportsHostProxy("macos", "host_browser")).toBe(true);
|
|
104
108
|
});
|
|
105
109
|
|
|
110
|
+
test("windows exposes its implemented host capabilities", () => {
|
|
111
|
+
expect(supportsHostProxy("windows")).toBe(true);
|
|
112
|
+
expect(supportsHostProxy("windows", "host_bash")).toBe(true);
|
|
113
|
+
expect(supportsHostProxy("windows", "host_file")).toBe(true);
|
|
114
|
+
expect(supportsHostProxy("windows", "host_cu")).toBe(true);
|
|
115
|
+
expect(supportsHostProxy("windows", "host_browser")).toBe(true);
|
|
116
|
+
expect(supportsHostProxy("windows", "host_ui_snapshot")).toBe(true);
|
|
117
|
+
expect(supportsHostProxy("windows", "host_app_control")).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
106
120
|
// ── chrome-extension: only host_browser. ──
|
|
107
121
|
test("chrome-extension returns false (no capability)", () => {
|
|
108
122
|
// Chrome extension does not support "any host proxy at all" — it only
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who a reply should be visible to, decided once for every producer.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { MessageAudience } from "@vellumai/gateway-client";
|
|
6
|
+
|
|
7
|
+
import { isSlackDmConversation } from "../messaging/providers/slack/message-metadata.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The audience a reply addressed at `userId` should carry, or `undefined` when
|
|
11
|
+
* the whole room may see it.
|
|
12
|
+
*
|
|
13
|
+
* Restricting a message only means something where someone else could read it.
|
|
14
|
+
* A room with one reader gets no restriction: a Slack DM is already private,
|
|
15
|
+
* and marking it restricted there costs durability, because an ephemeral
|
|
16
|
+
* message does not survive a reload.
|
|
17
|
+
*
|
|
18
|
+
* Slack is the only channel that can show one reader a message in a room it
|
|
19
|
+
* shares with others. The test is for a DM rather than for a channel id, so
|
|
20
|
+
* every other room shape counts as shared: a private group holds several
|
|
21
|
+
* readers even though its id begins with `G` rather than `C`.
|
|
22
|
+
*
|
|
23
|
+
* `sourceChannel` and `chatId` are loosely typed because the only questions
|
|
24
|
+
* asked of them are whether the channel is Slack and whether the room is a
|
|
25
|
+
* DM, and several callers hold values that were never narrowed. An unknown
|
|
26
|
+
* channel answers with no restriction rather than throwing.
|
|
27
|
+
*/
|
|
28
|
+
export function audienceForReader(
|
|
29
|
+
sourceChannel: string | null | undefined,
|
|
30
|
+
chatId: string | null | undefined,
|
|
31
|
+
userId: string | null | undefined,
|
|
32
|
+
): MessageAudience | undefined {
|
|
33
|
+
if (sourceChannel !== "slack" || !userId || !chatId) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
if (isSlackDmConversation(chatId)) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return { kind: "oneReader", userId };
|
|
40
|
+
}
|
package/src/channels/types.ts
CHANGED
|
@@ -166,6 +166,7 @@ export const CHANNEL_METADATA: Partial<Record<ChannelId, ChannelInfo>> = {
|
|
|
166
166
|
|
|
167
167
|
export const INTERFACE_IDS = [
|
|
168
168
|
"macos",
|
|
169
|
+
"windows",
|
|
169
170
|
"ios",
|
|
170
171
|
"cli",
|
|
171
172
|
"telegram",
|
|
@@ -264,6 +265,7 @@ export function parseClientOs(value: unknown): ClientOs | null {
|
|
|
264
265
|
*/
|
|
265
266
|
export const INTERACTIVE_INTERFACES: ReadonlySet<InterfaceId> = new Set([
|
|
266
267
|
"macos",
|
|
268
|
+
"windows",
|
|
267
269
|
"ios",
|
|
268
270
|
"cli",
|
|
269
271
|
"web",
|
|
@@ -274,9 +276,9 @@ export function isInteractiveInterface(id: InterfaceId): boolean {
|
|
|
274
276
|
}
|
|
275
277
|
|
|
276
278
|
/**
|
|
277
|
-
* Host proxy capabilities that an interface can support.
|
|
278
|
-
*
|
|
279
|
-
* host_browser
|
|
279
|
+
* Host proxy capabilities that an interface can support. macOS supports all
|
|
280
|
+
* of them, Windows withholds app control, and chrome-extension supports only
|
|
281
|
+
* host_browser through the Chrome DevTools Protocol proxy.
|
|
280
282
|
*/
|
|
281
283
|
export const HOST_PROXY_CAPABILITIES = [
|
|
282
284
|
"host_bash",
|
|
@@ -290,29 +292,28 @@ export const HOST_PROXY_CAPABILITIES = [
|
|
|
290
292
|
export type HostProxyCapability = (typeof HOST_PROXY_CAPABILITIES)[number];
|
|
291
293
|
|
|
292
294
|
/**
|
|
293
|
-
* Interfaces that support
|
|
294
|
-
*
|
|
295
|
-
* by the discriminated transport metadata union and by the
|
|
295
|
+
* Interfaces that support desktop host-proxy tools. This identity is used by
|
|
296
|
+
* the discriminated transport metadata union and by the
|
|
296
297
|
* `supportsHostProxy(id)` type predicate.
|
|
297
298
|
*
|
|
298
299
|
* Extend this literal type AND the `supportsHostProxy` implementation
|
|
299
|
-
* below in lock-step when adding a new host-capable client
|
|
300
|
-
* Linux
|
|
300
|
+
* below in lock-step when adding a new host-capable client such as native
|
|
301
|
+
* Linux.
|
|
301
302
|
*/
|
|
302
|
-
export type HostProxyInterfaceId = "macos";
|
|
303
|
+
export type HostProxyInterfaceId = "macos" | "windows";
|
|
303
304
|
|
|
304
305
|
/**
|
|
305
306
|
* Whether the interface supports a host proxy capability.
|
|
306
307
|
*
|
|
307
308
|
* The no-arg form `supportsHostProxy(id)` asks "is this interface a desktop
|
|
308
|
-
* host-proxy client?"
|
|
309
|
+
* host-proxy client?" It returns `true` for native desktop clients and is the
|
|
309
310
|
* predicate that narrows `InterfaceId` to `HostProxyInterfaceId`. It returns
|
|
310
311
|
* `false` for chrome-extension because chrome-extension only supports
|
|
311
312
|
* `host_browser`, and the no-arg form is the gate that legacy desktop-only
|
|
312
313
|
* call sites use (e.g. preactivating computer-use, restoring host proxies
|
|
313
|
-
* in the drain queue). Callers that want to check a single capability
|
|
314
|
+
* in the drain queue). Callers that want to check a single capability,
|
|
314
315
|
* for example, to decide whether to keep `hostBrowserProxy` available for
|
|
315
|
-
* chrome-extension
|
|
316
|
+
* chrome-extension, should pass the capability explicitly:
|
|
316
317
|
* `supportsHostProxy(id, "host_browser")`.
|
|
317
318
|
*/
|
|
318
319
|
export function supportsHostProxy(id: InterfaceId): id is HostProxyInterfaceId;
|
|
@@ -331,6 +332,9 @@ export function supportsHostProxy(
|
|
|
331
332
|
if (id === "macos") {
|
|
332
333
|
return true;
|
|
333
334
|
}
|
|
335
|
+
if (id === "windows") {
|
|
336
|
+
return capability == null || capability !== "host_app_control";
|
|
337
|
+
}
|
|
334
338
|
if (id === "chrome-extension" && capability === "host_browser") {
|
|
335
339
|
return true;
|
|
336
340
|
}
|