@vellumai/assistant 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -88,6 +88,7 @@ function getBroadcaster(): NotificationBroadcaster {
|
|
|
88
88
|
targetGuardianPrincipalId: info.targetGuardianPrincipalId,
|
|
89
89
|
groupId: info.groupId,
|
|
90
90
|
source: info.source,
|
|
91
|
+
silent: info.silent,
|
|
91
92
|
});
|
|
92
93
|
log.info(
|
|
93
94
|
{
|
|
@@ -387,10 +388,18 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
387
388
|
// Step 5: Mirror background-origin signals into the home activity feed.
|
|
388
389
|
// The helper itself decides whether to write (background filter); we
|
|
389
390
|
// catch and log so a feed-write failure cannot poison the dispatch result.
|
|
391
|
+
// Pass the paired vellum delivery conversation as a fallback so producers
|
|
392
|
+
// whose `sourceContextId` is a sentinel string (e.g. heartbeat startup,
|
|
393
|
+
// credential health, watcher emits, scheduler retries-exhausted) still
|
|
394
|
+
// get a "Go to Convo" button — pointing at the conversation the
|
|
395
|
+
// broadcaster paired the notification with.
|
|
396
|
+
const pairedVellumConversationId = dispatchResult.deliveryResults.find(
|
|
397
|
+
(r) => r.channel === "vellum",
|
|
398
|
+
)?.conversationId;
|
|
390
399
|
await writeHomeFeedItemForSignal(
|
|
391
400
|
signal,
|
|
392
401
|
decision,
|
|
393
|
-
|
|
402
|
+
pairedVellumConversationId,
|
|
394
403
|
).catch((err) => {
|
|
395
404
|
log.warn({ err, signalId }, "writeHomeFeedItemForSignal threw");
|
|
396
405
|
});
|
|
@@ -22,10 +22,7 @@ import { getConversation } from "../memory/conversation-crud.js";
|
|
|
22
22
|
import { isBackgroundConversationType } from "../memory/conversation-types.js";
|
|
23
23
|
import { getLogger } from "../util/logger.js";
|
|
24
24
|
import type { NotificationSignal } from "./signal.js";
|
|
25
|
-
import type {
|
|
26
|
-
NotificationDecision,
|
|
27
|
-
NotificationDeliveryResult,
|
|
28
|
-
} from "./types.js";
|
|
25
|
+
import type { NotificationDecision, RenderedChannelCopy } from "./types.js";
|
|
29
26
|
|
|
30
27
|
const log = getLogger("home-feed-side-effect");
|
|
31
28
|
|
|
@@ -40,6 +37,16 @@ const FEED_ITEM_URGENCIES: ReadonlySet<string> = new Set<FeedItemUrgency>([
|
|
|
40
37
|
* Append a `FeedItem` for the given notification signal when the
|
|
41
38
|
* filter criteria pass.
|
|
42
39
|
*
|
|
40
|
+
* `fallbackConversationId` is used as the feed item's "Go to Convo"
|
|
41
|
+
* navigation target when `signal.sourceContextId` doesn't resolve to a
|
|
42
|
+
* real conversation row. The notification broadcaster pairs the vellum
|
|
43
|
+
* delivery with a conversation (newly created or reused) before this
|
|
44
|
+
* function runs, so callers can thread that paired id through here for
|
|
45
|
+
* producers whose `sourceContextId` is a sentinel (heartbeat startup,
|
|
46
|
+
* credential health, watcher emits, scheduler retries-exhausted) — the
|
|
47
|
+
* feed item will then carry the paired delivery conversation and the
|
|
48
|
+
* "Go to Convo" button can render.
|
|
49
|
+
*
|
|
43
50
|
* Returns the persisted `FeedItem`, or `null` if the signal does not
|
|
44
51
|
* qualify for home-feed mirroring (non-background origin AND no
|
|
45
52
|
* `isAsyncBackground` hint) or if schema validation fails.
|
|
@@ -47,17 +54,39 @@ const FEED_ITEM_URGENCIES: ReadonlySet<string> = new Set<FeedItemUrgency>([
|
|
|
47
54
|
export async function writeHomeFeedItemForSignal(
|
|
48
55
|
signal: NotificationSignal,
|
|
49
56
|
decision: NotificationDecision,
|
|
50
|
-
|
|
57
|
+
fallbackConversationId?: string,
|
|
51
58
|
): Promise<FeedItem | null> {
|
|
52
|
-
|
|
59
|
+
const { mirror, sourceConversationId } = resolveHomeFeedMirror(
|
|
60
|
+
signal,
|
|
61
|
+
fallbackConversationId,
|
|
62
|
+
);
|
|
63
|
+
if (!mirror) return null;
|
|
53
64
|
|
|
54
|
-
const renderedCopy =
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
const renderedCopy =
|
|
66
|
+
decision.renderedCopy.vellum ??
|
|
67
|
+
firstSelectedRenderedCopy(decision.renderedCopy, decision.selectedChannels);
|
|
68
|
+
const payloadTitle =
|
|
69
|
+
readPayloadString(signal.contextPayload, "title") ??
|
|
70
|
+
readPayloadString(signal.contextPayload, "requestedTitle");
|
|
71
|
+
const payloadBody =
|
|
72
|
+
readPayloadString(signal.contextPayload, "body") ??
|
|
73
|
+
readPayloadString(signal.contextPayload, "requestedMessage");
|
|
74
|
+
|
|
75
|
+
// Source the title from the payload only. The LLM's `renderedCopy.title`
|
|
76
|
+
// often echoes the body when no explicit title was passed, which stutters
|
|
77
|
+
// against `summary` in the row. Leave undefined when absent; renderers
|
|
78
|
+
// fall back to `summary`.
|
|
79
|
+
const resolvedTitle = payloadTitle?.trim() || undefined;
|
|
80
|
+
const resolvedSummary =
|
|
81
|
+
renderedCopy?.body?.trim() || payloadBody?.trim() || "";
|
|
82
|
+
if (!resolvedSummary) {
|
|
83
|
+
log.warn(
|
|
84
|
+
{ signalId: signal.signalId, sourceEventName: signal.sourceEventName },
|
|
85
|
+
"Home-feed write skipped: no summary available (would have fallen back to event name)",
|
|
86
|
+
);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
57
89
|
|
|
58
|
-
const conversationId = deliveryResults.find(
|
|
59
|
-
(r) => r.channel === "vellum",
|
|
60
|
-
)?.conversationId;
|
|
61
90
|
const urgency = FEED_ITEM_URGENCIES.has(signal.attentionHints.urgency)
|
|
62
91
|
? (signal.attentionHints.urgency as FeedItemUrgency)
|
|
63
92
|
: undefined;
|
|
@@ -76,14 +105,16 @@ export async function writeHomeFeedItemForSignal(
|
|
|
76
105
|
id: `notif:${signal.signalId}`,
|
|
77
106
|
type: "notification",
|
|
78
107
|
priority: 50,
|
|
79
|
-
title:
|
|
80
|
-
summary:
|
|
108
|
+
...(resolvedTitle ? { title: resolvedTitle } : {}),
|
|
109
|
+
summary: resolvedSummary,
|
|
81
110
|
timestamp: now,
|
|
82
111
|
createdAt: now,
|
|
83
112
|
status: "new",
|
|
84
113
|
category,
|
|
114
|
+
noteworthy: deriveNoteworthy(signal),
|
|
115
|
+
fromAssistant: signal.sourceChannel === "assistant_tool",
|
|
85
116
|
...(urgency ? { urgency } : {}),
|
|
86
|
-
...(
|
|
117
|
+
...(sourceConversationId ? { conversationId: sourceConversationId } : {}),
|
|
87
118
|
...(panelKind ? { detailPanel: { kind: panelKind } } : {}),
|
|
88
119
|
...(metadata ? { metadata } : {}),
|
|
89
120
|
};
|
|
@@ -108,7 +139,6 @@ const EVENT_CATEGORY_MAP: Record<string, FeedItemCategory> = {
|
|
|
108
139
|
"credential.health_alert": "security",
|
|
109
140
|
"activity.failed": "background",
|
|
110
141
|
"activity.complete": "background",
|
|
111
|
-
"heartbeat.alert": "system",
|
|
112
142
|
"watcher.notification": "system",
|
|
113
143
|
"schedule.notify": "scheduling",
|
|
114
144
|
"guardian.question": "security",
|
|
@@ -143,19 +173,51 @@ function deriveDetailPanelKind(
|
|
|
143
173
|
}
|
|
144
174
|
|
|
145
175
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
176
|
+
* The lookup is best-effort and unified: a single `getConversation` call
|
|
177
|
+
* both gates the "background conversation" mirror branch and populates
|
|
178
|
+
* `sourceConversationId` for the "Go to Thread" navigation target. Misses
|
|
179
|
+
* (scheduler job ids, watcher event ids, CLI tool-call ids) leave
|
|
180
|
+
* `sourceConversationId` undefined so the client hides the affordance.
|
|
181
|
+
*
|
|
182
|
+
* `assistant_tool` mirrors unconditionally because the documented
|
|
183
|
+
* `notifications send` skill (and background-job failure emits) deliberately
|
|
184
|
+
* does not require a background-typed conversation or the
|
|
185
|
+
* `isAsyncBackground` hint.
|
|
149
186
|
*/
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
187
|
+
function resolveHomeFeedMirror(
|
|
188
|
+
signal: NotificationSignal,
|
|
189
|
+
fallbackConversationId?: string,
|
|
190
|
+
): {
|
|
191
|
+
mirror: boolean;
|
|
192
|
+
sourceConversationId?: string;
|
|
193
|
+
} {
|
|
194
|
+
let sourceRow: { conversationType?: string } | null = null;
|
|
195
|
+
if (signal.sourceContextId) {
|
|
196
|
+
try {
|
|
197
|
+
sourceRow = getConversation(signal.sourceContextId) ?? null;
|
|
198
|
+
} catch {
|
|
199
|
+
sourceRow = null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Prefer the producer's source context (e.g. the heartbeat / background
|
|
203
|
+
// job conversation that emitted the signal) for the "Go to Convo" target,
|
|
204
|
+
// since that's where the work actually happened. Fall back to the paired
|
|
205
|
+
// delivery conversation only when the source context didn't resolve —
|
|
206
|
+
// covers producers whose `sourceContextId` is a sentinel string.
|
|
207
|
+
const sourceConversationId = sourceRow
|
|
208
|
+
? signal.sourceContextId
|
|
209
|
+
: fallbackConversationId;
|
|
210
|
+
|
|
211
|
+
if (signal.sourceChannel === "assistant_tool") {
|
|
212
|
+
return { mirror: true, sourceConversationId };
|
|
213
|
+
}
|
|
214
|
+
if (signal.attentionHints.isAsyncBackground) {
|
|
215
|
+
return { mirror: true, sourceConversationId };
|
|
158
216
|
}
|
|
217
|
+
if (isBackgroundConversationType(sourceRow?.conversationType)) {
|
|
218
|
+
return { mirror: true, sourceConversationId };
|
|
219
|
+
}
|
|
220
|
+
return { mirror: false };
|
|
159
221
|
}
|
|
160
222
|
|
|
161
223
|
function readPayloadString(payload: unknown, key: string): string | undefined {
|
|
@@ -163,3 +225,50 @@ function readPayloadString(payload: unknown, key: string): string | undefined {
|
|
|
163
225
|
const value = (payload as Record<string, unknown>)[key];
|
|
164
226
|
return typeof value === "string" ? value : undefined;
|
|
165
227
|
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Routing-intent enforcement can prune `selectedChannels` without also
|
|
231
|
+
* pruning `renderedCopy`, so iterating `renderedCopy` directly risks
|
|
232
|
+
* surfacing copy for a channel that was never delivered. Walk
|
|
233
|
+
* `selectedChannels` in order instead so the channel that actually shipped
|
|
234
|
+
* wins.
|
|
235
|
+
*/
|
|
236
|
+
function firstSelectedRenderedCopy(
|
|
237
|
+
renderedCopy: NotificationDecision["renderedCopy"],
|
|
238
|
+
selectedChannels: NotificationDecision["selectedChannels"],
|
|
239
|
+
): RenderedChannelCopy | undefined {
|
|
240
|
+
for (const channel of selectedChannels) {
|
|
241
|
+
const copy = renderedCopy[channel];
|
|
242
|
+
if (copy && (copy.title?.trim() || copy.body?.trim())) return copy;
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── Noteworthy derivation ─────────────────────────────────────────────
|
|
248
|
+
//
|
|
249
|
+
// Clients split the feed into inbox-style (noteworthy) and activity-style
|
|
250
|
+
// (routine) surfaces. Assistant-initiated shares and a small allow-list of
|
|
251
|
+
// high-importance system events land in the inbox; routine background
|
|
252
|
+
// signals stay in activity.
|
|
253
|
+
|
|
254
|
+
const NOTEWORTHY_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
255
|
+
"guardian.question",
|
|
256
|
+
"guardian.channel_activation",
|
|
257
|
+
"ingress.access_request",
|
|
258
|
+
"ingress.escalation",
|
|
259
|
+
"credential.health_alert",
|
|
260
|
+
]);
|
|
261
|
+
|
|
262
|
+
function deriveNoteworthy(signal: NotificationSignal): boolean {
|
|
263
|
+
// Background-job failures emit with `sourceChannel: "assistant_tool"`
|
|
264
|
+
// (see `runtime/background-job-runner.ts`), so the activity.failed rule
|
|
265
|
+
// must run BEFORE the assistant_tool short-circuit — otherwise every
|
|
266
|
+
// routine watcher/heartbeat failure would land in the Inbox instead of
|
|
267
|
+
// staying in the activity feed.
|
|
268
|
+
if (signal.sourceEventName === "activity.failed") {
|
|
269
|
+
return signal.attentionHints.urgency === "critical";
|
|
270
|
+
}
|
|
271
|
+
if (signal.sourceChannel === "assistant_tool") return true;
|
|
272
|
+
if (NOTEWORTHY_EVENT_NAMES.has(signal.sourceEventName)) return true;
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
@@ -107,10 +107,6 @@ export const NOTIFICATION_SOURCE_EVENT_NAMES = [
|
|
|
107
107
|
description:
|
|
108
108
|
"OAuth credential health issue detected (expired, revoked, missing scopes)",
|
|
109
109
|
},
|
|
110
|
-
{
|
|
111
|
-
id: "heartbeat.alert",
|
|
112
|
-
description: "Heartbeat found something worth surfacing to the guardian",
|
|
113
|
-
},
|
|
114
110
|
] as const;
|
|
115
111
|
|
|
116
112
|
export type NotificationSourceEventName =
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ChannelPolicies } from "../channels/config.js";
|
|
9
9
|
import type { ChannelId } from "../channels/types.js";
|
|
10
|
+
import type { AttentionHints } from "./signal.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Derived from the channel policy registry: only channels whose
|
|
@@ -81,6 +82,13 @@ export interface ChannelDeliveryPayload {
|
|
|
81
82
|
deepLinkTarget?: Record<string, unknown>;
|
|
82
83
|
/** Original signal context payload — available for channel-specific structured rendering. */
|
|
83
84
|
contextPayload?: Record<string, unknown>;
|
|
85
|
+
/**
|
|
86
|
+
* Forwarded from the originating signal so adapters can make
|
|
87
|
+
* urgency-aware decisions (e.g. the vellum adapter suppresses the OS
|
|
88
|
+
* banner for non-urgent intents while still emitting the conversation
|
|
89
|
+
* pairing side effects).
|
|
90
|
+
*/
|
|
91
|
+
urgency: AttentionHints["urgency"];
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
/** Interface that each channel adapter must implement. */
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { emitPostConnectNudge } from "../home/post-connect-feed.js";
|
|
23
|
+
import { invalidateAssistantSuggestedPromptsCache } from "../home/suggested-prompts.js";
|
|
23
24
|
import type { TokenEndpointAuthMethod } from "../security/oauth2.js";
|
|
24
25
|
import { prepareOAuth2Flow, startOAuth2Flow } from "../security/oauth2.js";
|
|
25
26
|
import { getLogger } from "../util/logger.js";
|
|
@@ -252,6 +253,7 @@ export async function orchestrateOAuthConnect(
|
|
|
252
253
|
},
|
|
253
254
|
"Deferred OAuth2 flow completed — tokens stored",
|
|
254
255
|
);
|
|
256
|
+
invalidateAssistantSuggestedPromptsCache();
|
|
255
257
|
void emitPostConnectNudge(options.service);
|
|
256
258
|
options.onDeferredComplete?.({
|
|
257
259
|
success: true,
|
|
@@ -375,6 +377,7 @@ export async function orchestrateOAuthConnect(
|
|
|
375
377
|
"orchestrateOAuthConnect: tokens stored, connect complete",
|
|
376
378
|
);
|
|
377
379
|
|
|
380
|
+
invalidateAssistantSuggestedPromptsCache();
|
|
378
381
|
void emitPostConnectNudge(options.service);
|
|
379
382
|
|
|
380
383
|
return {
|
|
@@ -49,6 +49,8 @@ function manualTokenAccessCredentialKey(provider: string): string | null {
|
|
|
49
49
|
return credentialKey("slack_channel", "bot_token");
|
|
50
50
|
case "telegram":
|
|
51
51
|
return credentialKey("telegram", "bot_token");
|
|
52
|
+
case "sanity":
|
|
53
|
+
return credentialKey("sanity", "api_token");
|
|
52
54
|
default:
|
|
53
55
|
return null;
|
|
54
56
|
}
|
|
@@ -122,6 +122,24 @@ export async function syncManualTokenConnection(
|
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
case "sanity": {
|
|
126
|
+
const tokenResult = await getSecureKeyResultAsync(
|
|
127
|
+
credentialKey("sanity", "api_token"),
|
|
128
|
+
);
|
|
129
|
+
if (tokenResult.unreachable) {
|
|
130
|
+
log.warn(
|
|
131
|
+
"Skipping sanity manual-token reconciliation — credential backend unreachable",
|
|
132
|
+
);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (tokenResult.value) {
|
|
136
|
+
await ensureManualTokenConnection(provider, accountInfo);
|
|
137
|
+
} else {
|
|
138
|
+
removeManualTokenConnection(provider);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
125
143
|
default:
|
|
126
144
|
return;
|
|
127
145
|
}
|
|
@@ -142,4 +160,5 @@ export async function syncManualTokenConnection(
|
|
|
142
160
|
export async function backfillManualTokenConnections(): Promise<void> {
|
|
143
161
|
await syncManualTokenConnection("telegram");
|
|
144
162
|
await syncManualTokenConnection("slack_channel");
|
|
163
|
+
await syncManualTokenConnection("sanity");
|
|
145
164
|
}
|
package/src/oauth/oauth-store.ts
CHANGED
|
@@ -1100,5 +1100,17 @@ export async function disconnectOAuthProvider(
|
|
|
1100
1100
|
|
|
1101
1101
|
deleteConnection(conn.id);
|
|
1102
1102
|
|
|
1103
|
+
// Dynamic import: `suggested-prompts.ts` imports from this module, so a
|
|
1104
|
+
// static import here would create a cycle. The cache invalidation is
|
|
1105
|
+
// best-effort — failures must not block disconnect.
|
|
1106
|
+
void import("../home/suggested-prompts.js")
|
|
1107
|
+
.then((m) => m.invalidateAssistantSuggestedPromptsCache())
|
|
1108
|
+
.catch((err) => {
|
|
1109
|
+
log.warn(
|
|
1110
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
1111
|
+
"Failed to invalidate suggested-prompts cache after disconnect",
|
|
1112
|
+
);
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1103
1115
|
return "disconnected";
|
|
1104
1116
|
}
|
|
@@ -261,17 +261,57 @@ describe("PlatformOAuthConnection", () => {
|
|
|
261
261
|
).rejects.toThrow(CredentialRequiredError);
|
|
262
262
|
});
|
|
263
263
|
|
|
264
|
-
test("502 response throws ProviderUnreachableError", async () => {
|
|
264
|
+
test("502 response retries then throws ProviderUnreachableError", async () => {
|
|
265
|
+
let callCount = 0;
|
|
266
|
+
const client = makeMockClient(
|
|
267
|
+
mock(async () => {
|
|
268
|
+
callCount++;
|
|
269
|
+
return new Response("", { status: 502 });
|
|
270
|
+
}) as unknown as typeof globalThis.fetch,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
|
|
274
|
+
await expect(
|
|
275
|
+
conn.request({ method: "GET", path: "/test" }),
|
|
276
|
+
).rejects.toThrow(ProviderUnreachableError);
|
|
277
|
+
// 1 initial + 3 retries = 4 total attempts
|
|
278
|
+
expect(callCount).toBe(4);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("502 response includes detail from response body", async () => {
|
|
265
282
|
const client = makeMockClient(
|
|
266
283
|
mock(
|
|
267
|
-
async () => new Response("", { status: 502 }),
|
|
284
|
+
async () => new Response("upstream timeout after 30s", { status: 502 }),
|
|
268
285
|
) as unknown as typeof globalThis.fetch,
|
|
269
286
|
);
|
|
270
287
|
|
|
271
288
|
const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
|
|
272
289
|
await expect(
|
|
273
290
|
conn.request({ method: "GET", path: "/test" }),
|
|
274
|
-
).rejects.toThrow(
|
|
291
|
+
).rejects.toThrow(/upstream timeout after 30s/);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("502 recovers on retry", async () => {
|
|
295
|
+
let callCount = 0;
|
|
296
|
+
const client = makeMockClient(
|
|
297
|
+
mock(async () => {
|
|
298
|
+
callCount++;
|
|
299
|
+
if (callCount <= 2) {
|
|
300
|
+
return new Response("", { status: 502 });
|
|
301
|
+
}
|
|
302
|
+
return new Response(
|
|
303
|
+
JSON.stringify({ status: 200, headers: {}, body: { ok: true } }),
|
|
304
|
+
{ status: 200 },
|
|
305
|
+
);
|
|
306
|
+
}) as unknown as typeof globalThis.fetch,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
|
|
310
|
+
const result = await conn.request({ method: "GET", path: "/test" });
|
|
311
|
+
|
|
312
|
+
expect(result.status).toBe(200);
|
|
313
|
+
expect(result.body).toEqual({ ok: true });
|
|
314
|
+
expect(callCount).toBe(3);
|
|
275
315
|
});
|
|
276
316
|
|
|
277
317
|
test("withToken throws clear error", async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { VellumPlatformClient } from "../platform/client.js";
|
|
2
2
|
import { BackendError } from "../util/errors.js";
|
|
3
|
+
import { getLogger } from "../util/logger.js";
|
|
3
4
|
import { getHttpRetryDelay, isRetryableStatus, sleep } from "../util/retry.js";
|
|
4
5
|
import type {
|
|
5
6
|
OAuthConnection,
|
|
@@ -7,6 +8,7 @@ import type {
|
|
|
7
8
|
OAuthConnectionResponse,
|
|
8
9
|
} from "./connection.js";
|
|
9
10
|
|
|
11
|
+
const log = getLogger("platform-oauth-connection");
|
|
10
12
|
const MAX_RETRIES = 3;
|
|
11
13
|
|
|
12
14
|
export class CredentialRequiredError extends BackendError {
|
|
@@ -111,19 +113,26 @@ export class PlatformOAuthConnection implements OAuthConnection {
|
|
|
111
113
|
throw new CredentialRequiredError();
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
if (response.status === 502) {
|
|
115
|
-
throw new ProviderUnreachableError();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
116
|
if (
|
|
119
117
|
!response.ok &&
|
|
120
118
|
isRetryableStatus(response.status) &&
|
|
121
119
|
attempt < MAX_RETRIES
|
|
122
120
|
) {
|
|
121
|
+
log.warn(
|
|
122
|
+
{ status: response.status, attempt, provider: "platform-proxy" },
|
|
123
|
+
`Retryable status ${response.status} from platform proxy (attempt ${attempt + 1}/${MAX_RETRIES + 1})`,
|
|
124
|
+
);
|
|
123
125
|
await sleep(getHttpRetryDelay(response, attempt));
|
|
124
126
|
continue;
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
if (response.status === 502) {
|
|
130
|
+
const detail = await response.text().catch(() => "");
|
|
131
|
+
throw new ProviderUnreachableError(
|
|
132
|
+
`The external service provider is temporarily unreachable (HTTP 502).${detail ? ` Detail: ${detail}` : ""} This may be a transient issue — retry after a brief pause.`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
127
136
|
if (!response.ok) {
|
|
128
137
|
throw new BackendError(
|
|
129
138
|
`Platform proxy returned unexpected status ${response.status}`,
|
|
@@ -746,6 +746,28 @@ export const PROVIDER_SEED_DATA: Record<
|
|
|
746
746
|
logoUrl: "https://cdn.simpleicons.org/telegram",
|
|
747
747
|
defaultScopes: [],
|
|
748
748
|
},
|
|
749
|
+
|
|
750
|
+
sanity: {
|
|
751
|
+
provider: "sanity",
|
|
752
|
+
authorizeUrl: "urn:manual-token",
|
|
753
|
+
tokenExchangeUrl: "urn:manual-token",
|
|
754
|
+
baseUrl: "https://api.sanity.io",
|
|
755
|
+
displayLabel: "Sanity",
|
|
756
|
+
description: "Content management platform",
|
|
757
|
+
dashboardUrl: "https://www.sanity.io/manage",
|
|
758
|
+
clientIdPlaceholder: null,
|
|
759
|
+
requiresClientSecret: false,
|
|
760
|
+
logoUrl: "https://cdn.simpleicons.org/sanity",
|
|
761
|
+
defaultScopes: [],
|
|
762
|
+
injectionTemplates: [
|
|
763
|
+
{
|
|
764
|
+
hostPattern: "*.sanity.io",
|
|
765
|
+
injectionType: "header",
|
|
766
|
+
headerName: "Authorization",
|
|
767
|
+
valuePrefix: "Bearer ",
|
|
768
|
+
},
|
|
769
|
+
],
|
|
770
|
+
},
|
|
749
771
|
};
|
|
750
772
|
|
|
751
773
|
export const SEEDED_PROVIDER_KEYS = new Set(Object.keys(PROVIDER_SEED_DATA));
|
|
@@ -196,7 +196,10 @@ export class PermissionPrompter {
|
|
|
196
196
|
}
|
|
197
197
|
// The prompter owns deregistration; all callers use get() to peek before
|
|
198
198
|
// routing to resolveConfirmation, which fires the rpcResolve callback.
|
|
199
|
-
const interaction = pendingInteractions.resolve(
|
|
199
|
+
const interaction = pendingInteractions.resolve(
|
|
200
|
+
requestId,
|
|
201
|
+
decision === "allow" ? "approved" : "rejected",
|
|
202
|
+
);
|
|
200
203
|
this.ownedIds.delete(requestId);
|
|
201
204
|
(interaction?.rpcResolve as ((v: ConfirmResult) => void) | undefined)?.(
|
|
202
205
|
{ decision, selectedPattern, selectedScope, decisionContext },
|
|
@@ -210,7 +213,7 @@ export class PermissionPrompter {
|
|
|
210
213
|
*/
|
|
211
214
|
denyAllPending(): void {
|
|
212
215
|
for (const requestId of [...this.ownedIds]) {
|
|
213
|
-
const interaction = pendingInteractions.resolve(requestId);
|
|
216
|
+
const interaction = pendingInteractions.resolve(requestId, "superseded");
|
|
214
217
|
this.ownedIds.delete(requestId);
|
|
215
218
|
(interaction?.rpcResolve as ((v: ConfirmResult) => void) | undefined)?.(
|
|
216
219
|
{
|
|
@@ -130,7 +130,10 @@ export class SecretPrompter {
|
|
|
130
130
|
}
|
|
131
131
|
// approval-routes calls pendingInteractions.get() before routing here;
|
|
132
132
|
// the prompter owns deregistration so it fires the Promise callback cleanly.
|
|
133
|
-
const interaction = pendingInteractions.resolve(
|
|
133
|
+
const interaction = pendingInteractions.resolve(
|
|
134
|
+
requestId,
|
|
135
|
+
value === undefined ? "cancelled" : "answered",
|
|
136
|
+
);
|
|
134
137
|
this.ownedIds.delete(requestId);
|
|
135
138
|
(interaction?.rpcResolve as ((v: SecretPromptResult) => void) | undefined)?.(
|
|
136
139
|
{ value: value ?? null, delivery: delivery ?? "store" },
|