@vellumai/assistant 0.8.8-dev.202606081859.f7bdc00 → 0.8.8-dev.202606081950.5bd40e7

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 (43) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/approval-cascade.test.ts +1 -1
  3. package/src/__tests__/compaction-direct.test.ts +32 -18
  4. package/src/__tests__/compaction-events.test.ts +2 -2
  5. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  6. package/src/__tests__/context-overflow-reducer.test.ts +5 -5
  7. package/src/__tests__/context-window-manager-compact-retry.test.ts +1 -1
  8. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  9. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  10. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  11. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  12. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  13. package/src/__tests__/conversation-queue.test.ts +1 -1
  14. package/src/__tests__/conversation-runtime-assembly.test.ts +71 -0
  15. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  16. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  17. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  18. package/src/__tests__/conversation-undo.test.ts +2 -2
  19. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  20. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  21. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  22. package/src/__tests__/memory-retrieval-hook.test.ts +1 -1
  23. package/src/agent/loop.ts +5 -19
  24. package/src/cli/lib/__tests__/install-from-github.test.ts +199 -20
  25. package/src/cli/lib/__tests__/plugin-details.test.ts +18 -11
  26. package/src/cli/lib/__tests__/search-plugins.test.ts +12 -5
  27. package/src/cli/lib/install-from-github.ts +246 -41
  28. package/src/cli/lib/plugin-details.ts +20 -13
  29. package/src/cli/lib/search-plugins.ts +14 -8
  30. package/src/config/feature-flag-registry.json +3 -2
  31. package/src/daemon/conversation-agent-loop-handlers.ts +1 -1
  32. package/src/daemon/conversation-agent-loop.ts +19 -53
  33. package/src/daemon/conversation-history.ts +1 -1
  34. package/src/daemon/conversation-media-retry.ts +19 -6
  35. package/src/daemon/conversation-process.ts +1 -1
  36. package/src/daemon/conversation-runtime-assembly.ts +37 -1
  37. package/src/daemon/conversation.ts +11 -11
  38. package/src/plugins/defaults/compaction/compact.ts +24 -15
  39. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +4 -4
  40. package/src/plugins/defaults/compaction/manager-store.ts +1 -1
  41. package/src/{context → plugins/defaults/compaction}/window-manager.ts +12 -12
  42. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +9 -10
  43. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.8-dev.202606081859.f7bdc00",
3
+ "version": "0.8.8-dev.202606081950.5bd40e7",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -173,7 +173,7 @@ mock.module("../memory/retriever.js", () => ({
173
173
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
174
174
  }));
175
175
 
176
- mock.module("../context/window-manager.js", () => ({
176
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
177
177
  ContextWindowManager: class {
178
178
  constructor() {}
179
179
  updateConfig() {}
@@ -3,19 +3,29 @@
3
3
  *
4
4
  * The agent loop calls {@link defaultCompact} directly with a
5
5
  * {@link CompactionContext} rather than routing through a middleware pipeline.
6
- * These tests assert that the default implementation delegates to the
7
- * supplied {@link ContextWindowManager} and forwards the request's
8
- * conversational options verbatim. The orchestrator integration path
9
- * (conversation-agent-loop) is exercised by
6
+ * These tests assert that the default implementation resolves the
7
+ * conversation's {@link ContextWindowManager} from the compaction store and
8
+ * forwards the request's conversational options verbatim. The orchestrator
9
+ * integration path (conversation-agent-loop) is exercised by
10
10
  * `conversation-agent-loop-overflow.test.ts`.
11
11
  */
12
12
 
13
- import { describe, expect, test } from "bun:test";
13
+ import { describe, expect, mock, test } from "bun:test";
14
14
 
15
- import {
16
- type CompactionContext,
17
- defaultCompact,
18
- } from "../plugins/defaults/compaction/compact.js";
15
+ // `defaultCompact` resolves the manager from the per-conversation compaction
16
+ // store. Register each test's canned stub in a map the mocked store reads from.
17
+ const fakeContextWindowManagers = new Map<string, unknown>();
18
+ mock.module("../plugins/defaults/compaction/manager-store.js", () => ({
19
+ createContextWindowManager: () => undefined,
20
+ getContextWindowManager: (conversationId: string) =>
21
+ fakeContextWindowManagers.get(conversationId),
22
+ disposeContextWindowManager: (conversationId: string) => {
23
+ fakeContextWindowManagers.delete(conversationId);
24
+ },
25
+ }));
26
+
27
+ const { defaultCompact } =
28
+ await import("../plugins/defaults/compaction/compact.js");
19
29
 
20
30
  type ContextWindowResultShape = {
21
31
  compacted: boolean;
@@ -55,13 +65,17 @@ function makeResult(
55
65
  };
56
66
  }
57
67
 
58
- function makeManager(result: ContextWindowResultShape) {
68
+ /** Register a stub manager under a conversation id and return its recorder. */
69
+ function registerManager(
70
+ conversationId: string,
71
+ result: ContextWindowResultShape,
72
+ ) {
59
73
  const observed: {
60
74
  messages: unknown;
61
75
  signal: unknown;
62
76
  options: unknown;
63
77
  }[] = [];
64
- const manager = {
78
+ fakeContextWindowManagers.set(conversationId, {
65
79
  maybeCompact: async (
66
80
  messages: unknown,
67
81
  signal: unknown,
@@ -70,8 +84,8 @@ function makeManager(result: ContextWindowResultShape) {
70
84
  observed.push({ messages, signal, options });
71
85
  return result;
72
86
  },
73
- } as unknown as CompactionContext["manager"];
74
- return { manager, observed };
87
+ });
88
+ return observed;
75
89
  }
76
90
 
77
91
  describe("defaultCompact", () => {
@@ -81,13 +95,13 @@ describe("defaultCompact", () => {
81
95
  summaryText: "manager-summary",
82
96
  compactedMessages: 7,
83
97
  });
84
- const { manager, observed } = makeManager(expected);
98
+ const observed = registerManager("conv-delegates", expected);
85
99
  const messages = [{ role: "user", content: "hi" }] as never;
86
100
  const signal = new AbortController().signal;
87
101
 
88
102
  // WHEN defaultCompact runs with those messages and a signal
89
103
  const result = (await defaultCompact({
90
- manager,
104
+ conversationId: "conv-delegates",
91
105
  messages,
92
106
  signal,
93
107
  })) as unknown as ContextWindowResultShape;
@@ -105,11 +119,11 @@ describe("defaultCompact", () => {
105
119
 
106
120
  test("forwards the request's compaction options to the manager", async () => {
107
121
  // GIVEN a manager and a fully-populated compaction context
108
- const { manager, observed } = makeManager(makeResult());
122
+ const observed = registerManager("conv-options", makeResult());
109
123
 
110
124
  // WHEN defaultCompact runs with force/profile/trust options
111
125
  await defaultCompact({
112
- manager,
126
+ conversationId: "conv-options",
113
127
  messages: [] as never,
114
128
  force: true,
115
129
  overrideProfile: "fast-profile",
@@ -118,7 +132,7 @@ describe("defaultCompact", () => {
118
132
  actorTrustClass: "guardian",
119
133
  });
120
134
 
121
- // THEN the manager received exactly those options, and the manager,
135
+ // THEN the manager received exactly those options, and the conversation id,
122
136
  // messages, and signal are not leaked into the options bag
123
137
  expect(observed).toHaveLength(1);
124
138
  expect(observed[0]!.options).toEqual({
@@ -12,8 +12,8 @@ import { describe, expect, mock, test } from "bun:test";
12
12
 
13
13
  import { CompactionCircuit } from "../agent/compaction-circuit.js";
14
14
  import type { AgentEvent } from "../agent/loop.js";
15
- import type { ContextWindowResult } from "../context/window-manager.js";
16
15
  import type { ServerMessage } from "../daemon/message-protocol.js";
16
+ import type { ContextWindowResult } from "../plugins/defaults/compaction/window-manager.js";
17
17
  import type { Message, ProviderResponse } from "../providers/types.js";
18
18
 
19
19
  // ---------------------------------------------------------------------------
@@ -187,7 +187,7 @@ let mockCompactResult: ContextWindowResult = {
187
187
  summaryText: "",
188
188
  };
189
189
 
190
- mock.module("../context/window-manager.js", () => ({
190
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
191
191
  ContextWindowManager: class {
192
192
  nonPersistedPrefixCount = 0;
193
193
  constructor() {}
@@ -12,7 +12,7 @@ import { describe, expect, mock, test } from "bun:test";
12
12
 
13
13
  import { DEFAULT_CONFIG } from "../config/defaults.js";
14
14
  import { estimatePromptTokens } from "../context/token-estimator.js";
15
- import { ContextWindowManager } from "../context/window-manager.js";
15
+ import { ContextWindowManager } from "../plugins/defaults/compaction/window-manager.js";
16
16
  import type { Message, Provider } from "../providers/types.js";
17
17
 
18
18
  mock.module("../util/logger.js", () => ({
@@ -1,17 +1,17 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import { estimatePromptTokens } from "../context/token-estimator.js";
4
- import type {
5
- ContextWindowCompactOptions,
6
- ContextWindowResult,
7
- } from "../context/window-manager.js";
8
- import { createContextSummaryMessage } from "../context/window-manager.js";
9
4
  import {
10
5
  createInitialReducerState,
11
6
  reduceContextOverflow,
12
7
  type ReducerConfig,
13
8
  type ReducerState,
14
9
  } from "../plugins/defaults/compaction/context-overflow-reducer.js";
10
+ import type {
11
+ ContextWindowCompactOptions,
12
+ ContextWindowResult,
13
+ } from "../plugins/defaults/compaction/window-manager.js";
14
+ import { createContextSummaryMessage } from "../plugins/defaults/compaction/window-manager.js";
15
15
  import type { Message } from "../providers/types.js";
16
16
 
17
17
  function msg(role: "user" | "assistant", text: string): Message {
@@ -94,7 +94,7 @@ mock.module("../context/compactor.js", () => ({
94
94
  },
95
95
  }));
96
96
 
97
- import { ContextWindowManager } from "../context/window-manager.js";
97
+ import { ContextWindowManager } from "../plugins/defaults/compaction/window-manager.js";
98
98
  import type { Message, Provider } from "../providers/types.js";
99
99
 
100
100
  function makeProvider(): Provider {
@@ -167,7 +167,7 @@ mock.module("../memory/retriever.js", () => ({
167
167
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
168
168
  }));
169
169
 
170
- mock.module("../context/window-manager.js", () => ({
170
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
171
171
  ContextWindowManager: class {
172
172
  constructor() {}
173
173
  updateConfig() {}
@@ -164,7 +164,7 @@ mock.module("../memory/retriever.js", () => ({
164
164
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
165
165
  }));
166
166
 
167
- mock.module("../context/window-manager.js", () => ({
167
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
168
168
  ContextWindowManager: class {
169
169
  constructor() {}
170
170
  updateConfig() {}
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import { createContextSummaryMessage } from "../context/window-manager.js";
4
3
  import { stripMediaPayloadsForRetry } from "../daemon/conversation-media-retry.js";
4
+ import { createContextSummaryMessage } from "../plugins/defaults/compaction/window-manager.js";
5
5
  import type { ContentBlock, Message } from "../providers/types.js";
6
6
 
7
7
  // ---------------------------------------------------------------------------
@@ -249,7 +249,7 @@ mock.module("../agent/loop.js", () => ({
249
249
  },
250
250
  }));
251
251
 
252
- mock.module("../context/window-manager.js", () => ({
252
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
253
253
  ContextWindowManager: class {
254
254
  constructor() {}
255
255
  updateConfig() {}
@@ -221,7 +221,7 @@ mock.module("../memory/retriever.js", () => ({
221
221
  let maybeCompactCalls: Array<{ force: boolean }> = [];
222
222
  let forceCompactionEnabled = false;
223
223
 
224
- mock.module("../context/window-manager.js", () => ({
224
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
225
225
  ContextWindowManager: class {
226
226
  constructor() {}
227
227
  updateConfig() {}
@@ -262,7 +262,7 @@ mock.module("../memory/retriever.js", () => ({
262
262
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
263
263
  }));
264
264
 
265
- mock.module("../context/window-manager.js", () => ({
265
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
266
266
  ContextWindowManager: class {
267
267
  constructor() {}
268
268
  updateConfig() {}
@@ -86,12 +86,14 @@ import {
86
86
  loadSlackChronologicalContext,
87
87
  loadSlackChronologicalMessages,
88
88
  resolveChannelCapabilities,
89
+ resolveTurnInboundActorContext,
89
90
  stripChannelCapabilityContext,
90
91
  stripInjectionsForCompaction,
91
92
  stripNowScratchpad,
92
93
  } from "../daemon/conversation-runtime-assembly.js";
93
94
  import type { SurfaceData, SurfaceType } from "../daemon/message-protocol.js";
94
95
  import { buildPkbReminder } from "../daemon/pkb-reminder-builder.js";
96
+ import type { TrustContext } from "../daemon/trust-context.js";
95
97
  import type { MessageRow } from "../memory/conversation-crud.js";
96
98
  import { getDb } from "../memory/db-connection.js";
97
99
  import { initializeDb } from "../memory/db-init.js";
@@ -1438,6 +1440,75 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1438
1440
  });
1439
1441
  });
1440
1442
 
1443
+ // ---------------------------------------------------------------------------
1444
+ // resolveTurnInboundActorContext
1445
+ // ---------------------------------------------------------------------------
1446
+
1447
+ describe("resolveTurnInboundActorContext", () => {
1448
+ /**
1449
+ * No trust context means there is no inbound actor to ground the turn on, so
1450
+ * the actor section is suppressed.
1451
+ */
1452
+ test("returns null when there is no trust context", () => {
1453
+ // GIVEN a turn with no trust context
1454
+ // WHEN the inbound actor context is resolved
1455
+ const actorContext = resolveTurnInboundActorContext(undefined, "asst-1");
1456
+
1457
+ // THEN there is no actor context to inject
1458
+ expect(actorContext).toBeNull();
1459
+ });
1460
+
1461
+ /**
1462
+ * Guardian (owner) turns suppress the actor section — the unified turn context
1463
+ * only renders actor fields for non-guardian actors.
1464
+ */
1465
+ test("returns null on guardian turns", () => {
1466
+ // GIVEN a guardian trust context (no requester chat id, so the direct
1467
+ // projection branch is taken)
1468
+ const trustContext: TrustContext = {
1469
+ sourceChannel: "vellum",
1470
+ trustClass: "guardian",
1471
+ requesterDisplayName: "Alice",
1472
+ };
1473
+
1474
+ // WHEN the inbound actor context is resolved
1475
+ const actorContext = resolveTurnInboundActorContext(trustContext, "asst-1");
1476
+
1477
+ // THEN the actor section is suppressed for the owner
1478
+ expect(actorContext).toBeNull();
1479
+ });
1480
+
1481
+ /**
1482
+ * A non-guardian trust context without the identity fields needed for the
1483
+ * unified actor-trust lookup is projected directly into the actor context.
1484
+ */
1485
+ test("projects a non-guardian trust context directly", () => {
1486
+ // GIVEN a trusted-contact trust context lacking requester chat/user ids
1487
+ const trustContext: TrustContext = {
1488
+ sourceChannel: "telegram",
1489
+ trustClass: "trusted_contact",
1490
+ requesterIdentifier: "@bob_handle",
1491
+ requesterDisplayName: "Bob",
1492
+ requesterSenderDisplayName: "Bobby",
1493
+ };
1494
+
1495
+ // WHEN the inbound actor context is resolved
1496
+ const actorContext = resolveTurnInboundActorContext(trustContext, "asst-1");
1497
+
1498
+ // THEN the trust context is projected into the model-facing actor context
1499
+ expect(actorContext).toEqual({
1500
+ sourceChannel: "telegram",
1501
+ canonicalActorIdentity: null,
1502
+ actorIdentifier: "@bob_handle",
1503
+ actorDisplayName: "Bob",
1504
+ actorSenderDisplayName: "Bobby",
1505
+ actorMemberDisplayName: undefined,
1506
+ trustClass: "trusted_contact",
1507
+ guardianIdentity: undefined,
1508
+ });
1509
+ });
1510
+ });
1511
+
1441
1512
  // ---------------------------------------------------------------------------
1442
1513
  // buildUnifiedTurnContextBlock
1443
1514
  // ---------------------------------------------------------------------------
@@ -153,7 +153,7 @@ mock.module("../memory/retriever.js", () => ({
153
153
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
154
154
  }));
155
155
 
156
- mock.module("../context/window-manager.js", () => ({
156
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
157
157
  ContextWindowManager: class {
158
158
  constructor() {}
159
159
  updateConfig() {}
@@ -154,7 +154,7 @@ mock.module("../memory/retriever.js", () => ({
154
154
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
155
155
  }));
156
156
 
157
- mock.module("../context/window-manager.js", () => ({
157
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
158
158
  ContextWindowManager: class {
159
159
  constructor() {}
160
160
  updateConfig() {}
@@ -170,7 +170,7 @@ mock.module("../memory/retriever.js", () => ({
170
170
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
171
171
  }));
172
172
 
173
- mock.module("../context/window-manager.js", () => ({
173
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
174
174
  ContextWindowManager: class {
175
175
  constructor() {}
176
176
  updateConfig() {}
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
+ import { findLastUndoableUserMessageIndex } from "../daemon/conversation.js";
3
4
  import {
4
5
  CONTEXT_SUMMARY_MARKER,
5
6
  createContextSummaryMessage,
6
- } from "../context/window-manager.js";
7
- import { findLastUndoableUserMessageIndex } from "../daemon/conversation.js";
7
+ } from "../plugins/defaults/compaction/window-manager.js";
8
8
  import type { Message } from "../providers/types.js";
9
9
 
10
10
  function textMessage(role: "user" | "assistant", text: string): Message {
@@ -137,7 +137,7 @@ mock.module("../memory/retrieval-budget.js", () => ({
137
137
  computeRecallBudget: () => 0,
138
138
  }));
139
139
 
140
- mock.module("../context/window-manager.js", () => ({
140
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
141
141
  ContextWindowManager: class {
142
142
  constructor() {}
143
143
  updateConfig() {}
@@ -181,7 +181,7 @@ mock.module("../memory/query-builder.js", () => ({
181
181
  mock.module("../memory/retrieval-budget.js", () => ({
182
182
  computeRecallBudget: () => 0,
183
183
  }));
184
- mock.module("../context/window-manager.js", () => ({
184
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
185
185
  ContextWindowManager: class {
186
186
  updateConfig() {}
187
187
  shouldCompact() {
@@ -178,7 +178,7 @@ mock.module("../memory/query-builder.js", () => ({
178
178
  mock.module("../memory/retrieval-budget.js", () => ({
179
179
  computeRecallBudget: () => 0,
180
180
  }));
181
- mock.module("../context/window-manager.js", () => ({
181
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
182
182
  ContextWindowManager: class {
183
183
  updateConfig() {}
184
184
  shouldCompact() {
@@ -36,6 +36,7 @@ const applyRuntimeInjectionsMock = mock(async (messages: unknown) => ({
36
36
  }));
37
37
  mock.module("../daemon/conversation-runtime-assembly.js", () => ({
38
38
  applyRuntimeInjections: applyRuntimeInjectionsMock,
39
+ resolveTurnInboundActorContext: () => null,
39
40
  }));
40
41
 
41
42
  // The hook self-resolves the live conversation and its trust class; both are
@@ -148,7 +149,6 @@ function makeHookCtx(
148
149
  requestId: "req-test",
149
150
  isNonInteractive: false,
150
151
  modelProfile: null,
151
- actorContext: null,
152
152
  ...overrides,
153
153
  };
154
154
  }
package/src/agent/loop.ts CHANGED
@@ -10,7 +10,6 @@ import {
10
10
  estimateToolsTokens,
11
11
  getCalibrationProviderKey,
12
12
  } from "../context/token-estimator.js";
13
- import type { ContextWindowResult } from "../context/window-manager.js";
14
13
  import type { InboundActorContext } from "../daemon/conversation-runtime-assembly.js";
15
14
  import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
16
15
  import type { TrustContext } from "../daemon/trust-context.js";
@@ -22,15 +21,11 @@ import type {
22
21
  PreModelCallContext,
23
22
  StopContext,
24
23
  } from "../plugin-api/types.js";
25
- import {
26
- DEFAULT_COMPACTION_PLUGIN_NAME,
27
- defaultCompact,
28
- } from "../plugins/defaults/compaction/compact.js";
29
- import { getContextWindowManager } from "../plugins/defaults/compaction/manager-store.js";
24
+ import { defaultCompact } from "../plugins/defaults/compaction/compact.js";
25
+ import type { ContextWindowResult } from "../plugins/defaults/compaction/window-manager.js";
30
26
  import postCompact from "../plugins/defaults/memory-retrieval/hooks/post-compact.js";
31
27
  import { runHook } from "../plugins/pipeline.js";
32
28
  import type { CompactionCircuitEvent } from "../plugins/types.js";
33
- import { PluginExecutionError } from "../plugins/types.js";
34
29
  import { normalizeThinkingConfigForWire } from "../providers/thinking-config.js";
35
30
  import type {
36
31
  ContentBlock,
@@ -730,17 +725,8 @@ export class AgentLoop {
730
725
  // Record the history-stripped marker right after stripping, before the
731
726
  // pipeline runs.
732
727
  await onEvent({ type: "history_stripped" });
733
- // The compaction module owns the per-conversation manager; resolve it from
734
- // the store rather than holding a handle on the loop. Absent for callers
735
- // without a compaction path (agent wakes, standalone unit tests), which
736
- // never reach this gate.
737
- const manager = getContextWindowManager(this.conversationId);
738
- if (manager == null) {
739
- throw new PluginExecutionError(
740
- `default-compaction: no ContextWindowManager registered for conversation ${this.conversationId} — the compaction store must construct one before compaction runs`,
741
- DEFAULT_COMPACTION_PLUGIN_NAME,
742
- );
743
- }
728
+ // The compaction module owns the per-conversation manager; pass the
729
+ // conversation id and let `defaultCompact` resolve it from the store.
744
730
  // The mid-loop budget gate is reached only when this turn decides to
745
731
  // compact in place, so `force` past the auto-threshold check.
746
732
  // `actorTrustClass` comes from the turn's trust snapshot (the actor whose
@@ -748,7 +734,7 @@ export class AgentLoop {
748
734
  // guardian-only attachments for untrusted actors. `overrideProfile` is the
749
735
  // turn's resolved inference-profile override for the summary call.
750
736
  const compactResult = await defaultCompact({
751
- manager,
737
+ conversationId: this.conversationId,
752
738
  messages: rawHistory,
753
739
  signal,
754
740
  force: true,