@tyvm/knowhow 0.0.48 → 0.0.50

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 (42) hide show
  1. package/package.json +1 -1
  2. package/src/agents/base/base.ts +9 -1
  3. package/src/auth/browserLogin.ts +4 -4
  4. package/src/chat/modules/AgentModule.ts +22 -3
  5. package/src/embeddings.ts +2 -4
  6. package/src/login.ts +27 -20
  7. package/src/plugins/GitPlugin.ts +10 -5
  8. package/src/processors/CustomVariables.ts +48 -70
  9. package/src/processors/TokenCompressor.ts +5 -4
  10. package/src/processors/ToolResponseCache.ts +6 -9
  11. package/src/services/KnowhowClient.ts +43 -16
  12. package/tests/processors/CustomVariables.test.ts +110 -55
  13. package/tests/processors/TokenCompressor.test.ts +48 -49
  14. package/tests/processors/ToolResponseCache.test.ts +309 -261
  15. package/ts_build/package.json +1 -1
  16. package/ts_build/src/agents/base/base.d.ts +5 -0
  17. package/ts_build/src/agents/base/base.js +3 -1
  18. package/ts_build/src/agents/base/base.js.map +1 -1
  19. package/ts_build/src/chat/modules/AgentModule.js +9 -1
  20. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  21. package/ts_build/src/embeddings.js +2 -4
  22. package/ts_build/src/embeddings.js.map +1 -1
  23. package/ts_build/src/login.d.ts +4 -0
  24. package/ts_build/src/login.js +21 -16
  25. package/ts_build/src/login.js.map +1 -1
  26. package/ts_build/src/plugins/GitPlugin.js +5 -5
  27. package/ts_build/src/plugins/GitPlugin.js.map +1 -1
  28. package/ts_build/src/processors/CustomVariables.js +36 -47
  29. package/ts_build/src/processors/CustomVariables.js.map +1 -1
  30. package/ts_build/src/processors/TokenCompressor.js +2 -2
  31. package/ts_build/src/processors/TokenCompressor.js.map +1 -1
  32. package/ts_build/src/processors/ToolResponseCache.js +6 -8
  33. package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
  34. package/ts_build/src/services/KnowhowClient.d.ts +2 -1
  35. package/ts_build/src/services/KnowhowClient.js +36 -16
  36. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  37. package/ts_build/tests/processors/CustomVariables.test.js +41 -38
  38. package/ts_build/tests/processors/CustomVariables.test.js.map +1 -1
  39. package/ts_build/tests/processors/TokenCompressor.test.js +4 -5
  40. package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
  41. package/ts_build/tests/processors/ToolResponseCache.test.js +89 -78
  42. package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -1
@@ -8,7 +8,7 @@ describe("CustomVariables", () => {
8
8
 
9
9
  beforeEach(() => {
10
10
  mockToolsService = {
11
- addTool: jest.fn(),
11
+ addTools: jest.fn(),
12
12
  addFunctions: jest.fn(),
13
13
  getTool: jest.fn().mockReturnValue(undefined),
14
14
  callTool: jest.fn(),
@@ -23,12 +23,12 @@ describe("CustomVariables", () => {
23
23
 
24
24
  describe("constructor", () => {
25
25
  it("should register all variable tools with ToolsService", () => {
26
- expect(mockToolsService.addTool).toHaveBeenCalledTimes(5);
27
- expect(mockToolsService.addFunctions).toHaveBeenCalledTimes(5);
26
+ expect(mockToolsService.addTools).toHaveBeenCalledTimes(1);
27
+ expect(mockToolsService.addFunctions).toHaveBeenCalledTimes(1);
28
28
 
29
29
  // Verify all tools are registered
30
- const addToolCalls = mockToolsService.addTool.mock.calls;
31
- const toolNames = addToolCalls.map(call => call[0].function.name);
30
+ const addToolCalls = mockToolsService.addTools.mock.calls;
31
+ const toolNames = addToolCalls[0][0].map((args) => args.function.name);
32
32
 
33
33
  expect(toolNames).toContain("setVariable");
34
34
  expect(toolNames).toContain("getVariable");
@@ -37,12 +37,15 @@ describe("CustomVariables", () => {
37
37
  expect(toolNames).toContain("deleteVariable");
38
38
  });
39
39
 
40
- it("should not register tools if they already exist", () => {
41
- mockToolsService.getTool.mockReturnValue({ type: "function", function: { name: "setVariable" } } as any);
40
+ it("should overwrite tools if they already exist", () => {
41
+ mockToolsService.getTool.mockReturnValue({
42
+ type: "function",
43
+ function: { name: "setVariable" },
44
+ } as any);
42
45
  const newCustomVariables = new CustomVariables(mockToolsService);
43
46
 
44
- // Should still be called only from the first instance (5 times)
45
- expect(mockToolsService.addTool).toHaveBeenCalledTimes(5);
47
+ // Should still be called once per instance creation
48
+ expect(mockToolsService.addTools).toHaveBeenCalledTimes(2);
46
49
  });
47
50
  });
48
51
 
@@ -51,7 +54,9 @@ describe("CustomVariables", () => {
51
54
 
52
55
  beforeEach(() => {
53
56
  const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
54
- const setVariableCall = addFunctionsCalls.find(call => call[0].setVariable);
57
+ const setVariableCall = addFunctionsCalls.find(
58
+ (call) => call[0].setVariable
59
+ );
55
60
  setVariableFunction = setVariableCall[0].setVariable;
56
61
  });
57
62
 
@@ -86,7 +91,9 @@ describe("CustomVariables", () => {
86
91
 
87
92
  beforeEach(() => {
88
93
  const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
89
- const setVariableCall = addFunctionsCalls.find(call => call[0].setVariable);
94
+ const setVariableCall = addFunctionsCalls.find(
95
+ (call) => call[0].setVariable
96
+ );
90
97
  setVariableFunction = setVariableCall[0].setVariable;
91
98
  });
92
99
 
@@ -122,8 +129,12 @@ describe("CustomVariables", () => {
122
129
 
123
130
  beforeEach(() => {
124
131
  const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
125
- const setVariableCall = addFunctionsCalls.find(call => call[0].setVariable);
126
- const getVariableCall = addFunctionsCalls.find(call => call[0].getVariable);
132
+ const setVariableCall = addFunctionsCalls.find(
133
+ (call) => call[0].setVariable
134
+ );
135
+ const getVariableCall = addFunctionsCalls.find(
136
+ (call) => call[0].getVariable
137
+ );
127
138
  setVariableFunction = setVariableCall[0].setVariable;
128
139
  getVariableFunction = getVariableCall[0].getVariable;
129
140
  });
@@ -166,8 +177,12 @@ describe("CustomVariables", () => {
166
177
 
167
178
  beforeEach(() => {
168
179
  const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
169
- const setVariableCall = addFunctionsCalls.find(call => call[0].setVariable);
170
- const listVariablesCall = addFunctionsCalls.find(call => call[0].listVariables);
180
+ const setVariableCall = addFunctionsCalls.find(
181
+ (call) => call[0].setVariable
182
+ );
183
+ const listVariablesCall = addFunctionsCalls.find(
184
+ (call) => call[0].listVariables
185
+ );
171
186
  setVariableFunction = setVariableCall[0].setVariable;
172
187
  listVariablesFunction = listVariablesCall[0].listVariables;
173
188
  });
@@ -204,8 +219,12 @@ describe("CustomVariables", () => {
204
219
 
205
220
  beforeEach(() => {
206
221
  const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
207
- const setVariableCall = addFunctionsCalls.find(call => call[0].setVariable);
208
- const deleteVariableCall = addFunctionsCalls.find(call => call[0].deleteVariable);
222
+ const setVariableCall = addFunctionsCalls.find(
223
+ (call) => call[0].setVariable
224
+ );
225
+ const deleteVariableCall = addFunctionsCalls.find(
226
+ (call) => call[0].deleteVariable
227
+ );
209
228
  setVariableFunction = setVariableCall[0].setVariable;
210
229
  deleteVariableFunction = deleteVariableCall[0].deleteVariable;
211
230
  });
@@ -213,7 +232,9 @@ describe("CustomVariables", () => {
213
232
  it("should delete existing variables", () => {
214
233
  setVariableFunction("testVar", "test value");
215
234
  const result = deleteVariableFunction("testVar");
216
- expect(result).toContain('Variable "testVar" has been deleted successfully');
235
+ expect(result).toContain(
236
+ 'Variable "testVar" has been deleted successfully'
237
+ );
217
238
  expect(customVariables.getVariableNames()).not.toContain("testVar");
218
239
  });
219
240
 
@@ -229,11 +250,17 @@ describe("CustomVariables", () => {
229
250
  });
230
251
 
231
252
  describe("storeToolCallToVariable functionality", () => {
232
- let storeToolCallFunction: (varName: string, toolName: string, toolArgs: string) => Promise<string>;
253
+ let storeToolCallFunction: (
254
+ varName: string,
255
+ toolName: string,
256
+ toolArgs: string
257
+ ) => Promise<string>;
233
258
 
234
259
  beforeEach(() => {
235
260
  const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
236
- const storeToolCallCall = addFunctionsCalls.find(call => call[0].storeToolCallToVariable);
261
+ const storeToolCallCall = addFunctionsCalls.find(
262
+ (call) => call[0].storeToolCallToVariable
263
+ );
237
264
  storeToolCallFunction = storeToolCallCall[0].storeToolCallToVariable;
238
265
  });
239
266
 
@@ -243,38 +270,56 @@ describe("CustomVariables", () => {
243
270
  toolCallId: "test-call-id",
244
271
  functionName: "testTool",
245
272
  functionArgs: { param1: "value1" },
246
- functionResp: { success: true, data: "test data" }
273
+ functionResp: { success: true, data: "test data" },
247
274
  };
248
275
  mockToolsService.callTool.mockResolvedValue(mockResult);
249
276
 
250
- const result = await storeToolCallFunction("resultVar", "testTool", '{"param1": "value1"}');
277
+ const result = await storeToolCallFunction(
278
+ "resultVar",
279
+ "testTool",
280
+ '{"param1": "value1"}'
281
+ );
251
282
 
252
- expect(result).toContain('Tool call result for "testTool" has been stored in variable "resultVar"');
283
+ expect(result).toContain(
284
+ 'Tool call result for "testTool" has been stored in variable "resultVar"'
285
+ );
253
286
  expect(mockToolsService.callTool).toHaveBeenCalledWith({
254
287
  id: expect.any(String),
255
288
  type: "function",
256
289
  function: {
257
290
  name: "testTool",
258
- arguments: { param1: "value1" }
259
- }
291
+ arguments: { param1: "value1" },
292
+ },
260
293
  });
261
294
  });
262
295
 
263
296
  it("should return error for invalid JSON arguments", async () => {
264
- const result = await storeToolCallFunction("resultVar", "testTool", "invalid json");
297
+ const result = await storeToolCallFunction(
298
+ "resultVar",
299
+ "testTool",
300
+ "invalid json"
301
+ );
265
302
  expect(result).toContain("Error: Invalid JSON in toolArgs parameter");
266
303
  });
267
304
 
268
305
  it("should return error for invalid variable names", async () => {
269
- const result = await storeToolCallFunction("invalid-name", "testTool", "{}");
306
+ const result = await storeToolCallFunction(
307
+ "invalid-name",
308
+ "testTool",
309
+ "{}"
310
+ );
270
311
  expect(result).toContain("Error: Invalid variable name");
271
312
  });
272
313
 
273
314
  it("should handle tool execution errors", async () => {
274
- mockToolsService.callTool.mockRejectedValue(new Error("Tool execution failed"));
315
+ mockToolsService.callTool.mockRejectedValue(
316
+ new Error("Tool execution failed")
317
+ );
275
318
 
276
319
  const result = await storeToolCallFunction("resultVar", "testTool", "{}");
277
- expect(result).toContain("Error storing tool call result: Tool execution failed");
320
+ expect(result).toContain(
321
+ "Error storing tool call result: Tool execution failed"
322
+ );
278
323
  });
279
324
  });
280
325
 
@@ -283,7 +328,9 @@ describe("CustomVariables", () => {
283
328
 
284
329
  beforeEach(() => {
285
330
  const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
286
- const setVariableCall = addFunctionsCalls.find(call => call[0].setVariable);
331
+ const setVariableCall = addFunctionsCalls.find(
332
+ (call) => call[0].setVariable
333
+ );
287
334
  setVariableFunction = setVariableCall[0].setVariable;
288
335
  });
289
336
 
@@ -294,14 +341,16 @@ describe("CustomVariables", () => {
294
341
  const messages = [
295
342
  {
296
343
  role: "user" as const,
297
- content: "{{greeting}} {{userName}}, how are you today?"
298
- }
344
+ content: "{{greeting}} {{userName}}, how are you today?",
345
+ },
299
346
  ];
300
347
 
301
348
  const processor = customVariables.createProcessor();
302
349
  const modifiedMessages = [...messages];
303
350
  await processor(messages, modifiedMessages);
304
- expect(modifiedMessages[0].content).toBe("Hello Alice, how are you today?");
351
+ expect(modifiedMessages[0].content).toBe(
352
+ "Hello Alice, how are you today?"
353
+ );
305
354
  });
306
355
 
307
356
  it("should handle multiple substitutions in single message", async () => {
@@ -312,8 +361,8 @@ describe("CustomVariables", () => {
312
361
  const messages = [
313
362
  {
314
363
  role: "user" as const,
315
- content: "{{var1}} and {{var2}} and {{var3}}"
316
- }
364
+ content: "{{var1}} and {{var2}} and {{var3}}",
365
+ },
317
366
  ];
318
367
 
319
368
  const processor = customVariables.createProcessor();
@@ -328,30 +377,34 @@ describe("CustomVariables", () => {
328
377
  const messages = [
329
378
  {
330
379
  role: "user" as const,
331
- content: "Configuration: {{config}}"
332
- }
380
+ content: "Configuration: {{config}}",
381
+ },
333
382
  ];
334
383
 
335
384
  const processor = customVariables.createProcessor();
336
385
  const modifiedMessages = [...messages];
337
386
  await processor(messages, modifiedMessages);
338
-
339
- expect(modifiedMessages[0].content).toBe('Configuration: {"api":"v1","timeout":5000}');
387
+
388
+ expect(modifiedMessages[0].content).toBe(
389
+ 'Configuration: {"api":"v1","timeout":5000}'
390
+ );
340
391
  });
341
392
 
342
393
  it("should return error for undefined variables", async () => {
343
394
  const messages = [
344
395
  {
345
396
  role: "user" as const,
346
- content: "Hello {{undefinedVar}}"
347
- }
397
+ content: "Hello {{undefinedVar}}",
398
+ },
348
399
  ];
349
400
 
350
401
  const processor = customVariables.createProcessor();
351
402
  const modifiedMessages = [...messages];
352
403
  await processor(messages, modifiedMessages);
353
-
354
- expect(modifiedMessages[0].content).toBe('{{ERROR: Variable "undefinedVar" is not defined}}');
404
+
405
+ expect(modifiedMessages[0].content).toBe(
406
+ '{{ERROR: Variable "undefinedVar" is not defined}}'
407
+ );
355
408
  });
356
409
 
357
410
  it("should handle partial substitutions with mixed defined/undefined vars", async () => {
@@ -360,15 +413,17 @@ describe("CustomVariables", () => {
360
413
  const messages = [
361
414
  {
362
415
  role: "user" as const,
363
- content: "{{defined}} and {{undefined}}"
364
- }
416
+ content: "{{defined}} and {{undefined}}",
417
+ },
365
418
  ];
366
419
 
367
420
  const processor = customVariables.createProcessor();
368
421
  const modifiedMessages = [...messages];
369
422
  await processor(messages, modifiedMessages);
370
-
371
- expect(modifiedMessages[0].content).toBe('value and {{ERROR: Variable "undefined" is not defined}}');
423
+
424
+ expect(modifiedMessages[0].content).toBe(
425
+ 'value and {{ERROR: Variable "undefined" is not defined}}'
426
+ );
372
427
  });
373
428
 
374
429
  it("should preserve message structure while substituting content", async () => {
@@ -378,18 +433,18 @@ describe("CustomVariables", () => {
378
433
  {
379
434
  role: "assistant" as const,
380
435
  content: "Original {{test}} content",
381
- metadata: { id: "test-id" }
382
- }
436
+ metadata: { id: "test-id" },
437
+ },
383
438
  ];
384
439
 
385
440
  const processor = customVariables.createProcessor();
386
441
  const modifiedMessages = [...messages];
387
442
  await processor(messages, modifiedMessages);
388
-
443
+
389
444
  expect(modifiedMessages[0]).toEqual({
390
445
  role: "assistant",
391
446
  content: "Original replaced content",
392
- metadata: { id: "test-id" }
447
+ metadata: { id: "test-id" },
393
448
  });
394
449
  });
395
450
 
@@ -400,8 +455,8 @@ describe("CustomVariables", () => {
400
455
  const messages = [
401
456
  {
402
457
  role: "user" as const,
403
- content: "{{outerVar}} value"
404
- }
458
+ content: "{{outerVar}} value",
459
+ },
405
460
  ];
406
461
 
407
462
  const processor = customVariables.createProcessor();
@@ -417,8 +472,8 @@ describe("CustomVariables", () => {
417
472
  const messages = [
418
473
  {
419
474
  role: "user" as const,
420
- content: "Empty: '{{empty}}' Spaces: '{{spaces}}'"
421
- }
475
+ content: "Empty: '{{empty}}' Spaces: '{{spaces}}'",
476
+ },
422
477
  ];
423
478
 
424
479
  const processor = customVariables.createProcessor();
@@ -8,11 +8,11 @@ describe("TokenCompressor", () => {
8
8
 
9
9
  beforeEach(() => {
10
10
  mockToolsService = {
11
- addTool: jest.fn(),
11
+ addTools: jest.fn(),
12
12
  addFunctions: jest.fn(),
13
13
  getTool: jest.fn(),
14
14
  } as any;
15
-
15
+
16
16
  tokenCompressor = new TokenCompressor(mockToolsService);
17
17
  });
18
18
 
@@ -22,17 +22,16 @@ describe("TokenCompressor", () => {
22
22
 
23
23
  describe("constructor", () => {
24
24
  it("should register expandTokens tool with ToolsService", () => {
25
- expect(mockToolsService.getTool).toHaveBeenCalledWith("expandTokens");
26
- expect(mockToolsService.addTool).toHaveBeenCalled();
25
+ expect(mockToolsService.addTools).toHaveBeenCalled();
27
26
  expect(mockToolsService.addFunctions).toHaveBeenCalled();
28
27
  });
29
28
 
30
- it("should not register tool if already exists", () => {
29
+ it("should overwrite tool if already exists", () => {
31
30
  mockToolsService.getTool.mockReturnValue({ type: "function", function: { name: "expandTokens" } } as any);
32
31
  const newCompressor = new TokenCompressor(mockToolsService);
33
-
32
+
34
33
  // Should only be called once from the first instance
35
- expect(mockToolsService.addTool).toHaveBeenCalledTimes(1);
34
+ expect(mockToolsService.addTools).toHaveBeenCalledTimes(2);
36
35
  });
37
36
 
38
37
  it("should work without ToolsService", () => {
@@ -50,7 +49,7 @@ describe("TokenCompressor", () => {
50
49
  it("should compress long strings using chunking", () => {
51
50
  const longContent = "x".repeat(40000); // Well above threshold (40k chars = 10k tokens > 8k maxTokens)
52
51
  const result = tokenCompressor.compressContent(longContent);
53
-
52
+
54
53
  expect(result).toContain("[COMPRESSED_STRING");
55
54
  expect(result).toContain("Key:");
56
55
  expect(result).toContain("expandTokens");
@@ -64,7 +63,7 @@ describe("TokenCompressor", () => {
64
63
  };
65
64
  const content = JSON.stringify(largeJson);
66
65
  const result = tokenCompressor.compressContent(content);
67
-
66
+
68
67
  // Should be parsed and compressed as JSON
69
68
  const parsed = JSON.parse(result);
70
69
  expect(typeof parsed.data).toBe("string");
@@ -79,7 +78,7 @@ describe("TokenCompressor", () => {
79
78
  };
80
79
  const content = JSON.stringify(complexJson);
81
80
  const result = tokenCompressor.compressContent(content);
82
-
81
+
83
82
  const parsed = JSON.parse(result);
84
83
  expect(parsed.smallProp).toBe("small"); // Should remain unchanged
85
84
  expect(parsed.largeProp).toContain("[COMPRESSED_JSON_PROPERTY"); // Should be compressed
@@ -96,11 +95,11 @@ describe("TokenCompressor", () => {
96
95
  it("should create chunked compression for large strings", () => {
97
96
  const content = "x".repeat(20000);
98
97
  const result = tokenCompressor.compressStringInChunks(content);
99
-
98
+
100
99
  expect(result).toContain("[COMPRESSED_STRING");
101
100
  expect(result).toContain("chunks");
102
101
  expect(result).toContain("Key:");
103
-
102
+
104
103
  // Should have stored chunks in storage
105
104
  expect(tokenCompressor.getStorageSize()).toBeGreaterThan(0);
106
105
  });
@@ -108,10 +107,10 @@ describe("TokenCompressor", () => {
108
107
  it("should create chain of NEXT_CHUNK_KEY references", () => {
109
108
  const content = "a".repeat(20000);
110
109
  tokenCompressor.compressStringInChunks(content);
111
-
110
+
112
111
  const keys = tokenCompressor.getStorageKeys();
113
112
  expect(keys.length).toBeGreaterThan(1);
114
-
113
+
115
114
  // Check that chunks are linked
116
115
  const firstChunk = tokenCompressor.retrieveString(keys[0]);
117
116
  expect(firstChunk).toContain("NEXT_CHUNK_KEY");
@@ -123,7 +122,7 @@ describe("TokenCompressor", () => {
123
122
  // Create array that will definitely create chunks > 4000 chars (50 elements × 200 chars = 10k+ chars per potential chunk)
124
123
  const largeArray = new Array(15).fill("x".repeat(2200));
125
124
  const result = tokenCompressor.compressJsonProperties(largeArray);
126
-
125
+
127
126
  // Should contain compression markers
128
127
  expect(JSON.stringify(result)).toContain("[COMPRESSED_JSON_ARRAY_CHUNK");
129
128
  });
@@ -136,7 +135,7 @@ describe("TokenCompressor", () => {
136
135
  }
137
136
  }
138
137
  };
139
-
138
+
140
139
  const result = tokenCompressor.compressJsonProperties(nestedObj);
141
140
  expect(JSON.stringify(result)).toContain("[COMPRESSED_JSON");
142
141
  });
@@ -148,7 +147,7 @@ describe("TokenCompressor", () => {
148
147
  { data: "y".repeat(5000) }, // Large object
149
148
  "another small item"
150
149
  ];
151
-
150
+
152
151
  const result = tokenCompressor.compressJsonProperties(mixedArray);
153
152
  expect(Array.isArray(result)).toBe(true);
154
153
  });
@@ -166,9 +165,9 @@ describe("TokenCompressor", () => {
166
165
  role: "user",
167
166
  content: "x".repeat(20000)
168
167
  };
169
-
168
+
170
169
  await tokenCompressor.compressMessage(message);
171
-
170
+
172
171
  expect(message.content).toContain("[COMPRESSED_STRING");
173
172
  });
174
173
 
@@ -180,12 +179,12 @@ describe("TokenCompressor", () => {
180
179
  { type: "image_url", image_url: { url: "http://example.com/img.jpg" } }
181
180
  ]
182
181
  };
183
-
182
+
184
183
  await tokenCompressor.compressMessage(message);
185
-
184
+
186
185
  const textContent = message.content[0] as { type: string; text: string };
187
186
  expect(textContent.text).toContain("[COMPRESSED_STRING");
188
-
187
+
189
188
  // Non-text content should remain unchanged
190
189
  const imageContent = message.content[1] as { type: string; image_url: { url: string } };
191
190
  expect(imageContent.image_url.url).toBe("http://example.com/img.jpg");
@@ -204,9 +203,9 @@ describe("TokenCompressor", () => {
204
203
  { role: "user", content: "x".repeat(20000) },
205
204
  { role: "assistant", content: "short response" }
206
205
  ];
207
-
206
+
208
207
  await processor([], messages);
209
-
208
+
210
209
  expect(messages[0].content).toContain("[COMPRESSED_STRING");
211
210
  expect(messages[1].content).toBe("short response"); // Should remain short
212
211
  });
@@ -219,9 +218,9 @@ describe("TokenCompressor", () => {
219
218
  { role: "user", content: "x".repeat(20000) },
220
219
  { role: "assistant", content: "x".repeat(20000) }
221
220
  ];
222
-
221
+
223
222
  await processor([], messages);
224
-
223
+
225
224
  expect(messages[0].content).toContain("[COMPRESSED_STRING");
226
225
  expect(messages[1].content).not.toContain("[COMPRESSED_STRING");
227
226
  });
@@ -231,10 +230,10 @@ describe("TokenCompressor", () => {
231
230
  it("should store and retrieve strings", () => {
232
231
  const key = "test_key";
233
232
  const value = "test_value";
234
-
233
+
235
234
  tokenCompressor.storeString(key, value);
236
235
  const retrieved = tokenCompressor.retrieveString(key);
237
-
236
+
238
237
  expect(retrieved).toBe(value);
239
238
  });
240
239
 
@@ -246,9 +245,9 @@ describe("TokenCompressor", () => {
246
245
  it("should clear all storage", () => {
247
246
  tokenCompressor.storeString("key1", "value1");
248
247
  tokenCompressor.storeString("key2", "value2");
249
-
248
+
250
249
  expect(tokenCompressor.getStorageSize()).toBe(2);
251
-
250
+
252
251
  tokenCompressor.clearStorage();
253
252
  expect(tokenCompressor.getStorageSize()).toBe(0);
254
253
  });
@@ -256,7 +255,7 @@ describe("TokenCompressor", () => {
256
255
  it("should return storage keys", () => {
257
256
  tokenCompressor.storeString("key1", "value1");
258
257
  tokenCompressor.storeString("key2", "value2");
259
-
258
+
260
259
  const keys = tokenCompressor.getStorageKeys();
261
260
  expect(keys).toContain("key1");
262
261
  expect(keys).toContain("key2");
@@ -266,23 +265,23 @@ describe("TokenCompressor", () => {
266
265
  describe("configuration", () => {
267
266
  it("should allow setting compression threshold", () => {
268
267
  tokenCompressor.setCompressionThreshold(1000);
269
-
268
+
270
269
  const content = "x".repeat(5000); // Above new threshold (5000 chars = 1250 tokens > 1000)
271
270
  const result = tokenCompressor.compressContent(content);
272
-
271
+
273
272
  expect(result).toContain("[COMPRESSED_STRING");
274
273
  });
275
274
 
276
275
  it("should adjust character limit with threshold", () => {
277
276
  const originalThreshold = 4000;
278
277
  const newThreshold = 2000;
279
-
278
+
280
279
  tokenCompressor.setCompressionThreshold(newThreshold);
281
-
280
+
282
281
  // Should compress at lower threshold
283
282
  const content = "x".repeat(9000); // Above new threshold (9000 chars = 2250 tokens > 2000)
284
283
  const result = tokenCompressor.compressContent(content);
285
-
284
+
286
285
  expect(result).toContain("[COMPRESSED_STRING");
287
286
  });
288
287
  });
@@ -291,7 +290,7 @@ describe("TokenCompressor", () => {
291
290
  it("should register expandTokens function correctly", () => {
292
291
  const toolsServiceCalls = mockToolsService.addFunctions.mock.calls;
293
292
  expect(toolsServiceCalls.length).toBe(1);
294
-
293
+
295
294
  const functions = toolsServiceCalls[0][0];
296
295
  expect(functions.expandTokens).toBeDefined();
297
296
  expect(typeof functions.expandTokens).toBe("function");
@@ -301,11 +300,11 @@ describe("TokenCompressor", () => {
301
300
  const key = "test_key";
302
301
  const value = "test_value";
303
302
  tokenCompressor.storeString(key, value);
304
-
303
+
305
304
  const toolsServiceCalls = mockToolsService.addFunctions.mock.calls;
306
305
  const functions = toolsServiceCalls[0][0];
307
306
  const result = functions.expandTokens(key);
308
-
307
+
309
308
  expect(result).toBe(value);
310
309
  });
311
310
 
@@ -313,7 +312,7 @@ describe("TokenCompressor", () => {
313
312
  const toolsServiceCalls = mockToolsService.addFunctions.mock.calls;
314
313
  const functions = toolsServiceCalls[0][0];
315
314
  const result = functions.expandTokens("non_existent");
316
-
315
+
317
316
  expect(result).toContain("Error: No data found for key");
318
317
  expect(result).toContain("Available keys:");
319
318
  });
@@ -328,7 +327,7 @@ describe("TokenCompressor", () => {
328
327
  it("should handle malformed JSON gracefully", () => {
329
328
  const malformedJson = '{"incomplete": "json"'; // Missing closing brace
330
329
  const result = tokenCompressor.compressContent(malformedJson);
331
-
330
+
332
331
  // Should treat as string, not JSON
333
332
  if (result.length > malformedJson.length) {
334
333
  expect(result).toContain("[COMPRESSED_STRING");
@@ -340,13 +339,13 @@ describe("TokenCompressor", () => {
340
339
  it("should handle very large objects without stack overflow", () => {
341
340
  const deepObject = { level: 0 } as any;
342
341
  let current = deepObject;
343
-
342
+
344
343
  // Create deeply nested object
345
344
  for (let i = 1; i < 100; i++) {
346
345
  current.next = { level: i, data: "x".repeat(100) };
347
346
  current = current.next;
348
347
  }
349
-
348
+
350
349
  expect(() => {
351
350
  tokenCompressor.compressJsonProperties(deepObject);
352
351
  }).not.toThrow();
@@ -355,11 +354,11 @@ describe("TokenCompressor", () => {
355
354
  it("should handle circular references in JSON", () => {
356
355
  const obj: any = { name: "test" };
357
356
  obj.self = obj; // Create circular reference
358
-
357
+
359
358
  expect(() => {
360
359
  JSON.stringify(obj); // This should throw
361
360
  }).toThrow();
362
-
361
+
363
362
  // Our compressor should handle this gracefully by not receiving circular JSON
364
363
  const safeObj = { name: "test", data: "x".repeat(10000) };
365
364
  expect(() => {
@@ -372,9 +371,9 @@ describe("TokenCompressor", () => {
372
371
  it("should handle large arrays efficiently", () => {
373
372
  const largeArray = new Array(1000).fill("x".repeat(100));
374
373
  const startTime = Date.now();
375
-
374
+
376
375
  tokenCompressor.compressJsonProperties(largeArray);
377
-
376
+
378
377
  const duration = Date.now() - startTime;
379
378
  expect(duration).toBeLessThan(5000); // Should complete within 5 seconds
380
379
  });
@@ -383,9 +382,9 @@ describe("TokenCompressor", () => {
383
382
  const content = "small content";
384
383
  const result1 = tokenCompressor.compressContent(content);
385
384
  const result2 = tokenCompressor.compressContent(result1);
386
-
385
+
387
386
  expect(result1).toBe(content);
388
387
  expect(result2).toBe(content);
389
388
  });
390
389
  });
391
- });
390
+ });