@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
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { CommentRecord } from "../documents/document-comments-store.js";
|
|
4
|
+
|
|
5
|
+
let listCommentsMock = mock((..._args: unknown[]) => [] as CommentRecord[]);
|
|
6
|
+
|
|
7
|
+
mock.module("../documents/document-comments-store.js", () => ({
|
|
8
|
+
listComments: (...args: unknown[]) => listCommentsMock(...args),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const { DEFAULT_INJECTOR_ORDER, defaultInjectorsPlugin } =
|
|
12
|
+
await import("../plugins/defaults/injectors.js");
|
|
13
|
+
import type { Injector, TurnContext } from "../plugins/types.js";
|
|
14
|
+
|
|
15
|
+
function findInjector(name: string): Injector {
|
|
16
|
+
const injector = defaultInjectorsPlugin.injectors?.find(
|
|
17
|
+
(candidate) => candidate.name === name,
|
|
18
|
+
);
|
|
19
|
+
if (!injector) {
|
|
20
|
+
throw new Error(`injector '${name}' not registered`);
|
|
21
|
+
}
|
|
22
|
+
return injector;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function makeContext(overrides: Partial<TurnContext> = {}): TurnContext {
|
|
26
|
+
return {
|
|
27
|
+
requestId: "req-test",
|
|
28
|
+
conversationId: "conv-test",
|
|
29
|
+
turnIndex: 0,
|
|
30
|
+
trust: { sourceChannel: "vellum", trustClass: "guardian" },
|
|
31
|
+
...overrides,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function makeComment(overrides: Partial<CommentRecord> = {}): CommentRecord {
|
|
36
|
+
return {
|
|
37
|
+
id: "comment-abc",
|
|
38
|
+
surfaceId: "doc-1",
|
|
39
|
+
conversationId: "conv-test",
|
|
40
|
+
author: "user",
|
|
41
|
+
content: "Fix this paragraph",
|
|
42
|
+
anchorStart: null,
|
|
43
|
+
anchorEnd: null,
|
|
44
|
+
anchorText: null,
|
|
45
|
+
parentCommentId: null,
|
|
46
|
+
status: "open",
|
|
47
|
+
resolvedBy: null,
|
|
48
|
+
resolvedAt: null,
|
|
49
|
+
createdAt: 1000,
|
|
50
|
+
updatedAt: 1000,
|
|
51
|
+
...overrides,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const injector = findInjector("document-comments");
|
|
56
|
+
|
|
57
|
+
describe("document-comments injector", () => {
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
listCommentsMock = mock(() => [] as CommentRecord[]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("returns null when no active documents exist", async () => {
|
|
63
|
+
const block = await injector.produce(
|
|
64
|
+
makeContext({
|
|
65
|
+
injectionInputs: { mode: "full", activeDocuments: [] },
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
expect(block).toBeNull();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("returns null when activeDocuments is undefined", async () => {
|
|
72
|
+
const block = await injector.produce(
|
|
73
|
+
makeContext({
|
|
74
|
+
injectionInputs: { mode: "full" },
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
expect(block).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("returns null when mode is minimal", async () => {
|
|
81
|
+
const block = await injector.produce(
|
|
82
|
+
makeContext({
|
|
83
|
+
injectionInputs: {
|
|
84
|
+
mode: "minimal",
|
|
85
|
+
activeDocuments: [
|
|
86
|
+
{
|
|
87
|
+
surfaceId: "doc-1",
|
|
88
|
+
title: "My Doc",
|
|
89
|
+
wordCount: 100,
|
|
90
|
+
updatedAt: 1000,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
expect(block).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("returns null when no documents have open comments", async () => {
|
|
100
|
+
listCommentsMock = mock(() => []);
|
|
101
|
+
const block = await injector.produce(
|
|
102
|
+
makeContext({
|
|
103
|
+
injectionInputs: {
|
|
104
|
+
mode: "full",
|
|
105
|
+
activeDocuments: [
|
|
106
|
+
{
|
|
107
|
+
surfaceId: "doc-1",
|
|
108
|
+
title: "My Doc",
|
|
109
|
+
wordCount: 100,
|
|
110
|
+
updatedAt: 1000,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
expect(block).toBeNull();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("formats doc-level comments correctly", async () => {
|
|
120
|
+
listCommentsMock = mock(() => [
|
|
121
|
+
makeComment({
|
|
122
|
+
id: "comment-id1",
|
|
123
|
+
content: "This introduction needs more context",
|
|
124
|
+
anchorText: null,
|
|
125
|
+
}),
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
const block = await injector.produce(
|
|
129
|
+
makeContext({
|
|
130
|
+
injectionInputs: {
|
|
131
|
+
mode: "full",
|
|
132
|
+
activeDocuments: [
|
|
133
|
+
{
|
|
134
|
+
surfaceId: "doc-1",
|
|
135
|
+
title: "My Doc",
|
|
136
|
+
wordCount: 100,
|
|
137
|
+
updatedAt: 1000,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(block).not.toBeNull();
|
|
145
|
+
expect(block!.id).toBe("document-comments");
|
|
146
|
+
expect(block!.placement).toBe("prepend-user-tail");
|
|
147
|
+
expect(block!.text).toContain("(doc-level)");
|
|
148
|
+
expect(block!.text).toContain("Comment #comment-id1");
|
|
149
|
+
expect(block!.text).toContain('"This introduction needs more context"');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("formats inline comments with anchor text", async () => {
|
|
153
|
+
listCommentsMock = mock(() => [
|
|
154
|
+
makeComment({
|
|
155
|
+
id: "comment-id2",
|
|
156
|
+
content: "Cite the research paper",
|
|
157
|
+
anchorText: "the quick brown fox",
|
|
158
|
+
anchorStart: 10,
|
|
159
|
+
anchorEnd: 29,
|
|
160
|
+
}),
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
const block = await injector.produce(
|
|
164
|
+
makeContext({
|
|
165
|
+
injectionInputs: {
|
|
166
|
+
mode: "full",
|
|
167
|
+
activeDocuments: [
|
|
168
|
+
{
|
|
169
|
+
surfaceId: "doc-1",
|
|
170
|
+
title: "My Doc",
|
|
171
|
+
wordCount: 100,
|
|
172
|
+
updatedAt: 1000,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
expect(block).not.toBeNull();
|
|
180
|
+
expect(block!.text).toContain('inline, anchored to "the quick brown fox"');
|
|
181
|
+
expect(block!.text).toContain("Comment #comment-id2");
|
|
182
|
+
expect(block!.text).toContain('"Cite the research paper"');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("mixes doc-level and inline comments for the same document", async () => {
|
|
186
|
+
listCommentsMock = mock(() => [
|
|
187
|
+
makeComment({
|
|
188
|
+
id: "comment-id1",
|
|
189
|
+
content: "This introduction needs more context",
|
|
190
|
+
anchorText: null,
|
|
191
|
+
}),
|
|
192
|
+
makeComment({
|
|
193
|
+
id: "comment-id2",
|
|
194
|
+
content: "Cite the research paper",
|
|
195
|
+
anchorText: "the quick brown fox",
|
|
196
|
+
anchorStart: 10,
|
|
197
|
+
anchorEnd: 29,
|
|
198
|
+
}),
|
|
199
|
+
]);
|
|
200
|
+
|
|
201
|
+
const block = await injector.produce(
|
|
202
|
+
makeContext({
|
|
203
|
+
injectionInputs: {
|
|
204
|
+
mode: "full",
|
|
205
|
+
activeDocuments: [
|
|
206
|
+
{
|
|
207
|
+
surfaceId: "doc-xxx",
|
|
208
|
+
title: "Title",
|
|
209
|
+
wordCount: 200,
|
|
210
|
+
updatedAt: 1000,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
expect(block).not.toBeNull();
|
|
218
|
+
expect(block!.text).toContain('Document: "Title" (surface_id: "doc-xxx")');
|
|
219
|
+
expect(block!.text).toContain(
|
|
220
|
+
'- Comment #comment-id1 (doc-level): "This introduction needs more context"',
|
|
221
|
+
);
|
|
222
|
+
expect(block!.text).toContain(
|
|
223
|
+
'- Comment #comment-id2 (inline, anchored to "the quick brown fox"): "Cite the research paper"',
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("respects the 10-comment cap per document", async () => {
|
|
228
|
+
const comments = Array.from({ length: 15 }, (_, i) =>
|
|
229
|
+
makeComment({
|
|
230
|
+
id: `comment-${i}`,
|
|
231
|
+
content: `Comment number ${i}`,
|
|
232
|
+
createdAt: 1000 + i,
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
235
|
+
listCommentsMock = mock(() => comments);
|
|
236
|
+
|
|
237
|
+
const block = await injector.produce(
|
|
238
|
+
makeContext({
|
|
239
|
+
injectionInputs: {
|
|
240
|
+
mode: "full",
|
|
241
|
+
activeDocuments: [
|
|
242
|
+
{
|
|
243
|
+
surfaceId: "doc-1",
|
|
244
|
+
title: "Big Doc",
|
|
245
|
+
wordCount: 500,
|
|
246
|
+
updatedAt: 1000,
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
}),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
expect(block).not.toBeNull();
|
|
254
|
+
// The store returns ASC order; .slice(-10) takes the 10 most recent
|
|
255
|
+
for (let i = 5; i < 15; i++) {
|
|
256
|
+
expect(block!.text).toContain(`Comment #comment-${i}`);
|
|
257
|
+
}
|
|
258
|
+
// Earlier comments should be excluded (use exact line match to avoid
|
|
259
|
+
// substring collisions like "comment-1" matching "comment-10")
|
|
260
|
+
for (let i = 0; i < 5; i++) {
|
|
261
|
+
expect(block!.text).not.toContain(`Comment #comment-${i} (`);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("handles multiple documents with comments", async () => {
|
|
266
|
+
let callCount = 0;
|
|
267
|
+
listCommentsMock = mock(() => {
|
|
268
|
+
callCount++;
|
|
269
|
+
if (callCount === 1) {
|
|
270
|
+
return [makeComment({ id: "c1", content: "Fix typo" })];
|
|
271
|
+
}
|
|
272
|
+
return [
|
|
273
|
+
makeComment({
|
|
274
|
+
id: "c2",
|
|
275
|
+
content: "Add citation",
|
|
276
|
+
anchorText: "some text",
|
|
277
|
+
anchorStart: 0,
|
|
278
|
+
anchorEnd: 9,
|
|
279
|
+
}),
|
|
280
|
+
];
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const block = await injector.produce(
|
|
284
|
+
makeContext({
|
|
285
|
+
injectionInputs: {
|
|
286
|
+
mode: "full",
|
|
287
|
+
activeDocuments: [
|
|
288
|
+
{
|
|
289
|
+
surfaceId: "doc-1",
|
|
290
|
+
title: "Doc A",
|
|
291
|
+
wordCount: 100,
|
|
292
|
+
updatedAt: 1000,
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
surfaceId: "doc-2",
|
|
296
|
+
title: "Doc B",
|
|
297
|
+
wordCount: 200,
|
|
298
|
+
updatedAt: 2000,
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
}),
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
expect(block).not.toBeNull();
|
|
306
|
+
expect(block!.text).toContain('Document: "Doc A" (surface_id: "doc-1")');
|
|
307
|
+
expect(block!.text).toContain('Document: "Doc B" (surface_id: "doc-2")');
|
|
308
|
+
expect(block!.text).toContain("Comment #c1");
|
|
309
|
+
expect(block!.text).toContain("Comment #c2");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("skips documents with zero open comments", async () => {
|
|
313
|
+
let callCount = 0;
|
|
314
|
+
listCommentsMock = mock(() => {
|
|
315
|
+
callCount++;
|
|
316
|
+
if (callCount === 1) return [];
|
|
317
|
+
return [makeComment({ id: "c1", content: "Needs work" })];
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const block = await injector.produce(
|
|
321
|
+
makeContext({
|
|
322
|
+
injectionInputs: {
|
|
323
|
+
mode: "full",
|
|
324
|
+
activeDocuments: [
|
|
325
|
+
{
|
|
326
|
+
surfaceId: "doc-1",
|
|
327
|
+
title: "Empty Doc",
|
|
328
|
+
wordCount: 100,
|
|
329
|
+
updatedAt: 1000,
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
surfaceId: "doc-2",
|
|
333
|
+
title: "Commented Doc",
|
|
334
|
+
wordCount: 200,
|
|
335
|
+
updatedAt: 2000,
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
},
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
expect(block).not.toBeNull();
|
|
343
|
+
expect(block!.text).not.toContain("Empty Doc");
|
|
344
|
+
expect(block!.text).toContain("Commented Doc");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("has the correct order value", () => {
|
|
348
|
+
expect(injector.order).toBe(DEFAULT_INJECTOR_ORDER.documentComments);
|
|
349
|
+
expect(injector.order).toBe(46);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test("wraps output in <document_comments> tags with instructions", async () => {
|
|
353
|
+
listCommentsMock = mock(() => [
|
|
354
|
+
makeComment({ id: "c1", content: "Fix this" }),
|
|
355
|
+
]);
|
|
356
|
+
|
|
357
|
+
const block = await injector.produce(
|
|
358
|
+
makeContext({
|
|
359
|
+
injectionInputs: {
|
|
360
|
+
mode: "full",
|
|
361
|
+
activeDocuments: [
|
|
362
|
+
{
|
|
363
|
+
surfaceId: "doc-1",
|
|
364
|
+
title: "My Doc",
|
|
365
|
+
wordCount: 100,
|
|
366
|
+
updatedAt: 1000,
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
}),
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
expect(block).not.toBeNull();
|
|
374
|
+
expect(block!.text).toMatch(/^<document_comments>/);
|
|
375
|
+
expect(block!.text).toMatch(/<\/document_comments>$/);
|
|
376
|
+
expect(block!.text).toContain("comment_resolve");
|
|
377
|
+
});
|
|
378
|
+
});
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* When `getConfig().memory.v2.enabled` is true:
|
|
5
5
|
* - `pkb-context` silences itself (concept pages own retrieval).
|
|
6
|
-
* - `pkb-reminder`
|
|
7
|
-
*
|
|
6
|
+
* - `pkb-reminder` silences itself (the v2 static `<memory>` block
|
|
7
|
+
* supplants the generic recall/remember nudge).
|
|
8
8
|
* - `now-md` fires unchanged (workspace state, independent of PKB).
|
|
9
9
|
*
|
|
10
10
|
* Mocks `getConfig` at the module level so each test can flip the effective
|
|
@@ -85,7 +85,7 @@ describe("PKB injector v2 cutover behavior", () => {
|
|
|
85
85
|
expect(texts.some((t) => t.includes("<NOW.md"))).toBe(true);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
test("v2 active → pkb-context
|
|
88
|
+
test("v2 active → pkb-context and pkb-reminder silenced; now-md still fires", async () => {
|
|
89
89
|
v2Active = true;
|
|
90
90
|
const result = await applyRuntimeInjections(RUN_MESSAGES, {
|
|
91
91
|
turnContext: makeTurnContext(),
|
|
@@ -99,29 +99,8 @@ describe("PKB injector v2 cutover behavior", () => {
|
|
|
99
99
|
|
|
100
100
|
const texts = tailTexts(result.messages);
|
|
101
101
|
expect(texts.some((t) => t.includes("<knowledge_base>"))).toBe(false);
|
|
102
|
-
expect(texts.some((t) => t.includes("<system_reminder>"))).toBe(
|
|
102
|
+
expect(texts.some((t) => t.includes("<system_reminder>"))).toBe(false);
|
|
103
103
|
expect(texts.some((t) => t.includes("<NOW.md"))).toBe(true);
|
|
104
104
|
expect(texts).toContain("What next?");
|
|
105
105
|
});
|
|
106
|
-
|
|
107
|
-
test("v2 active → pkb-reminder body fires without the hybrid-search hints", async () => {
|
|
108
|
-
v2Active = true;
|
|
109
|
-
const result = await applyRuntimeInjections(RUN_MESSAGES, {
|
|
110
|
-
turnContext: makeTurnContext(),
|
|
111
|
-
pkbActive: true,
|
|
112
|
-
pkbScopeId: "scope-default",
|
|
113
|
-
pkbRoot: "/tmp/pkb",
|
|
114
|
-
pkbConversation: { messages: [] },
|
|
115
|
-
// Provide a query vector so the v1 path WOULD have called searchPkbFiles
|
|
116
|
-
// and rendered hints. Under v2, the call is skipped and the reminder
|
|
117
|
-
// is rendered with empty hints — i.e. no "files look especially
|
|
118
|
-
// relevant" line.
|
|
119
|
-
pkbQueryVector: [0.1, 0.2, 0.3],
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const texts = tailTexts(result.messages);
|
|
123
|
-
const reminder = texts.find((t) => t.includes("<system_reminder>"));
|
|
124
|
-
expect(reminder).toBeDefined();
|
|
125
|
-
expect(reminder).not.toContain("files look especially relevant");
|
|
126
|
-
});
|
|
127
106
|
});
|
|
@@ -78,7 +78,7 @@ describe("handleListMessages attachments", () => {
|
|
|
78
78
|
const stored = uploadAttachment("photo.png", "image/png", IMAGE_BASE64);
|
|
79
79
|
linkAttachmentToMessage(msg.id, stored.id, 0);
|
|
80
80
|
|
|
81
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
81
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
82
82
|
const body = response as { messages: MessagePayload[] };
|
|
83
83
|
|
|
84
84
|
expect(body.messages).toHaveLength(1);
|
|
@@ -103,7 +103,7 @@ describe("handleListMessages attachments", () => {
|
|
|
103
103
|
);
|
|
104
104
|
linkAttachmentToMessage(msg.id, stored.id, 0);
|
|
105
105
|
|
|
106
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
106
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
107
107
|
const body = response as { messages: MessagePayload[] };
|
|
108
108
|
|
|
109
109
|
expect(body.messages).toHaveLength(1);
|
|
@@ -125,7 +125,7 @@ describe("handleListMessages attachments", () => {
|
|
|
125
125
|
const stored = uploadAttachment("result.png", "image/png", IMAGE_BASE64);
|
|
126
126
|
linkAttachmentToMessage(msg.id, stored.id, 0);
|
|
127
127
|
|
|
128
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
128
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
129
129
|
const body = response as { messages: MessagePayload[] };
|
|
130
130
|
|
|
131
131
|
expect(body.messages).toHaveLength(1);
|
|
@@ -153,7 +153,7 @@ describe("handleListMessages attachments", () => {
|
|
|
153
153
|
linkAttachmentToMessage(msg.id, imgStored.id, 0);
|
|
154
154
|
linkAttachmentToMessage(msg.id, docStored.id, 1);
|
|
155
155
|
|
|
156
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
156
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
157
157
|
const body = response as { messages: MessagePayload[] };
|
|
158
158
|
|
|
159
159
|
const attachments = body.messages[0].attachments!;
|
|
@@ -177,7 +177,7 @@ describe("handleListMessages no_response filtering", () => {
|
|
|
177
177
|
JSON.stringify([{ type: "text", text: "<no_response/>" }]),
|
|
178
178
|
);
|
|
179
179
|
|
|
180
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
180
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
181
181
|
const body = response as {
|
|
182
182
|
messages: { content: string; textSegments: string[] }[];
|
|
183
183
|
};
|
|
@@ -199,7 +199,7 @@ describe("handleListMessages no_response filtering", () => {
|
|
|
199
199
|
]),
|
|
200
200
|
);
|
|
201
201
|
|
|
202
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
202
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
203
203
|
const body = response as {
|
|
204
204
|
messages: { content: string; textSegments: string[] }[];
|
|
205
205
|
};
|
|
@@ -228,7 +228,7 @@ describe("handleListMessages no_response filtering", () => {
|
|
|
228
228
|
]),
|
|
229
229
|
);
|
|
230
230
|
|
|
231
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
231
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
232
232
|
const body = response as {
|
|
233
233
|
messages: {
|
|
234
234
|
content: string;
|
|
@@ -252,7 +252,7 @@ describe("handleListMessages no_response filtering", () => {
|
|
|
252
252
|
JSON.stringify([{ type: "text", text: "What does <no_response/> do?" }]),
|
|
253
253
|
);
|
|
254
254
|
|
|
255
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
255
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
256
256
|
const body = response as {
|
|
257
257
|
messages: { content: string }[];
|
|
258
258
|
};
|
|
@@ -308,7 +308,7 @@ describe("handleListMessages pagination", () => {
|
|
|
308
308
|
const conv = createConversation();
|
|
309
309
|
await insertMessages(conv.id, 5);
|
|
310
310
|
|
|
311
|
-
const response = handleListMessages(createTestArgs(conv.id)
|
|
311
|
+
const response = handleListMessages(createTestArgs(conv.id));
|
|
312
312
|
const body = response as unknown as PaginatedResponse;
|
|
313
313
|
|
|
314
314
|
expect(body.messages).toHaveLength(5);
|
|
@@ -322,7 +322,7 @@ describe("handleListMessages pagination", () => {
|
|
|
322
322
|
await insertMessages(conv.id, 5);
|
|
323
323
|
|
|
324
324
|
const args = createPaginatedArgs(conv.id, { limit: "3" });
|
|
325
|
-
const response = handleListMessages(args
|
|
325
|
+
const response = handleListMessages(args);
|
|
326
326
|
const body = response as unknown as PaginatedResponse;
|
|
327
327
|
|
|
328
328
|
// Option A: without beforeTimestamp, all messages are returned regardless of limit
|
|
@@ -339,7 +339,7 @@ describe("handleListMessages pagination", () => {
|
|
|
339
339
|
beforeTimestamp: String(msgs[7].createdAt),
|
|
340
340
|
limit: "3",
|
|
341
341
|
});
|
|
342
|
-
const response = handleListMessages(args
|
|
342
|
+
const response = handleListMessages(args);
|
|
343
343
|
const body = response as unknown as PaginatedResponse;
|
|
344
344
|
|
|
345
345
|
expect(body.messages).toHaveLength(3);
|
|
@@ -360,7 +360,7 @@ describe("handleListMessages pagination", () => {
|
|
|
360
360
|
beforeTimestamp: String(msgs[1].createdAt),
|
|
361
361
|
limit: "10",
|
|
362
362
|
});
|
|
363
|
-
const response = handleListMessages(args
|
|
363
|
+
const response = handleListMessages(args);
|
|
364
364
|
const body = response as unknown as PaginatedResponse;
|
|
365
365
|
|
|
366
366
|
const ids = body.messages.map((m) => m.id);
|
|
@@ -378,7 +378,7 @@ describe("handleListMessages pagination", () => {
|
|
|
378
378
|
beforeTimestamp: String(msgs[4].createdAt + 1),
|
|
379
379
|
limit: "10",
|
|
380
380
|
});
|
|
381
|
-
const response = handleListMessages(args
|
|
381
|
+
const response = handleListMessages(args);
|
|
382
382
|
const body = response as unknown as PaginatedResponse;
|
|
383
383
|
|
|
384
384
|
expect(body.messages).toHaveLength(5);
|
|
@@ -394,7 +394,7 @@ describe("handleListMessages pagination", () => {
|
|
|
394
394
|
beforeTimestamp: String(msgs[4].createdAt + 1),
|
|
395
395
|
limit: "3",
|
|
396
396
|
});
|
|
397
|
-
const response = handleListMessages(args
|
|
397
|
+
const response = handleListMessages(args);
|
|
398
398
|
const body = response as unknown as PaginatedResponse;
|
|
399
399
|
|
|
400
400
|
expect(body.messages).toHaveLength(3);
|
|
@@ -405,7 +405,7 @@ describe("handleListMessages pagination", () => {
|
|
|
405
405
|
|
|
406
406
|
test("empty / nonexistent conversation → empty messages, no pagination metadata", async () => {
|
|
407
407
|
const args = createPaginatedArgs("nonexistent-conv-id");
|
|
408
|
-
const response = handleListMessages(args
|
|
408
|
+
const response = handleListMessages(args);
|
|
409
409
|
const body = response as unknown as PaginatedResponse;
|
|
410
410
|
|
|
411
411
|
expect(body.messages).toEqual([]);
|
|
@@ -418,13 +418,17 @@ describe("handleListMessages pagination", () => {
|
|
|
418
418
|
const conv = createConversation();
|
|
419
419
|
const args = createPaginatedArgs(conv.id, { limit: "abc" });
|
|
420
420
|
|
|
421
|
-
expect(() => handleListMessages(args
|
|
421
|
+
expect(() => handleListMessages(args)).toThrow(
|
|
422
|
+
"limit must be a valid number",
|
|
423
|
+
);
|
|
422
424
|
});
|
|
423
425
|
|
|
424
426
|
test("invalid beforeTimestamp (NaN) → 400", async () => {
|
|
425
427
|
const conv = createConversation();
|
|
426
428
|
const args = createPaginatedArgs(conv.id, { beforeTimestamp: "abc" });
|
|
427
429
|
|
|
428
|
-
expect(() => handleListMessages(args
|
|
430
|
+
expect(() => handleListMessages(args)).toThrow(
|
|
431
|
+
"beforeTimestamp must be a valid number",
|
|
432
|
+
);
|
|
429
433
|
});
|
|
430
434
|
});
|