@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
|
@@ -32,5 +32,7 @@ export function getVisibleProviderCatalog(
|
|
|
32
32
|
if (visibleModels.length === entry.models.length) return entry;
|
|
33
33
|
return { ...entry, models: visibleModels };
|
|
34
34
|
})
|
|
35
|
-
.filter(
|
|
35
|
+
.filter(
|
|
36
|
+
(entry) => entry.models.length > 0 || entry.defaultModel === "",
|
|
37
|
+
);
|
|
36
38
|
}
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
8
8
|
import { getConfig } from "../config/loader.js";
|
|
9
9
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
10
|
+
import { getDb } from "../memory/db-connection.js";
|
|
10
11
|
import { getLogger } from "../util/logger.js";
|
|
11
12
|
import { tryResolveProviderForConnectionName } from "./connection-resolution.js";
|
|
13
|
+
import { listConnections } from "./inference/connections.js";
|
|
12
14
|
import { initializeProviders, listProviders } from "./registry.js";
|
|
13
15
|
import type {
|
|
14
16
|
ContentBlock,
|
|
@@ -110,22 +112,35 @@ export async function resolveConfiguredProvider(
|
|
|
110
112
|
|
|
111
113
|
const resolved = resolveCallSiteConfig(callSite, config.llm, opts);
|
|
112
114
|
const inferenceProvider = resolved.provider;
|
|
113
|
-
|
|
115
|
+
let connectionName = resolved.provider_connection;
|
|
114
116
|
|
|
115
117
|
// Connection-aware path: every dispatch goes through `provider_connection`.
|
|
116
118
|
// The boot-time backfill ensures every profile has one in production.
|
|
117
|
-
// When unset (
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
// Hard config errors — connection lookup failure, provider mismatch —
|
|
122
|
-
// still throw via `tryResolveProviderForConnectionName` below.
|
|
119
|
+
// When unset (profile set provider with "Any active" connection, test envs
|
|
120
|
+
// that skip backfill, freshly-installed configs not yet backfilled, or
|
|
121
|
+
// users who manually cleared the field), try to auto-resolve from the
|
|
122
|
+
// provider before falling back to null.
|
|
123
123
|
if (!connectionName) {
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
if (inferenceProvider) {
|
|
125
|
+
try {
|
|
126
|
+
const candidates = listConnections(getDb(), {
|
|
127
|
+
provider: inferenceProvider,
|
|
128
|
+
});
|
|
129
|
+
const active = candidates.find((c) => c.status === "active");
|
|
130
|
+
if (active) {
|
|
131
|
+
connectionName = active.name;
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// DB not available — fall through to the existing null-return path.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!connectionName) {
|
|
138
|
+
log.debug(
|
|
139
|
+
{ callSite, inferenceProvider },
|
|
140
|
+
"resolveCallSiteConfig yielded no provider_connection — returning null so callsite can fall back",
|
|
141
|
+
);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
129
144
|
}
|
|
130
145
|
|
|
131
146
|
const connectionProvider = await tryResolveProviderForConnectionName(
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
1
2
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
3
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
2
4
|
import { type LLMConfig } from "../config/schemas/llm.js";
|
|
3
5
|
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
4
6
|
import { ProviderNotConfiguredError } from "../util/errors.js";
|
|
@@ -26,6 +28,7 @@ const log = getLogger("provider-registry");
|
|
|
26
28
|
|
|
27
29
|
const providers = new Map<string, Provider>();
|
|
28
30
|
const routingSources = new Map<string, "user-key" | "managed-proxy">();
|
|
31
|
+
const OPENAI_COMPATIBLE_ENDPOINTS_FLAG = "openai-compatible-endpoints";
|
|
29
32
|
|
|
30
33
|
/** Per-connection provider cache, keyed by connection name. */
|
|
31
34
|
const connectionProviders = new Map<string, Provider>();
|
|
@@ -69,6 +72,16 @@ export interface ProvidersConfig {
|
|
|
69
72
|
timeouts?: { providerStreamTimeoutSec?: number };
|
|
70
73
|
}
|
|
71
74
|
|
|
75
|
+
function isProviderFeatureFlagEnabled(
|
|
76
|
+
key: string,
|
|
77
|
+
config: ProvidersConfig,
|
|
78
|
+
): boolean {
|
|
79
|
+
return isAssistantFeatureFlagEnabled(
|
|
80
|
+
key,
|
|
81
|
+
config as unknown as AssistantConfig,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
72
85
|
function resolveModel(config: ProvidersConfig, providerName: string): string {
|
|
73
86
|
const resolved = resolveCallSiteConfig("mainAgent", config.llm);
|
|
74
87
|
const inferenceProvider = resolved.provider;
|
|
@@ -130,6 +143,13 @@ export async function initializeProviders(
|
|
|
130
143
|
).provider;
|
|
131
144
|
|
|
132
145
|
for (const entry of PROVIDER_CATALOG) {
|
|
146
|
+
if (
|
|
147
|
+
entry.featureFlag &&
|
|
148
|
+
!isProviderFeatureFlagEnabled(entry.featureFlag, config)
|
|
149
|
+
) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
133
153
|
const isKeyless = entry.setupMode === "keyless";
|
|
134
154
|
|
|
135
155
|
// Credential resolution: user key first, managed proxy second. Keyless
|
|
@@ -202,10 +222,19 @@ export async function resolveProviderFromConnection(
|
|
|
202
222
|
connection: ProviderConnection,
|
|
203
223
|
config: ProvidersConfig,
|
|
204
224
|
): Promise<Provider | null> {
|
|
225
|
+
if (
|
|
226
|
+
connection.provider === "openai-compatible" &&
|
|
227
|
+
!isProviderFeatureFlagEnabled(OPENAI_COMPATIBLE_ENDPOINTS_FLAG, config)
|
|
228
|
+
) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
205
232
|
const cached = connectionProviders.get(connection.name);
|
|
206
233
|
if (cached) return cached;
|
|
207
234
|
|
|
208
|
-
const authResult = await resolveAuth(connection.auth, connection.provider
|
|
235
|
+
const authResult = await resolveAuth(connection.auth, connection.provider, {
|
|
236
|
+
baseUrl: connection.baseUrl,
|
|
237
|
+
});
|
|
209
238
|
if (!authResult.ok) {
|
|
210
239
|
const err = authResult.error;
|
|
211
240
|
if (err.code === "not_implemented") {
|
package/src/providers/types.ts
CHANGED
|
@@ -27,6 +27,15 @@ export interface FileContent {
|
|
|
27
27
|
filename: string;
|
|
28
28
|
};
|
|
29
29
|
extracted_text?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Internal id linking this block to a row in the attachments table.
|
|
32
|
+
* Set when the file block originates from a persisted user-message
|
|
33
|
+
* attachment so downstream consumers (DB joins, inline-chip
|
|
34
|
+
* positioning) can correlate the block back to its attachment id.
|
|
35
|
+
* Stripped by `daemon/handlers/shared.ts` before sending to the
|
|
36
|
+
* model.
|
|
37
|
+
*/
|
|
38
|
+
_attachmentId?: string;
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
export interface ToolUseContent {
|
|
@@ -137,6 +146,22 @@ export type ProviderEvent =
|
|
|
137
146
|
toolUseId: string;
|
|
138
147
|
isError: boolean;
|
|
139
148
|
content?: unknown[];
|
|
149
|
+
/**
|
|
150
|
+
* Finalized input for the server tool call (e.g. the actual query).
|
|
151
|
+
* Anthropic streams `server_tool_use` block input via `input_json_delta`
|
|
152
|
+
* events, so consumers reading the input at `server_tool_start` see `{}`.
|
|
153
|
+
* The provider accumulates the JSON and surfaces it here once the block
|
|
154
|
+
* stops, so downstream handlers can build accurate activity metadata.
|
|
155
|
+
*/
|
|
156
|
+
resolvedInput?: Record<string, unknown>;
|
|
157
|
+
/**
|
|
158
|
+
* Provider-specific error code when `isError` is true (e.g. Anthropic's
|
|
159
|
+
* `max_uses_exceeded`, `query_too_long`). Surfaced so user-facing
|
|
160
|
+
* messages can be specific instead of a generic "Search failed".
|
|
161
|
+
*/
|
|
162
|
+
errorCode?: string;
|
|
163
|
+
/** Optional human-readable error message from the provider. */
|
|
164
|
+
errorMessage?: string;
|
|
140
165
|
};
|
|
141
166
|
|
|
142
167
|
export interface SendMessageConfig {
|
|
@@ -22,8 +22,29 @@ import type { DiskPressureStatus } from "../../daemon/disk-pressure-guard.js";
|
|
|
22
22
|
// Stub the DB-backed override-profile read so unit tests don't need a
|
|
23
23
|
// real SQLite database. The wake helper calls this on every invocation
|
|
24
24
|
// to honor the conversation's pinned inference profile.
|
|
25
|
+
// `getConversation` is consumed by `defaultResolveTarget` — most tests
|
|
26
|
+
// pass explicit `deps.resolveTarget` and bypass it, but the
|
|
27
|
+
// trust-context threading test below drives the default resolver and
|
|
28
|
+
// needs the existence/archived check to pass.
|
|
25
29
|
mock.module("../../memory/conversation-crud.js", () => ({
|
|
26
30
|
getConversationOverrideProfile: () => undefined,
|
|
31
|
+
getConversation: () => ({ archivedAt: null }),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const mockGetOrCreateConversationCalls: Array<{
|
|
35
|
+
conversationId: string;
|
|
36
|
+
options: unknown;
|
|
37
|
+
}> = [];
|
|
38
|
+
mock.module("../../daemon/conversation-store.js", () => ({
|
|
39
|
+
getOrCreateConversation: (conversationId: string, options?: unknown) => {
|
|
40
|
+
mockGetOrCreateConversationCalls.push({ conversationId, options });
|
|
41
|
+
return Promise.resolve({ __mockConversation: true });
|
|
42
|
+
},
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
let mockResolverTarget: unknown = null;
|
|
46
|
+
mock.module("../../daemon/wake-target-adapter.js", () => ({
|
|
47
|
+
conversationToWakeTarget: () => mockResolverTarget,
|
|
27
48
|
}));
|
|
28
49
|
|
|
29
50
|
mock.module("../../config/loader.js", () => ({
|
|
@@ -248,6 +269,8 @@ function makeTarget(options: {
|
|
|
248
269
|
beforeEach(() => {
|
|
249
270
|
__resetWakeChainForTests();
|
|
250
271
|
recordRequestLogCalls.length = 0;
|
|
272
|
+
mockGetOrCreateConversationCalls.length = 0;
|
|
273
|
+
mockResolverTarget = null;
|
|
251
274
|
mockDiskPressureStatus = {
|
|
252
275
|
enabled: false,
|
|
253
276
|
state: "disabled",
|
|
@@ -1587,4 +1610,195 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1587
1610
|
// No log row was inserted because JSON.stringify threw.
|
|
1588
1611
|
expect(recordRequestLogCalls).toHaveLength(0);
|
|
1589
1612
|
});
|
|
1613
|
+
|
|
1614
|
+
// Regression guard for fork-based memory retrospectives: PR #31260
|
|
1615
|
+
// forked a conversation and waked it with a guardian trustContext,
|
|
1616
|
+
// but the wake's default resolver called
|
|
1617
|
+
// `getOrCreateConversation(conversationId)` with no options, so the
|
|
1618
|
+
// store hydrated with `trustContext === undefined`. `loadFromDb`
|
|
1619
|
+
// fail-closes to `trustClass: "unknown"` and filters out every
|
|
1620
|
+
// guardian-provenance message — so the LLM saw an empty history and
|
|
1621
|
+
// every fork sent `messages: []`. Threading trustContext through
|
|
1622
|
+
// ensures `setTrustContext` + `ensureActorScopedHistory` run during
|
|
1623
|
+
// hydration.
|
|
1624
|
+
const makeDefaultResolverTarget = (conversationId: string): WakeTarget => {
|
|
1625
|
+
const history: Message[] = [];
|
|
1626
|
+
let processing = false;
|
|
1627
|
+
return {
|
|
1628
|
+
conversationId,
|
|
1629
|
+
agentLoop: { run: async (input) => input },
|
|
1630
|
+
getMessages: () => history,
|
|
1631
|
+
pushMessage: () => {},
|
|
1632
|
+
emitAgentEvent: () => {},
|
|
1633
|
+
isProcessing: () => processing,
|
|
1634
|
+
markProcessing: (on) => {
|
|
1635
|
+
processing = on;
|
|
1636
|
+
},
|
|
1637
|
+
persistTailMessage: async () => {},
|
|
1638
|
+
};
|
|
1639
|
+
};
|
|
1640
|
+
|
|
1641
|
+
test("default resolver threads WakeOptions.trustContext into getOrCreateConversation", async () => {
|
|
1642
|
+
mockResolverTarget = makeDefaultResolverTarget("conv-thread-trust");
|
|
1643
|
+
const trustContext = {
|
|
1644
|
+
sourceChannel: "vellum",
|
|
1645
|
+
trustClass: "guardian",
|
|
1646
|
+
} as const;
|
|
1647
|
+
|
|
1648
|
+
await wakeAgentForOpportunity({
|
|
1649
|
+
conversationId: "conv-thread-trust",
|
|
1650
|
+
hint: "consolidate",
|
|
1651
|
+
source: "memory_v2_consolidation",
|
|
1652
|
+
trustContext,
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
expect(mockGetOrCreateConversationCalls).toEqual([
|
|
1656
|
+
{ conversationId: "conv-thread-trust", options: { trustContext } },
|
|
1657
|
+
]);
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
test("default resolver passes trustContext: undefined when WakeOptions.trustContext is omitted", async () => {
|
|
1661
|
+
// Inbound user-turn wakes get trust via processMessage(); the wake
|
|
1662
|
+
// must not synthesize a trust context out of thin air.
|
|
1663
|
+
mockResolverTarget = makeDefaultResolverTarget("conv-no-trust-default");
|
|
1664
|
+
|
|
1665
|
+
await wakeAgentForOpportunity({
|
|
1666
|
+
conversationId: "conv-no-trust-default",
|
|
1667
|
+
hint: "x",
|
|
1668
|
+
source: "unit-test",
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
expect(mockGetOrCreateConversationCalls).toEqual([
|
|
1672
|
+
{
|
|
1673
|
+
conversationId: "conv-no-trust-default",
|
|
1674
|
+
options: { trustContext: undefined },
|
|
1675
|
+
},
|
|
1676
|
+
]);
|
|
1677
|
+
});
|
|
1678
|
+
|
|
1679
|
+
describe("suppressWakeSurface option", () => {
|
|
1680
|
+
function makeCheckpointTarget(): {
|
|
1681
|
+
target: WakeTarget;
|
|
1682
|
+
persistedTailCalls: Message[];
|
|
1683
|
+
wakeProducedOutputCalls: string[];
|
|
1684
|
+
} {
|
|
1685
|
+
const firstAssistant: Message = {
|
|
1686
|
+
role: "assistant",
|
|
1687
|
+
content: [
|
|
1688
|
+
{ type: "tool_use", id: "tu-1", name: "some_tool", input: {} },
|
|
1689
|
+
],
|
|
1690
|
+
};
|
|
1691
|
+
const toolResult: Message = {
|
|
1692
|
+
role: "user",
|
|
1693
|
+
content: [{ type: "tool_result", tool_use_id: "tu-1", content: "ok" }],
|
|
1694
|
+
};
|
|
1695
|
+
const persistedTailCalls: Message[] = [];
|
|
1696
|
+
const baseline: Message[] = [
|
|
1697
|
+
{ role: "user", content: [{ type: "text", text: "hi" }] },
|
|
1698
|
+
];
|
|
1699
|
+
const history: Message[] = [...baseline];
|
|
1700
|
+
let processing = false;
|
|
1701
|
+
const wakeProducedOutputCalls: string[] = [];
|
|
1702
|
+
|
|
1703
|
+
const target: WakeTarget = {
|
|
1704
|
+
conversationId: "conv-suppress-surface",
|
|
1705
|
+
agentLoop: {
|
|
1706
|
+
run: async (_input, _onEvent, _signal, _requestId, onCheckpoint) => {
|
|
1707
|
+
const runHistory: Message[] = [..._input];
|
|
1708
|
+
runHistory.push(firstAssistant);
|
|
1709
|
+
runHistory.push(toolResult);
|
|
1710
|
+
await onCheckpoint!({
|
|
1711
|
+
turnIndex: 0,
|
|
1712
|
+
toolCount: 1,
|
|
1713
|
+
hasToolUse: true,
|
|
1714
|
+
history: runHistory,
|
|
1715
|
+
});
|
|
1716
|
+
return runHistory;
|
|
1717
|
+
},
|
|
1718
|
+
},
|
|
1719
|
+
getMessages: () => history,
|
|
1720
|
+
pushMessage: (msg) => {
|
|
1721
|
+
history.push(msg);
|
|
1722
|
+
},
|
|
1723
|
+
emitAgentEvent: () => {},
|
|
1724
|
+
isProcessing: () => processing,
|
|
1725
|
+
markProcessing: (on) => {
|
|
1726
|
+
processing = on;
|
|
1727
|
+
},
|
|
1728
|
+
persistTailMessage: async (msg) => {
|
|
1729
|
+
persistedTailCalls.push(msg);
|
|
1730
|
+
},
|
|
1731
|
+
onWakeProducedOutput: (_source, _hint, surfaceId) => {
|
|
1732
|
+
wakeProducedOutputCalls.push(surfaceId);
|
|
1733
|
+
},
|
|
1734
|
+
};
|
|
1735
|
+
return { target, persistedTailCalls, wakeProducedOutputCalls };
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
test(
|
|
1739
|
+
"default (suppressWakeSurface omitted) still injects the ui_surface " +
|
|
1740
|
+
"card and calls onWakeProducedOutput",
|
|
1741
|
+
async () => {
|
|
1742
|
+
const { target, persistedTailCalls, wakeProducedOutputCalls } =
|
|
1743
|
+
makeCheckpointTarget();
|
|
1744
|
+
|
|
1745
|
+
await wakeAgentForOpportunity(
|
|
1746
|
+
{
|
|
1747
|
+
conversationId: "conv-suppress-surface",
|
|
1748
|
+
hint: "do the thing",
|
|
1749
|
+
source: "memory_v2_consolidation",
|
|
1750
|
+
},
|
|
1751
|
+
{ resolveTarget: async () => target },
|
|
1752
|
+
);
|
|
1753
|
+
|
|
1754
|
+
// Existing behavior: card injected, broadcast fired exactly once.
|
|
1755
|
+
expect(wakeProducedOutputCalls).toHaveLength(1);
|
|
1756
|
+
const persistedFirst = persistedTailCalls[0];
|
|
1757
|
+
expect(persistedFirst).toBeDefined();
|
|
1758
|
+
const blocks = Array.isArray(persistedFirst!.content)
|
|
1759
|
+
? persistedFirst!.content
|
|
1760
|
+
: [];
|
|
1761
|
+
const uiBlock = blocks.find(
|
|
1762
|
+
(b: { type?: string }) => b.type === "ui_surface",
|
|
1763
|
+
);
|
|
1764
|
+
expect(uiBlock).toBeDefined();
|
|
1765
|
+
},
|
|
1766
|
+
);
|
|
1767
|
+
|
|
1768
|
+
test(
|
|
1769
|
+
"suppressWakeSurface: true produces output but skips the ui_surface " +
|
|
1770
|
+
"card injection and the onWakeProducedOutput broadcast",
|
|
1771
|
+
async () => {
|
|
1772
|
+
const { target, persistedTailCalls, wakeProducedOutputCalls } =
|
|
1773
|
+
makeCheckpointTarget();
|
|
1774
|
+
|
|
1775
|
+
await wakeAgentForOpportunity(
|
|
1776
|
+
{
|
|
1777
|
+
conversationId: "conv-suppress-surface",
|
|
1778
|
+
hint: "do the thing",
|
|
1779
|
+
source: "memory_v2_consolidation",
|
|
1780
|
+
suppressWakeSurface: true,
|
|
1781
|
+
},
|
|
1782
|
+
{ resolveTarget: async () => target },
|
|
1783
|
+
);
|
|
1784
|
+
|
|
1785
|
+
// Tail still persisted (wake produced real output).
|
|
1786
|
+
const persistedFirst = persistedTailCalls[0];
|
|
1787
|
+
expect(persistedFirst).toBeDefined();
|
|
1788
|
+
// First assistant tail message should NOT have a ui_surface block
|
|
1789
|
+
// prepended at the front.
|
|
1790
|
+
const blocks = Array.isArray(persistedFirst!.content)
|
|
1791
|
+
? persistedFirst!.content
|
|
1792
|
+
: [];
|
|
1793
|
+
const firstBlock = blocks[0] as { type?: string } | undefined;
|
|
1794
|
+
expect(firstBlock?.type).not.toBe("ui_surface");
|
|
1795
|
+
const uiBlock = blocks.find(
|
|
1796
|
+
(b: { type?: string }) => b.type === "ui_surface",
|
|
1797
|
+
);
|
|
1798
|
+
expect(uiBlock).toBeUndefined();
|
|
1799
|
+
// Live broadcast was suppressed.
|
|
1800
|
+
expect(wakeProducedOutputCalls).toHaveLength(0);
|
|
1801
|
+
},
|
|
1802
|
+
);
|
|
1803
|
+
});
|
|
1590
1804
|
});
|
|
@@ -94,6 +94,8 @@ mock.module("../pre-first-message-gate.js", () => ({
|
|
|
94
94
|
|
|
95
95
|
// Import after mocks are in place.
|
|
96
96
|
const { runBackgroundJob } = await import("../background-job-runner.js");
|
|
97
|
+
const { bufferIfDeferred, resetDeferredForTest } =
|
|
98
|
+
await import("../../notifications/deferred-emit.js");
|
|
97
99
|
|
|
98
100
|
// ── Shared fixtures ──────────────────────────────────────────────────
|
|
99
101
|
|
|
@@ -121,6 +123,7 @@ beforeEach(() => {
|
|
|
121
123
|
processMessageCalls.length = 0;
|
|
122
124
|
emitCalls.length = 0;
|
|
123
125
|
addMessageCalls.length = 0;
|
|
126
|
+
resetDeferredForTest();
|
|
124
127
|
preFirstMessageGateOpen = true;
|
|
125
128
|
processMessageImpl = async () => ({ messageId: "msg-1" });
|
|
126
129
|
emitImpl = async () => ({
|
|
@@ -354,4 +357,129 @@ describe("runBackgroundJob", () => {
|
|
|
354
357
|
expect(processMessageCalls).toHaveLength(1);
|
|
355
358
|
});
|
|
356
359
|
});
|
|
360
|
+
|
|
361
|
+
describe("deferNotifications", () => {
|
|
362
|
+
function buildSkillNotificationParams(message: string) {
|
|
363
|
+
return {
|
|
364
|
+
sourceEventName: "assistant.share",
|
|
365
|
+
sourceChannel: "assistant_tool" as const,
|
|
366
|
+
sourceContextId: "skill-ctx",
|
|
367
|
+
contextPayload: { requestedMessage: message },
|
|
368
|
+
attentionHints: {
|
|
369
|
+
requiresAction: false,
|
|
370
|
+
urgency: "low" as const,
|
|
371
|
+
isAsyncBackground: false,
|
|
372
|
+
visibleInSourceNow: false,
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
test("success path commits buffered in-band notifications", async () => {
|
|
378
|
+
processMessageImpl = async () => {
|
|
379
|
+
// Stand in for the IPC route's bufferIfDeferred call when the model
|
|
380
|
+
// invokes `notifications send` mid-turn.
|
|
381
|
+
const buffered = bufferIfDeferred(
|
|
382
|
+
STUB_CONVERSATION_ID,
|
|
383
|
+
buildSkillNotificationParams("all green"),
|
|
384
|
+
);
|
|
385
|
+
expect(buffered).not.toBeNull();
|
|
386
|
+
expect(buffered!.dispatched).toBe(false);
|
|
387
|
+
return { messageId: "msg-success" };
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const result = await runBackgroundJob(
|
|
391
|
+
baseOpts({ deferNotifications: true }),
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
expect(result.ok).toBe(true);
|
|
395
|
+
// Commit flushed the buffered notification through emitNotificationSignal.
|
|
396
|
+
const successEmits = emitCalls.filter(
|
|
397
|
+
(e) => e.sourceEventName !== "activity.failed",
|
|
398
|
+
);
|
|
399
|
+
expect(successEmits).toHaveLength(1);
|
|
400
|
+
expect(successEmits[0].sourceEventName).toBe("assistant.share");
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Regression for the PR #31216 Codex P1 finding: a heartbeat that calls
|
|
404
|
+
// the notifications skill and then times out must not leave a "success"
|
|
405
|
+
// notification standing alongside the runner's `activity.failed` emit.
|
|
406
|
+
test("timeout drops buffered in-band notifications; only activity.failed emits", async () => {
|
|
407
|
+
processMessageImpl = () => {
|
|
408
|
+
bufferIfDeferred(
|
|
409
|
+
STUB_CONVERSATION_ID,
|
|
410
|
+
buildSkillNotificationParams("premature success"),
|
|
411
|
+
);
|
|
412
|
+
return new Promise(() => {});
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const result = await runBackgroundJob(
|
|
416
|
+
baseOpts({ deferNotifications: true, timeoutMs: 30 }),
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
expect(result.ok).toBe(false);
|
|
420
|
+
expect(result.errorKind).toBe("timeout");
|
|
421
|
+
// Only the runner's failure signal makes it out — the buffered
|
|
422
|
+
// "success" notification is discarded.
|
|
423
|
+
expect(emitCalls).toHaveLength(1);
|
|
424
|
+
expect(emitCalls[0].sourceEventName).toBe("activity.failed");
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("thrown exception also drops buffered notifications", async () => {
|
|
428
|
+
processMessageImpl = async () => {
|
|
429
|
+
bufferIfDeferred(
|
|
430
|
+
STUB_CONVERSATION_ID,
|
|
431
|
+
buildSkillNotificationParams("doomed"),
|
|
432
|
+
);
|
|
433
|
+
throw new Error("kaboom");
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const result = await runBackgroundJob(
|
|
437
|
+
baseOpts({ deferNotifications: true }),
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
expect(result.ok).toBe(false);
|
|
441
|
+
expect(emitCalls).toHaveLength(1);
|
|
442
|
+
expect(emitCalls[0].sourceEventName).toBe("activity.failed");
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Regression: after timeout, `processMessage` keeps running and may
|
|
446
|
+
// emit a late skill call. The tombstone must swallow it instead of
|
|
447
|
+
// letting it bypass the buffer and reach the dispatch pipeline.
|
|
448
|
+
test("late skill call after timeout is swallowed by the tombstone", async () => {
|
|
449
|
+
processMessageImpl = () => new Promise(() => {});
|
|
450
|
+
|
|
451
|
+
const result = await runBackgroundJob(
|
|
452
|
+
baseOpts({ deferNotifications: true, timeoutMs: 20 }),
|
|
453
|
+
);
|
|
454
|
+
expect(result.ok).toBe(false);
|
|
455
|
+
|
|
456
|
+
const late = bufferIfDeferred(
|
|
457
|
+
STUB_CONVERSATION_ID,
|
|
458
|
+
buildSkillNotificationParams("post-timeout"),
|
|
459
|
+
);
|
|
460
|
+
expect(late).not.toBeNull();
|
|
461
|
+
expect(late!.dispatched).toBe(false);
|
|
462
|
+
expect(late!.reason).toMatch(/did not complete/);
|
|
463
|
+
|
|
464
|
+
// Only the runner's failure signal made it out.
|
|
465
|
+
expect(emitCalls).toHaveLength(1);
|
|
466
|
+
expect(emitCalls[0].sourceEventName).toBe("activity.failed");
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
test("without deferNotifications, bufferIfDeferred is a no-op", async () => {
|
|
470
|
+
processMessageImpl = async () => {
|
|
471
|
+
const buffered = bufferIfDeferred(
|
|
472
|
+
STUB_CONVERSATION_ID,
|
|
473
|
+
buildSkillNotificationParams("immediate"),
|
|
474
|
+
);
|
|
475
|
+
// Buffer was never armed, so the call returns null and the IPC
|
|
476
|
+
// handler would emit directly.
|
|
477
|
+
expect(buffered).toBeNull();
|
|
478
|
+
return { messageId: "msg-success" };
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const result = await runBackgroundJob(baseOpts());
|
|
482
|
+
expect(result.ok).toBe(true);
|
|
483
|
+
});
|
|
484
|
+
});
|
|
357
485
|
});
|