@vellumai/assistant 0.8.8-dev.202606071835.08695c1 → 0.8.8-dev.202606072033.0e97ff6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.8-dev.202606071835.08695c1",
3
+ "version": "0.8.8-dev.202606072033.0e97ff6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -22,8 +22,8 @@ import type {
22
22
  CheckpointInfo,
23
23
  } from "../agent/loop.js";
24
24
  import { AgentLoop, isMaxTokensStopReason } from "../agent/loop.js";
25
+ import type { TrustContext } from "../daemon/trust-context.js";
25
26
  import type { PostCompactionHookInput } from "../plugins/defaults/memory-retrieval/hooks/post-compact.js";
26
- import type { TurnContext } from "../plugins/types.js";
27
27
  import type {
28
28
  Message,
29
29
  Provider,
@@ -120,21 +120,16 @@ const userMessage: Message = {
120
120
  content: [{ type: "text", text: "Hello" }],
121
121
  };
122
122
 
123
- // A turn context plus a context-window manager whose `maybeCompact` returns a
123
+ // A trust snapshot plus a context-window manager whose `maybeCompact` returns a
124
124
  // canned result, so the loop's compaction call runs without the real
125
- // orchestrator machinery. The manager is supplied to the loop as a dedicated
126
- // run option rather than riding on the turn context.
125
+ // orchestrator machinery. Both are supplied to the loop as dedicated run
126
+ // options.
127
127
  function fakeCompaction(result: { compacted: boolean; exhausted: boolean }): {
128
- turnContext: TurnContext;
128
+ trust: TrustContext;
129
129
  contextWindowManager: AgentLoopRunOptions["contextWindowManager"];
130
130
  } {
131
131
  return {
132
- turnContext: {
133
- requestId: "req-compact",
134
- conversationId: "conv-compact",
135
- turnIndex: 0,
136
- trust: { sourceChannel: "vellum", trustClass: "unknown" },
137
- },
132
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
138
133
  contextWindowManager: {
139
134
  maybeCompact: async () => result,
140
135
  } as unknown as AgentLoopRunOptions["contextWindowManager"],
@@ -190,7 +190,6 @@ let mockCompactResult: ContextWindowResult = {
190
190
  mock.module("../context/window-manager.js", () => ({
191
191
  ContextWindowManager: class {
192
192
  nonPersistedPrefixCount = 0;
193
- summaryIsInjected = false;
194
193
  constructor() {}
195
194
  updateConfig() {}
196
195
  shouldCompact() {
@@ -53,6 +53,10 @@ mock.module("../memory/pkb/pkb-search.js", () => ({
53
53
  // `buildUnifiedTurnContextBlock` expectations below. The rest of the module
54
54
  // (timezone canonicalization/resolution) keeps its real behavior.
55
55
  const FIXED_TURN_TIMESTAMP = "2026-04-02T12:00:00Z";
56
+ // Conversation id the runtime-injection tests register their live conversation
57
+ // under; passed explicitly now that `applyRuntimeInjections` requires the
58
+ // caller to name the conversation it resolves through.
59
+ const FALLBACK_CONVERSATION_ID = "runtime-assembly-fallback";
56
60
  const realDateContextForAssemblyTest =
57
61
  await import("../daemon/date-context.js");
58
62
  mock.module("../daemon/date-context.js", () => ({
@@ -695,7 +699,9 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
695
699
  };
696
700
 
697
701
  seedChannelCapabilitiesConversation(caps);
698
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
702
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
703
+ conversationId: FALLBACK_CONVERSATION_ID,
704
+ });
699
705
 
700
706
  expect(result.length).toBe(1);
701
707
  expect(result[0].content.length).toBe(2);
@@ -707,14 +713,18 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
707
713
 
708
714
  test("does not inject when channelCapabilities is null", async () => {
709
715
  seedChannelCapabilitiesConversation(null);
710
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
716
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
717
+ conversationId: FALLBACK_CONVERSATION_ID,
718
+ });
711
719
 
712
720
  expect(result.length).toBe(1);
713
721
  expect(result[0].content.length).toBe(1);
714
722
  });
715
723
 
716
724
  test("does not inject when channelCapabilities is omitted", async () => {
717
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
725
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
726
+ conversationId: FALLBACK_CONVERSATION_ID,
727
+ });
718
728
 
719
729
  expect(result.length).toBe(1);
720
730
  expect(result[0].content.length).toBe(1);
@@ -729,7 +739,9 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
729
739
  };
730
740
 
731
741
  seedChannelCapabilitiesConversation(caps);
732
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
742
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
743
+ conversationId: FALLBACK_CONVERSATION_ID,
744
+ });
733
745
 
734
746
  expect(result.length).toBe(1);
735
747
  // channelCapabilities prepends
@@ -1007,20 +1019,17 @@ describe("applyRuntimeInjections — injection mode", () => {
1007
1019
  expect(texts).toContain("Hello");
1008
1020
  });
1009
1021
 
1010
- test("post-compaction re-injection resolves the live conversation from the turn context", async () => {
1022
+ test("post-compaction re-injection resolves the live conversation from the conversation id", async () => {
1011
1023
  // GIVEN the seeded live conversation `injection-mode-conv` whose
1012
1024
  // conversation-scoped blocks (`<active_workspace>`, `<channel_capabilities>`)
1013
1025
  // only resolve when `applyRuntimeInjections` finds it by conversation id
1014
- // AND the agent loop hands the post-compaction hook its per-turn context as
1015
- // a single nested `turnContext` (its own working-state unit)
1026
+ // AND the agent loop hands the post-compaction hook the turn-identity fields
1027
+ // flat (`requestId`, `conversationId`, `trust`)
1016
1028
  const { messages: result } = await postCompactReinject({
1017
1029
  history: baseMessages,
1018
- turnContext: {
1019
- requestId: "reinject-req",
1020
- conversationId: "injection-mode-conv",
1021
- turnIndex: 0,
1022
- trust: { sourceChannel: "vellum", trustClass: "guardian" },
1023
- },
1030
+ requestId: "reinject-req",
1031
+ conversationId: "injection-mode-conv",
1032
+ trust: { sourceChannel: "vellum", trustClass: "guardian" },
1024
1033
  isNonInteractive: false,
1025
1034
  mode: "full",
1026
1035
  modelProfile: null,
@@ -1031,9 +1040,9 @@ describe("applyRuntimeInjections — injection mode", () => {
1031
1040
  .map((b) => b.text)
1032
1041
  .join("\n");
1033
1042
 
1034
- // THEN the hook unnests `turnContext.conversationId` onto the flat injection
1035
- // options, so the re-injection resolves the same live conversation as the
1036
- // turn's initial assembly rather than the synthetic fallback.
1043
+ // THEN the hook forwards `conversationId` onto the flat injection options,
1044
+ // so the re-injection resolves the same live conversation as the turn's
1045
+ // initial assembly rather than the synthetic fallback.
1037
1046
  expect(allText).toContain("<active_workspace>");
1038
1047
  expect(allText).toContain("<channel_capabilities>");
1039
1048
  });
@@ -1376,7 +1385,9 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1376
1385
 
1377
1386
  test("injects NOW.md block when the file has content", async () => {
1378
1387
  seedNowScratchpad("Current focus: fix the bug");
1379
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1388
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
1389
+ conversationId: FALLBACK_CONVERSATION_ID,
1390
+ });
1380
1391
 
1381
1392
  expect(result.length).toBe(1);
1382
1393
  expect(result[0].content.length).toBe(2);
@@ -1388,7 +1399,9 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1388
1399
 
1389
1400
  test("scratchpad appears before user's original text content", async () => {
1390
1401
  seedNowScratchpad("scratchpad notes");
1391
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1402
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
1403
+ conversationId: FALLBACK_CONVERSATION_ID,
1404
+ });
1392
1405
 
1393
1406
  // Scratchpad comes first (before user content)
1394
1407
  expect(
@@ -1401,7 +1414,9 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1401
1414
  });
1402
1415
 
1403
1416
  test("does not inject when the NOW.md file is absent", async () => {
1404
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1417
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
1418
+ conversationId: FALLBACK_CONVERSATION_ID,
1419
+ });
1405
1420
 
1406
1421
  expect(result.length).toBe(1);
1407
1422
  expect(result[0].content.length).toBe(1);
@@ -1410,6 +1425,7 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1410
1425
  test("skipped in minimal mode", async () => {
1411
1426
  seedNowScratchpad("Current focus: fix the bug");
1412
1427
  const { messages: result } = await applyRuntimeInjections(baseMessages, {
1428
+ conversationId: FALLBACK_CONVERSATION_ID,
1413
1429
  mode: "minimal",
1414
1430
  });
1415
1431
 
@@ -1876,7 +1892,9 @@ describe("applyRuntimeInjections with unifiedTurnContext", () => {
1876
1892
  test("injects the turn-context block when the inputs are provided", async () => {
1877
1893
  seedTemporalSnapshot();
1878
1894
 
1879
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1895
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
1896
+ conversationId: FALLBACK_CONVERSATION_ID,
1897
+ });
1880
1898
 
1881
1899
  expect(result).toHaveLength(1);
1882
1900
  expect(result[0].content).toHaveLength(2);
@@ -1890,7 +1908,9 @@ describe("applyRuntimeInjections with unifiedTurnContext", () => {
1890
1908
  });
1891
1909
 
1892
1910
  test("does not inject when the timestamp snapshot is absent", async () => {
1893
- const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1911
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {
1912
+ conversationId: FALLBACK_CONVERSATION_ID,
1913
+ });
1894
1914
 
1895
1915
  expect(result).toHaveLength(1);
1896
1916
  expect(result[0].content).toHaveLength(1);
@@ -1900,6 +1920,7 @@ describe("applyRuntimeInjections with unifiedTurnContext", () => {
1900
1920
  seedTemporalSnapshot();
1901
1921
 
1902
1922
  const { messages: result } = await applyRuntimeInjections(baseMessages, {
1923
+ conversationId: FALLBACK_CONVERSATION_ID,
1903
1924
  ...sampleOptions,
1904
1925
  mode: "full",
1905
1926
  });
@@ -1916,6 +1937,7 @@ describe("applyRuntimeInjections with unifiedTurnContext", () => {
1916
1937
  seedTemporalSnapshot();
1917
1938
 
1918
1939
  const { messages: result } = await applyRuntimeInjections(baseMessages, {
1940
+ conversationId: FALLBACK_CONVERSATION_ID,
1919
1941
  ...sampleOptions,
1920
1942
  mode: "minimal",
1921
1943
  });
@@ -1990,7 +2012,9 @@ describe("applyRuntimeInjections timezone resolution", () => {
1990
2012
  seedTemporalSnapshot(null);
1991
2013
 
1992
2014
  // WHEN a turn is assembled
1993
- const result = await applyRuntimeInjections(baseMessages, {});
2015
+ const result = await applyRuntimeInjections(baseMessages, {
2016
+ conversationId: FALLBACK_CONVERSATION_ID,
2017
+ });
1994
2018
 
1995
2019
  // THEN the injector surfaces the mismatch using the config-sourced values
1996
2020
  const injected = injectedText(result);
@@ -2012,7 +2036,9 @@ describe("applyRuntimeInjections timezone resolution", () => {
2012
2036
  seedTemporalSnapshot("America/Los_Angeles");
2013
2037
 
2014
2038
  // WHEN a turn is assembled
2015
- const result = await applyRuntimeInjections(baseMessages, {});
2039
+ const result = await applyRuntimeInjections(baseMessages, {
2040
+ conversationId: FALLBACK_CONVERSATION_ID,
2041
+ });
2016
2042
 
2017
2043
  // THEN the injector surfaces the mismatch using the snapshot's client value
2018
2044
  const injected = injectedText(result);
@@ -2032,7 +2058,9 @@ describe("applyRuntimeInjections timezone resolution", () => {
2032
2058
  seedTemporalSnapshot("America/New_York");
2033
2059
 
2034
2060
  // WHEN a turn is assembled
2035
- const result = await applyRuntimeInjections(baseMessages, {});
2061
+ const result = await applyRuntimeInjections(baseMessages, {
2062
+ conversationId: FALLBACK_CONVERSATION_ID,
2063
+ });
2036
2064
 
2037
2065
  // THEN no timezone-mismatch lines are injected
2038
2066
  const injected = injectedText(result);
@@ -2050,7 +2078,9 @@ describe("applyRuntimeInjections timezone resolution", () => {
2050
2078
  seedTemporalSnapshot(null, "2d ago");
2051
2079
 
2052
2080
  // WHEN a turn is assembled without the gap in the options bag
2053
- const result = await applyRuntimeInjections(baseMessages, {});
2081
+ const result = await applyRuntimeInjections(baseMessages, {
2082
+ conversationId: FALLBACK_CONVERSATION_ID,
2083
+ });
2054
2084
 
2055
2085
  // THEN the injector surfaces the gap from the snapshot
2056
2086
  const injected = injectedText(result);
@@ -2066,7 +2096,9 @@ describe("applyRuntimeInjections timezone resolution", () => {
2066
2096
  seedTemporalSnapshot(null, null);
2067
2097
 
2068
2098
  // WHEN a turn is assembled
2069
- const result = await applyRuntimeInjections(baseMessages, {});
2099
+ const result = await applyRuntimeInjections(baseMessages, {
2100
+ conversationId: FALLBACK_CONVERSATION_ID,
2101
+ });
2070
2102
 
2071
2103
  // THEN no gap line is injected
2072
2104
  const injected = injectedText(result);
@@ -2122,7 +2154,9 @@ describe("applyRuntimeInjections blocks.unifiedTurnContext", () => {
2122
2154
  test("captures unifiedTurnContext when tail is a user message", async () => {
2123
2155
  seedTemporalSnapshot();
2124
2156
 
2125
- const result = await applyRuntimeInjections(userTailMessages, {});
2157
+ const result = await applyRuntimeInjections(userTailMessages, {
2158
+ conversationId: FALLBACK_CONVERSATION_ID,
2159
+ });
2126
2160
 
2127
2161
  expect(result.blocks.unifiedTurnContext).toBe(sampleBlock);
2128
2162
  });
@@ -2141,13 +2175,17 @@ describe("applyRuntimeInjections blocks.unifiedTurnContext", () => {
2141
2175
  },
2142
2176
  ];
2143
2177
 
2144
- const result = await applyRuntimeInjections(assistantTailMessages, {});
2178
+ const result = await applyRuntimeInjections(assistantTailMessages, {
2179
+ conversationId: FALLBACK_CONVERSATION_ID,
2180
+ });
2145
2181
 
2146
2182
  expect(result.blocks.unifiedTurnContext).toBeUndefined();
2147
2183
  });
2148
2184
 
2149
2185
  test("does not capture when unifiedTurnContext option is absent", async () => {
2150
- const result = await applyRuntimeInjections(userTailMessages, {});
2186
+ const result = await applyRuntimeInjections(userTailMessages, {
2187
+ conversationId: FALLBACK_CONVERSATION_ID,
2188
+ });
2151
2189
 
2152
2190
  expect(result.blocks.unifiedTurnContext).toBeUndefined();
2153
2191
  });
@@ -2303,6 +2341,7 @@ describe("applyRuntimeInjections — subagent status", () => {
2303
2341
 
2304
2342
  // WHEN runtime injections run in full mode
2305
2343
  const { messages: result } = await applyRuntimeInjections([userMsg], {
2344
+ conversationId: FALLBACK_CONVERSATION_ID,
2306
2345
  mode: "full",
2307
2346
  });
2308
2347
 
@@ -2325,6 +2364,7 @@ describe("applyRuntimeInjections — subagent status", () => {
2325
2364
 
2326
2365
  // WHEN runtime injections run in minimal mode
2327
2366
  const { messages: result } = await applyRuntimeInjections([userMsg], {
2367
+ conversationId: FALLBACK_CONVERSATION_ID,
2328
2368
  mode: "minimal",
2329
2369
  });
2330
2370
 
@@ -2354,6 +2394,7 @@ describe("applyRuntimeInjections — subagent status", () => {
2354
2394
 
2355
2395
  // WHEN runtime injections run in full mode
2356
2396
  const { messages: result } = await applyRuntimeInjections([userMsg], {
2397
+ conversationId: FALLBACK_CONVERSATION_ID,
2357
2398
  mode: "full",
2358
2399
  });
2359
2400
 
@@ -2430,6 +2471,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2430
2471
 
2431
2472
  function makePkbOptions(overrides: Record<string, unknown> = {}) {
2432
2473
  return {
2474
+ conversationId: FALLBACK_CONVERSATION_ID,
2433
2475
  ...overrides,
2434
2476
  };
2435
2477
  }
@@ -2716,7 +2758,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2716
2758
  // filtered out.
2717
2759
  const { messages: initialResult } = await applyRuntimeInjections(
2718
2760
  preCompactionMessages,
2719
- {},
2761
+ { conversationId: FALLBACK_CONVERSATION_ID },
2720
2762
  );
2721
2763
  // Unwrap the injected reminder from the last user message.
2722
2764
  const initialTexts = extractTexts(initialResult);
@@ -2750,7 +2792,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2750
2792
  // should be filtered, and beta (no longer "in context") should appear.
2751
2793
  const { messages: rebuiltResult } = await applyRuntimeInjections(
2752
2794
  postCompactionMessages,
2753
- {},
2795
+ { conversationId: FALLBACK_CONVERSATION_ID },
2754
2796
  );
2755
2797
  const rebuiltTexts = extractTexts(rebuiltResult);
2756
2798
  const rebuiltReminder = rebuiltTexts.find(
@@ -2867,7 +2909,9 @@ describe("Slack channel chronological rendering — multi-thread", () => {
2867
2909
  // self-resolves the chronological transcript from conversation state,
2868
2910
  // exactly as production does.
2869
2911
  seedSlackChannelConversationWithRows(slackChannelCaps, rows);
2870
- const { messages } = await applyRuntimeInjections([lastUserMessage], {});
2912
+ const { messages } = await applyRuntimeInjections([lastUserMessage], {
2913
+ conversationId: FALLBACK_CONVERSATION_ID,
2914
+ });
2871
2915
  return messages;
2872
2916
  }
2873
2917
 
@@ -3161,7 +3205,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3161
3205
  });
3162
3206
  const { messages: result } = await applyRuntimeInjections(
3163
3207
  [lastUserMessage],
3164
- {},
3208
+ { conversationId: FALLBACK_CONVERSATION_ID },
3165
3209
  );
3166
3210
  expect(result.length).toBe(1);
3167
3211
  const allText = result[0].content
@@ -3208,7 +3252,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3208
3252
  );
3209
3253
  const { messages: result } = await applyRuntimeInjections(
3210
3254
  [lastUserMessage],
3211
- {},
3255
+ { conversationId: FALLBACK_CONVERSATION_ID },
3212
3256
  );
3213
3257
  // The self-resolved chronological transcript REPLACES the default
3214
3258
  // runMessages, so the inbound `DM question` text does not appear — only
@@ -3265,7 +3309,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3265
3309
  content: [{ type: "text", text: "post-compaction question" }],
3266
3310
  },
3267
3311
  ],
3268
- {},
3312
+ { conversationId: FALLBACK_CONVERSATION_ID },
3269
3313
  );
3270
3314
  const allText = result
3271
3315
  .flatMap((m) => m.content)
@@ -3319,7 +3363,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3319
3363
  seedSlackChannelConversationWithRows(slackCaps, rows);
3320
3364
  const { messages: result } = await applyRuntimeInjections(
3321
3365
  runMessagesWithMemory,
3322
- {},
3366
+ { conversationId: FALLBACK_CONVERSATION_ID },
3323
3367
  );
3324
3368
  const tail = result[result.length - 1];
3325
3369
  expect(tail.role).toBe("user");
@@ -3385,7 +3429,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3385
3429
  seedSlackChannelConversationWithRows(slackCaps, rows);
3386
3430
  const { messages: result } = await applyRuntimeInjections(
3387
3431
  runMessagesWithMemory,
3388
- {},
3432
+ { conversationId: FALLBACK_CONVERSATION_ID },
3389
3433
  );
3390
3434
  const tail = result[result.length - 1];
3391
3435
  expect(tail.role).toBe("user");
@@ -3426,7 +3470,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3426
3470
  seedSlackChannelConversationWithRows(slackCaps, rows);
3427
3471
  const { messages: result } = await applyRuntimeInjections(
3428
3472
  [{ role: "user", content: [{ type: "text", text: "inbound" }] }],
3429
- {},
3473
+ { conversationId: FALLBACK_CONVERSATION_ID },
3430
3474
  );
3431
3475
  const tail = result[result.length - 1];
3432
3476
  const allText = tail.content
@@ -3451,7 +3495,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3451
3495
  ]);
3452
3496
  const { messages: result } = await applyRuntimeInjections(
3453
3497
  [{ role: "user", content: [{ type: "text", text: "current turn" }] }],
3454
- {},
3498
+ { conversationId: FALLBACK_CONVERSATION_ID },
3455
3499
  );
3456
3500
 
3457
3501
  const allText = result
@@ -3478,7 +3522,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3478
3522
  seedChannelCapabilitiesConversation(slackDmCaps, ["dm context: ..."]);
3479
3523
  const { messages: result } = await applyRuntimeInjections(
3480
3524
  [{ role: "user", content: [{ type: "text", text: "hi DM" }] }],
3481
- {},
3525
+ { conversationId: FALLBACK_CONVERSATION_ID },
3482
3526
  );
3483
3527
 
3484
3528
  const allText = result
@@ -3504,7 +3548,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3504
3548
  );
3505
3549
  const { messages: result } = await applyRuntimeInjections(
3506
3550
  [{ role: "user", content: [{ type: "text", text: "hi" }] }],
3507
- {},
3551
+ { conversationId: FALLBACK_CONVERSATION_ID },
3508
3552
  );
3509
3553
  const allText = result
3510
3554
  .flatMap((m) => m.content)
@@ -3892,7 +3936,9 @@ describe("Slack channel chronological rendering — multi-thread", () => {
3892
3936
  role: "user",
3893
3937
  content: [{ type: "text", text: "current turn" }],
3894
3938
  };
3895
- const { messages } = await applyRuntimeInjections([lastUserMessage], {});
3939
+ const { messages } = await applyRuntimeInjections([lastUserMessage], {
3940
+ conversationId: FALLBACK_CONVERSATION_ID,
3941
+ });
3896
3942
  return { messages, focusBlock };
3897
3943
  }
3898
3944
 
@@ -4280,7 +4326,9 @@ describe("Slack channel chronological rendering — multi-thread", () => {
4280
4326
  // Re-run injection on the stripped messages — the conversation is still
4281
4327
  // seeded, so the rebuild re-derives the focus block; only ONE
4282
4328
  // `<active_thread>` block must end up in the result.
4283
- const { messages: reInjected } = await applyRuntimeInjections(stripped, {});
4329
+ const { messages: reInjected } = await applyRuntimeInjections(stripped, {
4330
+ conversationId: FALLBACK_CONVERSATION_ID,
4331
+ });
4284
4332
  const reInjectedTexts = reInjected
4285
4333
  .flatMap((m) => m.content)
4286
4334
  .filter((b): b is { type: "text"; text: string } => b.type === "text")
@@ -4306,7 +4354,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
4306
4354
  });
4307
4355
  const { messages: result } = await applyRuntimeInjections(
4308
4356
  [{ role: "user", content: [{ type: "text", text: "vellum question" }] }],
4309
- {},
4357
+ { conversationId: FALLBACK_CONVERSATION_ID },
4310
4358
  );
4311
4359
  const allText = result
4312
4360
  .flatMap((m) => m.content)
@@ -4330,7 +4378,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
4330
4378
  });
4331
4379
  const { messages: result } = await applyRuntimeInjections(
4332
4380
  [{ role: "user", content: [{ type: "text", text: "DM question" }] }],
4333
- {},
4381
+ { conversationId: FALLBACK_CONVERSATION_ID },
4334
4382
  );
4335
4383
  const allText = result
4336
4384
  .flatMap((m) => m.content)
@@ -5686,6 +5734,7 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
5686
5734
  pkbSearchResults = [];
5687
5735
  pkbSearchThrows = null;
5688
5736
  const { blocks } = await applyRuntimeInjections(baseMessages, {
5737
+ conversationId: FALLBACK_CONVERSATION_ID,
5689
5738
  mode: "full",
5690
5739
  });
5691
5740
 
@@ -5696,6 +5745,7 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
5696
5745
  test("not captured in minimal mode", async () => {
5697
5746
  seedPkbContent();
5698
5747
  const { blocks } = await applyRuntimeInjections(baseMessages, {
5748
+ conversationId: FALLBACK_CONVERSATION_ID,
5699
5749
  mode: "minimal",
5700
5750
  });
5701
5751
 
@@ -5704,6 +5754,7 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
5704
5754
 
5705
5755
  test("not captured when PKB inactive", async () => {
5706
5756
  const { blocks } = await applyRuntimeInjections(baseMessages, {
5757
+ conversationId: FALLBACK_CONVERSATION_ID,
5707
5758
  mode: "full",
5708
5759
  });
5709
5760
 
@@ -78,7 +78,9 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
78
78
  test("injects workspace context when registered", async () => {
79
79
  seedWorkspaceContext(sampleContext);
80
80
  const messages: Message[] = [userMsg("Hello")];
81
- const { messages: result } = await applyRuntimeInjections(messages, {});
81
+ const { messages: result } = await applyRuntimeInjections(messages, {
82
+ conversationId: FALLBACK_CONVERSATION_ID,
83
+ });
82
84
 
83
85
  expect(result).toHaveLength(1);
84
86
  expect(result[0].content).toHaveLength(2);
@@ -88,7 +90,9 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
88
90
 
89
91
  test("does not inject when no workspace context is registered", async () => {
90
92
  const messages: Message[] = [userMsg("Hello")];
91
- const { messages: result } = await applyRuntimeInjections(messages, {});
93
+ const { messages: result } = await applyRuntimeInjections(messages, {
94
+ conversationId: FALLBACK_CONVERSATION_ID,
95
+ });
92
96
 
93
97
  expect(result).toHaveLength(1);
94
98
  expect(result[0].content).toHaveLength(1);
@@ -101,7 +105,9 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
101
105
  const messages: Message[] = [userMsg(sampleContext), userMsg("Hello")];
102
106
 
103
107
  // WHEN injections are applied
104
- const { messages: result } = await applyRuntimeInjections(messages, {});
108
+ const { messages: result } = await applyRuntimeInjections(messages, {
109
+ conversationId: FALLBACK_CONVERSATION_ID,
110
+ });
105
111
 
106
112
  // THEN presence detection skips the block to keep the prefix stable.
107
113
  expect(result).toHaveLength(2);
@@ -117,7 +123,9 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
117
123
  surfaceState: makeSurfaceState("sf_1", { html: "<div>test</div>" }),
118
124
  });
119
125
  const messages: Message[] = [userMsg("Hello")];
120
- const { messages: result } = await applyRuntimeInjections(messages, {});
126
+ const { messages: result } = await applyRuntimeInjections(messages, {
127
+ conversationId: FALLBACK_CONVERSATION_ID,
128
+ });
121
129
 
122
130
  // Workspace is injected last (in applyRuntimeInjections order) so it
123
131
  // prepends to whatever was already prepended by activeSurface.
@@ -144,7 +152,9 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
144
152
  }),
145
153
  });
146
154
  const messages: Message[] = [userMsg("Edit this app")];
147
- const { messages: result } = await applyRuntimeInjections(messages, {});
155
+ const { messages: result } = await applyRuntimeInjections(messages, {
156
+ conversationId: FALLBACK_CONVERSATION_ID,
157
+ });
148
158
 
149
159
  const activeWorkspaceText = (result[0].content[0] as { text: string }).text;
150
160
  expect(activeWorkspaceText).toContain('skill: "app-builder"');
@@ -161,6 +171,7 @@ describe("applyRuntimeInjections — minimal mode skips workspace blocks", () =>
161
171
  seedWorkspaceContext(sampleContext);
162
172
  const messages: Message[] = [userMsg("Hello")];
163
173
  const { messages: result } = await applyRuntimeInjections(messages, {
174
+ conversationId: FALLBACK_CONVERSATION_ID,
164
175
  mode: "minimal",
165
176
  });
166
177
 
@@ -176,6 +187,7 @@ describe("applyRuntimeInjections — minimal mode skips workspace blocks", () =>
176
187
  });
177
188
  const messages: Message[] = [userMsg("Hello")];
178
189
  const { messages: result } = await applyRuntimeInjections(messages, {
190
+ conversationId: FALLBACK_CONVERSATION_ID,
179
191
  mode: "minimal",
180
192
  });
181
193
 
@@ -192,7 +204,9 @@ describe("applyRuntimeInjections — minimal mode skips workspace blocks", () =>
192
204
  surfaceState: makeSurfaceState("sf_1", { html: "<div>test</div>" }),
193
205
  });
194
206
  const messages: Message[] = [userMsg("Hello")];
195
- const { messages: result } = await applyRuntimeInjections(messages, {});
207
+ const { messages: result } = await applyRuntimeInjections(messages, {
208
+ conversationId: FALLBACK_CONVERSATION_ID,
209
+ });
196
210
 
197
211
  expect(result[0].content).toHaveLength(3);
198
212
  expect((result[0].content[0] as { text: string }).text).toBe(sampleContext);
@@ -88,6 +88,10 @@ initializeDb();
88
88
  // `makeTurnContext` and the workspace registry seed share this id so the
89
89
  // `workspace-context` injector resolves the seeded block for the turn.
90
90
  const TEST_CONVERSATION_ID = "conv-test-1";
91
+ // Conversation id the fallback-context test registers its live conversation
92
+ // under; passed explicitly now that `applyRuntimeInjections` requires the
93
+ // caller to name the conversation it resolves through.
94
+ const FALLBACK_CONVERSATION_ID = "runtime-assembly-fallback";
91
95
 
92
96
  /** A fake TurnContext sufficient for driving `composeInjectorChain`. */
93
97
  function makeTurnContext(): TurnContext {
@@ -402,7 +406,9 @@ describe("injector chain", () => {
402
406
  interfaceName: "web",
403
407
  });
404
408
  seedFallbackTemporalSnapshot();
405
- const result = await applyRuntimeInjections(runMessages, {});
409
+ const result = await applyRuntimeInjections(runMessages, {
410
+ conversationId: FALLBACK_CONVERSATION_ID,
411
+ });
406
412
 
407
413
  // The unified-turn-context injector fires even without a caller-supplied
408
414
  // turnContext, proving the chain runs under the synthesized context.