@vellumai/assistant 0.8.12 → 0.9.0-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 +0 -14
- package/ARCHITECTURE.md +45 -45
- package/README.md +1 -1
- package/bun.lock +200 -154
- package/docs/architecture/integrations.md +3 -3
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/security.md +10 -10
- package/docs/runbook-trusted-contacts.md +12 -12
- package/docs/skills.md +6 -6
- package/docs/workflows-testing.md +221 -0
- package/docs/workflows.md +510 -0
- package/examples/plugins/echo/README.md +5 -5
- package/knip.json +2 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
- package/openapi.yaml +7197 -5708
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +66 -114
- package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
- package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
- package/src/__tests__/app-compiler.test.ts +7 -1
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/app-executors.test.ts +43 -0
- package/src/__tests__/approval-cascade.test.ts +0 -5
- package/src/__tests__/approval-routes-http.test.ts +91 -0
- package/src/__tests__/assistant-stream-state.test.ts +107 -0
- package/src/__tests__/browser-fill-credential.test.ts +3 -3
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +63 -7
- package/src/__tests__/compaction-trail-store.test.ts +74 -1
- package/src/__tests__/compaction.benchmark.test.ts +63 -41
- package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -5
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
- package/src/__tests__/conversation-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
- package/src/__tests__/conversation-speed-override.test.ts +0 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
- package/src/__tests__/credential-broker-server-use.test.ts +2 -2
- package/src/__tests__/credential-broker.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +417 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +37 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
- package/src/__tests__/dynamic-page-surface.test.ts +219 -0
- package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
- package/src/__tests__/gateway-flag-listener.test.ts +24 -7
- package/src/__tests__/guardian-action-sweep.test.ts +56 -219
- package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
- package/src/__tests__/llm-resolver.test.ts +115 -0
- package/src/__tests__/managed-profile-guard.test.ts +6 -5
- package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
- package/src/__tests__/media-generate-image.test.ts +20 -9
- package/src/__tests__/mock-gateway-ipc.ts +23 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/normalize-onboarding.test.ts +26 -0
- package/src/__tests__/notification-decision-strategy.test.ts +4 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
- package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
- package/src/__tests__/require-fresh-approval.test.ts +425 -1
- package/src/__tests__/resolve-app-id.test.ts +56 -0
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
- package/src/__tests__/schedule-routes.test.ts +257 -4
- package/src/__tests__/schedule-store.test.ts +60 -0
- package/src/__tests__/schedule-tools.test.ts +247 -2
- package/src/__tests__/skill-execute-input.test.ts +85 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
- package/src/__tests__/task-scheduler.test.ts +299 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
- package/src/__tests__/tool-result-spool.test.ts +3 -1
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
- package/src/agent/compaction-circuit.ts +11 -0
- package/src/agent/loop.ts +181 -12
- package/src/api/constants/call-sites.ts +12 -0
- package/src/api/events/assistant-thinking-delta.ts +10 -0
- package/src/api/events/usage-update.ts +7 -0
- package/src/api/index.ts +4 -1
- package/src/api/responses/memory-v3-selection-log.ts +18 -11
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/background-wake/background-wake-routes.test.ts +5 -2
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/bundler/package-resolver.ts +0 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +16 -93
- package/src/cli/AGENTS.md +4 -0
- package/src/cli/commands/__tests__/schedules.test.ts +430 -1
- package/src/cli/commands/credentials.ts +28 -24
- package/src/cli/commands/image-generation.ts +23 -9
- package/src/cli/commands/notifications.ts +1 -1
- package/src/cli/commands/plugins.ts +89 -46
- package/src/cli/commands/schedules.ts +384 -11
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
- package/src/cli/lib/inspect-plugin.ts +62 -1
- package/src/cli/lib/install-from-github.ts +52 -5
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/config/__tests__/workflows-schema.test.ts +60 -0
- package/src/config/bundled-skills/acp/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
- package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
- package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
- package/src/config/bundled-skills/schedule/SKILL.md +7 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
- package/src/config/bundled-skills/workflows/SKILL.md +214 -0
- package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
- package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
- package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
- package/src/config/bundled-tool-registry.ts +14 -2
- package/src/config/call-site-defaults.ts +5 -0
- package/src/config/feature-flag-registry.json +12 -4
- package/src/config/llm-context-resolution.ts +8 -0
- package/src/config/llm-resolver.ts +30 -0
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
- package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
- package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
- package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/heartbeat.ts +4 -1
- package/src/config/schemas/llm.ts +33 -26
- package/src/config/schemas/memory-retrospective.ts +19 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/services.ts +5 -2
- package/src/config/schemas/workflows.ts +42 -0
- package/src/config/skills.ts +3 -3
- package/src/context/compactor.ts +273 -39
- package/src/context/post-turn-tool-result-truncation.ts +23 -6
- package/src/context/tool-result-spool.ts +12 -17
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/process-manager.ts +37 -3
- package/src/credential-execution/prompted-credential.ts +205 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
- package/src/daemon/conversation-process.ts +11 -2
- package/src/daemon/conversation-surfaces.ts +167 -3
- package/src/daemon/conversation-tool-setup.ts +103 -26
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +115 -11
- package/src/daemon/handlers/shared.ts +26 -14
- package/src/daemon/host-cu-proxy.ts +15 -12
- package/src/daemon/host-file-proxy.ts +15 -12
- package/src/daemon/host-transfer-proxy.ts +30 -24
- package/src/daemon/lifecycle.ts +40 -3
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/messages.ts +2 -10
- package/src/daemon/message-types/workflows.ts +49 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
- package/src/daemon/process-message.ts +6 -0
- package/src/daemon/tool-setup-types.ts +57 -0
- package/src/daemon/wake-conversation-ops.ts +18 -0
- package/src/heartbeat/heartbeat-run-store.ts +8 -2
- package/src/home/feed-types.ts +1 -1
- package/src/ipc/gateway-flag-listener.ts +28 -6
- package/src/mcp/mcp-auth-state.ts +8 -20
- package/src/media/__tests__/image-models.test.ts +57 -0
- package/src/media/image-models.ts +66 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
- package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
- package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
- package/src/memory/auto-analysis-enqueue.ts +14 -1
- package/src/memory/compaction-log-store-clickhouse.ts +6 -4
- package/src/memory/conversation-crud.ts +9 -2
- package/src/memory/conversation-disk-view.ts +1 -1
- package/src/memory/conversation-queries.ts +22 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/db-maintenance.ts +16 -0
- package/src/memory/embedding-runtime-manager.ts +1 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
- package/src/memory/llm-request-log-source-local.ts +19 -1
- package/src/memory/llm-request-log-source.ts +35 -6
- package/src/memory/llm-request-log-store.ts +90 -2
- package/src/memory/memory-retrospective-constants.ts +9 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -6
- package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
- package/src/memory/memory-retrospective-job.ts +500 -208
- package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
- package/src/memory/memory-retrospective-state.ts +85 -2
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
- package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
- package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
- package/src/memory/migrations/284-workflow-runs.ts +51 -0
- package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
- package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
- package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
- package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
- package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
- package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
- package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/preloaded-apps.ts +116 -0
- package/src/memory/schema/infrastructure.ts +4 -0
- package/src/memory/schema/memory-core.ts +4 -0
- package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
- package/src/memory/v2/__tests__/page-store.test.ts +13 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -6
- package/src/memory/v2/page-store.ts +4 -3
- package/src/memory/v2/qdrant.ts +42 -3
- package/src/memory/v2/types.ts +16 -10
- package/src/messaging/draft-store.ts +1 -1
- package/src/notifications/access-request-copy.ts +200 -113
- package/src/notifications/adapters/slack.ts +250 -111
- package/src/notifications/adapters/telegram.ts +7 -44
- package/src/notifications/approval-card-builder.ts +93 -0
- package/src/notifications/broadcaster.ts +74 -0
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/copy-composer.ts +32 -26
- package/src/notifications/decision-engine.ts +59 -7
- package/src/notifications/guardian-question-mode.ts +145 -155
- package/src/notifications/home-feed-side-effect.ts +28 -11
- package/src/notifications/notification-utils.ts +66 -0
- package/src/notifications/signal.ts +6 -0
- package/src/notifications/tool-approval-copy.ts +142 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/threshold.ts +11 -0
- package/src/plugin-api/types.ts +16 -4
- package/src/plugins/defaults/compaction/window-manager.ts +44 -0
- package/src/plugins/defaults/index.ts +46 -0
- package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
- package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
- package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
- package/src/prompts/__tests__/system-prompt.test.ts +100 -1
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
- package/src/prompts/normalize-onboarding.ts +2 -0
- package/src/prompts/persona-resolver.ts +3 -0
- package/src/prompts/system-prompt.ts +51 -2
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
- package/src/prompts/templates/system-sections.ts +8 -3
- package/src/providers/call-site-routing.ts +6 -3
- package/src/providers/fireworks/client.ts +3 -0
- package/src/providers/inference/auth.ts +52 -46
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +47 -9
- package/src/providers/openai/coerce-object-args.ts +104 -0
- package/src/providers/retry.ts +8 -5
- package/src/providers/types.ts +10 -0
- package/src/runtime/__tests__/agent-wake.test.ts +629 -7
- package/src/runtime/access-request-helper.ts +16 -9
- package/src/runtime/agent-wake.ts +302 -51
- package/src/runtime/assistant-stream-state.ts +141 -8
- package/src/runtime/background-job-runner.ts +9 -0
- package/src/runtime/channel-approval-types.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -5
- package/src/runtime/channel-invite-transports/voice.ts +2 -2
- package/src/runtime/channel-invite-types.ts +4 -2
- package/src/runtime/channel-retry-sweep.ts +19 -41
- package/src/runtime/finalize-event-delivery.ts +72 -0
- package/src/runtime/guardian-action-message-composer.ts +0 -54
- package/src/runtime/http-server.ts +6 -14
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/message-composer-types.ts +0 -9
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
- package/src/runtime/middleware/auth.ts +27 -3
- package/src/runtime/middleware/rate-limiter.ts +28 -1
- package/src/runtime/migrations/vbundle-builder.ts +6 -5
- package/src/runtime/pending-interactions.ts +20 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
- package/src/runtime/routes/approval-routes.ts +35 -8
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
- package/src/runtime/routes/btw-routes.ts +37 -1
- package/src/runtime/routes/channel-delivery-routes.ts +11 -7
- package/src/runtime/routes/channel-route-definitions.ts +3 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -1
- package/src/runtime/routes/consolidation-routes.ts +17 -13
- package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
- package/src/runtime/routes/conversation-list-routes.ts +41 -4
- package/src/runtime/routes/conversation-query-routes.ts +196 -11
- package/src/runtime/routes/conversation-routes.ts +15 -1
- package/src/runtime/routes/credential-prompt-routes.ts +39 -17
- package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
- package/src/runtime/routes/heartbeat-routes.ts +18 -13
- package/src/runtime/routes/image-generation-routes.ts +20 -2
- package/src/runtime/routes/inbound-message-handler.ts +13 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/log-export/AGENTS.md +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +5 -9
- package/src/runtime/routes/plugins-routes.ts +26 -0
- package/src/runtime/routes/ps-routes.ts +10 -8
- package/src/runtime/routes/retrospective-routes.ts +235 -0
- package/src/runtime/routes/runs-pagination.ts +75 -0
- package/src/runtime/routes/schedule-routes.ts +367 -59
- package/src/runtime/routes/sounds-config-routes.ts +239 -0
- package/src/runtime/routes/surface-action-routes.ts +84 -4
- package/src/runtime/routes/workflow-routes.test.ts +372 -0
- package/src/runtime/routes/workflow-routes.ts +363 -0
- package/src/runtime/routes/workspace-routes.test.ts +61 -1
- package/src/runtime/routes/workspace-routes.ts +26 -1
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
- package/src/runtime/services/analyze-conversation.ts +26 -13
- package/src/schedule/inference-profile.ts +28 -0
- package/src/schedule/schedule-store.ts +152 -4
- package/src/schedule/scheduler-types.ts +6 -0
- package/src/schedule/scheduler.ts +96 -0
- package/src/security/secret-allowlist.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/tools/apps/executors.ts +23 -0
- package/src/tools/apps/resolve-app-id.ts +42 -0
- package/src/tools/browser/browser-execution.ts +9 -11
- package/src/tools/credentials/broker.ts +4 -4
- package/src/tools/credentials/vault.ts +26 -137
- package/src/tools/executor.ts +69 -0
- package/src/tools/flag-gated-tools.test.ts +76 -0
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/registry.ts +51 -0
- package/src/tools/schedule/create.ts +77 -1
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +73 -1
- package/src/tools/skills/execute.ts +56 -0
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/ui-surface/definitions.ts +91 -2
- package/src/tools/workflows/manage-workflows.ts +183 -0
- package/src/tools/workflows/run-workflow.test.ts +442 -0
- package/src/tools/workflows/run-workflow.ts +88 -0
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +24 -0
- package/src/util/canonicalize-identity.ts +12 -3
- package/src/util/platform.ts +17 -17
- package/src/watcher/__tests__/engine.test.ts +24 -0
- package/src/watcher/__tests__/telemetry.test.ts +135 -0
- package/src/watcher/engine.ts +7 -0
- package/src/watcher/telemetry.ts +74 -0
- package/src/workflows/capabilities.test.ts +365 -0
- package/src/workflows/capabilities.ts +359 -0
- package/src/workflows/deterministic-stringify.ts +27 -0
- package/src/workflows/engine-integration.test.ts +656 -0
- package/src/workflows/engine.test.ts +1144 -0
- package/src/workflows/engine.ts +1078 -0
- package/src/workflows/fanout-load.test.ts +168 -0
- package/src/workflows/journal-store.test.ts +369 -0
- package/src/workflows/journal-store.ts +470 -0
- package/src/workflows/leaf-runner.test.ts +704 -0
- package/src/workflows/leaf-runner.ts +589 -0
- package/src/workflows/library.test.ts +134 -0
- package/src/workflows/library.ts +124 -0
- package/src/workflows/run-manager.test.ts +711 -0
- package/src/workflows/run-manager.ts +593 -0
- package/src/workflows/sandbox-escape.test.ts +339 -0
- package/src/workflows/sandbox.test.ts +251 -0
- package/src/workflows/sandbox.ts +447 -0
- package/src/workspace/adaptive-thinking-repair.ts +33 -11
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
- package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
- package/src/workspace/migrations/031-drop-user-md.ts +1 -4
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
- package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
- package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/migrations/runner.ts +1 -1
- package/tsconfig.json +1 -1
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
- package/src/__tests__/guardian-action-store.test.ts +0 -106
- package/src/daemon/guardian-action-generators.ts +0 -71
- package/src/memory/guardian-action-store.ts +0 -484
- package/src/runtime/guardian-action-grant-minter.ts +0 -150
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Slack channel adapter — delivers notifications to Slack DMs
|
|
3
|
-
*
|
|
3
|
+
* using native Card blocks for approval notifications.
|
|
4
|
+
*
|
|
5
|
+
* Approval notifications (access requests, tool approvals) render as a
|
|
6
|
+
* single Slack Card block with Approve/Reject action buttons, plus
|
|
7
|
+
* optional companion context blocks for details that exceed the card's
|
|
8
|
+
* character limits. Non-approval notifications use standard Block Kit
|
|
9
|
+
* text sections.
|
|
10
|
+
*
|
|
11
|
+
* Card block reference:
|
|
12
|
+
* https://docs.slack.dev/reference/block-kit/blocks/card-block
|
|
4
13
|
*/
|
|
5
14
|
|
|
6
15
|
import { sendSlackReply } from "../../messaging/providers/slack/send.js";
|
|
16
|
+
import type { ApprovalUIMetadata } from "../../runtime/channel-approval-types.js";
|
|
7
17
|
import { getLogger } from "../../util/logger.js";
|
|
8
18
|
import {
|
|
9
|
-
buildAccessRequestIdentityLine,
|
|
10
19
|
buildAccessRequestInviteDirective,
|
|
11
|
-
|
|
20
|
+
buildAccessRequestWarnings,
|
|
21
|
+
buildSlackMessagePermalink,
|
|
22
|
+
isSlackDmConversation,
|
|
23
|
+
parseAccessRequestPayload,
|
|
24
|
+
type ParsedAccessRequestPayload,
|
|
12
25
|
} from "../access-request-copy.js";
|
|
13
26
|
import { isConversationSeedSane } from "../conversation-seed-composer.js";
|
|
14
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
nonEmpty,
|
|
29
|
+
sanitizeIdentityField,
|
|
30
|
+
sanitizeMessagePreview,
|
|
31
|
+
} from "../notification-utils.js";
|
|
15
32
|
import type {
|
|
16
33
|
ChannelAdapter,
|
|
17
34
|
ChannelDeliveryPayload,
|
|
@@ -24,6 +41,10 @@ import type {
|
|
|
24
41
|
|
|
25
42
|
const log = getLogger("notif-adapter-slack");
|
|
26
43
|
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Text resolution
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
27
48
|
function resolveSlackMessageText(payload: ChannelDeliveryPayload): string {
|
|
28
49
|
const deliveryText = nonEmpty(payload.copy.deliveryText);
|
|
29
50
|
if (deliveryText) return deliveryText;
|
|
@@ -41,147 +62,270 @@ function resolveSlackMessageText(payload: ChannelDeliveryPayload): string {
|
|
|
41
62
|
return payload.sourceEventName.replace(/[._]/g, " ");
|
|
42
63
|
}
|
|
43
64
|
|
|
65
|
+
/** Truncate to `maxLength`, appending "…" when exceeded. */
|
|
66
|
+
function truncate(text: string, maxLength: number): string {
|
|
67
|
+
if (text.length <= maxLength) return text;
|
|
68
|
+
return text.slice(0, maxLength - 1) + "…";
|
|
69
|
+
}
|
|
70
|
+
|
|
44
71
|
// ---------------------------------------------------------------------------
|
|
45
|
-
//
|
|
72
|
+
// Slack Card block builders for approval notifications
|
|
46
73
|
// ---------------------------------------------------------------------------
|
|
47
74
|
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const blocks: unknown[] = [];
|
|
75
|
+
/** Build action buttons for a Slack Card block from approval metadata. */
|
|
76
|
+
function buildCardActions(approval: ApprovalUIMetadata): unknown[] {
|
|
77
|
+
return approval.actions.map((action) => ({
|
|
78
|
+
type: "button",
|
|
79
|
+
text: { type: "plain_text", text: action.label, emoji: true },
|
|
80
|
+
action_id: `apr:${approval.requestId}:${action.id}`,
|
|
81
|
+
value: `apr:${approval.requestId}:${action.id}`,
|
|
82
|
+
...(action.id === "reject" ? { style: "danger" } : { style: "primary" }),
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
59
85
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
text: { type: "plain_text", text: "New access request", emoji: true },
|
|
64
|
-
});
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Access request card
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
65
89
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
/** Concise requester identity for the card subtitle (≤150 chars). */
|
|
91
|
+
function buildAccessRequestSubtitle(p: ParsedAccessRequestPayload): string {
|
|
92
|
+
const rawName = nonEmpty(p.actorDisplayName) ?? nonEmpty(p.senderIdentifier);
|
|
93
|
+
const displayName = rawName ? sanitizeIdentityField(rawName) : "Someone";
|
|
94
|
+
const parts = [displayName];
|
|
95
|
+
|
|
96
|
+
const username = nonEmpty(p.actorUsername);
|
|
97
|
+
if (username) {
|
|
98
|
+
const safe = sanitizeIdentityField(username);
|
|
99
|
+
if (safe !== displayName) parts.push(`(@${safe})`);
|
|
100
|
+
}
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
const fields: Array<{ type: "mrkdwn"; text: string }> = [];
|
|
102
|
+
if (p.sourceChannel) parts.push(`via ${p.sourceChannel}`);
|
|
75
103
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
104
|
+
return truncate(parts.join(" "), 150);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Card body: message preview when available, otherwise a default label. */
|
|
108
|
+
function buildAccessRequestBody(p: ParsedAccessRequestPayload): string {
|
|
109
|
+
if (p.messagePreview) {
|
|
110
|
+
const sanitized = sanitizeMessagePreview(p.messagePreview);
|
|
111
|
+
if (sanitized) {
|
|
112
|
+
// Truncate content before wrapping so formatting chars stay balanced.
|
|
113
|
+
// Wrapper `> _"..."_` is 6 chars; reserve space for them.
|
|
114
|
+
const trimmed = truncate(sanitized, 200 - 6);
|
|
115
|
+
return `> _"${trimmed}"_`;
|
|
116
|
+
}
|
|
83
117
|
}
|
|
118
|
+
return "Requesting access to the assistant";
|
|
119
|
+
}
|
|
84
120
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
fields.push({ type: "mrkdwn", text: `*Username:*\n@${actorUsername}` });
|
|
121
|
+
/** Source-channel context block with Slack permalink when available. */
|
|
122
|
+
function buildSourceContextBlock(
|
|
123
|
+
p: ParsedAccessRequestPayload,
|
|
124
|
+
): unknown | undefined {
|
|
125
|
+
if (p.sourceChannel !== "slack" || !p.conversationExternalId) {
|
|
126
|
+
return undefined;
|
|
92
127
|
}
|
|
93
128
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (
|
|
100
|
-
|
|
129
|
+
const permalink = p.messageTs
|
|
130
|
+
? buildSlackMessagePermalink(p.conversationExternalId, p.messageTs)
|
|
131
|
+
: undefined;
|
|
132
|
+
|
|
133
|
+
let sourceText: string;
|
|
134
|
+
if (isSlackDmConversation(p.conversationExternalId)) {
|
|
135
|
+
sourceText = permalink
|
|
136
|
+
? `Source: Slack — Direct message · <${permalink}|View message>`
|
|
137
|
+
: "Source: Slack — Direct message";
|
|
138
|
+
} else {
|
|
139
|
+
sourceText = permalink
|
|
140
|
+
? `Source: Slack — <#${p.conversationExternalId}> · <${permalink}|View message>`
|
|
141
|
+
: `Source: Slack — <#${p.conversationExternalId}>`;
|
|
101
142
|
}
|
|
102
143
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
144
|
+
return {
|
|
145
|
+
type: "context",
|
|
146
|
+
elements: [{ type: "mrkdwn", text: sourceText }],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Stable requester identifier context block (external ID when it adds info). */
|
|
151
|
+
function buildRequesterIdBlock(
|
|
152
|
+
p: ParsedAccessRequestPayload,
|
|
153
|
+
): unknown | undefined {
|
|
154
|
+
const safeExternalId = nonEmpty(
|
|
155
|
+
p.actorExternalId ? sanitizeIdentityField(p.actorExternalId) : undefined,
|
|
107
156
|
);
|
|
108
|
-
if (
|
|
109
|
-
fields.push({ type: "mrkdwn", text: `*ID:*\n${actorExternalId}` });
|
|
110
|
-
}
|
|
157
|
+
if (!safeExternalId) return undefined;
|
|
111
158
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
159
|
+
const displayedName = nonEmpty(
|
|
160
|
+
p.actorDisplayName
|
|
161
|
+
? sanitizeIdentityField(p.actorDisplayName)
|
|
162
|
+
: p.senderIdentifier
|
|
163
|
+
? sanitizeIdentityField(p.senderIdentifier)
|
|
164
|
+
: undefined,
|
|
165
|
+
);
|
|
166
|
+
const displayedUsername = nonEmpty(
|
|
167
|
+
p.actorUsername ? sanitizeIdentityField(p.actorUsername) : undefined,
|
|
168
|
+
);
|
|
169
|
+
if (
|
|
170
|
+
safeExternalId === displayedName ||
|
|
171
|
+
safeExternalId === displayedUsername
|
|
172
|
+
) {
|
|
173
|
+
return undefined;
|
|
117
174
|
}
|
|
118
175
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
176
|
+
return {
|
|
177
|
+
type: "context",
|
|
178
|
+
elements: [{ type: "mrkdwn", text: `ID: ${safeExternalId}` }],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Build Slack blocks for an access request using a native Card block.
|
|
184
|
+
*
|
|
185
|
+
* Layout:
|
|
186
|
+
* Card — title + subtitle (identity) + body (preview) + actions + subtext (warnings)
|
|
187
|
+
* Context — source permalink (when the request is from Slack)
|
|
188
|
+
* Context — stable requester ID (when it adds info beyond subtitle)
|
|
189
|
+
* Context — invite directive
|
|
190
|
+
* Context — guardian verification note (conditional)
|
|
191
|
+
*/
|
|
192
|
+
function buildAccessRequestCardBlocks(
|
|
193
|
+
payload: ChannelDeliveryPayload,
|
|
194
|
+
): unknown[] {
|
|
195
|
+
const approval = payload.approvalContext!;
|
|
196
|
+
const p = parseAccessRequestPayload(payload.contextPayload!);
|
|
197
|
+
const blocks: unknown[] = [];
|
|
198
|
+
|
|
199
|
+
const subtitle = buildAccessRequestSubtitle(p);
|
|
200
|
+
const body = buildAccessRequestBody(p);
|
|
201
|
+
|
|
202
|
+
const warnings = buildAccessRequestWarnings(p);
|
|
203
|
+
const subtext =
|
|
204
|
+
warnings.length > 0
|
|
205
|
+
? truncate(warnings.map((w) => `:warning: ${w}`).join(" · "), 200)
|
|
123
206
|
: undefined;
|
|
124
|
-
|
|
207
|
+
|
|
208
|
+
const card: Record<string, unknown> = {
|
|
209
|
+
type: "card",
|
|
210
|
+
title: { type: "plain_text", text: "Access Request" },
|
|
211
|
+
subtitle: { type: "mrkdwn", text: subtitle },
|
|
212
|
+
body: { type: "mrkdwn", text: body },
|
|
213
|
+
actions: buildCardActions(approval),
|
|
214
|
+
};
|
|
215
|
+
if (subtext) card.subtext = { type: "mrkdwn", text: subtext };
|
|
216
|
+
blocks.push(card);
|
|
217
|
+
|
|
218
|
+
const sourceContext = buildSourceContextBlock(p);
|
|
219
|
+
if (sourceContext) blocks.push(sourceContext);
|
|
220
|
+
|
|
221
|
+
const idBlock = buildRequesterIdBlock(p);
|
|
222
|
+
if (idBlock) blocks.push(idBlock);
|
|
223
|
+
|
|
224
|
+
blocks.push({
|
|
225
|
+
type: "context",
|
|
226
|
+
elements: [{ type: "mrkdwn", text: buildAccessRequestInviteDirective() }],
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (
|
|
230
|
+
(p.guardianResolutionSource === "vellum-anchor" ||
|
|
231
|
+
p.guardianResolutionSource === "none") &&
|
|
232
|
+
p.sourceChannel
|
|
233
|
+
) {
|
|
125
234
|
blocks.push({
|
|
126
235
|
type: "context",
|
|
127
236
|
elements: [
|
|
128
237
|
{
|
|
129
238
|
type: "mrkdwn",
|
|
130
|
-
text:
|
|
239
|
+
text: `_You haven't verified your identity on ${p.sourceChannel} yet. If this was you trying to message your assistant, say "help me verify as guardian on ${p.sourceChannel}" to set up direct access._`,
|
|
131
240
|
},
|
|
132
241
|
],
|
|
133
242
|
});
|
|
134
243
|
}
|
|
135
244
|
|
|
136
|
-
|
|
137
|
-
|
|
245
|
+
return blocks;
|
|
246
|
+
}
|
|
138
247
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// Tool approval card
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Build Slack blocks for a tool approval notification using a native Card block.
|
|
254
|
+
*
|
|
255
|
+
* Layout:
|
|
256
|
+
* Card — title + subtitle (tool + requester) + body (notification text) + actions
|
|
257
|
+
*/
|
|
258
|
+
function buildToolApprovalCardBlocks(
|
|
259
|
+
payload: ChannelDeliveryPayload,
|
|
260
|
+
messageText: string,
|
|
261
|
+
): unknown[] {
|
|
262
|
+
const approval = payload.approvalContext!;
|
|
263
|
+
const blocks: unknown[] = [];
|
|
264
|
+
|
|
265
|
+
const details = approval.permissionDetails;
|
|
266
|
+
const toolName = details?.toolName;
|
|
267
|
+
const requester = details?.requesterIdentifier;
|
|
268
|
+
let subtitle: string | undefined;
|
|
269
|
+
if (toolName && requester) {
|
|
270
|
+
subtitle = truncate(`${toolName} — requested by ${requester}`, 150);
|
|
271
|
+
} else if (toolName) {
|
|
272
|
+
subtitle = truncate(toolName, 150);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const needsOverflow = messageText.length > 200;
|
|
276
|
+
const card: Record<string, unknown> = {
|
|
277
|
+
type: "card",
|
|
278
|
+
title: {
|
|
279
|
+
type: "plain_text",
|
|
280
|
+
text: details ? "Tool Approval" : "Approval Request",
|
|
281
|
+
},
|
|
282
|
+
body: {
|
|
283
|
+
type: "mrkdwn",
|
|
284
|
+
text: needsOverflow ? truncate(messageText, 197) + " ↓" : messageText,
|
|
285
|
+
},
|
|
286
|
+
actions: buildCardActions(approval),
|
|
287
|
+
};
|
|
288
|
+
if (subtitle) card.subtitle = { type: "mrkdwn", text: subtitle };
|
|
289
|
+
blocks.push(card);
|
|
290
|
+
|
|
291
|
+
// When the message exceeds the card body limit, show the full text in a
|
|
292
|
+
// companion section so the approver can see the complete command/context.
|
|
293
|
+
if (needsOverflow) {
|
|
145
294
|
blocks.push({
|
|
146
295
|
type: "section",
|
|
147
|
-
text: {
|
|
148
|
-
type: "mrkdwn",
|
|
149
|
-
text: `Reply *${code} approve* to grant access or *${code} reject* to deny.`,
|
|
150
|
-
},
|
|
296
|
+
text: { type: "mrkdwn", text: truncate(messageText, 3000) },
|
|
151
297
|
});
|
|
152
298
|
}
|
|
153
299
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
blocks.push({
|
|
157
|
-
type: "context",
|
|
158
|
-
elements: [{ type: "mrkdwn", text: inviteDirective }],
|
|
159
|
-
});
|
|
300
|
+
return blocks;
|
|
301
|
+
}
|
|
160
302
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
// Unified approval block dispatcher
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Build Slack blocks for any notification carrying approval context.
|
|
309
|
+
* Dispatches to the appropriate card builder based on source event type.
|
|
310
|
+
*/
|
|
311
|
+
function buildApprovalNotificationBlocks(
|
|
312
|
+
payload: ChannelDeliveryPayload,
|
|
313
|
+
messageText: string,
|
|
314
|
+
): unknown[] {
|
|
166
315
|
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
sourceChannel
|
|
316
|
+
payload.sourceEventName === "ingress.access_request" &&
|
|
317
|
+
payload.contextPayload != null
|
|
170
318
|
) {
|
|
171
|
-
|
|
172
|
-
type: "context",
|
|
173
|
-
elements: [
|
|
174
|
-
{
|
|
175
|
-
type: "mrkdwn",
|
|
176
|
-
text: `_You haven't verified your identity on ${sourceChannel} yet. If this was you trying to message your assistant, say "help me verify as guardian on ${sourceChannel}" to set up direct access._`,
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
});
|
|
319
|
+
return buildAccessRequestCardBlocks(payload);
|
|
180
320
|
}
|
|
181
321
|
|
|
182
|
-
return
|
|
322
|
+
return buildToolApprovalCardBlocks(payload, messageText);
|
|
183
323
|
}
|
|
184
324
|
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// Adapter
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
|
|
185
329
|
export class SlackAdapter implements ChannelAdapter {
|
|
186
330
|
readonly channel: NotificationChannel = "slack";
|
|
187
331
|
|
|
@@ -203,15 +347,10 @@ export class SlackAdapter implements ChannelAdapter {
|
|
|
203
347
|
|
|
204
348
|
const messageText = resolveSlackMessageText(payload);
|
|
205
349
|
|
|
206
|
-
// Build Block Kit blocks for access request notifications
|
|
207
|
-
const isAccessRequest =
|
|
208
|
-
payload.sourceEventName === "ingress.access_request" &&
|
|
209
|
-
payload.contextPayload != null;
|
|
210
|
-
|
|
211
350
|
try {
|
|
212
|
-
const result =
|
|
351
|
+
const result = payload.approvalContext
|
|
213
352
|
? await sendSlackReply(chatId, messageText, {
|
|
214
|
-
blocks:
|
|
353
|
+
blocks: buildApprovalNotificationBlocks(payload, messageText),
|
|
215
354
|
})
|
|
216
355
|
: await sendSlackReply(chatId, messageText, { useBlocks: true });
|
|
217
356
|
|
|
@@ -2,19 +2,17 @@
|
|
|
2
2
|
* Telegram channel adapter — delivers notifications to Telegram chats
|
|
3
3
|
* by calling the Telegram Bot API directly.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* If the rich delivery fails, the adapter falls back to
|
|
8
|
-
* typed-command instructions.
|
|
5
|
+
* When the delivery payload carries an `approvalContext` (built centrally
|
|
6
|
+
* by the broadcaster), inline keyboard buttons ("Approve once", "Reject")
|
|
7
|
+
* are attached. If the rich delivery fails, the adapter falls back to
|
|
8
|
+
* plain text with typed-command instructions.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { sendTelegramReply } from "../../messaging/providers/telegram-bot/send.js";
|
|
12
|
-
import type { ApprovalUIMetadata } from "../../runtime/channel-approval-types.js";
|
|
13
12
|
import { ConfigError } from "../../util/errors.js";
|
|
14
13
|
import { getLogger } from "../../util/logger.js";
|
|
15
|
-
import { buildAccessRequestContractText } from "../access-request-copy.js";
|
|
16
14
|
import { isConversationSeedSane } from "../conversation-seed-composer.js";
|
|
17
|
-
import { nonEmpty } from "../
|
|
15
|
+
import { nonEmpty } from "../notification-utils.js";
|
|
18
16
|
import type {
|
|
19
17
|
ChannelAdapter,
|
|
20
18
|
ChannelDeliveryPayload,
|
|
@@ -42,34 +40,6 @@ function resolveTelegramMessageText(payload: ChannelDeliveryPayload): string {
|
|
|
42
40
|
return payload.sourceEventName.replace(/[._]/g, " ");
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
/**
|
|
46
|
-
* Build an {@link ApprovalUIMetadata} for an access request so the delivery
|
|
47
|
-
* renders inline keyboard buttons in the Telegram message.
|
|
48
|
-
*
|
|
49
|
-
* Returns `undefined` when the context payload is missing the required
|
|
50
|
-
* `requestId`, in which case the caller should fall back to plain text.
|
|
51
|
-
*/
|
|
52
|
-
function buildAccessRequestApproval(
|
|
53
|
-
contextPayload: Record<string, unknown>,
|
|
54
|
-
): ApprovalUIMetadata | undefined {
|
|
55
|
-
const requestId =
|
|
56
|
-
typeof contextPayload.requestId === "string"
|
|
57
|
-
? contextPayload.requestId
|
|
58
|
-
: undefined;
|
|
59
|
-
if (!requestId) return undefined;
|
|
60
|
-
|
|
61
|
-
const plainTextFallback = buildAccessRequestContractText(contextPayload);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
requestId,
|
|
65
|
-
actions: [
|
|
66
|
-
{ id: "approve_once", label: "Approve once" },
|
|
67
|
-
{ id: "reject", label: "Reject" },
|
|
68
|
-
],
|
|
69
|
-
plainTextFallback,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
43
|
export class TelegramAdapter implements ChannelAdapter {
|
|
74
44
|
readonly channel: NotificationChannel = "telegram";
|
|
75
45
|
|
|
@@ -90,14 +60,7 @@ export class TelegramAdapter implements ChannelAdapter {
|
|
|
90
60
|
}
|
|
91
61
|
|
|
92
62
|
const messageText = resolveTelegramMessageText(payload);
|
|
93
|
-
|
|
94
|
-
const isAccessRequest =
|
|
95
|
-
payload.sourceEventName === "ingress.access_request" &&
|
|
96
|
-
payload.contextPayload != null;
|
|
97
|
-
|
|
98
|
-
const approval = isAccessRequest
|
|
99
|
-
? buildAccessRequestApproval(payload.contextPayload!)
|
|
100
|
-
: undefined;
|
|
63
|
+
const approval = payload.approvalContext;
|
|
101
64
|
|
|
102
65
|
try {
|
|
103
66
|
if (approval) {
|
|
@@ -108,7 +71,7 @@ export class TelegramAdapter implements ChannelAdapter {
|
|
|
108
71
|
|
|
109
72
|
log.info(
|
|
110
73
|
{ sourceEventName: payload.sourceEventName, chatId },
|
|
111
|
-
"Telegram
|
|
74
|
+
"Telegram approval notification delivered with inline buttons",
|
|
112
75
|
);
|
|
113
76
|
|
|
114
77
|
return { success: true };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared builder for guardian approval card seed content blocks.
|
|
3
|
+
*
|
|
4
|
+
* Both access-request and tool-approval notifications render a Surface card
|
|
5
|
+
* with Approve/Reject buttons in the Vellum in-app channel. This module
|
|
6
|
+
* provides the shared construction logic so each notification type only
|
|
7
|
+
* supplies its domain-specific data (title, subtitle, metadata, etc.)
|
|
8
|
+
* without duplicating the card structure or action wiring.
|
|
9
|
+
*
|
|
10
|
+
* The card data shape matches `CardSurfaceData` from
|
|
11
|
+
* `daemon/message-types/surfaces.ts`: `{ title, subtitle, body, metadata }`.
|
|
12
|
+
* Actions use the canonical `apr:<requestId>:<action>` callback format
|
|
13
|
+
* consumed by `surface-action-routes.ts` → `processGuardianDecision`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ── Public types ────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export interface ApprovalCardParams {
|
|
19
|
+
/** Prefix for `surfaceId` — combined with `requestId` to form e.g. "access-request-abc123". */
|
|
20
|
+
surfaceIdPrefix: string;
|
|
21
|
+
/** Top-level card title shown in the surface header (e.g. "Access Request", "Tool Approval"). */
|
|
22
|
+
cardTitle: string;
|
|
23
|
+
/** Primary line inside the card (typically the requester's display name). */
|
|
24
|
+
requesterName: string;
|
|
25
|
+
/** Secondary line below the requester name (e.g. "Requesting access to the assistant"). */
|
|
26
|
+
subtitle: string;
|
|
27
|
+
/** Markdown body — blockquotes, warnings, links, etc. */
|
|
28
|
+
body: string;
|
|
29
|
+
/** Key-value metadata grid rendered below the body. */
|
|
30
|
+
metadata: Array<{ label: string; value: string }>;
|
|
31
|
+
/** When present, Approve/Reject buttons are wired with `apr:<requestId>:*` action IDs. */
|
|
32
|
+
requestId?: string;
|
|
33
|
+
/** Plain-text content for the fallback `text` block. */
|
|
34
|
+
fallbackText: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── Builder ─────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build a `[ui_surface, text]` block pair for a guardian approval notification.
|
|
41
|
+
*
|
|
42
|
+
* The `ui_surface` block renders as a card with Approve/Reject buttons in
|
|
43
|
+
* clients that support Surface rendering. The `text` block provides a
|
|
44
|
+
* plain-text fallback for older clients, search indexing, and CLI display.
|
|
45
|
+
*/
|
|
46
|
+
export function buildApprovalCardBlocks(params: ApprovalCardParams): unknown[] {
|
|
47
|
+
const {
|
|
48
|
+
surfaceIdPrefix,
|
|
49
|
+
cardTitle,
|
|
50
|
+
requesterName,
|
|
51
|
+
subtitle,
|
|
52
|
+
body,
|
|
53
|
+
metadata,
|
|
54
|
+
requestId,
|
|
55
|
+
fallbackText,
|
|
56
|
+
} = params;
|
|
57
|
+
|
|
58
|
+
const actions = requestId
|
|
59
|
+
? [
|
|
60
|
+
{
|
|
61
|
+
id: `apr:${requestId}:approve_once`,
|
|
62
|
+
label: "Approve",
|
|
63
|
+
style: "primary",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: `apr:${requestId}:reject`,
|
|
67
|
+
label: "Reject",
|
|
68
|
+
style: "destructive",
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
: undefined;
|
|
72
|
+
|
|
73
|
+
const surfaceBlock = {
|
|
74
|
+
type: "ui_surface" as const,
|
|
75
|
+
surfaceId: `${surfaceIdPrefix}-${requestId ?? "unknown"}`,
|
|
76
|
+
surfaceType: "card" as const,
|
|
77
|
+
title: cardTitle,
|
|
78
|
+
data: {
|
|
79
|
+
title: requesterName,
|
|
80
|
+
subtitle,
|
|
81
|
+
body,
|
|
82
|
+
metadata,
|
|
83
|
+
},
|
|
84
|
+
...(actions ? { actions } : {}),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const textBlock = {
|
|
88
|
+
type: "text" as const,
|
|
89
|
+
text: fallbackText,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return [surfaceBlock, textBlock];
|
|
93
|
+
}
|