Skip to content

Commit d048637

Browse files
committed
fix(tests): update session state fixtures
1 parent 877a41e commit d048637

4 files changed

Lines changed: 47 additions & 15 deletions

File tree

README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ reminded of recent project context — regardless of what survived the summary.
2323
This plugin connects to a Graphiti MCP server and:
2424

2525
- Searches Graphiti for relevant facts and entities on each user message
26-
- Injects memories into the system prompt via
27-
`experimental.chat.system.transform`, keeping session titles clean
26+
- Injects memories into the last user message as a `<memory>` block via
27+
`experimental.chat.messages.transform`, keeping the system prompt static for
28+
prefix caching
2829
- Detects context drift using Jaccard similarity and re-injects when the
2930
conversation topic shifts
3031
- Buffers user and assistant messages, flushing them to Graphiti on idle or
@@ -128,22 +129,25 @@ On each user message the plugin searches Graphiti for facts and entities
128129
relevant to the message content. Results are split into project and user scopes
129130
(70% / 30% budget), deduplicated, filtered for validity, annotated with
130131
staleness if older than `factStaleDays`, and formatted as Markdown. The
131-
formatted context is cached on the session state for the system prompt hook to
132-
pick up.
132+
formatted context is cached on the session state for the messages transform hook
133+
to pick up.
133134

134135
On the very first message of a session, the plugin also loads the most recent
135136
session snapshot episode to prime the conversation with prior context.
136137

137138
The injection budget is calculated dynamically: 5% of the model's context limit
138139
(resolved from the provider list) multiplied by 4 characters per token.
139140

140-
### System Prompt Injection (`experimental.chat.system.transform`)
141+
### User Message Injection (`experimental.chat.messages.transform`)
141142

142-
A separate hook reads the cached memory context and appends it to the system
143-
prompt array. This approach keeps memory out of user message parts, which
144-
prevents it from influencing session titles or being treated as user
145-
instructions. The cache is cleared after injection so stale context is not
146-
re-injected on subsequent LLM calls within the same turn.
143+
A separate hook reads the cached memory context and prepends it to the last user
144+
message as a `<memory data-uuids="...">` block. The `data-uuids` attribute lists
145+
the fact UUIDs included in the injection, which are tracked in
146+
`visibleFactUuids` so subsequent searches can filter out already-visible facts.
147+
This approach keeps the system prompt static, enabling provider-side prefix
148+
caching, and avoids influencing session titles. The cache is cleared after
149+
injection so stale context is not re-injected on subsequent LLM calls within the
150+
same turn.
147151

148152
### Drift-Based Re-injection (`chat.message`)
149153

src/handlers/compacting.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ describe("compacting handler integration", () => {
8080
userGroupId: "test:user",
8181
injectedMemories: false,
8282
lastInjectionFactUuids: [],
83+
visibleFactUuids: [],
8384
messageCount: 0,
8485
pendingMessages: [],
8586
contextLimit: 200_000,
@@ -120,6 +121,7 @@ describe("compacting handler integration", () => {
120121
userGroupId: "test:user",
121122
injectedMemories: false,
122123
lastInjectionFactUuids: [],
124+
visibleFactUuids: [],
123125
messageCount: 0,
124126
pendingMessages: [],
125127
contextLimit: 200_000,
@@ -172,6 +174,7 @@ describe("compacting handler integration", () => {
172174
userGroupId: "test:user",
173175
injectedMemories: false,
174176
lastInjectionFactUuids: [],
177+
visibleFactUuids: [],
175178
messageCount: 0,
176179
pendingMessages: [],
177180
contextLimit: 200_000,
@@ -203,6 +206,7 @@ describe("compacting handler integration", () => {
203206
userGroupId: "test:user",
204207
injectedMemories: false,
205208
lastInjectionFactUuids: [],
209+
visibleFactUuids: [],
206210
messageCount: 0,
207211
pendingMessages: [],
208212
contextLimit: 200_000,
@@ -264,6 +268,7 @@ describe("compacting handler integration", () => {
264268
userGroupId: "test:user",
265269
injectedMemories: false,
266270
lastInjectionFactUuids: [],
271+
visibleFactUuids: [],
267272
messageCount: 0,
268273
pendingMessages: [],
269274
contextLimit: 10_000, // Small budget to test allocation
@@ -334,6 +339,7 @@ describe("compacting handler integration", () => {
334339
userGroupId: "test:user",
335340
injectedMemories: false,
336341
lastInjectionFactUuids: [],
342+
visibleFactUuids: [],
337343
messageCount: 0,
338344
pendingMessages: [],
339345
contextLimit: 200_000,
@@ -379,6 +385,7 @@ describe("compacting handler integration", () => {
379385
userGroupId: "test:user",
380386
injectedMemories: false,
381387
lastInjectionFactUuids: [],
388+
visibleFactUuids: [],
382389
messageCount: 0,
383390
pendingMessages: [],
384391
contextLimit: 200_000,
@@ -425,6 +432,7 @@ describe("compacting handler integration", () => {
425432
userGroupId: "test:user",
426433
injectedMemories: false,
427434
lastInjectionFactUuids: [],
435+
visibleFactUuids: [],
428436
messageCount: 0,
429437
pendingMessages: [],
430438
contextLimit: 200_000,
@@ -477,6 +485,7 @@ describe("compacting handler integration", () => {
477485
userGroupId: "", // Empty user group (not configured)
478486
injectedMemories: false,
479487
lastInjectionFactUuids: [],
488+
visibleFactUuids: [],
480489
messageCount: 0,
481490
pendingMessages: [],
482491
contextLimit: 200_000,
@@ -521,6 +530,7 @@ describe("compacting handler integration", () => {
521530
userGroupId: "test:user",
522531
injectedMemories: false,
523532
lastInjectionFactUuids: [],
533+
visibleFactUuids: [],
524534
messageCount: 0,
525535
pendingMessages: [],
526536
contextLimit: 200_000,

src/handlers/event.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ describe("event handler integration", () => {
237237
userGroupId: "test:user",
238238
injectedMemories: true,
239239
lastInjectionFactUuids: [],
240+
visibleFactUuids: [],
240241
messageCount: 5,
241242
pendingMessages: [
242243
"User: What is TypeScript?",
@@ -303,6 +304,7 @@ describe("event handler integration", () => {
303304
userGroupId: "test:user",
304305
injectedMemories: false,
305306
lastInjectionFactUuids: [],
307+
visibleFactUuids: [],
306308
messageCount: 2,
307309
pendingMessages: [
308310
"User: What is Deno?",
@@ -351,6 +353,7 @@ describe("event handler integration", () => {
351353
userGroupId: "test:user",
352354
injectedMemories: false,
353355
lastInjectionFactUuids: [],
356+
visibleFactUuids: [],
354357
messageCount: 0,
355358
pendingMessages: [],
356359
contextLimit: 200_000,
@@ -401,6 +404,7 @@ describe("event handler integration", () => {
401404
userGroupId: "test:user",
402405
injectedMemories: false,
403406
lastInjectionFactUuids: [],
407+
visibleFactUuids: [],
404408
messageCount: 0,
405409
pendingMessages: ["User: Hello"],
406410
contextLimit: 200_000,
@@ -473,6 +477,7 @@ describe("event handler integration", () => {
473477
userGroupId: "test:user",
474478
injectedMemories: false,
475479
lastInjectionFactUuids: [],
480+
visibleFactUuids: [],
476481
messageCount: 1,
477482
pendingMessages: ["User: Hello"],
478483
contextLimit: 200_000,
@@ -514,6 +519,7 @@ describe("event handler integration", () => {
514519
userGroupId: "test:user",
515520
injectedMemories: true,
516521
lastInjectionFactUuids: [],
522+
visibleFactUuids: [],
517523
messageCount: 3,
518524
pendingMessages: ["User: Test message"],
519525
contextLimit: 200_000,
@@ -560,6 +566,7 @@ describe("event handler integration", () => {
560566
userGroupId: "test:user",
561567
injectedMemories: false,
562568
lastInjectionFactUuids: [],
569+
visibleFactUuids: [],
563570
messageCount: 0,
564571
pendingMessages: ["User: Hello"],
565572
contextLimit: 200_000,
@@ -628,6 +635,7 @@ describe("event handler integration", () => {
628635
userGroupId: "test:user",
629636
injectedMemories: false,
630637
lastInjectionFactUuids: [],
638+
visibleFactUuids: [],
631639
messageCount: 0,
632640
pendingMessages: [],
633641
contextLimit: 200_000,
@@ -670,6 +678,7 @@ describe("event handler integration", () => {
670678
userGroupId: "test:user",
671679
injectedMemories: false,
672680
lastInjectionFactUuids: [],
681+
visibleFactUuids: [],
673682
messageCount: 0,
674683
pendingMessages: [],
675684
contextLimit: 200_000,
@@ -729,6 +738,7 @@ describe("event handler integration", () => {
729738
userGroupId: "test:user",
730739
injectedMemories: false,
731740
lastInjectionFactUuids: [],
741+
visibleFactUuids: [],
732742
messageCount: 0,
733743
pendingMessages: [],
734744
contextLimit: 200_000,
@@ -779,6 +789,7 @@ describe("event handler integration", () => {
779789
userGroupId: "test:user",
780790
injectedMemories: false,
781791
lastInjectionFactUuids: [],
792+
visibleFactUuids: [],
782793
messageCount: 0,
783794
pendingMessages: [],
784795
contextLimit: 200_000,
@@ -827,6 +838,7 @@ describe("event handler integration", () => {
827838
userGroupId: "test:user",
828839
injectedMemories: false,
829840
lastInjectionFactUuids: [],
841+
visibleFactUuids: [],
830842
messageCount: 0,
831843
pendingMessages: [],
832844
contextLimit: 200_000,
@@ -877,6 +889,7 @@ describe("event handler integration", () => {
877889
userGroupId: "test:user",
878890
injectedMemories: false,
879891
lastInjectionFactUuids: [],
892+
visibleFactUuids: [],
880893
messageCount: 0,
881894
pendingMessages: [],
882895
contextLimit: 200_000,

src/services/session-snapshot.test.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe("session-snapshot (planned)", () => {
1919
userGroupId: "test:user",
2020
injectedMemories: true,
2121
lastInjectionFactUuids: ["fact-1", "fact-2"],
22+
visibleFactUuids: [],
2223
messageCount: 10,
2324
pendingMessages: ["User: Hello", "Assistant: Hi"],
2425
contextLimit: 200_000,
@@ -36,6 +37,7 @@ describe("session-snapshot (planned)", () => {
3637
userGroupId: "test:user",
3738
injectedMemories: false,
3839
lastInjectionFactUuids: [],
40+
visibleFactUuids: [],
3941
messageCount: 0,
4042
pendingMessages: [],
4143
contextLimit: 200_000,
@@ -52,6 +54,7 @@ describe("session-snapshot (planned)", () => {
5254
userGroupId: "test:user",
5355
injectedMemories: false,
5456
lastInjectionFactUuids: [],
57+
visibleFactUuids: [],
5558
messageCount: 0,
5659
pendingMessages: [],
5760
contextLimit: 200_000,
@@ -67,6 +70,7 @@ describe("session-snapshot (planned)", () => {
6770
userGroupId: "test:user",
6871
injectedMemories: true,
6972
lastInjectionFactUuids: ["fact-1"],
73+
visibleFactUuids: [],
7074
messageCount: 10,
7175
pendingMessages: ["User: Discuss API design"],
7276
contextLimit: 200_000,
@@ -90,6 +94,7 @@ describe("session-snapshot (planned)", () => {
9094
userGroupId: "test:user",
9195
injectedMemories: true,
9296
lastInjectionFactUuids: ["fact-1"],
97+
visibleFactUuids: [],
9398
messageCount: 10,
9499
pendingMessages: ['Message with "quotes"'],
95100
contextLimit: 200_000,
@@ -106,7 +111,7 @@ describe("session-snapshot (planned)", () => {
106111
describe("loadSessionSnapshot", () => {
107112
it("should deserialize session state from JSON", () => {
108113
const json =
109-
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":true,"lastInjectionFactUuids":["fact-1"],"messageCount":10,"pendingMessages":["User: Hello"],"contextLimit":200000,"isMain":true}';
114+
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":true,"lastInjectionFactUuids":["fact-1"],"visibleFactUuids":[],"messageCount":10,"pendingMessages":["User: Hello"],"contextLimit":200000,"isMain":true}';
110115
// Expected: loadSessionSnapshot(json) => SessionState object
111116
const state = JSON.parse(json) as SessionState;
112117
assertEquals(state.groupId, "test:project");
@@ -124,7 +129,7 @@ describe("session-snapshot (planned)", () => {
124129

125130
it("should handle missing optional fields gracefully", () => {
126131
const minimalJson =
127-
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"messageCount":0,"pendingMessages":[],"contextLimit":200000,"isMain":true}';
132+
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"visibleFactUuids":[],"messageCount":0,"pendingMessages":[],"contextLimit":200000,"isMain":true}';
128133
// Expected: loadSessionSnapshot fills defaults for optional fields
129134
const state = JSON.parse(minimalJson) as SessionState;
130135
assertEquals(state.injectedMemories, false);
@@ -146,15 +151,15 @@ describe("session-snapshot (planned)", () => {
146151
it("should restore timestamp field", () => {
147152
const timestamp = Date.now();
148153
const json =
149-
`{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"messageCount":0,"pendingMessages":[],"contextLimit":200000,"isMain":true,"timestamp":${timestamp}}`;
154+
`{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"visibleFactUuids":[],"messageCount":0,"pendingMessages":[],"contextLimit":200000,"isMain":true,"timestamp":${timestamp}}`;
150155
// Expected: loadSessionSnapshot preserves timestamp
151156
const state = JSON.parse(json) as SessionState & { timestamp?: number };
152157
assertEquals(state.timestamp, timestamp);
153158
});
154159

155160
it("should handle escaped characters in pending messages", () => {
156161
const json =
157-
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"messageCount":1,"pendingMessages":["User: \\"quoted\\" text\\nwith newline"],"contextLimit":200000,"isMain":true}';
162+
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"visibleFactUuids":[],"messageCount":1,"pendingMessages":["User: \\"quoted\\" text\\nwith newline"],"contextLimit":200000,"isMain":true}';
158163
// Expected: loadSessionSnapshot correctly parses escaped strings
159164
const state = JSON.parse(json) as SessionState;
160165
assertEquals(state.pendingMessages[0].includes('"quoted"'), true);
@@ -163,7 +168,7 @@ describe("session-snapshot (planned)", () => {
163168

164169
it("should validate contextLimit is a number", () => {
165170
const json =
166-
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"messageCount":0,"pendingMessages":[],"contextLimit":"not a number","isMain":true}';
171+
'{"groupId":"test:project","userGroupId":"test:user","injectedMemories":false,"lastInjectionFactUuids":[],"visibleFactUuids":[],"messageCount":0,"pendingMessages":[],"contextLimit":"not a number","isMain":true}';
167172
// Expected: loadSessionSnapshot validates type of contextLimit
168173
const state = JSON.parse(json) as SessionState;
169174
assertEquals(typeof state.contextLimit, "string"); // Invalid but parsed

0 commit comments

Comments
 (0)