@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
|
@@ -195,6 +195,9 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
195
195
|
updateMessageContent: () => {},
|
|
196
196
|
updateMessageMetadata: () => {},
|
|
197
197
|
clearStrippedInjectionMetadataForConversation: () => {},
|
|
198
|
+
setLastNotifiedInferenceProfile: () => {},
|
|
199
|
+
getLastUserTimestampBefore: () => 0,
|
|
200
|
+
getConversationOverrideProfileFromRow: () => undefined,
|
|
198
201
|
}));
|
|
199
202
|
|
|
200
203
|
afterAll(() => {
|
|
@@ -301,6 +304,7 @@ mock.module("../daemon/history-repair.js", () => ({
|
|
|
301
304
|
}));
|
|
302
305
|
|
|
303
306
|
const recordUsageMock = mock((..._args: unknown[]) => {});
|
|
307
|
+
const setAgentLoopExitReasonOnLatestLogMock = mock(() => {});
|
|
304
308
|
mock.module("../daemon/conversation-usage.js", () => ({
|
|
305
309
|
recordUsage: recordUsageMock,
|
|
306
310
|
}));
|
|
@@ -349,12 +353,23 @@ mock.module("../workspace/git-service.js", () => ({
|
|
|
349
353
|
}));
|
|
350
354
|
|
|
351
355
|
mock.module("../daemon/conversation-error.js", () => ({
|
|
352
|
-
classifyConversationError: (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
356
|
+
classifyConversationError: (err: unknown, _ctx: unknown) => {
|
|
357
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
358
|
+
if (/context.?length.?exceeded/i.test(message)) {
|
|
359
|
+
return {
|
|
360
|
+
code: "CONTEXT_TOO_LARGE",
|
|
361
|
+
userMessage: "Context too large.",
|
|
362
|
+
retryable: false,
|
|
363
|
+
errorCategory: "context_too_large",
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
code: "CONVERSATION_PROCESSING_FAILED",
|
|
368
|
+
userMessage: "Something went wrong processing your message.",
|
|
369
|
+
retryable: false,
|
|
370
|
+
errorCategory: "processing_failed",
|
|
371
|
+
};
|
|
372
|
+
},
|
|
358
373
|
isUserCancellation: (err: unknown, ctx: { aborted?: boolean }) => {
|
|
359
374
|
if (!ctx.aborted) return false;
|
|
360
375
|
if (err instanceof DOMException && err.name === "AbortError") return true;
|
|
@@ -394,6 +409,7 @@ mock.module("../agent/message-types.js", () => ({
|
|
|
394
409
|
mock.module("../memory/llm-request-log-store.js", () => ({
|
|
395
410
|
recordRequestLog: () => {},
|
|
396
411
|
backfillMessageIdOnLogs: () => {},
|
|
412
|
+
setAgentLoopExitReasonOnLatestLog: setAgentLoopExitReasonOnLatestLogMock,
|
|
397
413
|
}));
|
|
398
414
|
|
|
399
415
|
mock.module("../memory/archive-store.js", () => ({
|
|
@@ -616,6 +632,7 @@ beforeEach(() => {
|
|
|
616
632
|
mockOverflowAction = "fail_gracefully";
|
|
617
633
|
mockApplyRuntimeInjections = (msgs) => msgs;
|
|
618
634
|
recordUsageMock.mockClear();
|
|
635
|
+
setAgentLoopExitReasonOnLatestLogMock.mockClear();
|
|
619
636
|
// Reset the plugin registry and re-register every default so the
|
|
620
637
|
// orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
|
|
621
638
|
// the default middleware, which in turn hits the mocked collaborators
|
|
@@ -1907,6 +1924,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1907
1924
|
// After exhausting mid-loop attempts, the convergence loop should
|
|
1908
1925
|
// have been triggered (contextTooLargeDetected set to true)
|
|
1909
1926
|
expect(convergenceReducerCalled).toBe(true);
|
|
1927
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
1928
|
+
"test-conv",
|
|
1929
|
+
"context_too_large",
|
|
1930
|
+
);
|
|
1910
1931
|
});
|
|
1911
1932
|
|
|
1912
1933
|
// ── Test 9 ────────────────────────────────────────────────────────
|
|
@@ -2068,6 +2089,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
2068
2089
|
|
|
2069
2090
|
// Agent loop: 1 initial + 3 mid-loop re-entries + 2 convergence re-runs = 6 calls
|
|
2070
2091
|
expect(agentLoopCallCount).toBe(6);
|
|
2092
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
2093
|
+
"test-conv",
|
|
2094
|
+
"context_too_large",
|
|
2095
|
+
);
|
|
2071
2096
|
});
|
|
2072
2097
|
|
|
2073
2098
|
// ── Test 8 ────────────────────────────────────────────────────────
|
|
@@ -283,6 +283,7 @@ let mockSlackChronologicalContext: {
|
|
|
283
283
|
renderedMessages: Array<{
|
|
284
284
|
message: Message;
|
|
285
285
|
sourceChannelTs: string | null;
|
|
286
|
+
tagLineProvenance: "none" | "slack-reaction" | "slack-timezone-message";
|
|
286
287
|
}>;
|
|
287
288
|
messages: Message[];
|
|
288
289
|
compactableStartIndex: number;
|
|
@@ -387,6 +388,7 @@ mock.module("../daemon/history-repair.js", () => ({
|
|
|
387
388
|
const recordUsageMock = mock(() => {});
|
|
388
389
|
const recordRequestLogMock = mock(() => {});
|
|
389
390
|
const backfillMessageIdOnLogsMock = mock(() => {});
|
|
391
|
+
const setAgentLoopExitReasonOnLatestLogMock = mock(() => {});
|
|
390
392
|
mock.module("../daemon/conversation-usage.js", () => ({
|
|
391
393
|
recordUsage: recordUsageMock,
|
|
392
394
|
}));
|
|
@@ -484,6 +486,7 @@ mock.module("../memory/archive-store.js", () => ({
|
|
|
484
486
|
mock.module("../memory/llm-request-log-store.js", () => ({
|
|
485
487
|
recordRequestLog: recordRequestLogMock,
|
|
486
488
|
backfillMessageIdOnLogs: backfillMessageIdOnLogsMock,
|
|
489
|
+
setAgentLoopExitReasonOnLatestLog: setAgentLoopExitReasonOnLatestLogMock,
|
|
487
490
|
}));
|
|
488
491
|
|
|
489
492
|
let mockHasProactiveArtifactCompleted = true;
|
|
@@ -660,6 +663,7 @@ beforeEach(() => {
|
|
|
660
663
|
recordUsageMock.mockClear();
|
|
661
664
|
recordRequestLogMock.mockClear();
|
|
662
665
|
backfillMessageIdOnLogsMock.mockClear();
|
|
666
|
+
setAgentLoopExitReasonOnLatestLogMock.mockClear();
|
|
663
667
|
syncMessageToDiskMock.mockClear();
|
|
664
668
|
rebuildConversationDiskViewFromDbStateMock.mockClear();
|
|
665
669
|
updateMessageMetadataMock.mockClear();
|
|
@@ -2300,6 +2304,10 @@ describe("session-agent-loop", () => {
|
|
|
2300
2304
|
|
|
2301
2305
|
const handoff = events.find((e) => e.type === "generation_handoff");
|
|
2302
2306
|
expect(handoff).toBeDefined();
|
|
2307
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
2308
|
+
"test-conv",
|
|
2309
|
+
"checkpoint_handoff",
|
|
2310
|
+
);
|
|
2303
2311
|
});
|
|
2304
2312
|
|
|
2305
2313
|
test("continues when canHandoffAtCheckpoint returns false", async () => {
|
|
@@ -2359,6 +2367,10 @@ describe("session-agent-loop", () => {
|
|
|
2359
2367
|
|
|
2360
2368
|
const handoff = events.find((e) => e.type === "generation_handoff");
|
|
2361
2369
|
expect(handoff).toBeUndefined();
|
|
2370
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).not.toHaveBeenCalledWith(
|
|
2371
|
+
"test-conv",
|
|
2372
|
+
"checkpoint_handoff",
|
|
2373
|
+
);
|
|
2362
2374
|
const complete = events.find((e) => e.type === "message_complete");
|
|
2363
2375
|
expect(complete).toBeDefined();
|
|
2364
2376
|
});
|
|
@@ -2903,11 +2915,8 @@ describe("session-agent-loop", () => {
|
|
|
2903
2915
|
// (from the mocked `addMessage` -> `{ id: "mock-msg-id" }`) into the
|
|
2904
2916
|
// backfill primitive, scoped to this conversation.
|
|
2905
2917
|
expect(backfillMessageIdOnLogsMock).toHaveBeenCalledTimes(1);
|
|
2906
|
-
const backfillCall =
|
|
2907
|
-
|
|
2908
|
-
string,
|
|
2909
|
-
string,
|
|
2910
|
-
];
|
|
2918
|
+
const backfillCall = backfillMessageIdOnLogsMock.mock
|
|
2919
|
+
.calls[0] as unknown as [string, string];
|
|
2911
2920
|
expect(backfillCall[0]).toBe("test-conv");
|
|
2912
2921
|
expect(backfillCall[1]).toBe("mock-msg-id");
|
|
2913
2922
|
});
|
|
@@ -3023,6 +3032,7 @@ describe("session-agent-loop", () => {
|
|
|
3023
3032
|
"1700000020.000000",
|
|
3024
3033
|
"1700000030.000000",
|
|
3025
3034
|
][index]!,
|
|
3035
|
+
tagLineProvenance: "none",
|
|
3026
3036
|
})),
|
|
3027
3037
|
compactableStartIndex: 0,
|
|
3028
3038
|
};
|
|
@@ -3122,6 +3132,7 @@ describe("session-agent-loop", () => {
|
|
|
3122
3132
|
"1700000020.000000",
|
|
3123
3133
|
"1700000030.000000",
|
|
3124
3134
|
][index]!,
|
|
3135
|
+
tagLineProvenance: "none",
|
|
3125
3136
|
})),
|
|
3126
3137
|
compactableStartIndex: 0,
|
|
3127
3138
|
};
|
|
@@ -3238,6 +3249,7 @@ describe("session-agent-loop", () => {
|
|
|
3238
3249
|
"1700000030.000000",
|
|
3239
3250
|
"1700000040.000000",
|
|
3240
3251
|
][index]!,
|
|
3252
|
+
tagLineProvenance: "none",
|
|
3241
3253
|
})),
|
|
3242
3254
|
compactableStartIndex: 0,
|
|
3243
3255
|
};
|
|
@@ -3343,9 +3355,10 @@ describe("session-agent-loop", () => {
|
|
|
3343
3355
|
{
|
|
3344
3356
|
message: firstSummaryMessage,
|
|
3345
3357
|
sourceChannelTs: null,
|
|
3358
|
+
tagLineProvenance: "none",
|
|
3346
3359
|
},
|
|
3347
|
-
mockSlackChronologicalContext
|
|
3348
|
-
mockSlackChronologicalContext
|
|
3360
|
+
mockSlackChronologicalContext!.renderedMessages[2],
|
|
3361
|
+
mockSlackChronologicalContext!.renderedMessages[3],
|
|
3349
3362
|
],
|
|
3350
3363
|
messages: firstCompactedMessages,
|
|
3351
3364
|
compactableStartIndex: 1,
|
|
@@ -3384,6 +3397,7 @@ describe("session-agent-loop", () => {
|
|
|
3384
3397
|
"1700000020.000000",
|
|
3385
3398
|
"1700000030.000000",
|
|
3386
3399
|
][index]!,
|
|
3400
|
+
tagLineProvenance: "none",
|
|
3387
3401
|
})),
|
|
3388
3402
|
compactableStartIndex: 0,
|
|
3389
3403
|
};
|
|
@@ -3541,6 +3555,7 @@ describe("session-agent-loop", () => {
|
|
|
3541
3555
|
],
|
|
3542
3556
|
},
|
|
3543
3557
|
sourceChannelTs: null,
|
|
3558
|
+
tagLineProvenance: "none",
|
|
3544
3559
|
},
|
|
3545
3560
|
{
|
|
3546
3561
|
message: {
|
|
@@ -3548,6 +3563,7 @@ describe("session-agent-loop", () => {
|
|
|
3548
3563
|
content: [{ type: "text", text: "after watermark reply" }],
|
|
3549
3564
|
},
|
|
3550
3565
|
sourceChannelTs: "1700000020.000000",
|
|
3566
|
+
tagLineProvenance: "none",
|
|
3551
3567
|
},
|
|
3552
3568
|
],
|
|
3553
3569
|
compactableStartIndex: 1,
|
|
@@ -3642,6 +3658,7 @@ describe("session-agent-loop", () => {
|
|
|
3642
3658
|
],
|
|
3643
3659
|
},
|
|
3644
3660
|
sourceChannelTs: null,
|
|
3661
|
+
tagLineProvenance: "none",
|
|
3645
3662
|
},
|
|
3646
3663
|
{
|
|
3647
3664
|
message: {
|
|
@@ -3654,6 +3671,7 @@ describe("session-agent-loop", () => {
|
|
|
3654
3671
|
],
|
|
3655
3672
|
},
|
|
3656
3673
|
sourceChannelTs: "1700000121.000000",
|
|
3674
|
+
tagLineProvenance: "none",
|
|
3657
3675
|
},
|
|
3658
3676
|
],
|
|
3659
3677
|
compactableStartIndex: 1,
|
|
@@ -22,7 +22,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
22
22
|
|
|
23
23
|
mock.module("../providers/registry.js", () => ({
|
|
24
24
|
getProvider: () => ({ name: "mock-provider" }),
|
|
25
|
-
initializeProviders: () => {},
|
|
25
|
+
initializeProviders: async () => {},
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
28
|
mock.module("../config/loader.js", () => ({
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the `/clean` slash command primitives.
|
|
3
|
+
*
|
|
4
|
+
* `/clean` is wired up so that:
|
|
5
|
+
* 1. Runtime injection prefixes are stripped from user-message text blocks
|
|
6
|
+
* (same allowlist `/compact` uses).
|
|
7
|
+
* 2. Assistant turns, tool_use blocks, and tool_result blocks are preserved
|
|
8
|
+
* verbatim — `/clean` never alters history shape.
|
|
9
|
+
* 3. The user-facing result message reports tokens reclaimed and the
|
|
10
|
+
* number of messages preserved.
|
|
11
|
+
*
|
|
12
|
+
* The slash-routing layer is covered by `conversation-slash-commands.test.ts`;
|
|
13
|
+
* the graph-memory eviction hook is covered by the v2 routing tests. This
|
|
14
|
+
* file focuses on the formatter and the strip behavior that defines the
|
|
15
|
+
* "no history loss" contract.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, expect, test } from "bun:test";
|
|
18
|
+
|
|
19
|
+
import { formatCleanResult } from "../daemon/conversation-process.js";
|
|
20
|
+
import { stripInjectionsForCompaction } from "../daemon/conversation-runtime-assembly.js";
|
|
21
|
+
import type { Message } from "../providers/types.js";
|
|
22
|
+
|
|
23
|
+
describe("formatCleanResult", () => {
|
|
24
|
+
test("formats token reclamation and preserved-message count", () => {
|
|
25
|
+
const out = formatCleanResult({
|
|
26
|
+
previousEstimatedInputTokens: 100_000,
|
|
27
|
+
estimatedInputTokens: 95_000,
|
|
28
|
+
maxInputTokens: 200_000,
|
|
29
|
+
preservedMessages: 250,
|
|
30
|
+
});
|
|
31
|
+
expect(out).toContain("Context Cleaned");
|
|
32
|
+
expect(out).toContain("100,000 → 95,000 (5,000 reclaimed)");
|
|
33
|
+
expect(out).toContain("95,000 / 200,000 tokens");
|
|
34
|
+
expect(out).toContain("250 preserved");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("renders zero reclaimed when nothing was stripped", () => {
|
|
38
|
+
const out = formatCleanResult({
|
|
39
|
+
previousEstimatedInputTokens: 12_345,
|
|
40
|
+
estimatedInputTokens: 12_345,
|
|
41
|
+
maxInputTokens: 200_000,
|
|
42
|
+
preservedMessages: 10,
|
|
43
|
+
});
|
|
44
|
+
expect(out).toContain("12,345 → 12,345 (0 reclaimed)");
|
|
45
|
+
expect(out).toContain("10 preserved");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("stripInjectionsForCompaction preserves history shape", () => {
|
|
50
|
+
test("strips known injection prefixes from user text blocks", () => {
|
|
51
|
+
const messages: Message[] = [
|
|
52
|
+
{
|
|
53
|
+
role: "user",
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: "text",
|
|
57
|
+
text: "<NOW.md Always keep this up to date>\nfoo\n</NOW.md>",
|
|
58
|
+
},
|
|
59
|
+
{ type: "text", text: "Hello, please help with X." },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
const out = stripInjectionsForCompaction(messages);
|
|
64
|
+
expect(out).toHaveLength(1);
|
|
65
|
+
expect(out[0].content).toHaveLength(1);
|
|
66
|
+
expect(out[0].content[0]).toEqual({
|
|
67
|
+
type: "text",
|
|
68
|
+
text: "Hello, please help with X.",
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("preserves assistant turns and tool_use/tool_result blocks verbatim", () => {
|
|
73
|
+
const messages: Message[] = [
|
|
74
|
+
{
|
|
75
|
+
role: "user",
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: "<knowledge_base>\nstale kb\n</knowledge_base>",
|
|
80
|
+
},
|
|
81
|
+
{ type: "text", text: "Run the calculator." },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
role: "assistant",
|
|
86
|
+
content: [
|
|
87
|
+
{ type: "text", text: "Using the calculator." },
|
|
88
|
+
{
|
|
89
|
+
type: "tool_use",
|
|
90
|
+
id: "tool-1",
|
|
91
|
+
name: "calculator",
|
|
92
|
+
input: { expr: "1 + 2" },
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
role: "user",
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: "tool_result",
|
|
101
|
+
tool_use_id: "tool-1",
|
|
102
|
+
content: "3",
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
role: "assistant",
|
|
108
|
+
content: [{ type: "text", text: "The answer is 3." }],
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const out = stripInjectionsForCompaction(messages);
|
|
113
|
+
|
|
114
|
+
expect(out).toHaveLength(4);
|
|
115
|
+
expect(out[0].content).toEqual([
|
|
116
|
+
{ type: "text", text: "Run the calculator." },
|
|
117
|
+
]);
|
|
118
|
+
expect(out[1]).toEqual(messages[1]);
|
|
119
|
+
expect(out[2]).toEqual(messages[2]);
|
|
120
|
+
expect(out[3]).toEqual(messages[3]);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("leaves <turn_context>, <workspace>, and <memory __injected> alone", () => {
|
|
124
|
+
const messages: Message[] = [
|
|
125
|
+
{
|
|
126
|
+
role: "user",
|
|
127
|
+
content: [
|
|
128
|
+
{ type: "text", text: "<turn_context>\nnow\n</turn_context>" },
|
|
129
|
+
{ type: "text", text: "<workspace>\nfiles\n</workspace>" },
|
|
130
|
+
{ type: "text", text: "<memory __injected>\nrecent\n</memory>" },
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
const out = stripInjectionsForCompaction(messages);
|
|
135
|
+
expect(out[0].content).toEqual(messages[0].content);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -51,7 +51,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
51
51
|
|
|
52
52
|
mock.module("../providers/registry.js", () => ({
|
|
53
53
|
getProvider: () => ({ name: "mock-provider" }),
|
|
54
|
-
initializeProviders: () => {},
|
|
54
|
+
initializeProviders: async () => {},
|
|
55
55
|
}));
|
|
56
56
|
|
|
57
57
|
mock.module("../config/loader.js", () => ({
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
getRetrospectiveState,
|
|
50
50
|
upsertRetrospectiveState,
|
|
51
51
|
} from "../memory/memory-retrospective-state.js";
|
|
52
|
+
import { rawGet, rawRun } from "../memory/raw-query.js";
|
|
52
53
|
import {
|
|
53
54
|
activationState,
|
|
54
55
|
channelInboundEvents,
|
|
@@ -296,6 +297,114 @@ describe("forkConversation", () => {
|
|
|
296
297
|
]);
|
|
297
298
|
});
|
|
298
299
|
|
|
300
|
+
test("inherits cleanedAt when forking past the clean event", async () => {
|
|
301
|
+
const source = createConversation("Clean thread");
|
|
302
|
+
await addMessage(source.id, "user", "Message 1", undefined, {
|
|
303
|
+
skipIndexing: true,
|
|
304
|
+
});
|
|
305
|
+
const preClean = await addMessage(
|
|
306
|
+
source.id,
|
|
307
|
+
"assistant",
|
|
308
|
+
"Message 2",
|
|
309
|
+
undefined,
|
|
310
|
+
{ skipIndexing: true },
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const cleanedAt = preClean.createdAt + 1;
|
|
314
|
+
getDb()
|
|
315
|
+
.update(conversations)
|
|
316
|
+
.set({ cleanedAt })
|
|
317
|
+
.where(eq(conversations.id, source.id))
|
|
318
|
+
.run();
|
|
319
|
+
|
|
320
|
+
const postClean = await addMessage(
|
|
321
|
+
source.id,
|
|
322
|
+
"user",
|
|
323
|
+
"Message 3",
|
|
324
|
+
undefined,
|
|
325
|
+
{ skipIndexing: true },
|
|
326
|
+
);
|
|
327
|
+
expect(postClean.createdAt).toBeGreaterThanOrEqual(cleanedAt);
|
|
328
|
+
|
|
329
|
+
const fork = forkConversation({
|
|
330
|
+
conversationId: source.id,
|
|
331
|
+
throughMessageId: postClean.id,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
expect(fork.cleanedAt).toBe(cleanedAt);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("does not inherit cleanedAt when forking before the clean event", async () => {
|
|
338
|
+
const source = createConversation("Clean thread");
|
|
339
|
+
await addMessage(source.id, "user", "Message 1", undefined, {
|
|
340
|
+
skipIndexing: true,
|
|
341
|
+
});
|
|
342
|
+
const preClean = await addMessage(
|
|
343
|
+
source.id,
|
|
344
|
+
"assistant",
|
|
345
|
+
"Message 2",
|
|
346
|
+
undefined,
|
|
347
|
+
{ skipIndexing: true },
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const cleanedAt = preClean.createdAt + 1;
|
|
351
|
+
getDb()
|
|
352
|
+
.update(conversations)
|
|
353
|
+
.set({ cleanedAt })
|
|
354
|
+
.where(eq(conversations.id, source.id))
|
|
355
|
+
.run();
|
|
356
|
+
|
|
357
|
+
await addMessage(source.id, "user", "Message 3", undefined, {
|
|
358
|
+
skipIndexing: true,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const fork = forkConversation({
|
|
362
|
+
conversationId: source.id,
|
|
363
|
+
throughMessageId: preClean.id,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
expect(fork.cleanedAt).toBeNull();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("inherits cleanedAt on a full-history fork", async () => {
|
|
370
|
+
const source = createConversation("Clean thread");
|
|
371
|
+
await addMessage(source.id, "user", "Message 1", undefined, {
|
|
372
|
+
skipIndexing: true,
|
|
373
|
+
});
|
|
374
|
+
const last = await addMessage(
|
|
375
|
+
source.id,
|
|
376
|
+
"assistant",
|
|
377
|
+
"Message 2",
|
|
378
|
+
undefined,
|
|
379
|
+
{ skipIndexing: true },
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
const cleanedAt = last.createdAt - 1;
|
|
383
|
+
getDb()
|
|
384
|
+
.update(conversations)
|
|
385
|
+
.set({ cleanedAt })
|
|
386
|
+
.where(eq(conversations.id, source.id))
|
|
387
|
+
.run();
|
|
388
|
+
|
|
389
|
+
const fork = forkConversation({ conversationId: source.id });
|
|
390
|
+
|
|
391
|
+
expect(fork.cleanedAt).toBe(cleanedAt);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test("leaves cleanedAt null when the source has no clean event", async () => {
|
|
395
|
+
const source = createConversation("Unclean thread");
|
|
396
|
+
await addMessage(source.id, "user", "Message 1", undefined, {
|
|
397
|
+
skipIndexing: true,
|
|
398
|
+
});
|
|
399
|
+
await addMessage(source.id, "assistant", "Message 2", undefined, {
|
|
400
|
+
skipIndexing: true,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const fork = forkConversation({ conversationId: source.id });
|
|
404
|
+
|
|
405
|
+
expect(fork.cleanedAt).toBeNull();
|
|
406
|
+
});
|
|
407
|
+
|
|
299
408
|
test("rejects forks when the source conversation has no persisted messages", () => {
|
|
300
409
|
const source = createConversation("Empty thread");
|
|
301
410
|
|
|
@@ -661,6 +770,58 @@ describe("forkConversation", () => {
|
|
|
661
770
|
expect(loadGraphMemoryState(fork.id)).toBeNull();
|
|
662
771
|
});
|
|
663
772
|
|
|
773
|
+
test("defaults conversationType to standard and inherits the parent's group", async () => {
|
|
774
|
+
const source = createConversation("Default inheritance thread");
|
|
775
|
+
await addMessage(source.id, "user", "first message", undefined, {
|
|
776
|
+
skipIndexing: true,
|
|
777
|
+
});
|
|
778
|
+
rawRun(
|
|
779
|
+
"UPDATE conversations SET group_id = ? WHERE id = ?",
|
|
780
|
+
"system:pinned",
|
|
781
|
+
source.id,
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
const fork = forkConversation({ conversationId: source.id });
|
|
785
|
+
const row = getDb()
|
|
786
|
+
.select()
|
|
787
|
+
.from(conversations)
|
|
788
|
+
.where(eq(conversations.id, fork.id))
|
|
789
|
+
.get();
|
|
790
|
+
const groupIdRow = rawGet<{ group_id: string | null }>(
|
|
791
|
+
"SELECT group_id FROM conversations WHERE id = ?",
|
|
792
|
+
fork.id,
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
expect(row?.conversationType).toBe("standard");
|
|
796
|
+
expect(groupIdRow?.group_id).toBe("system:pinned");
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test("honors conversationType and groupId overrides on the fork", async () => {
|
|
800
|
+
const source = createConversation("Override thread");
|
|
801
|
+
await addMessage(source.id, "user", "first message", undefined, {
|
|
802
|
+
skipIndexing: true,
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
const fork = forkConversation({
|
|
806
|
+
conversationId: source.id,
|
|
807
|
+
conversationType: "background",
|
|
808
|
+
groupId: "system:background",
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
const row = getDb()
|
|
812
|
+
.select()
|
|
813
|
+
.from(conversations)
|
|
814
|
+
.where(eq(conversations.id, fork.id))
|
|
815
|
+
.get();
|
|
816
|
+
const groupIdRow = rawGet<{ group_id: string | null }>(
|
|
817
|
+
"SELECT group_id FROM conversations WHERE id = ?",
|
|
818
|
+
fork.id,
|
|
819
|
+
);
|
|
820
|
+
|
|
821
|
+
expect(row?.conversationType).toBe("background");
|
|
822
|
+
expect(groupIdRow?.group_id).toBe("system:background");
|
|
823
|
+
});
|
|
824
|
+
|
|
664
825
|
test("copies memory state when throughMessageId points at the last message", async () => {
|
|
665
826
|
const source = createConversation("Through-last thread");
|
|
666
827
|
const lastMessage = await addMessage(
|
|
@@ -8,7 +8,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
8
8
|
|
|
9
9
|
mock.module("../providers/registry.js", () => ({
|
|
10
10
|
getProvider: () => ({ name: "mock-provider" }),
|
|
11
|
-
initializeProviders: () => {},
|
|
11
|
+
initializeProviders: async () => {},
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
14
|
mock.module("../config/loader.js", () => ({
|