@vellumai/assistant 0.8.3 → 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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- 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__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- 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 +25 -7
- 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-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 +264 -81
- 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__/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__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -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-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-chain.test.ts +2 -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__/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-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- 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.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- 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__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- 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-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- 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/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- 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 +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- 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/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- 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 +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- 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 +135 -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 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- 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-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- 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/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -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/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- 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/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -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/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- 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/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 +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- 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/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- 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__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- 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/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 +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- 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/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -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 +151 -56
- package/src/runtime/auth/route-policy.ts +7 -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-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- 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/chatgpt-subscription-auth-routes.ts +246 -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 +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -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 +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -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/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/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/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/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- 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/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- 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/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
|
@@ -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,
|
|
@@ -40,6 +40,20 @@ mock.module("../runtime/gateway-client.js", () => ({
|
|
|
40
40
|
deliverChannelReply: async () => {},
|
|
41
41
|
}));
|
|
42
42
|
|
|
43
|
+
mock.module("../messaging/providers/slack/adapter.js", () => ({
|
|
44
|
+
withSlackBotToken: async (
|
|
45
|
+
_account: string | undefined,
|
|
46
|
+
fn: (token: string) => Promise<unknown>,
|
|
47
|
+
) => fn("test-slack-token"),
|
|
48
|
+
resolveSlackBotUserId: async (
|
|
49
|
+
_account: string | undefined,
|
|
50
|
+
botId: string,
|
|
51
|
+
) => {
|
|
52
|
+
if (botId === "B_ASSISTANT") return "U_BOT";
|
|
53
|
+
return null;
|
|
54
|
+
},
|
|
55
|
+
}));
|
|
56
|
+
|
|
43
57
|
import type { Message } from "../messaging/provider-types.js";
|
|
44
58
|
|
|
45
59
|
// `backfillDm` is the only piece of the slack provider surface this test
|
|
@@ -59,6 +73,11 @@ mock.module("../messaging/providers/slack/backfill.js", () => ({
|
|
|
59
73
|
backfillThread: () => backfillThreadMock(),
|
|
60
74
|
}));
|
|
61
75
|
|
|
76
|
+
import {
|
|
77
|
+
loadRawConfig,
|
|
78
|
+
saveRawConfig,
|
|
79
|
+
setNestedValue,
|
|
80
|
+
} from "../config/loader.js";
|
|
62
81
|
import { upsertContactChannel } from "../contacts/contacts-write.js";
|
|
63
82
|
import { getDb } from "../memory/db-connection.js";
|
|
64
83
|
import { initializeDb } from "../memory/db-init.js";
|
|
@@ -88,6 +107,13 @@ function resetState(): void {
|
|
|
88
107
|
db.run("DELETE FROM contact_channels");
|
|
89
108
|
db.run("DELETE FROM contacts");
|
|
90
109
|
db.run("DELETE FROM external_conversation_bindings");
|
|
110
|
+
setConfiguredSlackBotUserId("U_BOT");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function setConfiguredSlackBotUserId(botUserId: string): void {
|
|
114
|
+
const raw = loadRawConfig();
|
|
115
|
+
setNestedValue(raw, "slack.botUserId", botUserId);
|
|
116
|
+
saveRawConfig(raw);
|
|
91
117
|
}
|
|
92
118
|
|
|
93
119
|
function seedActiveMember(): void {
|
|
@@ -494,6 +520,44 @@ describe("PR 23 — Slack DM cold-start backfill", () => {
|
|
|
494
520
|
expect(botRow?.provenanceRequesterIdentifier).toBe("B_BOT");
|
|
495
521
|
});
|
|
496
522
|
|
|
523
|
+
test("skips Slack assistant new-thread placeholder during DM backfill", async () => {
|
|
524
|
+
backfillDmMock.mockImplementation(async () => [
|
|
525
|
+
makeBackfilledMessage({
|
|
526
|
+
id: "1700000000.000001",
|
|
527
|
+
text: "New Assistant Thread",
|
|
528
|
+
sender: { id: "B_ASSISTANT", name: "Ada" },
|
|
529
|
+
metadata: { isBot: true, slackBotId: "B_ASSISTANT" },
|
|
530
|
+
}),
|
|
531
|
+
makeBackfilledMessage({
|
|
532
|
+
id: "1700000000.000002",
|
|
533
|
+
text: "real bot context",
|
|
534
|
+
sender: { id: "B_ASSISTANT", name: "Ada" },
|
|
535
|
+
metadata: { isBot: true, slackBotId: "B_ASSISTANT" },
|
|
536
|
+
}),
|
|
537
|
+
makeBackfilledMessage({
|
|
538
|
+
id: "1700000000.000003",
|
|
539
|
+
text: "New Assistant Thread",
|
|
540
|
+
sender: { id: "B_OTHER", name: "Build Bot" },
|
|
541
|
+
metadata: { isBot: true, slackBotId: "B_OTHER" },
|
|
542
|
+
}),
|
|
543
|
+
]);
|
|
544
|
+
|
|
545
|
+
await handleChannelInbound(
|
|
546
|
+
buildDmRequest("live new DM"),
|
|
547
|
+
noopProcessMessage,
|
|
548
|
+
TEST_BEARER_TOKEN,
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
const rows = readPersistedSlackRows();
|
|
552
|
+
expect(rows.map((row) => row.rawContent).sort()).toEqual([
|
|
553
|
+
"New Assistant Thread",
|
|
554
|
+
"real bot context",
|
|
555
|
+
]);
|
|
556
|
+
expect(
|
|
557
|
+
rows.some((row) => row.slackMeta?.actorExternalUserId === "B_OTHER"),
|
|
558
|
+
).toBe(true);
|
|
559
|
+
});
|
|
560
|
+
|
|
497
561
|
test("backfill skips channelTs values already stored", async () => {
|
|
498
562
|
// First DM: backfill returns three rows.
|
|
499
563
|
backfillDmMock.mockImplementation(async () => [
|
|
@@ -179,6 +179,39 @@ describe("PR 16 — Slack DM persistence parity", () => {
|
|
|
179
179
|
expect(slackMeta!.displayName).toBeUndefined();
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
+
test("DM inbound persists Slack actor timezone metadata", async () => {
|
|
183
|
+
const ctx = createSlackTurnContext();
|
|
184
|
+
await persistQueuedMessageBody(
|
|
185
|
+
ctx,
|
|
186
|
+
"hello across timezones",
|
|
187
|
+
[],
|
|
188
|
+
"req-dm-timezone",
|
|
189
|
+
{
|
|
190
|
+
slackInbound: {
|
|
191
|
+
channelId: "D0123DM",
|
|
192
|
+
channelTs: "1700000000.777777",
|
|
193
|
+
displayName: "Alice",
|
|
194
|
+
actorTimezone: "America/New_York",
|
|
195
|
+
actorTimezoneLabel: "ET",
|
|
196
|
+
actorTimezoneOffsetSeconds: -18000,
|
|
197
|
+
timestampTimezone: "America/New_York",
|
|
198
|
+
timestampTimezoneLabel: "ET",
|
|
199
|
+
speakerTimezoneLabel: "ET",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
undefined,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const slackMeta = lastPersistedSlackMeta();
|
|
206
|
+
expect(slackMeta).not.toBeNull();
|
|
207
|
+
expect(slackMeta!.actorTimezone).toBe("America/New_York");
|
|
208
|
+
expect(slackMeta!.actorTimezoneLabel).toBe("ET");
|
|
209
|
+
expect(slackMeta!.actorTimezoneOffsetSeconds).toBe(-18000);
|
|
210
|
+
expect(slackMeta!.timestampTimezone).toBe("America/New_York");
|
|
211
|
+
expect(slackMeta!.timestampTimezoneLabel).toBe("ET");
|
|
212
|
+
expect(slackMeta!.speakerTimezoneLabel).toBe("ET");
|
|
213
|
+
});
|
|
214
|
+
|
|
182
215
|
test("DM and channel-message envelopes differ only by threadTs", async () => {
|
|
183
216
|
// Capture the channel-thread case first.
|
|
184
217
|
const ctx = createSlackTurnContext();
|