@vellumai/assistant 0.4.49 → 0.4.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +24 -33
- package/README.md +3 -3
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/keychain-broker.md +6 -6
- package/docs/architecture/memory.md +180 -119
- package/knip.json +32 -0
- package/package.json +3 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/btw-routes.test.ts +61 -5
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +8 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +8 -7
- package/src/__tests__/credential-vault-unit.test.ts +23 -18
- package/src/__tests__/credential-vault.test.ts +30 -18
- package/src/__tests__/credentials-cli.test.ts +257 -82
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
- package/src/__tests__/integration-status.test.ts +31 -30
- package/src/__tests__/invite-redemption-service.test.ts +166 -13
- package/src/__tests__/invite-routes-http.test.ts +166 -5
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +193 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +824 -31
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +363 -17
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +55 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
- package/src/__tests__/secure-keys.test.ts +78 -18
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills.test.ts +2 -2
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +10 -8
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/twilio-config.test.ts +11 -10
- package/src/__tests__/twilio-provider.test.ts +9 -4
- package/src/__tests__/voice-invite-redemption.test.ts +85 -5
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +134 -3
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +44 -6
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +5 -4
- package/src/calls/twilio-provider.ts +14 -9
- package/src/calls/twilio-rest.ts +10 -7
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/config.ts +14 -9
- package/src/cli/commands/contacts.ts +3 -0
- package/src/cli/commands/credentials.ts +170 -174
- package/src/cli/commands/doctor.ts +11 -8
- package/src/cli/commands/keys.ts +9 -9
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +68 -10
- package/src/cli/commands/oauth/connections.ts +475 -105
- package/src/cli/commands/oauth/index.ts +3 -3
- package/src/cli/commands/oauth/providers.ts +18 -4
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +20 -22
- package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-skills/contacts/SKILL.md +35 -11
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +1 -1
- package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
- package/src/config/bundled-skills/messaging/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
- package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/loader.ts +6 -42
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/contacts/contact-store.ts +39 -2
- package/src/contacts/contacts-write.ts +9 -0
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +55 -2
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +59 -39
- package/src/daemon/handlers/config-telegram.ts +23 -14
- package/src/daemon/handlers/session-history.ts +1 -358
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/shared.ts +3 -17
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +39 -4
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -42
- package/src/daemon/server.ts +6 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +3 -5
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/email/providers/index.ts +2 -2
- package/src/instrument.ts +61 -1
- package/src/media/avatar-router.ts +1 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +25 -83
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/invite-store.ts +19 -0
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/157-invite-contact-id.ts +104 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
- package/src/messaging/providers/whatsapp/adapter.ts +13 -9
- package/src/messaging/registry.ts +9 -5
- package/src/oauth/byo-connection.test.ts +40 -25
- package/src/oauth/connect-orchestrator.ts +4 -10
- package/src/oauth/connection-resolver.ts +20 -6
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +183 -31
- package/src/oauth/platform-connection.test.ts +1 -1
- package/src/oauth/provider-behaviors.ts +503 -4
- package/src/oauth/seed-providers.ts +214 -8
- package/src/oauth/token-persistence.ts +31 -16
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/channel-readiness-service.ts +48 -40
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +72 -12
- package/src/runtime/invite-service.ts +43 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -1
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/btw-routes.ts +10 -5
- package/src/runtime/routes/conversation-routes.ts +56 -11
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/integrations/slack/channel.ts +2 -2
- package/src/runtime/routes/integrations/telegram.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +17 -17
- package/src/runtime/routes/invite-routes.ts +29 -4
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/secret-routes.ts +17 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +3 -3
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +9 -4
- package/src/runtime/routes/workspace-utils.ts +8 -2
- package/src/schedule/integration-status.ts +26 -19
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/oauth2.ts +6 -7
- package/src/security/secure-keys.ts +44 -19
- package/src/security/token-manager.ts +46 -39
- package/src/services/vercel-deploy.ts +0 -24
- package/src/signals/confirm.ts +78 -0
- package/src/signals/mcp-reload.ts +18 -0
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +22 -7
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/network/script-proxy/session-manager.ts +8 -8
- package/src/tools/schedule/create.ts +10 -3
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/watcher/provider-types.ts +1 -1
- package/src/watcher/providers/github.ts +1 -1
- package/src/watcher/providers/gmail.ts +3 -3
- package/src/watcher/providers/google-calendar.ts +3 -3
- package/src/watcher/providers/linear.ts +1 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for handleListMessages attachment handling.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that:
|
|
5
|
+
* - User message image attachments include base64 data for client thumbnail generation
|
|
6
|
+
* - User message non-image attachments stay metadata-only (no base64 blob)
|
|
7
|
+
* - Assistant message attachments remain metadata-only
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
14
|
+
|
|
15
|
+
const testDir = mkdtempSync(join(tmpdir(), "list-messages-test-"));
|
|
16
|
+
|
|
17
|
+
mock.module("../util/platform.js", () => ({
|
|
18
|
+
getDataDir: () => testDir,
|
|
19
|
+
isMacOS: () => process.platform === "darwin",
|
|
20
|
+
isLinux: () => process.platform === "linux",
|
|
21
|
+
isWindows: () => process.platform === "win32",
|
|
22
|
+
getPidPath: () => join(testDir, "test.pid"),
|
|
23
|
+
getDbPath: () => join(testDir, "test.db"),
|
|
24
|
+
getLogPath: () => join(testDir, "test.log"),
|
|
25
|
+
ensureDataDir: () => {},
|
|
26
|
+
getRootDir: () => testDir,
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
mock.module("../util/logger.js", () => ({
|
|
30
|
+
getLogger: () =>
|
|
31
|
+
new Proxy({} as Record<string, unknown>, {
|
|
32
|
+
get: () => () => {},
|
|
33
|
+
}),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
mock.module("../config/loader.js", () => ({
|
|
37
|
+
getConfig: () => ({
|
|
38
|
+
ui: {},
|
|
39
|
+
model: "test",
|
|
40
|
+
provider: "test",
|
|
41
|
+
apiKeys: {},
|
|
42
|
+
memory: { enabled: false },
|
|
43
|
+
rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
|
|
44
|
+
}),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
linkAttachmentToMessage,
|
|
49
|
+
uploadAttachment,
|
|
50
|
+
} from "../memory/attachments-store.js";
|
|
51
|
+
import { addMessage, createConversation } from "../memory/conversation-crud.js";
|
|
52
|
+
import { getDb, initializeDb, resetDb } from "../memory/db.js";
|
|
53
|
+
import { handleListMessages } from "../runtime/routes/conversation-routes.js";
|
|
54
|
+
|
|
55
|
+
initializeDb();
|
|
56
|
+
|
|
57
|
+
afterAll(() => {
|
|
58
|
+
resetDb();
|
|
59
|
+
try {
|
|
60
|
+
rmSync(testDir, { recursive: true });
|
|
61
|
+
} catch {
|
|
62
|
+
/* best effort */
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function resetTables() {
|
|
67
|
+
const db = getDb();
|
|
68
|
+
db.run("DELETE FROM message_attachments");
|
|
69
|
+
db.run("DELETE FROM attachments");
|
|
70
|
+
db.run("DELETE FROM messages");
|
|
71
|
+
db.run("DELETE FROM conversations");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createTestUrl(conversationId: string): URL {
|
|
75
|
+
return new URL(
|
|
76
|
+
`http://localhost/v1/messages?conversationId=${conversationId}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface AttachmentPayload {
|
|
81
|
+
data?: string;
|
|
82
|
+
mimeType: string;
|
|
83
|
+
thumbnailData?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface MessagePayload {
|
|
87
|
+
attachments?: AttachmentPayload[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const IMAGE_BASE64 =
|
|
91
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk";
|
|
92
|
+
const DOC_BASE64 = "JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo";
|
|
93
|
+
|
|
94
|
+
describe("handleListMessages attachments", () => {
|
|
95
|
+
beforeEach(resetTables);
|
|
96
|
+
|
|
97
|
+
test("user message image attachments include base64 data", async () => {
|
|
98
|
+
const conv = createConversation();
|
|
99
|
+
const msg = await addMessage(
|
|
100
|
+
conv.id,
|
|
101
|
+
"user",
|
|
102
|
+
JSON.stringify([{ type: "text", text: "check this image" }]),
|
|
103
|
+
);
|
|
104
|
+
const stored = uploadAttachment("photo.png", "image/png", IMAGE_BASE64);
|
|
105
|
+
linkAttachmentToMessage(msg.id, stored.id, 0);
|
|
106
|
+
|
|
107
|
+
const response = handleListMessages(createTestUrl(conv.id), null);
|
|
108
|
+
const body = (await response.json()) as { messages: MessagePayload[] };
|
|
109
|
+
|
|
110
|
+
expect(body.messages).toHaveLength(1);
|
|
111
|
+
const attachments = body.messages[0].attachments;
|
|
112
|
+
expect(attachments).toBeDefined();
|
|
113
|
+
expect(attachments).toHaveLength(1);
|
|
114
|
+
expect(attachments![0].mimeType).toBe("image/png");
|
|
115
|
+
expect(attachments![0].data).toBe(IMAGE_BASE64);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("user message non-image attachments stay metadata-only", async () => {
|
|
119
|
+
const conv = createConversation();
|
|
120
|
+
const msg = await addMessage(
|
|
121
|
+
conv.id,
|
|
122
|
+
"user",
|
|
123
|
+
JSON.stringify([{ type: "text", text: "check this doc" }]),
|
|
124
|
+
);
|
|
125
|
+
const stored = uploadAttachment(
|
|
126
|
+
"report.pdf",
|
|
127
|
+
"application/pdf",
|
|
128
|
+
DOC_BASE64,
|
|
129
|
+
);
|
|
130
|
+
linkAttachmentToMessage(msg.id, stored.id, 0);
|
|
131
|
+
|
|
132
|
+
const response = handleListMessages(createTestUrl(conv.id), null);
|
|
133
|
+
const body = (await response.json()) as { messages: MessagePayload[] };
|
|
134
|
+
|
|
135
|
+
expect(body.messages).toHaveLength(1);
|
|
136
|
+
const attachments = body.messages[0].attachments;
|
|
137
|
+
expect(attachments).toBeDefined();
|
|
138
|
+
expect(attachments).toHaveLength(1);
|
|
139
|
+
expect(attachments![0].mimeType).toBe("application/pdf");
|
|
140
|
+
// Non-image attachments should NOT include base64 data
|
|
141
|
+
expect(attachments![0].data).toBeUndefined();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("assistant message attachments remain metadata-only", async () => {
|
|
145
|
+
const conv = createConversation();
|
|
146
|
+
const msg = await addMessage(
|
|
147
|
+
conv.id,
|
|
148
|
+
"assistant",
|
|
149
|
+
JSON.stringify([{ type: "text", text: "here is an image" }]),
|
|
150
|
+
);
|
|
151
|
+
const stored = uploadAttachment("result.png", "image/png", IMAGE_BASE64);
|
|
152
|
+
linkAttachmentToMessage(msg.id, stored.id, 0);
|
|
153
|
+
|
|
154
|
+
const response = handleListMessages(createTestUrl(conv.id), null);
|
|
155
|
+
const body = (await response.json()) as { messages: MessagePayload[] };
|
|
156
|
+
|
|
157
|
+
expect(body.messages).toHaveLength(1);
|
|
158
|
+
const attachments = body.messages[0].attachments;
|
|
159
|
+
expect(attachments).toBeDefined();
|
|
160
|
+
expect(attachments).toHaveLength(1);
|
|
161
|
+
expect(attachments![0].mimeType).toBe("image/png");
|
|
162
|
+
// Assistant attachments should NOT include base64 data (metadata-only)
|
|
163
|
+
expect(attachments![0].data).toBeUndefined();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("user message with mixed attachments only inlines images", async () => {
|
|
167
|
+
const conv = createConversation();
|
|
168
|
+
const msg = await addMessage(
|
|
169
|
+
conv.id,
|
|
170
|
+
"user",
|
|
171
|
+
JSON.stringify([{ type: "text", text: "here are files" }]),
|
|
172
|
+
);
|
|
173
|
+
const imgStored = uploadAttachment("photo.jpg", "image/jpeg", IMAGE_BASE64);
|
|
174
|
+
const docStored = uploadAttachment(
|
|
175
|
+
"doc.pdf",
|
|
176
|
+
"application/pdf",
|
|
177
|
+
DOC_BASE64,
|
|
178
|
+
);
|
|
179
|
+
linkAttachmentToMessage(msg.id, imgStored.id, 0);
|
|
180
|
+
linkAttachmentToMessage(msg.id, docStored.id, 1);
|
|
181
|
+
|
|
182
|
+
const response = handleListMessages(createTestUrl(conv.id), null);
|
|
183
|
+
const body = (await response.json()) as { messages: MessagePayload[] };
|
|
184
|
+
|
|
185
|
+
const attachments = body.messages[0].attachments!;
|
|
186
|
+
expect(attachments).toHaveLength(2);
|
|
187
|
+
|
|
188
|
+
const imgAtt = attachments.find((a) => a.mimeType === "image/jpeg");
|
|
189
|
+
const docAtt = attachments.find((a) => a.mimeType === "application/pdf");
|
|
190
|
+
expect(imgAtt!.data).toBe(IMAGE_BASE64);
|
|
191
|
+
expect(docAtt!.data).toBeUndefined();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* - compaction.summaryCalls: 2-6
|
|
6
6
|
* - compaction.estimatedInputTokens: < previousEstimatedInputTokens
|
|
7
7
|
* - recall.injectedTokens: <= computed dynamic budget
|
|
8
|
-
* - recall.lexicalHits: > 0
|
|
9
8
|
* - recall.recencyHits: > 0
|
|
9
|
+
* - recall.enabled: true
|
|
10
10
|
*/
|
|
11
11
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
@@ -50,6 +50,37 @@ mock.module("../util/logger.js", () => ({
|
|
|
50
50
|
}),
|
|
51
51
|
}));
|
|
52
52
|
|
|
53
|
+
// Stub the local embedding backend so the real ONNX model never loads.
|
|
54
|
+
mock.module("../memory/embedding-local.js", () => ({
|
|
55
|
+
LocalEmbeddingBackend: class {
|
|
56
|
+
readonly provider = "local" as const;
|
|
57
|
+
readonly model: string;
|
|
58
|
+
constructor(model: string) {
|
|
59
|
+
this.model = model;
|
|
60
|
+
}
|
|
61
|
+
async embed(texts: string[]): Promise<number[][]> {
|
|
62
|
+
return texts.map(() => new Array(384).fill(0));
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
// Dynamic Qdrant mock so the benchmark can inject high-scoring results
|
|
68
|
+
let mockQdrantResults: Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
score: number;
|
|
71
|
+
payload: Record<string, unknown>;
|
|
72
|
+
}> = [];
|
|
73
|
+
|
|
74
|
+
mock.module("../memory/qdrant-client.js", () => ({
|
|
75
|
+
getQdrantClient: () => ({
|
|
76
|
+
searchWithFilter: async () => mockQdrantResults,
|
|
77
|
+
hybridSearch: async () => mockQdrantResults,
|
|
78
|
+
upsertPoints: async () => {},
|
|
79
|
+
deletePoints: async () => {},
|
|
80
|
+
}),
|
|
81
|
+
initQdrantClient: () => {},
|
|
82
|
+
}));
|
|
83
|
+
|
|
53
84
|
function makeLongMessages(turns: number): Message[] {
|
|
54
85
|
const rows: Message[] = [];
|
|
55
86
|
const userTail =
|
|
@@ -161,18 +192,15 @@ describe("Memory context benchmark", () => {
|
|
|
161
192
|
beforeEach(() => {
|
|
162
193
|
const db = getDb();
|
|
163
194
|
db.run("DELETE FROM memory_item_sources");
|
|
164
|
-
db.run("DELETE FROM memory_item_entities");
|
|
165
|
-
db.run("DELETE FROM memory_entity_relations");
|
|
166
|
-
db.run("DELETE FROM memory_entities");
|
|
167
195
|
db.run("DELETE FROM memory_embeddings");
|
|
168
|
-
db.run("DELETE FROM memory_summaries");
|
|
169
196
|
db.run("DELETE FROM memory_items");
|
|
170
|
-
|
|
197
|
+
|
|
171
198
|
db.run("DELETE FROM memory_segments");
|
|
172
199
|
db.run("DELETE FROM messages");
|
|
173
200
|
db.run("DELETE FROM conversations");
|
|
174
201
|
db.run("DELETE FROM memory_jobs");
|
|
175
202
|
db.run("DELETE FROM memory_checkpoints");
|
|
203
|
+
mockQdrantResults = [];
|
|
176
204
|
});
|
|
177
205
|
|
|
178
206
|
afterAll(() => {
|
|
@@ -225,13 +253,7 @@ describe("Memory context benchmark", () => {
|
|
|
225
253
|
},
|
|
226
254
|
retrieval: {
|
|
227
255
|
...DEFAULT_CONFIG.memory.retrieval,
|
|
228
|
-
lexicalTopK: 50,
|
|
229
|
-
semanticTopK: 20,
|
|
230
256
|
maxInjectTokens: 750,
|
|
231
|
-
reranking: {
|
|
232
|
-
...DEFAULT_CONFIG.memory.retrieval.reranking,
|
|
233
|
-
enabled: false,
|
|
234
|
-
},
|
|
235
257
|
dynamicBudget: {
|
|
236
258
|
enabled: true,
|
|
237
259
|
minInjectTokens: 160,
|
|
@@ -257,6 +279,23 @@ describe("Memory context benchmark", () => {
|
|
|
257
279
|
recallConfig.memory.retrieval.dynamicBudget.maxInjectTokens,
|
|
258
280
|
});
|
|
259
281
|
|
|
282
|
+
// Seed Qdrant mock with a representative decision segment so
|
|
283
|
+
// the benchmark validates content quality, not just pipeline completion.
|
|
284
|
+
mockQdrantResults = [
|
|
285
|
+
{
|
|
286
|
+
id: "emb-bench-decision",
|
|
287
|
+
score: 0.9,
|
|
288
|
+
payload: {
|
|
289
|
+
target_type: "segment",
|
|
290
|
+
target_id: "seg-bench-0",
|
|
291
|
+
text: "Decision 0: use Bun test fixtures for memory regressions and recall ranking checks.",
|
|
292
|
+
kind: "segment",
|
|
293
|
+
created_at: now,
|
|
294
|
+
last_seen_at: now,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
|
|
260
299
|
const recall = await buildMemoryRecall(
|
|
261
300
|
"What decisions did we make about Bun tests and retrieval diagnostics?",
|
|
262
301
|
conversationId,
|
|
@@ -264,13 +303,13 @@ describe("Memory context benchmark", () => {
|
|
|
264
303
|
{ maxInjectTokensOverride: recallBudget },
|
|
265
304
|
);
|
|
266
305
|
|
|
267
|
-
//
|
|
268
|
-
// fails and the retriever marks the result as degraded. The benchmark
|
|
269
|
-
// cares about compaction and lexical recall quality, not embedding
|
|
270
|
-
// availability, so we do not assert on `recall.degraded`.
|
|
271
|
-
expect(recall.lexicalHits).toBeGreaterThan(0);
|
|
306
|
+
// Recency search finds conversation-scoped segments.
|
|
272
307
|
expect(recall.recencyHits).toBeGreaterThan(0);
|
|
308
|
+
expect(recall.enabled).toBe(true);
|
|
309
|
+
// With Qdrant mock returning a high-scoring result, content should be injected.
|
|
273
310
|
expect(recall.selectedCount).toBeGreaterThan(0);
|
|
311
|
+
expect(recall.injectedText).toContain("Bun test fixtures");
|
|
312
|
+
expect(recall.injectedTokens).toBeGreaterThan(0);
|
|
274
313
|
expect(recall.injectedTokens).toBeLessThanOrEqual(recallBudget);
|
|
275
314
|
expect(recallBudget).toBeGreaterThanOrEqual(
|
|
276
315
|
recallConfig.memory.retrieval.dynamicBudget.minInjectTokens,
|
|
@@ -278,6 +317,5 @@ describe("Memory context benchmark", () => {
|
|
|
278
317
|
expect(recallBudget).toBeLessThanOrEqual(
|
|
279
318
|
recallConfig.memory.retrieval.dynamicBudget.maxInjectTokens,
|
|
280
319
|
);
|
|
281
|
-
expect(recall.injectedText).toContain("Bun test fixtures");
|
|
282
320
|
});
|
|
283
321
|
});
|