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,280 @@
1
+ //! Protected Runner
2
+ //!
3
+ //! Wraps Runner with input processor pipeline to validate/transform input
4
+ //! before execution begins.
5
+ //!
6
+ //! @see docs/TECHNICAL/17-GUARDRAILS-PROTECTION.md
7
+ //! @see docs/TECHNICAL/25-STREAM-PROCESSORS.md
8
+
9
+ use super::execution_runner::Runner;
10
+ use crate::callable::Callable;
11
+ use crate::graph::{CheckpointStore, CompiledGraph, NodeState};
12
+ use crate::kernel::ExecutionId;
13
+ use crate::policy::{
14
+ InputProcessor, InputProcessorPipeline, InputProcessorResult, PolicyAction, PolicyContext,
15
+ };
16
+ use crate::streaming::{EventEmitter, ProtectedEventEmitter};
17
+ use std::sync::Arc;
18
+
19
+ /// Error returned when input is blocked by processors
20
+ #[derive(Debug, Clone)]
21
+ pub struct InputBlockedError {
22
+ pub reason: String,
23
+ pub processor: String,
24
+ }
25
+
26
+ impl std::fmt::Display for InputBlockedError {
27
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28
+ write!(f, "Input blocked by {}: {}", self.processor, self.reason)
29
+ }
30
+ }
31
+
32
+ impl std::error::Error for InputBlockedError {}
33
+
34
+ /// Protected Runner
35
+ ///
36
+ /// Wraps a Runner with input processor pipeline and optional protected emitter.
37
+ /// Input is validated/transformed BEFORE execution begins.
38
+ ///
39
+ /// ## Usage
40
+ ///
41
+ /// ```ignore
42
+ /// use enact_core::runner::{ProtectedRunner, DefaultRunner};
43
+ /// use enact_core::policy::{PiiInputProcessor, PiiInputMode};
44
+ ///
45
+ /// let runner = DefaultRunner::default_new();
46
+ /// let protected = ProtectedRunner::new(runner)
47
+ /// .with_input_processor(Arc::new(
48
+ /// PiiInputProcessor::new().with_mode(PiiInputMode::BlockDirect)
49
+ /// ));
50
+ ///
51
+ /// // Input will be validated before callable runs
52
+ /// let result = protected.run_callable(&my_callable, "user input").await;
53
+ /// ```
54
+ pub struct ProtectedRunner<S: CheckpointStore> {
55
+ inner: Runner<S>,
56
+ input_pipeline: InputProcessorPipeline,
57
+ protected_emitter: Option<ProtectedEventEmitter>,
58
+ }
59
+
60
+ impl<S: CheckpointStore> ProtectedRunner<S> {
61
+ /// Create a new protected runner wrapping an existing runner
62
+ pub fn new(runner: Runner<S>) -> Self {
63
+ Self {
64
+ inner: runner,
65
+ input_pipeline: InputProcessorPipeline::new(),
66
+ protected_emitter: None,
67
+ }
68
+ }
69
+
70
+ /// Add an input processor to the pipeline
71
+ pub fn with_input_processor(mut self, processor: Arc<dyn InputProcessor>) -> Self {
72
+ self.input_pipeline = self.input_pipeline.add(processor);
73
+ self
74
+ }
75
+
76
+ /// Set a protected emitter for output processing
77
+ pub fn with_protected_emitter(mut self, emitter: ProtectedEventEmitter) -> Self {
78
+ self.protected_emitter = Some(emitter);
79
+ self
80
+ }
81
+
82
+ /// Get the execution ID
83
+ pub fn execution_id(&self) -> &ExecutionId {
84
+ self.inner.execution_id()
85
+ }
86
+
87
+ /// Get the event emitter (from inner runner)
88
+ pub fn emitter(&self) -> &EventEmitter {
89
+ self.inner.emitter()
90
+ }
91
+
92
+ /// Cancel the run
93
+ pub fn cancel(&self) {
94
+ self.inner.cancel();
95
+ }
96
+
97
+ /// Check if cancelled
98
+ pub fn is_cancelled(&self) -> bool {
99
+ self.inner.is_cancelled()
100
+ }
101
+
102
+ /// Pause the run
103
+ pub async fn pause(&self) -> anyhow::Result<()> {
104
+ self.inner.pause().await
105
+ }
106
+
107
+ /// Resume the run
108
+ pub fn resume(&self) {
109
+ self.inner.resume();
110
+ }
111
+
112
+ /// Check if paused
113
+ pub fn is_paused(&self) -> bool {
114
+ self.inner.is_paused()
115
+ }
116
+
117
+ /// Create policy context for input processing
118
+ fn create_policy_context(&self) -> PolicyContext {
119
+ PolicyContext {
120
+ tenant_id: None, // Could be set from runner context
121
+ user_id: None, // Could be set from runner context
122
+ action: PolicyAction::StartExecution { graph_id: None },
123
+ metadata: std::collections::HashMap::new(),
124
+ }
125
+ }
126
+
127
+ /// Process input through the pipeline
128
+ async fn process_input(&self, input: &str) -> anyhow::Result<String> {
129
+ if self.input_pipeline.is_empty() {
130
+ return Ok(input.to_string());
131
+ }
132
+
133
+ let ctx = self.create_policy_context();
134
+ let result = self.input_pipeline.process(input, &ctx).await?;
135
+
136
+ match result {
137
+ InputProcessorResult::Pass => Ok(input.to_string()),
138
+ InputProcessorResult::Block { reason, processor } => {
139
+ Err(InputBlockedError { reason, processor }.into())
140
+ }
141
+ InputProcessorResult::Modify { modified, .. } => Ok(modified),
142
+ }
143
+ }
144
+
145
+ /// Run a callable with input validation
146
+ pub async fn run_callable<A: Callable>(
147
+ &mut self,
148
+ callable: &A,
149
+ input: &str,
150
+ ) -> anyhow::Result<String> {
151
+ // Process input through pipeline
152
+ let processed_input = self.process_input(input).await?;
153
+
154
+ // Run with processed input
155
+ self.inner.run_callable(callable, &processed_input).await
156
+ }
157
+
158
+ /// Run a graph with input validation
159
+ pub async fn run_graph(
160
+ &mut self,
161
+ graph: &CompiledGraph,
162
+ input: &str,
163
+ ) -> anyhow::Result<NodeState> {
164
+ // Process input through pipeline
165
+ let processed_input = self.process_input(input).await?;
166
+
167
+ // Run with processed input
168
+ self.inner.run_graph(graph, &processed_input).await
169
+ }
170
+ }
171
+
172
+ /// Protected Runner with in-memory checkpoint store (default)
173
+ pub type DefaultProtectedRunner = ProtectedRunner<crate::graph::InMemoryCheckpointStore>;
174
+
175
+ impl DefaultProtectedRunner {
176
+ /// Create a new protected runner with in-memory checkpoint store
177
+ pub fn default_new() -> Self {
178
+ Self::new(crate::runner::DefaultRunner::default_new())
179
+ }
180
+ }
181
+
182
+ #[cfg(test)]
183
+ mod tests {
184
+ use super::*;
185
+ use crate::runner::DefaultRunner;
186
+ use async_trait::async_trait;
187
+
188
+ struct MockCallable {
189
+ response: String,
190
+ }
191
+
192
+ impl MockCallable {
193
+ fn new(response: &str) -> Self {
194
+ Self {
195
+ response: response.to_string(),
196
+ }
197
+ }
198
+ }
199
+
200
+ #[async_trait]
201
+ impl Callable for MockCallable {
202
+ fn name(&self) -> &str {
203
+ "mock"
204
+ }
205
+ async fn run(&self, _input: &str) -> anyhow::Result<String> {
206
+ Ok(self.response.clone())
207
+ }
208
+ }
209
+
210
+ struct BlockingProcessor;
211
+
212
+ #[async_trait]
213
+ impl InputProcessor for BlockingProcessor {
214
+ fn name(&self) -> &str {
215
+ "blocker"
216
+ }
217
+
218
+ async fn process(
219
+ &self,
220
+ _input: &str,
221
+ _ctx: &PolicyContext,
222
+ ) -> anyhow::Result<InputProcessorResult> {
223
+ Ok(InputProcessorResult::Block {
224
+ reason: "Always blocks".to_string(),
225
+ processor: "blocker".to_string(),
226
+ })
227
+ }
228
+ }
229
+
230
+ #[tokio::test]
231
+ async fn test_protected_runner_no_processors() {
232
+ let runner = DefaultRunner::default_new();
233
+ let mut protected = ProtectedRunner::new(runner);
234
+ let callable = MockCallable::new("response");
235
+
236
+ let result = protected.run_callable(&callable, "input").await;
237
+ assert!(result.is_ok());
238
+ assert_eq!(result.unwrap(), "response");
239
+ }
240
+
241
+ #[tokio::test]
242
+ async fn test_protected_runner_blocked_input() {
243
+ let runner = DefaultRunner::default_new();
244
+ let mut protected =
245
+ ProtectedRunner::new(runner).with_input_processor(Arc::new(BlockingProcessor));
246
+ let callable = MockCallable::new("response");
247
+
248
+ let result = protected.run_callable(&callable, "input").await;
249
+ assert!(result.is_err());
250
+ assert!(result.unwrap_err().to_string().contains("blocked"));
251
+ }
252
+
253
+ #[tokio::test]
254
+ async fn test_protected_runner_execution_id() {
255
+ let runner = DefaultRunner::default_new();
256
+ let protected = ProtectedRunner::new(runner);
257
+
258
+ // Should have a valid execution ID
259
+ assert!(!protected.execution_id().as_str().is_empty());
260
+ }
261
+
262
+ #[tokio::test]
263
+ async fn test_protected_runner_cancel() {
264
+ let runner = DefaultRunner::default_new();
265
+ let protected = ProtectedRunner::new(runner);
266
+
267
+ assert!(!protected.is_cancelled());
268
+ protected.cancel();
269
+ assert!(protected.is_cancelled());
270
+ }
271
+
272
+ #[tokio::test]
273
+ async fn test_default_protected_runner() {
274
+ let mut protected = DefaultProtectedRunner::default_new();
275
+ let callable = MockCallable::new("hello");
276
+
277
+ let result = protected.run_callable(&callable, "test").await;
278
+ assert!(result.is_ok());
279
+ }
280
+ }
@@ -0,0 +1,231 @@
1
+ //! In-memory SignalBus implementation for local/desktop/testing
2
+ //!
3
+ //! This is the default, in-memory-only implementation suitable for:
4
+ //! - Local development
5
+ //! - Desktop applications
6
+ //! - Testing environments
7
+ //! - Single-process deployments
8
+ //!
9
+ //! ## Important
10
+ //!
11
+ //! This implementation is **not distributed** and does **not** provide
12
+ //! cross-process signaling. For distributed signaling, use the control plane
13
+ //! which implements authoritative messaging (Redis, Kafka, etc.).
14
+
15
+ use super::{SignalBus, SignalReceiver};
16
+ use async_trait::async_trait;
17
+ use std::collections::HashMap;
18
+ use std::sync::Arc;
19
+ use tokio::sync::{broadcast, RwLock};
20
+
21
+ /// In-memory signal bus using tokio broadcast channels
22
+ ///
23
+ /// Suitable for desktop apps and testing (no external dependencies).
24
+ /// Signals are best-effort and may be lost if no receivers are subscribed.
25
+ pub struct InMemorySignalBus {
26
+ channels: Arc<RwLock<HashMap<String, broadcast::Sender<Vec<u8>>>>>,
27
+ capacity: usize,
28
+ }
29
+
30
+ impl InMemorySignalBus {
31
+ /// Create a new in-memory signal bus with the specified channel capacity
32
+ pub fn new(capacity: usize) -> Self {
33
+ Self {
34
+ channels: Arc::new(RwLock::new(HashMap::new())),
35
+ capacity,
36
+ }
37
+ }
38
+
39
+ /// Create a new in-memory signal bus with default capacity (1024)
40
+ pub fn default() -> Self {
41
+ Self::new(1024)
42
+ }
43
+ }
44
+
45
+ #[async_trait]
46
+ impl SignalBus for InMemorySignalBus {
47
+ async fn emit(&self, channel: &str, signal: &[u8]) -> anyhow::Result<()> {
48
+ let channels = self.channels.read().await;
49
+ if let Some(sender) = channels.get(channel) {
50
+ // Ignore send errors (no receivers) - this is best-effort
51
+ let _ = sender.send(signal.to_vec());
52
+ }
53
+ Ok(())
54
+ }
55
+
56
+ async fn subscribe(&self, channel: &str) -> anyhow::Result<SignalReceiver<Vec<u8>>> {
57
+ let mut channels = self.channels.write().await;
58
+ let sender = channels
59
+ .entry(channel.to_string())
60
+ .or_insert_with(|| broadcast::channel(self.capacity).0);
61
+ Ok(sender.subscribe())
62
+ }
63
+
64
+ async fn unsubscribe(&self, _channel: &str) -> anyhow::Result<()> {
65
+ // Broadcast receivers auto-cleanup when dropped
66
+ Ok(())
67
+ }
68
+ }
69
+
70
+ #[cfg(test)]
71
+ mod tests {
72
+ use super::*;
73
+
74
+ #[tokio::test]
75
+ async fn test_inmemory_signal_bus_new() {
76
+ let bus = InMemorySignalBus::new(100);
77
+ assert_eq!(bus.capacity, 100);
78
+ }
79
+
80
+ #[tokio::test]
81
+ async fn test_inmemory_signal_bus_default() {
82
+ let bus = InMemorySignalBus::default();
83
+ assert_eq!(bus.capacity, 1024);
84
+ }
85
+
86
+ #[tokio::test]
87
+ async fn test_subscribe_and_receive() {
88
+ let bus = InMemorySignalBus::default();
89
+
90
+ // Subscribe to a channel
91
+ let mut rx = bus.subscribe("test-channel").await.unwrap();
92
+
93
+ // Emit a signal
94
+ bus.emit("test-channel", b"hello world").await.unwrap();
95
+
96
+ // Receive the signal
97
+ let received = rx.recv().await.unwrap();
98
+ assert_eq!(received, b"hello world".to_vec());
99
+ }
100
+
101
+ #[tokio::test]
102
+ async fn test_multiple_subscribers() {
103
+ let bus = InMemorySignalBus::default();
104
+
105
+ // Subscribe two receivers to the same channel
106
+ let mut rx1 = bus.subscribe("multi-channel").await.unwrap();
107
+ let mut rx2 = bus.subscribe("multi-channel").await.unwrap();
108
+
109
+ // Emit a signal
110
+ bus.emit("multi-channel", b"broadcast").await.unwrap();
111
+
112
+ // Both receivers should get the signal
113
+ let received1 = rx1.recv().await.unwrap();
114
+ let received2 = rx2.recv().await.unwrap();
115
+
116
+ assert_eq!(received1, b"broadcast".to_vec());
117
+ assert_eq!(received2, b"broadcast".to_vec());
118
+ }
119
+
120
+ #[tokio::test]
121
+ async fn test_emit_without_subscribers() {
122
+ let bus = InMemorySignalBus::default();
123
+
124
+ // Emit to a channel with no subscribers - should not fail
125
+ let result = bus.emit("no-subscribers", b"data").await;
126
+ assert!(result.is_ok());
127
+ }
128
+
129
+ #[tokio::test]
130
+ async fn test_emit_to_different_channels() {
131
+ let bus = InMemorySignalBus::default();
132
+
133
+ let mut rx1 = bus.subscribe("channel-a").await.unwrap();
134
+ let mut rx2 = bus.subscribe("channel-b").await.unwrap();
135
+
136
+ bus.emit("channel-a", b"msg-a").await.unwrap();
137
+ bus.emit("channel-b", b"msg-b").await.unwrap();
138
+
139
+ // Each receiver should only get their channel's messages
140
+ let received1 = rx1.recv().await.unwrap();
141
+ let received2 = rx2.recv().await.unwrap();
142
+
143
+ assert_eq!(received1, b"msg-a".to_vec());
144
+ assert_eq!(received2, b"msg-b".to_vec());
145
+ }
146
+
147
+ #[tokio::test]
148
+ async fn test_unsubscribe() {
149
+ let bus = InMemorySignalBus::default();
150
+
151
+ let _rx = bus.subscribe("unsub-channel").await.unwrap();
152
+
153
+ // Unsubscribe should succeed (no-op for broadcast)
154
+ let result = bus.unsubscribe("unsub-channel").await;
155
+ assert!(result.is_ok());
156
+ }
157
+
158
+ #[tokio::test]
159
+ async fn test_multiple_messages() {
160
+ let bus = InMemorySignalBus::default();
161
+
162
+ let mut rx = bus.subscribe("multi-msg").await.unwrap();
163
+
164
+ bus.emit("multi-msg", b"first").await.unwrap();
165
+ bus.emit("multi-msg", b"second").await.unwrap();
166
+ bus.emit("multi-msg", b"third").await.unwrap();
167
+
168
+ assert_eq!(rx.recv().await.unwrap(), b"first".to_vec());
169
+ assert_eq!(rx.recv().await.unwrap(), b"second".to_vec());
170
+ assert_eq!(rx.recv().await.unwrap(), b"third".to_vec());
171
+ }
172
+
173
+ #[tokio::test]
174
+ async fn test_late_subscriber_misses_messages() {
175
+ let bus = InMemorySignalBus::default();
176
+
177
+ // Subscribe first receiver
178
+ let mut rx1 = bus.subscribe("late-sub").await.unwrap();
179
+
180
+ // Emit first message
181
+ bus.emit("late-sub", b"early").await.unwrap();
182
+
183
+ // Subscribe second receiver (late)
184
+ let mut rx2 = bus.subscribe("late-sub").await.unwrap();
185
+
186
+ // Emit second message
187
+ bus.emit("late-sub", b"late").await.unwrap();
188
+
189
+ // First receiver gets both
190
+ assert_eq!(rx1.recv().await.unwrap(), b"early".to_vec());
191
+ assert_eq!(rx1.recv().await.unwrap(), b"late".to_vec());
192
+
193
+ // Second receiver only gets the late message
194
+ assert_eq!(rx2.recv().await.unwrap(), b"late".to_vec());
195
+ }
196
+
197
+ #[tokio::test]
198
+ async fn test_concurrent_emit() {
199
+ let bus = Arc::new(InMemorySignalBus::default());
200
+
201
+ let mut rx = bus.subscribe("concurrent").await.unwrap();
202
+
203
+ let bus1 = bus.clone();
204
+ let bus2 = bus.clone();
205
+
206
+ // Spawn two tasks emitting concurrently
207
+ let h1 = tokio::spawn(async move {
208
+ for i in 0..5 {
209
+ bus1.emit("concurrent", format!("msg-a-{}", i).as_bytes()).await.unwrap();
210
+ }
211
+ });
212
+
213
+ let h2 = tokio::spawn(async move {
214
+ for i in 0..5 {
215
+ bus2.emit("concurrent", format!("msg-b-{}", i).as_bytes()).await.unwrap();
216
+ }
217
+ });
218
+
219
+ h1.await.unwrap();
220
+ h2.await.unwrap();
221
+
222
+ // Collect all received messages
223
+ let mut received = Vec::new();
224
+ while let Ok(msg) = rx.try_recv() {
225
+ received.push(msg);
226
+ }
227
+
228
+ // Should have received 10 messages total
229
+ assert_eq!(received.len(), 10);
230
+ }
231
+ }
@@ -0,0 +1,108 @@
1
+ //! Signal module - Optional, best-effort signaling for hints and notifications
2
+ //!
3
+ //! ## Important: Non-Authoritative
4
+ //!
5
+ //! This module provides **optional, best-effort signaling** for hints and
6
+ //! notifications. Signals are **not authoritative** and the kernel must work
7
+ //! correctly even if all signaling infrastructure is disabled.
8
+ //!
9
+ //! ## Three Kinds of "Messages"
10
+ //!
11
+ //! There are three distinct kinds of communication in the system:
12
+ //!
13
+ //! | Kind | Example | Where it belongs |
14
+ //! | -------------------------- | ------------------------------- | ----------------------- |
15
+ //! | **Execution events** | step started, decision made | **Kernel only** (`kernel/event.rs`) |
16
+ //! | **Control signals** | cancel execution, scale workers | **Control plane** (cloud) |
17
+ //! | **Infrastructure signals** | notify UI, wake worker | **This module** (optional) |
18
+ //!
19
+ //! ## Signal Semantics
20
+ //!
21
+ //! Signals are:
22
+ //! - ✅ Non-authoritative (may be ignored)
23
+ //! - ✅ Best-effort (delivery not guaranteed)
24
+ //! - ✅ Optional (kernel works perfectly without them)
25
+ //! - ✅ For hints only (wake-up, notify, nudge)
26
+ //!
27
+ //! Signals are NOT:
28
+ //! - ❌ Execution events (use `kernel/event.rs`)
29
+ //! - ❌ State changes (use `kernel/reducer.rs`)
30
+ //! - ❌ Required for correctness
31
+ //! - ❌ Durable queues (use control plane)
32
+ //!
33
+ //! ## Invariant
34
+ //!
35
+ //! **The kernel must remain correct, deterministic, and replayable
36
+ //! even if all signaling infrastructure is disabled.**
37
+ //!
38
+ //! ## CRITICAL INVARIANT: Signals Never Drive Execution State
39
+ //!
40
+ //! SignalBus implementations MUST NOT have access to:
41
+ //! - ExecutionKernel
42
+ //! - Reducer
43
+ //! - ExecutionState
44
+ //!
45
+ //! Signals are hints only. If someone can "resume execution" via signal,
46
+ //! that is an architectural leak.
47
+ //!
48
+ //! **Enforcement**: SignalBus trait and implementations should not import
49
+ //! any kernel modules. If you find yourself needing kernel types in signal
50
+ //! code, you are violating this invariant.
51
+ //!
52
+ //! ## Use Cases
53
+ //!
54
+ //! ### Allowed Signal Uses
55
+ //! - ✅ Wake a paused execution
56
+ //! - ✅ Notify local UI
57
+ //! - ✅ Tell a worker "check for work"
58
+ //! - ✅ Hint that something changed
59
+ //!
60
+ //! ### Forbidden Signal Uses
61
+ //! - ❌ Carry execution events (use `kernel/event.rs`)
62
+ //! - ❌ Control execution state (use `kernel/reducer.rs`)
63
+ //! - ❌ Replace durable queues (use control plane)
64
+ //! - ❌ Drive orchestration logic (use control plane)
65
+ //!
66
+ //! ## Control Plane Messaging
67
+ //!
68
+ //! Authoritative messaging (Redis, Kafka, durable queues) lives in the
69
+ //! control plane (`apps/api/` or future `enact-control-plane` crate), not here.
70
+ //! The control plane:
71
+ //! - Implements durable queues
72
+ //! - Translates infra messages → kernel invocations
73
+ //! - Fans out kernel events to external systems
74
+ //!
75
+ //! The kernel does not know *how* messages travel. It only knows:
76
+ //! > "I was invoked."
77
+
78
+ use async_trait::async_trait;
79
+ use std::sync::Arc;
80
+ use tokio::sync::broadcast;
81
+
82
+ mod inmemory;
83
+
84
+ pub use inmemory::InMemorySignalBus;
85
+
86
+ /// Receiver for subscribed signals
87
+ pub type SignalReceiver<T> = broadcast::Receiver<T>;
88
+
89
+ /// SignalBus trait - Optional, best-effort signaling abstraction
90
+ ///
91
+ /// This trait provides a non-authoritative signaling mechanism for hints
92
+ /// and notifications. Implementations should be lightweight and optional.
93
+ ///
94
+ /// The kernel must work correctly even if SignalBus is disabled or fails.
95
+ #[async_trait]
96
+ pub trait SignalBus: Send + Sync {
97
+ /// Emit a signal to a channel (best-effort, may be ignored)
98
+ async fn emit(&self, channel: &str, signal: &[u8]) -> anyhow::Result<()>;
99
+
100
+ /// Subscribe to a channel, returns a receiver for signals
101
+ async fn subscribe(&self, channel: &str) -> anyhow::Result<SignalReceiver<Vec<u8>>>;
102
+
103
+ /// Unsubscribe from a channel
104
+ async fn unsubscribe(&self, channel: &str) -> anyhow::Result<()>;
105
+ }
106
+
107
+ /// Boxed SignalBus for dynamic dispatch
108
+ pub type DynSignalBus = Arc<dyn SignalBus>;