@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
|
@@ -175,7 +175,7 @@ describe("isConversationSeedSane", () => {
|
|
|
175
175
|
|
|
176
176
|
describe("composeConversationSeed", () => {
|
|
177
177
|
describe("rich verbosity (vellum/macos)", () => {
|
|
178
|
-
test("
|
|
178
|
+
test("omits title when conversationTitle is absent (it's the chat header)", () => {
|
|
179
179
|
const signal = makeSignal();
|
|
180
180
|
const copy = makeCopy({ title: "Reminder", body: "Take out the trash" });
|
|
181
181
|
const seed = composeConversationSeed(
|
|
@@ -183,12 +183,71 @@ describe("composeConversationSeed", () => {
|
|
|
183
183
|
"vellum" as NotificationChannel,
|
|
184
184
|
copy,
|
|
185
185
|
);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
// The conversation header already shows copy.title — including it in
|
|
187
|
+
// the bubble would duplicate it.
|
|
188
|
+
expect(seed).toBe("Take out the trash");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("includes title when conversationTitle provides a distinct header", () => {
|
|
192
|
+
const signal = makeSignal();
|
|
193
|
+
const copy = makeCopy({
|
|
194
|
+
conversationTitle: "Updates",
|
|
195
|
+
title: "Heart rate spike",
|
|
196
|
+
body: "You hit 103 bpm.",
|
|
197
|
+
});
|
|
198
|
+
const seed = composeConversationSeed(
|
|
199
|
+
signal,
|
|
200
|
+
"vellum" as NotificationChannel,
|
|
201
|
+
copy,
|
|
202
|
+
);
|
|
203
|
+
expect(seed).toContain("Heart rate spike");
|
|
204
|
+
expect(seed).toContain("You hit 103 bpm");
|
|
189
205
|
expect(seed).not.toContain("\n");
|
|
190
206
|
});
|
|
191
207
|
|
|
208
|
+
test("does not duplicate title when body already starts with it", () => {
|
|
209
|
+
const signal = makeSignal();
|
|
210
|
+
const copy = makeCopy({
|
|
211
|
+
title: "Status update — service running",
|
|
212
|
+
body: "Status update — service running since 12:00 PM.",
|
|
213
|
+
});
|
|
214
|
+
const seed = composeConversationSeed(
|
|
215
|
+
signal,
|
|
216
|
+
"vellum" as NotificationChannel,
|
|
217
|
+
copy,
|
|
218
|
+
);
|
|
219
|
+
// Without the fix, this would produce
|
|
220
|
+
// "Status update — service running. Status update — service running since 12:00 PM."
|
|
221
|
+
expect(seed).toBe("Status update — service running since 12:00 PM.");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("falls back to title when body is empty and title is the header", () => {
|
|
225
|
+
const signal = makeSignal();
|
|
226
|
+
const copy = makeCopy({ title: "Reminder", body: "" });
|
|
227
|
+
const seed = composeConversationSeed(
|
|
228
|
+
signal,
|
|
229
|
+
"vellum" as NotificationChannel,
|
|
230
|
+
copy,
|
|
231
|
+
);
|
|
232
|
+
// Even though the title is the conversation header, keeping the
|
|
233
|
+
// bubble non-empty wins over avoiding the redundancy.
|
|
234
|
+
expect(seed).toBe("Reminder");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("falls back to title when body is whitespace-only", () => {
|
|
238
|
+
const signal = makeSignal();
|
|
239
|
+
const copy = makeCopy({ title: "Reminder", body: " " });
|
|
240
|
+
const seed = composeConversationSeed(
|
|
241
|
+
signal,
|
|
242
|
+
"vellum" as NotificationChannel,
|
|
243
|
+
copy,
|
|
244
|
+
);
|
|
245
|
+
// Whitespace-only bodies must be treated as empty — otherwise the
|
|
246
|
+
// title would be suppressed (header dedupe) and the seed would
|
|
247
|
+
// render as a blank bubble.
|
|
248
|
+
expect(seed).toBe("Reminder");
|
|
249
|
+
});
|
|
250
|
+
|
|
192
251
|
test('appends "Action required." when requiresAction is true', () => {
|
|
193
252
|
const signal = makeSignal({
|
|
194
253
|
attentionHints: {
|
|
@@ -277,6 +336,7 @@ describe("composeConversationSeed", () => {
|
|
|
277
336
|
test("preserves localized LLM copy on vellum (rich)", () => {
|
|
278
337
|
const signal = makeSignal();
|
|
279
338
|
const copy = makeCopy({
|
|
339
|
+
conversationTitle: "通知",
|
|
280
340
|
title: "リマインダー",
|
|
281
341
|
body: "ゴミを出してください",
|
|
282
342
|
});
|
|
@@ -285,6 +345,7 @@ describe("composeConversationSeed", () => {
|
|
|
285
345
|
"vellum" as NotificationChannel,
|
|
286
346
|
copy,
|
|
287
347
|
);
|
|
348
|
+
// conversationTitle is set, so the rich seed includes both title and body.
|
|
288
349
|
expect(seed).toContain("リマインダー");
|
|
289
350
|
expect(seed).toContain("ゴミを出してください");
|
|
290
351
|
});
|
|
@@ -314,6 +375,7 @@ describe("composeConversationSeed", () => {
|
|
|
314
375
|
},
|
|
315
376
|
});
|
|
316
377
|
const copy = makeCopy({
|
|
378
|
+
conversationTitle: "ガーディアン",
|
|
317
379
|
title: "ガーディアンの質問",
|
|
318
380
|
body: "ゲートコードは何ですか?",
|
|
319
381
|
});
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
invalidateConfigCache,
|
|
6
|
-
loadRawConfig,
|
|
7
|
-
} from "../config/loader.js";
|
|
4
|
+
import { invalidateConfigCache, loadRawConfig } from "../config/loader.js";
|
|
8
5
|
import {
|
|
9
6
|
classifySlash,
|
|
10
7
|
resolveSlash,
|
|
@@ -42,6 +39,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
42
39
|
expect(lines).toEqual([
|
|
43
40
|
"/commands — List all available commands",
|
|
44
41
|
"/compact — Force context compaction immediately",
|
|
42
|
+
"/clean — Strip injected runtime context and reset memory injection state (no summarization)",
|
|
45
43
|
"/context — Show conversation context usage",
|
|
46
44
|
"/model — List or switch inference profile",
|
|
47
45
|
"/models — List all available models",
|
|
@@ -58,6 +56,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
58
56
|
expect(lines).toEqual([
|
|
59
57
|
"/commands — List all available commands",
|
|
60
58
|
"/compact — Force context compaction immediately",
|
|
59
|
+
"/clean — Strip injected runtime context and reset memory injection state (no summarization)",
|
|
61
60
|
"/context — Show conversation context usage",
|
|
62
61
|
"/model — List or switch inference profile",
|
|
63
62
|
"/models — List all available models",
|
|
@@ -74,6 +73,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
74
73
|
expect(lines).toEqual([
|
|
75
74
|
"/commands — List all available commands",
|
|
76
75
|
"/compact — Force context compaction immediately",
|
|
76
|
+
"/clean — Strip injected runtime context and reset memory injection state (no summarization)",
|
|
77
77
|
"/context — Show conversation context usage",
|
|
78
78
|
"/model — List or switch inference profile",
|
|
79
79
|
"/models — List all available models",
|
|
@@ -87,6 +87,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
87
87
|
expect(lines).toEqual([
|
|
88
88
|
"/commands — List all available commands",
|
|
89
89
|
"/compact — Force context compaction immediately",
|
|
90
|
+
"/clean — Strip injected runtime context and reset memory injection state (no summarization)",
|
|
90
91
|
"/context — Show conversation context usage",
|
|
91
92
|
"/model — List or switch inference profile",
|
|
92
93
|
"/models — List all available models",
|
|
@@ -99,6 +100,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
99
100
|
expect(lines).toEqual([
|
|
100
101
|
"/commands — List all available commands",
|
|
101
102
|
"/compact — Force context compaction immediately",
|
|
103
|
+
"/clean — Strip injected runtime context and reset memory injection state (no summarization)",
|
|
102
104
|
"/model — List or switch inference profile",
|
|
103
105
|
"/models — List all available models",
|
|
104
106
|
]);
|
|
@@ -187,13 +189,38 @@ describe("resolveSlash /compact target override", () => {
|
|
|
187
189
|
});
|
|
188
190
|
});
|
|
189
191
|
|
|
192
|
+
describe("resolveSlash /clean", () => {
|
|
193
|
+
test("plain /clean resolves to kind=clean", async () => {
|
|
194
|
+
const result = await resolveSlash("/clean");
|
|
195
|
+
expect(result).toEqual({ kind: "clean" });
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("/clean tolerates surrounding whitespace", async () => {
|
|
199
|
+
const result = await resolveSlash(" /clean ");
|
|
200
|
+
expect(result).toEqual({ kind: "clean" });
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("/clean is case-insensitive", async () => {
|
|
204
|
+
const result = await resolveSlash("/CLEAN");
|
|
205
|
+
expect(result).toEqual({ kind: "clean" });
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("/clean rejects arguments with usage hint", async () => {
|
|
209
|
+
const result = await resolveSlash("/clean now");
|
|
210
|
+
expect(result.kind).toBe("unknown");
|
|
211
|
+
if (result.kind !== "unknown") throw new Error("expected unknown");
|
|
212
|
+
expect(result.message).toContain("/clean");
|
|
213
|
+
expect(result.message).toContain("does not take arguments");
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
190
217
|
describe("classifySlash is a pure classifier matching resolveSlash kinds", () => {
|
|
191
218
|
// Lookahead in `buildPassthroughBatch` must not run `resolveSlash`'s side
|
|
192
219
|
// effects. The pure classifier is synchronous, takes no side-effecting
|
|
193
220
|
// dependencies, and must agree with resolveSlash's `kind`.
|
|
194
221
|
const cases: Array<{
|
|
195
222
|
input: string;
|
|
196
|
-
kind: "passthrough" | "compact" | "unknown";
|
|
223
|
+
kind: "passthrough" | "compact" | "clean" | "unknown";
|
|
197
224
|
}> = [
|
|
198
225
|
{ input: "/models", kind: "unknown" },
|
|
199
226
|
{ input: "/context", kind: "unknown" },
|
|
@@ -204,6 +231,9 @@ describe("classifySlash is a pure classifier matching resolveSlash kinds", () =>
|
|
|
204
231
|
{ input: "/compact 30k", kind: "compact" },
|
|
205
232
|
{ input: "/compact 1.5M", kind: "compact" },
|
|
206
233
|
{ input: "/compact bogus", kind: "unknown" },
|
|
234
|
+
{ input: "/clean", kind: "clean" },
|
|
235
|
+
{ input: " /clean ", kind: "clean" },
|
|
236
|
+
{ input: "/clean foo", kind: "unknown" },
|
|
207
237
|
{ input: "/model", kind: "unknown" },
|
|
208
238
|
{ input: "/model foo", kind: "unknown" },
|
|
209
239
|
{ input: "/opus", kind: "unknown" },
|
|
@@ -318,9 +348,7 @@ describe("resolveSlash /model — inference profile switcher", () => {
|
|
|
318
348
|
const result = await resolveSlash("/model balanced");
|
|
319
349
|
expect(result.kind).toBe("unknown");
|
|
320
350
|
if (result.kind !== "unknown") throw new Error("expected unknown kind");
|
|
321
|
-
expect(result.message).toBe(
|
|
322
|
-
"Already using profile `balanced` (Balanced).",
|
|
323
|
-
);
|
|
351
|
+
expect(result.message).toBe("Already using profile `balanced` (Balanced).");
|
|
324
352
|
});
|
|
325
353
|
|
|
326
354
|
test("`/model` with no profiles defined points at Settings", async () => {
|
|
@@ -28,7 +28,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
28
28
|
|
|
29
29
|
mock.module("../providers/registry.js", () => ({
|
|
30
30
|
getProvider: () => ({ name: "mock-provider" }),
|
|
31
|
-
initializeProviders: () => {},
|
|
31
|
+
initializeProviders: async () => {},
|
|
32
32
|
}));
|
|
33
33
|
|
|
34
34
|
mock.module("../config/loader.js", () => ({
|
|
@@ -24,7 +24,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
24
24
|
|
|
25
25
|
mock.module("../providers/registry.js", () => ({
|
|
26
26
|
getProvider: () => ({ name: "mock-provider" }),
|
|
27
|
-
initializeProviders: () => {},
|
|
27
|
+
initializeProviders: async () => {},
|
|
28
28
|
}));
|
|
29
29
|
|
|
30
30
|
mock.module("../config/loader.js", () => ({
|
|
@@ -48,7 +48,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
48
48
|
|
|
49
49
|
mock.module("../providers/registry.js", () => ({
|
|
50
50
|
getProvider: () => ({ name: "mock-provider" }),
|
|
51
|
-
initializeProviders: () => {},
|
|
51
|
+
initializeProviders: async () => {},
|
|
52
52
|
}));
|
|
53
53
|
|
|
54
54
|
// Controllable config mock — speed and feature flag behavior are test-specific.
|
|
@@ -84,6 +84,95 @@ describe("task_progress surface compatibility", () => {
|
|
|
84
84
|
expect(sent).toHaveLength(0);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
test("allows Slack ui_show for task_progress card when dynamic UI is otherwise disabled", async () => {
|
|
88
|
+
const sent: ServerMessage[] = [];
|
|
89
|
+
const ctx = makeContext(sent, {
|
|
90
|
+
channel: "slack",
|
|
91
|
+
supportsDynamicUi: false,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
95
|
+
surface_type: "card",
|
|
96
|
+
title: "Working",
|
|
97
|
+
template: "task_progress",
|
|
98
|
+
templateData: {
|
|
99
|
+
status: "in_progress",
|
|
100
|
+
steps: [{ label: "Start", status: "in_progress" }],
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(result.isError).toBe(false);
|
|
105
|
+
const showMessage = sent.find(
|
|
106
|
+
(msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
|
|
107
|
+
);
|
|
108
|
+
expect(showMessage).toBeDefined();
|
|
109
|
+
if (!showMessage || showMessage.surfaceType !== "card") return;
|
|
110
|
+
expect((showMessage.data as CardSurfaceData).template).toBe(
|
|
111
|
+
"task_progress",
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("blocks Slack ui_show for non-task_progress card when dynamic UI is disabled", async () => {
|
|
116
|
+
const sent: ServerMessage[] = [];
|
|
117
|
+
const ctx = makeContext(sent, {
|
|
118
|
+
channel: "slack",
|
|
119
|
+
supportsDynamicUi: false,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
123
|
+
surface_type: "card",
|
|
124
|
+
title: "Blocked",
|
|
125
|
+
data: { title: "Blocked", body: "not progress" },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(result.isError).toBe(true);
|
|
129
|
+
expect(result.content).toContain(
|
|
130
|
+
'ui_show is unavailable on channel "slack"',
|
|
131
|
+
);
|
|
132
|
+
expect(sent).toHaveLength(0);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("blocks Slack ui_show when normalized card data is not task_progress", async () => {
|
|
136
|
+
const sent: ServerMessage[] = [];
|
|
137
|
+
const ctx = makeContext(sent, {
|
|
138
|
+
channel: "slack",
|
|
139
|
+
supportsDynamicUi: false,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
143
|
+
surface_type: "card",
|
|
144
|
+
title: "Blocked",
|
|
145
|
+
template: "task_progress",
|
|
146
|
+
data: { title: "Blocked", body: "not progress", template: "plain" },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(result.isError).toBe(true);
|
|
150
|
+
expect(result.content).toContain(
|
|
151
|
+
'ui_show is unavailable on channel "slack"',
|
|
152
|
+
);
|
|
153
|
+
expect(sent).toHaveLength(0);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("blocks Slack ui_show for non-card task_progress input when dynamic UI is disabled", async () => {
|
|
157
|
+
const sent: ServerMessage[] = [];
|
|
158
|
+
const ctx = makeContext(sent, {
|
|
159
|
+
channel: "slack",
|
|
160
|
+
supportsDynamicUi: false,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
164
|
+
surface_type: "dynamic_page",
|
|
165
|
+
title: "Blocked",
|
|
166
|
+
data: { template: "task_progress", html: "<p>Blocked</p>" },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(result.isError).toBe(true);
|
|
170
|
+
expect(result.content).toContain(
|
|
171
|
+
'ui_show is unavailable on channel "slack"',
|
|
172
|
+
);
|
|
173
|
+
expect(sent).toHaveLength(0);
|
|
174
|
+
});
|
|
175
|
+
|
|
87
176
|
test("ui_show maps legacy top-level task_progress fields into card data", async () => {
|
|
88
177
|
const sent: ServerMessage[] = [];
|
|
89
178
|
const ctx = makeContext(sent);
|
|
@@ -249,6 +338,137 @@ describe("task_progress surface compatibility", () => {
|
|
|
249
338
|
expect(Array.isArray(templateData.steps)).toBe(true);
|
|
250
339
|
});
|
|
251
340
|
|
|
341
|
+
test("allows Slack ui_update for stored task_progress card when dynamic UI is disabled", async () => {
|
|
342
|
+
const sent: ServerMessage[] = [];
|
|
343
|
+
const ctx = makeContext(sent, {
|
|
344
|
+
channel: "slack",
|
|
345
|
+
supportsDynamicUi: false,
|
|
346
|
+
});
|
|
347
|
+
ctx.surfaceState.set("surface-1", {
|
|
348
|
+
surfaceType: "card",
|
|
349
|
+
data: {
|
|
350
|
+
title: "Working",
|
|
351
|
+
body: "",
|
|
352
|
+
template: "task_progress",
|
|
353
|
+
templateData: { status: "in_progress", steps: [] },
|
|
354
|
+
} satisfies CardSurfaceData,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const result = await surfaceProxyResolver(ctx, "ui_update", {
|
|
358
|
+
surface_id: "surface-1",
|
|
359
|
+
data: { status: "completed" },
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(result.isError).toBe(false);
|
|
363
|
+
const updateMessage = sent.find(
|
|
364
|
+
(msg): msg is UiSurfaceUpdate => msg.type === "ui_surface_update",
|
|
365
|
+
);
|
|
366
|
+
expect(updateMessage).toBeDefined();
|
|
367
|
+
if (!updateMessage) return;
|
|
368
|
+
const templateData = (updateMessage.data as CardSurfaceData)
|
|
369
|
+
.templateData as Record<string, unknown>;
|
|
370
|
+
expect(templateData.status).toBe("completed");
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test("blocks Slack ui_update when stored surface is not a task_progress card", async () => {
|
|
374
|
+
const sent: ServerMessage[] = [];
|
|
375
|
+
const ctx = makeContext(sent, {
|
|
376
|
+
channel: "slack",
|
|
377
|
+
supportsDynamicUi: false,
|
|
378
|
+
});
|
|
379
|
+
ctx.surfaceState.set("surface-1", {
|
|
380
|
+
surfaceType: "card",
|
|
381
|
+
data: { title: "Plain", body: "No progress" } satisfies CardSurfaceData,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const result = await surfaceProxyResolver(ctx, "ui_update", {
|
|
385
|
+
surface_id: "surface-1",
|
|
386
|
+
data: { body: "still blocked" },
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
expect(result.isError).toBe(true);
|
|
390
|
+
expect(result.content).toContain(
|
|
391
|
+
'ui_update is unavailable on channel "slack"',
|
|
392
|
+
);
|
|
393
|
+
expect(sent).toHaveLength(0);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("blocks Slack ui_update that would convert a plain card to task_progress", async () => {
|
|
397
|
+
const sent: ServerMessage[] = [];
|
|
398
|
+
const ctx = makeContext(sent, {
|
|
399
|
+
channel: "slack",
|
|
400
|
+
supportsDynamicUi: false,
|
|
401
|
+
});
|
|
402
|
+
ctx.surfaceState.set("surface-1", {
|
|
403
|
+
surfaceType: "card",
|
|
404
|
+
data: { title: "Plain", body: "No progress" } satisfies CardSurfaceData,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const result = await surfaceProxyResolver(ctx, "ui_update", {
|
|
408
|
+
surface_id: "surface-1",
|
|
409
|
+
data: {
|
|
410
|
+
template: "task_progress",
|
|
411
|
+
templateData: { status: "in_progress", steps: [] },
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
expect(result.isError).toBe(true);
|
|
416
|
+
expect(result.content).toContain(
|
|
417
|
+
'ui_update is unavailable on channel "slack"',
|
|
418
|
+
);
|
|
419
|
+
expect(sent).toHaveLength(0);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("blocks Slack ui_update that would change task_progress card template", async () => {
|
|
423
|
+
const sent: ServerMessage[] = [];
|
|
424
|
+
const ctx = makeContext(sent, {
|
|
425
|
+
channel: "slack",
|
|
426
|
+
supportsDynamicUi: false,
|
|
427
|
+
});
|
|
428
|
+
ctx.surfaceState.set("surface-1", {
|
|
429
|
+
surfaceType: "card",
|
|
430
|
+
data: {
|
|
431
|
+
title: "Working",
|
|
432
|
+
body: "",
|
|
433
|
+
template: "task_progress",
|
|
434
|
+
templateData: { status: "in_progress", steps: [] },
|
|
435
|
+
} satisfies CardSurfaceData,
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const result = await surfaceProxyResolver(ctx, "ui_update", {
|
|
439
|
+
surface_id: "surface-1",
|
|
440
|
+
data: { template: "plain", body: "now a plain card" },
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
expect(result.isError).toBe(true);
|
|
444
|
+
expect(result.content).toContain(
|
|
445
|
+
'ui_update is unavailable on channel "slack"',
|
|
446
|
+
);
|
|
447
|
+
expect(sent).toHaveLength(0);
|
|
448
|
+
expect(
|
|
449
|
+
(ctx.surfaceState.get("surface-1")?.data as CardSurfaceData).template,
|
|
450
|
+
).toBe("task_progress");
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test("blocks Slack ui_update when the surface is not already stored", async () => {
|
|
454
|
+
const sent: ServerMessage[] = [];
|
|
455
|
+
const ctx = makeContext(sent, {
|
|
456
|
+
channel: "slack",
|
|
457
|
+
supportsDynamicUi: false,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const result = await surfaceProxyResolver(ctx, "ui_update", {
|
|
461
|
+
surface_id: "missing-surface",
|
|
462
|
+
data: { status: "completed" },
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
expect(result.isError).toBe(true);
|
|
466
|
+
expect(result.content).toContain(
|
|
467
|
+
'ui_update is unavailable on channel "slack"',
|
|
468
|
+
);
|
|
469
|
+
expect(sent).toHaveLength(0);
|
|
470
|
+
});
|
|
471
|
+
|
|
252
472
|
test("ui_show rejects new interactive surface when a non-dynamic_page pending surface exists", async () => {
|
|
253
473
|
const sent: ServerMessage[] = [];
|
|
254
474
|
const ctx = makeContext(sent);
|
|
@@ -18,7 +18,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
18
18
|
|
|
19
19
|
mock.module("../providers/registry.js", () => ({
|
|
20
20
|
getProvider: () => ({ name: "mock-provider" }),
|
|
21
|
-
initializeProviders: () => {},
|
|
21
|
+
initializeProviders: async () => {},
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
24
|
mock.module("../config/loader.js", () => ({
|
|
@@ -27,7 +27,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
27
27
|
|
|
28
28
|
mock.module("../providers/registry.js", () => ({
|
|
29
29
|
getProvider: () => ({ name: "mock-provider" }),
|
|
30
|
-
initializeProviders: () => {},
|
|
30
|
+
initializeProviders: async () => {},
|
|
31
31
|
}));
|
|
32
32
|
|
|
33
33
|
mock.module("../config/loader.js", () => ({
|
|
@@ -141,6 +141,10 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
141
141
|
deleteLastExchange: () => 0,
|
|
142
142
|
getMessageById: () => null,
|
|
143
143
|
getLastUserTimestampBefore: () => 0,
|
|
144
|
+
setLastNotifiedInferenceProfile: () => {},
|
|
145
|
+
getConversationOverrideProfileFromRow: () => undefined,
|
|
146
|
+
updateMessageMetadata: () => {},
|
|
147
|
+
clearStrippedInjectionMetadataForConversation: () => {},
|
|
144
148
|
}));
|
|
145
149
|
|
|
146
150
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -25,7 +25,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
25
25
|
|
|
26
26
|
mock.module("../providers/registry.js", () => ({
|
|
27
27
|
getProvider: () => ({ name: "mock-provider" }),
|
|
28
|
-
initializeProviders: () => {},
|
|
28
|
+
initializeProviders: async () => {},
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
31
|
mock.module("../config/loader.js", () => ({
|
|
@@ -138,6 +138,10 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
138
138
|
deleteLastExchange: () => 0,
|
|
139
139
|
getMessageById: () => null,
|
|
140
140
|
getLastUserTimestampBefore: () => 0,
|
|
141
|
+
setLastNotifiedInferenceProfile: () => {},
|
|
142
|
+
getConversationOverrideProfileFromRow: () => undefined,
|
|
143
|
+
updateMessageMetadata: () => {},
|
|
144
|
+
clearStrippedInjectionMetadataForConversation: () => {},
|
|
141
145
|
}));
|
|
142
146
|
|
|
143
147
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -209,6 +209,9 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
209
209
|
"config/bundled-skills/media-processing/tools/analyze-keyframes.ts", // keyframe analysis tool API key lookup
|
|
210
210
|
"providers/registry.ts", // provider registry API key lookup for initialization
|
|
211
211
|
"providers/inference/resolve-auth.ts", // provider_connection auth resolver (api_key path reads vault, mirrors registry.ts)
|
|
212
|
+
"providers/inference/codex-token-refresh.ts", // Codex OAuth token refresh (reads/writes access_token, refresh_token, expires_at)
|
|
213
|
+
"cli/commands/inference-providers.ts", // ChatGPT subscription OAuth token storage
|
|
214
|
+
"runtime/routes/chatgpt-subscription-auth-routes.ts", // ChatGPT subscription daemon OAuth flow (stores tokens in CES)
|
|
212
215
|
"providers/provider-availability.ts", // provider availability API key check
|
|
213
216
|
"media/image-credentials.ts", // shared image-gen credential resolver (provider API key lookup)
|
|
214
217
|
"memory/embedding-backend.ts", // embedding backend API key lookup
|
|
@@ -223,6 +226,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
223
226
|
"daemon/daemon-skill-host.ts", // SkillHost secureKeys facet adapter (delegates to getProviderKeyAsync)
|
|
224
227
|
"runtime/routes/credential-prompt-routes.ts", // Route for secure credential prompt (stores secret via setSecureKeyAsync)
|
|
225
228
|
"runtime/routes/credential-routes.ts", // CLI credential management routes (CLI-migrated to IPC)
|
|
229
|
+
"runtime/routes/sanity-routes.ts", // Sanity connect/discover routes (reads stored api_token from credential store)
|
|
226
230
|
"runtime/routes/platform-routes.ts", // CLI platform connect/disconnect/status routes (CLI-migrated to IPC)
|
|
227
231
|
"ipc/skill-routes/providers.ts", // host.providers.secureKeys.getProviderKey IPC route (out-of-process SkillHost companion)
|
|
228
232
|
"daemon/external-plugins-bootstrap.ts", // reads credentials at plugin init (manifest.requiresCredential) via the CES-mediated getSecureKeyAsync path
|
|
@@ -236,6 +240,8 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
236
240
|
"runtime/routes/avatar-routes.ts", // avatar generate route reads platform_base_url from credential store
|
|
237
241
|
"cli/commands/keys.ts", // CLI provider key management
|
|
238
242
|
"cli/commands/oauth/connect.ts", // CLI OAuth connect stored-secret verification
|
|
243
|
+
"runtime/routes/chatgpt-subscription-auth-routes.ts", // ChatGPT subscription OAuth token storage
|
|
244
|
+
"runtime/routes/identity-routes.ts", // health/readyz endpoint checks CES connectivity via getCesClient
|
|
239
245
|
]);
|
|
240
246
|
|
|
241
247
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -26,7 +26,16 @@ let mockCuClients: Array<{
|
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
29
|
-
broadcastMessage: (msg: unknown) =>
|
|
29
|
+
broadcastMessage: (msg: unknown) => {
|
|
30
|
+
// `interaction_resolved` envelopes are emitted by the
|
|
31
|
+
// pending-interactions tracker for every resolution. They are
|
|
32
|
+
// orthogonal to the surface-proxy wire messages these tests assert
|
|
33
|
+
// on, so swallow them here.
|
|
34
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
sentMessages.push(msg);
|
|
38
|
+
},
|
|
30
39
|
assistantEventHub: {
|
|
31
40
|
getMostRecentClientByCapability: (cap: string) =>
|
|
32
41
|
cap === "host_cu" && mockHasClient ? { id: "mock-client" } : null,
|
|
@@ -4,6 +4,7 @@ import { UiConfigSchema } from "../config/schemas/platform.js";
|
|
|
4
4
|
import {
|
|
5
5
|
canonicalizeTimeZone,
|
|
6
6
|
extractUserTimeZoneFromRecall,
|
|
7
|
+
formatLocalTimestamp,
|
|
7
8
|
formatTurnTimestamp,
|
|
8
9
|
resolveTurnTimezoneContext,
|
|
9
10
|
} from "../daemon/date-context.js";
|
|
@@ -321,3 +322,47 @@ describe("formatTurnTimestamp", () => {
|
|
|
321
322
|
expect(result).not.toContain("24:");
|
|
322
323
|
});
|
|
323
324
|
});
|
|
325
|
+
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
// formatLocalTimestamp
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
|
|
330
|
+
describe("formatLocalTimestamp", () => {
|
|
331
|
+
// 2026-05-11T17:30:00Z is 10:30 PDT (UTC-7, DST in effect).
|
|
332
|
+
const utcMidday = Date.parse("2026-05-11T17:30:00Z");
|
|
333
|
+
|
|
334
|
+
test("defaults to UTC when no timeZone is provided", () => {
|
|
335
|
+
expect(formatLocalTimestamp(utcMidday)).toBe("2026-05-11 17:30:00");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("explicit UTC matches the no-timezone default", () => {
|
|
339
|
+
expect(formatLocalTimestamp(utcMidday, "UTC")).toBe("2026-05-11 17:30:00");
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test("renders in America/Los_Angeles when configured", () => {
|
|
343
|
+
expect(formatLocalTimestamp(utcMidday, "America/Los_Angeles")).toBe(
|
|
344
|
+
"2026-05-11 10:30:00",
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test("renders in Asia/Tokyo when configured", () => {
|
|
349
|
+
// UTC+9 — 17:30 UTC is 02:30 next-day local.
|
|
350
|
+
expect(formatLocalTimestamp(utcMidday, "Asia/Tokyo")).toBe(
|
|
351
|
+
"2026-05-12 02:30:00",
|
|
352
|
+
);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test("handles a DST spring-forward boundary correctly", () => {
|
|
356
|
+
// 2026-03-08 06:30:00Z is 01:30 EST. The 02:00 → 03:00 spring-forward
|
|
357
|
+
// in America/New_York happens at 07:00 UTC. 07:30 UTC is 03:30 EDT.
|
|
358
|
+
const beforeJump = Date.parse("2026-03-08T06:30:00Z");
|
|
359
|
+
const afterJump = Date.parse("2026-03-08T07:30:00Z");
|
|
360
|
+
|
|
361
|
+
expect(formatLocalTimestamp(beforeJump, "America/New_York")).toBe(
|
|
362
|
+
"2026-03-08 01:30:00",
|
|
363
|
+
);
|
|
364
|
+
expect(formatLocalTimestamp(afterJump, "America/New_York")).toBe(
|
|
365
|
+
"2026-03-08 03:30:00",
|
|
366
|
+
);
|
|
367
|
+
});
|
|
368
|
+
});
|