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,148 @@
1
+ //! Token Counter
2
+ //!
3
+ //! Token counting using tiktoken for accurate context management.
4
+
5
+ use thiserror::Error;
6
+ use tiktoken_rs::{cl100k_base, CoreBPE};
7
+
8
+ /// Token counter errors
9
+ #[derive(Debug, Error)]
10
+ pub enum TokenCounterError {
11
+ #[error("Failed to initialize tokenizer: {0}")]
12
+ InitError(String),
13
+ }
14
+
15
+ /// Token counter using tiktoken
16
+ ///
17
+ /// Uses cl100k_base encoding (GPT-4, GPT-3.5-turbo, text-embedding-ada-002)
18
+ pub struct TokenCounter {
19
+ bpe: CoreBPE,
20
+ }
21
+
22
+ impl TokenCounter {
23
+ /// Create a new token counter with cl100k_base encoding
24
+ pub fn new() -> Result<Self, TokenCounterError> {
25
+ let bpe = cl100k_base().map_err(|e| TokenCounterError::InitError(e.to_string()))?;
26
+ Ok(Self { bpe })
27
+ }
28
+
29
+ /// Count tokens in a string
30
+ pub fn count(&self, text: &str) -> usize {
31
+ self.bpe.encode_with_special_tokens(text).len()
32
+ }
33
+
34
+ /// Count tokens for a chat message (includes role overhead)
35
+ ///
36
+ /// Approximates the token overhead for chat formatting:
37
+ /// - Each message has ~4 tokens overhead (role, separators)
38
+ /// - System messages have additional overhead
39
+ pub fn count_message(&self, role: &str, content: &str) -> usize {
40
+ let content_tokens = self.count(content);
41
+ let role_overhead = match role {
42
+ "system" => 4,
43
+ "user" => 4,
44
+ "assistant" => 4,
45
+ "function" | "tool" => 5,
46
+ _ => 4,
47
+ };
48
+ content_tokens + role_overhead
49
+ }
50
+
51
+ /// Count tokens for multiple messages
52
+ pub fn count_messages(&self, messages: &[(String, String)]) -> usize {
53
+ messages
54
+ .iter()
55
+ .map(|(role, content)| self.count_message(role, content))
56
+ .sum::<usize>()
57
+ + 3 // priming tokens
58
+ }
59
+
60
+ /// Truncate text to fit within a token limit
61
+ ///
62
+ /// Returns the truncated text and the actual token count.
63
+ pub fn truncate(&self, text: &str, max_tokens: usize) -> (String, usize) {
64
+ let tokens = self.bpe.encode_with_special_tokens(text);
65
+ if tokens.len() <= max_tokens {
66
+ return (text.to_string(), tokens.len());
67
+ }
68
+
69
+ let truncated_tokens = &tokens[..max_tokens];
70
+ let truncated_text = self.bpe.decode(truncated_tokens.to_vec())
71
+ .unwrap_or_else(|_| text[..text.len() / 2].to_string());
72
+
73
+ (truncated_text, max_tokens)
74
+ }
75
+
76
+ /// Split text into chunks of approximately equal token size
77
+ pub fn chunk(&self, text: &str, chunk_size: usize) -> Vec<String> {
78
+ let tokens = self.bpe.encode_with_special_tokens(text);
79
+ let mut chunks = Vec::new();
80
+
81
+ for chunk_tokens in tokens.chunks(chunk_size) {
82
+ if let Ok(chunk_text) = self.bpe.decode(chunk_tokens.to_vec()) {
83
+ chunks.push(chunk_text);
84
+ }
85
+ }
86
+
87
+ chunks
88
+ }
89
+
90
+ /// Estimate if text will fit within budget
91
+ pub fn will_fit(&self, text: &str, budget: usize) -> bool {
92
+ self.count(text) <= budget
93
+ }
94
+ }
95
+
96
+ impl Default for TokenCounter {
97
+ fn default() -> Self {
98
+ Self::new().expect("Failed to initialize default token counter")
99
+ }
100
+ }
101
+
102
+ #[cfg(test)]
103
+ mod tests {
104
+ use super::*;
105
+
106
+ #[test]
107
+ fn test_count_tokens() {
108
+ let counter = TokenCounter::new().unwrap();
109
+
110
+ // Empty string
111
+ assert_eq!(counter.count(""), 0);
112
+
113
+ // Simple text
114
+ let count = counter.count("Hello, world!");
115
+ assert!(count > 0);
116
+ assert!(count < 10);
117
+ }
118
+
119
+ #[test]
120
+ fn test_count_message() {
121
+ let counter = TokenCounter::new().unwrap();
122
+
123
+ let content_tokens = counter.count("Hello");
124
+ let message_tokens = counter.count_message("user", "Hello");
125
+
126
+ // Message should have overhead
127
+ assert!(message_tokens > content_tokens);
128
+ }
129
+
130
+ #[test]
131
+ fn test_truncate() {
132
+ let counter = TokenCounter::new().unwrap();
133
+
134
+ let long_text = "This is a long text that we want to truncate to a smaller size.";
135
+ let (truncated, count) = counter.truncate(long_text, 5);
136
+
137
+ assert!(count <= 5);
138
+ assert!(truncated.len() < long_text.len());
139
+ }
140
+
141
+ #[test]
142
+ fn test_will_fit() {
143
+ let counter = TokenCounter::new().unwrap();
144
+
145
+ assert!(counter.will_fit("Hello", 100));
146
+ assert!(!counter.will_fit("Hello ".repeat(1000).as_str(), 10));
147
+ }
148
+ }
@@ -0,0 +1,372 @@
1
+ //! Context Window Management
2
+ //!
3
+ //! The ContextWindow manages all segments within token budget limits.
4
+ //!
5
+ //! @see packages/enact-schemas/src/context.schemas.ts
6
+
7
+ use crate::budget::{BudgetHealth, ContextBudget};
8
+ use crate::compactor::{CompactionResult, CompactionStrategyType, Compactor};
9
+ use crate::segment::{ContextSegment, ContextSegmentType};
10
+ use crate::token_counter::TokenCounter;
11
+ use chrono::{DateTime, Utc};
12
+ use enact_core::kernel::ExecutionId;
13
+ use serde::{Deserialize, Serialize};
14
+ use std::time::Instant;
15
+ use thiserror::Error;
16
+
17
+ /// Context window errors
18
+ #[derive(Debug, Error)]
19
+ pub enum ContextWindowError {
20
+ #[error("Token counter error: {0}")]
21
+ TokenCounter(String),
22
+
23
+ #[error("Budget exceeded: need {needed} tokens, only {available} available")]
24
+ BudgetExceeded { needed: usize, available: usize },
25
+
26
+ #[error("Segment budget exceeded for {segment_type:?}: need {needed}, max {max}")]
27
+ SegmentBudgetExceeded {
28
+ segment_type: ContextSegmentType,
29
+ needed: usize,
30
+ max: usize,
31
+ },
32
+
33
+ #[error("Compaction failed: {0}")]
34
+ CompactionFailed(String),
35
+ }
36
+
37
+ /// Context window state
38
+ ///
39
+ /// Matches `contextWindowStateSchema` in @enact/schemas
40
+ #[derive(Debug, Clone, Serialize, Deserialize)]
41
+ #[serde(rename_all = "camelCase")]
42
+ pub struct ContextWindowState {
43
+ /// Execution ID
44
+ pub execution_id: ExecutionId,
45
+
46
+ /// All segments currently in context
47
+ pub segments: Vec<ContextSegment>,
48
+
49
+ /// Current budget state
50
+ pub budget: ContextBudget,
51
+
52
+ /// Compaction history
53
+ pub compaction_history: Vec<CompactionResult>,
54
+
55
+ /// Number of compactions performed
56
+ pub compaction_count: u32,
57
+
58
+ /// Total tokens saved by compaction
59
+ pub total_tokens_saved: usize,
60
+
61
+ /// Current health status
62
+ pub health: BudgetHealth,
63
+
64
+ /// Last updated timestamp
65
+ pub updated_at: DateTime<Utc>,
66
+ }
67
+
68
+ /// Context Window - manages segments within budget
69
+ pub struct ContextWindow {
70
+ /// Execution ID
71
+ execution_id: ExecutionId,
72
+
73
+ /// All segments in the context
74
+ segments: Vec<ContextSegment>,
75
+
76
+ /// Token budget
77
+ budget: ContextBudget,
78
+
79
+ /// Token counter
80
+ token_counter: TokenCounter,
81
+
82
+ /// Compaction history
83
+ compaction_history: Vec<CompactionResult>,
84
+
85
+ /// Next sequence number
86
+ next_sequence: u64,
87
+ }
88
+
89
+ impl ContextWindow {
90
+ /// Create a new context window
91
+ pub fn new(budget: ContextBudget) -> Result<Self, ContextWindowError> {
92
+ let token_counter = TokenCounter::new()
93
+ .map_err(|e| ContextWindowError::TokenCounter(e.to_string()))?;
94
+
95
+ Ok(Self {
96
+ execution_id: budget.execution_id.clone(),
97
+ segments: Vec::new(),
98
+ budget,
99
+ token_counter,
100
+ compaction_history: Vec::new(),
101
+ next_sequence: 0,
102
+ })
103
+ }
104
+
105
+ /// Create with a preset budget
106
+ pub fn with_preset_gpt4_128k(execution_id: ExecutionId) -> Result<Self, ContextWindowError> {
107
+ Self::new(ContextBudget::preset_gpt4_128k(execution_id))
108
+ }
109
+
110
+ /// Create with Claude 200K preset
111
+ pub fn with_preset_claude_200k(execution_id: ExecutionId) -> Result<Self, ContextWindowError> {
112
+ Self::new(ContextBudget::preset_claude_200k(execution_id))
113
+ }
114
+
115
+ /// Create with default (8K) preset
116
+ pub fn with_preset_default(execution_id: ExecutionId) -> Result<Self, ContextWindowError> {
117
+ Self::new(ContextBudget::preset_default(execution_id))
118
+ }
119
+
120
+ /// Get the execution ID
121
+ pub fn execution_id(&self) -> &ExecutionId {
122
+ &self.execution_id
123
+ }
124
+
125
+ /// Get all segments
126
+ pub fn segments(&self) -> &[ContextSegment] {
127
+ &self.segments
128
+ }
129
+
130
+ /// Get the budget
131
+ pub fn budget(&self) -> &ContextBudget {
132
+ &self.budget
133
+ }
134
+
135
+ /// Get mutable budget
136
+ pub fn budget_mut(&mut self) -> &mut ContextBudget {
137
+ &mut self.budget
138
+ }
139
+
140
+ /// Count tokens for text
141
+ pub fn count_tokens(&self, text: &str) -> usize {
142
+ self.token_counter.count(text)
143
+ }
144
+
145
+ /// Add a segment with automatic token counting
146
+ pub fn add_segment_auto(&mut self, mut segment: ContextSegment) -> Result<(), ContextWindowError> {
147
+ // Count tokens if not already set
148
+ if segment.token_count == 0 {
149
+ segment.token_count = self.token_counter.count(&segment.content);
150
+ }
151
+
152
+ self.add_segment(segment)
153
+ }
154
+
155
+ /// Add a segment to the context window
156
+ pub fn add_segment(&mut self, mut segment: ContextSegment) -> Result<(), ContextWindowError> {
157
+ // Check segment budget
158
+ if let Some(seg_budget) = self.budget.get_segment(segment.segment_type) {
159
+ let new_usage = seg_budget.current_tokens + segment.token_count;
160
+ if new_usage > seg_budget.max_tokens {
161
+ return Err(ContextWindowError::SegmentBudgetExceeded {
162
+ segment_type: segment.segment_type,
163
+ needed: segment.token_count,
164
+ max: seg_budget.max_tokens - seg_budget.current_tokens,
165
+ });
166
+ }
167
+ }
168
+
169
+ // Check total budget
170
+ let new_total = self.budget.used_tokens + segment.token_count;
171
+ if new_total > self.budget.available_tokens {
172
+ return Err(ContextWindowError::BudgetExceeded {
173
+ needed: segment.token_count,
174
+ available: self.budget.remaining(),
175
+ });
176
+ }
177
+
178
+ // Assign sequence number
179
+ segment.sequence = self.next_sequence;
180
+ self.next_sequence += 1;
181
+
182
+ // Update budget
183
+ self.budget.add_tokens(segment.segment_type, segment.token_count);
184
+
185
+ // Add segment
186
+ self.segments.push(segment);
187
+
188
+ Ok(())
189
+ }
190
+
191
+ /// Remove a segment by ID
192
+ pub fn remove_segment(&mut self, segment_id: &str) -> bool {
193
+ if let Some(pos) = self.segments.iter().position(|s| s.id == segment_id) {
194
+ let segment = self.segments.remove(pos);
195
+ self.budget.remove_tokens(segment.segment_type, segment.token_count);
196
+ true
197
+ } else {
198
+ false
199
+ }
200
+ }
201
+
202
+ /// Get segments of a specific type
203
+ pub fn segments_of_type(&self, segment_type: ContextSegmentType) -> Vec<&ContextSegment> {
204
+ self.segments
205
+ .iter()
206
+ .filter(|s| s.segment_type == segment_type)
207
+ .collect()
208
+ }
209
+
210
+ /// Total tokens currently used
211
+ pub fn used_tokens(&self) -> usize {
212
+ self.budget.used_tokens
213
+ }
214
+
215
+ /// Remaining tokens available
216
+ pub fn remaining_tokens(&self) -> usize {
217
+ self.budget.remaining()
218
+ }
219
+
220
+ /// Check if the window needs compaction
221
+ pub fn needs_compaction(&self) -> bool {
222
+ self.budget.is_warning()
223
+ }
224
+
225
+ /// Check if the window is in critical state
226
+ pub fn is_critical(&self) -> bool {
227
+ self.budget.is_critical()
228
+ }
229
+
230
+ /// Get health status
231
+ pub fn health(&self) -> BudgetHealth {
232
+ self.budget.health()
233
+ }
234
+
235
+ /// Compact the context using the given compactor
236
+ pub fn compact(&mut self, compactor: &Compactor) -> Result<CompactionResult, ContextWindowError> {
237
+ let start = Instant::now();
238
+ let tokens_before = self.budget.used_tokens;
239
+
240
+ let result = match compactor.strategy().strategy_type {
241
+ CompactionStrategyType::Truncate => {
242
+ compactor.compact_truncate(&mut self.segments, tokens_before)
243
+ }
244
+ CompactionStrategyType::SlidingWindow => {
245
+ compactor.compact_sliding_window(&mut self.segments)
246
+ }
247
+ _ => {
248
+ // Other strategies not implemented yet
249
+ return Err(ContextWindowError::CompactionFailed(
250
+ format!("Strategy {:?} not implemented", compactor.strategy().strategy_type)
251
+ ));
252
+ }
253
+ };
254
+
255
+ let duration_ms = start.elapsed().as_millis() as u64;
256
+
257
+ match result {
258
+ Ok(tokens_removed) => {
259
+ // Recalculate budget
260
+ self.recalculate_budget();
261
+
262
+ let tokens_after = self.budget.used_tokens;
263
+ let segments_compacted = (tokens_removed > 0) as usize;
264
+
265
+ let compaction_result = CompactionResult::success(
266
+ self.execution_id.clone(),
267
+ compactor.strategy().strategy_type,
268
+ tokens_before,
269
+ tokens_after,
270
+ segments_compacted,
271
+ duration_ms,
272
+ );
273
+
274
+ self.compaction_history.push(compaction_result.clone());
275
+ Ok(compaction_result)
276
+ }
277
+ Err(e) => {
278
+ let compaction_result = CompactionResult::failure(
279
+ self.execution_id.clone(),
280
+ compactor.strategy().strategy_type,
281
+ tokens_before,
282
+ e.to_string(),
283
+ duration_ms,
284
+ );
285
+
286
+ self.compaction_history.push(compaction_result.clone());
287
+ Err(ContextWindowError::CompactionFailed(e.to_string()))
288
+ }
289
+ }
290
+ }
291
+
292
+ /// Recalculate budget from current segments
293
+ fn recalculate_budget(&mut self) {
294
+ // Reset all segment budgets
295
+ for seg_budget in &mut self.budget.segments {
296
+ seg_budget.current_tokens = 0;
297
+ }
298
+
299
+ // Sum up tokens by segment type
300
+ for segment in &self.segments {
301
+ self.budget.add_tokens(segment.segment_type, segment.token_count);
302
+ }
303
+ }
304
+
305
+ /// Build the context as a single string (for LLM calls)
306
+ pub fn build_context(&self) -> String {
307
+ let mut parts: Vec<&str> = Vec::new();
308
+
309
+ // Sort segments by sequence
310
+ let mut sorted: Vec<&ContextSegment> = self.segments.iter().collect();
311
+ sorted.sort_by_key(|s| s.sequence);
312
+
313
+ for segment in sorted {
314
+ parts.push(&segment.content);
315
+ }
316
+
317
+ parts.join("\n\n")
318
+ }
319
+
320
+ /// Get the current state (for serialization)
321
+ pub fn state(&self) -> ContextWindowState {
322
+ ContextWindowState {
323
+ execution_id: self.execution_id.clone(),
324
+ segments: self.segments.clone(),
325
+ budget: self.budget.clone(),
326
+ compaction_history: self.compaction_history.clone(),
327
+ compaction_count: self.compaction_history.len() as u32,
328
+ total_tokens_saved: self.compaction_history.iter().map(|r| r.tokens_saved).sum(),
329
+ health: self.budget.health(),
330
+ updated_at: Utc::now(),
331
+ }
332
+ }
333
+ }
334
+
335
+ #[cfg(test)]
336
+ mod tests {
337
+ use super::*;
338
+
339
+ fn test_execution_id() -> ExecutionId {
340
+ ExecutionId::new()
341
+ }
342
+
343
+ #[test]
344
+ fn test_create_window() {
345
+ let budget = ContextBudget::preset_default(test_execution_id());
346
+ let window = ContextWindow::new(budget).unwrap();
347
+
348
+ assert_eq!(window.used_tokens(), 0);
349
+ assert!(window.remaining_tokens() > 0);
350
+ }
351
+
352
+ #[test]
353
+ fn test_add_segment() {
354
+ let budget = ContextBudget::preset_default(test_execution_id());
355
+ let mut window = ContextWindow::new(budget).unwrap();
356
+
357
+ let segment = ContextSegment::system("You are a helpful assistant.", 10);
358
+ window.add_segment(segment).unwrap();
359
+
360
+ assert_eq!(window.segments().len(), 1);
361
+ assert_eq!(window.used_tokens(), 10);
362
+ }
363
+
364
+ #[test]
365
+ fn test_health_tracking() {
366
+ let budget = ContextBudget::preset_default(test_execution_id());
367
+ let window = ContextWindow::new(budget).unwrap();
368
+
369
+ assert_eq!(window.health(), BudgetHealth::Healthy);
370
+ assert!(!window.needs_compaction());
371
+ }
372
+ }
@@ -0,0 +1,42 @@
1
+ [package]
2
+ name = "enact-core"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ description = "Core agent runtime for Enact - Graph-Native AI agents"
7
+ repository.workspace = true
8
+ homepage.workspace = true
9
+ keywords = ["agent", "runtime", "ai", "llm", "graph"]
10
+ categories.workspace = true
11
+
12
+ [dependencies]
13
+ tokio.workspace = true
14
+ async-trait.workspace = true
15
+ async-stream.workspace = true
16
+ futures.workspace = true
17
+ serde.workspace = true
18
+ serde_json.workspace = true
19
+ tracing.workspace = true
20
+ uuid.workspace = true
21
+ svix-ksuid.workspace = true
22
+ chrono.workspace = true
23
+ thiserror.workspace = true
24
+ anyhow.workspace = true
25
+ reqwest.workspace = true
26
+ tokio-util.workspace = true
27
+
28
+ # Compression & Hashing (feat-04: Artifact Lifecycle)
29
+ miniz_oxide.workspace = true
30
+ sha2.workspace = true
31
+ hex.workspace = true
32
+ aes-gcm.workspace = true
33
+ rand.workspace = true
34
+
35
+ serde_yaml = "0.9.34"
36
+ urlencoding = { workspace = true }
37
+ html-escape = { workspace = true }
38
+ regex = { workspace = true }
39
+
40
+ [dev-dependencies]
41
+ tokio = { workspace = true, features = ["test-util"] }
42
+ tempfile.workspace = true
@@ -0,0 +1,98 @@
1
+ # enact-core
2
+
3
+ `enact-core` is the core graph-first agent runtime crate for the Enact
4
+ platform. It provides the execution kernel, graph engine, and streaming
5
+ infrastructure for deterministic, auditable agent execution.
6
+
7
+ ## Key Features
8
+
9
+ ### Execution Kernel
10
+ - **Reducer-based state machine** - Pure function state transitions for determinism
11
+ - **Tenant isolation** - `TenantContext` required for all executions
12
+ - **Agentic DAG** - Dynamic step discovery with policy-bounded execution
13
+ - **Parallel step execution** - Independent steps execute concurrently via `tokio::join!`
14
+ - **Circular graph detection** - Topological sort validation at compile time
15
+
16
+ ### Mid-Execution Control
17
+ - **Inbox integration** - Inject guidance, evidence, and control signals mid-execution
18
+ - **Async cancellation** - Cooperative cancellation via `CancellationToken`
19
+ - **Checkpointing** - Save and restore execution state
20
+
21
+ ### Content Protection
22
+ - **ProtectedEventEmitter** - Optional content protection pipeline
23
+ - **PII detection** - Via `enact-guardrails` integration
24
+ - **Context-aware masking** - Different protection based on destination
25
+
26
+ ### Tool Execution
27
+ - **ToolExecutor** - Policy-aware tool execution
28
+ - **Policy decision events** - All tool policy decisions emitted for audit trail
29
+ - **Trust levels** - Untrusted, Low, Medium, High, System
30
+
31
+ ### Observability
32
+ - **StreamEvent** - AI SDK compatible wire format with `data-*` prefix
33
+ - **EventStore** - Append-only event persistence
34
+ - **Artifact lifecycle** - First-class artifacts with deterministic IDs
35
+
36
+ ## Module Structure
37
+
38
+ ```
39
+ src/
40
+ ├── kernel/ # Execution kernel (state machine, reducer, events)
41
+ │ ├── kernel.rs # ExecutionKernel entry point
42
+ │ ├── reducer.rs # Pure state transitions
43
+ │ ├── event.rs # ExecutionEvent types
44
+ │ ├── error.rs # Error taxonomy with retry policies
45
+ │ ├── execution_state.rs # ExecutionState, WaitReason
46
+ │ ├── interrupt.rs # Interrupt handling for HITL
47
+ │ └── artifact/ # Artifact lifecycle (feat-04)
48
+ ├── context/ # TenantContext, RuntimeContext
49
+ ├── flow/ # Execution semantics (sequential, parallel, conditional)
50
+ ├── callable/ # Callable trait, LlmCallable
51
+ ├── policy/ # ExecutionPolicy, ToolPolicy, TenantPolicy
52
+ ├── streaming/ # SSE streaming, EventStore, ProtectedEventEmitter
53
+ ├── graph/ # StateGraph, CompiledGraph, Node
54
+ ├── tool/ # Tool trait, ToolExecutor, policy enforcement
55
+ ├── inbox/ # Mid-execution guidance (INV-INBOX-*)
56
+ ├── providers/ # ModelProvider trait
57
+ ├── telemetry/ # OpenTelemetry integration
58
+ └── runner/ # Thin wiring shell
59
+ ```
60
+
61
+ ## Usage
62
+
63
+ ```rust
64
+ use enact_core::prelude::*;
65
+ use enact_core::context::TenantContext;
66
+
67
+ // Create kernel with required tenant context
68
+ let tenant = TenantContext::new(TenantId::from("tenant_123"));
69
+ let kernel = ExecutionKernel::new(tenant)
70
+ .with_inbox(Arc::new(InMemoryInboxStore::new()))
71
+ .with_artifact_store(Arc::new(InMemoryArtifactStore::new()));
72
+
73
+ // Compile and execute graph
74
+ let graph = StateGraph::new("my_agent")
75
+ .add_node("step1", my_callable)
76
+ .add_edge("start", "step1");
77
+
78
+ let compiled = graph.compile()?;
79
+ let result = kernel.execute_graph(&compiled, input).await?;
80
+ ```
81
+
82
+ ## Invariants
83
+
84
+ See [04-KERNEL_INVARIANTS.md](../../docs/TECHNICAL/04-KERNEL_INVARIANTS.md) for:
85
+ - Execution invariants (determinism, replay, tenant isolation)
86
+ - Graph execution invariants (parallel execution, cycle detection)
87
+ - Inbox invariants (INV-INBOX-001 through INV-INBOX-004)
88
+ - Cancellation invariants (cooperative, propagating, observable)
89
+ - Artifact lifecycle invariants (first-class, deterministic IDs, immutable)
90
+ - Content protection invariants (protected emission, bypass for control)
91
+ - Tool policy invariants (auditable decisions, ToolExecutor enforcement)
92
+
93
+ ## Related Documentation
94
+
95
+ - [02-ENACT-CORE-ARCHITECTURE.md](../../docs/TECHNICAL/02-ENACT-CORE-ARCHITECTURE.md) - Full architecture
96
+ - [07-EXECUTION_LIFECYCLE.md](../../docs/TECHNICAL/07-EXECUTION_LIFECYCLE.md) - Execution lifecycle
97
+ - [31-MID-EXECUTION-GUIDANCE.md](../../docs/TECHNICAL/31-MID-EXECUTION-GUIDANCE.md) - Inbox system
98
+ - [17-GUARDRAILS-PROTECTION.md](../../docs/TECHNICAL/17-GUARDRAILS-PROTECTION.md) - PII protection