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,278 @@
1
+ //! Integration tests for enact-runner
2
+ //!
3
+ //! These tests demonstrate how to use `AgentRunner` with mock callables and tools,
4
+ //! showing the full loop in action.
5
+
6
+ use async_trait::async_trait;
7
+ use enact_core::callable::Callable;
8
+ use enact_core::tool::Tool;
9
+ use enact_runner::{AgentRunner, DefaultAgentRunner, LoopOutcome, RunnerConfig};
10
+ use serde_json::{json, Value};
11
+ use std::sync::atomic::{AtomicUsize, Ordering};
12
+ use std::sync::Arc;
13
+
14
+ // =============================================================================
15
+ // Mock Callable — simulates an LLM that uses tools
16
+ // =============================================================================
17
+
18
+ /// A mock LLM that returns tool calls for the first N iterations,
19
+ /// then returns a final text response.
20
+ struct MockLlm {
21
+ call_count: AtomicUsize,
22
+ tool_calls_before_done: usize,
23
+ }
24
+
25
+ impl MockLlm {
26
+ fn new(tool_calls_before_done: usize) -> Self {
27
+ Self {
28
+ call_count: AtomicUsize::new(0),
29
+ tool_calls_before_done,
30
+ }
31
+ }
32
+ }
33
+
34
+ #[async_trait]
35
+ impl Callable for MockLlm {
36
+ fn name(&self) -> &str {
37
+ "mock-llm"
38
+ }
39
+
40
+ async fn run(&self, _input: &str) -> anyhow::Result<String> {
41
+ let count = self.call_count.fetch_add(1, Ordering::SeqCst);
42
+
43
+ if count < self.tool_calls_before_done {
44
+ // Return a JSON tool call
45
+ Ok(json!({
46
+ "tool_call": {
47
+ "name": "search",
48
+ "arguments": {"query": format!("iteration {}", count)}
49
+ }
50
+ })
51
+ .to_string())
52
+ } else {
53
+ // Final response — no tool call
54
+ Ok(format!(
55
+ "Done! Completed {} tool calls before finishing.",
56
+ count
57
+ ))
58
+ }
59
+ }
60
+ }
61
+
62
+ /// A mock LLM that returns XML-format tool calls.
63
+ struct MockXmlLlm {
64
+ call_count: AtomicUsize,
65
+ }
66
+
67
+ impl MockXmlLlm {
68
+ fn new() -> Self {
69
+ Self {
70
+ call_count: AtomicUsize::new(0),
71
+ }
72
+ }
73
+ }
74
+
75
+ #[async_trait]
76
+ impl Callable for MockXmlLlm {
77
+ fn name(&self) -> &str {
78
+ "mock-xml-llm"
79
+ }
80
+
81
+ async fn run(&self, _input: &str) -> anyhow::Result<String> {
82
+ let count = self.call_count.fetch_add(1, Ordering::SeqCst);
83
+
84
+ if count == 0 {
85
+ Ok(r#"Let me search for that.
86
+ <tool_call><name>search</name><arguments>{"query": "rust async"}</arguments></tool_call>"#
87
+ .to_string())
88
+ } else {
89
+ Ok("Here are the results for your query about Rust async.".to_string())
90
+ }
91
+ }
92
+ }
93
+
94
+ /// A mock LLM that always fails (for retry testing).
95
+ struct FailingLlm {
96
+ call_count: AtomicUsize,
97
+ fail_count: usize,
98
+ }
99
+
100
+ impl FailingLlm {
101
+ fn new(fail_count: usize) -> Self {
102
+ Self {
103
+ call_count: AtomicUsize::new(0),
104
+ fail_count,
105
+ }
106
+ }
107
+ }
108
+
109
+ #[async_trait]
110
+ impl Callable for FailingLlm {
111
+ fn name(&self) -> &str {
112
+ "failing-llm"
113
+ }
114
+
115
+ async fn run(&self, _input: &str) -> anyhow::Result<String> {
116
+ let count = self.call_count.fetch_add(1, Ordering::SeqCst);
117
+
118
+ if count < self.fail_count {
119
+ anyhow::bail!("Rate limit exceeded, please retry after 1s")
120
+ } else {
121
+ Ok("Success after retries!".to_string())
122
+ }
123
+ }
124
+ }
125
+
126
+ // =============================================================================
127
+ // Mock Tool
128
+ // =============================================================================
129
+
130
+ struct MockSearchTool;
131
+
132
+ #[async_trait]
133
+ impl Tool for MockSearchTool {
134
+ fn name(&self) -> &str {
135
+ "search"
136
+ }
137
+
138
+ fn description(&self) -> &str {
139
+ "Search for information"
140
+ }
141
+
142
+ fn parameters_schema(&self) -> Value {
143
+ json!({
144
+ "type": "object",
145
+ "properties": {
146
+ "query": {"type": "string"}
147
+ }
148
+ })
149
+ }
150
+
151
+ async fn execute(&self, args: Value) -> anyhow::Result<Value> {
152
+ let query = args
153
+ .get("query")
154
+ .and_then(|q| q.as_str())
155
+ .unwrap_or("unknown");
156
+ Ok(json!({
157
+ "results": [format!("Result for: {}", query)],
158
+ "count": 1
159
+ }))
160
+ }
161
+ }
162
+
163
+ // =============================================================================
164
+ // Tests
165
+ // =============================================================================
166
+
167
+ #[tokio::test]
168
+ async fn test_simple_no_tool_calls() {
169
+ // LLM returns final response immediately (0 tool calls before done)
170
+ let llm = MockLlm::new(0);
171
+ let mut runner = DefaultAgentRunner::default_new();
172
+
173
+ let outcome = runner.run(&llm, "Hello!").await.unwrap();
174
+
175
+ assert!(outcome.is_completed());
176
+ assert!(outcome.output().unwrap().contains("Done!"));
177
+ }
178
+
179
+ #[tokio::test]
180
+ async fn test_tool_call_loop_json() {
181
+ // LLM makes 3 tool calls, then returns final response
182
+ let llm = MockLlm::new(3);
183
+ let mut runner = DefaultAgentRunner::default_new().add_tool(MockSearchTool);
184
+
185
+ let outcome = runner.run(&llm, "Search for Rust").await.unwrap();
186
+
187
+ assert!(outcome.is_completed());
188
+ let output = outcome.output().unwrap();
189
+ assert!(output.contains("3 tool calls"));
190
+ }
191
+
192
+ #[tokio::test]
193
+ async fn test_tool_call_loop_xml() {
194
+ // LLM returns XML-format tool call, then final response
195
+ let llm = MockXmlLlm::new();
196
+ let mut runner = DefaultAgentRunner::default_new().add_tool(MockSearchTool);
197
+
198
+ let outcome = runner.run(&llm, "Search for Rust async").await.unwrap();
199
+
200
+ assert!(outcome.is_completed());
201
+ assert!(outcome.output().unwrap().contains("Rust async"));
202
+ }
203
+
204
+ #[tokio::test]
205
+ async fn test_max_iterations_reached() {
206
+ // LLM always returns tool calls — loop should hit max_iterations
207
+ let llm = MockLlm::new(1000); // Will never finish
208
+ let config = RunnerConfig {
209
+ max_iterations: 3,
210
+ ..Default::default()
211
+ };
212
+ let mut runner = DefaultAgentRunner::with_config(config).add_tool(MockSearchTool);
213
+
214
+ let outcome = runner.run(&llm, "Loop forever").await.unwrap();
215
+
216
+ assert!(!outcome.is_completed());
217
+ match outcome {
218
+ LoopOutcome::MaxIterationsReached { iterations, .. } => {
219
+ assert_eq!(iterations, 3);
220
+ }
221
+ other => panic!("Expected MaxIterationsReached, got {:?}", other),
222
+ }
223
+ }
224
+
225
+ #[tokio::test]
226
+ async fn test_retry_on_transient_error() {
227
+ // LLM fails twice with rate-limit error, then succeeds
228
+ let llm = FailingLlm::new(2);
229
+ let config = RunnerConfig {
230
+ retry: enact_runner::RetryConfig {
231
+ max_retries: 3,
232
+ initial_delay: std::time::Duration::from_millis(10), // Fast for tests
233
+ max_delay: std::time::Duration::from_millis(100),
234
+ backoff_multiplier: 2.0,
235
+ },
236
+ ..Default::default()
237
+ };
238
+ let mut runner = DefaultAgentRunner::with_config(config);
239
+
240
+ let outcome = runner.run(&llm, "Will fail then succeed").await.unwrap();
241
+
242
+ assert!(outcome.is_completed());
243
+ assert!(outcome.output().unwrap().contains("Success after retries"));
244
+ }
245
+
246
+ #[tokio::test]
247
+ async fn test_missing_tool_handled_gracefully() {
248
+ // LLM calls a tool that doesn't exist — runner should report missing tool
249
+ let llm = MockLlm::new(1);
250
+ // No tools registered!
251
+ let config = RunnerConfig {
252
+ max_iterations: 3,
253
+ ..Default::default()
254
+ };
255
+ let mut runner = DefaultAgentRunner::with_config(config);
256
+
257
+ // Should NOT panic — the loop handles missing tools gracefully
258
+ let outcome = runner.run(&llm, "Call missing tool").await.unwrap();
259
+
260
+ // It should eventually complete (the "tool not found" message gets fed back)
261
+ assert!(outcome.output().is_some());
262
+ }
263
+
264
+ #[tokio::test]
265
+ async fn test_cancellation() {
266
+ let llm = MockLlm::new(100);
267
+ let mut runner = DefaultAgentRunner::default_new().add_tool(MockSearchTool);
268
+
269
+ // Cancel immediately
270
+ runner.inner().cancel();
271
+
272
+ let outcome = runner.run(&llm, "Will be cancelled").await.unwrap();
273
+
274
+ match outcome {
275
+ LoopOutcome::Cancelled => {} // Expected
276
+ other => panic!("Expected Cancelled, got {:?}", other),
277
+ }
278
+ }
@@ -0,0 +1,22 @@
1
+ [package]
2
+ name = "enact-security"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ description = "Security policy, audit logging, and sandboxing for Enact agents"
7
+ repository.workspace = true
8
+ homepage.workspace = true
9
+ keywords = ["security", "sandbox", "audit", "policy"]
10
+ categories.workspace = true
11
+
12
+ [dependencies]
13
+ anyhow.workspace = true
14
+ chrono.workspace = true
15
+ serde.workspace = true
16
+ serde_json.workspace = true
17
+ tracing.workspace = true
18
+ uuid.workspace = true
19
+ parking_lot = "0.12"
20
+
21
+ [dev-dependencies]
22
+ tempfile.workspace = true
@@ -0,0 +1,375 @@
1
+ //! Audit logging for security events
2
+ //!
3
+ //! Provides structured logging of security-relevant events with rotation support.
4
+
5
+ use anyhow::Result;
6
+ use chrono::{DateTime, Utc};
7
+ use parking_lot::Mutex;
8
+ use serde::{Deserialize, Serialize};
9
+ use std::fs::OpenOptions;
10
+ use std::io::Write;
11
+ use std::path::PathBuf;
12
+ use uuid::Uuid;
13
+
14
+ /// Audit event types
15
+ #[derive(Debug, Clone, Serialize, Deserialize)]
16
+ #[serde(rename_all = "snake_case")]
17
+ pub enum AuditEventType {
18
+ CommandExecution,
19
+ FileAccess,
20
+ ConfigChange,
21
+ AuthSuccess,
22
+ AuthFailure,
23
+ PolicyViolation,
24
+ SecurityEvent,
25
+ ToolInvocation,
26
+ AgentAction,
27
+ }
28
+
29
+ /// Actor information (who performed the action)
30
+ #[derive(Debug, Clone, Serialize, Deserialize)]
31
+ pub struct Actor {
32
+ pub channel: String,
33
+ pub user_id: Option<String>,
34
+ pub username: Option<String>,
35
+ pub session_id: Option<String>,
36
+ }
37
+
38
+ /// Action information (what was done)
39
+ #[derive(Debug, Clone, Serialize, Deserialize)]
40
+ pub struct Action {
41
+ pub command: Option<String>,
42
+ pub tool_name: Option<String>,
43
+ pub risk_level: Option<String>,
44
+ pub approved: bool,
45
+ pub allowed: bool,
46
+ }
47
+
48
+ /// Execution result
49
+ #[derive(Debug, Clone, Serialize, Deserialize)]
50
+ pub struct ExecutionResult {
51
+ pub success: bool,
52
+ pub exit_code: Option<i32>,
53
+ pub duration_ms: Option<u64>,
54
+ pub error: Option<String>,
55
+ }
56
+
57
+ /// Security context
58
+ #[derive(Debug, Clone, Serialize, Deserialize)]
59
+ pub struct SecurityContext {
60
+ pub policy_violation: bool,
61
+ pub rate_limit_remaining: Option<u32>,
62
+ pub autonomy_level: Option<String>,
63
+ pub sandbox_backend: Option<String>,
64
+ }
65
+
66
+ impl Default for SecurityContext {
67
+ fn default() -> Self {
68
+ Self {
69
+ policy_violation: false,
70
+ rate_limit_remaining: None,
71
+ autonomy_level: None,
72
+ sandbox_backend: None,
73
+ }
74
+ }
75
+ }
76
+
77
+ /// Complete audit event
78
+ #[derive(Debug, Clone, Serialize, Deserialize)]
79
+ pub struct AuditEvent {
80
+ pub timestamp: DateTime<Utc>,
81
+ pub event_id: String,
82
+ pub event_type: AuditEventType,
83
+ pub actor: Option<Actor>,
84
+ pub action: Option<Action>,
85
+ pub result: Option<ExecutionResult>,
86
+ pub security: SecurityContext,
87
+ }
88
+
89
+ impl AuditEvent {
90
+ /// Create a new audit event
91
+ pub fn new(event_type: AuditEventType) -> Self {
92
+ Self {
93
+ timestamp: Utc::now(),
94
+ event_id: Uuid::new_v4().to_string(),
95
+ event_type,
96
+ actor: None,
97
+ action: None,
98
+ result: None,
99
+ security: SecurityContext::default(),
100
+ }
101
+ }
102
+
103
+ /// Set the actor
104
+ pub fn with_actor(mut self, actor: Actor) -> Self {
105
+ self.actor = Some(actor);
106
+ self
107
+ }
108
+
109
+ /// Set the action
110
+ pub fn with_action(mut self, action: Action) -> Self {
111
+ self.action = Some(action);
112
+ self
113
+ }
114
+
115
+ /// Set the result
116
+ pub fn with_result(mut self, result: ExecutionResult) -> Self {
117
+ self.result = Some(result);
118
+ self
119
+ }
120
+
121
+ /// Set security context
122
+ pub fn with_security(mut self, security: SecurityContext) -> Self {
123
+ self.security = security;
124
+ self
125
+ }
126
+
127
+ /// Mark as policy violation
128
+ pub fn as_violation(mut self) -> Self {
129
+ self.security.policy_violation = true;
130
+ self
131
+ }
132
+ }
133
+
134
+ /// Audit logger configuration
135
+ #[derive(Debug, Clone, Serialize, Deserialize)]
136
+ pub struct AuditConfig {
137
+ pub enabled: bool,
138
+ pub log_path: String,
139
+ pub max_size_mb: u32,
140
+ pub retain_days: u32,
141
+ }
142
+
143
+ impl Default for AuditConfig {
144
+ fn default() -> Self {
145
+ Self {
146
+ enabled: true,
147
+ log_path: "audit.log".to_string(),
148
+ max_size_mb: 100,
149
+ retain_days: 90,
150
+ }
151
+ }
152
+ }
153
+
154
+ /// Audit logger with rotation support
155
+ pub struct AuditLogger {
156
+ log_path: PathBuf,
157
+ config: AuditConfig,
158
+ buffer: Mutex<Vec<AuditEvent>>,
159
+ }
160
+
161
+ impl AuditLogger {
162
+ /// Create a new audit logger
163
+ pub fn new(config: AuditConfig, workspace_dir: PathBuf) -> Result<Self> {
164
+ let log_path = workspace_dir.join(&config.log_path);
165
+ Ok(Self {
166
+ log_path,
167
+ config,
168
+ buffer: Mutex::new(Vec::new()),
169
+ })
170
+ }
171
+
172
+ /// Log an event
173
+ pub fn log(&self, event: &AuditEvent) -> Result<()> {
174
+ if !self.config.enabled {
175
+ return Ok(());
176
+ }
177
+
178
+ self.rotate_if_needed()?;
179
+
180
+ let line = serde_json::to_string(event)?;
181
+ let mut file = OpenOptions::new()
182
+ .create(true)
183
+ .append(true)
184
+ .open(&self.log_path)?;
185
+
186
+ writeln!(file, "{}", line)?;
187
+ file.sync_all()?;
188
+
189
+ Ok(())
190
+ }
191
+
192
+ /// Log a command execution
193
+ pub fn log_command(
194
+ &self,
195
+ channel: &str,
196
+ command: &str,
197
+ risk_level: &str,
198
+ approved: bool,
199
+ allowed: bool,
200
+ success: bool,
201
+ duration_ms: u64,
202
+ ) -> Result<()> {
203
+ let event = AuditEvent::new(AuditEventType::CommandExecution)
204
+ .with_actor(Actor {
205
+ channel: channel.to_string(),
206
+ user_id: None,
207
+ username: None,
208
+ session_id: None,
209
+ })
210
+ .with_action(Action {
211
+ command: Some(command.to_string()),
212
+ tool_name: None,
213
+ risk_level: Some(risk_level.to_string()),
214
+ approved,
215
+ allowed,
216
+ })
217
+ .with_result(ExecutionResult {
218
+ success,
219
+ exit_code: None,
220
+ duration_ms: Some(duration_ms),
221
+ error: None,
222
+ });
223
+
224
+ self.log(&event)
225
+ }
226
+
227
+ /// Log a tool invocation
228
+ pub fn log_tool(
229
+ &self,
230
+ channel: &str,
231
+ tool_name: &str,
232
+ allowed: bool,
233
+ success: bool,
234
+ duration_ms: u64,
235
+ error: Option<&str>,
236
+ ) -> Result<()> {
237
+ let event = AuditEvent::new(AuditEventType::ToolInvocation)
238
+ .with_actor(Actor {
239
+ channel: channel.to_string(),
240
+ user_id: None,
241
+ username: None,
242
+ session_id: None,
243
+ })
244
+ .with_action(Action {
245
+ command: None,
246
+ tool_name: Some(tool_name.to_string()),
247
+ risk_level: None,
248
+ approved: true,
249
+ allowed,
250
+ })
251
+ .with_result(ExecutionResult {
252
+ success,
253
+ exit_code: None,
254
+ duration_ms: Some(duration_ms),
255
+ error: error.map(String::from),
256
+ });
257
+
258
+ self.log(&event)
259
+ }
260
+
261
+ /// Log a policy violation
262
+ pub fn log_violation(&self, channel: &str, description: &str) -> Result<()> {
263
+ let event = AuditEvent::new(AuditEventType::PolicyViolation)
264
+ .with_actor(Actor {
265
+ channel: channel.to_string(),
266
+ user_id: None,
267
+ username: None,
268
+ session_id: None,
269
+ })
270
+ .with_action(Action {
271
+ command: Some(description.to_string()),
272
+ tool_name: None,
273
+ risk_level: Some("high".to_string()),
274
+ approved: false,
275
+ allowed: false,
276
+ })
277
+ .as_violation();
278
+
279
+ self.log(&event)
280
+ }
281
+
282
+ /// Check if rotation is needed
283
+ fn rotate_if_needed(&self) -> Result<()> {
284
+ if let Ok(metadata) = std::fs::metadata(&self.log_path) {
285
+ let current_size_mb = metadata.len() / (1024 * 1024);
286
+ if current_size_mb >= u64::from(self.config.max_size_mb) {
287
+ self.rotate()?;
288
+ }
289
+ }
290
+ Ok(())
291
+ }
292
+
293
+ /// Rotate the log file
294
+ fn rotate(&self) -> Result<()> {
295
+ for i in (1..10).rev() {
296
+ let old_name = format!("{}.{}.log", self.log_path.display(), i);
297
+ let new_name = format!("{}.{}.log", self.log_path.display(), i + 1);
298
+ let _ = std::fs::rename(&old_name, &new_name);
299
+ }
300
+
301
+ let rotated = format!("{}.1.log", self.log_path.display());
302
+ std::fs::rename(&self.log_path, &rotated)?;
303
+ Ok(())
304
+ }
305
+ }
306
+
307
+ #[cfg(test)]
308
+ mod tests {
309
+ use super::*;
310
+ use tempfile::TempDir;
311
+
312
+ #[test]
313
+ fn audit_event_new_creates_unique_id() {
314
+ let event1 = AuditEvent::new(AuditEventType::CommandExecution);
315
+ let event2 = AuditEvent::new(AuditEventType::CommandExecution);
316
+ assert_ne!(event1.event_id, event2.event_id);
317
+ }
318
+
319
+ #[test]
320
+ fn audit_event_serializes_to_json() {
321
+ let event = AuditEvent::new(AuditEventType::CommandExecution)
322
+ .with_actor(Actor {
323
+ channel: "telegram".to_string(),
324
+ user_id: None,
325
+ username: None,
326
+ session_id: None,
327
+ })
328
+ .with_action(Action {
329
+ command: Some("ls".to_string()),
330
+ tool_name: None,
331
+ risk_level: Some("low".to_string()),
332
+ approved: false,
333
+ allowed: true,
334
+ });
335
+
336
+ let json = serde_json::to_string(&event);
337
+ assert!(json.is_ok());
338
+ }
339
+
340
+ #[test]
341
+ fn audit_logger_disabled_does_not_create_file() -> Result<()> {
342
+ let tmp = TempDir::new()?;
343
+ let config = AuditConfig {
344
+ enabled: false,
345
+ ..Default::default()
346
+ };
347
+ let logger = AuditLogger::new(config, tmp.path().to_path_buf())?;
348
+ let event = AuditEvent::new(AuditEventType::CommandExecution);
349
+
350
+ logger.log(&event)?;
351
+
352
+ assert!(!tmp.path().join("audit.log").exists());
353
+ Ok(())
354
+ }
355
+
356
+ #[test]
357
+ fn audit_logger_writes_event_when_enabled() -> Result<()> {
358
+ let tmp = TempDir::new()?;
359
+ let config = AuditConfig {
360
+ enabled: true,
361
+ max_size_mb: 10,
362
+ ..Default::default()
363
+ };
364
+ let logger = AuditLogger::new(config, tmp.path().to_path_buf())?;
365
+
366
+ logger.log_command("cli", "ls", "low", false, true, true, 15)?;
367
+
368
+ let log_path = tmp.path().join("audit.log");
369
+ assert!(log_path.exists());
370
+
371
+ let content = std::fs::read_to_string(&log_path)?;
372
+ assert!(!content.is_empty());
373
+ Ok(())
374
+ }
375
+ }
@@ -0,0 +1,37 @@
1
+ //! Security policy, audit logging, and sandboxing for Enact agents
2
+ //!
3
+ //! This crate provides:
4
+ //! - Security policy with autonomy levels
5
+ //! - Rate limiting
6
+ //! - Audit logging with rotation
7
+ //! - Action validation
8
+
9
+ pub mod audit;
10
+ pub mod policy;
11
+
12
+ pub use audit::{
13
+ Action, Actor, AuditConfig, AuditEvent, AuditEventType, AuditLogger, ExecutionResult,
14
+ SecurityContext,
15
+ };
16
+ pub use policy::{
17
+ ActionValidation, AutonomyLevel, PolicyConfig, RiskLevel, SecurityPolicy,
18
+ };
19
+
20
+ #[cfg(test)]
21
+ mod tests {
22
+ use super::*;
23
+ use std::path::PathBuf;
24
+
25
+ #[test]
26
+ fn policy_and_audit_integration() {
27
+ let policy = SecurityPolicy::default_for(PathBuf::from("/tmp/test"));
28
+ assert!(policy.can_act());
29
+ assert_eq!(policy.autonomy(), AutonomyLevel::Supervised);
30
+ }
31
+
32
+ #[test]
33
+ fn audit_event_types() {
34
+ let event = AuditEvent::new(AuditEventType::CommandExecution);
35
+ assert!(!event.event_id.is_empty());
36
+ }
37
+ }