@vellumai/assistant 0.8.8-dev.202606072033.0e97ff6 → 0.8.8-dev.202606072328.6710d73

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/openapi.yaml CHANGED
@@ -16027,6 +16027,8 @@ paths:
16027
16027
  type: array
16028
16028
  items:
16029
16029
  type: string
16030
+ clientMessageId:
16031
+ type: string
16030
16032
  role:
16031
16033
  type: string
16032
16034
  enum:
@@ -16995,6 +16997,12 @@ paths:
16995
16997
  type: string
16996
16998
  clientTimezone:
16997
16999
  type: string
17000
+ clientMessageId:
17001
+ type: string
17002
+ description:
17003
+ Client-generated idempotency nonce. Persisted on the row and echoed back on the message_echo event and the
17004
+ messages snapshot so the client can correlate its optimistic row by identity. Duplicate sends for
17005
+ the same (conversation, clientMessageId) are deduplicated server-side.
16998
17006
  inferenceProfile:
16999
17007
  anyOf:
17000
17008
  - type: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.8-dev.202606072033.0e97ff6",
3
+ "version": "0.8.8-dev.202606072328.6710d73",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -101,10 +101,14 @@ describe("AgentLoop — call-site precedence", () => {
101
101
 
102
102
  const { provider, lastConfig } = makePipeline("anthropic");
103
103
  const loop = new AgentLoop(provider, "system", {
104
+ conversationId: "test-conversation",
104
105
  config: { maxTokens: 64000 },
105
106
  });
106
107
 
107
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
108
+ await loop.run([userMessage], () => {}, {
109
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
110
+ callSite: "mainAgent",
111
+ });
108
112
 
109
113
  expect(lastConfig()!.max_tokens).toBe(4096);
110
114
  });
@@ -121,13 +125,17 @@ describe("AgentLoop — call-site precedence", () => {
121
125
 
122
126
  const { provider, lastConfig } = makePipeline("anthropic");
123
127
  const loop = new AgentLoop(provider, "system", {
128
+ conversationId: "test-conversation",
124
129
  config: {
125
130
  maxTokens: 64000,
126
131
  effort: "high",
127
132
  },
128
133
  });
129
134
 
130
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
135
+ await loop.run([userMessage], () => {}, {
136
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
137
+ callSite: "mainAgent",
138
+ });
131
139
 
132
140
  expect(lastConfig()!.effort).toBe("low");
133
141
  });
@@ -144,6 +152,7 @@ describe("AgentLoop — call-site precedence", () => {
144
152
 
145
153
  const { provider, lastConfig } = makePipeline("anthropic");
146
154
  const loop = new AgentLoop(provider, "system", {
155
+ conversationId: "test-conversation",
147
156
  config: {
148
157
  maxTokens: 64000,
149
158
  effort: "high",
@@ -153,7 +162,10 @@ describe("AgentLoop — call-site precedence", () => {
153
162
  },
154
163
  });
155
164
 
156
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
165
+ await loop.run([userMessage], () => {}, {
166
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
167
+ callSite: "mainAgent",
168
+ });
157
169
 
158
170
  expect(lastConfig()!.speed).toBe("fast");
159
171
  });
@@ -175,6 +187,7 @@ describe("AgentLoop — call-site precedence", () => {
175
187
 
176
188
  const { provider, lastConfig } = makePipeline("anthropic");
177
189
  const loop = new AgentLoop(provider, "system", {
190
+ conversationId: "test-conversation",
178
191
  config: {
179
192
  maxTokens: 64000,
180
193
  // Conversation default also has thinking on — without the fix, this
@@ -184,7 +197,10 @@ describe("AgentLoop — call-site precedence", () => {
184
197
  },
185
198
  });
186
199
 
187
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
200
+ await loop.run([userMessage], () => {}, {
201
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
202
+ callSite: "mainAgent",
203
+ });
188
204
 
189
205
  // Call-site override resolves `thinking.enabled: false`, so the
190
206
  // RetryProvider normalizer must send Anthropic's explicit disabled shape.
@@ -203,10 +219,14 @@ describe("AgentLoop — call-site precedence", () => {
203
219
 
204
220
  const { provider, lastConfig } = makePipeline("anthropic");
205
221
  const loop = new AgentLoop(provider, "system", {
222
+ conversationId: "test-conversation",
206
223
  config: { maxTokens: 64000 },
207
224
  });
208
225
 
209
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
226
+ await loop.run([userMessage], () => {}, {
227
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
228
+ callSite: "mainAgent",
229
+ });
210
230
 
211
231
  // Must be wire-format `{ type: "adaptive" }` so the Anthropic SDK's
212
232
  // `ThinkingConfigParam` accepts it. The schema-shape `{ enabled,
@@ -228,6 +248,7 @@ describe("AgentLoop — call-site precedence", () => {
228
248
 
229
249
  const { provider, lastConfig } = makePipeline("anthropic");
230
250
  const loop = new AgentLoop(provider, "system", {
251
+ conversationId: "test-conversation",
231
252
  config: {
232
253
  maxTokens: 64000,
233
254
  effort: "high",
@@ -236,7 +257,9 @@ describe("AgentLoop — call-site precedence", () => {
236
257
  },
237
258
  });
238
259
 
239
- await loop.run([userMessage], () => {});
260
+ await loop.run([userMessage], () => {}, {
261
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
262
+ });
240
263
 
241
264
  const config = lastConfig()!;
242
265
  expect(config.max_tokens).toBe(64000);
@@ -263,11 +286,15 @@ describe("AgentLoop — call-site precedence", () => {
263
286
  });
264
287
 
265
288
  const loop = new AgentLoop(provider, "system", {
289
+ conversationId: "test-conversation",
266
290
  config: { maxTokens: 64000 },
267
291
  resolveSystemPrompt: resolveSystemPrompt,
268
292
  });
269
293
 
270
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
294
+ await loop.run([userMessage], () => {}, {
295
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
296
+ callSite: "mainAgent",
297
+ });
271
298
 
272
299
  // Per-turn explicit value beats both the call-site (4096) and the
273
300
  // default (64000).
@@ -23,7 +23,7 @@ import type {
23
23
  } from "../agent/loop.js";
24
24
  import { AgentLoop, isMaxTokensStopReason } from "../agent/loop.js";
25
25
  import type { TrustContext } from "../daemon/trust-context.js";
26
- import type { PostCompactionHookInput } from "../plugins/defaults/memory-retrieval/hooks/post-compact.js";
26
+ import type { PostCompactContext } from "../plugins/defaults/memory-retrieval/hooks/post-compact.js";
27
27
  import type {
28
28
  Message,
29
29
  Provider,
@@ -35,18 +35,16 @@ import type {
35
35
  // The agent loop invokes the default post-compaction re-injection hook directly
36
36
  // when it compacts in place. Stub it so these unit tests can drive the
37
37
  // re-injection result without the daemon-level injector chain. Tests assign
38
- // `postCompactReinjectImpl` to observe the call or force a failure; when unset
38
+ // `postCompactImpl` to observe the call or force a failure; when unset
39
39
  // the hook is a no-op that returns the history it was handed.
40
- let postCompactReinjectImpl:
41
- | ((input: PostCompactionHookInput) => Promise<Message[]>)
40
+ let postCompactImpl:
41
+ | ((input: PostCompactContext) => Promise<Message[]>)
42
42
  | null = null;
43
43
  mock.module(
44
44
  "../plugins/defaults/memory-retrieval/hooks/post-compact.js",
45
45
  () => ({
46
- default: async (input: PostCompactionHookInput) => ({
47
- messages: postCompactReinjectImpl
48
- ? await postCompactReinjectImpl(input)
49
- : input.history,
46
+ default: async (input: PostCompactContext) => ({
47
+ messages: postCompactImpl ? await postCompactImpl(input) : input.history,
50
48
  }),
51
49
  }),
52
50
  );
@@ -165,12 +163,18 @@ describe("AgentLoop exit-reason instrumentation", () => {
165
163
 
166
164
  test("emits exit event exactly once with 'no_tool_calls' on plain text response", async () => {
167
165
  const { provider } = createMockProvider([textResponse("Hi there!")]);
168
- const loop = new AgentLoop(provider, "system prompt");
166
+ const loop = new AgentLoop(provider, "system prompt", {
167
+ conversationId: "test-conversation",
168
+ });
169
169
 
170
170
  const events: AgentEvent[] = [];
171
- await loop.run([userMessage], (e) => {
172
- events.push(e);
173
- });
171
+ await loop.run(
172
+ [userMessage],
173
+ (e) => {
174
+ events.push(e);
175
+ },
176
+ { trust: { sourceChannel: "vellum", trustClass: "unknown" } },
177
+ );
174
178
 
175
179
  expect(countExitEvents(events)).toBe(1);
176
180
  const exit = lastExitEvent(events);
@@ -179,12 +183,18 @@ describe("AgentLoop exit-reason instrumentation", () => {
179
183
 
180
184
  test("agent_loop_exit is the last event emitted", async () => {
181
185
  const { provider } = createMockProvider([textResponse("Hi there!")]);
182
- const loop = new AgentLoop(provider, "system prompt");
186
+ const loop = new AgentLoop(provider, "system prompt", {
187
+ conversationId: "test-conversation",
188
+ });
183
189
 
184
190
  const events: AgentEvent[] = [];
185
- await loop.run([userMessage], (e) => {
186
- events.push(e);
187
- });
191
+ await loop.run(
192
+ [userMessage],
193
+ (e) => {
194
+ events.push(e);
195
+ },
196
+ { trust: { sourceChannel: "vellum", trustClass: "unknown" } },
197
+ );
188
198
 
189
199
  expect(events.length).toBeGreaterThan(0);
190
200
  expect(events[events.length - 1].type).toBe("agent_loop_exit");
@@ -194,12 +204,18 @@ describe("AgentLoop exit-reason instrumentation", () => {
194
204
  const { provider } = createMockProvider([
195
205
  maxTokensResponse("Partial answer"),
196
206
  ]);
197
- const loop = new AgentLoop(provider, "system prompt");
207
+ const loop = new AgentLoop(provider, "system prompt", {
208
+ conversationId: "test-conversation",
209
+ });
198
210
 
199
211
  const events: AgentEvent[] = [];
200
- await loop.run([userMessage], (e) => {
201
- events.push(e);
202
- });
212
+ await loop.run(
213
+ [userMessage],
214
+ (e) => {
215
+ events.push(e);
216
+ },
217
+ { trust: { sourceChannel: "vellum", trustClass: "unknown" } },
218
+ );
203
219
 
204
220
  expect(events.map((e) => e.type)).toEqual([
205
221
  "llm_call_started",
@@ -230,13 +246,18 @@ describe("AgentLoop exit-reason instrumentation", () => {
230
246
  },
231
247
  ]);
232
248
  const loop = new AgentLoop(provider, "system prompt", {
249
+ conversationId: "test-conversation",
233
250
  tools: dummyTools,
234
251
  });
235
252
 
236
253
  const events: AgentEvent[] = [];
237
- const { history: result } = await loop.run([userMessage], (e) => {
238
- events.push(e);
239
- });
254
+ const { history: result } = await loop.run(
255
+ [userMessage],
256
+ (e) => {
257
+ events.push(e);
258
+ },
259
+ { trust: { sourceChannel: "vellum", trustClass: "unknown" } },
260
+ );
240
261
 
241
262
  expect(events.some((e) => e.type === "tool_use")).toBe(false);
242
263
  expect(lastExitEvent(events)?.reason).toBe("max_tokens_reached");
@@ -247,7 +268,9 @@ describe("AgentLoop exit-reason instrumentation", () => {
247
268
 
248
269
  test("emits 'aborted_pre_call' when signal is already aborted at run start", async () => {
249
270
  const { provider } = createMockProvider([textResponse("never sent")]);
250
- const loop = new AgentLoop(provider, "system prompt");
271
+ const loop = new AgentLoop(provider, "system prompt", {
272
+ conversationId: "test-conversation",
273
+ });
251
274
 
252
275
  const controller = new AbortController();
253
276
  controller.abort();
@@ -258,7 +281,10 @@ describe("AgentLoop exit-reason instrumentation", () => {
258
281
  (e) => {
259
282
  events.push(e);
260
283
  },
261
- { signal: controller.signal },
284
+ {
285
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
286
+ signal: controller.signal,
287
+ },
262
288
  );
263
289
 
264
290
  expect(countExitEvents(events)).toBe(1);
@@ -275,14 +301,19 @@ describe("AgentLoop exit-reason instrumentation", () => {
275
301
  yieldToUser: true,
276
302
  });
277
303
  const loop = new AgentLoop(provider, "system", {
304
+ conversationId: "test-conversation",
278
305
  tools: dummyTools,
279
306
  toolExecutor: toolExecutor,
280
307
  });
281
308
 
282
309
  const events: AgentEvent[] = [];
283
- await loop.run([userMessage], (e) => {
284
- events.push(e);
285
- });
310
+ await loop.run(
311
+ [userMessage],
312
+ (e) => {
313
+ events.push(e);
314
+ },
315
+ { trust: { sourceChannel: "vellum", trustClass: "unknown" } },
316
+ );
286
317
 
287
318
  expect(countExitEvents(events)).toBe(1);
288
319
  expect(lastExitEvent(events)?.reason).toBe("yield_to_user");
@@ -295,6 +326,7 @@ describe("AgentLoop exit-reason instrumentation", () => {
295
326
  ]);
296
327
  const toolExecutor = async () => ({ content: "ok", isError: false });
297
328
  const loop = new AgentLoop(provider, "system", {
329
+ conversationId: "test-conversation",
298
330
  tools: dummyTools,
299
331
  toolExecutor: toolExecutor,
300
332
  });
@@ -308,7 +340,10 @@ describe("AgentLoop exit-reason instrumentation", () => {
308
340
  (e) => {
309
341
  events.push(e);
310
342
  },
311
- { onCheckpoint },
343
+ {
344
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
345
+ onCheckpoint,
346
+ },
312
347
  );
313
348
 
314
349
  expect(countExitEvents(events)).toBe(0);
@@ -323,6 +358,7 @@ describe("AgentLoop exit-reason instrumentation", () => {
323
358
  ]);
324
359
  const toolExecutor = async () => ({ content: "ok", isError: false });
325
360
  const loop = new AgentLoop(provider, "system", {
361
+ conversationId: "test-conversation",
326
362
  tools: dummyTools,
327
363
  toolExecutor: toolExecutor,
328
364
  });
@@ -331,6 +367,7 @@ describe("AgentLoop exit-reason instrumentation", () => {
331
367
  // of the running history exceeds the mid-loop threshold.
332
368
  // WHEN the loop checkpoints after the tool results land
333
369
  const result = await loop.run([userMessage], () => {}, {
370
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
334
371
  resolveContextWindow: () => ({
335
372
  maxInputTokens: 10,
336
373
  overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
@@ -350,12 +387,14 @@ describe("AgentLoop exit-reason instrumentation", () => {
350
387
  ]);
351
388
  const toolExecutor = async () => ({ content: "ok", isError: false });
352
389
  const loop = new AgentLoop(provider, "system", {
390
+ conversationId: "test-conversation",
353
391
  tools: dummyTools,
354
392
  toolExecutor: toolExecutor,
355
393
  });
356
394
 
357
395
  // WHEN the loop runs to completion
358
396
  const result = await loop.run([userMessage], () => {}, {
397
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
359
398
  resolveContextWindow: () => ({
360
399
  maxInputTokens: 10,
361
400
  overflowRecovery: { enabled: false, safetyMarginRatio: 0 },
@@ -375,13 +414,14 @@ describe("AgentLoop exit-reason instrumentation", () => {
375
414
  ]);
376
415
  const toolExecutor = async () => ({ content: "ok", isError: false });
377
416
  const loop = new AgentLoop(provider, "system", {
417
+ conversationId: "test-conversation",
378
418
  tools: dummyTools,
379
419
  toolExecutor: toolExecutor,
380
420
  });
381
421
 
382
422
  let reinjected = false;
383
423
  const events: AgentEvent[] = [];
384
- postCompactReinjectImpl = async () => {
424
+ postCompactImpl = async () => {
385
425
  reinjected = true;
386
426
  return [userMessage];
387
427
  };
@@ -422,11 +462,12 @@ describe("AgentLoop exit-reason instrumentation", () => {
422
462
  ]);
423
463
  const toolExecutor = async () => ({ content: "ok", isError: false });
424
464
  const loop = new AgentLoop(provider, "system", {
465
+ conversationId: "test-conversation",
425
466
  tools: dummyTools,
426
467
  toolExecutor: toolExecutor,
427
468
  });
428
469
 
429
- postCompactReinjectImpl = async () => {
470
+ postCompactImpl = async () => {
430
471
  throw new Error(
431
472
  "post-compaction re-injection must not run when exhausted",
432
473
  );
@@ -456,12 +497,18 @@ describe("AgentLoop exit-reason instrumentation", () => {
456
497
  throw new Error("provider exploded");
457
498
  },
458
499
  };
459
- const loop = new AgentLoop(provider, "system prompt");
500
+ const loop = new AgentLoop(provider, "system prompt", {
501
+ conversationId: "test-conversation",
502
+ });
460
503
 
461
504
  const events: AgentEvent[] = [];
462
- await loop.run([userMessage], (e) => {
463
- events.push(e);
464
- });
505
+ await loop.run(
506
+ [userMessage],
507
+ (e) => {
508
+ events.push(e);
509
+ },
510
+ { trust: { sourceChannel: "vellum", trustClass: "unknown" } },
511
+ );
465
512
 
466
513
  expect(countExitEvents(events)).toBe(1);
467
514
  expect(lastExitEvent(events)?.reason).toBe("error");
@@ -480,14 +527,19 @@ describe("AgentLoop exit-reason instrumentation", () => {
480
527
  yieldToUser: true,
481
528
  });
482
529
  const loop = new AgentLoop(provider, "system", {
530
+ conversationId: "test-conversation",
483
531
  tools: dummyTools,
484
532
  toolExecutor: toolExecutor,
485
533
  });
486
534
 
487
535
  const events: AgentEvent[] = [];
488
- await loop.run([userMessage], (e) => {
489
- events.push(e);
490
- });
536
+ await loop.run(
537
+ [userMessage],
538
+ (e) => {
539
+ events.push(e);
540
+ },
541
+ { trust: { sourceChannel: "vellum", trustClass: "unknown" } },
542
+ );
491
543
 
492
544
  expect(countExitEvents(events)).toBe(1);
493
545
  });
@@ -504,6 +556,7 @@ describe("AgentLoop exit-reason instrumentation", () => {
504
556
  return { content: "ok", isError: false };
505
557
  };
506
558
  const loop = new AgentLoop(provider, "system", {
559
+ conversationId: "test-conversation",
507
560
  tools: dummyTools,
508
561
  toolExecutor: toolExecutor,
509
562
  });
@@ -514,7 +567,10 @@ describe("AgentLoop exit-reason instrumentation", () => {
514
567
  (e) => {
515
568
  events.push(e);
516
569
  },
517
- { signal: controller.signal },
570
+ {
571
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
572
+ signal: controller.signal,
573
+ },
518
574
  );
519
575
 
520
576
  expect(countExitEvents(events)).toBe(1);
@@ -105,13 +105,17 @@ describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () =>
105
105
  },
106
106
  ];
107
107
  const loop = new AgentLoop(provider, "system", {
108
+ conversationId: "test-conversation",
108
109
  config: { maxTokens: 1024 },
109
110
  tools: dummyTools,
110
111
  toolExecutor: async () => ({ content: "ok", isError: false }),
111
112
  });
112
113
 
113
114
  // WHEN the loop runs
114
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
115
+ await loop.run([userMessage], () => {}, {
116
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
117
+ callSite: "mainAgent",
118
+ });
115
119
 
116
120
  // THEN every send (initial + tool round-trip) carries the cache-anchor signal
117
121
  expect(configs()).toHaveLength(2);
@@ -127,11 +131,15 @@ describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () =>
127
131
  // AND a provider that records the config of each LLM call
128
132
  const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
129
133
  const loop = new AgentLoop(provider, "system", {
134
+ conversationId: "test-conversation",
130
135
  config: { maxTokens: 1024 },
131
136
  });
132
137
 
133
138
  // WHEN the loop runs
134
- await loop.run([userMessage], () => {}, { callSite: "mainAgent" });
139
+ await loop.run([userMessage], () => {}, {
140
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
141
+ callSite: "mainAgent",
142
+ });
135
143
 
136
144
  // THEN the field is omitted entirely, not carried as false/undefined
137
145
  expect(configs()).toHaveLength(1);
@@ -94,9 +94,13 @@ describe("agent loop output hooks", () => {
94
94
  },
95
95
  });
96
96
  const { provider } = createMockProvider([textResponse("my secret value")]);
97
- const loop = new AgentLoop(provider, "system");
97
+ const loop = new AgentLoop(provider, "system", {
98
+ conversationId: "test-conversation",
99
+ });
98
100
  const events: AgentEvent[] = [];
99
- const { history } = await loop.run([userMessage], collect(events));
101
+ const { history } = await loop.run([userMessage], collect(events), {
102
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
103
+ });
100
104
  expect(textOf(lastAssistant(history).content)).toBe("my [redacted] value");
101
105
  });
102
106
 
@@ -110,9 +114,13 @@ describe("agent loop output hooks", () => {
110
114
  },
111
115
  });
112
116
  const { provider } = createMockProvider([textResponse("my secret value")]);
113
- const loop = new AgentLoop(provider, "system");
117
+ const loop = new AgentLoop(provider, "system", {
118
+ conversationId: "test-conversation",
119
+ });
114
120
  const events: AgentEvent[] = [];
115
- const { history } = await loop.run([userMessage], collect(events));
121
+ const { history } = await loop.run([userMessage], collect(events), {
122
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
123
+ });
116
124
  // Her real words never streamed; only the transformed text did, once.
117
125
  expect(streamedText(events)).not.toContain("secret");
118
126
  expect(streamedText(events)).toBe("[filtered]");
@@ -126,9 +134,13 @@ describe("agent loop output hooks", () => {
126
134
  },
127
135
  });
128
136
  const { provider } = createMockProvider([textResponse("live text")]);
129
- const loop = new AgentLoop(provider, "system");
137
+ const loop = new AgentLoop(provider, "system", {
138
+ conversationId: "test-conversation",
139
+ });
130
140
  const events: AgentEvent[] = [];
131
- const { history } = await loop.run([userMessage], collect(events));
141
+ const { history } = await loop.run([userMessage], collect(events), {
142
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
143
+ });
132
144
  expect(streamedText(events)).toBe("live text"); // streamed live, untransformed
133
145
  expect(textOf(lastAssistant(history).content)).toBe("[stored]"); // storage transformed
134
146
  });
@@ -155,6 +167,7 @@ describe("agent loop output hooks", () => {
155
167
  textResponse("done"),
156
168
  ]);
157
169
  const loop = new AgentLoop(provider, "system", {
170
+ conversationId: "test-conversation",
158
171
  tools: [
159
172
  {
160
173
  name: "noop",
@@ -164,7 +177,9 @@ describe("agent loop output hooks", () => {
164
177
  ],
165
178
  toolExecutor: async () => ({ content: "ok", isError: false }),
166
179
  });
167
- const { history } = await loop.run([userMessage], collect([]));
180
+ const { history } = await loop.run([userMessage], collect([]), {
181
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
182
+ });
168
183
  const toolTurn = history.find(
169
184
  (m) =>
170
185
  m.role === "assistant" && m.content.some((b) => b.type === "tool_use"),
@@ -187,8 +202,12 @@ describe("agent loop output hooks", () => {
187
202
  },
188
203
  });
189
204
  const { provider, calls } = createMockProvider([textResponse("hi")]);
190
- const loop = new AgentLoop(provider, "base prompt");
191
- await loop.run([userMessage], collect([]));
205
+ const loop = new AgentLoop(provider, "base prompt", {
206
+ conversationId: "test-conversation",
207
+ });
208
+ await loop.run([userMessage], collect([]), {
209
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
210
+ });
192
211
  expect(calls[0].systemPrompt).toContain("[EDITED]");
193
212
  });
194
213
 
@@ -204,8 +223,12 @@ describe("agent loop output hooks", () => {
204
223
  },
205
224
  });
206
225
  const { provider } = createMockProvider([textResponse("untouched")]);
207
- const loop = new AgentLoop(provider, "system");
208
- const { history } = await loop.run([userMessage], collect([]));
226
+ const loop = new AgentLoop(provider, "system", {
227
+ conversationId: "test-conversation",
228
+ });
229
+ const { history } = await loop.run([userMessage], collect([]), {
230
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
231
+ });
209
232
  const finalContent = lastAssistant(history).content;
210
233
  expect(textOf(finalContent)).toBe("untouched");
211
234
  expect(
@@ -236,9 +259,13 @@ describe("agent loop output hooks", () => {
236
259
  stopReason: "max_tokens",
237
260
  };
238
261
  const { provider } = createMockProvider([truncated]);
239
- const loop = new AgentLoop(provider, "system");
262
+ const loop = new AgentLoop(provider, "system", {
263
+ conversationId: "test-conversation",
264
+ });
240
265
  const events: AgentEvent[] = [];
241
- const { history } = await loop.run([userMessage], collect(events));
266
+ const { history } = await loop.run([userMessage], collect(events), {
267
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
268
+ });
242
269
  expect(seen.calls).toBe(1);
243
270
  expect(textOf(lastAssistant(history).content)).toBe("PARTIAL ANSWER");
244
271
  // Real stream suppressed; transformed final text emitted once.
@@ -268,6 +295,7 @@ describe("agent loop output hooks", () => {
268
295
  ];
269
296
  const { provider } = createMockProvider(toolThenText);
270
297
  const loop = new AgentLoop(provider, "system", {
298
+ conversationId: "test-conversation",
271
299
  tools: [
272
300
  {
273
301
  name: "issue",
@@ -282,7 +310,9 @@ describe("agent loop output hooks", () => {
282
310
  }),
283
311
  });
284
312
  const events: AgentEvent[] = [];
285
- const { history } = await loop.run([userMessage], collect(events));
313
+ const { history } = await loop.run([userMessage], collect(events), {
314
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
315
+ });
286
316
  // Persisted message keeps the placeholder — model must never see real
287
317
  // values on reload.
288
318
  expect(textOf(lastAssistant(history).content)).toContain(placeholder);
@@ -111,12 +111,14 @@ describe("AgentLoop.run — overrideProfile plumbing", () => {
111
111
  ) => ({ content: "ok", isError: false });
112
112
 
113
113
  const loop = new AgentLoop(provider, "system", {
114
+ conversationId: "test-conversation",
114
115
  config: { maxTokens: 1024 },
115
116
  tools: dummyTools,
116
117
  toolExecutor: toolExecutor,
117
118
  });
118
119
 
119
120
  await loop.run([userMessage], () => {}, {
121
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
120
122
  callSite: "mainAgent",
121
123
  overrideProfile: "fast",
122
124
  });
@@ -131,10 +133,13 @@ describe("AgentLoop.run — overrideProfile plumbing", () => {
131
133
  test("omits overrideProfile from providerConfig when unset (default behavior unchanged)", async () => {
132
134
  const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
133
135
  const loop = new AgentLoop(provider, "system", {
136
+ conversationId: "test-conversation",
134
137
  config: { maxTokens: 1024 },
135
138
  });
136
139
 
137
- await loop.run([userMessage], () => {});
140
+ await loop.run([userMessage], () => {}, {
141
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
142
+ });
138
143
 
139
144
  // Single send, no overrideProfile field at all.
140
145
  expect(configs()).toHaveLength(1);
@@ -149,10 +154,12 @@ describe("AgentLoop.run — overrideProfile plumbing", () => {
149
154
  // provider layer (covered by provider-send-message-override-profile.test.ts).
150
155
  const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
151
156
  const loop = new AgentLoop(provider, "system", {
157
+ conversationId: "test-conversation",
152
158
  config: { maxTokens: 1024 },
153
159
  });
154
160
 
155
161
  await loop.run([userMessage], () => {}, {
162
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
156
163
  callSite: "mainAgent",
157
164
  overrideProfile: "does-not-exist",
158
165
  });