@vellumai/assistant 0.9.1-staging.1 → 0.10.0-staging.3
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/docs/activation-funnel-telemetry.md +24 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
- package/openapi.yaml +881 -16
- package/package.json +1 -1
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/approval-routes-http.test.ts +128 -0
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/document-tool-security.test.ts +9 -6
- package/src/__tests__/document-update-default-surface.test.ts +202 -0
- package/src/__tests__/dynamic-page-surface.test.ts +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/secret-response-routing.test.ts +35 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
- package/src/__tests__/skill-execute-input.test.ts +131 -0
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +150 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +33 -33
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +26 -0
- package/src/api/responses/home.ts +26 -0
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +181 -78
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +64 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/memory-v2-startup.test.ts +131 -0
- package/src/daemon/memory-v2-startup.ts +100 -1
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/permissions/secret-prompter.ts +15 -1
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/index.ts +27 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +35 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +49 -21
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +35 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
- package/src/runtime/routes/approval-routes.ts +40 -9
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +40 -7
- package/src/runtime/routes/secret-routes.ts +15 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +11 -4
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- package/src/tools/document/document-tool.ts +30 -6
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +67 -4
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -3
- package/src/workflows/run-manager.ts +64 -0
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified resolver + renderer for guardian approval card seed content.
|
|
3
|
+
*
|
|
4
|
+
* `contextPayload` resolves into a typed {@link ApprovalCardData} discriminated
|
|
5
|
+
* union, then renders into Surface `[ui_surface, text]` blocks via the shared
|
|
6
|
+
* {@link buildApprovalCardBlocks}. Access-request and tool-approval
|
|
7
|
+
* notifications share this single entry point, which parses and shapes the raw
|
|
8
|
+
* payload into card data.
|
|
9
|
+
*
|
|
10
|
+
* The Surface card data shape matches `CardSurfaceData` from
|
|
11
|
+
* `daemon/message-types/surfaces.ts`. Actions use the canonical
|
|
12
|
+
* `apr:<requestId>:<action>` callback format consumed by
|
|
13
|
+
* `surface-action-routes.ts` → `processGuardianDecision`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
buildAccessRequestCardView,
|
|
18
|
+
buildAccessRequestContractText,
|
|
19
|
+
parseAccessRequestPayload,
|
|
20
|
+
} from "./access-request-copy.js";
|
|
21
|
+
import {
|
|
22
|
+
type ApprovalCardParams,
|
|
23
|
+
buildApprovalCardBlocks,
|
|
24
|
+
} from "./approval-card-builder.js";
|
|
25
|
+
import {
|
|
26
|
+
buildGuardianRequestCodeInstruction,
|
|
27
|
+
type GuardianQuestionPayload,
|
|
28
|
+
type LenientToolApprovalPayload,
|
|
29
|
+
LenientToolApprovalPayloadSchema,
|
|
30
|
+
parseGuardianQuestionPayload,
|
|
31
|
+
resolveGuardianInstructionModeFromFields,
|
|
32
|
+
resolveGuardianInstructionModeFromPayload,
|
|
33
|
+
} from "./guardian-question-mode.js";
|
|
34
|
+
import { nonEmpty, sanitizeIdentityField } from "./notification-utils.js";
|
|
35
|
+
|
|
36
|
+
// ── Surface ids ──────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `surfaceId` prefixes for the in-app approval cards. The full id is
|
|
40
|
+
* `${prefix}-${requestId}` (see {@link buildApprovalCardBlocks}). These are the
|
|
41
|
+
* single source of truth for the prefix, shared by the card builders below and
|
|
42
|
+
* by {@link approvalCardSurfaceId} so the withdrawal path can recompute the id.
|
|
43
|
+
*/
|
|
44
|
+
const ACCESS_REQUEST_SURFACE_PREFIX = "access-request";
|
|
45
|
+
const TOOL_APPROVAL_SURFACE_PREFIX = "tool-approval";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the `ui_surface` id for a guardian request's in-app approval card
|
|
49
|
+
* from its kind, or `null` for kinds that never render an approval card.
|
|
50
|
+
*
|
|
51
|
+
* The card is rendered once at notification time; when the request is later
|
|
52
|
+
* resolved from a different surface the withdrawal path recomputes the id here
|
|
53
|
+
* to complete that card. Keeping this beside the builders ensures the two stay
|
|
54
|
+
* in lockstep.
|
|
55
|
+
*/
|
|
56
|
+
export function approvalCardSurfaceId(
|
|
57
|
+
kind: string,
|
|
58
|
+
requestId: string,
|
|
59
|
+
): string | null {
|
|
60
|
+
switch (kind) {
|
|
61
|
+
case "access_request":
|
|
62
|
+
return `${ACCESS_REQUEST_SURFACE_PREFIX}-${requestId}`;
|
|
63
|
+
case "tool_approval":
|
|
64
|
+
case "tool_grant_request":
|
|
65
|
+
case "pending_question":
|
|
66
|
+
return `${TOOL_APPROVAL_SURFACE_PREFIX}-${requestId}`;
|
|
67
|
+
default:
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Typed card data ─────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
/** Resolved card data for an access-request notification. */
|
|
75
|
+
export interface AccessRequestCardData {
|
|
76
|
+
kind: "access_request";
|
|
77
|
+
card: ApprovalCardParams;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Resolved card data for a tool-approval / tool-grant notification. */
|
|
81
|
+
export interface ToolApprovalCardData {
|
|
82
|
+
kind: "tool_approval";
|
|
83
|
+
card: ApprovalCardParams;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Channel-agnostic approval card content, resolved once from `contextPayload`.
|
|
88
|
+
* The discriminant `kind` lets consumers branch on the approval type without
|
|
89
|
+
* re-parsing the payload.
|
|
90
|
+
*/
|
|
91
|
+
export type ApprovalCardData = AccessRequestCardData | ToolApprovalCardData;
|
|
92
|
+
|
|
93
|
+
// ── Access-request resolution ────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
/** Shape the parsed access-request payload into card params via the view model. */
|
|
96
|
+
function resolveAccessRequestCard(
|
|
97
|
+
payload: Record<string, unknown>,
|
|
98
|
+
): ApprovalCardParams {
|
|
99
|
+
const view = buildAccessRequestCardView(parseAccessRequestPayload(payload));
|
|
100
|
+
|
|
101
|
+
const metadata: Array<{ label: string; value: string }> = [];
|
|
102
|
+
|
|
103
|
+
if (view.username) {
|
|
104
|
+
metadata.push({
|
|
105
|
+
label: "Username",
|
|
106
|
+
value: `@${view.username}`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (view.sourceChannel === "slack" && view.conversationExternalId) {
|
|
111
|
+
metadata.push({
|
|
112
|
+
label: "Source",
|
|
113
|
+
value: view.isSlackDm
|
|
114
|
+
? "Slack — Direct message"
|
|
115
|
+
: `Slack — #${view.conversationExternalId}`,
|
|
116
|
+
});
|
|
117
|
+
} else if (view.sourceChannel) {
|
|
118
|
+
metadata.push({ label: "Source", value: view.sourceChannel });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const bodyParts: string[] = [];
|
|
122
|
+
|
|
123
|
+
if (view.messagePreview) {
|
|
124
|
+
bodyParts.push(`> "${view.messagePreview}"`);
|
|
125
|
+
}
|
|
126
|
+
for (const w of view.warnings) {
|
|
127
|
+
bodyParts.push(`⚠️ ${w}`);
|
|
128
|
+
}
|
|
129
|
+
if (view.messagePermalink) {
|
|
130
|
+
bodyParts.push(`[View message](${view.messagePermalink})`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const body =
|
|
134
|
+
bodyParts.length > 0
|
|
135
|
+
? bodyParts.join("\n\n")
|
|
136
|
+
: "No additional context available.";
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
surfaceIdPrefix: ACCESS_REQUEST_SURFACE_PREFIX,
|
|
140
|
+
cardTitle: "Access Request",
|
|
141
|
+
requesterName: view.displayName,
|
|
142
|
+
subtitle: "Requesting access to the assistant",
|
|
143
|
+
body,
|
|
144
|
+
metadata,
|
|
145
|
+
requestId: view.requestId,
|
|
146
|
+
fallbackText: buildAccessRequestContractText(payload),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── Tool-approval resolution ─────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Determine whether a typed guardian.question payload represents a tool
|
|
154
|
+
* approval (as opposed to a free-text answer). Uses the canonical mode
|
|
155
|
+
* resolver in `guardian-question-mode.ts` — the single source of truth
|
|
156
|
+
* for the requestKind → instructionMode mapping.
|
|
157
|
+
*/
|
|
158
|
+
function isToolApprovalPayload(payload: GuardianQuestionPayload): boolean {
|
|
159
|
+
const { mode } = resolveGuardianInstructionModeFromPayload(payload);
|
|
160
|
+
return mode === "approval" && payload.requestKind !== "access_request";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Lenient approval detection for partially-constructed payloads that don't
|
|
165
|
+
* satisfy the strict discriminated union schema.
|
|
166
|
+
*/
|
|
167
|
+
function isLenientToolApproval(payload: LenientToolApprovalPayload): boolean {
|
|
168
|
+
const modeResolution = resolveGuardianInstructionModeFromFields(
|
|
169
|
+
payload.requestKind,
|
|
170
|
+
payload.toolName,
|
|
171
|
+
);
|
|
172
|
+
if (!modeResolution) return false;
|
|
173
|
+
return (
|
|
174
|
+
modeResolution.mode === "approval" &&
|
|
175
|
+
payload.requestKind !== "access_request"
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Shape a tool-approval/grant payload (strict or lenient) into card params. */
|
|
180
|
+
function extractToolApprovalCard(
|
|
181
|
+
p: GuardianQuestionPayload | LenientToolApprovalPayload,
|
|
182
|
+
): ApprovalCardParams {
|
|
183
|
+
const toolName =
|
|
184
|
+
("toolName" in p ? nonEmpty(p.toolName) : undefined) ?? "unknown tool";
|
|
185
|
+
const rawRequester = nonEmpty(p.requesterIdentifier);
|
|
186
|
+
const requester = rawRequester
|
|
187
|
+
? sanitizeIdentityField(rawRequester)
|
|
188
|
+
: "Someone";
|
|
189
|
+
|
|
190
|
+
const isGrant = p.requestKind === "tool_grant_request";
|
|
191
|
+
|
|
192
|
+
const metadata: Array<{ label: string; value: string }> = [];
|
|
193
|
+
metadata.push({ label: "Tool", value: toolName });
|
|
194
|
+
const sourceChannel = nonEmpty(p.sourceChannel);
|
|
195
|
+
if (sourceChannel) {
|
|
196
|
+
metadata.push({ label: "Source", value: sourceChannel });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const body = p.questionText
|
|
200
|
+
? `> ${p.questionText}`
|
|
201
|
+
: "No additional context available.";
|
|
202
|
+
|
|
203
|
+
// Fallback text with request-code instructions for older clients.
|
|
204
|
+
const baseFallback =
|
|
205
|
+
p.questionText ?? `${requester} is requesting approval to use ${toolName}`;
|
|
206
|
+
let fallbackText = baseFallback;
|
|
207
|
+
const requestCode = nonEmpty(p.requestCode);
|
|
208
|
+
if (requestCode) {
|
|
209
|
+
const modeResolution = resolveGuardianInstructionModeFromFields(
|
|
210
|
+
p.requestKind,
|
|
211
|
+
"toolName" in p ? (p.toolName ?? undefined) : undefined,
|
|
212
|
+
);
|
|
213
|
+
const mode = modeResolution?.mode ?? "approval";
|
|
214
|
+
const instruction = buildGuardianRequestCodeInstruction(
|
|
215
|
+
requestCode.trim().toUpperCase(),
|
|
216
|
+
mode,
|
|
217
|
+
);
|
|
218
|
+
fallbackText = `${baseFallback}\n\n${instruction}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
surfaceIdPrefix: TOOL_APPROVAL_SURFACE_PREFIX,
|
|
223
|
+
cardTitle: isGrant ? "Tool Grant Request" : "Tool Approval",
|
|
224
|
+
requesterName: requester,
|
|
225
|
+
subtitle: isGrant
|
|
226
|
+
? "Requesting permission to use this tool"
|
|
227
|
+
: "Requesting approval to run this tool",
|
|
228
|
+
body,
|
|
229
|
+
metadata,
|
|
230
|
+
requestId: nonEmpty(p.requestId),
|
|
231
|
+
fallbackText,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Resolve a guardian.question payload into tool-approval card params, or
|
|
237
|
+
* `null` when it does not represent a tool approval.
|
|
238
|
+
*
|
|
239
|
+
* Tries strict Zod parsing first (full discriminated union), then falls back
|
|
240
|
+
* to lenient field extraction so cards still render when optional fields are
|
|
241
|
+
* absent.
|
|
242
|
+
*/
|
|
243
|
+
function resolveToolApprovalCard(
|
|
244
|
+
payload: Record<string, unknown>,
|
|
245
|
+
): ApprovalCardParams | null {
|
|
246
|
+
const strict = parseGuardianQuestionPayload(payload);
|
|
247
|
+
if (strict) {
|
|
248
|
+
if (!isToolApprovalPayload(strict)) return null;
|
|
249
|
+
return extractToolApprovalCard(strict);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const lenient = LenientToolApprovalPayloadSchema.safeParse(payload);
|
|
253
|
+
if (!lenient.success) return null;
|
|
254
|
+
if (!isLenientToolApproval(lenient.data)) return null;
|
|
255
|
+
return extractToolApprovalCard(lenient.data);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ── Unified dispatcher + renderer ────────────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Resolve a notification's `contextPayload` into typed {@link ApprovalCardData}
|
|
262
|
+
* based on its source event, or `null` when the signal does not carry a
|
|
263
|
+
* renderable approval card. Single entry point so each consumer (copy
|
|
264
|
+
* composition, decision engine) shapes the payload identically.
|
|
265
|
+
*/
|
|
266
|
+
export function resolveApprovalCardData(
|
|
267
|
+
sourceEventName: string,
|
|
268
|
+
contextPayload: Record<string, unknown> | undefined,
|
|
269
|
+
): ApprovalCardData | null {
|
|
270
|
+
if (!contextPayload) return null;
|
|
271
|
+
|
|
272
|
+
if (sourceEventName === "ingress.access_request") {
|
|
273
|
+
return {
|
|
274
|
+
kind: "access_request",
|
|
275
|
+
card: resolveAccessRequestCard(contextPayload),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (sourceEventName === "guardian.question") {
|
|
280
|
+
const card = resolveToolApprovalCard(contextPayload);
|
|
281
|
+
return card ? { kind: "tool_approval", card } : null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Render resolved approval card data into Surface `[ui_surface, text]` blocks. */
|
|
288
|
+
export function renderApprovalCardData(data: ApprovalCardData): unknown[] {
|
|
289
|
+
return buildApprovalCardBlocks(data.card);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ── Public seed-content builders ─────────────────────────────────────────────
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Build structured content blocks for an access-request notification seed
|
|
296
|
+
* message. Produces a `ui_surface` card block that the web/macOS/iOS apps
|
|
297
|
+
* render as an interactive card via `SurfaceRouter → CardSurface`, plus a
|
|
298
|
+
* plain-text fallback block for search, CLI display, and backward-compatible
|
|
299
|
+
* clients that don't support surfaces.
|
|
300
|
+
*/
|
|
301
|
+
export function buildAccessRequestSeedContentBlocks(
|
|
302
|
+
payload: Record<string, unknown>,
|
|
303
|
+
): unknown[] {
|
|
304
|
+
return renderApprovalCardData({
|
|
305
|
+
kind: "access_request",
|
|
306
|
+
card: resolveAccessRequestCard(payload),
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Build structured content blocks for a tool approval/grant notification seed
|
|
312
|
+
* message. Returns `null` when the payload does not represent a tool approval.
|
|
313
|
+
*
|
|
314
|
+
* Accepts both strict `GuardianQuestionPayload` (Zod-validated) and raw
|
|
315
|
+
* `Record<string, unknown>` payloads. For raw payloads, attempts strict
|
|
316
|
+
* parsing first, then falls back to lenient field extraction so cards
|
|
317
|
+
* still render when optional fields are absent.
|
|
318
|
+
*/
|
|
319
|
+
export function buildToolApprovalSeedContentBlocks(
|
|
320
|
+
payload: GuardianQuestionPayload,
|
|
321
|
+
): unknown[] | null;
|
|
322
|
+
export function buildToolApprovalSeedContentBlocks(
|
|
323
|
+
payload: Record<string, unknown>,
|
|
324
|
+
): unknown[] | null;
|
|
325
|
+
export function buildToolApprovalSeedContentBlocks(
|
|
326
|
+
payload: GuardianQuestionPayload | Record<string, unknown>,
|
|
327
|
+
): unknown[] | null {
|
|
328
|
+
const data = resolveApprovalCardData(
|
|
329
|
+
"guardian.question",
|
|
330
|
+
payload as Record<string, unknown>,
|
|
331
|
+
);
|
|
332
|
+
return data ? renderApprovalCardData(data) : null;
|
|
333
|
+
}
|
|
@@ -83,11 +83,15 @@ function resolveApprovalContext(
|
|
|
83
83
|
// Extract tool context so channel adapters can render structured
|
|
84
84
|
// approval cards without re-parsing contextPayload.
|
|
85
85
|
let toolName: string | undefined;
|
|
86
|
+
let riskLevel: string | undefined;
|
|
87
|
+
let commandPreview: string | undefined;
|
|
86
88
|
if (
|
|
87
89
|
parsed.requestKind === "tool_approval" ||
|
|
88
90
|
parsed.requestKind === "tool_grant_request"
|
|
89
91
|
) {
|
|
90
92
|
toolName = nonEmpty(parsed.toolName);
|
|
93
|
+
riskLevel = nonEmpty(parsed.riskLevel);
|
|
94
|
+
commandPreview = nonEmpty(parsed.commandPreview);
|
|
91
95
|
} else if (parsed.requestKind === "pending_question") {
|
|
92
96
|
toolName = nonEmpty(parsed.toolName);
|
|
93
97
|
}
|
|
@@ -99,8 +103,8 @@ function resolveApprovalContext(
|
|
|
99
103
|
permissionDetails: toolName
|
|
100
104
|
? {
|
|
101
105
|
toolName,
|
|
102
|
-
riskLevel: "medium",
|
|
103
|
-
toolInput: {},
|
|
106
|
+
riskLevel: riskLevel ?? "medium",
|
|
107
|
+
toolInput: commandPreview ? { _summary: commandPreview } : {},
|
|
104
108
|
requesterIdentifier: nonEmpty(parsed.requesterIdentifier),
|
|
105
109
|
}
|
|
106
110
|
: undefined,
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single sink for the per-(request, surface) `canonical_guardian_deliveries`
|
|
3
|
+
* registry.
|
|
4
|
+
*
|
|
5
|
+
* Guardian-request producers (access requests, tool approvals, tool-grant
|
|
6
|
+
* escalations, voice questions, trusted-contact confirmations) emit a
|
|
7
|
+
* notification signal and then record one canonical delivery row per surface the
|
|
8
|
+
* card reached. Capturing the surface address here — the conversation id for the
|
|
9
|
+
* in-app vellum card, or the channel-native message id (e.g. Slack `ts`) for a
|
|
10
|
+
* channel card — is what lets a delivered card be addressed back to its request
|
|
11
|
+
* later:
|
|
12
|
+
*
|
|
13
|
+
* - to withdraw it in place when the request resolves
|
|
14
|
+
* (`approvals/guardian-card-withdrawal.ts`), and
|
|
15
|
+
* - to resolve an emoji reaction on the card to the right request when several
|
|
16
|
+
* are pending in the same chat
|
|
17
|
+
* (`getPendingCanonicalRequestByDestinationMessage`).
|
|
18
|
+
*
|
|
19
|
+
* Every producer records through here so the addressing convention lives in one
|
|
20
|
+
* place and cannot drift between the path that writes the row and the paths that
|
|
21
|
+
* read it back.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
type CanonicalGuardianDelivery,
|
|
26
|
+
createCanonicalGuardianDelivery,
|
|
27
|
+
updateCanonicalGuardianDelivery,
|
|
28
|
+
} from "../memory/canonical-guardian-store.js";
|
|
29
|
+
import { getLogger } from "../util/logger.js";
|
|
30
|
+
import type { NotificationDeliveryResult } from "./types.js";
|
|
31
|
+
|
|
32
|
+
const log = getLogger("canonical-delivery-recorder");
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Where an approval card was delivered. Exactly one addressing modality is
|
|
36
|
+
* meaningful per channel:
|
|
37
|
+
*
|
|
38
|
+
* - vellum (in-app): addressed by `conversationId` + the card's surface id.
|
|
39
|
+
* - channels (slack/telegram/...): addressed by `chatId` + the channel-native
|
|
40
|
+
* `messageId` (Slack `ts`).
|
|
41
|
+
*/
|
|
42
|
+
export interface ApprovalCardDeliveryAddress {
|
|
43
|
+
requestId: string;
|
|
44
|
+
channel: string;
|
|
45
|
+
/** In-app vellum addressing: the conversation the card was posted to. */
|
|
46
|
+
conversationId?: string;
|
|
47
|
+
/** Channel addressing: the chat the card was delivered to. */
|
|
48
|
+
chatId?: string;
|
|
49
|
+
/** Channel-native message id (e.g. Slack `ts`) — the reaction/withdrawal key. */
|
|
50
|
+
messageId?: string;
|
|
51
|
+
/** Initial delivery status (defaults to "pending"). */
|
|
52
|
+
status?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Record where an approval card was delivered so it can be located later.
|
|
57
|
+
*
|
|
58
|
+
* Best-effort: a recording failure must never be mistaken for a delivery
|
|
59
|
+
* failure (which, in the prompt path, would trigger a fallback re-post), so
|
|
60
|
+
* errors are swallowed and logged and `null` is returned. Callers that need the
|
|
61
|
+
* row id (to apply a status afterwards) must null-check the result.
|
|
62
|
+
*/
|
|
63
|
+
export function recordApprovalCardDelivery(
|
|
64
|
+
address: ApprovalCardDeliveryAddress,
|
|
65
|
+
): CanonicalGuardianDelivery | null {
|
|
66
|
+
try {
|
|
67
|
+
return createCanonicalGuardianDelivery({
|
|
68
|
+
requestId: address.requestId,
|
|
69
|
+
destinationChannel: address.channel,
|
|
70
|
+
destinationConversationId: address.conversationId,
|
|
71
|
+
destinationChatId: address.chatId,
|
|
72
|
+
destinationMessageId: address.messageId,
|
|
73
|
+
...(address.status ? { status: address.status } : {}),
|
|
74
|
+
});
|
|
75
|
+
} catch (err) {
|
|
76
|
+
log.error(
|
|
77
|
+
{ err, requestId: address.requestId, channel: address.channel },
|
|
78
|
+
"Failed to record approval card delivery; reaction/withdrawal on this card will not resolve",
|
|
79
|
+
);
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Record every delivery for a guardian request from a notification signal's
|
|
86
|
+
* results, persisting each delivery's terminal status.
|
|
87
|
+
*
|
|
88
|
+
* This is the shared post-broadcast recording loop for the signal-based
|
|
89
|
+
* producers. The vellum row is normally created up front in the signal's
|
|
90
|
+
* `onConversationCreated` callback so the in-app client sees it immediately; pass
|
|
91
|
+
* that row's id as `vellumDeliveryId` and it is reused (only its status applied)
|
|
92
|
+
* — otherwise the vellum row is created here from the result.
|
|
93
|
+
*
|
|
94
|
+
* The pipeline result carries the delivered address directly: a `vellum` result
|
|
95
|
+
* carries a `conversationId`; channel results carry the chat (`destination`) and
|
|
96
|
+
* channel-native id (`messageId`). A blank `destination` is recorded as unknown
|
|
97
|
+
* rather than persisting the literal channel name as a chat id. Status is
|
|
98
|
+
* diagnostic — the read paths key off addressing, not status.
|
|
99
|
+
*
|
|
100
|
+
* Returns the vellum delivery id (passed in, or created here) so a caller can
|
|
101
|
+
* record its own "pipeline produced no vellum delivery" fallback.
|
|
102
|
+
*/
|
|
103
|
+
export function recordGuardianRequestDeliveries(params: {
|
|
104
|
+
requestId: string;
|
|
105
|
+
deliveryResults: NotificationDeliveryResult[];
|
|
106
|
+
vellumDeliveryId?: string;
|
|
107
|
+
}): string | undefined {
|
|
108
|
+
const { requestId, deliveryResults } = params;
|
|
109
|
+
let vellumDeliveryId = params.vellumDeliveryId;
|
|
110
|
+
|
|
111
|
+
for (const result of deliveryResults) {
|
|
112
|
+
let deliveryId: string | undefined;
|
|
113
|
+
if (result.channel === "vellum") {
|
|
114
|
+
if (!vellumDeliveryId) {
|
|
115
|
+
vellumDeliveryId = recordApprovalCardDelivery({
|
|
116
|
+
requestId,
|
|
117
|
+
channel: "vellum",
|
|
118
|
+
conversationId: result.conversationId,
|
|
119
|
+
})?.id;
|
|
120
|
+
}
|
|
121
|
+
deliveryId = vellumDeliveryId;
|
|
122
|
+
} else {
|
|
123
|
+
deliveryId = recordApprovalCardDelivery({
|
|
124
|
+
requestId,
|
|
125
|
+
channel: result.channel,
|
|
126
|
+
chatId: result.destination.length > 0 ? result.destination : undefined,
|
|
127
|
+
messageId: result.messageId,
|
|
128
|
+
})?.id;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (deliveryId) {
|
|
132
|
+
updateCanonicalGuardianDelivery(deliveryId, {
|
|
133
|
+
status: result.status === "sent" ? "sent" : "failed",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return vellumDeliveryId;
|
|
139
|
+
}
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
* values from the context payload.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { buildAccessRequestContractText } from "./access-request-copy.js";
|
|
12
13
|
import {
|
|
13
|
-
buildAccessRequestContractText,
|
|
14
14
|
buildAccessRequestSeedContentBlocks,
|
|
15
|
-
|
|
15
|
+
buildToolApprovalSeedContentBlocks,
|
|
16
|
+
} from "./approval-card-data.js";
|
|
16
17
|
import {
|
|
17
18
|
buildGuardianRequestCodeInstruction,
|
|
18
19
|
parseGuardianQuestionPayload,
|
|
@@ -28,7 +29,6 @@ import type {
|
|
|
28
29
|
NotificationSignal,
|
|
29
30
|
NotificationSourceEventName,
|
|
30
31
|
} from "./signal.js";
|
|
31
|
-
import { buildToolApprovalSeedContentBlocks } from "./tool-approval-copy.js";
|
|
32
32
|
import type { NotificationChannel, RenderedChannelCopy } from "./types.js";
|
|
33
33
|
|
|
34
34
|
type CopyTemplate = (payload: Record<string, unknown>) => RenderedChannelCopy;
|
|
@@ -26,10 +26,13 @@ import { truncate } from "../util/truncate.js";
|
|
|
26
26
|
import {
|
|
27
27
|
buildAccessRequestContractText,
|
|
28
28
|
buildAccessRequestInviteDirective,
|
|
29
|
-
buildAccessRequestSeedContentBlocks,
|
|
30
29
|
hasAccessRequestInstructions,
|
|
31
30
|
hasInviteFlowDirective,
|
|
32
31
|
} from "./access-request-copy.js";
|
|
32
|
+
import {
|
|
33
|
+
buildAccessRequestSeedContentBlocks,
|
|
34
|
+
buildToolApprovalSeedContentBlocks,
|
|
35
|
+
} from "./approval-card-data.js";
|
|
33
36
|
import {
|
|
34
37
|
buildConversationCandidates,
|
|
35
38
|
type ConversationCandidateSet,
|
|
@@ -46,7 +49,6 @@ import {
|
|
|
46
49
|
import { nonEmpty, readPayloadString } from "./notification-utils.js";
|
|
47
50
|
import { getPreferenceSummary } from "./preference-summary.js";
|
|
48
51
|
import type { NotificationSignal, RoutingIntent } from "./signal.js";
|
|
49
|
-
import { buildToolApprovalSeedContentBlocks } from "./tool-approval-copy.js";
|
|
50
52
|
import type {
|
|
51
53
|
ConversationAction,
|
|
52
54
|
NotificationChannel,
|
|
@@ -70,13 +70,12 @@ export function resolveDestinations(
|
|
|
70
70
|
channel: channel as NotificationChannel,
|
|
71
71
|
endpoint: externalChatId,
|
|
72
72
|
metadata: {
|
|
73
|
-
externalUserId: guardianResult.channel.
|
|
73
|
+
externalUserId: guardianResult.channel.address,
|
|
74
74
|
},
|
|
75
75
|
bindingContext: {
|
|
76
76
|
sourceChannel: channel as NotificationChannel,
|
|
77
77
|
externalChatId,
|
|
78
|
-
externalUserId:
|
|
79
|
-
guardianResult.channel.externalUserId ?? undefined,
|
|
78
|
+
externalUserId: guardianResult.channel.address,
|
|
80
79
|
},
|
|
81
80
|
});
|
|
82
81
|
}
|
|
@@ -101,13 +100,12 @@ export function resolveDestinations(
|
|
|
101
100
|
channel: "slack",
|
|
102
101
|
endpoint: chatId,
|
|
103
102
|
metadata: {
|
|
104
|
-
externalUserId: guardianResult.channel.
|
|
103
|
+
externalUserId: guardianResult.channel.address,
|
|
105
104
|
},
|
|
106
105
|
bindingContext: {
|
|
107
106
|
sourceChannel: "slack",
|
|
108
107
|
externalChatId: chatId,
|
|
109
|
-
externalUserId:
|
|
110
|
-
guardianResult.channel.externalUserId ?? undefined,
|
|
108
|
+
externalUserId: guardianResult.channel.address,
|
|
111
109
|
},
|
|
112
110
|
});
|
|
113
111
|
} else if (guardianResult && chatId) {
|
|
@@ -82,11 +82,19 @@ export const ToolApprovalPayloadSchema =
|
|
|
82
82
|
GuardianQuestionPayloadBaseSchema.extend({
|
|
83
83
|
requestKind: z.literal("tool_approval"),
|
|
84
84
|
toolName: z.string().min(1),
|
|
85
|
+
/** Risk classification from the permission checker (e.g. "low", "medium", "high"). */
|
|
86
|
+
riskLevel: z.string().optional(),
|
|
87
|
+
/** Secret-redacted summary of the tool invocation arguments. */
|
|
88
|
+
commandPreview: z.string().optional(),
|
|
85
89
|
});
|
|
86
90
|
|
|
87
91
|
export const ToolGrantPayloadSchema = GuardianQuestionPayloadBaseSchema.extend({
|
|
88
92
|
requestKind: z.literal("tool_grant_request"),
|
|
89
93
|
toolName: z.string().min(1),
|
|
94
|
+
/** Risk classification from the permission checker (e.g. "low", "medium", "high"). */
|
|
95
|
+
riskLevel: z.string().optional(),
|
|
96
|
+
/** Secret-redacted summary of the tool invocation arguments. */
|
|
97
|
+
commandPreview: z.string().optional(),
|
|
90
98
|
});
|
|
91
99
|
|
|
92
100
|
export const AccessRequestGuardianPayloadSchema =
|
|
@@ -124,6 +132,8 @@ export const LenientToolApprovalPayloadSchema = z.object({
|
|
|
124
132
|
requesterIdentifier: z.string().nullable().optional(),
|
|
125
133
|
requesterExternalUserId: z.string().nullable().optional(),
|
|
126
134
|
requesterChatId: z.string().nullable().optional(),
|
|
135
|
+
riskLevel: z.string().nullable().optional(),
|
|
136
|
+
commandPreview: z.string().nullable().optional(),
|
|
127
137
|
});
|
|
128
138
|
|
|
129
139
|
export type LenientToolApprovalPayload = z.infer<
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
type FeedItemCategory,
|
|
16
16
|
type FeedItemDetailPanelKind,
|
|
17
17
|
feedItemSchema,
|
|
18
|
-
type FeedItemUrgency,
|
|
19
18
|
} from "../home/feed-types.js";
|
|
20
19
|
import { appendFeedItem } from "../home/feed-writer.js";
|
|
21
20
|
import { getConversation } from "../memory/conversation-crud.js";
|
|
@@ -28,13 +27,6 @@ import type { NotificationDecision, RenderedChannelCopy } from "./types.js";
|
|
|
28
27
|
|
|
29
28
|
const log = getLogger("home-feed-side-effect");
|
|
30
29
|
|
|
31
|
-
const FEED_ITEM_URGENCIES: ReadonlySet<string> = new Set<FeedItemUrgency>([
|
|
32
|
-
"low",
|
|
33
|
-
"medium",
|
|
34
|
-
"high",
|
|
35
|
-
"critical",
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
30
|
/**
|
|
39
31
|
* Append a `FeedItem` for the given notification signal when the
|
|
40
32
|
* filter criteria pass.
|
|
@@ -97,9 +89,7 @@ export async function writeHomeFeedItemForSignal(
|
|
|
97
89
|
return null;
|
|
98
90
|
}
|
|
99
91
|
|
|
100
|
-
const urgency =
|
|
101
|
-
? (signal.attentionHints.urgency as FeedItemUrgency)
|
|
102
|
-
: undefined;
|
|
92
|
+
const urgency = signal.attentionHints.urgency;
|
|
103
93
|
const now = new Date().toISOString();
|
|
104
94
|
|
|
105
95
|
const category = deriveCategory(signal);
|
|
@@ -109,7 +99,7 @@ export async function writeHomeFeedItemForSignal(
|
|
|
109
99
|
signal.contextPayload &&
|
|
110
100
|
typeof signal.contextPayload === "object" &&
|
|
111
101
|
!Array.isArray(signal.contextPayload)
|
|
112
|
-
? { ...
|
|
102
|
+
? { ...signal.contextPayload }
|
|
113
103
|
: undefined;
|
|
114
104
|
|
|
115
105
|
// Link scheduled-run notifications back to their schedule. `notify`-mode
|
|
@@ -187,7 +177,7 @@ function deriveDetailPanelKind(
|
|
|
187
177
|
const payload = signal.contextPayload;
|
|
188
178
|
const kind =
|
|
189
179
|
payload && typeof payload === "object" && "requestKind" in payload
|
|
190
|
-
?
|
|
180
|
+
? payload.requestKind
|
|
191
181
|
: undefined;
|
|
192
182
|
if (kind === "tool_approval" || kind === "tool_grant_request") {
|
|
193
183
|
return "permissionChat";
|
|
@@ -24,7 +24,8 @@ export function readPayloadString(
|
|
|
24
24
|
payload: unknown,
|
|
25
25
|
key: string,
|
|
26
26
|
): string | undefined {
|
|
27
|
-
if (!payload || typeof payload !== "object"
|
|
27
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
28
|
+
return undefined;
|
|
28
29
|
const value = (payload as Record<string, unknown>)[key];
|
|
29
30
|
return typeof value === "string" ? value : undefined;
|
|
30
31
|
}
|