@vellumai/assistant 0.5.4 → 0.5.6

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.
Files changed (151) hide show
  1. package/Dockerfile +17 -27
  2. package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -0
  3. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +42 -0
  4. package/package.json +1 -1
  5. package/src/__tests__/actor-token-service.test.ts +113 -0
  6. package/src/__tests__/config-schema.test.ts +2 -2
  7. package/src/__tests__/context-window-manager.test.ts +78 -0
  8. package/src/__tests__/conversation-title-service.test.ts +30 -1
  9. package/src/__tests__/credential-security-invariants.test.ts +2 -0
  10. package/src/__tests__/docker-signing-key-bootstrap.test.ts +207 -0
  11. package/src/__tests__/memory-regressions.test.ts +8 -30
  12. package/src/__tests__/openai-whisper.test.ts +93 -0
  13. package/src/__tests__/require-fresh-approval.test.ts +4 -0
  14. package/src/__tests__/slack-messaging-token-resolution.test.ts +319 -0
  15. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
  16. package/src/__tests__/tool-executor.test.ts +4 -0
  17. package/src/__tests__/volume-security-guard.test.ts +155 -0
  18. package/src/cli/commands/conversations.ts +0 -18
  19. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -0
  20. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +16 -37
  21. package/src/config/env-registry.ts +9 -0
  22. package/src/config/env.ts +8 -2
  23. package/src/config/feature-flag-registry.json +8 -8
  24. package/src/config/schema.ts +0 -12
  25. package/src/config/schemas/memory.ts +0 -4
  26. package/src/config/schemas/platform.ts +1 -1
  27. package/src/config/schemas/security.ts +4 -0
  28. package/src/context/window-manager.ts +53 -2
  29. package/src/credential-execution/managed-catalog.ts +5 -15
  30. package/src/daemon/conversation-agent-loop.ts +0 -60
  31. package/src/daemon/conversation-memory.ts +0 -117
  32. package/src/daemon/conversation-runtime-assembly.ts +0 -2
  33. package/src/daemon/daemon-control.ts +7 -0
  34. package/src/daemon/handlers/conversations.ts +0 -11
  35. package/src/daemon/lifecycle.ts +10 -47
  36. package/src/daemon/providers-setup.ts +2 -1
  37. package/src/followups/followup-store.ts +5 -2
  38. package/src/hooks/manager.ts +7 -0
  39. package/src/instrument.ts +33 -1
  40. package/src/memory/conversation-crud.ts +0 -236
  41. package/src/memory/conversation-title-service.ts +26 -10
  42. package/src/memory/db-init.ts +5 -13
  43. package/src/memory/embedding-local.ts +11 -5
  44. package/src/memory/indexer.ts +15 -106
  45. package/src/memory/job-handlers/conversation-starters.ts +24 -36
  46. package/src/memory/job-handlers/embedding.ts +0 -79
  47. package/src/memory/job-utils.ts +1 -1
  48. package/src/memory/jobs-store.ts +0 -8
  49. package/src/memory/jobs-worker.ts +0 -20
  50. package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
  51. package/src/memory/migrations/index.ts +1 -3
  52. package/src/memory/qdrant-client.ts +4 -6
  53. package/src/memory/schema/conversations.ts +0 -3
  54. package/src/memory/schema/index.ts +0 -2
  55. package/src/messaging/draft-store.ts +2 -2
  56. package/src/messaging/provider.ts +9 -0
  57. package/src/messaging/providers/slack/adapter.ts +29 -2
  58. package/src/oauth/connection-resolver.test.ts +22 -18
  59. package/src/oauth/connection-resolver.ts +92 -7
  60. package/src/oauth/platform-connection.test.ts +78 -69
  61. package/src/oauth/platform-connection.ts +12 -19
  62. package/src/permissions/defaults.ts +3 -3
  63. package/src/permissions/trust-client.ts +332 -0
  64. package/src/permissions/trust-store-interface.ts +105 -0
  65. package/src/permissions/trust-store.ts +531 -39
  66. package/src/platform/client.test.ts +148 -0
  67. package/src/platform/client.ts +71 -0
  68. package/src/providers/speech-to-text/openai-whisper.test.ts +190 -0
  69. package/src/providers/speech-to-text/openai-whisper.ts +68 -0
  70. package/src/providers/speech-to-text/resolve.ts +9 -0
  71. package/src/providers/speech-to-text/types.ts +17 -0
  72. package/src/runtime/auth/route-policy.ts +14 -0
  73. package/src/runtime/auth/token-service.ts +133 -0
  74. package/src/runtime/http-server.ts +4 -2
  75. package/src/runtime/routes/conversation-management-routes.ts +0 -36
  76. package/src/runtime/routes/conversation-query-routes.ts +44 -2
  77. package/src/runtime/routes/conversation-routes.ts +2 -1
  78. package/src/runtime/routes/inbound-message-handler.ts +27 -3
  79. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -1
  80. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +287 -0
  81. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +122 -0
  82. package/src/runtime/routes/log-export-routes.ts +1 -0
  83. package/src/runtime/routes/memory-item-routes.test.ts +221 -3
  84. package/src/runtime/routes/memory-item-routes.ts +124 -2
  85. package/src/runtime/routes/secret-routes.ts +4 -1
  86. package/src/runtime/routes/upgrade-broadcast-routes.ts +151 -0
  87. package/src/schedule/schedule-store.ts +0 -21
  88. package/src/security/ces-credential-client.ts +173 -0
  89. package/src/security/secure-keys.ts +65 -22
  90. package/src/signals/bash.ts +3 -0
  91. package/src/signals/cancel.ts +3 -0
  92. package/src/signals/confirm.ts +3 -0
  93. package/src/signals/conversation-undo.ts +3 -0
  94. package/src/signals/event-stream.ts +7 -0
  95. package/src/signals/shotgun.ts +3 -0
  96. package/src/signals/trust-rule.ts +3 -0
  97. package/src/skills/inline-command-render.ts +5 -1
  98. package/src/skills/inline-command-runner.ts +30 -2
  99. package/src/telemetry/usage-telemetry-reporter.test.ts +23 -36
  100. package/src/telemetry/usage-telemetry-reporter.ts +21 -19
  101. package/src/tools/memory/handlers.ts +1 -129
  102. package/src/tools/permission-checker.ts +18 -0
  103. package/src/tools/skills/load.ts +9 -2
  104. package/src/util/device-id.ts +70 -7
  105. package/src/util/logger.ts +35 -9
  106. package/src/util/platform.ts +29 -5
  107. package/src/util/xml.ts +8 -0
  108. package/src/workspace/heartbeat-service.ts +5 -24
  109. package/src/workspace/migrations/migrate-to-workspace-volume.ts +113 -0
  110. package/src/workspace/migrations/registry.ts +2 -0
  111. package/src/__tests__/archive-recall.test.ts +0 -560
  112. package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
  113. package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
  114. package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
  115. package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
  116. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
  117. package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
  118. package/src/__tests__/memory-brief-time.test.ts +0 -285
  119. package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
  120. package/src/__tests__/memory-chunk-archive.test.ts +0 -400
  121. package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
  122. package/src/__tests__/memory-episode-archive.test.ts +0 -370
  123. package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
  124. package/src/__tests__/memory-observation-archive.test.ts +0 -375
  125. package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
  126. package/src/__tests__/memory-reducer-job.test.ts +0 -538
  127. package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
  128. package/src/__tests__/memory-reducer-store.test.ts +0 -728
  129. package/src/__tests__/memory-reducer-types.test.ts +0 -707
  130. package/src/__tests__/memory-reducer.test.ts +0 -704
  131. package/src/__tests__/memory-simplified-config.test.ts +0 -281
  132. package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
  133. package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
  134. package/src/config/schemas/memory-simplified.ts +0 -101
  135. package/src/memory/archive-recall.ts +0 -516
  136. package/src/memory/archive-store.ts +0 -400
  137. package/src/memory/brief-formatting.ts +0 -33
  138. package/src/memory/brief-open-loops.ts +0 -266
  139. package/src/memory/brief-time.ts +0 -162
  140. package/src/memory/brief.ts +0 -75
  141. package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
  142. package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
  143. package/src/memory/migrations/185-memory-brief-state.ts +0 -52
  144. package/src/memory/migrations/186-memory-archive.ts +0 -109
  145. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
  146. package/src/memory/reducer-scheduler.ts +0 -242
  147. package/src/memory/reducer-store.ts +0 -271
  148. package/src/memory/reducer-types.ts +0 -106
  149. package/src/memory/reducer.ts +0 -467
  150. package/src/memory/schema/memory-archive.ts +0 -121
  151. package/src/memory/schema/memory-brief.ts +0 -55
@@ -1,453 +0,0 @@
1
- /**
2
- * Tests for the chunk dual-write path in the memory indexer.
3
- *
4
- * The indexer now writes both legacy memory_segments AND archive
5
- * memory_chunks using the same segmentation boundaries. These tests
6
- * verify:
7
- *
8
- * 1. Chunks are created alongside segments with matching boundaries.
9
- * 2. Unchanged chunk content does not enqueue duplicate embed_chunk jobs.
10
- * 3. Changed chunk content enqueues an embed_chunk job.
11
- * 4. The legacy memory_segments path remains intact.
12
- */
13
-
14
- import { mkdtempSync, rmSync } from "node:fs";
15
- import { tmpdir } from "node:os";
16
- import { join } from "node:path";
17
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
18
-
19
- import { eq } from "drizzle-orm";
20
-
21
- const testDir = mkdtempSync(join(tmpdir(), "memory-chunk-dual-write-"));
22
-
23
- mock.module("../util/platform.js", () => ({
24
- getDataDir: () => testDir,
25
- isMacOS: () => process.platform === "darwin",
26
- isLinux: () => process.platform === "linux",
27
- isWindows: () => process.platform === "win32",
28
- getPidPath: () => join(testDir, "test.pid"),
29
- getDbPath: () => join(testDir, "test.db"),
30
- getLogPath: () => join(testDir, "test.log"),
31
- ensureDataDir: () => {},
32
- }));
33
-
34
- mock.module("../util/logger.js", () => ({
35
- getLogger: () =>
36
- new Proxy({} as Record<string, unknown>, {
37
- get: () => () => {},
38
- }),
39
- }));
40
-
41
- mock.module("../memory/qdrant-client.js", () => ({
42
- getQdrantClient: () => ({
43
- searchWithFilter: async () => [],
44
- hybridSearch: async () => [],
45
- upsertPoints: async () => {},
46
- deletePoints: async () => {},
47
- }),
48
- initQdrantClient: () => {},
49
- }));
50
-
51
- import { DEFAULT_CONFIG } from "../config/defaults.js";
52
-
53
- const TEST_CONFIG = {
54
- ...DEFAULT_CONFIG,
55
- memory: {
56
- ...DEFAULT_CONFIG.memory,
57
- enabled: true,
58
- extraction: {
59
- ...DEFAULT_CONFIG.memory.extraction,
60
- useLLM: false,
61
- },
62
- },
63
- };
64
-
65
- mock.module("../config/loader.js", () => ({
66
- loadConfig: () => TEST_CONFIG,
67
- getConfig: () => TEST_CONFIG,
68
- loadRawConfig: () => ({}),
69
- saveRawConfig: () => {},
70
- invalidateConfigCache: () => {},
71
- }));
72
-
73
- import { getDb, initializeDb, resetDb, resetTestTables } from "../memory/db.js";
74
- import { indexMessageNow } from "../memory/indexer.js";
75
- import {
76
- conversations,
77
- memoryChunks,
78
- memoryJobs,
79
- memoryObservations,
80
- memorySegments,
81
- messages,
82
- } from "../memory/schema.js";
83
-
84
- // Initialize DB once for the entire file. Each test cleans its own tables.
85
- initializeDb();
86
-
87
- afterAll(() => {
88
- resetDb();
89
- try {
90
- rmSync(testDir, { recursive: true });
91
- } catch {
92
- // best effort cleanup
93
- }
94
- });
95
-
96
- function resetTables() {
97
- resetTestTables(
98
- "memory_chunks",
99
- "memory_observations",
100
- "memory_segments",
101
- "memory_jobs",
102
- "messages",
103
- "conversations",
104
- );
105
- }
106
-
107
- /** Insert a minimal conversation + message row for FK references. */
108
- function seedConversationAndMessage(
109
- conversationId: string,
110
- messageId: string,
111
- text: string,
112
- ): void {
113
- const db = getDb();
114
- const now = Date.now();
115
- db.insert(conversations)
116
- .values({
117
- id: conversationId,
118
- title: null,
119
- createdAt: now,
120
- updatedAt: now,
121
- })
122
- .onConflictDoNothing()
123
- .run();
124
-
125
- db.insert(messages)
126
- .values({
127
- id: messageId,
128
- conversationId,
129
- role: "user",
130
- content: JSON.stringify([{ type: "text", text }]),
131
- createdAt: now,
132
- })
133
- .onConflictDoNothing()
134
- .run();
135
- }
136
-
137
- // ─────────────────────────────────────────────────────────────────────────────
138
- // Test suite: chunk dual-write alongside legacy segments
139
- // ─────────────────────────────────────────────────────────────────────────────
140
-
141
- describe("chunk dual-write from the memory indexer", () => {
142
- beforeEach(() => {
143
- resetTables();
144
- });
145
-
146
- test("indexing a message creates chunks alongside segments with matching boundaries", async () => {
147
- const conversationId = "conv-dual-write-basic";
148
- const messageId = "msg-dual-write-basic";
149
- const text =
150
- "I prefer TypeScript for large projects and always use strict mode.";
151
-
152
- seedConversationAndMessage(conversationId, messageId, text);
153
-
154
- const config = TEST_CONFIG.memory;
155
- const result = await indexMessageNow(
156
- {
157
- messageId,
158
- conversationId,
159
- role: "user",
160
- content: JSON.stringify([{ type: "text", text }]),
161
- createdAt: Date.now(),
162
- },
163
- config,
164
- );
165
-
166
- expect(result.indexedSegments).toBeGreaterThanOrEqual(1);
167
-
168
- const db = getDb();
169
-
170
- // Verify segments were created (legacy path)
171
- const segments = db
172
- .select()
173
- .from(memorySegments)
174
- .where(eq(memorySegments.messageId, messageId))
175
- .all();
176
- expect(segments.length).toBeGreaterThanOrEqual(1);
177
-
178
- // Verify chunks were created (dual-write path)
179
- const observationId = `obs:${messageId}`;
180
- const chunks = db
181
- .select()
182
- .from(memoryChunks)
183
- .where(eq(memoryChunks.observationId, observationId))
184
- .all();
185
- expect(chunks.length).toBe(segments.length);
186
-
187
- // Verify the observation was created
188
- const observation = db
189
- .select()
190
- .from(memoryObservations)
191
- .where(eq(memoryObservations.id, observationId))
192
- .get();
193
- expect(observation).toBeDefined();
194
- expect(observation!.conversationId).toBe(conversationId);
195
- expect(observation!.messageId).toBe(messageId);
196
- expect(observation!.role).toBe("user");
197
-
198
- // Verify chunk content matches segment content (same boundaries)
199
- for (let i = 0; i < segments.length; i++) {
200
- const chunkId = `chunk:${messageId}:${i}`;
201
- const chunk = chunks.find((c) => c.id === chunkId);
202
- expect(chunk).toBeDefined();
203
- expect(chunk!.content).toBe(segments[i].text);
204
- expect(chunk!.tokenEstimate).toBe(segments[i].tokenEstimate);
205
- expect(chunk!.scopeId).toBe(segments[i].scopeId);
206
- }
207
- });
208
-
209
- test("unchanged chunk content does not enqueue duplicate embed_chunk jobs", async () => {
210
- const conversationId = "conv-unchanged-chunk";
211
- const messageId = "msg-unchanged-chunk";
212
- const text =
213
- "My preferred timezone is America/Los_Angeles and I work remotely.";
214
-
215
- seedConversationAndMessage(conversationId, messageId, text);
216
-
217
- const config = TEST_CONFIG.memory;
218
- const content = JSON.stringify([{ type: "text", text }]);
219
-
220
- // First indexing — should enqueue embed_chunk jobs
221
- await indexMessageNow(
222
- {
223
- messageId,
224
- conversationId,
225
- role: "user",
226
- content,
227
- createdAt: Date.now(),
228
- },
229
- config,
230
- );
231
-
232
- const db = getDb();
233
- const jobsAfterFirst = db
234
- .select()
235
- .from(memoryJobs)
236
- .where(eq(memoryJobs.type, "embed_chunk"))
237
- .all();
238
- const firstChunkJobCount = jobsAfterFirst.length;
239
- expect(firstChunkJobCount).toBeGreaterThanOrEqual(1);
240
-
241
- // Second indexing with identical content — should NOT enqueue more embed_chunk jobs
242
- await indexMessageNow(
243
- {
244
- messageId,
245
- conversationId,
246
- role: "user",
247
- content,
248
- createdAt: Date.now(),
249
- },
250
- config,
251
- );
252
-
253
- const jobsAfterSecond = db
254
- .select()
255
- .from(memoryJobs)
256
- .where(eq(memoryJobs.type, "embed_chunk"))
257
- .all();
258
-
259
- // No new embed_chunk jobs should have been enqueued
260
- expect(jobsAfterSecond.length).toBe(firstChunkJobCount);
261
- });
262
-
263
- test("changed chunk content enqueues new embed_chunk jobs", async () => {
264
- const conversationId = "conv-changed-chunk";
265
- const messageId = "msg-changed-chunk";
266
- const textV1 = "I prefer React for frontend development work.";
267
- const textV2 =
268
- "I prefer Vue for frontend development work on large projects instead.";
269
-
270
- seedConversationAndMessage(conversationId, messageId, textV1);
271
-
272
- const config = TEST_CONFIG.memory;
273
-
274
- // First indexing
275
- await indexMessageNow(
276
- {
277
- messageId,
278
- conversationId,
279
- role: "user",
280
- content: JSON.stringify([{ type: "text", text: textV1 }]),
281
- createdAt: Date.now(),
282
- },
283
- config,
284
- );
285
-
286
- const db = getDb();
287
- const jobsAfterFirst = db
288
- .select()
289
- .from(memoryJobs)
290
- .where(eq(memoryJobs.type, "embed_chunk"))
291
- .all();
292
- const firstChunkJobCount = jobsAfterFirst.length;
293
- expect(firstChunkJobCount).toBeGreaterThanOrEqual(1);
294
-
295
- // Second indexing with DIFFERENT content — should enqueue new embed_chunk jobs
296
- await indexMessageNow(
297
- {
298
- messageId,
299
- conversationId,
300
- role: "user",
301
- content: JSON.stringify([{ type: "text", text: textV2 }]),
302
- createdAt: Date.now(),
303
- },
304
- config,
305
- );
306
-
307
- const jobsAfterSecond = db
308
- .select()
309
- .from(memoryJobs)
310
- .where(eq(memoryJobs.type, "embed_chunk"))
311
- .all();
312
-
313
- // New embed_chunk jobs should have been enqueued for the changed content
314
- expect(jobsAfterSecond.length).toBeGreaterThan(firstChunkJobCount);
315
- });
316
-
317
- test("legacy memory_segments path remains intact", async () => {
318
- const conversationId = "conv-legacy-compat";
319
- const messageId = "msg-legacy-compat";
320
- const text =
321
- "I always prefer concise code reviews and I work in a distributed team.";
322
-
323
- seedConversationAndMessage(conversationId, messageId, text);
324
-
325
- const config = TEST_CONFIG.memory;
326
- const result = await indexMessageNow(
327
- {
328
- messageId,
329
- conversationId,
330
- role: "user",
331
- content: JSON.stringify([{ type: "text", text }]),
332
- createdAt: Date.now(),
333
- },
334
- config,
335
- );
336
-
337
- const db = getDb();
338
-
339
- // Legacy segments must be present and correctly formed
340
- const segments = db
341
- .select()
342
- .from(memorySegments)
343
- .where(eq(memorySegments.messageId, messageId))
344
- .all();
345
- expect(segments.length).toBe(result.indexedSegments);
346
-
347
- for (const seg of segments) {
348
- expect(seg.id.startsWith(messageId + ":")).toBe(true);
349
- expect(seg.conversationId).toBe(conversationId);
350
- expect(seg.role).toBe("user");
351
- expect(seg.text.length).toBeGreaterThan(0);
352
- expect(seg.contentHash).toBeTruthy();
353
- }
354
-
355
- // Legacy embed_segment jobs must be enqueued
356
- const segmentJobs = db
357
- .select()
358
- .from(memoryJobs)
359
- .where(eq(memoryJobs.type, "embed_segment"))
360
- .all();
361
- expect(segmentJobs.length).toBeGreaterThanOrEqual(1);
362
- });
363
-
364
- test("repeated indexing produces exactly one chunk per segment boundary", async () => {
365
- const conversationId = "conv-chunk-dedup";
366
- const messageId = "msg-chunk-dedup";
367
- const text =
368
- "I prefer TypeScript over plain JavaScript for large projects.";
369
-
370
- seedConversationAndMessage(conversationId, messageId, text);
371
-
372
- const config = TEST_CONFIG.memory;
373
- const content = JSON.stringify([{ type: "text", text }]);
374
-
375
- // Index the same message multiple times
376
- for (let i = 0; i < 5; i++) {
377
- await indexMessageNow(
378
- {
379
- messageId,
380
- conversationId,
381
- role: "user",
382
- content,
383
- createdAt: Date.now(),
384
- },
385
- config,
386
- );
387
- }
388
-
389
- const db = getDb();
390
- const observationId = `obs:${messageId}`;
391
-
392
- // Verify no duplicate chunks — one chunk per segment boundary
393
- const chunks = db
394
- .select()
395
- .from(memoryChunks)
396
- .where(eq(memoryChunks.observationId, observationId))
397
- .all();
398
- const segments = db
399
- .select()
400
- .from(memorySegments)
401
- .where(eq(memorySegments.messageId, messageId))
402
- .all();
403
-
404
- expect(chunks.length).toBe(segments.length);
405
-
406
- // Verify chunk IDs are unique
407
- const chunkIds = chunks.map((c) => c.id);
408
- const uniqueChunkIds = new Set(chunkIds);
409
- expect(uniqueChunkIds.size).toBe(chunkIds.length);
410
- });
411
-
412
- test("chunk dual-write respects custom scopeId", async () => {
413
- const conversationId = "conv-scope";
414
- const messageId = "msg-scope";
415
- const text = "Custom scoped message content.";
416
- const scopeId = "custom-scope-42";
417
-
418
- seedConversationAndMessage(conversationId, messageId, text);
419
-
420
- const config = TEST_CONFIG.memory;
421
- await indexMessageNow(
422
- {
423
- messageId,
424
- conversationId,
425
- role: "user",
426
- content: JSON.stringify([{ type: "text", text }]),
427
- createdAt: Date.now(),
428
- scopeId,
429
- },
430
- config,
431
- );
432
-
433
- const db = getDb();
434
- const observationId = `obs:${messageId}`;
435
-
436
- const observation = db
437
- .select()
438
- .from(memoryObservations)
439
- .where(eq(memoryObservations.id, observationId))
440
- .get();
441
- expect(observation!.scopeId).toBe(scopeId);
442
-
443
- const chunks = db
444
- .select()
445
- .from(memoryChunks)
446
- .where(eq(memoryChunks.observationId, observationId))
447
- .all();
448
- expect(chunks.length).toBeGreaterThanOrEqual(1);
449
- for (const chunk of chunks) {
450
- expect(chunk.scopeId).toBe(scopeId);
451
- }
452
- });
453
- });