@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
package/package.json CHANGED
@@ -1,9 +1,19 @@
1
1
  {
2
2
  "name": "@flink-app/flink",
3
- "version": "0.14.3",
3
+ "version": "2.0.0-alpha.100",
4
4
  "description": "Typescript only framework for creating REST-like APIs on top of Express and mongodb",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "main": "dist/src/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/src/index.d.ts",
10
+ "default": "./dist/src/index.js"
11
+ },
12
+ "./ai": {
13
+ "types": "./dist/src/ai/index.d.ts",
14
+ "default": "./dist/src/ai/index.js"
15
+ }
16
+ },
7
17
  "repository": "FrostDigital/flink",
8
18
  "bin": {
9
19
  "flink": "./dist/bin/flink.js"
@@ -18,17 +28,21 @@
18
28
  "@types/cors": "^2.8.10",
19
29
  "@types/express": "^4.17.13",
20
30
  "@types/fs-extra": "^9.0.12",
31
+ "@types/handlebars": "^4.1.0",
21
32
  "@types/ms": "^0.7.31",
22
33
  "@types/uuid": "^8.3.0",
23
34
  "ajv": "^8.2.0",
24
35
  "ajv-formats": "^2.1.0",
25
36
  "body-parser": "^1.19.0",
37
+ "chokidar": "^3.6.0",
26
38
  "cors": "^2.8.5",
27
39
  "croner": "^5.4.1",
28
40
  "express": "^4.17.1",
29
41
  "folder-hash": "^4.0.1",
30
42
  "fs-extra": "^10.0.0",
43
+ "handlebars": "^4.7.8",
31
44
  "json-schema": "^0.4.0",
45
+ "minimatch": "^10.0.1",
32
46
  "mkdirp": "^1.0.4",
33
47
  "mock-json-schema": "^1.0.8",
34
48
  "morgan": "^1.10.0",
@@ -39,22 +53,33 @@
39
53
  "reflect-metadata": "^0.1.13",
40
54
  "tiny-glob": "^0.2.9",
41
55
  "toad-scheduler": "^2.2.0",
42
- "ts-json-schema-generator": "2.3.0",
43
56
  "ts-morph": "24.0.0",
44
- "uuid": "^8.3.2"
57
+ "uuid": "^8.3.2",
58
+ "zod": "^4.3.6",
59
+ "@flink-app/ts-source-to-json-schema": "^0.1.7"
45
60
  },
46
61
  "devDependencies": {
62
+ "@swc/core": "^1.15.17",
47
63
  "@types/folder-hash": "^4.0.0",
48
64
  "@types/jasmine": "^3.7.1",
49
65
  "@types/json-schema": "^7.0.7",
50
66
  "@types/mkdirp": "^1.0.1",
51
67
  "@types/morgan": "^1.9.4",
52
68
  "@types/node": "22.13.10",
69
+ "@types/supertest": "^6.0.3",
53
70
  "mongodb": "^6.15.0",
71
+ "supertest": "^7.2.2",
54
72
  "ts-node": "^10.9.2"
55
73
  },
56
74
  "peerDependencies": {
57
- "mongodb": ">=3.7.0 <7.0.0"
75
+ "mongodb": ">=3.7.0 <7.0.0",
76
+ "zod": ">=3.25.0 <5.0.0",
77
+ "@swc/core": ">=1.3.0"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "@swc/core": {
81
+ "optional": true
82
+ }
58
83
  },
59
84
  "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
60
85
  "scripts": {
package/readme.md CHANGED
@@ -218,6 +218,505 @@ export const Props: RouteProps {
218
218
  }
219
219
  ```
220
220
 
221
+ ### AI Agents
222
+
223
+ Flink includes a comprehensive AI agents and tools framework for building LLM-powered applications with multi-agent orchestration, streaming responses, and type-safe tool execution.
224
+
225
+ Agents are placed in `src/agents/` folder and extend the `FlinkAgent` base class. They are automatically registered and available via the context.
226
+
227
+ #### Creating an Agent
228
+
229
+ Agents extend `FlinkAgent<Ctx>` and define their configuration as class properties:
230
+
231
+ ```typescript
232
+ // src/agents/CarAgent.ts
233
+ import { FlinkAgent } from "@flink-app/flink";
234
+ import AppCtx from "../AppCtx";
235
+ import GetCarsTool from "../tools/GetCarsTool";
236
+
237
+ export default class CarAgent extends FlinkAgent<AppCtx> {
238
+ id = "car-agent"; // Optional: defaults to "car-agent" (kebab-case class name)
239
+ description = "Expert in car models and specifications";
240
+ instructions = "You are a knowledgeable car expert...";
241
+ tools = [GetCarsTool]; // Tool class references
242
+
243
+ model = {
244
+ adapterId: "default", // References registered LLM adapter
245
+ maxTokens: 2000,
246
+ temperature: 0.7,
247
+ };
248
+
249
+ limits = {
250
+ maxSteps: 10, // Maximum execution steps
251
+ maxSubAgentDepth: 5, // Prevents infinite recursion (default: 5)
252
+ };
253
+
254
+ // Define domain-specific methods for better type safety
255
+ async searchByBrand(brand: string) {
256
+ const response = this.execute({
257
+ message: `Find all ${brand} cars`
258
+ });
259
+ return await response.result;
260
+ }
261
+ }
262
+ ```
263
+
264
+ #### Creating Tools
265
+
266
+ Tools define actions that agents can perform. They use Zod for type-safe input/output validation:
267
+
268
+ ```typescript
269
+ // src/tools/GetCarsTool.ts
270
+ import { FlinkTool, FlinkToolProps, ToolResult } from "@flink-app/flink";
271
+ import { z } from "zod";
272
+ import AppCtx from "../AppCtx";
273
+
274
+ export const Tool: FlinkToolProps = {
275
+ id: "get-cars-tool",
276
+ description: "Search for cars in inventory",
277
+ inputSchema: z.object({
278
+ brand: z.string().optional(),
279
+ maxPrice: z.number().optional(),
280
+ }),
281
+ };
282
+
283
+ const GetCarsTool: FlinkTool<AppCtx, z.infer<typeof Tool.inputSchema>> = async ({
284
+ input,
285
+ ctx
286
+ }) => {
287
+ const cars = await ctx.repos.carRepo.findAll({
288
+ brand: input.brand,
289
+ price: { $lte: input.maxPrice }
290
+ });
291
+
292
+ return { success: true, data: cars };
293
+ };
294
+
295
+ export default GetCarsTool;
296
+ ```
297
+
298
+ #### Multi-Agent Orchestration
299
+
300
+ Agents can delegate to specialized sub-agents using class references:
301
+
302
+ ```typescript
303
+ // src/agents/OrchestratorAgent.ts
304
+ import { FlinkAgent } from "@flink-app/flink";
305
+ import AppCtx from "../AppCtx";
306
+ import CarAgent from "./CarAgent";
307
+ import PricingAgent from "./PricingAgent";
308
+
309
+ export default class OrchestratorAgent extends FlinkAgent<AppCtx> {
310
+ description = "Coordinates between specialized agents";
311
+ instructions = "Route queries to appropriate specialists...";
312
+ tools = []; // No direct tools, only sub-agents
313
+ agents = [CarAgent, PricingAgent]; // Type-safe class references!
314
+
315
+ limits = {
316
+ maxSubAgentDepth: 3, // Prevent infinite delegation loops
317
+ };
318
+ }
319
+ ```
320
+
321
+ **Recursion Protection**: The `maxSubAgentDepth` limit (default: 5) prevents infinite loops when agents delegate to each other. If Agent A calls Agent B, which calls Agent A again, the framework will throw an error when the depth limit is exceeded, preventing stack overflow and infinite execution.
322
+
323
+ #### Persistent Conversations
324
+
325
+ For multi-turn conversations, extend `ConversationAgent` to automatically handle conversation history and provider metadata persistence:
326
+
327
+ ```typescript
328
+ // src/agents/ChatAgent.ts
329
+ import { ConversationAgent, ConversationData } from "@flink-app/flink";
330
+ import AppCtx from "../AppCtx";
331
+
332
+ export default class ChatAgent extends ConversationAgent<AppCtx> {
333
+ id = "chat-agent";
334
+ description = "Chat assistant with conversation memory";
335
+ instructions = "You are a helpful assistant...";
336
+ tools = ["search-knowledge"];
337
+
338
+ protected async loadConversation(conversationId: string) {
339
+ const conv = await this.ctx.repos.conversationRepo.getById(conversationId);
340
+ return conv ? {
341
+ messages: conv.messages,
342
+ providerMetadata: conv.providerMetadata
343
+ } : null;
344
+ }
345
+
346
+ protected async saveConversation(conversationId: string, data: ConversationData) {
347
+ await this.ctx.repos.conversationRepo.upsert({
348
+ _id: conversationId,
349
+ agentId: this.id,
350
+ messages: data.messages,
351
+ providerMetadata: data.providerMetadata,
352
+ updatedAt: new Date()
353
+ });
354
+ }
355
+ }
356
+ ```
357
+
358
+ **Benefits:**
359
+ - Automatic message history loading/saving
360
+ - Provider metadata persistence (e.g., OpenAI's `previousResponseId` for 40-80% cost savings)
361
+ - Sub-agent aware (parent handles persistence, not children)
362
+ - Storage backend flexibility (MongoDB, Redis, in-memory)
363
+
364
+ ##### Controlling Persistence Strategy
365
+
366
+ By default, ConversationAgent uses "auto" mode which automatically leverages provider optimizations (like OpenAI's previousResponseId) for 40-80% cost savings. You can control this behavior at the agent level or per-execution:
367
+
368
+ **Agent-level default:**
369
+ ```typescript
370
+ export default class ChatAgent extends ConversationAgent<AppCtx> {
371
+ protected persistenceStrategy = "full-history"; // Always send full history
372
+ // ... other configuration
373
+ }
374
+ ```
375
+
376
+ **Per-execution override:**
377
+ ```typescript
378
+ await agent.execute({
379
+ message: "Hello",
380
+ conversationId: "conv-123",
381
+ options: { persistenceStrategy: "full-history" } // Debug mode
382
+ });
383
+ ```
384
+
385
+ **Strategy options:**
386
+ - `"auto"` (default): Use provider optimization when available
387
+ - `"provider-optimized"`: Explicitly prefer optimization
388
+ - `"full-history"`: Always send full history (debugging, compliance)
389
+
390
+ **When to use "full-history":**
391
+ - Debugging caching issues
392
+ - Compliance requirements (audit trail of all messages)
393
+ - Testing with predictable behavior
394
+
395
+ See the [demo-app examples](../demo-app/src/agents) for MongoDB, Redis, and in-memory storage implementations.
396
+
397
+ #### Using Agents in Handlers
398
+
399
+ Access agents through the context and optionally bind a user for permissions:
400
+
401
+ ```typescript
402
+ // src/handlers/car/PostCarQuery.ts
403
+ import { Handler } from "@flink-app/flink";
404
+ import AppCtx from "../../AppCtx";
405
+
406
+ const PostCarQuery: Handler<AppCtx, { question: string }, { answer: string }> = async ({
407
+ ctx,
408
+ req
409
+ }) => {
410
+ const result = await ctx.agents.carAgent
411
+ .withUser(req.user)
412
+ .searchByBrand(req.body.question);
413
+
414
+ return { data: { answer: result.message } };
415
+ };
416
+
417
+ export default PostCarQuery;
418
+ ```
419
+
420
+ #### Streaming Responses
421
+
422
+ Agents support real-time streaming for better UX:
423
+
424
+ ```typescript
425
+ // src/handlers/car/PostCarQueryStream.ts
426
+ import { Handler } from "@flink-app/flink";
427
+ import AppCtx from "../../AppCtx";
428
+
429
+ const PostCarQueryStream: Handler<AppCtx, { question: string }> = async ({
430
+ ctx,
431
+ req,
432
+ res
433
+ }) => {
434
+ const response = ctx.agents.carAgent.execute({
435
+ message: req.body.question
436
+ });
437
+
438
+ res.setHeader("Content-Type", "text/event-stream");
439
+
440
+ for await (const text of response.textStream) {
441
+ res.write(`data: ${text}\n\n`);
442
+ }
443
+
444
+ res.end();
445
+ };
446
+
447
+ export default PostCarQueryStream;
448
+ ```
449
+
450
+ #### AI Permissions
451
+
452
+ Flink provides a flexible permission system for agents and tools that integrates with authentication plugins. Starting in v0.15.0, auth plugins populate `req.userPermissions` with resolved permissions based on roles, dynamic roles, or custom logic.
453
+
454
+ **Three Permission Patterns:**
455
+
456
+ 1. **String-based**: Single required permission
457
+ 2. **Array-based**: Multiple required permissions (ALL required, AND logic)
458
+ 3. **Function-based**: Custom permission logic with async support
459
+
460
+ **Agent Permissions Example:**
461
+
462
+ ```typescript
463
+ export default class CarAgent extends FlinkAgent<AppCtx> {
464
+ description = "Expert in car models";
465
+ instructions = "You are a car expert...";
466
+ tools = ["get-cars-tool", "create-car-tool"];
467
+ permissions = "car:access"; // Single permission required
468
+ }
469
+ ```
470
+
471
+ **Tool Permissions Example:**
472
+
473
+ ```typescript
474
+ // String-based: Simple permission check
475
+ export const Tool: FlinkToolProps = {
476
+ id: "create-car",
477
+ description: "Create a new car",
478
+ permissions: "car:create", // User must have this permission
479
+ inputSchema: z.object({
480
+ brand: z.string(),
481
+ model: z.string(),
482
+ }),
483
+ };
484
+
485
+ // Array-based: Multiple permissions required (AND logic)
486
+ export const Tool: FlinkToolProps = {
487
+ id: "premium-report",
488
+ description: "Generate premium analytics",
489
+ permissions: ["car:read", "premium-features"], // ALL required
490
+ inputSchema: z.object({
491
+ carId: z.string(),
492
+ }),
493
+ };
494
+
495
+ // Function-based: Custom logic (async supported)
496
+ export const Tool: FlinkToolProps = {
497
+ id: "delete-car",
498
+ description: "Delete a car (admin or owner only)",
499
+ permissions: async (input, user) => {
500
+ // Admin can delete any car
501
+ if (user?.permissions?.includes("admin")) return true;
502
+
503
+ // Owner can delete their own car
504
+ const car = await carRepo.getById(input.carId);
505
+ return car.ownerId === user?.id;
506
+ },
507
+ inputSchema: z.object({
508
+ carId: z.string(),
509
+ }),
510
+ };
511
+ ```
512
+
513
+ **Using Permissions in Handlers:**
514
+
515
+ Auth plugins (like JWT Auth Plugin) populate `req.userPermissions` during authentication. Pass this to agents for automatic permission checks:
516
+
517
+ ```typescript
518
+ const handler: Handler<AppCtx, RequestBody, ResponseBody> = async ({ req, ctx }) => {
519
+ const agent = ctx.agents.carAgent
520
+ .withUser(req.user)
521
+ .withPermissions(req.userPermissions); // Pass resolved permissions
522
+
523
+ return await agent.execute({ message: req.body.message });
524
+ };
525
+ ```
526
+
527
+ **How `userPermissions` Works:**
528
+
529
+ Auth plugins automatically populate `req.userPermissions` based on their configuration:
530
+
531
+ ```typescript
532
+ // Strategy 1: Direct permissions from user object
533
+ jwtAuthPlugin({
534
+ secret: process.env.JWT_SECRET,
535
+ getUser: async (tokenData) => {
536
+ const user = await userRepo.getById(tokenData.userId);
537
+ return {
538
+ id: user.id,
539
+ permissions: user.permissions, // Direct permissions array
540
+ };
541
+ },
542
+ rolePermissions: {},
543
+ });
544
+ // Result: req.userPermissions = user.permissions
545
+
546
+ // Strategy 2: Static role mapping
547
+ jwtAuthPlugin({
548
+ secret: process.env.JWT_SECRET,
549
+ getUser: async (tokenData) => {
550
+ return { id: tokenData.userId };
551
+ },
552
+ rolePermissions: {
553
+ admin: ["car:read", "car:create", "car:delete"],
554
+ user: ["car:read"],
555
+ premium: ["car:read", "car:create", "premium-features"],
556
+ },
557
+ });
558
+ // JWT token has roles: ["user", "premium"]
559
+ // Result: req.userPermissions = ["car:read", "car:create", "premium-features"]
560
+
561
+ // Strategy 3: Dynamic roles (multi-tenant)
562
+ jwtAuthPlugin({
563
+ secret: process.env.JWT_SECRET,
564
+ useDynamicRoles: true, // Enable dynamic role resolution
565
+ getUser: async (tokenData, req) => {
566
+ const orgId = req.headers['x-organization-id'];
567
+ const membership = await orgMembershipRepo.getByUserAndOrg(
568
+ tokenData.userId,
569
+ orgId
570
+ );
571
+ return {
572
+ id: tokenData.userId,
573
+ roles: [membership.role], // Org-specific role
574
+ };
575
+ },
576
+ rolePermissions: {
577
+ admin: ["car:read", "car:create", "car:delete"],
578
+ user: ["car:read"],
579
+ },
580
+ });
581
+ // Same user, different orgs → different permissions
582
+ // Org A: admin role → ["car:read", "car:create", "car:delete"]
583
+ // Org B: user role → ["car:read"]
584
+ ```
585
+
586
+ **Permission Benefits:**
587
+
588
+ - ✅ **Performance**: Permissions computed once per request (not per tool use)
589
+ - ✅ **Tool Filtering**: LLM only sees tools user has access to (prevents hallucination)
590
+ - ✅ **Consistent**: Same permission logic across handlers, agents, and tools
591
+ - ✅ **Flexible**: String/array for simple cases, function for complex logic
592
+ - ✅ **Multi-tenant**: Dynamic roles support org-specific permissions
593
+
594
+ **Backward Compatibility:**
595
+
596
+ The system is fully backward compatible:
597
+ - If `req.userPermissions` is not set → falls back to `user.permissions`
598
+ - Function-based permissions continue to work as before
599
+ - No breaking changes to existing apps
600
+
601
+ #### Registering LLM Adapters
602
+
603
+ LLM adapters are configured in `FlinkOptions.ai.llms`:
604
+
605
+ ```typescript
606
+ // src/index.ts
607
+ import Anthropic from "@anthropic-ai/sdk";
608
+ import { FlinkApp } from "@flink-app/flink";
609
+ import { AnthropicAdapter } from "@flink-app/anthropic-adapter";
610
+ import AppCtx from "./AppCtx";
611
+
612
+ const app = new FlinkApp<AppCtx>({
613
+ name: "My App",
614
+ ai: {
615
+ llms: {
616
+ default: new AnthropicAdapter(
617
+ new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }),
618
+ "claude-3-5-sonnet-20241022"
619
+ ),
620
+ fast: new AnthropicAdapter(
621
+ new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }),
622
+ "claude-3-haiku-20240307"
623
+ ),
624
+ },
625
+ },
626
+ });
627
+
628
+ app.start();
629
+ ```
630
+
631
+ #### Custom LLM Adapters
632
+
633
+ Create custom adapters by implementing the `LLMAdapter` interface:
634
+
635
+ ```typescript
636
+ import { LLMAdapter, LLMStreamChunk } from "@flink-app/flink";
637
+
638
+ /**
639
+ * Custom LLM adapter implementation
640
+ *
641
+ * Note: LLM adapters are streaming-only. Flink's AgentRunner only calls stream().
642
+ * The streaming approach provides better UX (time-to-first-token <500ms) and
643
+ * FlinkAgent's lazy generator supports both streaming and non-streaming consumption.
644
+ */
645
+ export class CustomAdapter implements LLMAdapter {
646
+ constructor(private client: any, private model: string) {}
647
+
648
+ async *stream(params: {
649
+ instructions: string;
650
+ messages: any[];
651
+ tools: any[];
652
+ maxTokens: number;
653
+ temperature: number;
654
+ }): AsyncGenerator<LLMStreamChunk> {
655
+ const stream = await this.client.chat.completions.create({
656
+ model: this.model,
657
+ messages: [
658
+ { role: "system", content: params.instructions },
659
+ ...params.messages
660
+ ],
661
+ tools: params.tools,
662
+ max_tokens: params.maxTokens,
663
+ temperature: params.temperature,
664
+ stream: true,
665
+ });
666
+
667
+ let inputTokens = 0;
668
+ let outputTokens = 0;
669
+
670
+ for await (const chunk of stream) {
671
+ const delta = chunk.choices[0]?.delta;
672
+
673
+ // Stream text deltas
674
+ if (delta?.content) {
675
+ yield { type: "text", delta: delta.content };
676
+ }
677
+
678
+ // Stream tool calls
679
+ if (delta?.tool_calls) {
680
+ for (const toolCall of delta.tool_calls) {
681
+ yield {
682
+ type: "tool_call",
683
+ toolCall: {
684
+ id: toolCall.id,
685
+ name: toolCall.function.name,
686
+ input: JSON.parse(toolCall.function.arguments),
687
+ },
688
+ };
689
+ }
690
+ }
691
+
692
+ // Track usage
693
+ if (chunk.usage) {
694
+ inputTokens = chunk.usage.prompt_tokens;
695
+ outputTokens = chunk.usage.completion_tokens;
696
+ }
697
+ }
698
+
699
+ // Emit usage
700
+ yield {
701
+ type: "usage",
702
+ usage: { inputTokens, outputTokens },
703
+ };
704
+
705
+ // Emit done
706
+ yield {
707
+ type: "done",
708
+ stopReason: "end_turn",
709
+ };
710
+ }
711
+ }
712
+ ```
713
+
714
+ #### Available Packages
715
+
716
+ - `@flink-app/flink` - Core framework with AI agents & tools
717
+ - `@flink-app/anthropic-adapter` - Official Anthropic Claude adapter
718
+ - `@flink-app/fake-llm-adapter` - Deterministic testing adapter
719
+
221
720
  ## 🤕 Known issues
222
721
 
223
722
  - Current TypeScript compiler is slow when project gets larger