@falai/agent 3.4.5 → 4.0.0-alpha.1

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 (847) hide show
  1. package/README.md +41 -34
  2. package/dist/cjs/core/Agent.d.ts +22 -378
  3. package/dist/cjs/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/core/Agent.js +104 -1178
  5. package/dist/cjs/core/Agent.js.map +1 -1
  6. package/dist/cjs/core/CompactionEngine.d.ts.map +1 -1
  7. package/dist/cjs/core/CompactionEngine.js +5 -3
  8. package/dist/cjs/core/CompactionEngine.js.map +1 -1
  9. package/dist/cjs/core/FlowSpec.d.ts +136 -0
  10. package/dist/cjs/core/FlowSpec.d.ts.map +1 -0
  11. package/dist/cjs/core/FlowSpec.js +522 -0
  12. package/dist/cjs/core/FlowSpec.js.map +1 -0
  13. package/dist/cjs/core/Migrate.d.ts +38 -0
  14. package/dist/cjs/core/Migrate.d.ts.map +1 -0
  15. package/dist/cjs/core/Migrate.js +270 -0
  16. package/dist/cjs/core/Migrate.js.map +1 -0
  17. package/dist/cjs/core/Prompt.d.ts +54 -0
  18. package/dist/cjs/core/Prompt.d.ts.map +1 -0
  19. package/dist/cjs/core/Prompt.js +143 -0
  20. package/dist/cjs/core/Prompt.js.map +1 -0
  21. package/dist/cjs/core/Runner.d.ts +160 -0
  22. package/dist/cjs/core/Runner.d.ts.map +1 -0
  23. package/dist/cjs/core/Runner.js +1131 -0
  24. package/dist/cjs/core/Runner.js.map +1 -0
  25. package/dist/cjs/core/Speak.d.ts +37 -0
  26. package/dist/cjs/core/Speak.d.ts.map +1 -0
  27. package/dist/cjs/core/Speak.js +364 -0
  28. package/dist/cjs/core/Speak.js.map +1 -0
  29. package/dist/cjs/core/Understand.d.ts +28 -0
  30. package/dist/cjs/core/Understand.d.ts.map +1 -0
  31. package/dist/cjs/core/Understand.js +353 -0
  32. package/dist/cjs/core/Understand.js.map +1 -0
  33. package/dist/cjs/core/contracts.d.ts +122 -0
  34. package/dist/cjs/core/contracts.d.ts.map +1 -0
  35. package/dist/cjs/core/contracts.js +11 -0
  36. package/dist/cjs/core/contracts.js.map +1 -0
  37. package/dist/cjs/core/falai.d.ts +57 -0
  38. package/dist/cjs/core/falai.d.ts.map +1 -0
  39. package/dist/cjs/core/falai.js +43 -0
  40. package/dist/cjs/core/falai.js.map +1 -0
  41. package/dist/cjs/core/predicate.d.ts +9 -0
  42. package/dist/cjs/core/predicate.d.ts.map +1 -0
  43. package/dist/cjs/core/predicate.js +58 -0
  44. package/dist/cjs/core/predicate.js.map +1 -0
  45. package/dist/cjs/index.d.ts +26 -31
  46. package/dist/cjs/index.d.ts.map +1 -1
  47. package/dist/cjs/index.js +46 -68
  48. package/dist/cjs/index.js.map +1 -1
  49. package/dist/cjs/persistence/MemoryStore.d.ts +15 -0
  50. package/dist/cjs/persistence/MemoryStore.d.ts.map +1 -0
  51. package/dist/cjs/persistence/MemoryStore.js +39 -0
  52. package/dist/cjs/persistence/MemoryStore.js.map +1 -0
  53. package/dist/cjs/persistence/MongoStore.d.ts +42 -0
  54. package/dist/cjs/persistence/MongoStore.d.ts.map +1 -0
  55. package/dist/cjs/persistence/MongoStore.js +60 -0
  56. package/dist/cjs/persistence/MongoStore.js.map +1 -0
  57. package/dist/cjs/persistence/OpenSearchStore.d.ts +86 -0
  58. package/dist/cjs/persistence/OpenSearchStore.d.ts.map +1 -0
  59. package/dist/cjs/persistence/OpenSearchStore.js +120 -0
  60. package/dist/cjs/persistence/OpenSearchStore.js.map +1 -0
  61. package/dist/cjs/persistence/PostgresStore.d.ts +41 -0
  62. package/dist/cjs/persistence/PostgresStore.d.ts.map +1 -0
  63. package/dist/cjs/persistence/PostgresStore.js +58 -0
  64. package/dist/cjs/persistence/PostgresStore.js.map +1 -0
  65. package/dist/cjs/persistence/PrismaStore.d.ts +65 -0
  66. package/dist/cjs/persistence/PrismaStore.d.ts.map +1 -0
  67. package/dist/cjs/persistence/PrismaStore.js +95 -0
  68. package/dist/cjs/persistence/PrismaStore.js.map +1 -0
  69. package/dist/cjs/persistence/RedisStore.d.ts +34 -0
  70. package/dist/cjs/persistence/RedisStore.d.ts.map +1 -0
  71. package/dist/cjs/persistence/RedisStore.js +61 -0
  72. package/dist/cjs/persistence/RedisStore.js.map +1 -0
  73. package/dist/cjs/persistence/SQLiteStore.d.ts +45 -0
  74. package/dist/cjs/persistence/SQLiteStore.d.ts.map +1 -0
  75. package/dist/cjs/persistence/SQLiteStore.js +74 -0
  76. package/dist/cjs/persistence/SQLiteStore.js.map +1 -0
  77. package/dist/cjs/persistence/sessionRow.d.ts +14 -0
  78. package/dist/cjs/persistence/sessionRow.d.ts.map +1 -0
  79. package/dist/cjs/persistence/sessionRow.js +50 -0
  80. package/dist/cjs/persistence/sessionRow.js.map +1 -0
  81. package/dist/cjs/providers/DeepSeekProvider.d.ts.map +1 -1
  82. package/dist/cjs/providers/DeepSeekProvider.js +8 -3
  83. package/dist/cjs/providers/DeepSeekProvider.js.map +1 -1
  84. package/dist/cjs/providers/GeminiProvider.d.ts +4 -3
  85. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  86. package/dist/cjs/providers/GeminiProvider.js +4 -3
  87. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  88. package/dist/cjs/providers/OpenAICompatibleProvider.d.ts +4 -0
  89. package/dist/cjs/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  90. package/dist/cjs/providers/OpenAICompatibleProvider.js +2 -0
  91. package/dist/cjs/providers/OpenAICompatibleProvider.js.map +1 -1
  92. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  93. package/dist/cjs/providers/OpenRouterProvider.js +2 -4
  94. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  95. package/dist/cjs/providers/ProviderAdapter.d.ts +1 -1
  96. package/dist/cjs/providers/ProviderAdapter.d.ts.map +1 -1
  97. package/dist/cjs/providers/ProviderAdapter.js +33 -10
  98. package/dist/cjs/providers/ProviderAdapter.js.map +1 -1
  99. package/dist/cjs/types/agent.d.ts +153 -383
  100. package/dist/cjs/types/agent.d.ts.map +1 -1
  101. package/dist/cjs/types/agent.js +1 -1
  102. package/dist/cjs/types/ai.d.ts +32 -1
  103. package/dist/cjs/types/ai.d.ts.map +1 -1
  104. package/dist/cjs/types/compaction.d.ts +3 -1
  105. package/dist/cjs/types/compaction.d.ts.map +1 -1
  106. package/dist/cjs/types/errors.d.ts +9 -12
  107. package/dist/cjs/types/errors.d.ts.map +1 -1
  108. package/dist/cjs/types/errors.js +14 -17
  109. package/dist/cjs/types/errors.js.map +1 -1
  110. package/dist/cjs/types/flow.d.ts +265 -513
  111. package/dist/cjs/types/flow.d.ts.map +1 -1
  112. package/dist/cjs/types/flow.js +7 -1
  113. package/dist/cjs/types/flow.js.map +1 -1
  114. package/dist/cjs/types/history.d.ts +7 -18
  115. package/dist/cjs/types/history.d.ts.map +1 -1
  116. package/dist/cjs/types/history.js.map +1 -1
  117. package/dist/cjs/types/index.d.ts +9 -15
  118. package/dist/cjs/types/index.d.ts.map +1 -1
  119. package/dist/cjs/types/index.js +4 -14
  120. package/dist/cjs/types/index.js.map +1 -1
  121. package/dist/cjs/types/session.d.ts +94 -64
  122. package/dist/cjs/types/session.d.ts.map +1 -1
  123. package/dist/cjs/types/session.js +5 -1
  124. package/dist/cjs/types/session.js.map +1 -1
  125. package/dist/cjs/types/tool.d.ts +37 -207
  126. package/dist/cjs/types/tool.d.ts.map +1 -1
  127. package/dist/cjs/types/tool.js +5 -14
  128. package/dist/cjs/types/tool.js.map +1 -1
  129. package/dist/cjs/utils/clock.d.ts +28 -0
  130. package/dist/cjs/utils/clock.d.ts.map +1 -0
  131. package/dist/cjs/utils/clock.js +64 -0
  132. package/dist/cjs/utils/clock.js.map +1 -0
  133. package/dist/cjs/utils/duration.d.ts +11 -0
  134. package/dist/cjs/utils/duration.d.ts.map +1 -0
  135. package/dist/cjs/utils/duration.js +31 -0
  136. package/dist/cjs/utils/duration.js.map +1 -0
  137. package/dist/cjs/utils/history.d.ts +4 -1
  138. package/dist/cjs/utils/history.d.ts.map +1 -1
  139. package/dist/cjs/utils/history.js +2 -2
  140. package/dist/cjs/utils/history.js.map +1 -1
  141. package/dist/cjs/utils/index.d.ts +4 -10
  142. package/dist/cjs/utils/index.d.ts.map +1 -1
  143. package/dist/cjs/utils/index.js +14 -61
  144. package/dist/cjs/utils/index.js.map +1 -1
  145. package/dist/cjs/utils/json.d.ts +2 -0
  146. package/dist/cjs/utils/json.d.ts.map +1 -1
  147. package/dist/cjs/utils/json.js +5 -0
  148. package/dist/cjs/utils/json.js.map +1 -1
  149. package/dist/cjs/utils/outcomes.d.ts +48 -0
  150. package/dist/cjs/utils/outcomes.d.ts.map +1 -0
  151. package/dist/cjs/utils/outcomes.js +51 -0
  152. package/dist/cjs/utils/outcomes.js.map +1 -0
  153. package/dist/cjs/utils/schema.d.ts +50 -0
  154. package/dist/cjs/utils/schema.d.ts.map +1 -0
  155. package/dist/cjs/utils/schema.js +138 -0
  156. package/dist/cjs/utils/schema.js.map +1 -0
  157. package/dist/cjs/utils/streamingMessage.d.ts +3 -2
  158. package/dist/cjs/utils/streamingMessage.d.ts.map +1 -1
  159. package/dist/cjs/utils/streamingMessage.js +38 -4
  160. package/dist/cjs/utils/streamingMessage.js.map +1 -1
  161. package/dist/cjs/utils/template.d.ts +13 -149
  162. package/dist/cjs/utils/template.d.ts.map +1 -1
  163. package/dist/cjs/utils/template.js +31 -363
  164. package/dist/cjs/utils/template.js.map +1 -1
  165. package/dist/cjs/utils/usage.d.ts +19 -0
  166. package/dist/cjs/utils/usage.d.ts.map +1 -0
  167. package/dist/cjs/utils/usage.js +35 -0
  168. package/dist/cjs/utils/usage.js.map +1 -0
  169. package/dist/core/Agent.d.ts +22 -378
  170. package/dist/core/Agent.d.ts.map +1 -1
  171. package/dist/core/Agent.js +107 -1181
  172. package/dist/core/Agent.js.map +1 -1
  173. package/dist/core/CompactionEngine.d.ts.map +1 -1
  174. package/dist/core/CompactionEngine.js +5 -3
  175. package/dist/core/CompactionEngine.js.map +1 -1
  176. package/dist/core/FlowSpec.d.ts +136 -0
  177. package/dist/core/FlowSpec.d.ts.map +1 -0
  178. package/dist/core/FlowSpec.js +516 -0
  179. package/dist/core/FlowSpec.js.map +1 -0
  180. package/dist/core/Migrate.d.ts +38 -0
  181. package/dist/core/Migrate.d.ts.map +1 -0
  182. package/dist/core/Migrate.js +264 -0
  183. package/dist/core/Migrate.js.map +1 -0
  184. package/dist/core/Prompt.d.ts +54 -0
  185. package/dist/core/Prompt.d.ts.map +1 -0
  186. package/dist/core/Prompt.js +133 -0
  187. package/dist/core/Prompt.js.map +1 -0
  188. package/dist/core/Runner.d.ts +160 -0
  189. package/dist/core/Runner.d.ts.map +1 -0
  190. package/dist/core/Runner.js +1127 -0
  191. package/dist/core/Runner.js.map +1 -0
  192. package/dist/core/Speak.d.ts +37 -0
  193. package/dist/core/Speak.d.ts.map +1 -0
  194. package/dist/core/Speak.js +360 -0
  195. package/dist/core/Speak.js.map +1 -0
  196. package/dist/core/Understand.d.ts +28 -0
  197. package/dist/core/Understand.d.ts.map +1 -0
  198. package/dist/core/Understand.js +349 -0
  199. package/dist/core/Understand.js.map +1 -0
  200. package/dist/core/contracts.d.ts +122 -0
  201. package/dist/core/contracts.d.ts.map +1 -0
  202. package/dist/core/contracts.js +10 -0
  203. package/dist/core/contracts.js.map +1 -0
  204. package/dist/core/falai.d.ts +57 -0
  205. package/dist/core/falai.d.ts.map +1 -0
  206. package/dist/core/falai.js +40 -0
  207. package/dist/core/falai.js.map +1 -0
  208. package/dist/core/predicate.d.ts +9 -0
  209. package/dist/core/predicate.d.ts.map +1 -0
  210. package/dist/core/predicate.js +54 -0
  211. package/dist/core/predicate.js.map +1 -0
  212. package/dist/index.d.ts +26 -31
  213. package/dist/index.d.ts.map +1 -1
  214. package/dist/index.js +19 -24
  215. package/dist/index.js.map +1 -1
  216. package/dist/persistence/MemoryStore.d.ts +15 -0
  217. package/dist/persistence/MemoryStore.d.ts.map +1 -0
  218. package/dist/persistence/MemoryStore.js +35 -0
  219. package/dist/persistence/MemoryStore.js.map +1 -0
  220. package/dist/persistence/MongoStore.d.ts +42 -0
  221. package/dist/persistence/MongoStore.d.ts.map +1 -0
  222. package/dist/persistence/MongoStore.js +56 -0
  223. package/dist/persistence/MongoStore.js.map +1 -0
  224. package/dist/persistence/OpenSearchStore.d.ts +86 -0
  225. package/dist/persistence/OpenSearchStore.d.ts.map +1 -0
  226. package/dist/persistence/OpenSearchStore.js +116 -0
  227. package/dist/persistence/OpenSearchStore.js.map +1 -0
  228. package/dist/persistence/PostgresStore.d.ts +41 -0
  229. package/dist/persistence/PostgresStore.d.ts.map +1 -0
  230. package/dist/persistence/PostgresStore.js +54 -0
  231. package/dist/persistence/PostgresStore.js.map +1 -0
  232. package/dist/persistence/PrismaStore.d.ts +65 -0
  233. package/dist/persistence/PrismaStore.d.ts.map +1 -0
  234. package/dist/persistence/PrismaStore.js +91 -0
  235. package/dist/persistence/PrismaStore.js.map +1 -0
  236. package/dist/persistence/RedisStore.d.ts +34 -0
  237. package/dist/persistence/RedisStore.d.ts.map +1 -0
  238. package/dist/persistence/RedisStore.js +57 -0
  239. package/dist/persistence/RedisStore.js.map +1 -0
  240. package/dist/persistence/SQLiteStore.d.ts +45 -0
  241. package/dist/persistence/SQLiteStore.d.ts.map +1 -0
  242. package/dist/persistence/SQLiteStore.js +70 -0
  243. package/dist/persistence/SQLiteStore.js.map +1 -0
  244. package/dist/persistence/sessionRow.d.ts +14 -0
  245. package/dist/persistence/sessionRow.d.ts.map +1 -0
  246. package/dist/persistence/sessionRow.js +45 -0
  247. package/dist/persistence/sessionRow.js.map +1 -0
  248. package/dist/providers/DeepSeekProvider.d.ts.map +1 -1
  249. package/dist/providers/DeepSeekProvider.js +8 -3
  250. package/dist/providers/DeepSeekProvider.js.map +1 -1
  251. package/dist/providers/GeminiProvider.d.ts +4 -3
  252. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  253. package/dist/providers/GeminiProvider.js +4 -3
  254. package/dist/providers/GeminiProvider.js.map +1 -1
  255. package/dist/providers/OpenAICompatibleProvider.d.ts +4 -0
  256. package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  257. package/dist/providers/OpenAICompatibleProvider.js +2 -0
  258. package/dist/providers/OpenAICompatibleProvider.js.map +1 -1
  259. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  260. package/dist/providers/OpenRouterProvider.js +2 -4
  261. package/dist/providers/OpenRouterProvider.js.map +1 -1
  262. package/dist/providers/ProviderAdapter.d.ts +1 -1
  263. package/dist/providers/ProviderAdapter.d.ts.map +1 -1
  264. package/dist/providers/ProviderAdapter.js +34 -11
  265. package/dist/providers/ProviderAdapter.js.map +1 -1
  266. package/dist/types/agent.d.ts +153 -383
  267. package/dist/types/agent.d.ts.map +1 -1
  268. package/dist/types/agent.js +1 -1
  269. package/dist/types/ai.d.ts +32 -1
  270. package/dist/types/ai.d.ts.map +1 -1
  271. package/dist/types/compaction.d.ts +3 -1
  272. package/dist/types/compaction.d.ts.map +1 -1
  273. package/dist/types/errors.d.ts +9 -12
  274. package/dist/types/errors.d.ts.map +1 -1
  275. package/dist/types/errors.js +12 -15
  276. package/dist/types/errors.js.map +1 -1
  277. package/dist/types/flow.d.ts +265 -513
  278. package/dist/types/flow.d.ts.map +1 -1
  279. package/dist/types/flow.js +7 -1
  280. package/dist/types/flow.js.map +1 -1
  281. package/dist/types/history.d.ts +7 -18
  282. package/dist/types/history.d.ts.map +1 -1
  283. package/dist/types/history.js.map +1 -1
  284. package/dist/types/index.d.ts +9 -15
  285. package/dist/types/index.d.ts.map +1 -1
  286. package/dist/types/index.js +2 -7
  287. package/dist/types/index.js.map +1 -1
  288. package/dist/types/session.d.ts +94 -64
  289. package/dist/types/session.d.ts.map +1 -1
  290. package/dist/types/session.js +5 -1
  291. package/dist/types/session.js.map +1 -1
  292. package/dist/types/tool.d.ts +37 -207
  293. package/dist/types/tool.d.ts.map +1 -1
  294. package/dist/types/tool.js +6 -13
  295. package/dist/types/tool.js.map +1 -1
  296. package/dist/utils/clock.d.ts +28 -0
  297. package/dist/utils/clock.d.ts.map +1 -0
  298. package/dist/utils/clock.js +59 -0
  299. package/dist/utils/clock.js.map +1 -0
  300. package/dist/utils/duration.d.ts +11 -0
  301. package/dist/utils/duration.d.ts.map +1 -0
  302. package/dist/utils/duration.js +26 -0
  303. package/dist/utils/duration.js.map +1 -0
  304. package/dist/utils/history.d.ts +4 -1
  305. package/dist/utils/history.d.ts.map +1 -1
  306. package/dist/utils/history.js +2 -2
  307. package/dist/utils/history.js.map +1 -1
  308. package/dist/utils/index.d.ts +4 -10
  309. package/dist/utils/index.d.ts.map +1 -1
  310. package/dist/utils/index.js +4 -21
  311. package/dist/utils/index.js.map +1 -1
  312. package/dist/utils/json.d.ts +2 -0
  313. package/dist/utils/json.d.ts.map +1 -1
  314. package/dist/utils/json.js +4 -0
  315. package/dist/utils/json.js.map +1 -1
  316. package/dist/utils/outcomes.d.ts +48 -0
  317. package/dist/utils/outcomes.d.ts.map +1 -0
  318. package/dist/utils/outcomes.js +48 -0
  319. package/dist/utils/outcomes.js.map +1 -0
  320. package/dist/utils/schema.d.ts +50 -0
  321. package/dist/utils/schema.d.ts.map +1 -0
  322. package/dist/utils/schema.js +129 -0
  323. package/dist/utils/schema.js.map +1 -0
  324. package/dist/utils/streamingMessage.d.ts +3 -2
  325. package/dist/utils/streamingMessage.d.ts.map +1 -1
  326. package/dist/utils/streamingMessage.js +38 -4
  327. package/dist/utils/streamingMessage.js.map +1 -1
  328. package/dist/utils/template.d.ts +13 -149
  329. package/dist/utils/template.d.ts.map +1 -1
  330. package/dist/utils/template.js +28 -355
  331. package/dist/utils/template.js.map +1 -1
  332. package/dist/utils/usage.d.ts +19 -0
  333. package/dist/utils/usage.d.ts.map +1 -0
  334. package/dist/utils/usage.js +31 -0
  335. package/dist/utils/usage.js.map +1 -0
  336. package/docs/README.md +37 -19
  337. package/docs/concepts/architecture.md +117 -239
  338. package/docs/concepts/collection.md +170 -0
  339. package/docs/concepts/pipeline.md +132 -378
  340. package/docs/concepts/runs-and-waits.md +192 -0
  341. package/docs/guides/actions-and-events.md +276 -0
  342. package/docs/guides/branching.md +119 -208
  343. package/docs/guides/compaction.md +63 -158
  344. package/docs/guides/conditions.md +164 -128
  345. package/docs/guides/error-handling.md +168 -164
  346. package/docs/guides/flow-control.md +210 -349
  347. package/docs/guides/flows-from-json.md +224 -0
  348. package/docs/guides/instructions.md +125 -161
  349. package/docs/guides/persistence.md +182 -206
  350. package/docs/guides/streaming.md +50 -114
  351. package/docs/guides/testing.md +284 -0
  352. package/docs/guides/triggers.md +401 -0
  353. package/docs/migration/README.md +8 -15
  354. package/docs/migration/v1-to-v2.md +1 -1
  355. package/docs/migration/v2-3-to-v2-4.md +2 -2
  356. package/docs/migration/v2-6-to-v2-7.md +4 -4
  357. package/docs/migration/v3-to-v4.md +452 -0
  358. package/docs/reference/actions-events-conditions.md +396 -0
  359. package/docs/reference/agent.md +244 -0
  360. package/docs/reference/branches.md +75 -203
  361. package/docs/reference/errors.md +188 -144
  362. package/docs/reference/fields.md +125 -0
  363. package/docs/reference/flow-spec.md +248 -0
  364. package/docs/reference/flow.md +104 -192
  365. package/docs/reference/instruction.md +83 -137
  366. package/docs/reference/outcomes.md +273 -0
  367. package/docs/reference/providers.md +525 -302
  368. package/docs/reference/session.md +210 -0
  369. package/docs/reference/step.md +194 -312
  370. package/docs/reference/stores.md +496 -0
  371. package/docs/reference/tool.md +162 -231
  372. package/docs/reference/trigger.md +180 -0
  373. package/docs/rfc/v4-one-flow.md +477 -0
  374. package/docs/start/01-install.md +59 -44
  375. package/docs/start/02-first-agent.md +97 -147
  376. package/docs/start/03-collect-data.md +78 -183
  377. package/docs/start/04-add-tools.md +159 -227
  378. package/docs/start/05-go-to-production.md +167 -164
  379. package/examples/01-quickstart.ts +26 -16
  380. package/examples/02-fields.ts +75 -0
  381. package/examples/03-tools.ts +79 -119
  382. package/examples/04-instructions.ts +60 -87
  383. package/examples/05-branches.ts +78 -0
  384. package/examples/06-triggers-and-waits.ts +148 -0
  385. package/examples/07-streaming.ts +34 -60
  386. package/examples/08-store-and-migration.ts +97 -0
  387. package/examples/09-flows-from-json.ts +107 -0
  388. package/package.json +9 -6
  389. package/src/core/Agent.ts +116 -1512
  390. package/src/core/CompactionEngine.ts +7 -4
  391. package/src/core/FlowSpec.ts +712 -0
  392. package/src/core/Migrate.ts +256 -0
  393. package/src/core/Prompt.ts +156 -0
  394. package/src/core/Runner.ts +1181 -0
  395. package/src/core/Speak.ts +451 -0
  396. package/src/core/Understand.ts +422 -0
  397. package/src/core/contracts.ts +111 -0
  398. package/src/core/falai.ts +86 -0
  399. package/src/core/predicate.ts +56 -0
  400. package/src/index.ts +119 -147
  401. package/src/persistence/MemoryStore.ts +37 -0
  402. package/src/persistence/MongoStore.ts +89 -0
  403. package/src/persistence/OpenSearchStore.ts +153 -0
  404. package/src/persistence/PostgresStore.ts +89 -0
  405. package/src/persistence/PrismaStore.ts +127 -0
  406. package/src/persistence/RedisStore.ts +90 -0
  407. package/src/persistence/SQLiteStore.ts +103 -0
  408. package/src/persistence/sessionRow.ts +45 -0
  409. package/src/providers/DeepSeekProvider.ts +8 -3
  410. package/src/providers/GeminiProvider.ts +4 -3
  411. package/src/providers/OpenAICompatibleProvider.ts +6 -0
  412. package/src/providers/OpenRouterProvider.ts +2 -4
  413. package/src/providers/ProviderAdapter.ts +36 -8
  414. package/src/types/agent.ts +124 -397
  415. package/src/types/ai.ts +33 -1
  416. package/src/types/compaction.ts +3 -1
  417. package/src/types/errors.ts +13 -16
  418. package/src/types/flow.ts +249 -550
  419. package/src/types/history.ts +7 -20
  420. package/src/types/index.ts +87 -139
  421. package/src/types/session.ts +135 -70
  422. package/src/types/tool.ts +42 -267
  423. package/src/utils/clock.ts +70 -0
  424. package/src/utils/duration.ts +33 -0
  425. package/src/utils/history.ts +3 -2
  426. package/src/utils/index.ts +8 -66
  427. package/src/utils/json.ts +5 -0
  428. package/src/utils/outcomes.ts +56 -0
  429. package/src/utils/schema.ts +145 -0
  430. package/src/utils/streamingMessage.ts +34 -4
  431. package/src/utils/template.ts +32 -423
  432. package/src/utils/usage.ts +37 -0
  433. package/dist/adapters/MemoryAdapter.d.ts +0 -47
  434. package/dist/adapters/MemoryAdapter.d.ts.map +0 -1
  435. package/dist/adapters/MemoryAdapter.js +0 -204
  436. package/dist/adapters/MemoryAdapter.js.map +0 -1
  437. package/dist/adapters/MongoAdapter.d.ts +0 -97
  438. package/dist/adapters/MongoAdapter.d.ts.map +0 -1
  439. package/dist/adapters/MongoAdapter.js +0 -196
  440. package/dist/adapters/MongoAdapter.js.map +0 -1
  441. package/dist/adapters/OpenSearchAdapter.d.ts +0 -169
  442. package/dist/adapters/OpenSearchAdapter.d.ts.map +0 -1
  443. package/dist/adapters/OpenSearchAdapter.js +0 -471
  444. package/dist/adapters/OpenSearchAdapter.js.map +0 -1
  445. package/dist/adapters/PostgreSQLAdapter.d.ts +0 -85
  446. package/dist/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  447. package/dist/adapters/PostgreSQLAdapter.js +0 -308
  448. package/dist/adapters/PostgreSQLAdapter.js.map +0 -1
  449. package/dist/adapters/PrismaAdapter.d.ts +0 -115
  450. package/dist/adapters/PrismaAdapter.d.ts.map +0 -1
  451. package/dist/adapters/PrismaAdapter.js +0 -406
  452. package/dist/adapters/PrismaAdapter.js.map +0 -1
  453. package/dist/adapters/RedisAdapter.d.ts +0 -72
  454. package/dist/adapters/RedisAdapter.d.ts.map +0 -1
  455. package/dist/adapters/RedisAdapter.js +0 -286
  456. package/dist/adapters/RedisAdapter.js.map +0 -1
  457. package/dist/adapters/SQLiteAdapter.d.ts +0 -86
  458. package/dist/adapters/SQLiteAdapter.d.ts.map +0 -1
  459. package/dist/adapters/SQLiteAdapter.js +0 -337
  460. package/dist/adapters/SQLiteAdapter.js.map +0 -1
  461. package/dist/adapters/index.d.ts +0 -17
  462. package/dist/adapters/index.d.ts.map +0 -1
  463. package/dist/adapters/index.js +0 -11
  464. package/dist/adapters/index.js.map +0 -1
  465. package/dist/adapters/sessionRow.d.ts +0 -22
  466. package/dist/adapters/sessionRow.d.ts.map +0 -1
  467. package/dist/adapters/sessionRow.js +0 -48
  468. package/dist/adapters/sessionRow.js.map +0 -1
  469. package/dist/cjs/adapters/MemoryAdapter.d.ts +0 -47
  470. package/dist/cjs/adapters/MemoryAdapter.d.ts.map +0 -1
  471. package/dist/cjs/adapters/MemoryAdapter.js +0 -208
  472. package/dist/cjs/adapters/MemoryAdapter.js.map +0 -1
  473. package/dist/cjs/adapters/MongoAdapter.d.ts +0 -97
  474. package/dist/cjs/adapters/MongoAdapter.d.ts.map +0 -1
  475. package/dist/cjs/adapters/MongoAdapter.js +0 -200
  476. package/dist/cjs/adapters/MongoAdapter.js.map +0 -1
  477. package/dist/cjs/adapters/OpenSearchAdapter.d.ts +0 -169
  478. package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +0 -1
  479. package/dist/cjs/adapters/OpenSearchAdapter.js +0 -475
  480. package/dist/cjs/adapters/OpenSearchAdapter.js.map +0 -1
  481. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +0 -85
  482. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  483. package/dist/cjs/adapters/PostgreSQLAdapter.js +0 -312
  484. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +0 -1
  485. package/dist/cjs/adapters/PrismaAdapter.d.ts +0 -115
  486. package/dist/cjs/adapters/PrismaAdapter.d.ts.map +0 -1
  487. package/dist/cjs/adapters/PrismaAdapter.js +0 -410
  488. package/dist/cjs/adapters/PrismaAdapter.js.map +0 -1
  489. package/dist/cjs/adapters/RedisAdapter.d.ts +0 -72
  490. package/dist/cjs/adapters/RedisAdapter.d.ts.map +0 -1
  491. package/dist/cjs/adapters/RedisAdapter.js +0 -290
  492. package/dist/cjs/adapters/RedisAdapter.js.map +0 -1
  493. package/dist/cjs/adapters/SQLiteAdapter.d.ts +0 -86
  494. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +0 -1
  495. package/dist/cjs/adapters/SQLiteAdapter.js +0 -341
  496. package/dist/cjs/adapters/SQLiteAdapter.js.map +0 -1
  497. package/dist/cjs/adapters/index.d.ts +0 -17
  498. package/dist/cjs/adapters/index.d.ts.map +0 -1
  499. package/dist/cjs/adapters/index.js +0 -21
  500. package/dist/cjs/adapters/index.js.map +0 -1
  501. package/dist/cjs/adapters/sessionRow.d.ts +0 -22
  502. package/dist/cjs/adapters/sessionRow.d.ts.map +0 -1
  503. package/dist/cjs/adapters/sessionRow.js +0 -52
  504. package/dist/cjs/adapters/sessionRow.js.map +0 -1
  505. package/dist/cjs/constants/index.d.ts +0 -1
  506. package/dist/cjs/constants/index.d.ts.map +0 -1
  507. package/dist/cjs/constants/index.js +0 -4
  508. package/dist/cjs/constants/index.js.map +0 -1
  509. package/dist/cjs/core/AutoChainExecutor.d.ts +0 -97
  510. package/dist/cjs/core/AutoChainExecutor.d.ts.map +0 -1
  511. package/dist/cjs/core/AutoChainExecutor.js +0 -288
  512. package/dist/cjs/core/AutoChainExecutor.js.map +0 -1
  513. package/dist/cjs/core/BranchEvaluator.d.ts +0 -55
  514. package/dist/cjs/core/BranchEvaluator.d.ts.map +0 -1
  515. package/dist/cjs/core/BranchEvaluator.js +0 -125
  516. package/dist/cjs/core/BranchEvaluator.js.map +0 -1
  517. package/dist/cjs/core/DirectiveChainTracker.d.ts +0 -49
  518. package/dist/cjs/core/DirectiveChainTracker.d.ts.map +0 -1
  519. package/dist/cjs/core/DirectiveChainTracker.js +0 -121
  520. package/dist/cjs/core/DirectiveChainTracker.js.map +0 -1
  521. package/dist/cjs/core/Events.d.ts +0 -26
  522. package/dist/cjs/core/Events.d.ts.map +0 -1
  523. package/dist/cjs/core/Events.js +0 -144
  524. package/dist/cjs/core/Events.js.map +0 -1
  525. package/dist/cjs/core/Flow.d.ts +0 -183
  526. package/dist/cjs/core/Flow.d.ts.map +0 -1
  527. package/dist/cjs/core/Flow.js +0 -551
  528. package/dist/cjs/core/Flow.js.map +0 -1
  529. package/dist/cjs/core/FlowRouter.d.ts +0 -183
  530. package/dist/cjs/core/FlowRouter.d.ts.map +0 -1
  531. package/dist/cjs/core/FlowRouter.js +0 -1047
  532. package/dist/cjs/core/FlowRouter.js.map +0 -1
  533. package/dist/cjs/core/PersistenceManager.d.ts +0 -114
  534. package/dist/cjs/core/PersistenceManager.d.ts.map +0 -1
  535. package/dist/cjs/core/PersistenceManager.js +0 -336
  536. package/dist/cjs/core/PersistenceManager.js.map +0 -1
  537. package/dist/cjs/core/PromptComposer.d.ts +0 -47
  538. package/dist/cjs/core/PromptComposer.d.ts.map +0 -1
  539. package/dist/cjs/core/PromptComposer.js +0 -397
  540. package/dist/cjs/core/PromptComposer.js.map +0 -1
  541. package/dist/cjs/core/PromptSectionCache.d.ts +0 -48
  542. package/dist/cjs/core/PromptSectionCache.d.ts.map +0 -1
  543. package/dist/cjs/core/PromptSectionCache.js +0 -108
  544. package/dist/cjs/core/PromptSectionCache.js.map +0 -1
  545. package/dist/cjs/core/ResponseEngine.d.ts +0 -43
  546. package/dist/cjs/core/ResponseEngine.d.ts.map +0 -1
  547. package/dist/cjs/core/ResponseEngine.js +0 -235
  548. package/dist/cjs/core/ResponseEngine.js.map +0 -1
  549. package/dist/cjs/core/ResponseGenerationError.d.ts +0 -30
  550. package/dist/cjs/core/ResponseGenerationError.d.ts.map +0 -1
  551. package/dist/cjs/core/ResponseGenerationError.js +0 -35
  552. package/dist/cjs/core/ResponseGenerationError.js.map +0 -1
  553. package/dist/cjs/core/ResponseModal.d.ts +0 -305
  554. package/dist/cjs/core/ResponseModal.d.ts.map +0 -1
  555. package/dist/cjs/core/ResponseModal.js +0 -1414
  556. package/dist/cjs/core/ResponseModal.js.map +0 -1
  557. package/dist/cjs/core/ResponsePipeline.d.ts +0 -220
  558. package/dist/cjs/core/ResponsePipeline.d.ts.map +0 -1
  559. package/dist/cjs/core/ResponsePipeline.js +0 -1040
  560. package/dist/cjs/core/ResponsePipeline.js.map +0 -1
  561. package/dist/cjs/core/SessionFinalizer.d.ts +0 -34
  562. package/dist/cjs/core/SessionFinalizer.d.ts.map +0 -1
  563. package/dist/cjs/core/SessionFinalizer.js +0 -88
  564. package/dist/cjs/core/SessionFinalizer.js.map +0 -1
  565. package/dist/cjs/core/SessionManager.d.ts +0 -112
  566. package/dist/cjs/core/SessionManager.d.ts.map +0 -1
  567. package/dist/cjs/core/SessionManager.js +0 -308
  568. package/dist/cjs/core/SessionManager.js.map +0 -1
  569. package/dist/cjs/core/SignalCoordinator.d.ts +0 -103
  570. package/dist/cjs/core/SignalCoordinator.d.ts.map +0 -1
  571. package/dist/cjs/core/SignalCoordinator.js +0 -207
  572. package/dist/cjs/core/SignalCoordinator.js.map +0 -1
  573. package/dist/cjs/core/SignalEvaluator.d.ts +0 -86
  574. package/dist/cjs/core/SignalEvaluator.d.ts.map +0 -1
  575. package/dist/cjs/core/SignalEvaluator.js +0 -319
  576. package/dist/cjs/core/SignalEvaluator.js.map +0 -1
  577. package/dist/cjs/core/SignalProcessor.d.ts +0 -152
  578. package/dist/cjs/core/SignalProcessor.d.ts.map +0 -1
  579. package/dist/cjs/core/SignalProcessor.js +0 -505
  580. package/dist/cjs/core/SignalProcessor.js.map +0 -1
  581. package/dist/cjs/core/Step.d.ts +0 -184
  582. package/dist/cjs/core/Step.d.ts.map +0 -1
  583. package/dist/cjs/core/Step.js +0 -599
  584. package/dist/cjs/core/Step.js.map +0 -1
  585. package/dist/cjs/core/StepLifecycle.d.ts +0 -43
  586. package/dist/cjs/core/StepLifecycle.d.ts.map +0 -1
  587. package/dist/cjs/core/StepLifecycle.js +0 -180
  588. package/dist/cjs/core/StepLifecycle.js.map +0 -1
  589. package/dist/cjs/core/StreamingToolExecutor.d.ts +0 -142
  590. package/dist/cjs/core/StreamingToolExecutor.d.ts.map +0 -1
  591. package/dist/cjs/core/StreamingToolExecutor.js +0 -490
  592. package/dist/cjs/core/StreamingToolExecutor.js.map +0 -1
  593. package/dist/cjs/core/ToolLoopExecutor.d.ts +0 -133
  594. package/dist/cjs/core/ToolLoopExecutor.d.ts.map +0 -1
  595. package/dist/cjs/core/ToolLoopExecutor.js +0 -568
  596. package/dist/cjs/core/ToolLoopExecutor.js.map +0 -1
  597. package/dist/cjs/core/ToolManager.d.ts +0 -250
  598. package/dist/cjs/core/ToolManager.d.ts.map +0 -1
  599. package/dist/cjs/core/ToolManager.js +0 -1104
  600. package/dist/cjs/core/ToolManager.js.map +0 -1
  601. package/dist/cjs/core/createAgent.d.ts +0 -35
  602. package/dist/cjs/core/createAgent.d.ts.map +0 -1
  603. package/dist/cjs/core/createAgent.js +0 -39
  604. package/dist/cjs/core/createAgent.js.map +0 -1
  605. package/dist/cjs/core/flow-namespace.d.ts +0 -64
  606. package/dist/cjs/core/flow-namespace.d.ts.map +0 -1
  607. package/dist/cjs/core/flow-namespace.js +0 -182
  608. package/dist/cjs/core/flow-namespace.js.map +0 -1
  609. package/dist/cjs/core/toolGates.d.ts +0 -24
  610. package/dist/cjs/core/toolGates.d.ts.map +0 -1
  611. package/dist/cjs/core/toolGates.js +0 -52
  612. package/dist/cjs/core/toolGates.js.map +0 -1
  613. package/dist/cjs/types/persistence.d.ts +0 -254
  614. package/dist/cjs/types/persistence.d.ts.map +0 -1
  615. package/dist/cjs/types/persistence.js +0 -7
  616. package/dist/cjs/types/persistence.js.map +0 -1
  617. package/dist/cjs/types/prompt-cache.d.ts +0 -15
  618. package/dist/cjs/types/prompt-cache.d.ts.map +0 -1
  619. package/dist/cjs/types/prompt-cache.js +0 -6
  620. package/dist/cjs/types/prompt-cache.js.map +0 -1
  621. package/dist/cjs/types/signals.d.ts +0 -263
  622. package/dist/cjs/types/signals.d.ts.map +0 -1
  623. package/dist/cjs/types/signals.js +0 -11
  624. package/dist/cjs/types/signals.js.map +0 -1
  625. package/dist/cjs/types/template.d.ts +0 -84
  626. package/dist/cjs/types/template.d.ts.map +0 -1
  627. package/dist/cjs/types/template.js +0 -3
  628. package/dist/cjs/types/template.js.map +0 -1
  629. package/dist/cjs/utils/condition.d.ts +0 -63
  630. package/dist/cjs/utils/condition.d.ts.map +0 -1
  631. package/dist/cjs/utils/condition.js +0 -239
  632. package/dist/cjs/utils/condition.js.map +0 -1
  633. package/dist/cjs/utils/event.d.ts +0 -6
  634. package/dist/cjs/utils/event.d.ts.map +0 -1
  635. package/dist/cjs/utils/event.js +0 -20
  636. package/dist/cjs/utils/event.js.map +0 -1
  637. package/dist/cjs/utils/id.d.ts +0 -33
  638. package/dist/cjs/utils/id.d.ts.map +0 -1
  639. package/dist/cjs/utils/id.js +0 -84
  640. package/dist/cjs/utils/id.js.map +0 -1
  641. package/dist/cjs/utils/serialize.d.ts +0 -36
  642. package/dist/cjs/utils/serialize.d.ts.map +0 -1
  643. package/dist/cjs/utils/serialize.js +0 -77
  644. package/dist/cjs/utils/serialize.js.map +0 -1
  645. package/dist/cjs/utils/session.d.ts +0 -124
  646. package/dist/cjs/utils/session.d.ts.map +0 -1
  647. package/dist/cjs/utils/session.js +0 -396
  648. package/dist/cjs/utils/session.js.map +0 -1
  649. package/dist/constants/index.d.ts +0 -2
  650. package/dist/constants/index.d.ts.map +0 -1
  651. package/dist/constants/index.js +0 -4
  652. package/dist/constants/index.js.map +0 -1
  653. package/dist/core/AutoChainExecutor.d.ts +0 -97
  654. package/dist/core/AutoChainExecutor.d.ts.map +0 -1
  655. package/dist/core/AutoChainExecutor.js +0 -284
  656. package/dist/core/AutoChainExecutor.js.map +0 -1
  657. package/dist/core/BranchEvaluator.d.ts +0 -55
  658. package/dist/core/BranchEvaluator.d.ts.map +0 -1
  659. package/dist/core/BranchEvaluator.js +0 -121
  660. package/dist/core/BranchEvaluator.js.map +0 -1
  661. package/dist/core/DirectiveChainTracker.d.ts +0 -49
  662. package/dist/core/DirectiveChainTracker.d.ts.map +0 -1
  663. package/dist/core/DirectiveChainTracker.js +0 -117
  664. package/dist/core/DirectiveChainTracker.js.map +0 -1
  665. package/dist/core/Events.d.ts +0 -26
  666. package/dist/core/Events.d.ts.map +0 -1
  667. package/dist/core/Events.js +0 -137
  668. package/dist/core/Events.js.map +0 -1
  669. package/dist/core/Flow.d.ts +0 -183
  670. package/dist/core/Flow.d.ts.map +0 -1
  671. package/dist/core/Flow.js +0 -547
  672. package/dist/core/Flow.js.map +0 -1
  673. package/dist/core/FlowRouter.d.ts +0 -183
  674. package/dist/core/FlowRouter.d.ts.map +0 -1
  675. package/dist/core/FlowRouter.js +0 -1043
  676. package/dist/core/FlowRouter.js.map +0 -1
  677. package/dist/core/PersistenceManager.d.ts +0 -114
  678. package/dist/core/PersistenceManager.d.ts.map +0 -1
  679. package/dist/core/PersistenceManager.js +0 -332
  680. package/dist/core/PersistenceManager.js.map +0 -1
  681. package/dist/core/PromptComposer.d.ts +0 -47
  682. package/dist/core/PromptComposer.d.ts.map +0 -1
  683. package/dist/core/PromptComposer.js +0 -393
  684. package/dist/core/PromptComposer.js.map +0 -1
  685. package/dist/core/PromptSectionCache.d.ts +0 -48
  686. package/dist/core/PromptSectionCache.d.ts.map +0 -1
  687. package/dist/core/PromptSectionCache.js +0 -104
  688. package/dist/core/PromptSectionCache.js.map +0 -1
  689. package/dist/core/ResponseEngine.d.ts +0 -43
  690. package/dist/core/ResponseEngine.d.ts.map +0 -1
  691. package/dist/core/ResponseEngine.js +0 -231
  692. package/dist/core/ResponseEngine.js.map +0 -1
  693. package/dist/core/ResponseGenerationError.d.ts +0 -30
  694. package/dist/core/ResponseGenerationError.d.ts.map +0 -1
  695. package/dist/core/ResponseGenerationError.js +0 -31
  696. package/dist/core/ResponseGenerationError.js.map +0 -1
  697. package/dist/core/ResponseModal.d.ts +0 -305
  698. package/dist/core/ResponseModal.d.ts.map +0 -1
  699. package/dist/core/ResponseModal.js +0 -1410
  700. package/dist/core/ResponseModal.js.map +0 -1
  701. package/dist/core/ResponsePipeline.d.ts +0 -220
  702. package/dist/core/ResponsePipeline.d.ts.map +0 -1
  703. package/dist/core/ResponsePipeline.js +0 -1035
  704. package/dist/core/ResponsePipeline.js.map +0 -1
  705. package/dist/core/SessionFinalizer.d.ts +0 -34
  706. package/dist/core/SessionFinalizer.d.ts.map +0 -1
  707. package/dist/core/SessionFinalizer.js +0 -84
  708. package/dist/core/SessionFinalizer.js.map +0 -1
  709. package/dist/core/SessionManager.d.ts +0 -112
  710. package/dist/core/SessionManager.d.ts.map +0 -1
  711. package/dist/core/SessionManager.js +0 -301
  712. package/dist/core/SessionManager.js.map +0 -1
  713. package/dist/core/SignalCoordinator.d.ts +0 -103
  714. package/dist/core/SignalCoordinator.d.ts.map +0 -1
  715. package/dist/core/SignalCoordinator.js +0 -203
  716. package/dist/core/SignalCoordinator.js.map +0 -1
  717. package/dist/core/SignalEvaluator.d.ts +0 -86
  718. package/dist/core/SignalEvaluator.d.ts.map +0 -1
  719. package/dist/core/SignalEvaluator.js +0 -312
  720. package/dist/core/SignalEvaluator.js.map +0 -1
  721. package/dist/core/SignalProcessor.d.ts +0 -152
  722. package/dist/core/SignalProcessor.d.ts.map +0 -1
  723. package/dist/core/SignalProcessor.js +0 -498
  724. package/dist/core/SignalProcessor.js.map +0 -1
  725. package/dist/core/Step.d.ts +0 -184
  726. package/dist/core/Step.d.ts.map +0 -1
  727. package/dist/core/Step.js +0 -594
  728. package/dist/core/Step.js.map +0 -1
  729. package/dist/core/StepLifecycle.d.ts +0 -43
  730. package/dist/core/StepLifecycle.d.ts.map +0 -1
  731. package/dist/core/StepLifecycle.js +0 -176
  732. package/dist/core/StepLifecycle.js.map +0 -1
  733. package/dist/core/StreamingToolExecutor.d.ts +0 -142
  734. package/dist/core/StreamingToolExecutor.d.ts.map +0 -1
  735. package/dist/core/StreamingToolExecutor.js +0 -483
  736. package/dist/core/StreamingToolExecutor.js.map +0 -1
  737. package/dist/core/ToolLoopExecutor.d.ts +0 -133
  738. package/dist/core/ToolLoopExecutor.d.ts.map +0 -1
  739. package/dist/core/ToolLoopExecutor.js +0 -564
  740. package/dist/core/ToolLoopExecutor.js.map +0 -1
  741. package/dist/core/ToolManager.d.ts +0 -250
  742. package/dist/core/ToolManager.d.ts.map +0 -1
  743. package/dist/core/ToolManager.js +0 -1098
  744. package/dist/core/ToolManager.js.map +0 -1
  745. package/dist/core/createAgent.d.ts +0 -35
  746. package/dist/core/createAgent.d.ts.map +0 -1
  747. package/dist/core/createAgent.js +0 -36
  748. package/dist/core/createAgent.js.map +0 -1
  749. package/dist/core/flow-namespace.d.ts +0 -64
  750. package/dist/core/flow-namespace.d.ts.map +0 -1
  751. package/dist/core/flow-namespace.js +0 -179
  752. package/dist/core/flow-namespace.js.map +0 -1
  753. package/dist/core/toolGates.d.ts +0 -24
  754. package/dist/core/toolGates.d.ts.map +0 -1
  755. package/dist/core/toolGates.js +0 -49
  756. package/dist/core/toolGates.js.map +0 -1
  757. package/dist/types/persistence.d.ts +0 -254
  758. package/dist/types/persistence.d.ts.map +0 -1
  759. package/dist/types/persistence.js +0 -6
  760. package/dist/types/persistence.js.map +0 -1
  761. package/dist/types/prompt-cache.d.ts +0 -15
  762. package/dist/types/prompt-cache.d.ts.map +0 -1
  763. package/dist/types/prompt-cache.js +0 -5
  764. package/dist/types/prompt-cache.js.map +0 -1
  765. package/dist/types/signals.d.ts +0 -263
  766. package/dist/types/signals.d.ts.map +0 -1
  767. package/dist/types/signals.js +0 -10
  768. package/dist/types/signals.js.map +0 -1
  769. package/dist/types/template.d.ts +0 -84
  770. package/dist/types/template.d.ts.map +0 -1
  771. package/dist/types/template.js +0 -2
  772. package/dist/types/template.js.map +0 -1
  773. package/dist/utils/condition.d.ts +0 -63
  774. package/dist/utils/condition.d.ts.map +0 -1
  775. package/dist/utils/condition.js +0 -230
  776. package/dist/utils/condition.js.map +0 -1
  777. package/dist/utils/event.d.ts +0 -6
  778. package/dist/utils/event.d.ts.map +0 -1
  779. package/dist/utils/event.js +0 -17
  780. package/dist/utils/event.js.map +0 -1
  781. package/dist/utils/id.d.ts +0 -33
  782. package/dist/utils/id.d.ts.map +0 -1
  783. package/dist/utils/id.js +0 -77
  784. package/dist/utils/id.js.map +0 -1
  785. package/dist/utils/serialize.d.ts +0 -36
  786. package/dist/utils/serialize.d.ts.map +0 -1
  787. package/dist/utils/serialize.js +0 -72
  788. package/dist/utils/serialize.js.map +0 -1
  789. package/dist/utils/session.d.ts +0 -124
  790. package/dist/utils/session.d.ts.map +0 -1
  791. package/dist/utils/session.js +0 -379
  792. package/dist/utils/session.js.map +0 -1
  793. package/docs/concepts/directives.md +0 -369
  794. package/docs/reference/adapters.md +0 -543
  795. package/docs/reference/create-agent.md +0 -216
  796. package/docs/reference/directive.md +0 -242
  797. package/docs/reference/signals.md +0 -368
  798. package/examples/02-data-extraction.ts +0 -90
  799. package/examples/05-branching.ts +0 -140
  800. package/examples/06-flow-control.ts +0 -103
  801. package/examples/08-persistence.ts +0 -98
  802. package/examples/09-signals.ts +0 -144
  803. package/src/adapters/MemoryAdapter.ts +0 -281
  804. package/src/adapters/MongoAdapter.ts +0 -341
  805. package/src/adapters/OpenSearchAdapter.ts +0 -693
  806. package/src/adapters/PostgreSQLAdapter.ts +0 -487
  807. package/src/adapters/PrismaAdapter.ts +0 -617
  808. package/src/adapters/RedisAdapter.ts +0 -439
  809. package/src/adapters/SQLiteAdapter.ts +0 -496
  810. package/src/adapters/index.ts +0 -43
  811. package/src/adapters/sessionRow.ts +0 -57
  812. package/src/constants/index.ts +0 -2
  813. package/src/core/AutoChainExecutor.ts +0 -397
  814. package/src/core/BranchEvaluator.ts +0 -161
  815. package/src/core/DirectiveChainTracker.ts +0 -144
  816. package/src/core/Events.ts +0 -164
  817. package/src/core/Flow.ts +0 -665
  818. package/src/core/FlowRouter.ts +0 -1540
  819. package/src/core/PersistenceManager.ts +0 -446
  820. package/src/core/PromptComposer.ts +0 -448
  821. package/src/core/PromptSectionCache.ts +0 -125
  822. package/src/core/ResponseEngine.ts +0 -338
  823. package/src/core/ResponseGenerationError.ts +0 -53
  824. package/src/core/ResponseModal.ts +0 -1902
  825. package/src/core/ResponsePipeline.ts +0 -1404
  826. package/src/core/SessionFinalizer.ts +0 -108
  827. package/src/core/SessionManager.ts +0 -372
  828. package/src/core/SignalCoordinator.ts +0 -263
  829. package/src/core/SignalEvaluator.ts +0 -404
  830. package/src/core/SignalProcessor.ts +0 -663
  831. package/src/core/Step.ts +0 -782
  832. package/src/core/StepLifecycle.ts +0 -242
  833. package/src/core/StreamingToolExecutor.ts +0 -609
  834. package/src/core/ToolLoopExecutor.ts +0 -749
  835. package/src/core/ToolManager.ts +0 -1379
  836. package/src/core/createAgent.ts +0 -40
  837. package/src/core/flow-namespace.ts +0 -227
  838. package/src/core/toolGates.ts +0 -72
  839. package/src/types/persistence.ts +0 -303
  840. package/src/types/prompt-cache.ts +0 -17
  841. package/src/types/signals.ts +0 -338
  842. package/src/types/template.ts +0 -98
  843. package/src/utils/condition.ts +0 -296
  844. package/src/utils/event.ts +0 -16
  845. package/src/utils/id.ts +0 -91
  846. package/src/utils/serialize.ts +0 -86
  847. package/src/utils/session.ts +0 -501
@@ -0,0 +1,477 @@
1
+ ---
2
+ title: "@falai/agent v4 — one Flow (design v2)"
3
+ description: "Design record for @falai/agent 4.0: flows, automations and signals become one Flow with triggers, runs, waits and per-field asks. Not part of the docs sidebar."
4
+ type: concept
5
+ order: 99
6
+ sidebar: false
7
+ ---
8
+
9
+ > **Design record, approved 2026-09-20.** This is the reference the 4.0 implementation follows.
10
+ > It came out of a read-only research pass over `@falai/agent` 3.4.5 and its three consumers
11
+ > (prospectar, atendime, ilojista), five independent designs, three judges, one synthesis, four
12
+ > adversarial critiques (90 holes) and a repair pass. Section numbers below are cited from the
13
+ > implementation plan and from commit messages as "design §N".
14
+ >
15
+ > **Decided with Gus:** scope is the framework plus all three products; all seven persistence
16
+ > adapters stay, reshaped to `Store` implementations; the host gate (`silenced`) is one mouth and
17
+ > closes `say`, `prompt` and speaking `do` steps alike; routing, mention detection and extraction
18
+ > share one `understand` call, eval-gated on Gemini and GLM before `4.0.0`.
19
+ >
20
+ > **Assumed defaults:** `maxAsks` 3; `repeat: 'once'` per session for `message` flows; zero LLM
21
+ > calls while a human owns the lead unless `silenced: { understand: true }`; lead-only events run
22
+ > in a `lead:<id>` session with `silenced: 'sem conversa'`; END_FLOW / BOT_DETECTED / SCHEDULING
23
+ > ship as `kind: 'system'` recipes; `criar_fluxo` keeps the host's generation call with
24
+ > `flowSpecSchema`; session = conversation with `anchor: 'lead'`; migrated flows get
25
+ > `onEnd: 'stay'` written explicitly.
26
+ >
27
+ > **Refined during implementation (slices 1–2, 2026-09-20):**
28
+ > - The factory is a two-step chain: `falai<C>()` returns a root whose `.fields(defs)` binds the
29
+ > data type, and `flow` / `action` / `event` / `condition` / `agent` hang off that bound object,
30
+ > so `collect`, `ask`, `clearOnStart` and `ctx.set` are checked against the slugs. `f.agent()`
31
+ > passes the fields itself; `type Data = DataOf<typeof f>` replaces `InferData<typeof fields>`.
32
+ > TypeScript cannot infer one generic while another is written by hand, which is why §3's
33
+ > `const fields = f.fields(...)` then `f.agent({ fields })` shape was dropped.
34
+ > - Action, event and condition names inside a flow are plain strings, checked when the agent is
35
+ > built, the same path a JSON `FlowSpec` takes. The mapped-type unions in §3 (`{ [K in keyof
36
+ > A]: ... }[keyof A]`) would have made every flow generic in four parameters.
37
+ > - `Condition` is `{ check(ctx, arg) }` and `Action` is `{ parameters, run(params, ctx) }`, as
38
+ > method signatures: method bivariance lets a heterogeneous map type without `any`.
39
+ > - `Store.save` returns the saved session, with the bumped version, instead of `void`.
40
+ > - A mention trigger's `extract` is a flat map of parameter definitions (`{ trecho: { type:
41
+ > 'string' } }`), the S7 form, not a JSON schema object: one shape for fields, action
42
+ > parameters and extraction, and `toWireSchema` applies to all three.
43
+ > - Tool handlers take `(args, ctx)`, the order every provider SDK uses.
44
+ > - Prompt scaffolding (section headings, envelope instructions, routing rules) is written in
45
+ > English; the authored content it carries (prompts, `ask` texts, instructions, knowledge) and
46
+ > the outcome `detail` strings the host shows in *Execuções* stay in the product's language.
47
+
48
+ # @falai/agent v4 — one Flow (design v2)
49
+
50
+ ## 1. Mental model
51
+
52
+ A **Flow** is a trigger plus an ordered list of steps. The trigger says when a **run** starts: the lead asks for this (`message`: the AI routes the conversation here), the lead mentions this (`mention`: the AI detects it, the run reacts beside the conversation), something happens in the host (`event` with a payload), the lead goes quiet (`silence`), or nothing — the host starts it (*Início manual*). Each step is one of five things: the AI talks (`prompt` / `collect`), a fixed message goes out (`say`), the host does something (`do`), the run waits (`wait`), or the code forks (`if`). Fields live on the agent schema, each with its own *como perguntar*; they land in any order and a step ends when its fields are known. A **session** (one per conversation) holds many runs; at most one run is asking a question at a time. One method, `agent.turn()`, takes whatever just happened, moves the runs by code, spends at most two LLM calls (understand the lead, phrase the reply) plus one per tool round, and hands back the messages to send, the timers to set and a per-step outcome line. The framework never sends, never sleeps, never saves; the host does those three things after a successful compare-and-swap. Product side: one page (*Fluxos*, sections *Funil* / *Atendimento*), one card (*Quando X → faça Y*), one editor, one assistant tool, one drawer (*Execuções*).
53
+
54
+ Child: "A flow is a list of steps that starts when something happens. The AI does the talking. The code does the rest." Junior: "`agent.turn({ message: 'oi' })` returns the messages to send."
55
+
56
+ ## 2. Primitives: stays, merges, dies
57
+
58
+ | Old | Fate |
59
+ |---|---|
60
+ | Agent | Stays; immutable config, one instance serves every session. `_context`, `_pendingData`, `currentSession` die; PromptSectionCache becomes a per-turn instance (its property tests stay). `context` and `history` arrive per turn. |
61
+ | Flow | THE noun; absorbs Signal and CrmAutomation. Gains `on[]`, `anchor`, `while`, `clearOnStart`, `onEnd`. Loses `when`/`if` (→ triggers), `requiredFields`, `optionalFields`, `reentrant`, `onComplete`, `hooks`. |
62
+ | Step `prompt` / `auto` / `reply` | `prompt` stays (AI talks); `reply` → `say`; `auto` → `do`, `if`, `wait`. |
63
+ | `collect` | Stays as step completion: a talk step ends when its fields are known, skipped without a call when already known. `{ collect: [...] }` alone asks with the schema's `ask`. |
64
+ | `requires`, `skip`, `requiredFields`, `optionalFields` | Die. Order, known-field skipping, `if` steps and `maxAsks` cover every use. |
65
+ | `branches` (`when`/`if` mid-step exits) | Stay as `branches[]` on talk and `wait` steps, judged while the step is asking — `if` free, `when` inside the understand call. There is no standalone `when` step: AI forks only exist where fresh text exists. |
66
+ | `onComplete`, `reentrant` | → `onEnd` (`'end' \| 'stay' \| 'reset'`), `repeat` on the trigger, `clearOnStart`. |
67
+ | prepare / finalize / onEnter / onExit | Die → a `do` step at that position. |
68
+ | Directive, `dispatch`, `pendingDirective`, `flow.merge/validate`, five appliers | Die. Every position change goes through one `advance()`. Tools return `{ value?, data? }`. |
69
+ | Signal | → Flow with a `mention` trigger (§7). |
70
+ | CrmAutomation `trigger` | → `on[]`: `stage_entered`/`meeting_booked`/`tag_added`/`known_contact` → `event`; `stage_stagnant` → `event` + `after`; `signal` → `mention`; `conversation_idle` → `silence`; `manual` → no trigger. |
71
+ | CrmAutomation action / wait / wait_reply / condition | → `do` / `wait` / `wait` + `else` / `if`. |
72
+ | CrmAutomation `conditions`, `hopCount` | → trigger `if` as a JSON condition; `hop` rides on events and `then: { flow }`. |
73
+ | 14 actions | Host `do` actions, except `ai_message` → `prompt`, `send_whatsapp_message` → `say`, `start_automation` → `then: { flow }`. |
74
+ | StepIntegrations / flow integrations / step `media` / `initialData` | → `do` steps / `say` with `media` (`once: true` keeps "once per conversation"); `initialData` removed, no replacement. |
75
+ | `endBehavior` | → `onEnd`; *vai para outro fluxo* → `then: { flow }` on the last step. |
76
+ | `ai_message` + composer | → a `prompt` step reached by a wake. Composer deleted. |
77
+ | `haltReply` | → a `say` first in a mention flow: another run's `say` silences the floor's talk that turn. |
78
+ | behavior once/cooldown (two ledgers) | → one `claims` ledger (§5). |
79
+ | PersistenceAdapter ×7, `restoreSession`, `autoSave` | → `Store { load, save(expectedVersion) }`, `SessionConflictError`, `MemoryStore`, one `PostgresStore` reference. |
80
+ | `respondStream` | → `agent.turnStream()`. |
81
+
82
+ Core modules removed: ResponsePipeline, FlowRouter, ResponseModal, Signal*, AutoChainExecutor, StepLifecycle, DirectiveChainTracker, PersistenceManager, five adapters — about half of `src/core`. One `Runner` with one `advance()` plus `Understand.ts`, `Speak.ts`, `Envelope.ts`, `Migrate.ts`.
83
+
84
+ ## 3. Public TypeScript API
85
+
86
+ ```ts
87
+ import { falai, InferData, StructuredSchema, Instruction, tool } from '@falai/agent';
88
+
89
+ type Duration = `${number}${'s' | 'm' | 'h' | 'd'}`;
90
+ type Repeat = 'once' | 'always' | { cooldown: Duration };
91
+ type Template = string; // {{data.x}} {{context.x}} {{input.x}}
92
+ type Next = string | 'end' | { step: string; clear?: string[] } | { flow: Template; input?: unknown };
93
+ type PredCtx<C, D, P = unknown> = { context: C; data: Partial<D>; input: P; run: Run; silenced?: string; now: Date };
94
+ type ConditionSpec<Cond> = { [K in keyof Cond]?: Arg<Cond[K]> }; // JSON form; built-ins: equals, known, silenced
95
+ type Pred<C, D, Cond, P = unknown> = ((ctx: PredCtx<C, D, P>) => boolean) | ConditionSpec<Cond>;
96
+ type Branch<C, D, Cond> = { then: Next } & ({ when: string } | { if: Pred<C, D, Cond> });
97
+
98
+ type Trigger<C, D, Cond, E> = { repeat?: Repeat } & ( // default: message/mention/silence 'once', event 'always'
99
+ | { message: string[]; if?: Pred<C, D, Cond> } // "o cliente pede isso"; [] = catch-all
100
+ | { mention: string[]; extract?: StructuredSchema; if?: Pred<C, D, Cond> } // "o cliente fala disso"; [] + if = code-only
101
+ | { silence: Duration; if?: Pred<C, D, Cond>; businessHours?: boolean }
102
+ | { [K in keyof E]: { event: K; if?: Pred<C, D, Cond, E[K]>; after?: Duration; businessHours?: boolean } }[keyof E]
103
+ );
104
+
105
+ type Talk<C, D, Cond> = ({ prompt: Template; collect?: (keyof D)[] } | { collect: (keyof D)[]; prompt?: Template }) & {
106
+ ask?: Partial<Record<keyof D, string>>; // per-flow wording; schema `ask` is the default
107
+ maxAsks?: number; branches?: Branch<C, D, Cond>[]; tools?: string[]; instructions?: Instruction<C, D>[];
108
+ };
109
+
110
+ type Step<C, D, Cond, A extends ActionMap, E> = { id: string; label?: string; then?: Next; ui?: Record<string, unknown> } & (
111
+ | Talk<C, D, Cond>
112
+ | { say: Template; media?: { slug: string }; once?: boolean }
113
+ | { [K in keyof A]: { do: K; with: Params<A[K]>; onFail?: Next } }[keyof A]
114
+ | { wait: Duration; businessHours?: boolean; else?: Next; branches?: Branch<C, D, Cond>[] } // then = timed out, else = lead replied
115
+ | { [K in keyof E]: { wait: { event: K; upTo?: Duration }; else?: Next } }[keyof E] // then = event came, else = timed out (upTo default 30d)
116
+ | { if: Pred<C, D, Cond>; else?: Next } // then = true, else = false (default 'end')
117
+ );
118
+
119
+ interface Flow<C, D, Cond, A extends ActionMap, E> {
120
+ id: string; name: string; description?: string;
121
+ on?: Trigger<C, D, Cond, E>[]; // absent or [] = Início manual
122
+ anchor?: string; // 'session' (default) or a host anchor name — "vale por conversa / por lead"
123
+ while?: Pred<C, D, Cond>; // re-checked whenever the run moves; default = trigger `if`
124
+ clearOnStart?: (keyof D)[];
125
+ steps: Step<C, D, Cond, A, E>[]; // ids required, unique, never 'end'
126
+ onEnd?: 'end' | 'stay' | 'reset'; // default 'end'
127
+ instructions?: Instruction<C, D>[]; tools?: string[];
128
+ }
129
+
130
+ type ActionResult = { ok: true; detail?: string; spoke?: true } | { skipped: string } | { failed: string } | { defer: Duration; detail: string };
131
+ interface ActionCtx<C, D> { context: C; data: Partial<D>; input: unknown; run: Run; key: string; dedupeKey: string; silenced?: string; now: Date; set(patch: Partial<D>): void }
132
+ type ToolResult<D> = { value?: unknown; data?: Partial<D> };
133
+ ```
134
+
135
+ One factory carries the only explicit generic an app writes; everything else is inferred from values.
136
+
137
+ ```ts
138
+ interface LeadContext { lead: { id: string; name?: string; tags: string[]; stageId: string; assignee?: string; owner: 'ai' | 'human' }; phone: string }
139
+ const f = falai<LeadContext>(); // falai() with no context; createAgent = falai().agent
140
+
141
+ const fields = f.fields({
142
+ nome: { type: 'string', ask: 'Pergunte o nome de um jeito leve, sem tom de formulário.' },
143
+ empresa: { type: 'string', ask: 'Pergunte de qual empresa a pessoa fala.' },
144
+ tamanho: { type: 'string', enum: ['1-10', '11-50', '51-200', '200+'], ask: 'Pergunte quantas pessoas trabalham lá; ofereça as faixas.' },
145
+ urgencia: { type: 'string', enum: ['agora', '30 dias', 'sem prazo'], ask: 'Pergunte para quando precisam resolver.' },
146
+ orcamento: { type: 'number', ask: 'Pergunte a faixa de investimento, dizendo que é só para orientar.' },
147
+ confirmado:{ type: 'boolean', ask: 'Resuma em uma frase o que anotou e pergunte se está tudo certo.' }, // boolean → extract: 'asked' by default
148
+ });
149
+ type Data = InferData<typeof fields>;
150
+
151
+ const actions = {
152
+ notify: f.action({ parameters: { recipient: { type: 'string' }, message: { type: 'string' } },
153
+ run: async (w, ctx) => { await notifications.send(ctx.context.lead, w.recipient, w.message, { key: ctx.key }); return { ok: true, detail: 'aviso enviado' }; } }),
154
+ add_tags: f.action({ parameters: { tags: { type: 'array', items: { type: 'string' } } },
155
+ run: async (w, ctx) => { await crm.addTags(ctx.context.lead.id, w.tags); return { ok: true }; } }),
156
+ assign_lead: f.action({ parameters: { to: { type: 'string' } },
157
+ run: async (w, ctx) => (await ownership.assign(ctx.context.lead.id, w.to)) ? { ok: true } : { skipped: 'lead já está com uma pessoa' } }),
158
+ send_template: f.action({ parameters: { templateId: { type: 'string' } },
159
+ run: async (w, ctx) => { const r = await meta.sendPaid(ctx.context.phone, w.templateId, { idempotencyKey: ctx.key });
160
+ return r.noCredits ? { defer: '24h', detail: 'sem créditos' } : { ok: true, spoke: true }; } }),
161
+ book: f.action({ parameters: { date: { type: 'string' }, time: { type: 'string' } },
162
+ run: async (w, ctx) => { const ev = await agenda.create(w, { idempotencyKey: ctx.key }); ctx.set({ agenda_event_id: ev.id }); return { ok: true }; } }),
163
+ };
164
+ const events = {
165
+ stage_entered: f.event<{ stageId: string }>(),
166
+ meeting_booked: f.event<{ eventId: string }>(),
167
+ ig_comment: f.event<{ postId: string; text: string }>(),
168
+ reaction: f.event<{ emoji: string }>({ direction: 'inbound' }), // resolves reply waits, stamps lastUserAt
169
+ human_message: f.event<{ text: string }>({ direction: 'outbound' }), // stamps lastAssistantAt, re-arms silence
170
+ };
171
+ const conditions = { // plus built-ins equals / known / silenced
172
+ tagsAny: f.condition((ctx, tags: string[]) => tags.some(t => ctx.context.lead.tags.includes(t))),
173
+ inStage: f.condition((ctx, stageId: string) => ctx.context.lead.stageId === stageId),
174
+ channel: f.condition((ctx, kind: 'assistant' | 'campaign') => ctx.context.channel === kind),
175
+ };
176
+ ```
177
+
178
+ **S1 — Triagem, complete.** The confirmation is a collected boolean; a "no" clears it and re-asks; `avisa` runs only after the lead's ok.
179
+
180
+ ```ts
181
+ const triagem = f.flow({
182
+ id: 'triagem', name: 'Triagem', description: 'Quando alguém chega querendo saber se o produto serve para a empresa dele',
183
+ on: [{ message: ['quer saber como funciona', 'pede um orçamento', 'quer saber se serve para a empresa'] }],
184
+ steps: [
185
+ { id: 'quem', prompt: 'Descubra quem é e de onde fala.', collect: ['nome', 'empresa'] },
186
+ { id: 'porte', collect: ['tamanho', 'urgencia'] },
187
+ { id: 'grana', collect: ['orcamento'], maxAsks: 2 },
188
+ { id: 'confirma', collect: ['confirmado'] },
189
+ { id: 'ok', if: { equals: { confirmado: true } }, else: { step: 'quem', clear: ['confirmado'] } },
190
+ { id: 'avisa', do: 'notify', with: { recipient: 'leadAssignee', message: 'Lead qualificado: {{data.nome}} ({{data.empresa}}), {{data.tamanho}} pessoas, {{data.urgencia}}.' } },
191
+ { id: 'tchau', prompt: 'Agradeça e diga que um vendedor continua daqui.' },
192
+ ],
193
+ });
194
+ ```
195
+
196
+ **S2 — Retomar quem sumiu, complete.** Started by the silence timer the framework arms after it speaks; any reply ends the run; while a human owns the lead the seller gets a reminder instead.
197
+
198
+ ```ts
199
+ const retomar = f.flow({
200
+ id: 'retomar', name: 'Retomar quem sumiu',
201
+ on: [{ silence: '24h', businessHours: true, if: ({ context }) => context.lead.owner === 'ai' }],
202
+ anchor: 'lead',
203
+ steps: [
204
+ { id: 'gate', if: { silenced: true }, then: 'lembra', else: 'p1' },
205
+ { id: 'p1', prompt: 'Retome a conversa de forma leve: relembre o assunto em aberto e pergunte se ainda faz sentido.' },
206
+ { id: 'w1', wait: '2d', else: 'end' },
207
+ { id: 'p2', prompt: 'Última tentativa, curta e sem pressão: fica à disposição quando quiser retomar.' },
208
+ { id: 'w2', wait: '3d', else: 'end' },
209
+ { id: 'n1', do: 'notify', with: { recipient: 'leadAssignee', message: '{{data.nome}} não respondeu a duas retomadas — vale um contato manual.' }, then: 'end' },
210
+ { id: 'lembra', do: 'notify', with: { recipient: 'leadAssignee', message: 'Hora de fazer follow-up com {{data.nome}}.' } },
211
+ ],
212
+ });
213
+
214
+ const agent = f.agent({
215
+ name: 'Ana', provider, fields, actions, events, conditions, tools: [checkAvailability],
216
+ flows: [triagem, retomar],
217
+ idle: { prompt: 'Responda pela empresa; não invente preços.' }, // the one speaker that is not a step; 'silent' mutes it
218
+ clock: () => new Date(), // tests pass fakeClock
219
+ businessHours: (at, ctx) => nextWorkingTime(at, ctx.context.lead), // snaps, never clamps
220
+ });
221
+ ```
222
+
223
+ Entry points and result:
224
+
225
+ ```ts
226
+ type TurnInput<C, D, E> = {
227
+ sessionId: string; session?: Session<D>; // absent = first turn; a wake never creates a session
228
+ context?: C; history?: History; // hosts pass history on EVERY input kind, wakes included
229
+ silenced?: string | { reason: string; understand?: boolean }; // host gate; default zero calls, understand: true opts in
230
+ anchors?: Record<string, { key: string; lastInboundAt?: string }>; // { lead: { key: 'lead:456', lastInboundAt } }
231
+ claims?: { held: Record<string, string>; active: string[] }; // dedupeKey → at; `${flowId}:${anchor}` live in other sessions
232
+ } & (
233
+ | { message: string; id?: string; at?: string } // hosts pass channel id + receipt time; the playground may omit
234
+ | { wake: string }
235
+ | { [K in keyof E]: { event: K; payload: E[K]; key: string; hop?: number } }[keyof E]
236
+ | { start: { flow: string; input?: unknown; key: string; hop?: number } }
237
+ );
238
+
239
+ interface TurnResult<D> {
240
+ session: Session<D>; changed: boolean; // changed: false → save nothing
241
+ messages: Array<{ text: string; kind: 'ai' | 'verbatim'; media?: { slug: string }; afterMs: number; key: string; runId?: string; stepId?: string }>;
242
+ schedule: Array<{ key: string; at: Date; replaces?: string }>; // jobId = key; at fire: turn({ wake: key })
243
+ outcomes: StepOutcome[];
244
+ started: Array<{ runId: string; flowId: string; anchor: string; dedupeKey: string }>;
245
+ ended: Array<Run & { reason: 'end' | 'flow' | 'reset' | 'skipped' | 'failed' | 'replaced' }>;
246
+ skipped: Array<{ flowId: string; anchor: string; triggerKey: string; detail: string }>; // trigger-level, for Execuções
247
+ llmCalls: number;
248
+ }
249
+ declare function turnStream(input: TurnInput): AsyncIterable<{ delta: string } | { done: true; result: TurnResult }>;
250
+
251
+ const r = await agent.turn({ sessionId: 'demo', message: 'oi' }); // README: one call, one obvious result
252
+ console.log(r.messages[0].text);
253
+ ```
254
+
255
+ Stored flows load as `FlowSpec`: the same object as JSON with a **flat step** `{ id, kind: 'prompt' | 'collect' | 'say' | 'do' | 'wait' | 'waitEvent' | 'if', ...props, then?, else? }`, `do` names, condition names and field slugs resolved through the agent's registries. `validateFlow(spec, agent)` throws `FlowConfigurationError` naming the unknown field, action, event, condition or step id, the reserved id `end`, a missing `else` on a backward `if`, and warns on a backward edge without `clear`. `flowSpecSchema(agent)` returns FlowSpec's JSON schema with the workspace's actions (with their parameter schemas), events, conditions and fields enumerated — a closed schema Gemini accepts; it is the response schema of the host's generation call.
256
+
257
+ ## 4. The turn pipeline
258
+
259
+ Eight phases, one order for every input; only 3 and 6 spend LLM calls. `turn()` does no I/O but the provider and the host's action handlers.
260
+
261
+ 1. **Load** (code). Migrate a legacy blob (§10). `wake` with no session → `ignorado: sessão inexistente`, `changed: false`.
262
+ 2. **Ingest** (code).
263
+ - `message` / inbound `event`: `lastUserAt = at ?? clock()`; a keyed `id` already in `session.inputs` → no-op, `changed: false`. Every run parked on a timer `wait` with `else` takes `else` (oldest first); a `wait: { event }` on this event takes `then`.
264
+ - Outbound `event`: stamps `lastAssistantAt`, nothing else.
265
+ - `wake` starting with `silence:`: honored only when its `lastAssistantAtMs` equals `session.lastAssistantAt` and the lead has not written since (`lastUserAt`, and the anchor's `lastInboundAt` for lead-anchored flows, both `< lastAssistantAt`); it then goes through the start order below with `trigger.kind: 'silence'`. Otherwise `ignorado: silêncio quebrado`, `changed: false`. Any other `wake`: the run whose `waiting.key` equals it, else `ignorado: wake antigo`. A timer `wait` with `else` whose lead wrote after `waiting.setAt` takes `else` — the reply beat the job.
266
+ - `event` / `start`: flows with a matching trigger start, in this order: `if` (payload as `input`) → `repeat` via claims (cooldown = interval check on `claims[key].at`) → `hop < 5` else `pulado: limite de encadeamento` → one active run per (flow, anchor) against `session.runs` and `claims.active`: a live run still parked on its own `after` timer is **replaced** (`ended.reason: 'replaced'`, its job self-skips), any other live run → `pulado: já em andamento` → `after` parks the new run.
267
+ - Every run about to move re-checks `while` with fresh context; false → ends `pulado: premissa mudou`. Silence-triggered runs add the premise "no lead message since the run started" → `pulado: lead escreveu nesse meio-tempo`.
268
+ 3. **Understand** (≤1 call, `schemaName: 'understand'`). Only for `message` and inbound events with text; skipped under `silenced` unless `understand: true`, and when nothing is AI-conditioned. Candidates: the floor holder's flow (always, whatever its trigger), `message` flows passing `if` + `repeat`, `mention` flows with non-empty `mention` passing `if` + `repeat`. Envelope, every property required and nullable: `{ flows: { id: 0-100 }, mentions: { id: boolean }, extract: { id: {...} }, branches: { 'runId/stepId/i': boolean }, fields: { every pending field with extract 'anywhere' } }`. Shortcuts: exactly one eligible `message` flow and no floor → it starts, no scoring; zero candidates and zero pending fields → zero calls (S9).
269
+ 4. **Decide** (code). Runs starting this turn apply `clearOnStart` first. Extracted values are checked: unknown keys dropped (`ignorado: campo desconhecido`), strings coerced to number/boolean, enum membership enforced, otherwise `campo descartado: valor fora da lista`; then written to `session.data` (one `known`: not `undefined | null | ''`). Mention runs start in flow order (a trigger `if` with `extract` sees `input` now); `mention: []` code-only detectors start here without the call. The first true branch of the asking step takes its `then`. Routing: if a run took or resumed the floor in Ingest, routing is skipped (scores recorded, not applied). Otherwise the floor (any run `running`/`asking`, not `waiting`) stays unless another flow scores ≥ current + 15 and ≥ 40; no floor → best ≥ 40 starts (a `suspended` run of that flow resumes instead), then a `message: []` catch-all, else the `idle` speaker.
270
+ 5. **Run** (code). If no run is `asking`, the most recently `suspended` returns to `asking`. The Runner advances every run that can move, oldest first, through `advance()`: `do` runs the handler with `key = ${runId}:${stepId}:${visit}` (`run.visits[stepId]` increments on entry; at-least-once, before the save); `failed` writes a `failed` outcome and continues to `then` unless `onFail`; `skipped` continues; `defer` re-parks under a fresh wake key; `spoke: true` makes it the turn's speaker. `say` appends a message (`once` → claim `${flowId}:${stepId}:${sessionId}`). `wait ≤ 10s` carries as `afterMs` to the next message emitted this turn, or schedules a real wake if none follows; longer waits park with `waiting` and a `schedule` entry, snapped by `businessHours`. `if` picks `then`/`else`. A talk step whose fields are all known is skipped (0 calls); otherwise it is queued for phase 6 and the current asker becomes `suspended` (a run started this turn by routing never suspends one that took the floor in Ingest). Flow missing from the agent → `pulado: fluxo desativado ou removido`; step missing → `pulado: passo removido`. Caps: 50 steps per run per turn → `falhou: laço de passos`; hop 5.
271
+ 6. **Speak** (≤1 call + tool rounds, `schemaName: 'speak'`). One talk step speaks. Talk step = `prompt`, `collect`, `say`. Rules: a `say` or `spoke: true` emitted this turn by a run **other than** the floor holder, in reply to a user message, silences the floor's talk (`pulado: outra resposta já saiu`; the run stays `asking`) — a run's own `say` never silences its own talk. Under `silenced` no message-producing step runs and no run advances past one: an `asking` run stays `asking`; a run whose talk step was reached by a wake, event or start ends `silenciado: <reason>` (an earlier `if: { silenced: true }` routes around it); `do` steps still run; zero calls. Prompt = identity + flow + step guideline + this step's pending fields with their `ask` (all of them; the step prompt says how many to ask) + known fields as facts + tools + instructions + history. A wake states *não há mensagem nova do cliente; você fala primeiro*. Envelope `{ message, ...pending fields of this step }`, all required and nullable, shallow-merged last-wins across tool rounds. Provider failure: in phase 3 the turn throws `ProviderError` (nothing ran, nothing saved; the host retries the input); in phase 6 the turn returns with the talk step re-parked under `${runId}:${stepId}:${visit}:retry:${atMs}` (+1m, +5m, +15m), outcome `deferred: IA indisponível`, session saved, phase 5 effects not repeated.
272
+ 7. **Settle** (code, the ONE applier). A collect step stays `asking` until its fields are known, a branch fires, or a field hits `maxAsks` (default 3 → `campo pulado: perguntado 3 vezes`); `asked[field]` increments when the step spoke with the field pending and the next lead message left it unknown — detours count, the ceiling is documented. A prompt without `collect` advances after speaking once. `then`: step (visits++), `{ step, clear }` (clears, then jumps), `'end'` → `onEnd`, `{ flow }` (template resolved against `input`/`context`; this run ends `reason: 'flow'`, the new run has `trigger.kind: 'flow'`, key `${parentRunId}:${stepId}:${visit}`, hop+1, and holds the floor). When no run is `asking`, the most recently suspended resumes. If the assistant spoke last, every `silence` flow passing `if` and `repeat` gets `schedule: { key: silence:${flowId}:${sessionId}:${lastAssistantAtMs}, at, replaces: <previous key> }`.
273
+ 8. **Return**: `session` (version unchanged; the host bumps it), `changed`, `messages` in emission order, `schedule`, `outcomes`, `started`, `ended`, `skipped`, `llmCalls`.
274
+
275
+ Budget: message turn 2 calls + tool rounds (1 with a single flow and no pending fields, 0 when silenced); wake to a talk step 1; wake to `do`/`wait` 0. `llmCalls` on every result makes it a test, not a promise.
276
+
277
+ ## 5. Waiting, timers, events
278
+
279
+ ```ts
280
+ interface Run {
281
+ id: string; // `${flowId}#${triggerKey}` — deterministic, replay mints the same keys
282
+ flowId: string; anchor: string; dedupeKey: string; stepId: string | null;
283
+ status: 'running' | 'asking' | 'waiting' | 'suspended';
284
+ trigger: { kind: 'message' | 'mention' | 'silence' | 'event' | 'start' | 'flow'; key: string; payload?: unknown };
285
+ input?: unknown; hop: number; startedAt: string;
286
+ waiting?: { kind: 'timer' | 'event'; key?: string; until?: string; setAt: string; event?: string };
287
+ asked: Record<string, number>; visits: Record<string, number>;
288
+ outcomes: StepOutcome[];
289
+ }
290
+ interface StepOutcome { runId?: string; flowId?: string; stepId?: string; key?: string;
291
+ kind: 'prompt' | 'collect' | 'say' | 'do' | 'wait' | 'if' | 'idle'; status: 'ok' | 'skipped' | 'failed' | 'waiting' | 'deferred';
292
+ detail?: string; next?: string; until?: string; at: string; llmCalls?: number }
293
+
294
+ interface Store<D> { load(id: string): Promise<Session<D> | null>; save(session: Session<D>, expectedVersion: number): Promise<void> } // 0 = insert-if-absent; throws SessionConflictError
295
+ ```
296
+
297
+ **Invariants** (docs verbatim):
298
+
299
+ - I1. The session blob is the unit of consistency: `load(v) → turn → save(expectedVersion = v)`. A losing turn is discarded and the same input replayed.
300
+ - I2. Messages, schedules, claims and ended runs leave the host's hands only after a successful save. `do` handlers are the exception: they run inside the turn, **at-least-once**, and must be idempotent on `ctx.key` (or `ctx.dedupeKey` for `once`/cooldown flows shared across a lead's sessions). `always` flows cannot be made exactly-once across sessions by the framework.
301
+ - I3. A run wake is honored only by the run whose `waiting.key` equals it; a silence wake only when the blob still shows that silence. Everything else is `ignorado`, `changed: false`. Nothing is ever cancelled; `replaces` is a best-effort hint.
302
+ - I4. Every `do` and message carries `key = ${runId}:${stepId}:${visit}`; run ids come from host keys (message `id`, event/start `key`, wake key, parent run), so a replay of the same input mints the same keys and a revisit mints new ones.
303
+ - I5. A run's claim is written in the same save as its first step; a run never exists without its claim.
304
+
305
+ **Keys.** Trigger key: `message`/`mention` → the input `id` (playground: `at`, replays not idempotent); `silence` → `lastAssistantAtMs`; `event`/`start` → the host key; `flow` → `${parentRunId}:${stepId}:${visit}`. Wake key: `${runId}:${stepId}:${atMs}`. Dedupe key `${flowId}:${anchor}:${nonce}`, nonce `''` for `once` and cooldown (blocked while `now - claims[key].at < cooldown`, else overwritten), the trigger key for `always` (last 50 kept).
306
+
307
+ **Host contract:** (a) one `turn` per session at a time; a wake queues behind a debounced inbound not yet turned. (b) On every input: fresh `context`, `history`, `anchors` (with the lead's `lastInboundAt`), `claims` for non-`always` flows, `silenced` for every "cannot speak now" reason (ownership, Pausa, closed 24h window, quota, `sem conversa`); inbound messages carry the channel `id` and `at`. (c) `changed: false` → nothing. Else one transaction: `store.save`, `started[].dedupeKey` into a unique index, `started`/`ended` into a partial unique index `(flowId, anchor) WHERE live`, `outcomes`/`ended`/`skipped` into the `flowRuns` mirror, `messages[]` and `schedule[]` into the outbox. Conflict or unique violation → discard, replay. (d) Drain the outbox: send honoring `afterMs`, record a refused send against `key` in the mirror (`enviada`/`recusada` beside the framework's `gerada`); enqueue wakes with `jobId = key`, `queue.remove(replaces)` best-effort. (e) At fire: `turn({ wake, silenced })`. (f) Call `turn` for every inbound even while a human owns the lead. (g) `businessHours(at)` snaps, never clamps. (h) Lead-level events go to the lead's latest open conversation; none → session `lead:<id>` with `silenced: 'sem conversa'`. (i) `ProviderError` → retry the input with backoff.
308
+
309
+ ```ts
310
+ const clock = fakeClock('2026-09-20T10:00Z');
311
+ const agent = f.agent({ provider: mockProvider({ understand: [...], speak: [...] }), clock, fields, actions: stubs, flows: [triagem, retomar] });
312
+ let r = await agent.turn({ sessionId: 's1', message: 'oi', id: 'm1' });
313
+ clock.advance('24h');
314
+ r = await agent.turn({ sessionId: 's1', session: r.session, wake: r.schedule[0].key, history });
315
+ assert.equal(r.messages[0].kind, 'ai'); assert.equal(r.llmCalls, 1); assert.equal(r.started[0].flowId, 'retomar');
316
+ ```
317
+
318
+ ## 6. Field collection
319
+
320
+ `pending(step) = step.collect in order, minus known fields, minus fields at maxAsks`, computed by code; the model sees every pending field of the current step with its `ask` (step `ask` overrides the schema's). Fields are authored once: `{ type, enum?, description?, ask, extract?: 'anywhere' | 'asked' }`. `'anywhere'` (default for strings and numbers) is harvested by the understand call from any text; `'asked'` (default for booleans) only by the speak envelope of the step that lists it, so a stray "sim" never opens a code gate. `toWireSchema()` allow-lists JSON-schema keys and strips `ask`/`extract`. In the UI a field row is `{ slug, rótulo, tipo, como perguntar }`; `buildSchema(flows)` merges rows by slug and fails when two flows give one slug two types; suffix-based inference dies.
321
+
322
+ Out of order is the default. A collect step skips when its fields are known and stays `asking` until they are; a `prompt` without `collect` speaks once. Confirmation is a collected boolean behind an `if`; a correction clears it (`{ step, clear }`) and re-asks. `maxAsks` (default 3, loud in Execuções) replaces the `requires` deadlock; `maxAsks: 1` is "ask once, don't insist". `clearOnStart` (applied before this turn's extraction) replaces `reentrant`.
323
+
324
+ ## 7. Signals → triggers
325
+
326
+ | Signal facet | v4 |
327
+ |---|---|
328
+ | `when[]` incl. `!` exclusions | `mention: [...]`, judged in the understand call |
329
+ | `if` | trigger `if`, free; with `extract` set it also sees `input` after the call; `mention: []` + `if` = code-only detector, no call |
330
+ | `extract` | trigger `extract` → `run.input`, `{{input.x}}`, never `data` |
331
+ | `phase: 'pre'` + `halt` + `reply` | first step `say` (or a `do` returning `spoke: true`); another run's say silences the floor that turn |
332
+ | `phase: 'post'` | dies; `do`-only mention flows run beside the reply, same turn |
333
+ | `behavior` once/always/cooldown | `repeat` → claim, one ledger, interval cooldown |
334
+ | `priority` | dies; flow order |
335
+ | `stopOtherSignals` | dies; one speaker per turn |
336
+ | handler side effects | `do` steps |
337
+
338
+ ## 8. Concurrency and anchors
339
+
340
+ Session = conversation. Runs inside a session are concurrent; the floor is single: at most one run is `asking`; a talk step from another run pushes it onto a LIFO stack of `suspended` runs, and whenever nobody is asking the most recently suspended resumes. Order inside a turn: reply-branch resolutions → mention runs → floor talk; `messages[]` in that order.
341
+
342
+ Anchors are host keys per call; a flow names one or defaults to the session. Inside the session the framework dedupes; across a lead's sessions the host passes `claims.held` (nonce rule) and `claims.active` (live `${flowId}:${anchor}` pairs from its mirror) and writes both indexes in the save transaction. A lead-less campaign thread falls back to the session anchor.
343
+
344
+ S10 with versions. R1 (triagem) `asking`; R2 (retomar) `waiting` on W1. W1 fires: load v7 → premise holds → `p1` speaks (1 call) → `w1` parks, `schedule(W2)` → save v7→v8. The lead's text (id m9, at 10:00:02) lands concurrently: load v7 → save fails → replay on v8 → `w1.else` ends R2 (W2 fires stale later) → R1, still the floor, extracts and asks (2 calls) → save v8→v9. Wire: nudge, question. Had the text arrived first (or been debounced, per (a)), the wake finds `lastUserAt > setAt` → `w1.else` → no nudge.
345
+
346
+ ## 9. Flow end
347
+
348
+ A run ends when `then` reaches `'end'` or the last step completes; `onEnd`: `'end'` (default; `ended[]` carries it, session idle), `'stay'` (*repete o último passo*), `'reset'` (first step, data kept). *Vai para outro fluxo* is `then: { flow }` on the last step. The framework never speaks at the boundary. A `message` flow is `repeat: 'once'` per session by default; S8 sets `'always'` + `clearOnStart`.
349
+
350
+ ## 10. Persistence
351
+
352
+ ```ts
353
+ interface Session<D> {
354
+ id: string; v: 4; version: number;
355
+ data: Partial<D>; runs: Run[]; // live runs only
356
+ claims: Record<string, { at: string }>; // once/cooldown: one key per flow; always: last 50
357
+ inputs: string[]; // last 50 keyed input ids
358
+ lastUserAt?: string; lastAssistantAt?: string;
359
+ history?: History; // playground only
360
+ metadata: Record<string, unknown>;
361
+ }
362
+ ```
363
+
364
+ `migrateSession(blob, { flowIdOf })` runs at each app's choke point (`deserializeFalaiSessionState`): no `v` → `data` verbatim; `currentFlow/currentStep` → one run `{ id: '${flowId}#legacy', stepId, status: 'asking', trigger: { kind: 'message', key: 'legacy' }, visits: {} }`; `signals.triggers[key]` and `flowHistory[].completed` → `claims['${flowIdOf(key)}:${sessionId}:']`; `pendingDirective` dropped. `migrateFlows` gives signal-triggered flows `id = trigger.signal` so `flowIdOf` is identity for ilojista's 315 live once-signals — a real ilojista blob is a test fixture. Talk-step ids survive, so 844 + 315 cursors keep position; split-out steps get `${stepId}:media` / `${stepId}:integration`.
365
+
366
+ ## 11. Consumer migration
367
+
368
+ **One stored object.** Table `flows` (replaces `workspace.agent.flows` and `crmAutomationRules`): `id, workspaceId, name, kind ('user' | 'system'), enabled, position, section (derived), stageId?, spec JSONB`. `migrateFlows.ts`: AgentFlow → `on: [{ message: when, if: { channel } }]` from `contexts`; steps → `prompt`+`collect` (media → preceding `say` with `once`, integrations → following `do`, branches → `branches[]` on the step); `onEnd: endBehavior ?? 'stay'` (special flows `'end'`), `redirect` → `then: { flow }`; `{{lead_nome}}`-style variables rewritten to `{{context.lead.name}}` from a table; canvas positions → `ui`. CrmAutomation → `on` per §2, steps 1:1, `conditions` → trigger `if`, `haltReply` → first `say`. `defaultFlows(workspace)` (interesse, pediu_humano, known_contact → Triagem, meeting_booked → rodízio, agenda, bot_detected, conversation_ended) seeds `kind: 'system'` rows where `seedDefaultAutomationRules` runs today. The worker builds `f.agent({ flows: rows.map(r => r.spec) })` once per workspace config version.
369
+
370
+ **One editor.** *Fluxos*: *Título*, *Descrição*, *Quando ativar* (*o cliente pede isso* · *o cliente fala disso* · *acontece algo* · *silêncio do cliente* · *início manual*), *Só se*, *Vale por: conversa / lead*, *Repetição*, *Continuar só enquanto*, *Campos* with *Como perguntar*, *Ao recomeçar, esquecer*, *Ao terminar*, *Regras só deste fluxo*. Canvas *+ Adicionar passo*: *Mensagem & IA* (Enviar mensagem = `say`, IA escreve a próxima mensagem = `prompt`, Perguntar = `collect` with *Insistir até N vezes* and *Ramificação (a IA decide)* edges, Enviar modelo aprovado = `do send_template`), *Esperar* (`wait`, edges *Sem resposta* / *Respondeu*), *CRM* (`do` …, Condição = `if`, edges *Sim* / *Não*), *Equipe & integrações* (`do` …, Iniciar outro fluxo = `then: { flow }` edge). *Execuções* reads `flowRuns`: row upserted from `started[]`, status = last outcome (`Parcial` when a failed/skipped sits beside an ok), `aguardando até` from `until`, *Seguiu "Respondeu"* from `next`, trigger skips from `skipped[]`.
371
+
372
+ **One assistant tool.** `criar_fluxo({ descricao })` runs the host's generation call with `flowSpecSchema(agent)` as response schema, then `validateFlow`, then saves; `ajustar_fluxo`, `ver_fluxos`. `criar_automacao` and siblings die.
373
+
374
+ **Per app (prospectar paths; siblings identical):**
375
+
376
+ | Delete | Move | Rewrite |
377
+ |---|---|---|
378
+ | `apps/worker/src/services/crm-automation.service.ts` (1,548), `automation-message-composer.service.ts`, `conversation-idle-sweep.service.ts`, `integration.service.ts`, `utils/integration-behavior.utils.ts`, `utils/follow-up-time.utils.ts`; `apps/api/src/services/crm-automation.service.ts`, `assistant-tools/automation.tools.ts`, `backfill-scheduling-flow.ts`; `packages/server/.../scheduling-flow.utils.ts`, `signals.builder.ts`, `modules/crm/signal-rule.mapper.ts`; `contracts/.../automation.types.ts`; AgentFlow/Step/StepIntegrations/FlowEndBehavior in `agent-core.types.ts`; `apps/web/src/components/Automation/**` (4,127) | From `flow.utils.ts`: `buildStructuredKnowledgeBase` → `knowledge.utils.ts`, `buildFalaiInstructions` → `instructions.builder.ts`; the rest of the file dies. `schema.utils.ts` → 30-line `buildSchema`. `automationLabels.ts`/`automationRecipes.ts` → `flowLabels.ts`/`flowRecipes.ts`. DEFAULT_SIGNALS → `defaultFlows.ts` | `falai-agent.factory.ts` (one agent per config version, no per-turn rebuild, `buildNativeSignals`/`createStepMediaHook` gone); `ai.service.ts` (`detectFlowEndThisTurn`, `reconcilePersistedSignalState`, `pinCampaignEntryFlows` gone; `runTurn` from §12); `apps/web/src/components/Agent/**` (5,169) merged with the automation canvas into one editor (~9.3k lines of UI touched); api call sites `overview.tool.ts`, `campaign.service.ts` (followUp → `flowId`), `playground.service.ts` (`immediateAutomationActions`, `buildSignalAiEffects` → FlowSpec walkers), `agent-entity.service.ts`, `onboarding-activation.service.ts`, `automation-rule.repository.ts` → `flow.repository.ts`; `StartAutomationDialog`/`SidePanel` graph walkers ported to FlowSpec |
379
+
380
+ New host code (~1k lines): `actions.ts` (11 handlers), `wake.worker.ts`, event publishers, `migrateFlows.ts`, `defaultFlows.ts`, the transactional save. Stays host-side: the gate (→ `silenced`), channels, CRM writes inside actions, BullMQ, history building, the `flowRuns` mirror, the Instagram comment matcher.
381
+
382
+ **Framework side.** Tests: about 50 of 76 files exercise routing, directives, signals, completion, adapters or the `respond()` shape and are deleted or rewritten; ~25 kept (providers, envelope salvage, streaming decoder, tool loop, templates, history, schema, prompt-section-cache); new: `understand`, `runner`, `wakes` (fakeClock + MemoryScheduler), `events`, `session-v4` + legacy fixtures (prospectar and ilojista blobs), one file per scenario. `mockProvider({ understand, speak })` holds FIFO queues per schema name (last entry repeats) and records `.calls`. Docs rewritten in place; `docs/migration/v3-to-v4.md` with a Removed | Replacement table and the blob recipe.
383
+
384
+ ## 12. Scenarios
385
+
386
+ **S1 Triage.** "oi, quero saber como funciona" (id m1) → understand scores `triagem` (1 call) → `quem` asks (1 call). "João, da Acme, somos 30" → nome, empresa, tamanho land → `quem` complete, `porte` asks only urgência. "pra ontem, quanto custa?" → urgência lands; triagem is the floor and scored; `grana` answers price from the KB and asks orçamento. Two evasions → `maxAsks: 2` → `campo pulado`. `confirma` asks; "não, somos 50" → `confirmado: false`, tamanho updated → `ok` false → `clear: ['confirmado']`, `quem`/`porte`/`grana` skip, `confirma` re-asks. "sim" → `avisa` (key `…:avisa:1`) → `tchau` → `'end'`; "obrigado" gets the `idle` reply. 2 calls per turn.
387
+
388
+ **S2 Cadence.** Assistant speaks at T → `schedule(silence:retomar:s1:T)`. Wake: premise holds → run starts (claim now) → `gate` false → `p1` speaks first (1 call) → `w1` parks. Any reply on either channel → `w1.else` → end (the anchor's `lastInboundAt` covers the other channel at the next wake). Timeout → `p2` → `w2` → `n1` (0 calls). Human owns → `gate` true → `lembra` notifies the seller, run ends.
389
+
390
+ **S3 No-show.** `turn({ event: 'stage_entered', payload: { stageId }, key: 'stage:456:T' })` → `if: { inStage: 'nao-compareceu' }, after: '1h'` parks; a re-entry replaces it. Wake → `while` holds → `prompt` + `collect: ['data_preferida', 'horario_preferido']` with agenda tools speaks first (the lead having written at T+20m does not stop it: the premise is the stage), suspends triage, collects → `do book` → host emits `meeting_booked` → *Reunião marcada* notifies. Triage resumes when no-show stops asking.
391
+
392
+ **S4 Pediu humano.** `on: [{ mention: ['quer falar com uma pessoa'], repeat: { cooldown: '1h' } }]`, steps `say 'Já chamo alguém'` → `do assign_lead` → end. The say silences triage's talk that turn; triage stays `asking`. While a human owns, `silenced` turns cost 0 calls. Handback needs no event: the next message finds triage at the same step. A second request an hour later starts a fresh run.
393
+
394
+ **S5 Campaign.** `turn({ start: { flow: 'campanha', input: { campaignId, templateId, flowId }, key } })` → `do send_template` (`spoke: true`, `defer` on no credits) → `wait '1d', else: { flow: '{{input.flowId}}' }` → timeout → `prompt` nudge → `wait '2d', else: { flow: '{{input.flowId}}' }` → end. A reply enters the campaign's own funnel, which holds the floor: routing that turn is skipped. 0 calls until the nudge.
395
+
396
+ **S6 Instagram.** `ig_comment` on the IG session → `say` DM → `wait '1d', else: 'qualifica'` → `prompt` → `then: { flow: 'agendar' }`. `anchor: 'lead'` + `claims.active` stop a second run on WhatsApp.
397
+
398
+ **S7 Rule from chat.** Same FlowSpec, same table, same card; fires inside the understand call, no talk step, runs beside the reply, once per conversation.
399
+
400
+ ```json
401
+ { "id": "concorrente", "name": "Lead falou de concorrente",
402
+ "on": [{ "mention": ["o lead cita ou compara com um concorrente"], "extract": { "trecho": { "type": "string" } }, "repeat": "once" }],
403
+ "steps": [
404
+ { "id": "tag", "kind": "do", "do": "add_tags", "with": { "tags": ["concorrente"] } },
405
+ { "id": "avisa", "kind": "do", "do": "notify", "with": { "recipient": "owner", "message": "{{data.nome}} falou de concorrente: \"{{input.trecho}}\"" } }
406
+ ] }
407
+ ```
408
+
409
+ ```ts
410
+ const criarFluxo = tool({
411
+ id: 'criar_fluxo', description: 'Cria um fluxo a partir do que o dono pediu no chat',
412
+ parameters: { descricao: { type: 'string' } },
413
+ handler: async ({ descricao }, ctx) => {
414
+ const spec = await generateFlowSpec(descricao, flowSpecSchema(agent)); // host generation call, closed schema
415
+ validateFlow(spec, agent); await db.flows.insert(ctx.context.workspaceId, spec);
416
+ return { value: { criado: spec.id } };
417
+ },
418
+ });
419
+ ```
420
+
421
+ **S8 Scheduling.** `on: [{ message: ['quer marcar', 'quer remarcar'], repeat: 'always' }]`, `clearOnStart: ['data_preferida', 'horario_preferido', 'agenda_event_id', 'confirmado']` (applied before "sexta às 15h" lands), collect with agenda tools, `{ collect: ['confirmado'], ask: { confirmado: 'Confirme a data e o horário em uma frase.' } }`, `do book`, last step `then: { flow: 'pos-agendamento' }`.
422
+
423
+ **S9 Config assistant.** Zero flows, zero fields → understand skipped → one speak call with tools. Interview mode: one `message` flow → single-flow shortcut, no scoring; collect steps skip when known.
424
+
425
+ **S10.** §8.
426
+
427
+ **S11 Playground.** `agent.turn({ sessionId, session, message })` without `id`, `MemoryStore`, `schedule` fed to `MemoryScheduler.due(now)` or ignored; actions return `{ ok: true }`; mention flows run as no-ops and show in `started`.
428
+
429
+ **S12 AI speaks first, from a timer, mid-flow.** A `prompt` reached by a wake runs through the same agent, prompt, tools, instructions, `session.data` and the `history` the host passes; the gate arrives as `silenced`.
430
+
431
+ ```ts
432
+ const propostaParada = f.flow({
433
+ id: 'proposta-parada', name: 'Proposta parada',
434
+ on: [{ event: 'stage_entered', if: { inStage: 'proposta-enviada' }, after: '3d', businessHours: true }],
435
+ steps: [
436
+ { id: 'p1', prompt: 'A proposta foi enviada há três dias sem retorno. Pergunte, em uma frase, se ficou alguma dúvida — use o que já sabe sobre {{data.empresa}}.' },
437
+ { id: 'w1', wait: '2d', else: 'end' },
438
+ { id: 'n1', do: 'notify', with: { recipient: 'leadAssignee', message: 'Proposta de {{data.nome}} sem resposta há 5 dias.' } },
439
+ ],
440
+ });
441
+
442
+ wakeQueue.process(async (job: { data: { sessionId: string; key: string } }) => {
443
+ const { sessionId, key } = job.data;
444
+ await runTurn(sessionId, { wake: key });
445
+ });
446
+
447
+ async function runTurn(sessionId: string, input: TurnInputBody) {
448
+ for (let attempt = 0; attempt < 3; attempt++) {
449
+ const [session, context, history] = await Promise.all([store.load(sessionId), loadLeadContext(sessionId), loadHistory(sessionId)]);
450
+ const anchors = { lead: { key: `lead:${context.lead.id}`, lastInboundAt: context.lead.lastInboundAt } };
451
+ const r = await agent.turn({ ...input, sessionId, session, context, history, anchors,
452
+ claims: await ledger.claims(anchors), silenced: await gate.reason(sessionId) });
453
+ if (!r.changed) return;
454
+ try {
455
+ await db.transaction(async (tx) => {
456
+ await store.save(r.session, session?.version ?? 0); // CAS, 0 = insert
457
+ await ledger.write(tx, r.started, r.ended, r.outcomes, r.skipped); // unique(dedupeKey), partial unique(flowId, anchor) WHERE live, Execuções
458
+ await outbox.put(tx, r.messages, r.schedule); // wakes ride in the same transaction
459
+ });
460
+ } catch (e) { if (e instanceof SessionConflictError || isUniqueViolation(e)) continue; throw e; }
461
+ await outbox.drain(sessionId); // send honoring afterMs, enqueue wakes jobId = key
462
+ return;
463
+ }
464
+ }
465
+ ```
466
+
467
+ **S13 TRID script.** `[{ id: 'a', say: 'Oi! Aqui é da TRID.' }, { id: 'p', wait: '3s' }, { id: 'b', say: 'Chegou o iPhone 17, pronta entrega.' }, { id: 'q', prompt: 'Pergunte qual modelo interessa.', collect: ['modelo'] }]` → one turn, `messages: [A, B afterMs 3000, C]`; own says never silence own talk. 2 calls (understand + speak).
468
+
469
+ ## 13. Risks
470
+
471
+ 1. The understand call bundles routing, mentions, branches and extraction; a weak model may score routing worse than today's dedicated call. Run a scenario eval on GLM and Gemini before cutover.
472
+ 2. The speak envelope with pending fields must be probed per provider (Gemini strict, Zai prompt-only); `toWireSchema()` asserts `isStrictSchema` in tests.
473
+ 3. A wake-started talk step suspending a live conversation is new UX; the silence premise and `while` are the guards.
474
+ 4. Dropping five adapters affects npm users beyond the three consumers.
475
+ 5. Three apps migrate blobs, stored flows and ~9k lines of editor UI in lockstep with the worker rewrite; the shared FlowSpec is the chance to share one package.
476
+ 6. Instruction precedence ("cara ou coroa") is untouched.
477
+ 7. `do` is at-least-once before the save; a host handler that ignores `ctx.key` can double-fire on a CAS replay.