@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,335 @@
1
+ import { Project } from "ts-morph";
2
+ import path from "path";
3
+ import fs from "fs";
4
+
5
+ describe("Agent Descendant Detection", () => {
6
+ let project: Project;
7
+ let tempDir: string;
8
+
9
+ beforeEach(() => {
10
+ // Create a new ts-morph project for each test
11
+ project = new Project({
12
+ compilerOptions: {
13
+ target: 99, // ESNext
14
+ module: 99, // ESNext
15
+ experimentalDecorators: true,
16
+ },
17
+ useInMemoryFileSystem: true,
18
+ });
19
+
20
+ // Create temp directory path (virtual in-memory)
21
+ tempDir = "/test";
22
+ });
23
+
24
+ /**
25
+ * Helper to test if isFlinkAgentDescendant would return true for a class in source code
26
+ */
27
+ function testAgentDetection(sourceCode: string, expectedDetection: boolean): void {
28
+ const sourceFile = project.createSourceFile(path.join(tempDir, "TestAgent.ts"), sourceCode);
29
+
30
+ // Find the exported class (mimics what TypeScriptCompiler does)
31
+ const classes = sourceFile.getClasses();
32
+ expect(classes.length).toBeGreaterThan(0);
33
+
34
+ // Get the last class in the file (which is typically the agent we're testing)
35
+ const agentClass = classes[classes.length - 1];
36
+
37
+ // Call the same logic as isFlinkAgentDescendant (inline for testing)
38
+ const result = isFlinkAgentDescendant(agentClass as any);
39
+
40
+ expect(result).toBe(expectedDetection);
41
+ }
42
+
43
+ /**
44
+ * Inline version of TypeScriptCompiler.isFlinkAgentDescendant for testing
45
+ */
46
+ function isFlinkAgentDescendant(cls: any): boolean {
47
+ const extendsClause = cls.getExtends();
48
+ if (!extendsClause) return false;
49
+
50
+ let currentExtends = extendsClause;
51
+ let depth = 0;
52
+ const maxDepth = 10;
53
+
54
+ while (currentExtends && depth < maxDepth) {
55
+ depth++;
56
+
57
+ const extendsText = currentExtends.getText().trim();
58
+
59
+ if (extendsText.includes("FlinkAgent")) {
60
+ return true;
61
+ }
62
+
63
+ const baseClassExpression = currentExtends.getExpression();
64
+ const baseClassSymbol = baseClassExpression.getType().getSymbol();
65
+
66
+ if (!baseClassSymbol) {
67
+ return false;
68
+ }
69
+
70
+ const declarations = baseClassSymbol.getDeclarations();
71
+ if (declarations.length === 0) {
72
+ return false;
73
+ }
74
+
75
+ const baseClassDecl = declarations[0];
76
+
77
+ if (!baseClassDecl.getKindName().includes("Class")) {
78
+ return false;
79
+ }
80
+
81
+ const baseClass = baseClassDecl as any;
82
+ currentExtends = baseClass.getExtends?.();
83
+
84
+ if (!currentExtends) {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ return false;
90
+ }
91
+
92
+ describe("Direct FlinkAgent subclasses", () => {
93
+ it("should detect direct FlinkAgent subclass without generics", () => {
94
+ const source = `
95
+ class FlinkAgent<Ctx> {}
96
+
97
+ export default class MyAgent extends FlinkAgent {
98
+ id = "my-agent";
99
+ }
100
+ `;
101
+ testAgentDetection(source, true);
102
+ });
103
+
104
+ it("should detect direct FlinkAgent subclass with generics", () => {
105
+ const source = `
106
+ class FlinkAgent<Ctx> {}
107
+
108
+ export default class MyAgent extends FlinkAgent<any> {
109
+ id = "my-agent";
110
+ }
111
+ `;
112
+ testAgentDetection(source, true);
113
+ });
114
+
115
+ it("should detect direct FlinkAgent subclass with complex generic type", () => {
116
+ const source = `
117
+ interface AppContext {}
118
+ class FlinkAgent<Ctx, ConvCtx> {}
119
+
120
+ export default class MyAgent extends FlinkAgent<AppContext, { userId: string }> {
121
+ id = "my-agent";
122
+ }
123
+ `;
124
+ testAgentDetection(source, true);
125
+ });
126
+ });
127
+
128
+ describe("ConversationAgent subclasses", () => {
129
+ it("should detect ConversationAgent subclass", () => {
130
+ const source = `
131
+ class FlinkAgent<Ctx> {}
132
+ class ConversationAgent<Ctx> extends FlinkAgent<Ctx> {}
133
+
134
+ export default class MyAgent extends ConversationAgent<any> {
135
+ id = "my-agent";
136
+ async loadConversation(id: string) { return null; }
137
+ async saveConversation(id: string, data: any) {}
138
+ }
139
+ `;
140
+ testAgentDetection(source, true);
141
+ });
142
+
143
+ it("should detect ConversationAgent subclass with specific context type", () => {
144
+ const source = `
145
+ interface AppContext {}
146
+ class FlinkAgent<Ctx> {}
147
+ class ConversationAgent<Ctx> extends FlinkAgent<Ctx> {}
148
+
149
+ export default class MyAgent extends ConversationAgent<AppContext> {
150
+ id = "my-agent";
151
+ async loadConversation(id: string) { return null; }
152
+ async saveConversation(id: string, data: any) {}
153
+ }
154
+ `;
155
+ testAgentDetection(source, true);
156
+ });
157
+ });
158
+
159
+ describe("InMemoryConversationAgent subclasses", () => {
160
+ it("should detect InMemoryConversationAgent subclass (3-level inheritance)", () => {
161
+ const source = `
162
+ class FlinkAgent<Ctx> {}
163
+ class ConversationAgent<Ctx> extends FlinkAgent<Ctx> {}
164
+ class InMemoryConversationAgent<Ctx> extends ConversationAgent<Ctx> {}
165
+
166
+ export default class MyAgent extends InMemoryConversationAgent<any> {
167
+ id = "my-agent";
168
+ description = "Test agent";
169
+ instructions() { return "You are helpful"; }
170
+ }
171
+ `;
172
+ testAgentDetection(source, true);
173
+ });
174
+
175
+ it("should detect InMemoryConversationAgent with complex generics", () => {
176
+ const source = `
177
+ interface AppContext { repos: any; }
178
+ class FlinkAgent<Ctx, ConvCtx> {}
179
+ class ConversationAgent<Ctx> extends FlinkAgent<Ctx, any> {}
180
+ class InMemoryConversationAgent<Ctx> extends ConversationAgent<Ctx> {}
181
+
182
+ export default class MyAgent extends InMemoryConversationAgent<AppContext> {
183
+ id = "my-agent";
184
+ description = "Test agent";
185
+ instructions() { return "You are helpful"; }
186
+ }
187
+ `;
188
+ testAgentDetection(source, true);
189
+ });
190
+ });
191
+
192
+ describe("Custom intermediate base classes", () => {
193
+ it("should detect agent extending custom base that extends FlinkAgent", () => {
194
+ const source = `
195
+ class FlinkAgent<Ctx> {}
196
+
197
+ abstract class MyCustomBase<Ctx> extends FlinkAgent<Ctx> {
198
+ // Custom shared logic
199
+ }
200
+
201
+ export default class MyAgent extends MyCustomBase<any> {
202
+ id = "my-agent";
203
+ }
204
+ `;
205
+ testAgentDetection(source, true);
206
+ });
207
+
208
+ it("should detect agent with 4-level custom inheritance chain", () => {
209
+ const source = `
210
+ class FlinkAgent<Ctx> {}
211
+ abstract class BaseAgent<Ctx> extends FlinkAgent<Ctx> {}
212
+ abstract class IntermediateAgent<Ctx> extends BaseAgent<Ctx> {}
213
+ abstract class SpecializedAgent<Ctx> extends IntermediateAgent<Ctx> {}
214
+
215
+ export default class MyAgent extends SpecializedAgent<any> {
216
+ id = "deep-agent";
217
+ }
218
+ `;
219
+ testAgentDetection(source, true);
220
+ });
221
+
222
+ it("should detect agent extending custom base extending ConversationAgent", () => {
223
+ const source = `
224
+ class FlinkAgent<Ctx> {}
225
+ class ConversationAgent<Ctx> extends FlinkAgent<Ctx> {}
226
+
227
+ abstract class MyConversationBase<Ctx> extends ConversationAgent<Ctx> {
228
+ // Custom conversation logic
229
+ }
230
+
231
+ export default class MyAgent extends MyConversationBase<any> {
232
+ id = "my-agent";
233
+ async loadConversation(id: string) { return null; }
234
+ async saveConversation(id: string, data: any) {}
235
+ }
236
+ `;
237
+ testAgentDetection(source, true);
238
+ });
239
+ });
240
+
241
+ describe("Negative cases - Non-agent classes", () => {
242
+ it("should NOT detect class with no extends clause", () => {
243
+ const source = `
244
+ export default class NotAnAgent {
245
+ id = "not-agent";
246
+ }
247
+ `;
248
+ testAgentDetection(source, false);
249
+ });
250
+
251
+ it("should NOT detect class extending unrelated base class", () => {
252
+ const source = `
253
+ class SomeOtherClass {}
254
+
255
+ export default class MyClass extends SomeOtherClass {
256
+ id = "not-agent";
257
+ }
258
+ `;
259
+ testAgentDetection(source, false);
260
+ });
261
+
262
+ it("should NOT detect class with similar name but different inheritance", () => {
263
+ const source = `
264
+ class Agent {}
265
+
266
+ export default class MyAgent extends Agent {
267
+ id = "not-flink-agent";
268
+ }
269
+ `;
270
+ testAgentDetection(source, false);
271
+ });
272
+
273
+ it("should NOT detect class extending EventEmitter", () => {
274
+ const source = `
275
+ class EventEmitter {}
276
+
277
+ export default class MyService extends EventEmitter {
278
+ id = "service";
279
+ }
280
+ `;
281
+ testAgentDetection(source, false);
282
+ });
283
+ });
284
+
285
+ describe("Edge cases", () => {
286
+ it("should handle class extending external module (unresolvable)", () => {
287
+ const source = `
288
+ import { SomeExternalClass } from "external-package";
289
+
290
+ export default class MyClass extends SomeExternalClass {
291
+ id = "external";
292
+ }
293
+ `;
294
+ // Should return false when type resolution fails
295
+ testAgentDetection(source, false);
296
+ });
297
+
298
+ it("should respect max depth limit to prevent infinite loops", () => {
299
+ // Create a chain at maxDepth (10): MyAgent → Level8 → ... → Level1 → FlinkAgent
300
+ // Total depth: 10 levels (MyAgent is level 1, FlinkAgent is level 10)
301
+ const source = `
302
+ class FlinkAgent<Ctx> {}
303
+ class Level1<Ctx> extends FlinkAgent<Ctx> {}
304
+ class Level2<Ctx> extends Level1<Ctx> {}
305
+ class Level3<Ctx> extends Level2<Ctx> {}
306
+ class Level4<Ctx> extends Level3<Ctx> {}
307
+ class Level5<Ctx> extends Level4<Ctx> {}
308
+ class Level6<Ctx> extends Level5<Ctx> {}
309
+ class Level7<Ctx> extends Level6<Ctx> {}
310
+ class Level8<Ctx> extends Level7<Ctx> {}
311
+
312
+ export default class MyAgent extends Level8<any> {
313
+ id = "deep-agent";
314
+ }
315
+ `;
316
+ // Should still detect within maxDepth (10 levels)
317
+ testAgentDetection(source, true);
318
+ });
319
+
320
+ it("should handle abstract classes in inheritance chain", () => {
321
+ const source = `
322
+ class FlinkAgent<Ctx> {}
323
+ abstract class AbstractBase<Ctx> extends FlinkAgent<Ctx> {
324
+ abstract doSomething(): void;
325
+ }
326
+
327
+ export default class MyAgent extends AbstractBase<any> {
328
+ id = "my-agent";
329
+ doSomething() {}
330
+ }
331
+ `;
332
+ testAgentDetection(source, true);
333
+ });
334
+ });
335
+ });
@@ -0,0 +1,112 @@
1
+ import { toKebabCase } from "../src/utils";
2
+
3
+ describe("Agent Duplicate Detection Logic", () => {
4
+ describe("Instance name collisions (camelCase)", () => {
5
+ // Instance names are derived by lowercasing first character
6
+ const getInstanceName = (className: string) => {
7
+ return className.charAt(0).toLowerCase() + className.substring(1);
8
+ };
9
+
10
+ it("should show CarAgent vs Caragent do NOT collide (different casing)", () => {
11
+ const instance1 = getInstanceName("CarAgent");
12
+ const instance2 = getInstanceName("Caragent");
13
+
14
+ // CarAgent → carAgent, Caragent → caragent (no collision)
15
+ expect(instance1).toBe("carAgent");
16
+ expect(instance2).toBe("caragent");
17
+ expect(instance1).not.toBe(instance2);
18
+ });
19
+
20
+ it("should detect collision example: CarAgent vs CARAgent", () => {
21
+ // This is a contrived example, but shows the logic
22
+ const instance1 = getInstanceName("CarAgent");
23
+ const instance2 = getInstanceName("CarAgent"); // Exact same name
24
+
25
+ // Both become "carAgent" - COLLISION!
26
+ expect(instance1).toBe("carAgent");
27
+ expect(instance2).toBe("carAgent");
28
+ expect(instance1).toBe(instance2);
29
+ });
30
+
31
+ it("should not collide for different class names", () => {
32
+ const instance1 = getInstanceName("CarAgent");
33
+ const instance2 = getInstanceName("BikeAgent");
34
+
35
+ expect(instance1).toBe("carAgent");
36
+ expect(instance2).toBe("bikeAgent");
37
+ expect(instance1).not.toBe(instance2);
38
+ });
39
+ });
40
+
41
+ describe("Agent ID collisions (kebab-case)", () => {
42
+ it("should detect APIAgent vs ApiAgent collision", () => {
43
+ const id1 = toKebabCase("APIAgent");
44
+ const id2 = toKebabCase("ApiAgent");
45
+
46
+ // Both become "api-agent" - COLLISION!
47
+ expect(id1).toBe("api-agent");
48
+ expect(id2).toBe("api-agent");
49
+ expect(id1).toBe(id2);
50
+ });
51
+
52
+ it("should detect HTMLParser vs HtmlParser collision", () => {
53
+ const id1 = toKebabCase("HTMLParser");
54
+ const id2 = toKebabCase("HtmlParser");
55
+
56
+ // Both become "html-parser" - COLLISION!
57
+ expect(id1).toBe("html-parser");
58
+ expect(id2).toBe("html-parser");
59
+ expect(id1).toBe(id2);
60
+ });
61
+
62
+ it("should detect XMLHttpRequest vs XmlHttpRequest collision", () => {
63
+ const id1 = toKebabCase("XMLHttpRequest");
64
+ const id2 = toKebabCase("XmlHttpRequest");
65
+
66
+ // Both become "xml-http-request" - COLLISION!
67
+ expect(id1).toBe("xml-http-request");
68
+ expect(id2).toBe("xml-http-request");
69
+ expect(id1).toBe(id2);
70
+ });
71
+
72
+ it("should not collide for different agent names", () => {
73
+ const id1 = toKebabCase("CarAgent");
74
+ const id2 = toKebabCase("BikeAgent");
75
+
76
+ expect(id1).toBe("car-agent");
77
+ expect(id2).toBe("bike-agent");
78
+ expect(id1).not.toBe(id2);
79
+ });
80
+
81
+ it("should not collide for similar but distinct names", () => {
82
+ const id1 = toKebabCase("CarAgent");
83
+ const id2 = toKebabCase("CarPricingAgent");
84
+
85
+ expect(id1).toBe("car-agent");
86
+ expect(id2).toBe("car-pricing-agent");
87
+ expect(id1).not.toBe(id2);
88
+ });
89
+ });
90
+
91
+ describe("Real-world collision scenarios", () => {
92
+ it("should show that explicit IDs prevent collisions", () => {
93
+ // Even if class names would collide, explicit IDs prevent issues
94
+ const explicitId1 = "api-agent-v1";
95
+ const explicitId2 = "api-agent-v2";
96
+
97
+ expect(explicitId1).not.toBe(explicitId2);
98
+ });
99
+
100
+ it("should demonstrate the collision resolution path", () => {
101
+ // Problem: Both APIAgent and ApiAgent → "api-agent"
102
+ const problematicId1 = toKebabCase("APIAgent");
103
+ const problematicId2 = toKebabCase("ApiAgent");
104
+ expect(problematicId1).toBe(problematicId2); // Collision!
105
+
106
+ // Solution: Use explicit IDs
107
+ const resolvedId1 = "api-agent-v1";
108
+ const resolvedId2 = "api-agent-v2";
109
+ expect(resolvedId1).not.toBe(resolvedId2); // No collision
110
+ });
111
+ });
112
+ });