enya-agent 0.1.0

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 (389) hide show
  1. package/.env.example +20 -0
  2. package/.github/workflows/ci.yml +70 -0
  3. package/.github/workflows/publish.yml +250 -0
  4. package/.gitmodules +3 -0
  5. package/Cargo.lock +3584 -0
  6. package/Cargo.toml +97 -0
  7. package/crates/enact/Cargo.toml +27 -0
  8. package/crates/enact/src/lib.rs +60 -0
  9. package/crates/enact-a2a/Cargo.toml +25 -0
  10. package/crates/enact-a2a/src/lib.rs +411 -0
  11. package/crates/enact-channels/Cargo.toml +64 -0
  12. package/crates/enact-channels/examples/README.md +80 -0
  13. package/crates/enact-channels/examples/channel_bot.rs +169 -0
  14. package/crates/enact-channels/examples/telegram-echo.rs +34 -0
  15. package/crates/enact-channels/examples/whatsapp-echo.rs +142 -0
  16. package/crates/enact-channels/src/config.rs +213 -0
  17. package/crates/enact-channels/src/lib.rs +25 -0
  18. package/crates/enact-channels/src/runtime.rs +237 -0
  19. package/crates/enact-channels/src/security/mod.rs +5 -0
  20. package/crates/enact-channels/src/security/pairing.rs +205 -0
  21. package/crates/enact-channels/src/teams.rs +601 -0
  22. package/crates/enact-channels/src/telegram.rs +2833 -0
  23. package/crates/enact-channels/src/traits.rs +200 -0
  24. package/crates/enact-channels/src/webhook.rs +262 -0
  25. package/crates/enact-channels/src/whatsapp.rs +310 -0
  26. package/crates/enact-cli/Cargo.toml +40 -0
  27. package/crates/enact-cli/src/commands/doctor.rs +62 -0
  28. package/crates/enact-cli/src/commands/mod.rs +3 -0
  29. package/crates/enact-cli/src/commands/run.rs +69 -0
  30. package/crates/enact-cli/src/commands/serve.rs +81 -0
  31. package/crates/enact-cli/src/config.rs +2 -0
  32. package/crates/enact-cli/src/main.rs +79 -0
  33. package/crates/enact-config/Cargo.toml +36 -0
  34. package/crates/enact-config/ENV_VAR_MAPPING.md +135 -0
  35. package/crates/enact-config/QUICK_REFERENCE.md +92 -0
  36. package/crates/enact-config/README.md +107 -0
  37. package/crates/enact-config/TESTING.md +161 -0
  38. package/crates/enact-config/examples/test-env-vars.rs +100 -0
  39. package/crates/enact-config/src/config.rs +399 -0
  40. package/crates/enact-config/src/encrypted_store.rs +211 -0
  41. package/crates/enact-config/src/lib.rs +298 -0
  42. package/crates/enact-config/src/secrets.rs +149 -0
  43. package/crates/enact-config/src/sync.rs +260 -0
  44. package/crates/enact-config/test-env-vars.sh +34 -0
  45. package/crates/enact-config/tests/README.md +99 -0
  46. package/crates/enact-config/tests/config_integration_test.rs +202 -0
  47. package/crates/enact-config/tests/security_test.rs +140 -0
  48. package/crates/enact-context/Cargo.toml +41 -0
  49. package/crates/enact-context/src/budget.rs +314 -0
  50. package/crates/enact-context/src/calibrator.rs +535 -0
  51. package/crates/enact-context/src/compactor.rs +392 -0
  52. package/crates/enact-context/src/condenser.rs +826 -0
  53. package/crates/enact-context/src/lib.rs +94 -0
  54. package/crates/enact-context/src/segment.rs +238 -0
  55. package/crates/enact-context/src/step_context.rs +645 -0
  56. package/crates/enact-context/src/token_counter.rs +148 -0
  57. package/crates/enact-context/src/window.rs +372 -0
  58. package/crates/enact-core/Cargo.toml +42 -0
  59. package/crates/enact-core/README.md +98 -0
  60. package/crates/enact-core/src/background/executor.rs +524 -0
  61. package/crates/enact-core/src/background/mod.rs +48 -0
  62. package/crates/enact-core/src/background/target_binding.rs +390 -0
  63. package/crates/enact-core/src/background/trigger.rs +511 -0
  64. package/crates/enact-core/src/callable/callable.rs +152 -0
  65. package/crates/enact-core/src/callable/composite.rs +817 -0
  66. package/crates/enact-core/src/callable/graph.rs +104 -0
  67. package/crates/enact-core/src/callable/llm.rs +211 -0
  68. package/crates/enact-core/src/callable/mod.rs +64 -0
  69. package/crates/enact-core/src/callable/registry.rs +206 -0
  70. package/crates/enact-core/src/context/execution_context.rs +757 -0
  71. package/crates/enact-core/src/context/invocation.rs +99 -0
  72. package/crates/enact-core/src/context/mod.rs +50 -0
  73. package/crates/enact-core/src/context/tenant.rs +175 -0
  74. package/crates/enact-core/src/context/trace.rs +127 -0
  75. package/crates/enact-core/src/flow/conditional.rs +293 -0
  76. package/crates/enact-core/src/flow/mod.rs +43 -0
  77. package/crates/enact-core/src/flow/parallel.rs +437 -0
  78. package/crates/enact-core/src/flow/repeat.rs +534 -0
  79. package/crates/enact-core/src/flow/sequential.rs +248 -0
  80. package/crates/enact-core/src/graph/checkpoint.rs +79 -0
  81. package/crates/enact-core/src/graph/checkpoint_store.rs +76 -0
  82. package/crates/enact-core/src/graph/compiled.rs +189 -0
  83. package/crates/enact-core/src/graph/edge.rs +59 -0
  84. package/crates/enact-core/src/graph/graph_schema.rs +218 -0
  85. package/crates/enact-core/src/graph/loader.rs +155 -0
  86. package/crates/enact-core/src/graph/mod.rs +18 -0
  87. package/crates/enact-core/src/graph/node/function.rs +49 -0
  88. package/crates/enact-core/src/graph/node/mod.rs +48 -0
  89. package/crates/enact-core/src/graph/schema.rs +62 -0
  90. package/crates/enact-core/src/inbox/message.rs +405 -0
  91. package/crates/enact-core/src/inbox/mod.rs +31 -0
  92. package/crates/enact-core/src/inbox/store.rs +355 -0
  93. package/crates/enact-core/src/kernel/artifact/filesystem.rs +546 -0
  94. package/crates/enact-core/src/kernel/artifact/metadata.rs +283 -0
  95. package/crates/enact-core/src/kernel/artifact/mod.rs +27 -0
  96. package/crates/enact-core/src/kernel/artifact/store.rs +427 -0
  97. package/crates/enact-core/src/kernel/enforcement.rs +1315 -0
  98. package/crates/enact-core/src/kernel/error.rs +1200 -0
  99. package/crates/enact-core/src/kernel/event.rs +1394 -0
  100. package/crates/enact-core/src/kernel/execution_model.rs +831 -0
  101. package/crates/enact-core/src/kernel/execution_state.rs +189 -0
  102. package/crates/enact-core/src/kernel/execution_strategy.rs +117 -0
  103. package/crates/enact-core/src/kernel/ids.rs +2086 -0
  104. package/crates/enact-core/src/kernel/interrupt.rs +125 -0
  105. package/crates/enact-core/src/kernel/kernel.rs +1283 -0
  106. package/crates/enact-core/src/kernel/mod.rs +205 -0
  107. package/crates/enact-core/src/kernel/persistence/event_store.rs +270 -0
  108. package/crates/enact-core/src/kernel/persistence/message_store.rs +908 -0
  109. package/crates/enact-core/src/kernel/persistence/mod.rs +102 -0
  110. package/crates/enact-core/src/kernel/persistence/state_store.rs +228 -0
  111. package/crates/enact-core/src/kernel/persistence/vector_store.rs +299 -0
  112. package/crates/enact-core/src/kernel/reducer.rs +808 -0
  113. package/crates/enact-core/src/kernel/replay.rs +153 -0
  114. package/crates/enact-core/src/lib.rs +413 -0
  115. package/crates/enact-core/src/memory/episodic.rs +0 -0
  116. package/crates/enact-core/src/memory/mod.rs +6 -0
  117. package/crates/enact-core/src/memory/semantic.rs +0 -0
  118. package/crates/enact-core/src/memory/trait.rs +0 -0
  119. package/crates/enact-core/src/memory/vector_db.rs +0 -0
  120. package/crates/enact-core/src/memory/working.rs +0 -0
  121. package/crates/enact-core/src/policy/execution_policy.rs +292 -0
  122. package/crates/enact-core/src/policy/filters.rs +458 -0
  123. package/crates/enact-core/src/policy/input_processor.rs +407 -0
  124. package/crates/enact-core/src/policy/long_running.rs +134 -0
  125. package/crates/enact-core/src/policy/mod.rs +193 -0
  126. package/crates/enact-core/src/policy/pii_input.rs +274 -0
  127. package/crates/enact-core/src/policy/tenant_policy.rs +453 -0
  128. package/crates/enact-core/src/policy/tool_policy.rs +407 -0
  129. package/crates/enact-core/src/providers/mod.rs +63 -0
  130. package/crates/enact-core/src/providers/trait.rs +292 -0
  131. package/crates/enact-core/src/runner/callbacks.rs +6 -0
  132. package/crates/enact-core/src/runner/execution_runner.rs +476 -0
  133. package/crates/enact-core/src/runner/loop.rs +117 -0
  134. package/crates/enact-core/src/runner/mod.rs +58 -0
  135. package/crates/enact-core/src/runner/protected_runner.rs +280 -0
  136. package/crates/enact-core/src/signal/inmemory.rs +231 -0
  137. package/crates/enact-core/src/signal/mod.rs +108 -0
  138. package/crates/enact-core/src/streaming/event_logger.rs +195 -0
  139. package/crates/enact-core/src/streaming/event_stream.rs +1423 -0
  140. package/crates/enact-core/src/streaming/mod.rs +108 -0
  141. package/crates/enact-core/src/streaming/pause_cancel.rs +0 -0
  142. package/crates/enact-core/src/streaming/protected_emitter.rs +173 -0
  143. package/crates/enact-core/src/streaming/protection/context.rs +136 -0
  144. package/crates/enact-core/src/streaming/protection/encryption.rs +289 -0
  145. package/crates/enact-core/src/streaming/protection/mod.rs +43 -0
  146. package/crates/enact-core/src/streaming/protection/pii_protection.rs +243 -0
  147. package/crates/enact-core/src/streaming/protection/processor.rs +166 -0
  148. package/crates/enact-core/src/streaming/sse.rs +0 -0
  149. package/crates/enact-core/src/telemetry/exporter.rs +0 -0
  150. package/crates/enact-core/src/telemetry/init.rs +0 -0
  151. package/crates/enact-core/src/telemetry/mod.rs +49 -0
  152. package/crates/enact-core/src/telemetry/spans.rs +245 -0
  153. package/crates/enact-core/src/tool/agent_tool.rs +177 -0
  154. package/crates/enact-core/src/tool/browser/mod.rs +0 -0
  155. package/crates/enact-core/src/tool/browser/webdriver.rs +0 -0
  156. package/crates/enact-core/src/tool/cost.rs +247 -0
  157. package/crates/enact-core/src/tool/discovery.rs +0 -0
  158. package/crates/enact-core/src/tool/dispatcher.rs +347 -0
  159. package/crates/enact-core/src/tool/filesystem.rs +231 -0
  160. package/crates/enact-core/src/tool/function.rs +99 -0
  161. package/crates/enact-core/src/tool/git.rs +162 -0
  162. package/crates/enact-core/src/tool/http.rs +214 -0
  163. package/crates/enact-core/src/tool/mcp/client.rs +0 -0
  164. package/crates/enact-core/src/tool/mcp/mod.rs +0 -0
  165. package/crates/enact-core/src/tool/mod.rs +51 -0
  166. package/crates/enact-core/src/tool/reasoning/debugging.rs +0 -0
  167. package/crates/enact-core/src/tool/reasoning/mcts.rs +0 -0
  168. package/crates/enact-core/src/tool/reasoning/mod.rs +0 -0
  169. package/crates/enact-core/src/tool/reasoning/sequential.rs +0 -0
  170. package/crates/enact-core/src/tool/sandbox/dagger.rs +0 -0
  171. package/crates/enact-core/src/tool/sandbox/mod.rs +0 -0
  172. package/crates/enact-core/src/tool/shell.rs +147 -0
  173. package/crates/enact-core/src/tool/trait.rs +33 -0
  174. package/crates/enact-core/src/tool/web_search.rs +277 -0
  175. package/crates/enact-core/src/util/config.rs +0 -0
  176. package/crates/enact-core/src/util/errors.rs +0 -0
  177. package/crates/enact-core/src/util/mod.rs +6 -0
  178. package/crates/enact-core/tests/airgapped_e2e_test.rs +291 -0
  179. package/crates/enact-core/tests/e2e_agentic_loop.rs +119 -0
  180. package/crates/enact-core/tests/e2e_test.rs +259 -0
  181. package/crates/enact-core/tests/graph_test.rs +130 -0
  182. package/crates/enact-core/tests/stream_event_id_validation.rs +435 -0
  183. package/crates/enact-cron/Cargo.toml +28 -0
  184. package/crates/enact-cron/src/lib.rs +44 -0
  185. package/crates/enact-cron/src/schedule.rs +156 -0
  186. package/crates/enact-cron/src/store.rs +589 -0
  187. package/crates/enact-cron/src/types.rs +148 -0
  188. package/crates/enact-gateway/Cargo.toml +31 -0
  189. package/crates/enact-gateway/README.md +30 -0
  190. package/crates/enact-gateway/examples/whatsapp-gateway-runner-mock.rs +59 -0
  191. package/crates/enact-gateway/examples/whatsapp-gateway.rs +42 -0
  192. package/crates/enact-gateway/src/lib.rs +582 -0
  193. package/crates/enact-mcp/Cargo.toml +24 -0
  194. package/crates/enact-mcp/src/lib.rs +178 -0
  195. package/crates/enact-memory/Cargo.toml +25 -0
  196. package/crates/enact-memory/src/backend.rs +20 -0
  197. package/crates/enact-memory/src/chunker.rs +230 -0
  198. package/crates/enact-memory/src/embeddings.rs +221 -0
  199. package/crates/enact-memory/src/lib.rs +67 -0
  200. package/crates/enact-memory/src/markdown.rs +127 -0
  201. package/crates/enact-memory/src/none.rs +61 -0
  202. package/crates/enact-memory/src/sqlite.rs +276 -0
  203. package/crates/enact-memory/src/traits.rs +65 -0
  204. package/crates/enact-memory/src/vector.rs +198 -0
  205. package/crates/enact-oauth/Cargo.toml +27 -0
  206. package/crates/enact-oauth/src/lib.rs +584 -0
  207. package/crates/enact-observability/Cargo.toml +22 -0
  208. package/crates/enact-observability/src/lib.rs +197 -0
  209. package/crates/enact-providers/Cargo.toml +33 -0
  210. package/crates/enact-providers/examples/hello-agent.rs +33 -0
  211. package/crates/enact-providers/src/anthropic.rs +182 -0
  212. package/crates/enact-providers/src/azure.rs +96 -0
  213. package/crates/enact-providers/src/bridge.rs +221 -0
  214. package/crates/enact-providers/src/gemini.rs +227 -0
  215. package/crates/enact-providers/src/http.rs +78 -0
  216. package/crates/enact-providers/src/lib.rs +53 -0
  217. package/crates/enact-providers/src/openai_compatible.rs +167 -0
  218. package/crates/enact-providers/src/openrouter.rs +33 -0
  219. package/crates/enact-runner/Cargo.toml +24 -0
  220. package/crates/enact-runner/README.md +76 -0
  221. package/crates/enact-runner/src/compaction.rs +225 -0
  222. package/crates/enact-runner/src/config.rs +118 -0
  223. package/crates/enact-runner/src/lib.rs +63 -0
  224. package/crates/enact-runner/src/loop_driver.rs +414 -0
  225. package/crates/enact-runner/src/parser.rs +421 -0
  226. package/crates/enact-runner/src/retry.rs +262 -0
  227. package/crates/enact-runner/tests/integration.rs +278 -0
  228. package/crates/enact-security/Cargo.toml +22 -0
  229. package/crates/enact-security/src/audit.rs +375 -0
  230. package/crates/enact-security/src/lib.rs +37 -0
  231. package/crates/enact-security/src/policy.rs +406 -0
  232. package/crates/enact-skills/Cargo.toml +25 -0
  233. package/crates/enact-skills/src/lib.rs +506 -0
  234. package/crates/enact-tools/Cargo.toml +22 -0
  235. package/crates/enact-tools/src/file_read.rs +166 -0
  236. package/crates/enact-tools/src/file_write.rs +216 -0
  237. package/crates/enact-tools/src/git_operations.rs +513 -0
  238. package/crates/enact-tools/src/http_request.rs +417 -0
  239. package/crates/enact-tools/src/lib.rs +104 -0
  240. package/crates/enact-tools/src/security.rs +227 -0
  241. package/crates/enact-tools/src/shell.rs +191 -0
  242. package/crates/enact-tools/src/traits.rs +159 -0
  243. package/docs/Makefile +74 -0
  244. package/docs/config.toml +62 -0
  245. package/docs/content/_index.md +174 -0
  246. package/docs/content/a2a/_index.md +431 -0
  247. package/docs/content/api/_index.md +323 -0
  248. package/docs/content/channels/_index.md +160 -0
  249. package/docs/content/channels/teams.md +205 -0
  250. package/docs/content/channels/telegram.md +182 -0
  251. package/docs/content/channels/webhook.md +423 -0
  252. package/docs/content/channels/whatsapp.md +240 -0
  253. package/docs/content/cli/_index.md +261 -0
  254. package/docs/content/concepts/_index.md +273 -0
  255. package/docs/content/configuration/_index.md +241 -0
  256. package/docs/content/cron/_index.md +248 -0
  257. package/docs/content/developers/_index.md +278 -0
  258. package/docs/content/getting-started/_index.md +180 -0
  259. package/docs/content/installation/_index.md +186 -0
  260. package/docs/content/installation/uninstall.md +101 -0
  261. package/docs/content/installation/updating.md +120 -0
  262. package/docs/content/mcp/_index.md +215 -0
  263. package/docs/content/memory/_index.md +163 -0
  264. package/docs/content/oauth/_index.md +515 -0
  265. package/docs/content/providers/_index.md +206 -0
  266. package/docs/content/roadmap/_index.md +199 -0
  267. package/docs/content/security/_index.md +219 -0
  268. package/docs/content/skills/_index.md +228 -0
  269. package/docs/content/tools/_index.md +485 -0
  270. package/docs/content/troubleshooting/_index.md +259 -0
  271. package/docs/content/yaml-schema/_index.md +294 -0
  272. package/docs/static/giallo-dark.css +91 -0
  273. package/docs/static/giallo-light.css +91 -0
  274. package/docs/themes/tanuki/.github/workflows/deploy.yml +44 -0
  275. package/docs/themes/tanuki/LICENSE +21 -0
  276. package/docs/themes/tanuki/README.md +166 -0
  277. package/docs/themes/tanuki/examples/blog/config.toml +58 -0
  278. package/docs/themes/tanuki/examples/blog/content/_index.md +4 -0
  279. package/docs/themes/tanuki/examples/blog/content/about.md +33 -0
  280. package/docs/themes/tanuki/examples/blog/content/blog/_index.md +7 -0
  281. package/docs/themes/tanuki/examples/blog/content/blog/api-design-best-practices.md +245 -0
  282. package/docs/themes/tanuki/examples/blog/content/blog/building-accessible-websites.md +147 -0
  283. package/docs/themes/tanuki/examples/blog/content/blog/css-grid-vs-flexbox.md +165 -0
  284. package/docs/themes/tanuki/examples/blog/content/blog/customizing-catppuccin-colors.md +137 -0
  285. package/docs/themes/tanuki/examples/blog/content/blog/dark-mode-best-practices.md +82 -0
  286. package/docs/themes/tanuki/examples/blog/content/blog/docker-essentials.md +301 -0
  287. package/docs/themes/tanuki/examples/blog/content/blog/getting-started-with-zola.md +129 -0
  288. package/docs/themes/tanuki/examples/blog/content/blog/git-workflow-for-content.md +112 -0
  289. package/docs/themes/tanuki/examples/blog/content/blog/introduction-to-webassembly.md +183 -0
  290. package/docs/themes/tanuki/examples/blog/content/blog/modern-javascript-features.md +234 -0
  291. package/docs/themes/tanuki/examples/blog/content/blog/testing-strategies.md +311 -0
  292. package/docs/themes/tanuki/examples/blog/content/blog/typography-for-developers.md +104 -0
  293. package/docs/themes/tanuki/examples/blog/content/blog/welcome-to-tanuki.md +67 -0
  294. package/docs/themes/tanuki/examples/blog/content/blog/why-static-sites.md +85 -0
  295. package/docs/themes/tanuki/examples/blog/content/projects.md +64 -0
  296. package/docs/themes/tanuki/examples/book/config.toml +17 -0
  297. package/docs/themes/tanuki/examples/book/content/_index.md +12 -0
  298. package/docs/themes/tanuki/examples/book/content/chapter-1.md +90 -0
  299. package/docs/themes/tanuki/examples/book/content/chapter-2.md +143 -0
  300. package/docs/themes/tanuki/examples/book/content/chapter-3.md +217 -0
  301. package/docs/themes/tanuki/examples/book/content/chapter-4.md +224 -0
  302. package/docs/themes/tanuki/examples/book/content/chapter-5.md +297 -0
  303. package/docs/themes/tanuki/examples/book/content/print.md +6 -0
  304. package/docs/themes/tanuki/examples/docs/config.toml +28 -0
  305. package/docs/themes/tanuki/examples/docs/content/_index.md +20 -0
  306. package/docs/themes/tanuki/examples/docs/content/components.md +156 -0
  307. package/docs/themes/tanuki/examples/docs/content/configuration.md +94 -0
  308. package/docs/themes/tanuki/examples/docs/content/customization.md +202 -0
  309. package/docs/themes/tanuki/examples/docs/content/deployment.md +204 -0
  310. package/docs/themes/tanuki/examples/docs/content/installation.md +59 -0
  311. package/docs/themes/tanuki/examples/docs/content/print.md +6 -0
  312. package/docs/themes/tanuki/examples/docs/static/img/tanuki-icon.avif +0 -0
  313. package/docs/themes/tanuki/examples/index.html +2104 -0
  314. package/docs/themes/tanuki/mise.toml +108 -0
  315. package/docs/themes/tanuki/sass/base/_catppuccin.scss +164 -0
  316. package/docs/themes/tanuki/sass/base/_fonts.scss +64 -0
  317. package/docs/themes/tanuki/sass/base/_reset.scss +152 -0
  318. package/docs/themes/tanuki/sass/base/_typography.scss +523 -0
  319. package/docs/themes/tanuki/sass/components/_buttons.scss +209 -0
  320. package/docs/themes/tanuki/sass/components/_code.scss +457 -0
  321. package/docs/themes/tanuki/sass/components/_landing.scss +633 -0
  322. package/docs/themes/tanuki/sass/components/_layout.scss +294 -0
  323. package/docs/themes/tanuki/sass/components/_navigation.scss +1200 -0
  324. package/docs/themes/tanuki/sass/components/_print.scss +237 -0
  325. package/docs/themes/tanuki/sass/components/_search.scss +224 -0
  326. package/docs/themes/tanuki/sass/components/_sidebar.scss +473 -0
  327. package/docs/themes/tanuki/sass/components/_theme-toggle.scss +186 -0
  328. package/docs/themes/tanuki/sass/modes/_blog.scss +366 -0
  329. package/docs/themes/tanuki/sass/modes/_product.scss +875 -0
  330. package/docs/themes/tanuki/sass/modes/_raskell.scss +1696 -0
  331. package/docs/themes/tanuki/sass/patterns/_buttons.scss +183 -0
  332. package/docs/themes/tanuki/sass/patterns/_cards.scss +144 -0
  333. package/docs/themes/tanuki/sass/patterns/_index.scss +9 -0
  334. package/docs/themes/tanuki/sass/patterns/_lists.scss +259 -0
  335. package/docs/themes/tanuki/sass/patterns/_sections.scss +243 -0
  336. package/docs/themes/tanuki/sass/style.scss +47 -0
  337. package/docs/themes/tanuki/sass/tokens/_colors.scss +139 -0
  338. package/docs/themes/tanuki/sass/tokens/_spacing.scss +100 -0
  339. package/docs/themes/tanuki/sass/tokens/_typography.scss +186 -0
  340. package/docs/themes/tanuki/screenshot.png +0 -0
  341. package/docs/themes/tanuki/sentinel.kdl +59 -0
  342. package/docs/themes/tanuki/static/elasticlunr.min.js +10 -0
  343. package/docs/themes/tanuki/static/fonts/GEIST-LICENSE.txt +92 -0
  344. package/docs/themes/tanuki/static/fonts/Geist-Variable.woff2 +0 -0
  345. package/docs/themes/tanuki/static/fonts/GeistMono-Variable.woff2 +0 -0
  346. package/docs/themes/tanuki/static/img/tanuki-icon.avif +0 -0
  347. package/docs/themes/tanuki/static/img/tanuki-icon.png +0 -0
  348. package/docs/themes/tanuki/static/js/anchors.js +18 -0
  349. package/docs/themes/tanuki/static/js/app.js +274 -0
  350. package/docs/themes/tanuki/static/js/code.js +394 -0
  351. package/docs/themes/tanuki/static/js/navigation.js +778 -0
  352. package/docs/themes/tanuki/static/js/scroll-to-top.js +33 -0
  353. package/docs/themes/tanuki/static/js/search-raskell.js +240 -0
  354. package/docs/themes/tanuki/static/js/search.js +215 -0
  355. package/docs/themes/tanuki/static/js/theme.js +169 -0
  356. package/docs/themes/tanuki/static/syntax-dark.css +151 -0
  357. package/docs/themes/tanuki/static/syntax-light.css +151 -0
  358. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm.js +486 -0
  359. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm_bg.wasm +0 -0
  360. package/docs/themes/tanuki/templates/404.html +52 -0
  361. package/docs/themes/tanuki/templates/base.html +428 -0
  362. package/docs/themes/tanuki/templates/blog.html +66 -0
  363. package/docs/themes/tanuki/templates/home.html +108 -0
  364. package/docs/themes/tanuki/templates/index.html +178 -0
  365. package/docs/themes/tanuki/templates/landing.html +168 -0
  366. package/docs/themes/tanuki/templates/macros/nav.html +128 -0
  367. package/docs/themes/tanuki/templates/macros/posts.html +101 -0
  368. package/docs/themes/tanuki/templates/macros/ui.html +159 -0
  369. package/docs/themes/tanuki/templates/page.html +135 -0
  370. package/docs/themes/tanuki/templates/partials/footer.html +38 -0
  371. package/docs/themes/tanuki/templates/partials/header.html +366 -0
  372. package/docs/themes/tanuki/templates/partials/nav-buttons.html +55 -0
  373. package/docs/themes/tanuki/templates/partials/nav-overlay.html +81 -0
  374. package/docs/themes/tanuki/templates/partials/page-toc-panel.html +43 -0
  375. package/docs/themes/tanuki/templates/partials/search.html +52 -0
  376. package/docs/themes/tanuki/templates/partials/sidebar.html +107 -0
  377. package/docs/themes/tanuki/templates/partials/theme-toggle.html +35 -0
  378. package/docs/themes/tanuki/templates/partials/toc-overlay.html +146 -0
  379. package/docs/themes/tanuki/templates/partials/version-picker.html +38 -0
  380. package/docs/themes/tanuki/templates/print.html +244 -0
  381. package/docs/themes/tanuki/templates/section.html +186 -0
  382. package/docs/themes/tanuki/templates/taxonomy_list.html +18 -0
  383. package/docs/themes/tanuki/templates/taxonomy_single.html +31 -0
  384. package/docs/themes/tanuki/theme.toml +58 -0
  385. package/examples/hello-agent.rs +55 -0
  386. package/package.json +36 -0
  387. package/proto/config.proto +60 -0
  388. package/proto/events.proto +0 -0
  389. package/proto/runtime.proto +215 -0
@@ -0,0 +1,248 @@
1
+ //! Sequential Flow - Execute callables one after another
2
+ //!
3
+ //! The output of each step becomes the input to the next.
4
+
5
+ use crate::callable::Callable;
6
+ use std::sync::Arc;
7
+
8
+ /// Sequential execution flow
9
+ pub struct SequentialFlow<C: Callable> {
10
+ /// Ordered list of callables to execute
11
+ steps: Vec<Arc<C>>,
12
+ /// Flow name
13
+ name: String,
14
+ }
15
+
16
+ impl<C: Callable> SequentialFlow<C> {
17
+ /// Create a new sequential flow
18
+ pub fn new(name: impl Into<String>) -> Self {
19
+ Self {
20
+ steps: Vec::new(),
21
+ name: name.into(),
22
+ }
23
+ }
24
+
25
+ /// Add a step to the flow
26
+ pub fn add_step(mut self, callable: Arc<C>) -> Self {
27
+ self.steps.push(callable);
28
+ self
29
+ }
30
+
31
+ /// Add multiple steps
32
+ pub fn with_steps(mut self, callables: Vec<Arc<C>>) -> Self {
33
+ self.steps.extend(callables);
34
+ self
35
+ }
36
+
37
+ /// Execute the flow
38
+ pub async fn execute(&self, input: &str) -> anyhow::Result<String> {
39
+ let mut current_output = input.to_string();
40
+
41
+ for step in &self.steps {
42
+ current_output = step.run(&current_output).await?;
43
+ }
44
+
45
+ Ok(current_output)
46
+ }
47
+
48
+ /// Get the flow name
49
+ pub fn name(&self) -> &str {
50
+ &self.name
51
+ }
52
+
53
+ /// Get the number of steps
54
+ pub fn len(&self) -> usize {
55
+ self.steps.len()
56
+ }
57
+
58
+ /// Check if empty
59
+ pub fn is_empty(&self) -> bool {
60
+ self.steps.is_empty()
61
+ }
62
+ }
63
+
64
+ #[cfg(test)]
65
+ mod tests {
66
+ use super::*;
67
+ use async_trait::async_trait;
68
+
69
+ /// Mock callable for testing - transforms input in a predictable way
70
+ struct MockCallable {
71
+ name: String,
72
+ transform: Box<dyn Fn(&str) -> String + Send + Sync>,
73
+ }
74
+
75
+ impl MockCallable {
76
+ fn new(name: &str, transform: impl Fn(&str) -> String + Send + Sync + 'static) -> Self {
77
+ Self {
78
+ name: name.to_string(),
79
+ transform: Box::new(transform),
80
+ }
81
+ }
82
+
83
+ fn uppercase(name: &str) -> Self {
84
+ Self::new(name, |s| s.to_uppercase())
85
+ }
86
+
87
+ fn append(name: &str, suffix: &'static str) -> Self {
88
+ Self::new(name, move |s| format!("{}{}", s, suffix))
89
+ }
90
+
91
+ fn prepend(name: &str, prefix: &'static str) -> Self {
92
+ Self::new(name, move |s| format!("{}{}", prefix, s))
93
+ }
94
+ }
95
+
96
+ #[async_trait]
97
+ impl Callable for MockCallable {
98
+ fn name(&self) -> &str {
99
+ &self.name
100
+ }
101
+
102
+ async fn run(&self, input: &str) -> anyhow::Result<String> {
103
+ Ok((self.transform)(input))
104
+ }
105
+ }
106
+
107
+ #[tokio::test]
108
+ async fn test_sequential_empty() {
109
+ let flow: SequentialFlow<MockCallable> = SequentialFlow::new("empty");
110
+ assert!(flow.is_empty());
111
+ assert_eq!(flow.len(), 0);
112
+
113
+ let result = flow.execute("input").await.unwrap();
114
+ assert_eq!(result, "input"); // No steps = input passes through
115
+ }
116
+
117
+ #[tokio::test]
118
+ async fn test_sequential_single_step() {
119
+ let step = Arc::new(MockCallable::uppercase("upper"));
120
+ let flow = SequentialFlow::new("single").add_step(step);
121
+
122
+ assert_eq!(flow.len(), 1);
123
+ assert!(!flow.is_empty());
124
+ assert_eq!(flow.name(), "single");
125
+
126
+ let result = flow.execute("hello").await.unwrap();
127
+ assert_eq!(result, "HELLO");
128
+ }
129
+
130
+ #[tokio::test]
131
+ async fn test_sequential_multiple_steps() {
132
+ let flow = SequentialFlow::new("chain")
133
+ .add_step(Arc::new(MockCallable::uppercase("step1")))
134
+ .add_step(Arc::new(MockCallable::append("step2", "!")))
135
+ .add_step(Arc::new(MockCallable::prepend("step3", ">> ")));
136
+
137
+ assert_eq!(flow.len(), 3);
138
+
139
+ let result = flow.execute("hello").await.unwrap();
140
+ assert_eq!(result, ">> HELLO!");
141
+ }
142
+
143
+ #[tokio::test]
144
+ async fn test_sequential_with_steps() {
145
+ let steps = vec![
146
+ Arc::new(MockCallable::uppercase("s1")),
147
+ Arc::new(MockCallable::append("s2", "_done")),
148
+ ];
149
+ let flow = SequentialFlow::new("batch").with_steps(steps);
150
+
151
+ assert_eq!(flow.len(), 2);
152
+ let result = flow.execute("test").await.unwrap();
153
+ assert_eq!(result, "TEST_done");
154
+ }
155
+
156
+ #[tokio::test]
157
+ async fn test_sequential_error_propagation() {
158
+ struct FailingCallable {
159
+ name: String,
160
+ fail_on_call: usize,
161
+ call_count: std::sync::atomic::AtomicUsize,
162
+ }
163
+
164
+ impl FailingCallable {
165
+ fn new(name: &str, fail_on: usize) -> Self {
166
+ Self {
167
+ name: name.to_string(),
168
+ fail_on_call: fail_on,
169
+ call_count: std::sync::atomic::AtomicUsize::new(0),
170
+ }
171
+ }
172
+ }
173
+
174
+ #[async_trait]
175
+ impl Callable for FailingCallable {
176
+ fn name(&self) -> &str {
177
+ &self.name
178
+ }
179
+
180
+ async fn run(&self, input: &str) -> anyhow::Result<String> {
181
+ let n = self.call_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
182
+ if n >= self.fail_on_call {
183
+ anyhow::bail!("Intentional failure at step {}", n)
184
+ }
185
+ Ok(input.to_uppercase())
186
+ }
187
+ }
188
+
189
+ // Flow of 3 FailingCallable steps, second one fails
190
+ let flow = SequentialFlow::new("with_error")
191
+ .add_step(Arc::new(FailingCallable::new("step1", 10))) // Won't fail
192
+ .add_step(Arc::new(FailingCallable::new("step2", 0))) // Fails immediately
193
+ .add_step(Arc::new(FailingCallable::new("step3", 10))); // Never reached
194
+
195
+ let result = flow.execute("hello").await;
196
+ assert!(result.is_err());
197
+ assert!(result.unwrap_err().to_string().contains("Intentional failure"));
198
+ }
199
+
200
+ #[tokio::test]
201
+ async fn test_sequential_preserves_order() {
202
+ use std::sync::atomic::{AtomicUsize, Ordering};
203
+
204
+ let counter = Arc::new(AtomicUsize::new(0));
205
+ let execution_order = Arc::new(std::sync::Mutex::new(Vec::new()));
206
+
207
+ struct OrderTracker {
208
+ name: String,
209
+ counter: Arc<AtomicUsize>,
210
+ order: Arc<std::sync::Mutex<Vec<usize>>>,
211
+ }
212
+
213
+ #[async_trait]
214
+ impl Callable for OrderTracker {
215
+ fn name(&self) -> &str {
216
+ &self.name
217
+ }
218
+
219
+ async fn run(&self, input: &str) -> anyhow::Result<String> {
220
+ let n = self.counter.fetch_add(1, Ordering::SeqCst);
221
+ self.order.lock().unwrap().push(n);
222
+ Ok(input.to_string())
223
+ }
224
+ }
225
+
226
+ let flow = SequentialFlow::new("ordered")
227
+ .add_step(Arc::new(OrderTracker {
228
+ name: "first".to_string(),
229
+ counter: counter.clone(),
230
+ order: execution_order.clone(),
231
+ }))
232
+ .add_step(Arc::new(OrderTracker {
233
+ name: "second".to_string(),
234
+ counter: counter.clone(),
235
+ order: execution_order.clone(),
236
+ }))
237
+ .add_step(Arc::new(OrderTracker {
238
+ name: "third".to_string(),
239
+ counter: counter.clone(),
240
+ order: execution_order.clone(),
241
+ }));
242
+
243
+ flow.execute("test").await.unwrap();
244
+
245
+ let order = execution_order.lock().unwrap();
246
+ assert_eq!(*order, vec![0, 1, 2]);
247
+ }
248
+ }
@@ -0,0 +1,79 @@
1
+ //! Checkpoint types for save/resume execution
2
+
3
+ use crate::kernel::{GraphId, RunId};
4
+ use serde::{Deserialize, Serialize};
5
+ use serde_json::Value;
6
+ use std::collections::HashMap;
7
+ use svix_ksuid::KsuidLike;
8
+
9
+ /// Checkpoint - saved state of execution
10
+ #[derive(Debug, Clone, Serialize, Deserialize)]
11
+ pub struct Checkpoint {
12
+ /// Unique checkpoint ID
13
+ pub id: String,
14
+ /// Run this checkpoint belongs to
15
+ pub run_id: RunId,
16
+ /// Graph being executed
17
+ pub graph_id: Option<GraphId>,
18
+ /// Current node in execution
19
+ pub current_node: Option<String>,
20
+ /// State at this checkpoint
21
+ pub state: Value,
22
+ /// Messages history (for LLM agents)
23
+ pub messages: Vec<MessageRecord>,
24
+ /// Tool results collected so far
25
+ pub tool_results: HashMap<String, Value>,
26
+ /// Created timestamp
27
+ pub created_at: chrono::DateTime<chrono::Utc>,
28
+ /// Metadata
29
+ pub metadata: HashMap<String, Value>,
30
+ }
31
+
32
+ /// Message record for checkpoint
33
+ #[derive(Debug, Clone, Serialize, Deserialize)]
34
+ pub struct MessageRecord {
35
+ pub role: String,
36
+ pub content: String,
37
+ }
38
+
39
+ impl Checkpoint {
40
+ /// Create a new checkpoint
41
+ pub fn new(run_id: RunId) -> Self {
42
+ Self {
43
+ id: format!("ckpt_{}", svix_ksuid::Ksuid::new(None, None)),
44
+ run_id,
45
+ graph_id: None,
46
+ current_node: None,
47
+ state: Value::Null,
48
+ messages: Vec::new(),
49
+ tool_results: HashMap::new(),
50
+ created_at: chrono::Utc::now(),
51
+ metadata: HashMap::new(),
52
+ }
53
+ }
54
+
55
+ /// Set the current state
56
+ pub fn with_state(mut self, state: Value) -> Self {
57
+ self.state = state;
58
+ self
59
+ }
60
+
61
+ /// Set the current node
62
+ pub fn with_node(mut self, node: impl Into<String>) -> Self {
63
+ self.current_node = Some(node.into());
64
+ self
65
+ }
66
+
67
+ /// Add a message to history
68
+ pub fn add_message(&mut self, role: impl Into<String>, content: impl Into<String>) {
69
+ self.messages.push(MessageRecord {
70
+ role: role.into(),
71
+ content: content.into(),
72
+ });
73
+ }
74
+
75
+ /// Add a tool result
76
+ pub fn add_tool_result(&mut self, tool_name: impl Into<String>, result: Value) {
77
+ self.tool_results.insert(tool_name.into(), result);
78
+ }
79
+ }
@@ -0,0 +1,76 @@
1
+ //! Checkpoint store trait and implementations
2
+
3
+ use super::Checkpoint;
4
+ use async_trait::async_trait;
5
+ use std::collections::HashMap;
6
+ use std::sync::{Arc, RwLock};
7
+
8
+ /// Checkpoint store trait
9
+ #[async_trait]
10
+ pub trait CheckpointStore: Send + Sync {
11
+ /// Save a checkpoint
12
+ async fn save(&self, checkpoint: Checkpoint) -> anyhow::Result<()>;
13
+
14
+ /// Load a checkpoint by ID
15
+ async fn load(&self, id: &str) -> anyhow::Result<Option<Checkpoint>>;
16
+
17
+ /// Load latest checkpoint for a run
18
+ async fn load_latest(&self, run_id: &str) -> anyhow::Result<Option<Checkpoint>>;
19
+
20
+ /// List checkpoints for a run
21
+ async fn list(&self, run_id: &str) -> anyhow::Result<Vec<Checkpoint>>;
22
+
23
+ /// Delete a checkpoint
24
+ async fn delete(&self, id: &str) -> anyhow::Result<()>;
25
+ }
26
+
27
+ /// In-memory checkpoint store (for testing/development)
28
+ #[derive(Default)]
29
+ pub struct InMemoryCheckpointStore {
30
+ checkpoints: Arc<RwLock<HashMap<String, Checkpoint>>>,
31
+ }
32
+
33
+ impl InMemoryCheckpointStore {
34
+ pub fn new() -> Self {
35
+ Self::default()
36
+ }
37
+ }
38
+
39
+ #[async_trait]
40
+ impl CheckpointStore for InMemoryCheckpointStore {
41
+ async fn save(&self, checkpoint: Checkpoint) -> anyhow::Result<()> {
42
+ let mut store = self.checkpoints.write().unwrap();
43
+ store.insert(checkpoint.id.clone(), checkpoint);
44
+ Ok(())
45
+ }
46
+
47
+ async fn load(&self, id: &str) -> anyhow::Result<Option<Checkpoint>> {
48
+ let store = self.checkpoints.read().unwrap();
49
+ Ok(store.get(id).cloned())
50
+ }
51
+
52
+ async fn load_latest(&self, run_id: &str) -> anyhow::Result<Option<Checkpoint>> {
53
+ let store = self.checkpoints.read().unwrap();
54
+ let latest = store
55
+ .values()
56
+ .filter(|c| c.run_id.as_str() == run_id)
57
+ .max_by_key(|c| c.created_at);
58
+ Ok(latest.cloned())
59
+ }
60
+
61
+ async fn list(&self, run_id: &str) -> anyhow::Result<Vec<Checkpoint>> {
62
+ let store = self.checkpoints.read().unwrap();
63
+ let checkpoints: Vec<_> = store
64
+ .values()
65
+ .filter(|c| c.run_id.as_str() == run_id)
66
+ .cloned()
67
+ .collect();
68
+ Ok(checkpoints)
69
+ }
70
+
71
+ async fn delete(&self, id: &str) -> anyhow::Result<()> {
72
+ let mut store = self.checkpoints.write().unwrap();
73
+ store.remove(id);
74
+ Ok(())
75
+ }
76
+ }
@@ -0,0 +1,189 @@
1
+ //! CompiledGraph - validated and ready-to-execute graph
2
+ //!
3
+ //! Supports parallel execution when multiple targets are available.
4
+
5
+ use super::edge::{ConditionalEdge, Edge, EdgeTarget};
6
+ use super::node::{DynNode, NodeState};
7
+ use futures::future::join_all;
8
+ use std::collections::HashMap;
9
+
10
+ /// Compiled graph - validated and ready to execute
11
+ pub struct CompiledGraph {
12
+ pub(crate) nodes: HashMap<String, DynNode>,
13
+ pub(crate) edges: Vec<Edge>,
14
+ pub(crate) conditional_edges: Vec<ConditionalEdge>,
15
+ pub(crate) entry_point: String,
16
+ }
17
+
18
+ impl CompiledGraph {
19
+ /// Get a node by name
20
+ pub fn get_node(&self, name: &str) -> Option<&DynNode> {
21
+ self.nodes.get(name)
22
+ }
23
+
24
+ /// Get the entry point
25
+ pub fn entry_point(&self) -> &str {
26
+ &self.entry_point
27
+ }
28
+
29
+ /// Get the next node(s) after the given node
30
+ pub fn get_next(&self, from: &str, output: &str) -> Vec<EdgeTarget> {
31
+ let mut targets = Vec::new();
32
+
33
+ // Check conditional edges first
34
+ for ce in &self.conditional_edges {
35
+ if ce.from == from {
36
+ targets.push((ce.router)(output));
37
+ }
38
+ }
39
+
40
+ // Then check regular edges
41
+ for edge in &self.edges {
42
+ if edge.from == from {
43
+ targets.push(edge.to.clone());
44
+ }
45
+ }
46
+
47
+ targets
48
+ }
49
+
50
+ /// Run the graph with an initial input
51
+ pub async fn run(&self, input: impl Into<String>) -> anyhow::Result<NodeState> {
52
+ let initial_state = NodeState::from_str(&input.into());
53
+ self.run_with_state(initial_state).await
54
+ }
55
+
56
+ /// Run the graph with an initial state
57
+ ///
58
+ /// When multiple targets are available, executes them in parallel.
59
+ /// This implements the Agentic DAG execution model where independent
60
+ /// nodes can run concurrently.
61
+ pub async fn run_with_state(&self, initial_state: NodeState) -> anyhow::Result<NodeState> {
62
+ let mut current_node = self.entry_point.clone();
63
+ let mut state = initial_state;
64
+
65
+ loop {
66
+ // Get the current node
67
+ let node = self
68
+ .nodes
69
+ .get(&current_node)
70
+ .ok_or_else(|| anyhow::anyhow!("Node '{}' not found", current_node))?;
71
+
72
+ // Execute the node
73
+ tracing::debug!(node = %current_node, "Executing node");
74
+ state = node.execute(state).await?;
75
+
76
+ // Get the output for routing
77
+ let output = state.as_str().unwrap_or_default().to_string();
78
+
79
+ // Find next node(s)
80
+ let next_targets = self.get_next(&current_node, &output);
81
+
82
+ if next_targets.is_empty() {
83
+ // No outgoing edges - end execution
84
+ tracing::debug!(node = %current_node, "No outgoing edges, ending");
85
+ break;
86
+ }
87
+
88
+ // Check for END target
89
+ let has_end = next_targets.iter().any(|t| matches!(t, EdgeTarget::End));
90
+ if has_end {
91
+ tracing::debug!("Reached END");
92
+ break;
93
+ }
94
+
95
+ // Collect node targets (filter out End)
96
+ let node_targets: Vec<String> = next_targets
97
+ .iter()
98
+ .filter_map(|t| match t {
99
+ EdgeTarget::Node(n) => Some(n.clone()),
100
+ EdgeTarget::End => None,
101
+ })
102
+ .collect();
103
+
104
+ if node_targets.is_empty() {
105
+ break;
106
+ }
107
+
108
+ // Single target - sequential execution
109
+ if node_targets.len() == 1 {
110
+ current_node = node_targets[0].clone();
111
+ continue;
112
+ }
113
+
114
+ // Multiple targets - PARALLEL EXECUTION
115
+ tracing::debug!(
116
+ targets = ?node_targets,
117
+ "Executing {} nodes in parallel",
118
+ node_targets.len()
119
+ );
120
+
121
+ // Execute all target nodes in parallel
122
+ let parallel_results = self
123
+ .execute_nodes_parallel(&node_targets, state.clone())
124
+ .await?;
125
+
126
+ // Aggregate results: combine all outputs
127
+ // For now, we use the last successful result as the state
128
+ // In a full implementation, this would support custom aggregation strategies
129
+ if let Some(last_state) = parallel_results.into_iter().last() {
130
+ state = last_state;
131
+ }
132
+
133
+ // After parallel execution, check if any nodes have outgoing edges
134
+ // For simplicity, we end after parallel execution
135
+ // A full implementation would continue with fan-in logic
136
+ tracing::debug!("Parallel execution complete");
137
+ break;
138
+ }
139
+
140
+ Ok(state)
141
+ }
142
+
143
+ /// Execute multiple nodes in parallel
144
+ ///
145
+ /// Returns results from all nodes that completed successfully.
146
+ async fn execute_nodes_parallel(
147
+ &self,
148
+ node_names: &[String],
149
+ input_state: NodeState,
150
+ ) -> anyhow::Result<Vec<NodeState>> {
151
+ let futures: Vec<_> = node_names
152
+ .iter()
153
+ .filter_map(|name| {
154
+ self.nodes.get(name).map(|node| {
155
+ let state = input_state.clone();
156
+ let node_name = name.clone();
157
+ async move {
158
+ tracing::debug!(node = %node_name, "Executing parallel node");
159
+ node.execute(state).await
160
+ }
161
+ })
162
+ })
163
+ .collect();
164
+
165
+ let results = join_all(futures).await;
166
+
167
+ // Collect successful results
168
+ let successful: Vec<NodeState> = results
169
+ .into_iter()
170
+ .filter_map(|r| r.ok())
171
+ .collect();
172
+
173
+ if successful.is_empty() {
174
+ anyhow::bail!("All parallel nodes failed");
175
+ }
176
+
177
+ Ok(successful)
178
+ }
179
+
180
+ /// Get node count
181
+ pub fn node_count(&self) -> usize {
182
+ self.nodes.len()
183
+ }
184
+
185
+ /// Get edge count
186
+ pub fn edge_count(&self) -> usize {
187
+ self.edges.len() + self.conditional_edges.len()
188
+ }
189
+ }
@@ -0,0 +1,59 @@
1
+ //! Edge types for graph connections
2
+
3
+ /// Target for an edge - either a specific node or END
4
+ #[derive(Debug, Clone, PartialEq, Eq)]
5
+ pub enum EdgeTarget {
6
+ /// Target a specific node by name
7
+ Node(String),
8
+ /// End the graph execution
9
+ End,
10
+ }
11
+
12
+ impl EdgeTarget {
13
+ pub fn node(name: impl Into<String>) -> Self {
14
+ Self::Node(name.into())
15
+ }
16
+
17
+ pub fn end() -> Self {
18
+ Self::End
19
+ }
20
+
21
+ pub fn is_end(&self) -> bool {
22
+ matches!(self, Self::End)
23
+ }
24
+ }
25
+
26
+ /// Edge in the graph
27
+ #[derive(Debug, Clone)]
28
+ pub struct Edge {
29
+ pub from: String,
30
+ pub to: EdgeTarget,
31
+ }
32
+
33
+ impl Edge {
34
+ pub fn new(from: impl Into<String>, to: EdgeTarget) -> Self {
35
+ Self {
36
+ from: from.into(),
37
+ to,
38
+ }
39
+ }
40
+ }
41
+
42
+ /// Conditional edge - routes based on a function
43
+ #[derive(Clone)]
44
+ pub struct ConditionalEdge {
45
+ pub from: String,
46
+ pub router: ConditionalRouter,
47
+ }
48
+
49
+ /// Router function type
50
+ pub type ConditionalRouter = std::sync::Arc<dyn Fn(&str) -> EdgeTarget + Send + Sync>;
51
+
52
+ impl std::fmt::Debug for ConditionalEdge {
53
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54
+ f.debug_struct("ConditionalEdge")
55
+ .field("from", &self.from)
56
+ .field("router", &"<fn>")
57
+ .finish()
58
+ }
59
+ }