@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,370 +0,0 @@
1
- /**
2
- * Tests for episode archive store insertion helpers and embed_episode job dispatch.
3
- *
4
- * Verifies:
5
- * - Episode rows can be inserted via compaction and resolution helpers
6
- * - embed_episode jobs are enqueued on insertion
7
- * - embed_episode dispatches correctly through the jobs worker
8
- */
9
- import { mkdtempSync, rmSync } from "node:fs";
10
- import { tmpdir } from "node:os";
11
- import { join } from "node:path";
12
- import {
13
- afterAll,
14
- beforeAll,
15
- beforeEach,
16
- describe,
17
- expect,
18
- mock,
19
- test,
20
- } from "bun:test";
21
-
22
- import { eq } from "drizzle-orm";
23
-
24
- import { DEFAULT_CONFIG } from "../config/defaults.js";
25
-
26
- const testDir = mkdtempSync(join(tmpdir(), "memory-episode-archive-"));
27
-
28
- mock.module("../util/platform.js", () => ({
29
- getDataDir: () => testDir,
30
- isMacOS: () => process.platform === "darwin",
31
- isLinux: () => process.platform === "linux",
32
- isWindows: () => process.platform === "win32",
33
- getPidPath: () => join(testDir, "test.pid"),
34
- getDbPath: () => join(testDir, "test.db"),
35
- getLogPath: () => join(testDir, "test.log"),
36
- ensureDataDir: () => {},
37
- }));
38
-
39
- mock.module("../util/logger.js", () => ({
40
- getLogger: () =>
41
- new Proxy({} as Record<string, unknown>, {
42
- get: () => () => {},
43
- }),
44
- }));
45
-
46
- // Track embedAndUpsert calls to verify embedding dispatch without needing a real backend
47
- const embedCalls: Array<{
48
- targetType: string;
49
- targetId: string;
50
- text: string;
51
- extraPayload: Record<string, unknown>;
52
- }> = [];
53
-
54
- // eslint-disable-next-line @typescript-eslint/no-require-imports
55
- const realJobUtils = require("../memory/job-utils.js");
56
- mock.module("../memory/job-utils.js", () => ({
57
- ...realJobUtils,
58
- embedAndUpsert: async (
59
- _config: unknown,
60
- targetType: string,
61
- targetId: string,
62
- text: string,
63
- extraPayload: Record<string, unknown>,
64
- ) => {
65
- embedCalls.push({ targetType, targetId, text, extraPayload });
66
- },
67
- }));
68
-
69
- mock.module("../memory/qdrant-client.js", () => ({
70
- getQdrantClient: () => ({
71
- searchWithFilter: async () => [],
72
- hybridSearch: async () => [],
73
- upsertPoints: async () => {},
74
- deletePoints: async () => {},
75
- }),
76
- initQdrantClient: () => {},
77
- }));
78
-
79
- const TEST_CONFIG = {
80
- ...DEFAULT_CONFIG,
81
- memory: {
82
- ...DEFAULT_CONFIG.memory,
83
- embeddings: {
84
- ...DEFAULT_CONFIG.memory.embeddings,
85
- provider: "openai" as const,
86
- required: false,
87
- },
88
- extraction: {
89
- ...DEFAULT_CONFIG.memory.extraction,
90
- useLLM: false,
91
- },
92
- },
93
- };
94
-
95
- mock.module("../config/loader.js", () => ({
96
- loadConfig: () => TEST_CONFIG,
97
- getConfig: () => TEST_CONFIG,
98
- loadRawConfig: () => ({}),
99
- saveRawConfig: () => {},
100
- invalidateConfigCache: () => {},
101
- }));
102
-
103
- import {
104
- insertCompactionEpisode,
105
- insertResolutionEpisode,
106
- } from "../memory/archive-store.js";
107
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
108
- import { claimMemoryJobs, enqueueMemoryJob } from "../memory/jobs-store.js";
109
- import { runMemoryJobsOnce } from "../memory/jobs-worker.js";
110
- import { conversations, memoryEpisodes, memoryJobs } from "../memory/schema.js";
111
-
112
- describe("episode archive store", () => {
113
- const now = 1_710_000_000_000;
114
- const convId = "conv-episode-test";
115
-
116
- beforeAll(() => {
117
- initializeDb();
118
- });
119
-
120
- beforeEach(() => {
121
- const db = getDb();
122
- db.delete(memoryJobs).run();
123
- db.delete(memoryEpisodes).run();
124
- db.delete(conversations).run();
125
- embedCalls.length = 0;
126
-
127
- // Seed a conversation for FK references
128
- db.insert(conversations)
129
- .values({
130
- id: convId,
131
- title: null,
132
- createdAt: now,
133
- updatedAt: now,
134
- totalInputTokens: 0,
135
- totalOutputTokens: 0,
136
- totalEstimatedCost: 0,
137
- contextSummary: null,
138
- contextCompactedMessageCount: 0,
139
- })
140
- .run();
141
- });
142
-
143
- afterAll(() => {
144
- resetDb();
145
- try {
146
- rmSync(testDir, { recursive: true });
147
- } catch {
148
- // best effort
149
- }
150
- });
151
-
152
- // ── Insertion helpers ───────────────────────────────────────────────
153
-
154
- test("insertCompactionEpisode inserts an episode row", () => {
155
- const { episodeId } = insertCompactionEpisode({
156
- conversationId: convId,
157
- title: "Morning standup discussion",
158
- summary: "User discussed project blockers and timeline updates",
159
- tokenEstimate: 42,
160
- source: "vellum",
161
- startAt: now - 60_000,
162
- endAt: now,
163
- });
164
-
165
- const db = getDb();
166
- const episode = db
167
- .select()
168
- .from(memoryEpisodes)
169
- .where(eq(memoryEpisodes.id, episodeId))
170
- .get();
171
-
172
- expect(episode).not.toBeNull();
173
- expect(episode!.conversationId).toBe(convId);
174
- expect(episode!.title).toBe("Morning standup discussion");
175
- expect(episode!.summary).toBe(
176
- "User discussed project blockers and timeline updates",
177
- );
178
- expect(episode!.tokenEstimate).toBe(42);
179
- expect(episode!.source).toBe("vellum");
180
- expect(episode!.startAt).toBe(now - 60_000);
181
- expect(episode!.endAt).toBe(now);
182
- expect(episode!.scopeId).toBe("default");
183
- });
184
-
185
- test("insertResolutionEpisode inserts an episode row", () => {
186
- const { episodeId } = insertResolutionEpisode({
187
- conversationId: convId,
188
- title: "Full conversation summary",
189
- summary: "A complete discussion about project architecture decisions",
190
- tokenEstimate: 100,
191
- startAt: now - 3_600_000,
192
- endAt: now,
193
- });
194
-
195
- const db = getDb();
196
- const episode = db
197
- .select()
198
- .from(memoryEpisodes)
199
- .where(eq(memoryEpisodes.id, episodeId))
200
- .get();
201
-
202
- expect(episode).not.toBeNull();
203
- expect(episode!.title).toBe("Full conversation summary");
204
- expect(episode!.source).toBeNull();
205
- });
206
-
207
- test("insertCompactionEpisode respects custom scopeId", () => {
208
- const { episodeId } = insertCompactionEpisode({
209
- scopeId: "project-alpha",
210
- conversationId: convId,
211
- title: "Scoped episode",
212
- summary: "Testing scope assignment",
213
- tokenEstimate: 10,
214
- startAt: now,
215
- endAt: now,
216
- });
217
-
218
- const db = getDb();
219
- const episode = db
220
- .select()
221
- .from(memoryEpisodes)
222
- .where(eq(memoryEpisodes.id, episodeId))
223
- .get();
224
-
225
- expect(episode!.scopeId).toBe("project-alpha");
226
- });
227
-
228
- // ── Job enqueue ──────────────────────────────────────────────────
229
-
230
- test("insertCompactionEpisode enqueues an embed_episode job", () => {
231
- const { episodeId, jobId } = insertCompactionEpisode({
232
- conversationId: convId,
233
- title: "Job test",
234
- summary: "Verifying job enqueue",
235
- tokenEstimate: 5,
236
- startAt: now,
237
- endAt: now,
238
- });
239
-
240
- expect(jobId).toBeTruthy();
241
-
242
- const db = getDb();
243
- const job = db
244
- .select()
245
- .from(memoryJobs)
246
- .where(eq(memoryJobs.id, jobId))
247
- .get();
248
-
249
- expect(job).not.toBeNull();
250
- expect(job!.type).toBe("embed_episode");
251
- expect(job!.status).toBe("pending");
252
-
253
- const payload = JSON.parse(job!.payload);
254
- expect(payload.episodeId).toBe(episodeId);
255
- });
256
-
257
- test("insertResolutionEpisode enqueues an embed_episode job", () => {
258
- const { episodeId, jobId } = insertResolutionEpisode({
259
- conversationId: convId,
260
- title: "Resolution job test",
261
- summary: "Verifying resolution job enqueue",
262
- tokenEstimate: 8,
263
- startAt: now - 1000,
264
- endAt: now,
265
- });
266
-
267
- expect(jobId).toBeTruthy();
268
-
269
- const db = getDb();
270
- const job = db
271
- .select()
272
- .from(memoryJobs)
273
- .where(eq(memoryJobs.id, jobId))
274
- .get();
275
-
276
- expect(job).not.toBeNull();
277
- expect(job!.type).toBe("embed_episode");
278
-
279
- const payload = JSON.parse(job!.payload);
280
- expect(payload.episodeId).toBe(episodeId);
281
- });
282
-
283
- // ── Worker dispatch ──────────────────────────────────────────────
284
-
285
- test("embed_episode jobs are claimed and dispatched through the worker", async () => {
286
- const { episodeId } = insertCompactionEpisode({
287
- conversationId: convId,
288
- title: "Worker dispatch test",
289
- summary: "Verifying worker dispatches embed_episode correctly",
290
- tokenEstimate: 15,
291
- source: "telegram",
292
- startAt: now - 30_000,
293
- endAt: now,
294
- });
295
-
296
- const processed = await runMemoryJobsOnce();
297
- expect(processed).toBe(1);
298
-
299
- // The mock embedAndUpsert should have been called with "episode" targetType
300
- expect(embedCalls.length).toBe(1);
301
- expect(embedCalls[0]!.targetType).toBe("episode");
302
- expect(embedCalls[0]!.targetId).toBe(episodeId);
303
- expect(embedCalls[0]!.text).toContain("[episode]");
304
- expect(embedCalls[0]!.text).toContain("Worker dispatch test");
305
- expect(embedCalls[0]!.extraPayload.conversation_id).toBe(convId);
306
- expect(embedCalls[0]!.extraPayload.memory_scope_id).toBe("default");
307
- });
308
-
309
- test("embed_episode job is classified as an embed job type for scheduling priority", () => {
310
- // Verify that embed_episode jobs are claimed alongside other embed jobs
311
- // by enqueuing both an embed_episode and checking claim behavior
312
- enqueueMemoryJob("embed_episode", { episodeId: "nonexistent" });
313
-
314
- const jobs = claimMemoryJobs(10);
315
- expect(jobs.length).toBe(1);
316
- expect(jobs[0]!.type).toBe("embed_episode");
317
- });
318
-
319
- // ── Multiple episodes per conversation ────────────────────────────
320
-
321
- test("multiple episodes can be inserted for the same conversation", () => {
322
- const { episodeId: ep1 } = insertCompactionEpisode({
323
- conversationId: convId,
324
- title: "First compaction",
325
- summary: "First block of turns",
326
- tokenEstimate: 20,
327
- startAt: now - 120_000,
328
- endAt: now - 60_000,
329
- });
330
-
331
- const { episodeId: ep2 } = insertCompactionEpisode({
332
- conversationId: convId,
333
- title: "Second compaction",
334
- summary: "Second block of turns",
335
- tokenEstimate: 25,
336
- startAt: now - 60_000,
337
- endAt: now,
338
- });
339
-
340
- const { episodeId: ep3 } = insertResolutionEpisode({
341
- conversationId: convId,
342
- title: "Final resolution",
343
- summary: "Full conversation narrative",
344
- tokenEstimate: 50,
345
- startAt: now - 120_000,
346
- endAt: now,
347
- });
348
-
349
- expect(ep1).not.toBe(ep2);
350
- expect(ep2).not.toBe(ep3);
351
-
352
- const db = getDb();
353
- const episodes = db
354
- .select()
355
- .from(memoryEpisodes)
356
- .where(eq(memoryEpisodes.conversationId, convId))
357
- .all();
358
-
359
- expect(episodes.length).toBe(3);
360
-
361
- // All three should have enqueued embed jobs
362
- const jobs = db
363
- .select()
364
- .from(memoryJobs)
365
- .where(eq(memoryJobs.type, "embed_episode"))
366
- .all();
367
-
368
- expect(jobs.length).toBe(3);
369
- });
370
- });