@flink-app/flink 0.14.3 → 2.0.0-alpha.100

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 (280) hide show
  1. package/CHANGELOG.md +1051 -0
  2. package/SCHEMA_EXTRACTION_ANALYSIS.md +494 -0
  3. package/SIMPLE_AST_FEASIBILITY.md +570 -0
  4. package/bin/flink.ts +13 -2
  5. package/cli/build.ts +24 -44
  6. package/cli/clean.ts +13 -25
  7. package/cli/cli-utils.ts +190 -17
  8. package/cli/dev.ts +252 -0
  9. package/cli/loadEnvFiles.ts +116 -0
  10. package/cli/run.ts +45 -62
  11. package/dist/bin/flink.js +61 -2
  12. package/dist/cli/build.js +20 -25
  13. package/dist/cli/clean.js +12 -10
  14. package/dist/cli/cli-utils.d.ts +34 -3
  15. package/dist/cli/cli-utils.js +193 -12
  16. package/dist/cli/dev.d.ts +2 -0
  17. package/dist/cli/dev.js +279 -0
  18. package/dist/cli/loadEnvFiles.d.ts +30 -0
  19. package/dist/cli/loadEnvFiles.js +113 -0
  20. package/dist/cli/run.js +47 -46
  21. package/dist/src/DependencyTracker.d.ts +44 -0
  22. package/dist/src/DependencyTracker.js +239 -0
  23. package/dist/src/FlinkApp.d.ts +163 -10
  24. package/dist/src/FlinkApp.js +847 -184
  25. package/dist/src/FlinkContext.d.ts +41 -0
  26. package/dist/src/FlinkErrors.d.ts +19 -6
  27. package/dist/src/FlinkErrors.js +36 -42
  28. package/dist/src/FlinkHttpHandler.d.ts +219 -26
  29. package/dist/src/FlinkHttpHandler.js +37 -1
  30. package/dist/src/FlinkJob.d.ts +10 -0
  31. package/dist/src/FlinkLog.d.ts +82 -18
  32. package/dist/src/FlinkLog.js +165 -13
  33. package/dist/src/FlinkLogFactory.d.ts +288 -0
  34. package/dist/src/FlinkLogFactory.js +619 -0
  35. package/dist/src/FlinkRepo.d.ts +10 -2
  36. package/dist/src/FlinkRepo.js +11 -1
  37. package/dist/src/FlinkRequestContext.d.ts +63 -0
  38. package/dist/src/FlinkRequestContext.js +74 -0
  39. package/dist/src/FlinkResponse.d.ts +6 -0
  40. package/dist/src/FlinkService.d.ts +38 -0
  41. package/dist/src/FlinkService.js +46 -0
  42. package/dist/src/LeaderElection.d.ts +45 -0
  43. package/dist/src/LeaderElection.js +269 -0
  44. package/dist/src/SchemaCache.d.ts +84 -0
  45. package/dist/src/SchemaCache.js +289 -0
  46. package/dist/src/TypeScriptCompiler.d.ts +161 -51
  47. package/dist/src/TypeScriptCompiler.js +1253 -617
  48. package/dist/src/TypeScriptUtils.js +4 -0
  49. package/dist/src/ai/AgentRunner.d.ts +39 -0
  50. package/dist/src/ai/AgentRunner.js +760 -0
  51. package/dist/src/ai/ConversationAgent.d.ts +279 -0
  52. package/dist/src/ai/ConversationAgent.js +404 -0
  53. package/dist/src/ai/ConversationFlinkAgent.d.ts +278 -0
  54. package/dist/src/ai/ConversationFlinkAgent.js +404 -0
  55. package/dist/src/ai/FlinkAgent.d.ts +690 -0
  56. package/dist/src/ai/FlinkAgent.js +729 -0
  57. package/dist/src/ai/FlinkTool.d.ts +135 -0
  58. package/dist/src/ai/FlinkTool.js +2 -0
  59. package/dist/src/ai/InMemoryConversationAgent.d.ts +121 -0
  60. package/dist/src/ai/InMemoryConversationAgent.js +209 -0
  61. package/dist/src/ai/LLMAdapter.d.ts +148 -0
  62. package/dist/src/ai/LLMAdapter.js +2 -0
  63. package/dist/src/ai/PersistentFlinkAgent.d.ts +278 -0
  64. package/dist/src/ai/PersistentFlinkAgent.js +403 -0
  65. package/dist/src/ai/SubAgentExecutor.d.ts +38 -0
  66. package/dist/src/ai/SubAgentExecutor.js +223 -0
  67. package/dist/src/ai/ToolExecutor.d.ts +64 -0
  68. package/dist/src/ai/ToolExecutor.js +497 -0
  69. package/dist/src/ai/agentInstructions.d.ts +68 -0
  70. package/dist/src/ai/agentInstructions.js +286 -0
  71. package/dist/src/ai/index.d.ts +8 -0
  72. package/dist/src/ai/index.js +26 -0
  73. package/dist/src/ai/instructionFileLoader.d.ts +44 -0
  74. package/dist/src/ai/instructionFileLoader.js +179 -0
  75. package/dist/src/auth/FlinkAuthPlugin.d.ts +1 -1
  76. package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
  77. package/dist/src/handlers/StreamWriterFactory.js +83 -0
  78. package/dist/src/index.d.ts +14 -0
  79. package/dist/src/index.js +17 -0
  80. package/dist/src/loadPluginSchemas.d.ts +45 -0
  81. package/dist/src/loadPluginSchemas.js +143 -0
  82. package/dist/src/schema-extraction/ComplexTypeDetection.d.ts +40 -0
  83. package/dist/src/schema-extraction/ComplexTypeDetection.js +75 -0
  84. package/dist/src/schema-extraction/TypeScriptSourceParser.d.ts +321 -0
  85. package/dist/src/schema-extraction/TypeScriptSourceParser.js +925 -0
  86. package/dist/src/schema-extraction/TypeScriptSourceParser.spec.d.ts +1 -0
  87. package/dist/src/schema-extraction/TypeScriptSourceParser.spec.js +233 -0
  88. package/dist/src/schema-extraction/TypeScriptTokenizer.d.ts +57 -0
  89. package/dist/src/schema-extraction/TypeScriptTokenizer.js +177 -0
  90. package/dist/src/schema-extraction/index.d.ts +2 -0
  91. package/dist/src/schema-extraction/index.js +20 -0
  92. package/dist/src/schema-extraction/types.d.ts +31 -0
  93. package/dist/src/schema-extraction/types.js +2 -0
  94. package/dist/src/utils/loadFlinkConfig.d.ts +53 -0
  95. package/dist/src/utils/loadFlinkConfig.js +77 -0
  96. package/dist/src/utils.d.ts +30 -0
  97. package/dist/src/utils.js +52 -0
  98. package/dist/src/workers/SchemaGeneratorWorker.d.ts +1 -0
  99. package/dist/src/workers/SchemaGeneratorWorker.js +49 -0
  100. package/dist/src/workers/WorkerPool.d.ts +60 -0
  101. package/dist/src/workers/WorkerPool.js +306 -0
  102. package/examples/logging-hierarchical-example.ts +125 -0
  103. package/package.json +29 -4
  104. package/readme.md +499 -0
  105. package/spec/AgentDescendantDetection.spec.ts +335 -0
  106. package/spec/AgentDuplicateDetection.spec.ts +112 -0
  107. package/spec/AgentObserver.spec.ts +266 -0
  108. package/spec/AgentRunner.spec.ts +1062 -0
  109. package/spec/AsyncLocalStorageContext.spec.ts +223 -0
  110. package/spec/ConversationHooks.spec.ts +257 -0
  111. package/spec/FlinkAgent.spec.ts +681 -0
  112. package/spec/FlinkApp.htmlResponse.spec.ts +260 -0
  113. package/spec/FlinkApp.onError.invocation.spec.ts +151 -0
  114. package/spec/FlinkApp.onError.spec.ts +1 -2
  115. package/spec/FlinkApp.query.spec.ts +107 -0
  116. package/spec/FlinkApp.routeOrdering.spec.ts +61 -0
  117. package/spec/FlinkApp.undefinedResponse.spec.ts +123 -0
  118. package/spec/FlinkApp.validationMode.spec.ts +155 -0
  119. package/spec/FlinkJob.spec.ts +171 -0
  120. package/spec/FlinkLogFactory.spec.ts +337 -0
  121. package/spec/FlinkRepo.spec.ts +1 -1
  122. package/spec/LeaderElection.spec.ts +174 -0
  123. package/spec/StreamingIntegration.spec.ts +139 -0
  124. package/spec/ToolExecutor.spec.ts +465 -0
  125. package/spec/TypeScriptCompiler.spec.ts +1 -1
  126. package/spec/TypeScriptSourceParser.spec.ts +1215 -0
  127. package/spec/TypeScriptTokenizer.spec.ts +366 -0
  128. package/spec/ai/ContextCompaction.spec.ts +405 -0
  129. package/spec/ai/ConversationAgent.spec.ts +520 -0
  130. package/spec/ai/InMemoryConversationAgent.spec.ts +144 -0
  131. package/spec/ai/agentInstructions.spec.ts +358 -0
  132. package/spec/fixtures/agent-instructions/TestAgent.ts +24 -0
  133. package/spec/fixtures/agent-instructions/simple.md +3 -0
  134. package/spec/fixtures/agent-instructions/template.md +18 -0
  135. package/spec/fixtures/agent-instructions/yaml-format.yaml +9 -0
  136. package/spec/mock-project/dist/.tsbuildinfo +1 -0
  137. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +56 -0
  138. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +58 -0
  139. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +52 -0
  140. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +52 -0
  141. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +52 -0
  142. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +54 -0
  143. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +54 -0
  144. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +57 -0
  145. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +57 -0
  146. package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
  147. package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
  148. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +57 -0
  149. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +75 -0
  150. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +57 -0
  151. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +58 -0
  152. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +58 -0
  153. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +54 -0
  154. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +55 -0
  155. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +54 -0
  156. package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +54 -0
  157. package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
  158. package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
  159. package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
  160. package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
  161. package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
  162. package/spec/mock-project/dist/src/FlinkApp.js +1000 -0
  163. package/spec/mock-project/dist/src/FlinkContext.js +2 -0
  164. package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
  165. package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
  166. package/spec/mock-project/dist/src/FlinkJob.js +2 -0
  167. package/spec/mock-project/dist/src/FlinkLog.js +119 -0
  168. package/spec/mock-project/dist/src/FlinkLogFactory.js +617 -0
  169. package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
  170. package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
  171. package/spec/mock-project/dist/src/FlinkRequestContext.js +74 -0
  172. package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
  173. package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
  174. package/spec/mock-project/dist/src/ai/AgentRunner.js +632 -0
  175. package/spec/mock-project/dist/src/ai/ConversationAgent.js +402 -0
  176. package/spec/mock-project/dist/src/ai/ConversationFlinkAgent.js +422 -0
  177. package/spec/mock-project/dist/src/ai/FlinkAgent.js +699 -0
  178. package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
  179. package/spec/mock-project/dist/src/ai/InMemoryConversationAgent.js +209 -0
  180. package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
  181. package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +223 -0
  182. package/spec/mock-project/dist/src/ai/ToolExecutor.js +412 -0
  183. package/spec/mock-project/dist/src/ai/agentInstructions.js +246 -0
  184. package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
  185. package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
  186. package/spec/mock-project/dist/src/handlers/GetCar.js +26 -52
  187. package/spec/mock-project/dist/src/handlers/GetCar.js.map +1 -0
  188. package/spec/mock-project/dist/src/handlers/GetCar2.js +32 -54
  189. package/spec/mock-project/dist/src/handlers/GetCar2.js.map +1 -0
  190. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +26 -48
  191. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js.map +1 -0
  192. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +28 -48
  193. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js.map +1 -0
  194. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +29 -48
  195. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js.map +1 -0
  196. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +26 -50
  197. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js.map +1 -0
  198. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +28 -50
  199. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js.map +1 -0
  200. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +27 -53
  201. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js.map +1 -0
  202. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +29 -53
  203. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js.map +1 -0
  204. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +16 -49
  205. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js.map +1 -0
  206. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +25 -50
  207. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js.map +1 -0
  208. package/spec/mock-project/dist/src/handlers/PatchCar.js +27 -53
  209. package/spec/mock-project/dist/src/handlers/PatchCar.js.map +1 -0
  210. package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js +44 -70
  211. package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js.map +1 -0
  212. package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js +27 -53
  213. package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js.map +1 -0
  214. package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js +28 -54
  215. package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js.map +1 -0
  216. package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js +28 -54
  217. package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js.map +1 -0
  218. package/spec/mock-project/dist/src/handlers/PostCar.js +24 -50
  219. package/spec/mock-project/dist/src/handlers/PostCar.js.map +1 -0
  220. package/spec/mock-project/dist/src/handlers/PostLogin.js +25 -51
  221. package/spec/mock-project/dist/src/handlers/PostLogin.js.map +1 -0
  222. package/spec/mock-project/dist/src/handlers/PostLogout.js +24 -50
  223. package/spec/mock-project/dist/src/handlers/PostLogout.js.map +1 -0
  224. package/spec/mock-project/dist/src/handlers/PutCar.js +24 -50
  225. package/spec/mock-project/dist/src/handlers/PutCar.js.map +1 -0
  226. package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
  227. package/spec/mock-project/dist/src/index.js +52 -76
  228. package/spec/mock-project/dist/src/index.js.map +1 -0
  229. package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
  230. package/spec/mock-project/dist/src/repos/CarRepo.js +12 -24
  231. package/spec/mock-project/dist/src/repos/CarRepo.js.map +1 -0
  232. package/spec/mock-project/dist/src/schemas/Car.js +3 -1
  233. package/spec/mock-project/dist/src/schemas/Car.js.map +1 -0
  234. package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js +3 -1
  235. package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js.map +1 -0
  236. package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js +3 -1
  237. package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js.map +1 -0
  238. package/spec/mock-project/dist/src/utils.js +290 -0
  239. package/spec/mock-project/tsconfig.json +6 -1
  240. package/spec/schema-generation-nested-objects.spec.ts +97 -0
  241. package/spec/testHelpers.ts +49 -0
  242. package/spec/utils.caseConversion.spec.ts +78 -0
  243. package/spec/utils.spec.ts +13 -13
  244. package/src/DependencyTracker.ts +166 -0
  245. package/src/FlinkApp.ts +919 -155
  246. package/src/FlinkContext.ts +43 -0
  247. package/src/FlinkErrors.ts +32 -12
  248. package/src/FlinkHttpHandler.ts +246 -28
  249. package/src/FlinkJob.ts +11 -0
  250. package/src/FlinkLog.ts +119 -12
  251. package/src/FlinkLogFactory.ts +699 -0
  252. package/src/FlinkRepo.ts +10 -3
  253. package/src/FlinkRequestContext.ts +95 -0
  254. package/src/FlinkResponse.ts +6 -0
  255. package/src/FlinkService.ts +49 -0
  256. package/src/LeaderElection.ts +203 -0
  257. package/src/SchemaCache.ts +232 -0
  258. package/src/TypeScriptCompiler.ts +1347 -610
  259. package/src/TypeScriptUtils.ts +5 -0
  260. package/src/ai/AgentRunner.ts +646 -0
  261. package/src/ai/ConversationAgent.ts +413 -0
  262. package/src/ai/FlinkAgent.ts +1069 -0
  263. package/src/ai/FlinkTool.ts +165 -0
  264. package/src/ai/InMemoryConversationAgent.ts +149 -0
  265. package/src/ai/LLMAdapter.ts +126 -0
  266. package/src/ai/ToolExecutor.ts +485 -0
  267. package/src/ai/agentInstructions.ts +245 -0
  268. package/src/ai/index.ts +8 -0
  269. package/src/ai/instructionFileLoader.ts +156 -0
  270. package/src/auth/FlinkAuthPlugin.ts +2 -1
  271. package/src/handlers/StreamWriterFactory.ts +84 -0
  272. package/src/index.ts +14 -0
  273. package/src/loadPluginSchemas.ts +141 -0
  274. package/src/schema-extraction/TypeScriptSourceParser.ts +1058 -0
  275. package/src/schema-extraction/TypeScriptTokenizer.ts +205 -0
  276. package/src/schema-extraction/index.ts +2 -0
  277. package/src/schema-extraction/types.ts +34 -0
  278. package/src/utils/loadFlinkConfig.ts +89 -0
  279. package/src/utils.ts +52 -0
  280. package/tsconfig.json +6 -1
@@ -0,0 +1,405 @@
1
+ import { FlinkAgent } from "../../src/ai/FlinkAgent";
2
+ import { FlinkContext } from "../../src/FlinkContext";
3
+ import { createStreamingMock } from "../testHelpers";
4
+ import { LLMAdapter, LLMMessage } from "../../src/ai/LLMAdapter";
5
+
6
+ // Test agent with context compaction
7
+ class CompactingAgent extends FlinkAgent<FlinkContext> {
8
+ id = "compacting-agent";
9
+ description = "Test agent with context compaction";
10
+ instructions() { return "You are a test assistant"; }
11
+
12
+ setCallbacks(
13
+ shouldCompact?: (messages: LLMMessage[], step: number) => boolean | Promise<boolean>,
14
+ compactHistory?: (messages: LLMMessage[], step: number) => Promise<LLMMessage[]> | LLMMessage[]
15
+ ) {
16
+ (this as any).shouldCompact = shouldCompact;
17
+ (this as any).compactHistory = compactHistory;
18
+ // Clear runner cache to pick up new callbacks
19
+ (this as any).runner = undefined;
20
+ }
21
+
22
+ async query(message: string) {
23
+ const response = (this as any).execute({ message });
24
+ return await response.result;
25
+ }
26
+ }
27
+
28
+ describe("Context Compaction", () => {
29
+ let ctx: FlinkContext;
30
+ let agent: CompactingAgent;
31
+ let llmAdapter: LLMAdapter;
32
+ let llmAdapters: Map<string, LLMAdapter>;
33
+
34
+ beforeEach(() => {
35
+ ctx = {} as FlinkContext;
36
+ agent = new CompactingAgent();
37
+ agent.ctx = ctx;
38
+ });
39
+
40
+ describe("Basic compaction trigger", () => {
41
+ it("should not compact when shouldCompact is not provided", async () => {
42
+ // Create agent without compaction callbacks
43
+ agent.setCallbacks(undefined, undefined);
44
+
45
+ // Mock LLM to return a response
46
+ llmAdapter = createStreamingMock([
47
+ {
48
+ textContent: "Hello!",
49
+ toolCalls: [],
50
+ usage: { inputTokens: 10, outputTokens: 5 },
51
+ stopReason: "end_turn",
52
+ },
53
+ ]);
54
+ llmAdapters = new Map([["default", llmAdapter]]);
55
+ agent.__init(llmAdapters, {});
56
+
57
+ const result = await agent.query("Hi");
58
+
59
+ expect(result.message).toBe("Hello!");
60
+ // Should only be called once (no compaction)
61
+ expect((llmAdapter.stream as jasmine.Spy).calls.count()).toBe(1);
62
+ });
63
+
64
+ it("should compact when shouldCompact returns true", async () => {
65
+ let compactCalled = false;
66
+ let messagesBeforeCompact: LLMMessage[] = [];
67
+
68
+ agent.setCallbacks(
69
+ (messages, step) => {
70
+ messagesBeforeCompact = [...messages];
71
+ return messages.length > 5;
72
+ },
73
+ (messages, step) => {
74
+ compactCalled = true;
75
+ return messages.slice(-3); // Keep last 3 messages
76
+ }
77
+ );
78
+
79
+ // Mock LLM to return a response
80
+ llmAdapter = createStreamingMock([
81
+ {
82
+ textContent: "Response",
83
+ toolCalls: [],
84
+ usage: { inputTokens: 10, outputTokens: 5 },
85
+ stopReason: "end_turn",
86
+ },
87
+ ]);
88
+ llmAdapters = new Map([["default", llmAdapter]]);
89
+ agent.__init(llmAdapters, {});
90
+
91
+ // Create history with 5 messages
92
+ const history = [
93
+ { role: "user" as const, content: "Message 1" },
94
+ { role: "assistant" as const, content: "Response 1" },
95
+ { role: "user" as const, content: "Message 2" },
96
+ { role: "assistant" as const, content: "Response 2" },
97
+ { role: "user" as const, content: "Message 3" },
98
+ ];
99
+
100
+ const result = await (agent as any).execute({ message: "New message", history }).result;
101
+
102
+ expect(result.message).toBe("Response");
103
+ expect(compactCalled).toBe(true);
104
+ expect(messagesBeforeCompact.length).toBe(6); // 5 history + 1 new message
105
+ });
106
+
107
+ it("should not compact when shouldCompact returns false", async () => {
108
+ let compactCalled = false;
109
+
110
+ agent.setCallbacks(
111
+ (messages) => false,
112
+ (messages) => {
113
+ compactCalled = true;
114
+ return messages.slice(-3);
115
+ }
116
+ );
117
+
118
+ llmAdapter = createStreamingMock([
119
+ {
120
+ textContent: "Response",
121
+ toolCalls: [],
122
+ usage: { inputTokens: 10, outputTokens: 5 },
123
+ stopReason: "end_turn",
124
+ },
125
+ ]);
126
+ llmAdapters = new Map([["default", llmAdapter]]);
127
+ agent.__init(llmAdapters, {});
128
+
129
+ const result = await agent.query("Message");
130
+
131
+ expect(result.message).toBe("Response");
132
+ expect(compactCalled).toBe(false);
133
+ });
134
+ });
135
+
136
+ describe("Custom compactHistory strategies", () => {
137
+ it("should support sliding window strategy", async () => {
138
+ agent.setCallbacks(
139
+ (messages) => messages.length > 5,
140
+ (messages) => messages.slice(-3) // Keep last 3
141
+ );
142
+
143
+ llmAdapter = createStreamingMock([
144
+ {
145
+ textContent: "Response",
146
+ toolCalls: [],
147
+ usage: { inputTokens: 10, outputTokens: 5 },
148
+ stopReason: "end_turn",
149
+ },
150
+ ]);
151
+ llmAdapters = new Map([["default", llmAdapter]]);
152
+ agent.__init(llmAdapters, {});
153
+
154
+ const history: LLMMessage[] = [
155
+ { role: "user", content: "1" },
156
+ { role: "assistant", content: "2" },
157
+ { role: "user", content: "3" },
158
+ { role: "assistant", content: "4" },
159
+ { role: "user", content: "5" },
160
+ ];
161
+
162
+ await (agent as any).execute({ message: "6", history }).result;
163
+
164
+ // Verify LLM received compacted messages
165
+ const llmCall = (llmAdapter.stream as jasmine.Spy).calls.mostRecent().args[0];
166
+ // Should have compacted to ~3 messages (may be +1 due to internal processing)
167
+ expect(llmCall.messages.length).toBeLessThanOrEqual(4);
168
+ });
169
+
170
+ it("should support keep first + last N strategy", async () => {
171
+ agent.setCallbacks(
172
+ (messages) => messages.length > 6,
173
+ (messages) => {
174
+ if (messages.length <= 1) return messages;
175
+ return [messages[0], ...messages.slice(-3)];
176
+ }
177
+ );
178
+
179
+ llmAdapter = createStreamingMock([
180
+ {
181
+ textContent: "Response",
182
+ toolCalls: [],
183
+ usage: { inputTokens: 10, outputTokens: 5 },
184
+ stopReason: "end_turn",
185
+ },
186
+ ]);
187
+ llmAdapters = new Map([["default", llmAdapter]]);
188
+ agent.__init(llmAdapters, {});
189
+
190
+ const history: LLMMessage[] = [
191
+ { role: "user", content: "FIRST" },
192
+ { role: "assistant", content: "2" },
193
+ { role: "user", content: "3" },
194
+ { role: "assistant", content: "4" },
195
+ { role: "user", content: "5" },
196
+ { role: "assistant", content: "6" },
197
+ ];
198
+
199
+ await (agent as any).execute({ message: "LAST", history }).result;
200
+
201
+ const llmCall = (llmAdapter.stream as jasmine.Spy).calls.mostRecent().args[0];
202
+ // Should have first message + last N
203
+ expect(llmCall.messages.length).toBeLessThanOrEqual(5);
204
+ expect(llmCall.messages[0].content).toBe("FIRST");
205
+ // Verify compaction preserved first message
206
+ const userMessages = llmCall.messages.filter((m: any) => m.role === "user");
207
+ expect(userMessages.length).toBeGreaterThan(0);
208
+ expect(userMessages[0].content).toBe("FIRST");
209
+ });
210
+ });
211
+
212
+ describe("Default behavior", () => {
213
+ it("should use default strategy when shouldCompact is true but compactHistory is not provided", async () => {
214
+ agent.setCallbacks(
215
+ (messages) => messages.length > 5,
216
+ undefined // No custom strategy
217
+ );
218
+
219
+ llmAdapter = createStreamingMock([
220
+ {
221
+ textContent: "Response",
222
+ toolCalls: [],
223
+ usage: { inputTokens: 10, outputTokens: 5 },
224
+ stopReason: "end_turn",
225
+ },
226
+ ]);
227
+ llmAdapters = new Map([["default", llmAdapter]]);
228
+ agent.__init(llmAdapters, {});
229
+
230
+ const history: LLMMessage[] = [];
231
+ for (let i = 0; i < 15; i++) {
232
+ history.push({ role: i % 2 === 0 ? "user" : "assistant", content: `Message ${i}` });
233
+ }
234
+
235
+ await (agent as any).execute({ message: "New", history }).result;
236
+
237
+ const llmCall = (llmAdapter.stream as jasmine.Spy).calls.mostRecent().args[0];
238
+ // Default strategy keeps last 10 messages (may be +1 due to internal processing)
239
+ expect(llmCall.messages.length).toBeLessThanOrEqual(11);
240
+ expect(llmCall.messages.length).toBeGreaterThan(5);
241
+ });
242
+ });
243
+
244
+ describe("Async callbacks", () => {
245
+ it("should support async shouldCompact", async () => {
246
+ agent.setCallbacks(
247
+ async (messages) => {
248
+ await new Promise((resolve) => setTimeout(resolve, 10));
249
+ return messages.length > 5;
250
+ },
251
+ (messages) => messages.slice(-3)
252
+ );
253
+
254
+ llmAdapter = createStreamingMock([
255
+ {
256
+ textContent: "Response",
257
+ toolCalls: [],
258
+ usage: { inputTokens: 10, outputTokens: 5 },
259
+ stopReason: "end_turn",
260
+ },
261
+ ]);
262
+ llmAdapters = new Map([["default", llmAdapter]]);
263
+ agent.__init(llmAdapters, {});
264
+
265
+ const history: LLMMessage[] = [];
266
+ for (let i = 0; i < 6; i++) {
267
+ history.push({ role: i % 2 === 0 ? "user" : "assistant", content: `Message ${i}` });
268
+ }
269
+
270
+ await (agent as any).execute({ message: "New", history }).result;
271
+
272
+ const llmCall = (llmAdapter.stream as jasmine.Spy).calls.mostRecent().args[0];
273
+ // Should be compacted to ~3 messages
274
+ expect(llmCall.messages.length).toBeLessThanOrEqual(4);
275
+ });
276
+
277
+ it("should support async compactHistory", async () => {
278
+ agent.setCallbacks(
279
+ (messages) => messages.length > 5,
280
+ async (messages) => {
281
+ await new Promise((resolve) => setTimeout(resolve, 10));
282
+ return messages.slice(-3);
283
+ }
284
+ );
285
+
286
+ llmAdapter = createStreamingMock([
287
+ {
288
+ textContent: "Response",
289
+ toolCalls: [],
290
+ usage: { inputTokens: 10, outputTokens: 5 },
291
+ stopReason: "end_turn",
292
+ },
293
+ ]);
294
+ llmAdapters = new Map([["default", llmAdapter]]);
295
+ agent.__init(llmAdapters, {});
296
+
297
+ const history: LLMMessage[] = [];
298
+ for (let i = 0; i < 6; i++) {
299
+ history.push({ role: i % 2 === 0 ? "user" : "assistant", content: `Message ${i}` });
300
+ }
301
+
302
+ await (agent as any).execute({ message: "New", history }).result;
303
+
304
+ const llmCall = (llmAdapter.stream as jasmine.Spy).calls.mostRecent().args[0];
305
+ // Should be compacted to ~3 messages
306
+ expect(llmCall.messages.length).toBeLessThanOrEqual(4);
307
+ });
308
+ });
309
+
310
+ describe("Error handling", () => {
311
+ it("should log error and continue when compactHistory returns empty array", async () => {
312
+ spyOn(console, "error"); // Spy on console.error to verify logging
313
+
314
+ agent.setCallbacks(
315
+ (messages) => messages.length > 5,
316
+ (messages) => [] // Invalid - empty array
317
+ );
318
+
319
+ llmAdapter = createStreamingMock([
320
+ {
321
+ textContent: "Response",
322
+ toolCalls: [],
323
+ usage: { inputTokens: 10, outputTokens: 5 },
324
+ stopReason: "end_turn",
325
+ },
326
+ ]);
327
+ llmAdapters = new Map([["default", llmAdapter]]);
328
+ agent.__init(llmAdapters, {});
329
+
330
+ const history: LLMMessage[] = [];
331
+ for (let i = 0; i < 6; i++) {
332
+ history.push({ role: i % 2 === 0 ? "user" : "assistant", content: `Message ${i}` });
333
+ }
334
+
335
+ // Should not throw, but continue with original messages
336
+ const result = await (agent as any).execute({ message: "New", history }).result;
337
+
338
+ expect(result.message).toBe("Response");
339
+
340
+ // LLM should receive original (uncompacted) messages due to error
341
+ const llmCall = (llmAdapter.stream as jasmine.Spy).calls.mostRecent().args[0];
342
+ // Should have all original messages (6 history + 1 new = 7, or may be +1)
343
+ expect(llmCall.messages.length).toBeGreaterThanOrEqual(7);
344
+ });
345
+
346
+ it("should log error and continue when shouldCompact throws exception", async () => {
347
+ spyOn(console, "error");
348
+
349
+ agent.setCallbacks(
350
+ (messages) => {
351
+ throw new Error("Test error in shouldCompact");
352
+ },
353
+ (messages) => messages.slice(-3)
354
+ );
355
+
356
+ llmAdapter = createStreamingMock([
357
+ {
358
+ textContent: "Response",
359
+ toolCalls: [],
360
+ usage: { inputTokens: 10, outputTokens: 5 },
361
+ stopReason: "end_turn",
362
+ },
363
+ ]);
364
+ llmAdapters = new Map([["default", llmAdapter]]);
365
+ agent.__init(llmAdapters, {});
366
+
367
+ // Should not throw, execution continues
368
+ const result = await agent.query("Message");
369
+
370
+ expect(result.message).toBe("Response");
371
+ });
372
+
373
+ it("should log error and continue when compactHistory throws exception", async () => {
374
+ spyOn(console, "error");
375
+
376
+ agent.setCallbacks(
377
+ (messages) => messages.length > 5,
378
+ (messages) => {
379
+ throw new Error("Test error in compactHistory");
380
+ }
381
+ );
382
+
383
+ llmAdapter = createStreamingMock([
384
+ {
385
+ textContent: "Response",
386
+ toolCalls: [],
387
+ usage: { inputTokens: 10, outputTokens: 5 },
388
+ stopReason: "end_turn",
389
+ },
390
+ ]);
391
+ llmAdapters = new Map([["default", llmAdapter]]);
392
+ agent.__init(llmAdapters, {});
393
+
394
+ const history: LLMMessage[] = [];
395
+ for (let i = 0; i < 6; i++) {
396
+ history.push({ role: i % 2 === 0 ? "user" : "assistant", content: `Message ${i}` });
397
+ }
398
+
399
+ // Should not throw, execution continues with original messages
400
+ const result = await (agent as any).execute({ message: "New", history }).result;
401
+
402
+ expect(result.message).toBe("Response");
403
+ });
404
+ });
405
+ });