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,1394 @@
1
+ //! Execution Events for telemetry and audit
2
+ //!
3
+ //! This module defines all event types emitted during graph execution.
4
+ //! Events provide observability into execution progress, decisions,
5
+ //! and control signals.
6
+ //!
7
+ //! ## Event Types
8
+ //! - `ExecutionEvent` - Base event for all execution events
9
+ //! - `DecisionRecord` - Audit trail for decisions
10
+ //! - `ControlEvent` - Governance and intervention signals
11
+ //!
12
+ //! @see docs/TECHNICAL/01-EXECUTION-TELEMETRY.md
13
+
14
+ use super::ids::{ArtifactId, ExecutionId, ParentLink, StepId, StepType, TenantId, UserId};
15
+ use serde::{Deserialize, Serialize};
16
+ use svix_ksuid::{Ksuid, KsuidLike};
17
+
18
+ /// Generate a new event ID
19
+ fn new_event_id() -> String {
20
+ format!("evt_{}", Ksuid::new(None, None))
21
+ }
22
+
23
+ // =============================================================================
24
+ // Event Types
25
+ // =============================================================================
26
+
27
+ /// ExecutionEventType - All event types in the execution lifecycle
28
+ ///
29
+ /// Naming convention: `<entity>.<action>`
30
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31
+ #[serde(rename_all = "snake_case")]
32
+ pub enum ExecutionEventType {
33
+ // Execution lifecycle
34
+ ExecutionStart,
35
+ ExecutionEnd,
36
+ ExecutionFailed,
37
+ ExecutionCancelled,
38
+
39
+ // Step lifecycle
40
+ StepStart,
41
+ StepEnd,
42
+ StepFailed,
43
+ StepDiscovered,
44
+
45
+ // Artifact events
46
+ ArtifactCreated,
47
+
48
+ // State snapshots
49
+ StateSnapshot,
50
+
51
+ // Decision audit
52
+ DecisionMade,
53
+
54
+ // Control signals
55
+ ControlPause,
56
+ ControlResume,
57
+ ControlCancel,
58
+
59
+ // Inbox messages (INV-INBOX-003: audit trail)
60
+ InboxMessage,
61
+
62
+ // Tool execution
63
+ ToolCallStart,
64
+ ToolCallEnd,
65
+
66
+ // Agentic loop events
67
+ CheckpointSaved,
68
+ GoalEvaluated,
69
+ }
70
+
71
+ impl ExecutionEventType {
72
+ /// Get the event type as a string (e.g., "execution.start")
73
+ pub fn as_str(&self) -> &'static str {
74
+ match self {
75
+ Self::ExecutionStart => "execution.start",
76
+ Self::ExecutionEnd => "execution.end",
77
+ Self::ExecutionFailed => "execution.failed",
78
+ Self::ExecutionCancelled => "execution.cancelled",
79
+ Self::StepStart => "step.start",
80
+ Self::StepEnd => "step.end",
81
+ Self::StepFailed => "step.failed",
82
+ Self::StepDiscovered => "step.discovered",
83
+ Self::ArtifactCreated => "artifact.created",
84
+ Self::StateSnapshot => "state.snapshot",
85
+ Self::DecisionMade => "decision.made",
86
+ Self::ControlPause => "control.pause",
87
+ Self::ControlResume => "control.resume",
88
+ Self::ControlCancel => "control.cancel",
89
+ Self::InboxMessage => "inbox.message",
90
+ Self::ToolCallStart => "tool.start",
91
+ Self::ToolCallEnd => "tool.end",
92
+ Self::CheckpointSaved => "state.checkpoint",
93
+ Self::GoalEvaluated => "goal.evaluated",
94
+ }
95
+ }
96
+ }
97
+
98
+ // =============================================================================
99
+ // Execution Context (attached to all events)
100
+ // =============================================================================
101
+
102
+ /// ExecutionContext - Minimal context attached to every event
103
+ ///
104
+ /// Replaces the old BaseIdHierarchy with a simpler structure.
105
+ #[derive(Debug, Clone, Serialize, Deserialize)]
106
+ pub struct ExecutionContext {
107
+ /// Required: The execution this event belongs to
108
+ pub execution_id: ExecutionId,
109
+
110
+ /// Optional: The specific step
111
+ pub step_id: Option<StepId>,
112
+
113
+ /// Optional: The artifact produced
114
+ pub artifact_id: Option<ArtifactId>,
115
+
116
+ /// Parent linkage (causal origin)
117
+ pub parent: Option<ParentLink>,
118
+
119
+ /// Tenant context
120
+ pub tenant_id: Option<TenantId>,
121
+ pub user_id: Option<UserId>,
122
+ }
123
+
124
+ impl ExecutionContext {
125
+ /// Create a new ExecutionContext for an execution
126
+ pub fn new(execution_id: ExecutionId) -> Self {
127
+ Self {
128
+ execution_id,
129
+ step_id: None,
130
+ artifact_id: None,
131
+ parent: None,
132
+ tenant_id: None,
133
+ user_id: None,
134
+ }
135
+ }
136
+
137
+ /// Add step context
138
+ pub fn with_step(mut self, step_id: StepId) -> Self {
139
+ self.step_id = Some(step_id);
140
+ self
141
+ }
142
+
143
+ /// Add artifact context
144
+ pub fn with_artifact(mut self, artifact_id: ArtifactId) -> Self {
145
+ self.artifact_id = Some(artifact_id);
146
+ self
147
+ }
148
+
149
+ /// Add parent linkage
150
+ pub fn with_parent(mut self, parent: ParentLink) -> Self {
151
+ self.parent = Some(parent);
152
+ self
153
+ }
154
+
155
+ /// Add tenant context
156
+ pub fn with_tenant(mut self, tenant_id: TenantId, user_id: Option<UserId>) -> Self {
157
+ self.tenant_id = Some(tenant_id);
158
+ self.user_id = user_id;
159
+ self
160
+ }
161
+ }
162
+
163
+ // =============================================================================
164
+ // Execution Event (Base Event Type)
165
+ // =============================================================================
166
+
167
+ /// ExecutionEvent - Base event schema for all execution events
168
+ ///
169
+ /// All events include execution context for traceability.
170
+ #[derive(Debug, Clone, Serialize, Deserialize)]
171
+ pub struct ExecutionEvent {
172
+ /// Unique event ID
173
+ pub event_id: String,
174
+
175
+ /// Event type
176
+ pub event_type: ExecutionEventType,
177
+
178
+ /// Execution context
179
+ pub context: ExecutionContext,
180
+
181
+ /// Timestamp
182
+ pub timestamp: chrono::DateTime<chrono::Utc>,
183
+
184
+ /// Duration in milliseconds (for start/end pairs)
185
+ pub duration_ms: Option<u64>,
186
+
187
+ /// Event-specific payload
188
+ pub payload: Option<serde_json::Value>,
189
+ }
190
+
191
+ impl ExecutionEvent {
192
+ /// Create a new ExecutionEvent
193
+ pub fn new(event_type: ExecutionEventType, context: ExecutionContext) -> Self {
194
+ Self {
195
+ event_id: new_event_id(),
196
+ event_type,
197
+ context,
198
+ timestamp: chrono::Utc::now(),
199
+ duration_ms: None,
200
+ payload: None,
201
+ }
202
+ }
203
+
204
+ /// Add duration
205
+ pub fn with_duration(mut self, ms: u64) -> Self {
206
+ self.duration_ms = Some(ms);
207
+ self
208
+ }
209
+
210
+ /// Add payload
211
+ pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
212
+ self.payload = Some(payload);
213
+ self
214
+ }
215
+
216
+ // --- Factory methods for common events ---
217
+
218
+ /// Create an execution.start event
219
+ pub fn execution_start(execution_id: ExecutionId, parent: Option<ParentLink>) -> Self {
220
+ let mut ctx = ExecutionContext::new(execution_id);
221
+ if let Some(p) = parent {
222
+ ctx = ctx.with_parent(p);
223
+ }
224
+ Self::new(ExecutionEventType::ExecutionStart, ctx)
225
+ }
226
+
227
+ /// Create an execution.end event
228
+ pub fn execution_end(execution_id: ExecutionId, duration_ms: Option<u64>) -> Self {
229
+ let ctx = ExecutionContext::new(execution_id);
230
+ let mut event = Self::new(ExecutionEventType::ExecutionEnd, ctx);
231
+ event.duration_ms = duration_ms;
232
+ event
233
+ }
234
+
235
+ /// Create a step.start event
236
+ pub fn step_start(
237
+ execution_id: ExecutionId,
238
+ step_id: StepId,
239
+ step_type: StepType,
240
+ name: &str,
241
+ ) -> Self {
242
+ let ctx = ExecutionContext::new(execution_id).with_step(step_id);
243
+ Self::new(ExecutionEventType::StepStart, ctx).with_payload(serde_json::json!({
244
+ "step_type": step_type.to_string(),
245
+ "name": name,
246
+ }))
247
+ }
248
+
249
+ /// Create a step.end event
250
+ pub fn step_end(execution_id: ExecutionId, step_id: StepId, duration_ms: u64) -> Self {
251
+ let ctx = ExecutionContext::new(execution_id).with_step(step_id);
252
+ Self::new(ExecutionEventType::StepEnd, ctx).with_duration(duration_ms)
253
+ }
254
+
255
+ /// Create an artifact.created event
256
+ pub fn artifact_created(
257
+ execution_id: ExecutionId,
258
+ step_id: StepId,
259
+ artifact_id: ArtifactId,
260
+ artifact_type: &str,
261
+ ) -> Self {
262
+ let ctx = ExecutionContext::new(execution_id)
263
+ .with_step(step_id)
264
+ .with_artifact(artifact_id);
265
+ Self::new(ExecutionEventType::ArtifactCreated, ctx).with_payload(serde_json::json!({
266
+ "artifact_type": artifact_type,
267
+ }))
268
+ }
269
+ }
270
+
271
+ // =============================================================================
272
+ // Decision Record (Audit Trail)
273
+ // =============================================================================
274
+
275
+ /// DecisionType - Types of decisions made during execution
276
+ ///
277
+ /// @see packages/enact-schemas/src/streaming.schemas.ts - decisionEventDataSchema.type
278
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
279
+ #[serde(rename_all = "snake_case")]
280
+ pub enum DecisionType {
281
+ /// Route selection (which agent/node to call)
282
+ Routing,
283
+ /// Conditional branch decision
284
+ Branch,
285
+ /// HITL approval decision
286
+ Approval,
287
+ /// Escalate to human
288
+ Escalation,
289
+ /// Retry failed operation
290
+ Retry,
291
+ /// Use fallback strategy
292
+ Fallback,
293
+ /// Policy evaluation decision
294
+ PolicyEvaluation,
295
+ /// Tool selection decision
296
+ ToolSelection,
297
+ /// Rejection decision
298
+ Rejection,
299
+ }
300
+
301
+ /// DecisionInput - Inputs that informed the decision
302
+ #[derive(Debug, Clone, Serialize, Deserialize)]
303
+ pub struct DecisionInput {
304
+ /// Facts used in decision
305
+ pub facts: Vec<String>,
306
+ /// Constraints applied
307
+ pub constraints: Vec<String>,
308
+ /// Evidence IDs (artifacts, memory, RAG results)
309
+ pub evidence_ids: Vec<String>,
310
+ }
311
+
312
+ /// DecisionAlternative - An alternative option that was not chosen
313
+ #[derive(Debug, Clone, Serialize, Deserialize)]
314
+ pub struct DecisionAlternative {
315
+ /// Name/ID of the alternative
316
+ pub option: String,
317
+ /// Confidence score (0-1)
318
+ pub score: f64,
319
+ /// Why it was rejected
320
+ pub rejected_reason: String,
321
+ }
322
+
323
+ /// ModelContext - Model configuration used for decision
324
+ #[derive(Debug, Clone, Serialize, Deserialize)]
325
+ pub struct ModelContext {
326
+ /// Provider (e.g., 'openai', 'anthropic')
327
+ pub provider: String,
328
+ /// Model (e.g., 'gpt-4', 'claude-3-opus')
329
+ pub model: String,
330
+ /// Model version
331
+ pub version: Option<String>,
332
+ /// Temperature
333
+ pub temperature: Option<f64>,
334
+ /// Max tokens
335
+ pub max_tokens: Option<u32>,
336
+ }
337
+
338
+ /// DecisionRecord - Audit trail for decisions made during execution
339
+ ///
340
+ /// Records what decision was made, why, what alternatives were considered,
341
+ /// and what context/model was used.
342
+ #[derive(Debug, Clone, Serialize, Deserialize)]
343
+ pub struct DecisionRecord {
344
+ /// Unique decision ID
345
+ pub decision_id: String,
346
+
347
+ /// Execution context
348
+ pub execution_id: ExecutionId,
349
+ pub step_id: Option<StepId>,
350
+
351
+ /// Decision metadata
352
+ pub decision_type: DecisionType,
353
+ pub timestamp: chrono::DateTime<chrono::Utc>,
354
+
355
+ /// Decision outcome
356
+ pub outcome: String,
357
+ /// Confidence in decision (0-1)
358
+ pub confidence: f64,
359
+
360
+ /// Decision inputs
361
+ pub inputs: DecisionInput,
362
+
363
+ /// Alternatives considered
364
+ pub alternatives: Option<Vec<DecisionAlternative>>,
365
+
366
+ /// Model context (if LLM was used)
367
+ pub model_context: Option<ModelContext>,
368
+
369
+ /// Reasoning trace (optional explanation)
370
+ pub reasoning: Option<String>,
371
+ }
372
+
373
+ impl DecisionRecord {
374
+ /// Create a new DecisionRecord
375
+ pub fn new(
376
+ decision_type: DecisionType,
377
+ execution_id: ExecutionId,
378
+ outcome: impl Into<String>,
379
+ confidence: f64,
380
+ inputs: DecisionInput,
381
+ ) -> Self {
382
+ Self {
383
+ decision_id: format!("dec_{}", Ksuid::new(None, None)),
384
+ execution_id,
385
+ step_id: None,
386
+ decision_type,
387
+ timestamp: chrono::Utc::now(),
388
+ outcome: outcome.into(),
389
+ confidence,
390
+ inputs,
391
+ alternatives: None,
392
+ model_context: None,
393
+ reasoning: None,
394
+ }
395
+ }
396
+
397
+ /// Add step context
398
+ pub fn with_step(mut self, step_id: StepId) -> Self {
399
+ self.step_id = Some(step_id);
400
+ self
401
+ }
402
+
403
+ /// Add alternatives
404
+ pub fn with_alternatives(mut self, alternatives: Vec<DecisionAlternative>) -> Self {
405
+ self.alternatives = Some(alternatives);
406
+ self
407
+ }
408
+
409
+ /// Add model context
410
+ pub fn with_model_context(mut self, model_context: ModelContext) -> Self {
411
+ self.model_context = Some(model_context);
412
+ self
413
+ }
414
+
415
+ /// Add reasoning
416
+ pub fn with_reasoning(mut self, reasoning: impl Into<String>) -> Self {
417
+ self.reasoning = Some(reasoning.into());
418
+ self
419
+ }
420
+ }
421
+
422
+ // =============================================================================
423
+ // Control Events (Governance)
424
+ // =============================================================================
425
+
426
+ /// ControlActor - Who/what initiated the control action
427
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
428
+ #[serde(rename_all = "snake_case")]
429
+ pub enum ControlActor {
430
+ /// System-initiated (timeout, policy)
431
+ System,
432
+ /// User-initiated (button click)
433
+ User,
434
+ /// Agent-initiated (self-regulation)
435
+ Agent,
436
+ /// Policy engine decision
437
+ PolicyEngine,
438
+ }
439
+
440
+ /// ControlAction - What control action was requested
441
+ ///
442
+ /// @see packages/enact-schemas/src/streaming.schemas.ts - controlSignalTypeSchema
443
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
444
+ #[serde(rename_all = "snake_case")]
445
+ pub enum ControlAction {
446
+ /// Stop execution completely
447
+ Stop,
448
+ /// Pause execution
449
+ Pause,
450
+ /// Resume execution
451
+ Resume,
452
+ /// Cancel execution
453
+ Cancel,
454
+ /// Approve HITL request
455
+ Approve,
456
+ /// Deny HITL request
457
+ Deny,
458
+ /// Escalate to human
459
+ Escalate,
460
+ }
461
+
462
+ /// ControlOutcome - What happened as a result of the control action
463
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
464
+ #[serde(rename_all = "snake_case")]
465
+ pub enum ControlOutcome {
466
+ /// Action was allowed and applied
467
+ Allowed,
468
+ /// Action was denied by policy
469
+ Denied,
470
+ /// Action was modified/adapted
471
+ Modified,
472
+ /// Action was escalated for approval
473
+ Escalated,
474
+ }
475
+
476
+ /// ControlEvent - Governance event for control signals
477
+ ///
478
+ /// Records who requested what action, why, and what the outcome was.
479
+ /// Used for audit trails and compliance.
480
+ #[derive(Debug, Clone, Serialize, Deserialize)]
481
+ pub struct ControlEvent {
482
+ /// Unique event ID
483
+ pub event_id: String,
484
+
485
+ /// Execution context
486
+ pub execution_id: ExecutionId,
487
+ pub step_id: Option<StepId>,
488
+
489
+ /// Control metadata
490
+ pub timestamp: chrono::DateTime<chrono::Utc>,
491
+ pub actor: ControlActor,
492
+ pub action: ControlAction,
493
+
494
+ /// Reasoning
495
+ pub reason: String,
496
+
497
+ /// Outcome
498
+ pub outcome: ControlOutcome,
499
+
500
+ /// Actor details (User ID, agent ID, etc.)
501
+ pub actor_id: Option<String>,
502
+ }
503
+
504
+ impl ControlEvent {
505
+ /// Create a new ControlEvent
506
+ pub fn new(
507
+ actor: ControlActor,
508
+ action: ControlAction,
509
+ execution_id: ExecutionId,
510
+ reason: impl Into<String>,
511
+ outcome: ControlOutcome,
512
+ ) -> Self {
513
+ Self {
514
+ event_id: format!("ctrl_{}", Ksuid::new(None, None)),
515
+ execution_id,
516
+ step_id: None,
517
+ timestamp: chrono::Utc::now(),
518
+ actor,
519
+ action,
520
+ reason: reason.into(),
521
+ outcome,
522
+ actor_id: None,
523
+ }
524
+ }
525
+
526
+ /// Add step context
527
+ pub fn with_step(mut self, step_id: StepId) -> Self {
528
+ self.step_id = Some(step_id);
529
+ self
530
+ }
531
+
532
+ /// Add actor ID
533
+ pub fn with_actor_id(mut self, actor_id: impl Into<String>) -> Self {
534
+ self.actor_id = Some(actor_id.into());
535
+ self
536
+ }
537
+
538
+ // --- Factory methods for common control events ---
539
+
540
+ /// Create a pause control event
541
+ pub fn pause(
542
+ execution_id: ExecutionId,
543
+ actor: ControlActor,
544
+ reason: impl Into<String>,
545
+ ) -> Self {
546
+ Self::new(
547
+ actor,
548
+ ControlAction::Pause,
549
+ execution_id,
550
+ reason,
551
+ ControlOutcome::Allowed,
552
+ )
553
+ }
554
+
555
+ /// Create a resume control event
556
+ pub fn resume(
557
+ execution_id: ExecutionId,
558
+ actor: ControlActor,
559
+ reason: impl Into<String>,
560
+ ) -> Self {
561
+ Self::new(
562
+ actor,
563
+ ControlAction::Resume,
564
+ execution_id,
565
+ reason,
566
+ ControlOutcome::Allowed,
567
+ )
568
+ }
569
+
570
+ /// Create a cancel control event
571
+ pub fn cancel(
572
+ execution_id: ExecutionId,
573
+ actor: ControlActor,
574
+ reason: impl Into<String>,
575
+ ) -> Self {
576
+ Self::new(
577
+ actor,
578
+ ControlAction::Cancel,
579
+ execution_id,
580
+ reason,
581
+ ControlOutcome::Allowed,
582
+ )
583
+ }
584
+ }
585
+
586
+ // =============================================================================
587
+ // Legacy Event (Backward Compatibility)
588
+ // =============================================================================
589
+
590
+ /// Event - Alias for ExecutionEvent
591
+ #[derive(Debug, Clone, Serialize, Deserialize)]
592
+ pub struct Event {
593
+ /// Event ID
594
+ pub id: String,
595
+ /// Run context (now ExecutionId)
596
+ pub run_id: ExecutionId,
597
+ /// Node context (now StepId)
598
+ pub node_id: Option<StepId>,
599
+ /// Event author (agent name, tool name, etc.)
600
+ pub author: String,
601
+ /// Timestamp
602
+ pub timestamp: chrono::DateTime<chrono::Utc>,
603
+ /// Event content
604
+ pub content: Option<String>,
605
+ /// Is this the final response?
606
+ pub is_final: bool,
607
+ }
608
+
609
+ impl Event {
610
+ pub fn new(run_id: ExecutionId, author: impl Into<String>) -> Self {
611
+ Self {
612
+ id: new_event_id(),
613
+ run_id,
614
+ node_id: None,
615
+ author: author.into(),
616
+ timestamp: chrono::Utc::now(),
617
+ content: None,
618
+ is_final: false,
619
+ }
620
+ }
621
+
622
+ pub fn with_content(mut self, content: impl Into<String>) -> Self {
623
+ self.content = Some(content.into());
624
+ self
625
+ }
626
+
627
+ pub fn with_node(mut self, node_id: StepId) -> Self {
628
+ self.node_id = Some(node_id);
629
+ self
630
+ }
631
+
632
+ pub fn final_response(mut self) -> Self {
633
+ self.is_final = true;
634
+ self
635
+ }
636
+ }
637
+
638
+ #[cfg(test)]
639
+ mod tests {
640
+ use super::*;
641
+
642
+ // =========================================================================
643
+ // ExecutionEventType Tests
644
+ // =========================================================================
645
+
646
+ #[test]
647
+ fn test_execution_event_type_as_str_execution_start() {
648
+ assert_eq!(
649
+ ExecutionEventType::ExecutionStart.as_str(),
650
+ "execution.start"
651
+ );
652
+ }
653
+
654
+ #[test]
655
+ fn test_execution_event_type_as_str_execution_end() {
656
+ assert_eq!(ExecutionEventType::ExecutionEnd.as_str(), "execution.end");
657
+ }
658
+
659
+ #[test]
660
+ fn test_execution_event_type_as_str_execution_failed() {
661
+ assert_eq!(
662
+ ExecutionEventType::ExecutionFailed.as_str(),
663
+ "execution.failed"
664
+ );
665
+ }
666
+
667
+ #[test]
668
+ fn test_execution_event_type_as_str_execution_cancelled() {
669
+ assert_eq!(
670
+ ExecutionEventType::ExecutionCancelled.as_str(),
671
+ "execution.cancelled"
672
+ );
673
+ }
674
+
675
+ #[test]
676
+ fn test_execution_event_type_as_str_step_start() {
677
+ assert_eq!(ExecutionEventType::StepStart.as_str(), "step.start");
678
+ }
679
+
680
+ #[test]
681
+ fn test_execution_event_type_as_str_step_end() {
682
+ assert_eq!(ExecutionEventType::StepEnd.as_str(), "step.end");
683
+ }
684
+
685
+ #[test]
686
+ fn test_execution_event_type_as_str_step_failed() {
687
+ assert_eq!(ExecutionEventType::StepFailed.as_str(), "step.failed");
688
+ }
689
+
690
+ #[test]
691
+ fn test_execution_event_type_as_str_artifact_created() {
692
+ assert_eq!(
693
+ ExecutionEventType::ArtifactCreated.as_str(),
694
+ "artifact.created"
695
+ );
696
+ }
697
+
698
+ #[test]
699
+ fn test_execution_event_type_as_str_state_snapshot() {
700
+ assert_eq!(ExecutionEventType::StateSnapshot.as_str(), "state.snapshot");
701
+ }
702
+
703
+ #[test]
704
+ fn test_execution_event_type_as_str_decision_made() {
705
+ assert_eq!(ExecutionEventType::DecisionMade.as_str(), "decision.made");
706
+ }
707
+
708
+ #[test]
709
+ fn test_execution_event_type_as_str_control_pause() {
710
+ assert_eq!(ExecutionEventType::ControlPause.as_str(), "control.pause");
711
+ }
712
+
713
+ #[test]
714
+ fn test_execution_event_type_as_str_control_resume() {
715
+ assert_eq!(ExecutionEventType::ControlResume.as_str(), "control.resume");
716
+ }
717
+
718
+ #[test]
719
+ fn test_execution_event_type_as_str_control_cancel() {
720
+ assert_eq!(ExecutionEventType::ControlCancel.as_str(), "control.cancel");
721
+ }
722
+
723
+ #[test]
724
+ fn test_execution_event_type_serde() {
725
+ let event_type = ExecutionEventType::ExecutionStart;
726
+ let json = serde_json::to_string(&event_type).unwrap();
727
+ let parsed: ExecutionEventType = serde_json::from_str(&json).unwrap();
728
+ assert_eq!(event_type, parsed);
729
+ }
730
+
731
+ #[test]
732
+ fn test_execution_event_type_equality() {
733
+ assert_eq!(ExecutionEventType::StepStart, ExecutionEventType::StepStart);
734
+ assert_ne!(ExecutionEventType::StepStart, ExecutionEventType::StepEnd);
735
+ }
736
+
737
+ // =========================================================================
738
+ // ExecutionContext Tests
739
+ // =========================================================================
740
+
741
+ #[test]
742
+ fn test_execution_context_new() {
743
+ let exec_id = ExecutionId::from_string("exec_test");
744
+ let ctx = ExecutionContext::new(exec_id.clone());
745
+ assert_eq!(ctx.execution_id.as_str(), "exec_test");
746
+ assert!(ctx.step_id.is_none());
747
+ assert!(ctx.artifact_id.is_none());
748
+ assert!(ctx.parent.is_none());
749
+ assert!(ctx.tenant_id.is_none());
750
+ assert!(ctx.user_id.is_none());
751
+ }
752
+
753
+ #[test]
754
+ fn test_execution_context_with_step() {
755
+ let exec_id = ExecutionId::from_string("exec_test");
756
+ let step_id = StepId::from_string("step_test");
757
+ let ctx = ExecutionContext::new(exec_id).with_step(step_id.clone());
758
+ assert!(ctx.step_id.is_some());
759
+ assert_eq!(ctx.step_id.unwrap().as_str(), "step_test");
760
+ }
761
+
762
+ #[test]
763
+ fn test_execution_context_with_artifact() {
764
+ let exec_id = ExecutionId::from_string("exec_test");
765
+ let artifact_id = ArtifactId::from_string("artifact_test");
766
+ let ctx = ExecutionContext::new(exec_id).with_artifact(artifact_id);
767
+ assert!(ctx.artifact_id.is_some());
768
+ assert_eq!(ctx.artifact_id.unwrap().as_str(), "artifact_test");
769
+ }
770
+
771
+ #[test]
772
+ fn test_execution_context_with_parent() {
773
+ let exec_id = ExecutionId::from_string("exec_test");
774
+ let parent = ParentLink::from_user_message("msg_123");
775
+ let ctx = ExecutionContext::new(exec_id).with_parent(parent);
776
+ assert!(ctx.parent.is_some());
777
+ assert_eq!(ctx.parent.unwrap().parent_id, "msg_123");
778
+ }
779
+
780
+ #[test]
781
+ fn test_execution_context_with_tenant() {
782
+ let exec_id = ExecutionId::from_string("exec_test");
783
+ let tenant_id = TenantId::from_string("tenant_test");
784
+ let user_id = UserId::from_string("user_test");
785
+ let ctx = ExecutionContext::new(exec_id).with_tenant(tenant_id, Some(user_id));
786
+ assert!(ctx.tenant_id.is_some());
787
+ assert!(ctx.user_id.is_some());
788
+ assert_eq!(ctx.tenant_id.unwrap().as_str(), "tenant_test");
789
+ assert_eq!(ctx.user_id.unwrap().as_str(), "user_test");
790
+ }
791
+
792
+ #[test]
793
+ fn test_execution_context_builder_chain() {
794
+ let exec_id = ExecutionId::from_string("exec_chain");
795
+ let step_id = StepId::from_string("step_chain");
796
+ let artifact_id = ArtifactId::from_string("artifact_chain");
797
+ let parent = ParentLink::system();
798
+ let tenant_id = TenantId::from_string("tenant_chain");
799
+
800
+ let ctx = ExecutionContext::new(exec_id)
801
+ .with_step(step_id)
802
+ .with_artifact(artifact_id)
803
+ .with_parent(parent)
804
+ .with_tenant(tenant_id, None);
805
+
806
+ assert!(ctx.step_id.is_some());
807
+ assert!(ctx.artifact_id.is_some());
808
+ assert!(ctx.parent.is_some());
809
+ assert!(ctx.tenant_id.is_some());
810
+ assert!(ctx.user_id.is_none());
811
+ }
812
+
813
+ #[test]
814
+ fn test_execution_context_serde() {
815
+ let exec_id = ExecutionId::from_string("exec_serde");
816
+ let ctx = ExecutionContext::new(exec_id);
817
+ let json = serde_json::to_string(&ctx).unwrap();
818
+ let parsed: ExecutionContext = serde_json::from_str(&json).unwrap();
819
+ assert_eq!(ctx.execution_id.as_str(), parsed.execution_id.as_str());
820
+ }
821
+
822
+ // =========================================================================
823
+ // ExecutionEvent Tests
824
+ // =========================================================================
825
+
826
+ #[test]
827
+ fn test_execution_event_new() {
828
+ let ctx = ExecutionContext::new(ExecutionId::from_string("exec_test"));
829
+ let event = ExecutionEvent::new(ExecutionEventType::ExecutionStart, ctx);
830
+ assert!(event.event_id.starts_with("evt_"));
831
+ assert_eq!(event.event_type, ExecutionEventType::ExecutionStart);
832
+ assert!(event.duration_ms.is_none());
833
+ assert!(event.payload.is_none());
834
+ }
835
+
836
+ #[test]
837
+ fn test_execution_event_with_duration() {
838
+ let ctx = ExecutionContext::new(ExecutionId::from_string("exec_test"));
839
+ let event = ExecutionEvent::new(ExecutionEventType::StepEnd, ctx).with_duration(1500);
840
+ assert_eq!(event.duration_ms, Some(1500));
841
+ }
842
+
843
+ #[test]
844
+ fn test_execution_event_with_payload() {
845
+ let ctx = ExecutionContext::new(ExecutionId::from_string("exec_test"));
846
+ let payload = serde_json::json!({"key": "value"});
847
+ let event =
848
+ ExecutionEvent::new(ExecutionEventType::StepStart, ctx).with_payload(payload.clone());
849
+ assert!(event.payload.is_some());
850
+ assert_eq!(event.payload.unwrap(), payload);
851
+ }
852
+
853
+ #[test]
854
+ fn test_execution_event_execution_start() {
855
+ let exec_id = ExecutionId::from_string("exec_start");
856
+ let event = ExecutionEvent::execution_start(exec_id, None);
857
+ assert_eq!(event.event_type, ExecutionEventType::ExecutionStart);
858
+ assert!(event.context.parent.is_none());
859
+ }
860
+
861
+ #[test]
862
+ fn test_execution_event_execution_start_with_parent() {
863
+ let exec_id = ExecutionId::from_string("exec_start");
864
+ let parent = ParentLink::from_user_message("msg_trigger");
865
+ let event = ExecutionEvent::execution_start(exec_id, Some(parent));
866
+ assert_eq!(event.event_type, ExecutionEventType::ExecutionStart);
867
+ assert!(event.context.parent.is_some());
868
+ }
869
+
870
+ #[test]
871
+ fn test_execution_event_execution_end() {
872
+ let exec_id = ExecutionId::from_string("exec_end");
873
+ let event = ExecutionEvent::execution_end(exec_id, Some(5000));
874
+ assert_eq!(event.event_type, ExecutionEventType::ExecutionEnd);
875
+ assert_eq!(event.duration_ms, Some(5000));
876
+ }
877
+
878
+ #[test]
879
+ fn test_execution_event_step_start() {
880
+ let exec_id = ExecutionId::from_string("exec_step");
881
+ let step_id = StepId::from_string("step_start");
882
+ let event = ExecutionEvent::step_start(exec_id, step_id, StepType::LlmNode, "test_step");
883
+ assert_eq!(event.event_type, ExecutionEventType::StepStart);
884
+ assert!(event.payload.is_some());
885
+ let payload = event.payload.unwrap();
886
+ assert_eq!(payload["name"], "test_step");
887
+ }
888
+
889
+ #[test]
890
+ fn test_execution_event_step_end() {
891
+ let exec_id = ExecutionId::from_string("exec_step");
892
+ let step_id = StepId::from_string("step_end");
893
+ let event = ExecutionEvent::step_end(exec_id, step_id, 1000);
894
+ assert_eq!(event.event_type, ExecutionEventType::StepEnd);
895
+ assert_eq!(event.duration_ms, Some(1000));
896
+ }
897
+
898
+ #[test]
899
+ fn test_execution_event_artifact_created() {
900
+ let exec_id = ExecutionId::from_string("exec_artifact");
901
+ let step_id = StepId::from_string("step_artifact");
902
+ let artifact_id = ArtifactId::from_string("artifact_created");
903
+ let event = ExecutionEvent::artifact_created(exec_id, step_id, artifact_id, "code");
904
+ assert_eq!(event.event_type, ExecutionEventType::ArtifactCreated);
905
+ assert!(event.context.artifact_id.is_some());
906
+ assert!(event.payload.is_some());
907
+ assert_eq!(event.payload.unwrap()["artifact_type"], "code");
908
+ }
909
+
910
+ #[test]
911
+ fn test_execution_event_serde() {
912
+ let ctx = ExecutionContext::new(ExecutionId::from_string("exec_serde"));
913
+ let event = ExecutionEvent::new(ExecutionEventType::ExecutionStart, ctx);
914
+ let json = serde_json::to_string(&event).unwrap();
915
+ let parsed: ExecutionEvent = serde_json::from_str(&json).unwrap();
916
+ assert_eq!(event.event_type, parsed.event_type);
917
+ }
918
+
919
+ // =========================================================================
920
+ // DecisionType Tests
921
+ // =========================================================================
922
+
923
+ #[test]
924
+ fn test_decision_type_variants() {
925
+ let variants = vec![
926
+ DecisionType::Routing,
927
+ DecisionType::Branch,
928
+ DecisionType::Approval,
929
+ DecisionType::Escalation,
930
+ DecisionType::Retry,
931
+ DecisionType::Fallback,
932
+ DecisionType::PolicyEvaluation,
933
+ DecisionType::ToolSelection,
934
+ DecisionType::Rejection,
935
+ ];
936
+ for variant in variants {
937
+ let json = serde_json::to_string(&variant).unwrap();
938
+ let parsed: DecisionType = serde_json::from_str(&json).unwrap();
939
+ assert_eq!(variant, parsed);
940
+ }
941
+ }
942
+
943
+ #[test]
944
+ fn test_decision_type_equality() {
945
+ assert_eq!(DecisionType::Routing, DecisionType::Routing);
946
+ assert_ne!(DecisionType::Routing, DecisionType::Branch);
947
+ }
948
+
949
+ // =========================================================================
950
+ // DecisionInput Tests
951
+ // =========================================================================
952
+
953
+ #[test]
954
+ fn test_decision_input_creation() {
955
+ let input = DecisionInput {
956
+ facts: vec!["fact1".to_string(), "fact2".to_string()],
957
+ constraints: vec!["constraint1".to_string()],
958
+ evidence_ids: vec!["ev_123".to_string()],
959
+ };
960
+ assert_eq!(input.facts.len(), 2);
961
+ assert_eq!(input.constraints.len(), 1);
962
+ assert_eq!(input.evidence_ids.len(), 1);
963
+ }
964
+
965
+ #[test]
966
+ fn test_decision_input_serde() {
967
+ let input = DecisionInput {
968
+ facts: vec!["fact1".to_string()],
969
+ constraints: vec![],
970
+ evidence_ids: vec![],
971
+ };
972
+ let json = serde_json::to_string(&input).unwrap();
973
+ let parsed: DecisionInput = serde_json::from_str(&json).unwrap();
974
+ assert_eq!(input.facts, parsed.facts);
975
+ }
976
+
977
+ // =========================================================================
978
+ // DecisionAlternative Tests
979
+ // =========================================================================
980
+
981
+ #[test]
982
+ fn test_decision_alternative_creation() {
983
+ let alt = DecisionAlternative {
984
+ option: "option_b".to_string(),
985
+ score: 0.75,
986
+ rejected_reason: "Lower confidence".to_string(),
987
+ };
988
+ assert_eq!(alt.option, "option_b");
989
+ assert_eq!(alt.score, 0.75);
990
+ }
991
+
992
+ #[test]
993
+ fn test_decision_alternative_serde() {
994
+ let alt = DecisionAlternative {
995
+ option: "alt".to_string(),
996
+ score: 0.5,
997
+ rejected_reason: "reason".to_string(),
998
+ };
999
+ let json = serde_json::to_string(&alt).unwrap();
1000
+ let parsed: DecisionAlternative = serde_json::from_str(&json).unwrap();
1001
+ assert_eq!(alt.option, parsed.option);
1002
+ assert_eq!(alt.score, parsed.score);
1003
+ }
1004
+
1005
+ // =========================================================================
1006
+ // ModelContext Tests
1007
+ // =========================================================================
1008
+
1009
+ #[test]
1010
+ fn test_model_context_creation() {
1011
+ let ctx = ModelContext {
1012
+ provider: "anthropic".to_string(),
1013
+ model: "claude-3-opus".to_string(),
1014
+ version: Some("2024".to_string()),
1015
+ temperature: Some(0.7),
1016
+ max_tokens: Some(1000),
1017
+ };
1018
+ assert_eq!(ctx.provider, "anthropic");
1019
+ assert_eq!(ctx.model, "claude-3-opus");
1020
+ assert!(ctx.temperature.is_some());
1021
+ }
1022
+
1023
+ #[test]
1024
+ fn test_model_context_minimal() {
1025
+ let ctx = ModelContext {
1026
+ provider: "openai".to_string(),
1027
+ model: "gpt-4".to_string(),
1028
+ version: None,
1029
+ temperature: None,
1030
+ max_tokens: None,
1031
+ };
1032
+ assert!(ctx.version.is_none());
1033
+ assert!(ctx.temperature.is_none());
1034
+ }
1035
+
1036
+ #[test]
1037
+ fn test_model_context_serde() {
1038
+ let ctx = ModelContext {
1039
+ provider: "test".to_string(),
1040
+ model: "test-model".to_string(),
1041
+ version: None,
1042
+ temperature: Some(0.5),
1043
+ max_tokens: None,
1044
+ };
1045
+ let json = serde_json::to_string(&ctx).unwrap();
1046
+ let parsed: ModelContext = serde_json::from_str(&json).unwrap();
1047
+ assert_eq!(ctx.provider, parsed.provider);
1048
+ assert_eq!(ctx.temperature, parsed.temperature);
1049
+ }
1050
+
1051
+ // =========================================================================
1052
+ // DecisionRecord Tests
1053
+ // =========================================================================
1054
+
1055
+ #[test]
1056
+ fn test_decision_record_new() {
1057
+ let exec_id = ExecutionId::from_string("exec_decision");
1058
+ let input = DecisionInput {
1059
+ facts: vec!["fact".to_string()],
1060
+ constraints: vec![],
1061
+ evidence_ids: vec![],
1062
+ };
1063
+ let record = DecisionRecord::new(DecisionType::Routing, exec_id, "agent_a", 0.95, input);
1064
+ assert!(record.decision_id.starts_with("dec_"));
1065
+ assert_eq!(record.decision_type, DecisionType::Routing);
1066
+ assert_eq!(record.outcome, "agent_a");
1067
+ assert_eq!(record.confidence, 0.95);
1068
+ }
1069
+
1070
+ #[test]
1071
+ fn test_decision_record_with_step() {
1072
+ let exec_id = ExecutionId::from_string("exec_decision");
1073
+ let step_id = StepId::from_string("step_decision");
1074
+ let input = DecisionInput {
1075
+ facts: vec![],
1076
+ constraints: vec![],
1077
+ evidence_ids: vec![],
1078
+ };
1079
+ let record = DecisionRecord::new(DecisionType::Branch, exec_id, "yes", 0.8, input)
1080
+ .with_step(step_id);
1081
+ assert!(record.step_id.is_some());
1082
+ }
1083
+
1084
+ #[test]
1085
+ fn test_decision_record_with_alternatives() {
1086
+ let exec_id = ExecutionId::from_string("exec_decision");
1087
+ let input = DecisionInput {
1088
+ facts: vec![],
1089
+ constraints: vec![],
1090
+ evidence_ids: vec![],
1091
+ };
1092
+ let alternatives = vec![DecisionAlternative {
1093
+ option: "option_b".to_string(),
1094
+ score: 0.6,
1095
+ rejected_reason: "Lower score".to_string(),
1096
+ }];
1097
+ let record = DecisionRecord::new(DecisionType::Routing, exec_id, "option_a", 0.9, input)
1098
+ .with_alternatives(alternatives);
1099
+ assert!(record.alternatives.is_some());
1100
+ assert_eq!(record.alternatives.unwrap().len(), 1);
1101
+ }
1102
+
1103
+ #[test]
1104
+ fn test_decision_record_with_model_context() {
1105
+ let exec_id = ExecutionId::from_string("exec_decision");
1106
+ let input = DecisionInput {
1107
+ facts: vec![],
1108
+ constraints: vec![],
1109
+ evidence_ids: vec![],
1110
+ };
1111
+ let model_ctx = ModelContext {
1112
+ provider: "anthropic".to_string(),
1113
+ model: "claude".to_string(),
1114
+ version: None,
1115
+ temperature: None,
1116
+ max_tokens: None,
1117
+ };
1118
+ let record = DecisionRecord::new(DecisionType::Approval, exec_id, "approved", 1.0, input)
1119
+ .with_model_context(model_ctx);
1120
+ assert!(record.model_context.is_some());
1121
+ }
1122
+
1123
+ #[test]
1124
+ fn test_decision_record_with_reasoning() {
1125
+ let exec_id = ExecutionId::from_string("exec_decision");
1126
+ let input = DecisionInput {
1127
+ facts: vec![],
1128
+ constraints: vec![],
1129
+ evidence_ids: vec![],
1130
+ };
1131
+ let record =
1132
+ DecisionRecord::new(DecisionType::Escalation, exec_id, "escalated", 0.5, input)
1133
+ .with_reasoning("Confidence too low for autonomous action");
1134
+ assert!(record.reasoning.is_some());
1135
+ assert!(record.reasoning.unwrap().contains("Confidence"));
1136
+ }
1137
+
1138
+ #[test]
1139
+ fn test_decision_record_serde() {
1140
+ let exec_id = ExecutionId::from_string("exec_serde");
1141
+ let input = DecisionInput {
1142
+ facts: vec!["f".to_string()],
1143
+ constraints: vec![],
1144
+ evidence_ids: vec![],
1145
+ };
1146
+ let record = DecisionRecord::new(DecisionType::Retry, exec_id, "retry", 0.7, input);
1147
+ let json = serde_json::to_string(&record).unwrap();
1148
+ let parsed: DecisionRecord = serde_json::from_str(&json).unwrap();
1149
+ assert_eq!(record.decision_type, parsed.decision_type);
1150
+ }
1151
+
1152
+ // =========================================================================
1153
+ // ControlActor Tests
1154
+ // =========================================================================
1155
+
1156
+ #[test]
1157
+ fn test_control_actor_variants() {
1158
+ let variants = vec![
1159
+ ControlActor::System,
1160
+ ControlActor::User,
1161
+ ControlActor::Agent,
1162
+ ControlActor::PolicyEngine,
1163
+ ];
1164
+ for variant in variants {
1165
+ let json = serde_json::to_string(&variant).unwrap();
1166
+ let parsed: ControlActor = serde_json::from_str(&json).unwrap();
1167
+ assert_eq!(variant, parsed);
1168
+ }
1169
+ }
1170
+
1171
+ #[test]
1172
+ fn test_control_actor_equality() {
1173
+ assert_eq!(ControlActor::System, ControlActor::System);
1174
+ assert_ne!(ControlActor::System, ControlActor::User);
1175
+ }
1176
+
1177
+ // =========================================================================
1178
+ // ControlAction Tests
1179
+ // =========================================================================
1180
+
1181
+ #[test]
1182
+ fn test_control_action_variants() {
1183
+ let variants = vec![
1184
+ ControlAction::Stop,
1185
+ ControlAction::Pause,
1186
+ ControlAction::Resume,
1187
+ ControlAction::Cancel,
1188
+ ControlAction::Approve,
1189
+ ControlAction::Deny,
1190
+ ControlAction::Escalate,
1191
+ ];
1192
+ for variant in variants {
1193
+ let json = serde_json::to_string(&variant).unwrap();
1194
+ let parsed: ControlAction = serde_json::from_str(&json).unwrap();
1195
+ assert_eq!(variant, parsed);
1196
+ }
1197
+ }
1198
+
1199
+ // =========================================================================
1200
+ // ControlOutcome Tests
1201
+ // =========================================================================
1202
+
1203
+ #[test]
1204
+ fn test_control_outcome_variants() {
1205
+ let variants = vec![
1206
+ ControlOutcome::Allowed,
1207
+ ControlOutcome::Denied,
1208
+ ControlOutcome::Modified,
1209
+ ControlOutcome::Escalated,
1210
+ ];
1211
+ for variant in variants {
1212
+ let json = serde_json::to_string(&variant).unwrap();
1213
+ let parsed: ControlOutcome = serde_json::from_str(&json).unwrap();
1214
+ assert_eq!(variant, parsed);
1215
+ }
1216
+ }
1217
+
1218
+ // =========================================================================
1219
+ // ControlEvent Tests
1220
+ // =========================================================================
1221
+
1222
+ #[test]
1223
+ fn test_control_event_new() {
1224
+ let exec_id = ExecutionId::from_string("exec_ctrl");
1225
+ let event = ControlEvent::new(
1226
+ ControlActor::User,
1227
+ ControlAction::Pause,
1228
+ exec_id,
1229
+ "User requested pause",
1230
+ ControlOutcome::Allowed,
1231
+ );
1232
+ assert!(event.event_id.starts_with("ctrl_"));
1233
+ assert_eq!(event.actor, ControlActor::User);
1234
+ assert_eq!(event.action, ControlAction::Pause);
1235
+ assert_eq!(event.outcome, ControlOutcome::Allowed);
1236
+ }
1237
+
1238
+ #[test]
1239
+ fn test_control_event_with_step() {
1240
+ let exec_id = ExecutionId::from_string("exec_ctrl");
1241
+ let step_id = StepId::from_string("step_ctrl");
1242
+ let event = ControlEvent::new(
1243
+ ControlActor::System,
1244
+ ControlAction::Cancel,
1245
+ exec_id,
1246
+ "Timeout",
1247
+ ControlOutcome::Allowed,
1248
+ )
1249
+ .with_step(step_id);
1250
+ assert!(event.step_id.is_some());
1251
+ }
1252
+
1253
+ #[test]
1254
+ fn test_control_event_with_actor_id() {
1255
+ let exec_id = ExecutionId::from_string("exec_ctrl");
1256
+ let event = ControlEvent::new(
1257
+ ControlActor::User,
1258
+ ControlAction::Approve,
1259
+ exec_id,
1260
+ "Approved by admin",
1261
+ ControlOutcome::Allowed,
1262
+ )
1263
+ .with_actor_id("user_admin_123");
1264
+ assert!(event.actor_id.is_some());
1265
+ assert_eq!(event.actor_id.unwrap(), "user_admin_123");
1266
+ }
1267
+
1268
+ #[test]
1269
+ fn test_control_event_pause_factory() {
1270
+ let exec_id = ExecutionId::from_string("exec_pause");
1271
+ let event = ControlEvent::pause(exec_id, ControlActor::User, "Pausing for review");
1272
+ assert_eq!(event.action, ControlAction::Pause);
1273
+ assert_eq!(event.actor, ControlActor::User);
1274
+ assert_eq!(event.outcome, ControlOutcome::Allowed);
1275
+ }
1276
+
1277
+ #[test]
1278
+ fn test_control_event_resume_factory() {
1279
+ let exec_id = ExecutionId::from_string("exec_resume");
1280
+ let event = ControlEvent::resume(exec_id, ControlActor::User, "Resuming after review");
1281
+ assert_eq!(event.action, ControlAction::Resume);
1282
+ }
1283
+
1284
+ #[test]
1285
+ fn test_control_event_cancel_factory() {
1286
+ let exec_id = ExecutionId::from_string("exec_cancel");
1287
+ let event = ControlEvent::cancel(exec_id, ControlActor::System, "System timeout");
1288
+ assert_eq!(event.action, ControlAction::Cancel);
1289
+ assert_eq!(event.actor, ControlActor::System);
1290
+ }
1291
+
1292
+ #[test]
1293
+ fn test_control_event_serde() {
1294
+ let exec_id = ExecutionId::from_string("exec_serde");
1295
+ let event = ControlEvent::pause(exec_id, ControlActor::Agent, "Self-regulation");
1296
+ let json = serde_json::to_string(&event).unwrap();
1297
+ let parsed: ControlEvent = serde_json::from_str(&json).unwrap();
1298
+ assert_eq!(event.action, parsed.action);
1299
+ assert_eq!(event.actor, parsed.actor);
1300
+ }
1301
+
1302
+ // =========================================================================
1303
+ // Legacy Event Tests
1304
+ // =========================================================================
1305
+
1306
+ #[test]
1307
+ fn test_event_new() {
1308
+ let run_id = ExecutionId::from_string("exec_legacy");
1309
+ let event = Event::new(run_id, "test_author");
1310
+ assert!(event.id.starts_with("evt_"));
1311
+ assert_eq!(event.author, "test_author");
1312
+ assert!(!event.is_final);
1313
+ assert!(event.content.is_none());
1314
+ assert!(event.node_id.is_none());
1315
+ }
1316
+
1317
+ #[test]
1318
+ fn test_event_with_content() {
1319
+ let run_id = ExecutionId::from_string("exec_legacy");
1320
+ let event = Event::new(run_id, "author").with_content("Hello, world!");
1321
+ assert!(event.content.is_some());
1322
+ assert_eq!(event.content.unwrap(), "Hello, world!");
1323
+ }
1324
+
1325
+ #[test]
1326
+ fn test_event_with_node() {
1327
+ let run_id = ExecutionId::from_string("exec_legacy");
1328
+ let node_id = StepId::from_string("step_legacy");
1329
+ let event = Event::new(run_id, "author").with_node(node_id.clone());
1330
+ assert!(event.node_id.is_some());
1331
+ assert_eq!(event.node_id.unwrap().as_str(), "step_legacy");
1332
+ }
1333
+
1334
+ #[test]
1335
+ fn test_event_final_response() {
1336
+ let run_id = ExecutionId::from_string("exec_legacy");
1337
+ let event = Event::new(run_id, "author").final_response();
1338
+ assert!(event.is_final);
1339
+ }
1340
+
1341
+ #[test]
1342
+ fn test_event_builder_chain() {
1343
+ let run_id = ExecutionId::from_string("exec_chain");
1344
+ let node_id = StepId::from_string("step_chain");
1345
+ let event = Event::new(run_id, "test")
1346
+ .with_content("Content here")
1347
+ .with_node(node_id)
1348
+ .final_response();
1349
+ assert!(event.content.is_some());
1350
+ assert!(event.node_id.is_some());
1351
+ assert!(event.is_final);
1352
+ }
1353
+
1354
+ #[test]
1355
+ fn test_event_serde() {
1356
+ let run_id = ExecutionId::from_string("exec_serde");
1357
+ let event = Event::new(run_id, "author").with_content("test");
1358
+ let json = serde_json::to_string(&event).unwrap();
1359
+ let parsed: Event = serde_json::from_str(&json).unwrap();
1360
+ assert_eq!(event.author, parsed.author);
1361
+ }
1362
+
1363
+ // =========================================================================
1364
+ // Event ID Format Tests
1365
+ // =========================================================================
1366
+
1367
+ #[test]
1368
+ fn test_event_id_format() {
1369
+ let ctx = ExecutionContext::new(ExecutionId::from_string("exec_test"));
1370
+ let event = ExecutionEvent::new(ExecutionEventType::ExecutionStart, ctx);
1371
+ assert!(event.event_id.starts_with("evt_"));
1372
+ // evt_ (4 chars) + KSUID (27 chars) = 31 chars
1373
+ assert_eq!(event.event_id.len(), 31);
1374
+ }
1375
+
1376
+ #[test]
1377
+ fn test_decision_id_format() {
1378
+ let exec_id = ExecutionId::from_string("exec_test");
1379
+ let input = DecisionInput {
1380
+ facts: vec![],
1381
+ constraints: vec![],
1382
+ evidence_ids: vec![],
1383
+ };
1384
+ let record = DecisionRecord::new(DecisionType::Routing, exec_id, "outcome", 0.9, input);
1385
+ assert!(record.decision_id.starts_with("dec_"));
1386
+ }
1387
+
1388
+ #[test]
1389
+ fn test_control_event_id_format() {
1390
+ let exec_id = ExecutionId::from_string("exec_test");
1391
+ let event = ControlEvent::pause(exec_id, ControlActor::User, "test");
1392
+ assert!(event.event_id.starts_with("ctrl_"));
1393
+ }
1394
+ }