@vellumai/assistant 0.11.4 → 0.11.5-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/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__/config-loader-backfill.test.ts +19 -10
- 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__/managed-profile-guard.test.ts +4 -2
- 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/__tests__/voice-scoped-grant-consumer.test.ts +2 -0
- package/src/__tests__/voice-session-bridge.test.ts +42 -0
- 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/calls/__tests__/voice-session-bridge.test.ts +114 -0
- package/src/calls/voice-session-bridge.ts +60 -6
- 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/__tests__/balanced-model-experiment.test.ts +7 -7
- package/src/config/__tests__/default-profile-catalog.test.ts +3 -3
- package/src/config/default-profile-catalog.ts +5 -6
- package/src/config/feature-flag-registry.json +32 -32
- 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
|
@@ -18,6 +18,9 @@ const slack = {
|
|
|
18
18
|
sendSlackStreamOp: mock((..._args: unknown[]) =>
|
|
19
19
|
Promise.resolve({ ok: true, ts: "stream-ts" }),
|
|
20
20
|
),
|
|
21
|
+
updateSlackMessage: mock((..._args: unknown[]) =>
|
|
22
|
+
Promise.resolve({ ts: "slack-ts" }),
|
|
23
|
+
),
|
|
21
24
|
};
|
|
22
25
|
const telegram = {
|
|
23
26
|
sendTelegramReply: mock((..._args: unknown[]) => Promise.resolve()),
|
|
@@ -57,8 +60,17 @@ mock.module("../../../util/logger.js", () => ({
|
|
|
57
60
|
getLogger: () => ({ debug() {}, info() {}, warn() {}, error() {} }),
|
|
58
61
|
}));
|
|
59
62
|
|
|
60
|
-
const {
|
|
61
|
-
|
|
63
|
+
const {
|
|
64
|
+
deliverDirect,
|
|
65
|
+
editChannelMessage,
|
|
66
|
+
sendChannelReaction,
|
|
67
|
+
sendChannelStreamOp,
|
|
68
|
+
sendChannelTyping,
|
|
69
|
+
setChannelThreadStatus,
|
|
70
|
+
supportsChannelTyping,
|
|
71
|
+
isDirectDelivery,
|
|
72
|
+
getTransportForCallback,
|
|
73
|
+
} = await import("../index.js");
|
|
62
74
|
|
|
63
75
|
const BASE = "https://gateway.internal";
|
|
64
76
|
|
|
@@ -134,56 +146,70 @@ describe("Slack sub-operation selection", () => {
|
|
|
134
146
|
expect(opts.threadTs).toBe("1700.9");
|
|
135
147
|
});
|
|
136
148
|
|
|
137
|
-
test("
|
|
138
|
-
await
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
},
|
|
146
|
-
}),
|
|
147
|
-
);
|
|
149
|
+
test("sendChannelReaction reaches Slack without touching the text path", async () => {
|
|
150
|
+
await sendChannelReaction(`${BASE}/deliver/slack`, {
|
|
151
|
+
chatId: "C1",
|
|
152
|
+
messageId: "1700.5",
|
|
153
|
+
emoji: "white_check_mark",
|
|
154
|
+
action: "add",
|
|
155
|
+
});
|
|
156
|
+
|
|
148
157
|
expect(slack.sendSlackReaction).toHaveBeenCalledTimes(1);
|
|
149
158
|
expect(slack.sendSlackReply).not.toHaveBeenCalled();
|
|
150
159
|
});
|
|
151
160
|
|
|
152
|
-
test("
|
|
153
|
-
await
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
);
|
|
161
|
+
test("editChannelMessage updates in place instead of posting", async () => {
|
|
162
|
+
await editChannelMessage(`${BASE}/deliver/slack`, {
|
|
163
|
+
chatId: "C1",
|
|
164
|
+
messageId: "1700.5",
|
|
165
|
+
text: "revised",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(slack.updateSlackMessage).toHaveBeenCalledTimes(1);
|
|
169
|
+
// The distinction the split exists for: an edit never reaches the post
|
|
170
|
+
// path, so a failed edit cannot become a second visible message.
|
|
171
|
+
expect(slack.sendSlackReply).not.toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("setChannelThreadStatus reaches Slack without touching the text path", async () => {
|
|
175
|
+
await setChannelThreadStatus(`${BASE}/deliver/slack`, {
|
|
176
|
+
chatId: "C1",
|
|
177
|
+
threadTs: "1700.5",
|
|
178
|
+
status: "is thinking",
|
|
179
|
+
});
|
|
180
|
+
|
|
163
181
|
expect(slack.sendSlackAssistantThreadStatus).toHaveBeenCalledTimes(1);
|
|
164
182
|
expect(slack.sendSlackReply).not.toHaveBeenCalled();
|
|
165
183
|
});
|
|
166
184
|
|
|
167
|
-
test("a
|
|
168
|
-
await
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
185
|
+
test("a channel with no status surface resolves quietly", async () => {
|
|
186
|
+
const result = await setChannelThreadStatus(`${BASE}/deliver/telegram`, {
|
|
187
|
+
chatId: "123",
|
|
188
|
+
threadTs: "1700.5",
|
|
189
|
+
status: "is thinking",
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(result).toEqual({ ok: true });
|
|
193
|
+
expect(telegram.sendTelegramReply).not.toHaveBeenCalled();
|
|
173
194
|
});
|
|
174
195
|
|
|
175
|
-
test("
|
|
176
|
-
const result = await
|
|
196
|
+
test("sendChannelTyping resolves quietly for a channel with no typing capability", async () => {
|
|
197
|
+
const result = await sendChannelTyping(`${BASE}/deliver/slack`, "C1");
|
|
198
|
+
|
|
199
|
+
expect(result).toEqual({ ok: true });
|
|
200
|
+
expect(slack.sendSlackReply).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("sendChannelStreamOp reaches Slack without touching the text path", async () => {
|
|
204
|
+
const result = await sendChannelStreamOp(
|
|
177
205
|
`${BASE}/deliver/slack?threadTs=1700.5`,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
slackStream: { action: "start", threadTs: "1700.5" },
|
|
181
|
-
}),
|
|
206
|
+
"C1",
|
|
207
|
+
{ action: "start", threadTs: "1700.5", markdownText: "hi" },
|
|
182
208
|
);
|
|
183
209
|
expect(slack.sendSlackStreamOp).toHaveBeenCalledTimes(1);
|
|
184
210
|
expect(slack.sendSlackStreamOp.mock.calls[0]).toEqual([
|
|
185
211
|
"C1",
|
|
186
|
-
{ action: "start", threadTs: "1700.5" },
|
|
212
|
+
{ action: "start", threadTs: "1700.5", markdownText: "hi" },
|
|
187
213
|
]);
|
|
188
214
|
expect(slack.sendSlackReply).not.toHaveBeenCalled();
|
|
189
215
|
expect(result).toEqual({ ok: true, ts: "stream-ts" });
|
|
@@ -191,22 +217,49 @@ describe("Slack sub-operation selection", () => {
|
|
|
191
217
|
});
|
|
192
218
|
|
|
193
219
|
describe("capability gating across channels", () => {
|
|
194
|
-
test("a
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
220
|
+
test("a channel with no reaction capability resolves quietly", async () => {
|
|
221
|
+
// Slack is the only channel that implements it, because the only producer
|
|
222
|
+
// is Slack's own acknowledgement fallback. A channel without the method
|
|
223
|
+
// is not a failed delivery, so nothing is attempted and nothing throws.
|
|
224
|
+
const target = {
|
|
225
|
+
chatId: "C1",
|
|
226
|
+
messageId: "1",
|
|
227
|
+
emoji: "eyes",
|
|
228
|
+
action: "add",
|
|
229
|
+
} as const;
|
|
230
|
+
expect(
|
|
231
|
+
await sendChannelReaction(`${BASE}/deliver/telegram`, target),
|
|
232
|
+
).toEqual({ ok: true });
|
|
233
|
+
expect(telegram.sendTelegramReply).not.toHaveBeenCalled();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("a channel that cannot revise a sent message resolves quietly", async () => {
|
|
237
|
+
// Only Slack implements `edit` today. A channel without the method is not
|
|
238
|
+
// a failed delivery: nothing is attempted, nothing throws, and no fresh
|
|
239
|
+
// message is posted in place of the revision.
|
|
240
|
+
expect(
|
|
241
|
+
await editChannelMessage(`${BASE}/deliver/telegram`, {
|
|
242
|
+
chatId: "C1",
|
|
243
|
+
messageId: "1",
|
|
244
|
+
text: "revised",
|
|
200
245
|
}),
|
|
201
|
-
);
|
|
202
|
-
expect(telegram.sendTelegramReply).
|
|
246
|
+
).toEqual({ ok: true });
|
|
247
|
+
expect(telegram.sendTelegramReply).not.toHaveBeenCalled();
|
|
203
248
|
});
|
|
204
249
|
|
|
205
|
-
test("typing
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
);
|
|
250
|
+
test("the typing capability is read from the transport, not the channel name", () => {
|
|
251
|
+
// The heartbeat gate in background-dispatch asks this rather than testing
|
|
252
|
+
// `sourceChannel === "telegram"`, so a channel that implements the method
|
|
253
|
+
// starts showing an indicator without a caller being changed.
|
|
254
|
+
expect(supportsChannelTyping(`${BASE}/deliver/telegram`)).toBe(true);
|
|
255
|
+
expect(supportsChannelTyping(`${BASE}/deliver/discord`)).toBe(true);
|
|
256
|
+
expect(supportsChannelTyping(`${BASE}/deliver/slack`)).toBe(false);
|
|
257
|
+
expect(supportsChannelTyping(`${BASE}/deliver/whatsapp`)).toBe(false);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("sendChannelTyping reaches Telegram's typing indicator", async () => {
|
|
261
|
+
await sendChannelTyping(`${BASE}/deliver/telegram`, "123");
|
|
262
|
+
|
|
210
263
|
expect(telegram.sendTelegramTypingIndicator).toHaveBeenCalledTimes(1);
|
|
211
264
|
});
|
|
212
265
|
|
|
@@ -244,26 +297,21 @@ describe("capability gating across channels", () => {
|
|
|
244
297
|
});
|
|
245
298
|
});
|
|
246
299
|
|
|
247
|
-
test("
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
await deliverDirect(
|
|
252
|
-
`${BASE}/deliver/discord`,
|
|
253
|
-
payload({ chatAction: "typing", text: "hi" }),
|
|
254
|
-
);
|
|
255
|
-
expect(discord.sendDiscordReply).toHaveBeenCalledTimes(1);
|
|
300
|
+
test("sendChannelTyping reaches Discord's typing indicator", async () => {
|
|
301
|
+
await sendChannelTyping(`${BASE}/deliver/discord`, "999");
|
|
302
|
+
|
|
303
|
+
expect(discord.sendDiscordTypingIndicator).toHaveBeenCalledTimes(1);
|
|
256
304
|
});
|
|
257
305
|
|
|
258
|
-
test("a
|
|
259
|
-
await
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
);
|
|
266
|
-
expect(discord.sendDiscordReply).
|
|
306
|
+
test("a channel that cannot stream resolves quietly, and posts nothing", async () => {
|
|
307
|
+
const result = await sendChannelStreamOp(`${BASE}/deliver/discord`, "999", {
|
|
308
|
+
action: "start",
|
|
309
|
+
threadTs: "1700.5",
|
|
310
|
+
markdownText: "hi",
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
expect(result).toEqual({ ok: true });
|
|
314
|
+
expect(discord.sendDiscordReply).not.toHaveBeenCalled();
|
|
267
315
|
});
|
|
268
316
|
});
|
|
269
317
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { KnownBlock } from "@slack/types";
|
|
1
2
|
import type {
|
|
2
3
|
ChannelDeliveryResult,
|
|
3
4
|
ChannelReplyPayload,
|
|
5
|
+
SlackStreamOp,
|
|
4
6
|
} from "@vellumai/gateway-client";
|
|
5
7
|
|
|
6
8
|
import type { ChannelId } from "../../channels/types.js";
|
|
@@ -15,15 +17,52 @@ export interface CallbackContext {
|
|
|
15
17
|
readonly params: Readonly<Record<string, string>>;
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
/** The message an emoji reaction lands on, and what to do there. */
|
|
21
|
+
export interface ReactionTarget {
|
|
22
|
+
readonly chatId: string;
|
|
23
|
+
/** The target message, in the channel's own id space. */
|
|
24
|
+
readonly messageId: string;
|
|
25
|
+
readonly emoji: string;
|
|
26
|
+
readonly action: "add" | "remove";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A status surface update. `chatId` is the room, spelled the way every other
|
|
31
|
+
* method on this interface spells it.
|
|
32
|
+
*/
|
|
33
|
+
export interface ThreadStatus {
|
|
34
|
+
readonly chatId: string;
|
|
35
|
+
readonly threadTs: string;
|
|
36
|
+
readonly status: string;
|
|
37
|
+
/** Rotating hints shown under the status while it holds. */
|
|
38
|
+
readonly loadingMessages?: readonly string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* An existing message to replace in place, and what to replace it with.
|
|
43
|
+
*
|
|
44
|
+
* `messageId` is the target in the channel's own id space, the same way
|
|
45
|
+
* `chatId` is. `blocks` and `useBlocks` are the rendering inputs the edit
|
|
46
|
+
* carries, so a replacement renders the way the original did rather than
|
|
47
|
+
* degrading to plain text.
|
|
48
|
+
*/
|
|
49
|
+
export interface EditTarget {
|
|
50
|
+
readonly chatId: string;
|
|
51
|
+
readonly messageId: string;
|
|
52
|
+
readonly text: string;
|
|
53
|
+
readonly blocks?: readonly KnownBlock[];
|
|
54
|
+
readonly useBlocks?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
18
57
|
/**
|
|
19
58
|
* Direct outbound delivery for one channel, wrapping the channel's provider-API
|
|
20
59
|
* send functions behind a uniform surface. Transports are registered statically
|
|
21
60
|
* (delivery runs in non-daemon contexts) and dispatched by channel, resolved
|
|
22
61
|
* from the gateway callback URL via `callback-routing.ts`.
|
|
23
62
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
63
|
+
* Each operation takes its own parameters and is reached through its own entry
|
|
64
|
+
* point; a transport implements only the operations its channel supports, so
|
|
65
|
+
* an absent method is an absent capability.
|
|
27
66
|
*/
|
|
28
67
|
export interface ChannelTransport {
|
|
29
68
|
/** Canonical source channel id, e.g. `"slack"`. */
|
|
@@ -35,27 +74,65 @@ export interface ChannelTransport {
|
|
|
35
74
|
payload: ChannelReplyPayload,
|
|
36
75
|
): Promise<ChannelDeliveryResult>;
|
|
37
76
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Replace a message the assistant already sent.
|
|
79
|
+
*
|
|
80
|
+
* Distinct from `deliver`, which always posts something new. A channel that
|
|
81
|
+
* cannot revise a sent message omits this, and a caller that needs the
|
|
82
|
+
* revision to be visible has to post instead of silently doing nothing.
|
|
83
|
+
*
|
|
84
|
+
* An implementation must not fall back to posting when the edit fails. The
|
|
85
|
+
* original would remain beside the replacement, which reads as the assistant
|
|
86
|
+
* answering twice.
|
|
87
|
+
*/
|
|
88
|
+
edit?(
|
|
40
89
|
ctx: CallbackContext,
|
|
41
|
-
|
|
90
|
+
target: EditTarget,
|
|
42
91
|
): Promise<ChannelDeliveryResult>;
|
|
43
92
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Show that the assistant is working, in whatever form the channel has.
|
|
95
|
+
*
|
|
96
|
+
* Takes the place and nothing else, because that is all it needs. A channel
|
|
97
|
+
* without the affordance omits the method, so implementing it is the whole
|
|
98
|
+
* of declaring the capability.
|
|
99
|
+
*/
|
|
100
|
+
typing?(ctx: CallbackContext, chatId: string): Promise<ChannelDeliveryResult>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Add or remove one of the assistant's own emoji reactions on a message.
|
|
104
|
+
*
|
|
105
|
+
* `messageId` is the target's id in the channel's own space, the same way
|
|
106
|
+
* `chatId` is: Slack spells it as a timestamp, Discord and Telegram as ids.
|
|
107
|
+
* Nothing outside the channel reads it, so nothing outside needs a shared
|
|
108
|
+
* spelling for it.
|
|
109
|
+
*/
|
|
110
|
+
react?(
|
|
46
111
|
ctx: CallbackContext,
|
|
47
|
-
|
|
112
|
+
target: ReactionTarget,
|
|
48
113
|
): Promise<ChannelDeliveryResult>;
|
|
49
114
|
|
|
50
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Set or clear the channel's own "working on it" surface.
|
|
117
|
+
*
|
|
118
|
+
* Distinct from `typing`: an indicator a channel refreshes on a timer versus
|
|
119
|
+
* a status a channel holds until it is changed. Slack has both and uses this
|
|
120
|
+
* one; a channel with only the first implements only `typing`.
|
|
121
|
+
*/
|
|
51
122
|
setThreadStatus?(
|
|
52
123
|
ctx: CallbackContext,
|
|
53
|
-
|
|
124
|
+
status: ThreadStatus,
|
|
54
125
|
): Promise<ChannelDeliveryResult>;
|
|
55
126
|
|
|
56
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* Advance a streamed reply: open it, add to it, or close it.
|
|
129
|
+
*
|
|
130
|
+
* A channel that can only post a finished message omits this, and the
|
|
131
|
+
* caller falls back to delivering the reply whole.
|
|
132
|
+
*/
|
|
57
133
|
streamReply?(
|
|
58
134
|
ctx: CallbackContext,
|
|
59
|
-
|
|
135
|
+
chatId: string,
|
|
136
|
+
op: SlackStreamOp,
|
|
60
137
|
): Promise<ChannelDeliveryResult>;
|
|
61
138
|
}
|
|
@@ -210,3 +210,19 @@ export async function sendDiscordAttachments(
|
|
|
210
210
|
totalCount: attachments.length,
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Show the Discord typing indicator in a channel.
|
|
216
|
+
*
|
|
217
|
+
* Discord expires it after about ten seconds, so a caller that wants it to
|
|
218
|
+
* persist re-sends on a timer rather than clearing it; there is no stop route.
|
|
219
|
+
*/
|
|
220
|
+
export async function sendDiscordTypingIndicator(
|
|
221
|
+
target: DiscordSendTarget,
|
|
222
|
+
): Promise<void> {
|
|
223
|
+
await callDiscordApi(
|
|
224
|
+
"POST",
|
|
225
|
+
`/channels/${encodeURIComponent(target.channelId)}/typing`,
|
|
226
|
+
{},
|
|
227
|
+
);
|
|
228
|
+
}
|
|
@@ -7,7 +7,11 @@ import type {
|
|
|
7
7
|
} from "../channel-transport.js";
|
|
8
8
|
import { openDiscordDmChannel } from "./api.js";
|
|
9
9
|
import type { DiscordSendTarget } from "./send.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
sendDiscordAttachments,
|
|
12
|
+
sendDiscordReply,
|
|
13
|
+
sendDiscordTypingIndicator,
|
|
14
|
+
} from "./send.js";
|
|
11
15
|
|
|
12
16
|
const log = getLogger("discord-transport");
|
|
13
17
|
|
|
@@ -43,6 +47,12 @@ async function sendTarget(
|
|
|
43
47
|
export const discordTransport: ChannelTransport = {
|
|
44
48
|
channel: "discord",
|
|
45
49
|
|
|
50
|
+
async typing(ctx, chatId) {
|
|
51
|
+
await sendDiscordTypingIndicator(await sendTarget(ctx, chatId));
|
|
52
|
+
log.debug({ chatId }, "Discord typing indicator delivered (direct)");
|
|
53
|
+
return { ok: true };
|
|
54
|
+
},
|
|
55
|
+
|
|
46
56
|
async deliver(ctx, payload) {
|
|
47
57
|
const { chatId, text, attachments, approval } = payload;
|
|
48
58
|
const target = await sendTarget(ctx, chatId);
|
|
@@ -11,12 +11,19 @@
|
|
|
11
11
|
import type {
|
|
12
12
|
ChannelDeliveryResult,
|
|
13
13
|
ChannelReplyPayload,
|
|
14
|
+
SlackStreamOp,
|
|
14
15
|
} from "@vellumai/gateway-client";
|
|
15
16
|
|
|
16
17
|
import { a2aTransport } from "./a2a/transport.js";
|
|
17
18
|
import type { DirectDeliveryChannel } from "./callback-routing.js";
|
|
18
19
|
import { channelForCallback } from "./callback-routing.js";
|
|
19
|
-
import type {
|
|
20
|
+
import type {
|
|
21
|
+
CallbackContext,
|
|
22
|
+
ChannelTransport,
|
|
23
|
+
EditTarget,
|
|
24
|
+
ReactionTarget,
|
|
25
|
+
ThreadStatus,
|
|
26
|
+
} from "./channel-transport.js";
|
|
20
27
|
import { discordTransport } from "./discord/transport.js";
|
|
21
28
|
import { slackTransport } from "./slack/transport.js";
|
|
22
29
|
import { telegramTransport } from "./telegram-bot/transport.js";
|
|
@@ -45,6 +52,105 @@ export function getTransportForCallback(
|
|
|
45
52
|
return channel ? TRANSPORTS[channel] : undefined;
|
|
46
53
|
}
|
|
47
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Whether the channel this callback addresses can show a working indicator.
|
|
57
|
+
*
|
|
58
|
+
* Asks the transport rather than the channel id, so a channel that gains the
|
|
59
|
+
* method starts being asked without a caller being told about it.
|
|
60
|
+
*/
|
|
61
|
+
export function supportsChannelTyping(callbackUrl: string): boolean {
|
|
62
|
+
return getTransportForCallback(callbackUrl)?.typing !== undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Show that the assistant is working on the channel this callback addresses.
|
|
67
|
+
*
|
|
68
|
+
* Resolves to nothing when the channel has no such affordance, which is the
|
|
69
|
+
* ordinary case rather than a failure: the indicator is decoration, and a
|
|
70
|
+
* channel that cannot show one is not degraded by its absence.
|
|
71
|
+
*/
|
|
72
|
+
export async function sendChannelTyping(
|
|
73
|
+
callbackUrl: string,
|
|
74
|
+
chatId: string,
|
|
75
|
+
): Promise<ChannelDeliveryResult> {
|
|
76
|
+
const transport = getTransportForCallback(callbackUrl);
|
|
77
|
+
if (!transport?.typing) {
|
|
78
|
+
return { ok: true };
|
|
79
|
+
}
|
|
80
|
+
return transport.typing(callbackContext(callbackUrl), chatId);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Add or remove one of the assistant's own reactions on a message.
|
|
85
|
+
*
|
|
86
|
+
* Resolves to nothing when the channel has none, the same as typing: a
|
|
87
|
+
* reaction is an acknowledgement, and a channel that cannot show one is not a
|
|
88
|
+
* failed delivery.
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* Replace a message the assistant already sent.
|
|
92
|
+
*
|
|
93
|
+
* Returns `ok` without acting when the channel cannot revise a sent message,
|
|
94
|
+
* matching the other capability entry points: an absent method is an absent
|
|
95
|
+
* capability, not an error.
|
|
96
|
+
*/
|
|
97
|
+
export async function editChannelMessage(
|
|
98
|
+
callbackUrl: string,
|
|
99
|
+
target: EditTarget,
|
|
100
|
+
): Promise<ChannelDeliveryResult> {
|
|
101
|
+
const transport = getTransportForCallback(callbackUrl);
|
|
102
|
+
if (!transport?.edit) {
|
|
103
|
+
return { ok: true };
|
|
104
|
+
}
|
|
105
|
+
return transport.edit(callbackContext(callbackUrl), target);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function sendChannelReaction(
|
|
109
|
+
callbackUrl: string,
|
|
110
|
+
target: ReactionTarget,
|
|
111
|
+
): Promise<ChannelDeliveryResult> {
|
|
112
|
+
const transport = getTransportForCallback(callbackUrl);
|
|
113
|
+
if (!transport?.react) {
|
|
114
|
+
return { ok: true };
|
|
115
|
+
}
|
|
116
|
+
return transport.react(callbackContext(callbackUrl), target);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Set or clear the channel's status surface.
|
|
121
|
+
*
|
|
122
|
+
* Resolves to nothing when the channel holds none, so a caller does not have
|
|
123
|
+
* to know which channels do.
|
|
124
|
+
*/
|
|
125
|
+
export async function setChannelThreadStatus(
|
|
126
|
+
callbackUrl: string,
|
|
127
|
+
status: ThreadStatus,
|
|
128
|
+
): Promise<ChannelDeliveryResult> {
|
|
129
|
+
const transport = getTransportForCallback(callbackUrl);
|
|
130
|
+
if (!transport?.setThreadStatus) {
|
|
131
|
+
return { ok: true };
|
|
132
|
+
}
|
|
133
|
+
return transport.setThreadStatus(callbackContext(callbackUrl), status);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Advance a streamed reply on the channel this callback addresses.
|
|
138
|
+
*
|
|
139
|
+
* Resolves to nothing when the channel cannot stream, so a caller that wants a
|
|
140
|
+
* streamed reply learns it has to send the whole thing instead.
|
|
141
|
+
*/
|
|
142
|
+
export async function sendChannelStreamOp(
|
|
143
|
+
callbackUrl: string,
|
|
144
|
+
chatId: string,
|
|
145
|
+
op: SlackStreamOp,
|
|
146
|
+
): Promise<ChannelDeliveryResult> {
|
|
147
|
+
const transport = getTransportForCallback(callbackUrl);
|
|
148
|
+
if (!transport?.streamReply) {
|
|
149
|
+
return { ok: true };
|
|
150
|
+
}
|
|
151
|
+
return transport.streamReply(callbackContext(callbackUrl), chatId, op);
|
|
152
|
+
}
|
|
153
|
+
|
|
48
154
|
function callbackContext(callbackUrl: string): CallbackContext {
|
|
49
155
|
const params: Record<string, string> = {};
|
|
50
156
|
try {
|
|
@@ -74,9 +180,8 @@ export function isDirectDelivery(callbackUrl: string): boolean {
|
|
|
74
180
|
* Deliver a channel reply directly to the provider API, bypassing the gateway
|
|
75
181
|
* HTTP proxy. Callers MUST check `isDirectDelivery()` first.
|
|
76
182
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* otherwise the reply is delivered as text / approval / attachments.
|
|
183
|
+
* Delivers the reply itself: text, approval card, attachments. Every other
|
|
184
|
+
* operation a transport supports is reached through its own entry point.
|
|
80
185
|
*/
|
|
81
186
|
export async function deliverDirect(
|
|
82
187
|
callbackUrl: string,
|
|
@@ -90,17 +195,5 @@ export async function deliverDirect(
|
|
|
90
195
|
}
|
|
91
196
|
|
|
92
197
|
const ctx = callbackContext(callbackUrl);
|
|
93
|
-
if (payload.slackStream && transport.streamReply) {
|
|
94
|
-
return transport.streamReply(ctx, payload);
|
|
95
|
-
}
|
|
96
|
-
if (payload.reaction && transport.sendReaction) {
|
|
97
|
-
return transport.sendReaction(ctx, payload);
|
|
98
|
-
}
|
|
99
|
-
if (payload.assistantThreadStatus && transport.setThreadStatus) {
|
|
100
|
-
return transport.setThreadStatus(ctx, payload);
|
|
101
|
-
}
|
|
102
|
-
if (payload.chatAction === "typing" && transport.sendTyping) {
|
|
103
|
-
return transport.sendTyping(ctx, payload);
|
|
104
|
-
}
|
|
105
198
|
return transport.deliver(ctx, payload);
|
|
106
199
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
import type { ProviderMessageMetadata } from "../../provider-message-metadata.js";
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Typed Slack message metadata stored flat in the `messages.metadata` column
|
|
5
7
|
* alongside whatever other top-level keys the broader metadata envelope
|
|
@@ -288,6 +290,49 @@ export function readSlackMetadataFromMessageMetadata(
|
|
|
288
290
|
return opts?.allowFlatLegacy ? readSlackMetadata(metadata) : null;
|
|
289
291
|
}
|
|
290
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Present a Slack row's stored metadata as the channel-neutral shape.
|
|
295
|
+
*
|
|
296
|
+
* `channelTs` is the row's own provider id and `targetChannelTs` the id of the
|
|
297
|
+
* message a reaction was attached to. Both are Slack `ts` strings, which is a
|
|
298
|
+
* Slack detail of the id format, not of the fact.
|
|
299
|
+
*
|
|
300
|
+
* The rest of Slack's envelope (file markers, timezone labels, channel name)
|
|
301
|
+
* stays there and is read from there by the Slack transcript renderer, which
|
|
302
|
+
* is a provider renderer by design.
|
|
303
|
+
*/
|
|
304
|
+
export function slackMetadataAsProviderMetadata(
|
|
305
|
+
meta: SlackMessageMetadata,
|
|
306
|
+
): ProviderMessageMetadata {
|
|
307
|
+
return {
|
|
308
|
+
source: "slack",
|
|
309
|
+
conversationExternalId: meta.channelId,
|
|
310
|
+
// A reaction row stores the reacted message's ts in `channelTs`, which is
|
|
311
|
+
// its target rather than an id of its own.
|
|
312
|
+
...(meta.eventKind === "reaction" ? {} : { messageId: meta.channelTs }),
|
|
313
|
+
...(meta.actorExternalUserId
|
|
314
|
+
? { actorExternalId: meta.actorExternalUserId }
|
|
315
|
+
: {}),
|
|
316
|
+
...(meta.threadTs ? { threadId: meta.threadTs } : {}),
|
|
317
|
+
...(meta.displayName ? { displayName: meta.displayName } : {}),
|
|
318
|
+
eventKind: meta.eventKind,
|
|
319
|
+
...(meta.reaction
|
|
320
|
+
? {
|
|
321
|
+
reaction: {
|
|
322
|
+
targetMessageId: meta.reaction.targetChannelTs,
|
|
323
|
+
emoji: meta.reaction.emoji,
|
|
324
|
+
op: meta.reaction.op,
|
|
325
|
+
...(meta.reaction.actorDisplayName
|
|
326
|
+
? { actorDisplayName: meta.reaction.actorDisplayName }
|
|
327
|
+
: {}),
|
|
328
|
+
},
|
|
329
|
+
}
|
|
330
|
+
: {}),
|
|
331
|
+
...(meta.editedAt !== undefined ? { editedAt: meta.editedAt } : {}),
|
|
332
|
+
...(meta.deletedAt !== undefined ? { deletedAt: meta.deletedAt } : {}),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
291
336
|
/**
|
|
292
337
|
* Serialize `SlackMessageMetadata` to a JSON string suitable for a fresh
|
|
293
338
|
* write to the `messages.metadata` column. Use `mergeSlackMetadata` when an
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* `{role, content}[]` chronologically ordered with compact Slack tags so the
|
|
7
7
|
* model can reason across sibling threads in one channel.
|
|
8
8
|
*
|
|
9
|
-
* The function is pure: no I/O, no implicit clock reads.
|
|
10
|
-
* `opts.now` only when needed for relative formatting. Sort and tag rendering
|
|
9
|
+
* The function is pure: no I/O, no implicit clock reads. Sort and tag rendering
|
|
11
10
|
* are deterministic.
|
|
12
11
|
*
|
|
13
12
|
* Consumers wire this into inbound history rendering and the compaction
|
|
@@ -63,8 +62,6 @@ export interface RenderableSlackMessage {
|
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
export interface RenderOptions {
|
|
66
|
-
/** Reserved for future relative-time rendering; currently unused. */
|
|
67
|
-
now?: Date;
|
|
68
65
|
/** Cap rendered reactions per parent message; default 5. */
|
|
69
66
|
maxReactionsPerMessage?: number;
|
|
70
67
|
}
|