@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
|
@@ -42,6 +42,7 @@ mock.module("../runtime/pending-interactions.js", () => ({
|
|
|
42
42
|
const {
|
|
43
43
|
HostAppControlProxy,
|
|
44
44
|
_getActiveAppControlSession,
|
|
45
|
+
_getConfirmedAppControlSession,
|
|
45
46
|
_resetActiveAppControlSession,
|
|
46
47
|
_setActiveAppControlSession,
|
|
47
48
|
} = await import("../daemon/host-app-control-proxy.js");
|
|
@@ -826,6 +827,246 @@ describe("HostAppControlProxy", () => {
|
|
|
826
827
|
proxy.dispose();
|
|
827
828
|
});
|
|
828
829
|
|
|
830
|
+
test("both-succeed older-arrives-last does not overwrite newer confirmed session", async () => {
|
|
831
|
+
// Two same-conversation starts both succeed, but the older one's
|
|
832
|
+
// `running` response arrives AFTER the newer one's. With a
|
|
833
|
+
// conversation-only ownership guard, the older response would
|
|
834
|
+
// overwrite the newer confirmed session — a latent bug that surfaces
|
|
835
|
+
// on a subsequent rollback. Verify the confirmed pointer stays on the
|
|
836
|
+
// newer session (C), and that a later rollback restores to C, not A.
|
|
837
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
838
|
+
const ctrl = new AbortController();
|
|
839
|
+
|
|
840
|
+
const pA = proxy.request(
|
|
841
|
+
"app_control_start",
|
|
842
|
+
{ tool: "start", app: "com.example.a" },
|
|
843
|
+
"conv-1",
|
|
844
|
+
ctrl.signal,
|
|
845
|
+
);
|
|
846
|
+
const reqIdA = (sentMessages[0] as Record<string, unknown>)
|
|
847
|
+
.requestId as string;
|
|
848
|
+
|
|
849
|
+
sentMessages.length = 0;
|
|
850
|
+
const pC = proxy.request(
|
|
851
|
+
"app_control_start",
|
|
852
|
+
{ tool: "start", app: "com.example.c" },
|
|
853
|
+
"conv-1",
|
|
854
|
+
ctrl.signal,
|
|
855
|
+
);
|
|
856
|
+
const reqIdC = (sentMessages[0] as Record<string, unknown>)
|
|
857
|
+
.requestId as string;
|
|
858
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
859
|
+
|
|
860
|
+
// C succeeds first → active = C, confirmed = C.
|
|
861
|
+
proxy.resolve(reqIdC, payload({ pngBase64: PNG_A }));
|
|
862
|
+
await pC;
|
|
863
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
864
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.c");
|
|
865
|
+
|
|
866
|
+
// A succeeds second (older arrives last). The conversation-ownership
|
|
867
|
+
// check passes for A, but A is no longer the live optimistic write
|
|
868
|
+
// and a session is already confirmed — must NOT overwrite confirmed.
|
|
869
|
+
proxy.resolve(reqIdA, payload({ pngBase64: PNG_B }));
|
|
870
|
+
await pA;
|
|
871
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.c");
|
|
872
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
873
|
+
|
|
874
|
+
// A later restart D that fails must roll back to C (the true
|
|
875
|
+
// confirmed), not to A — guards against the latent-bug case.
|
|
876
|
+
sentMessages.length = 0;
|
|
877
|
+
const pD = proxy.request(
|
|
878
|
+
"app_control_start",
|
|
879
|
+
{ tool: "start", app: "com.example.d" },
|
|
880
|
+
"conv-1",
|
|
881
|
+
ctrl.signal,
|
|
882
|
+
);
|
|
883
|
+
const reqIdD = (sentMessages[0] as Record<string, unknown>)
|
|
884
|
+
.requestId as string;
|
|
885
|
+
proxy.resolve(reqIdD, payload({ state: "missing" }));
|
|
886
|
+
await pD;
|
|
887
|
+
|
|
888
|
+
const session = _getActiveAppControlSession();
|
|
889
|
+
expect(session?.conversationId).toBe("conv-1");
|
|
890
|
+
expect(session?.app).toBe("com.example.c");
|
|
891
|
+
|
|
892
|
+
proxy.dispose();
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
test("rollback after confirmed-A,dispatch-B,dispatch-C,B-confirms,C-fails restores B (Codex P2)", async () => {
|
|
896
|
+
// A is confirmed, then B and C are dispatched. B's `running` arrives
|
|
897
|
+
// while C is the live write; under the dispatch-counter rule that
|
|
898
|
+
// confirmation must promote because B is newer than A. A subsequent
|
|
899
|
+
// failure of C must then roll back to B, not back to A.
|
|
900
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
901
|
+
const ctrl = new AbortController();
|
|
902
|
+
|
|
903
|
+
const pA = proxy.request(
|
|
904
|
+
"app_control_start",
|
|
905
|
+
{ tool: "start", app: "com.example.a" },
|
|
906
|
+
"conv-1",
|
|
907
|
+
ctrl.signal,
|
|
908
|
+
);
|
|
909
|
+
const reqIdA = (sentMessages[0] as Record<string, unknown>)
|
|
910
|
+
.requestId as string;
|
|
911
|
+
proxy.resolve(reqIdA, payload({ pngBase64: PNG_A }));
|
|
912
|
+
await pA;
|
|
913
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.a");
|
|
914
|
+
|
|
915
|
+
sentMessages.length = 0;
|
|
916
|
+
const pB = proxy.request(
|
|
917
|
+
"app_control_start",
|
|
918
|
+
{ tool: "start", app: "com.example.b" },
|
|
919
|
+
"conv-1",
|
|
920
|
+
ctrl.signal,
|
|
921
|
+
);
|
|
922
|
+
const reqIdB = (sentMessages[0] as Record<string, unknown>)
|
|
923
|
+
.requestId as string;
|
|
924
|
+
|
|
925
|
+
sentMessages.length = 0;
|
|
926
|
+
const pC = proxy.request(
|
|
927
|
+
"app_control_start",
|
|
928
|
+
{ tool: "start", app: "com.example.c" },
|
|
929
|
+
"conv-1",
|
|
930
|
+
ctrl.signal,
|
|
931
|
+
);
|
|
932
|
+
const reqIdC = (sentMessages[0] as Record<string, unknown>)
|
|
933
|
+
.requestId as string;
|
|
934
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
935
|
+
|
|
936
|
+
// B confirms (newer than A): confirmed should move to B even though
|
|
937
|
+
// C is the live optimistic write.
|
|
938
|
+
proxy.resolve(reqIdB, payload({ pngBase64: PNG_B }));
|
|
939
|
+
await pB;
|
|
940
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.b");
|
|
941
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
942
|
+
|
|
943
|
+
// C fails → rollback restores to confirmed (B), not A.
|
|
944
|
+
proxy.resolve(reqIdC, payload({ state: "missing" }));
|
|
945
|
+
await pC;
|
|
946
|
+
const session = _getActiveAppControlSession();
|
|
947
|
+
expect(session?.conversationId).toBe("conv-1");
|
|
948
|
+
expect(session?.app).toBe("com.example.b");
|
|
949
|
+
|
|
950
|
+
proxy.dispose();
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
test("three-overlapping-starts A-confirms,B-confirms,C-fails restores B (Devin P2)", async () => {
|
|
954
|
+
// A, B, C dispatched in order with no prior confirmed session.
|
|
955
|
+
// A confirms first (confirmed = A), B confirms second (confirmed
|
|
956
|
+
// must move forward to B because B is newer), C fails → rollback
|
|
957
|
+
// restores to B, not A.
|
|
958
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
959
|
+
const ctrl = new AbortController();
|
|
960
|
+
|
|
961
|
+
const pA = proxy.request(
|
|
962
|
+
"app_control_start",
|
|
963
|
+
{ tool: "start", app: "com.example.a" },
|
|
964
|
+
"conv-1",
|
|
965
|
+
ctrl.signal,
|
|
966
|
+
);
|
|
967
|
+
const reqIdA = (sentMessages[0] as Record<string, unknown>)
|
|
968
|
+
.requestId as string;
|
|
969
|
+
|
|
970
|
+
sentMessages.length = 0;
|
|
971
|
+
const pB = proxy.request(
|
|
972
|
+
"app_control_start",
|
|
973
|
+
{ tool: "start", app: "com.example.b" },
|
|
974
|
+
"conv-1",
|
|
975
|
+
ctrl.signal,
|
|
976
|
+
);
|
|
977
|
+
const reqIdB = (sentMessages[0] as Record<string, unknown>)
|
|
978
|
+
.requestId as string;
|
|
979
|
+
|
|
980
|
+
sentMessages.length = 0;
|
|
981
|
+
const pC = proxy.request(
|
|
982
|
+
"app_control_start",
|
|
983
|
+
{ tool: "start", app: "com.example.c" },
|
|
984
|
+
"conv-1",
|
|
985
|
+
ctrl.signal,
|
|
986
|
+
);
|
|
987
|
+
const reqIdC = (sentMessages[0] as Record<string, unknown>)
|
|
988
|
+
.requestId as string;
|
|
989
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
990
|
+
|
|
991
|
+
proxy.resolve(reqIdA, payload({ pngBase64: PNG_A }));
|
|
992
|
+
await pA;
|
|
993
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.a");
|
|
994
|
+
|
|
995
|
+
proxy.resolve(reqIdB, payload({ pngBase64: PNG_B }));
|
|
996
|
+
await pB;
|
|
997
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.b");
|
|
998
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
999
|
+
|
|
1000
|
+
proxy.resolve(reqIdC, payload({ state: "missing" }));
|
|
1001
|
+
await pC;
|
|
1002
|
+
const session = _getActiveAppControlSession();
|
|
1003
|
+
expect(session?.conversationId).toBe("conv-1");
|
|
1004
|
+
expect(session?.app).toBe("com.example.b");
|
|
1005
|
+
|
|
1006
|
+
proxy.dispose();
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
test("confirmed-A,dispatch-B,dispatch-C,C-fails-first,B-confirms syncs active to B", async () => {
|
|
1010
|
+
// A confirmed, B and C dispatched, C returns `missing` first →
|
|
1011
|
+
// rollback restores active to A. Then B returns `running` →
|
|
1012
|
+
// confirmed advances to B; active must advance alongside it,
|
|
1013
|
+
// otherwise the tool reports B started while
|
|
1014
|
+
// app_control_observe/actions for B target A.
|
|
1015
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
1016
|
+
const ctrl = new AbortController();
|
|
1017
|
+
|
|
1018
|
+
const pA = proxy.request(
|
|
1019
|
+
"app_control_start",
|
|
1020
|
+
{ tool: "start", app: "com.example.a" },
|
|
1021
|
+
"conv-1",
|
|
1022
|
+
ctrl.signal,
|
|
1023
|
+
);
|
|
1024
|
+
const reqIdA = (sentMessages[0] as Record<string, unknown>)
|
|
1025
|
+
.requestId as string;
|
|
1026
|
+
proxy.resolve(reqIdA, payload({ pngBase64: PNG_A }));
|
|
1027
|
+
await pA;
|
|
1028
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.a");
|
|
1029
|
+
|
|
1030
|
+
sentMessages.length = 0;
|
|
1031
|
+
const pB = proxy.request(
|
|
1032
|
+
"app_control_start",
|
|
1033
|
+
{ tool: "start", app: "com.example.b" },
|
|
1034
|
+
"conv-1",
|
|
1035
|
+
ctrl.signal,
|
|
1036
|
+
);
|
|
1037
|
+
const reqIdB = (sentMessages[0] as Record<string, unknown>)
|
|
1038
|
+
.requestId as string;
|
|
1039
|
+
|
|
1040
|
+
sentMessages.length = 0;
|
|
1041
|
+
const pC = proxy.request(
|
|
1042
|
+
"app_control_start",
|
|
1043
|
+
{ tool: "start", app: "com.example.c" },
|
|
1044
|
+
"conv-1",
|
|
1045
|
+
ctrl.signal,
|
|
1046
|
+
);
|
|
1047
|
+
const reqIdC = (sentMessages[0] as Record<string, unknown>)
|
|
1048
|
+
.requestId as string;
|
|
1049
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.c");
|
|
1050
|
+
|
|
1051
|
+
// C fails first → rollback restores active to confirmed (A).
|
|
1052
|
+
proxy.resolve(reqIdC, payload({ state: "missing" }));
|
|
1053
|
+
await pC;
|
|
1054
|
+
expect(_getActiveAppControlSession()?.app).toBe("com.example.a");
|
|
1055
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.a");
|
|
1056
|
+
|
|
1057
|
+
// B confirms after C's rollback. Confirmed advances to B AND active
|
|
1058
|
+
// must advance to B too — otherwise non-start tool calls against B
|
|
1059
|
+
// would be rejected because active still targets A.
|
|
1060
|
+
proxy.resolve(reqIdB, payload({ pngBase64: PNG_B }));
|
|
1061
|
+
await pB;
|
|
1062
|
+
expect(_getConfirmedAppControlSession()?.app).toBe("com.example.b");
|
|
1063
|
+
const session = _getActiveAppControlSession();
|
|
1064
|
+
expect(session?.conversationId).toBe("conv-1");
|
|
1065
|
+
expect(session?.app).toBe("com.example.b");
|
|
1066
|
+
|
|
1067
|
+
proxy.dispose();
|
|
1068
|
+
});
|
|
1069
|
+
|
|
829
1070
|
test("first-start failure releases the lock (no prior session to restore)", async () => {
|
|
830
1071
|
const proxy = new HostAppControlProxy("conv-1");
|
|
831
1072
|
const ctrl = new AbortController();
|
|
@@ -31,6 +31,12 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
|
31
31
|
_conversationId?: string,
|
|
32
32
|
options?: unknown,
|
|
33
33
|
) => {
|
|
34
|
+
// `interaction_resolved` envelopes are emitted by the pending-interactions
|
|
35
|
+
// tracker for every resolution. They are orthogonal to the host-proxy
|
|
36
|
+
// wire messages these tests assert on, so swallow them here.
|
|
37
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
34
40
|
sentMessages.push(msg);
|
|
35
41
|
sentMessageOptions.push(options);
|
|
36
42
|
},
|
|
@@ -31,6 +31,13 @@ let mockClients: MockClient[] = [];
|
|
|
31
31
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
32
32
|
assistantEventHub: {
|
|
33
33
|
publish: async (event: unknown, _options?: unknown) => {
|
|
34
|
+
// `interaction_resolved` envelopes are emitted by the
|
|
35
|
+
// pending-interactions tracker for every resolution. They are
|
|
36
|
+
// orthogonal to the host-browser wire messages these tests assert
|
|
37
|
+
// on, so swallow them here.
|
|
38
|
+
if ((event as { type?: string } | null)?.type === "interaction_resolved") {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
34
41
|
publishedEvents.push(event);
|
|
35
42
|
},
|
|
36
43
|
getMostRecentClientByCapability: (cap: string) =>
|
|
@@ -41,6 +48,9 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
|
41
48
|
mockClients.find((c) => c.clientId === clientId)?.actorPrincipalId,
|
|
42
49
|
},
|
|
43
50
|
broadcastMessage: (msg: unknown) => {
|
|
51
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
44
54
|
publishedEvents.push(msg);
|
|
45
55
|
},
|
|
46
56
|
}));
|
|
@@ -10,7 +10,14 @@ type MockClient = {
|
|
|
10
10
|
let mockClients: MockClient[] = [];
|
|
11
11
|
|
|
12
12
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
13
|
-
broadcastMessage: (msg: unknown) =>
|
|
13
|
+
broadcastMessage: (msg: unknown) => {
|
|
14
|
+
// Skip `interaction_resolved` envelopes — pending-interactions emits one
|
|
15
|
+
// on every resolve and these tests assert on host-proxy wire messages.
|
|
16
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
sentMessages.push(msg);
|
|
20
|
+
},
|
|
14
21
|
assistantEventHub: {
|
|
15
22
|
getMostRecentClientByCapability: (cap: string) =>
|
|
16
23
|
cap === "host_cu" && mockHasClient ? { id: "mock-client" } : null,
|
|
@@ -12,7 +12,14 @@ interface MockClient {
|
|
|
12
12
|
let mockClients: MockClient[] = [];
|
|
13
13
|
|
|
14
14
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
15
|
-
broadcastMessage: (msg: unknown) =>
|
|
15
|
+
broadcastMessage: (msg: unknown) => {
|
|
16
|
+
// Skip `interaction_resolved` envelopes — pending-interactions emits one
|
|
17
|
+
// on every resolve and these tests assert on host-proxy wire messages.
|
|
18
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
sentMessages.push(msg);
|
|
22
|
+
},
|
|
16
23
|
assistantEventHub: {
|
|
17
24
|
getMostRecentClientByCapability: (cap: string) => {
|
|
18
25
|
if (mockClients.length > 0) {
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for `preactivateHostProxySkills
|
|
3
|
-
* in `host-proxy-preactivation.ts`.
|
|
2
|
+
* Tests for `evaluateHostProxyAttachment`, `preactivateHostProxySkills`, and
|
|
3
|
+
* `shouldAttachHostProxyForCapability` in `host-proxy-preactivation.ts`.
|
|
4
4
|
*
|
|
5
5
|
* Covers:
|
|
6
6
|
* - Source interface natively supports capability → preactivate (regression)
|
|
7
7
|
* - Source interface doesn't support but capable client connected → preactivate
|
|
8
8
|
* - Source interface doesn't support and no capable client → don't preactivate
|
|
9
9
|
* - chrome-extension source + capable client connected → don't preactivate (security boundary)
|
|
10
|
+
* - `evaluateHostProxyAttachment` returns the correct `reason` for each branch
|
|
11
|
+
* - `preactivateHostProxySkills` emits one structured log line per call with
|
|
12
|
+
* conversationId, sourceInterface, per-capability decisions, and final
|
|
13
|
+
* preactivatedSkillIds (used by ATL-609-class silent-gate diagnosis)
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -26,31 +30,74 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
|
26
30
|
broadcastMessage: () => {},
|
|
27
31
|
}));
|
|
28
32
|
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Mock the logger so we can assert on the structured info call.
|
|
35
|
+
// `info` calls are pushed into `loggedInfoCalls` for inspection.
|
|
36
|
+
// `child()` is exposed for callers that wrap the logger that way.
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
interface LoggedCall {
|
|
40
|
+
fields: Record<string, unknown>;
|
|
41
|
+
message: string;
|
|
42
|
+
}
|
|
43
|
+
const loggedInfoCalls: LoggedCall[] = [];
|
|
44
|
+
function captureInfo(fields: unknown, message: unknown) {
|
|
45
|
+
loggedInfoCalls.push({
|
|
46
|
+
fields: fields as Record<string, unknown>,
|
|
47
|
+
message: message as string,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
mock.module("../util/logger.js", () => ({
|
|
30
|
-
getLogger: () =>
|
|
31
|
-
|
|
52
|
+
getLogger: () => ({
|
|
53
|
+
info: captureInfo,
|
|
54
|
+
warn: () => {},
|
|
55
|
+
error: () => {},
|
|
56
|
+
debug: () => {},
|
|
57
|
+
trace: () => {},
|
|
58
|
+
fatal: () => {},
|
|
59
|
+
child: () => ({
|
|
60
|
+
info: captureInfo,
|
|
61
|
+
warn: () => {},
|
|
62
|
+
error: () => {},
|
|
63
|
+
debug: () => {},
|
|
64
|
+
trace: () => {},
|
|
65
|
+
fatal: () => {},
|
|
66
|
+
}),
|
|
67
|
+
}),
|
|
32
68
|
}));
|
|
33
69
|
|
|
34
70
|
// ---------------------------------------------------------------------------
|
|
35
|
-
// Imports under test
|
|
71
|
+
// Imports under test
|
|
72
|
+
//
|
|
73
|
+
// Type-only imports are erased at runtime and safe to hoist. The value import
|
|
74
|
+
// of `host-proxy-preactivation` must be dynamic (`await import`) so it
|
|
75
|
+
// resolves AFTER the `mock.module(...)` calls above — otherwise ES module
|
|
76
|
+
// hoisting loads the real logger before the mock registers, the production
|
|
77
|
+
// `const log = getLogger(...)` binds to real pino, and assertions against
|
|
78
|
+
// `loggedInfoCalls` see an empty array. Same pattern as
|
|
79
|
+
// `secret-prompt-log-hygiene.test.ts`.
|
|
36
80
|
// ---------------------------------------------------------------------------
|
|
37
81
|
|
|
38
82
|
import type { HostProxyCapability } from "../channels/types.js";
|
|
39
|
-
import {
|
|
40
|
-
|
|
83
|
+
import type { HostProxyPreactivationTarget } from "../daemon/host-proxy-preactivation.js";
|
|
84
|
+
|
|
85
|
+
const {
|
|
86
|
+
evaluateHostProxyAttachment,
|
|
41
87
|
preactivateHostProxySkills,
|
|
42
88
|
shouldAttachHostProxyForCapability,
|
|
43
|
-
}
|
|
89
|
+
} = await import("../daemon/host-proxy-preactivation.js");
|
|
44
90
|
|
|
45
91
|
// ---------------------------------------------------------------------------
|
|
46
92
|
// Helpers
|
|
47
93
|
// ---------------------------------------------------------------------------
|
|
48
94
|
|
|
49
|
-
function makeTarget(
|
|
50
|
-
|
|
51
|
-
} {
|
|
95
|
+
function makeTarget(
|
|
96
|
+
conversationId = "conv-test",
|
|
97
|
+
): HostProxyPreactivationTarget & { preactivatedSkillIds: string[] } {
|
|
52
98
|
const preactivatedSkillIds: string[] = [];
|
|
53
99
|
return {
|
|
100
|
+
conversationId,
|
|
54
101
|
preactivatedSkillIds,
|
|
55
102
|
addPreactivatedSkillId(id: string) {
|
|
56
103
|
preactivatedSkillIds.push(id);
|
|
@@ -73,6 +120,7 @@ function setCapableClient(
|
|
|
73
120
|
|
|
74
121
|
beforeEach(() => {
|
|
75
122
|
mockClientsByCapability = new Map();
|
|
123
|
+
loggedInfoCalls.length = 0;
|
|
76
124
|
});
|
|
77
125
|
|
|
78
126
|
// ---------------------------------------------------------------------------
|
|
@@ -143,7 +191,10 @@ describe("shouldAttachHostProxyForCapability", () => {
|
|
|
143
191
|
test("returns false for chrome-extension source even when a capable client is connected", () => {
|
|
144
192
|
setCapableClient("host_app_control", true);
|
|
145
193
|
expect(
|
|
146
|
-
shouldAttachHostProxyForCapability(
|
|
194
|
+
shouldAttachHostProxyForCapability(
|
|
195
|
+
"host_app_control",
|
|
196
|
+
"chrome-extension",
|
|
197
|
+
),
|
|
147
198
|
).toBe(false);
|
|
148
199
|
});
|
|
149
200
|
});
|
|
@@ -154,7 +205,7 @@ describe("shouldAttachHostProxyForCapability", () => {
|
|
|
154
205
|
// ---------------------------------------------------------------------------
|
|
155
206
|
|
|
156
207
|
describe("preactivateHostProxySkills", () => {
|
|
157
|
-
test("no
|
|
208
|
+
test("preactivates no skills when sourceInterface is undefined", () => {
|
|
158
209
|
const target = makeTarget();
|
|
159
210
|
preactivateHostProxySkills(target, undefined);
|
|
160
211
|
expect(target.preactivatedSkillIds).toEqual([]);
|
|
@@ -209,3 +260,139 @@ describe("preactivateHostProxySkills", () => {
|
|
|
209
260
|
expect(target.preactivatedSkillIds).toEqual([]);
|
|
210
261
|
});
|
|
211
262
|
});
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// evaluateHostProxyAttachment — reason coverage
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
describe("evaluateHostProxyAttachment", () => {
|
|
269
|
+
test("returns denied_no_interface when sourceInterface is undefined", () => {
|
|
270
|
+
expect(evaluateHostProxyAttachment("host_cu", undefined)).toEqual({
|
|
271
|
+
shouldAttach: false,
|
|
272
|
+
reason: "denied_no_interface",
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("returns native_support for macos + host_cu", () => {
|
|
277
|
+
expect(evaluateHostProxyAttachment("host_cu", "macos")).toEqual({
|
|
278
|
+
shouldAttach: true,
|
|
279
|
+
reason: "native_support",
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("returns denied_chrome_extension for chrome-extension source even when capable clients exist", () => {
|
|
284
|
+
setCapableClient("host_cu", true);
|
|
285
|
+
expect(evaluateHostProxyAttachment("host_cu", "chrome-extension")).toEqual({
|
|
286
|
+
shouldAttach: false,
|
|
287
|
+
reason: "denied_chrome_extension",
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("returns cross_client with clientCount when a capable client is connected", () => {
|
|
292
|
+
setCapableClient("host_cu", true);
|
|
293
|
+
expect(evaluateHostProxyAttachment("host_cu", "web")).toEqual({
|
|
294
|
+
shouldAttach: true,
|
|
295
|
+
reason: "cross_client",
|
|
296
|
+
clientCount: 1,
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("returns denied_no_clients with clientCount 0 when no capable client is connected", () => {
|
|
301
|
+
setCapableClient("host_cu", false);
|
|
302
|
+
expect(evaluateHostProxyAttachment("host_cu", "web")).toEqual({
|
|
303
|
+
shouldAttach: false,
|
|
304
|
+
reason: "denied_no_clients",
|
|
305
|
+
clientCount: 0,
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// preactivateHostProxySkills — structured logging
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
describe("preactivateHostProxySkills logging", () => {
|
|
315
|
+
test("emits exactly one info log per call", () => {
|
|
316
|
+
const target = makeTarget();
|
|
317
|
+
preactivateHostProxySkills(target, "macos");
|
|
318
|
+
expect(loggedInfoCalls).toHaveLength(1);
|
|
319
|
+
expect(loggedInfoCalls[0].message).toBe(
|
|
320
|
+
"host-proxy preactivation decision",
|
|
321
|
+
);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("log includes conversationId, sourceInterface, per-capability decisions, and preactivatedSkillIds for macos", () => {
|
|
325
|
+
const target = makeTarget("conv-macos-123");
|
|
326
|
+
preactivateHostProxySkills(target, "macos");
|
|
327
|
+
|
|
328
|
+
expect(loggedInfoCalls).toHaveLength(1);
|
|
329
|
+
const { fields } = loggedInfoCalls[0];
|
|
330
|
+
expect(fields.conversationId).toBe("conv-macos-123");
|
|
331
|
+
expect(fields.sourceInterface).toBe("macos");
|
|
332
|
+
expect(fields.decisions).toEqual({
|
|
333
|
+
host_cu: { shouldAttach: true, reason: "native_support" },
|
|
334
|
+
host_app_control: { shouldAttach: true, reason: "native_support" },
|
|
335
|
+
});
|
|
336
|
+
expect(fields.preactivatedSkillIds).toEqual([
|
|
337
|
+
"computer-use",
|
|
338
|
+
"app-control",
|
|
339
|
+
]);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test("log captures denied_no_interface for undefined sourceInterface (silent-gate diagnostic)", () => {
|
|
343
|
+
const target = makeTarget("conv-no-interface");
|
|
344
|
+
preactivateHostProxySkills(target, undefined);
|
|
345
|
+
|
|
346
|
+
expect(loggedInfoCalls).toHaveLength(1);
|
|
347
|
+
const { fields } = loggedInfoCalls[0];
|
|
348
|
+
expect(fields.conversationId).toBe("conv-no-interface");
|
|
349
|
+
expect(fields.sourceInterface).toBeUndefined();
|
|
350
|
+
expect(fields.decisions).toEqual({
|
|
351
|
+
host_cu: { shouldAttach: false, reason: "denied_no_interface" },
|
|
352
|
+
host_app_control: { shouldAttach: false, reason: "denied_no_interface" },
|
|
353
|
+
});
|
|
354
|
+
expect(fields.preactivatedSkillIds).toEqual([]);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("log captures cross_client + clientCount when a web source has a connected host_cu client", () => {
|
|
358
|
+
setCapableClient("host_cu", true);
|
|
359
|
+
const target = makeTarget();
|
|
360
|
+
preactivateHostProxySkills(target, "web");
|
|
361
|
+
|
|
362
|
+
expect(loggedInfoCalls).toHaveLength(1);
|
|
363
|
+
const decisions = loggedInfoCalls[0].fields.decisions as Record<
|
|
364
|
+
string,
|
|
365
|
+
unknown
|
|
366
|
+
>;
|
|
367
|
+
expect(decisions.host_cu).toEqual({
|
|
368
|
+
shouldAttach: true,
|
|
369
|
+
reason: "cross_client",
|
|
370
|
+
clientCount: 1,
|
|
371
|
+
});
|
|
372
|
+
expect(decisions.host_app_control).toEqual({
|
|
373
|
+
shouldAttach: false,
|
|
374
|
+
reason: "denied_no_clients",
|
|
375
|
+
clientCount: 0,
|
|
376
|
+
});
|
|
377
|
+
expect(loggedInfoCalls[0].fields.preactivatedSkillIds).toEqual([
|
|
378
|
+
"computer-use",
|
|
379
|
+
]);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test("log captures denied_chrome_extension reason for chrome-extension source", () => {
|
|
383
|
+
setCapableClient("host_cu", true);
|
|
384
|
+
const target = makeTarget();
|
|
385
|
+
preactivateHostProxySkills(target, "chrome-extension");
|
|
386
|
+
|
|
387
|
+
expect(loggedInfoCalls).toHaveLength(1);
|
|
388
|
+
const decisions = loggedInfoCalls[0].fields.decisions as Record<
|
|
389
|
+
string,
|
|
390
|
+
unknown
|
|
391
|
+
>;
|
|
392
|
+
expect(decisions.host_cu).toEqual({
|
|
393
|
+
shouldAttach: false,
|
|
394
|
+
reason: "denied_chrome_extension",
|
|
395
|
+
});
|
|
396
|
+
expect(loggedInfoCalls[0].fields.preactivatedSkillIds).toEqual([]);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
@@ -8,7 +8,14 @@ const sentMessages: unknown[] = [];
|
|
|
8
8
|
let mockHasClient = false;
|
|
9
9
|
|
|
10
10
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
11
|
-
broadcastMessage: (msg: unknown) =>
|
|
11
|
+
broadcastMessage: (msg: unknown) => {
|
|
12
|
+
// Skip `interaction_resolved` envelopes — pending-interactions emits one
|
|
13
|
+
// on every resolve and these tests assert on host-proxy wire messages.
|
|
14
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
sentMessages.push(msg);
|
|
18
|
+
},
|
|
12
19
|
assistantEventHub: {
|
|
13
20
|
getMostRecentClientByCapability: (cap: string) =>
|
|
14
21
|
cap === "host_file" && mockHasClient ? { id: "mock-client" } : null,
|
|
@@ -24,8 +24,12 @@ mock.module("../util/logger.js", () => ({
|
|
|
24
24
|
|
|
25
25
|
import {
|
|
26
26
|
handleDetailedHealth,
|
|
27
|
+
handleReadyz,
|
|
27
28
|
ROUTES,
|
|
28
29
|
} from "../runtime/routes/identity-routes.js";
|
|
30
|
+
import {
|
|
31
|
+
setCesClient,
|
|
32
|
+
} from "../security/secure-keys.js";
|
|
29
33
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
30
34
|
import {
|
|
31
35
|
getHatchedSidecarPath,
|
|
@@ -177,6 +181,59 @@ describe("identity routes — health endpoint", () => {
|
|
|
177
181
|
expect(body.timestamp).toBeDefined();
|
|
178
182
|
expect(body.migrations).toBeDefined();
|
|
179
183
|
});
|
|
184
|
+
|
|
185
|
+
test("includes ces.connected=false when no CES client is registered", async () => {
|
|
186
|
+
const res = handleDetailedHealth();
|
|
187
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
188
|
+
|
|
189
|
+
expect(body.ces).toBeDefined();
|
|
190
|
+
expect((body.ces as Record<string, unknown>).connected).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("CES readiness", () => {
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
setCesClient(undefined);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("readyz returns 200 and logs warning when CES is unavailable", () => {
|
|
200
|
+
const res = handleReadyz();
|
|
201
|
+
expect(res.status).toBe(200);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("readyz returns 200 when CES is connected and ready", () => {
|
|
205
|
+
const mockClient = { isReady: () => true, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
206
|
+
setCesClient(mockClient);
|
|
207
|
+
const res = handleReadyz();
|
|
208
|
+
expect(res.status).toBe(200);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("readyz returns 200 when CES client exists but is not ready", () => {
|
|
212
|
+
const mockClient = { isReady: () => false, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
213
|
+
setCesClient(mockClient);
|
|
214
|
+
const res = handleReadyz();
|
|
215
|
+
expect(res.status).toBe(200);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("/v1/health reports ces.connected=true when CES is ready", async () => {
|
|
219
|
+
const mockClient = { isReady: () => true, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
220
|
+
setCesClient(mockClient);
|
|
221
|
+
const res = handleDetailedHealth();
|
|
222
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
223
|
+
|
|
224
|
+
expect(body.ces).toBeDefined();
|
|
225
|
+
expect((body.ces as Record<string, unknown>).connected).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("/v1/health reports ces.connected=false when CES is not ready", async () => {
|
|
229
|
+
const mockClient = { isReady: () => false, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
230
|
+
setCesClient(mockClient);
|
|
231
|
+
const res = handleDetailedHealth();
|
|
232
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
233
|
+
|
|
234
|
+
expect(body.ces).toBeDefined();
|
|
235
|
+
expect((body.ces as Record<string, unknown>).connected).toBe(false);
|
|
236
|
+
});
|
|
180
237
|
});
|
|
181
238
|
|
|
182
239
|
describe("profiler payload (profiler enabled)", () => {
|