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,245 @@
1
+ //! Telemetry Spans - OpenTelemetry span helpers for execution tracing
2
+ //!
3
+ //! This module provides utilities for creating and managing OpenTelemetry spans
4
+ //! that correlate with ExecutionId and StepId for distributed tracing.
5
+ //!
6
+ //! ## Span Hierarchy
7
+ //! ```text
8
+ //! Execution Span (trace_id = TraceContext.trace_id)
9
+ //! ├── Step Span (span_id = unique per step)
10
+ //! │ └── Tool Span
11
+ //! ├── Step Span
12
+ //! │ └── LLM Span
13
+ //! └── Child Execution Span (sub-agent)
14
+ //! └── Step Span...
15
+ //! ```
16
+ //!
17
+ //! @see docs/TECHNICAL/01-EXECUTION-TELEMETRY.md
18
+
19
+ use crate::kernel::{ExecutionId, StepId, StepType};
20
+ use crate::runner::TraceContext;
21
+
22
+ /// Span attributes for execution-level spans
23
+ pub struct ExecutionSpanAttributes {
24
+ /// Execution ID
25
+ pub execution_id: String,
26
+ /// Parent ID (if nested execution)
27
+ pub parent_id: Option<String>,
28
+ /// Parent type
29
+ pub parent_type: Option<String>,
30
+ /// Tenant ID
31
+ pub tenant_id: Option<String>,
32
+ /// User ID
33
+ pub user_id: Option<String>,
34
+ }
35
+
36
+ impl ExecutionSpanAttributes {
37
+ /// Create from an ExecutionId
38
+ pub fn new(execution_id: &ExecutionId) -> Self {
39
+ Self {
40
+ execution_id: execution_id.as_str().to_string(),
41
+ parent_id: None,
42
+ parent_type: None,
43
+ tenant_id: None,
44
+ user_id: None,
45
+ }
46
+ }
47
+
48
+ /// Add parent context
49
+ pub fn with_parent(mut self, parent_id: impl Into<String>, parent_type: impl Into<String>) -> Self {
50
+ self.parent_id = Some(parent_id.into());
51
+ self.parent_type = Some(parent_type.into());
52
+ self
53
+ }
54
+
55
+ /// Add tenant context
56
+ pub fn with_tenant(mut self, tenant_id: impl Into<String>) -> Self {
57
+ self.tenant_id = Some(tenant_id.into());
58
+ self
59
+ }
60
+
61
+ /// Add user context
62
+ pub fn with_user(mut self, user_id: impl Into<String>) -> Self {
63
+ self.user_id = Some(user_id.into());
64
+ self
65
+ }
66
+
67
+ /// Convert to a map of attributes for OpenTelemetry
68
+ pub fn to_attributes(&self) -> Vec<(&'static str, String)> {
69
+ let mut attrs = vec![("enact.execution_id", self.execution_id.clone())];
70
+
71
+ if let Some(ref parent_id) = self.parent_id {
72
+ attrs.push(("enact.parent_id", parent_id.clone()));
73
+ }
74
+ if let Some(ref parent_type) = self.parent_type {
75
+ attrs.push(("enact.parent_type", parent_type.clone()));
76
+ }
77
+ if let Some(ref tenant_id) = self.tenant_id {
78
+ attrs.push(("enact.tenant_id", tenant_id.clone()));
79
+ }
80
+ if let Some(ref user_id) = self.user_id {
81
+ attrs.push(("enact.user_id", user_id.clone()));
82
+ }
83
+
84
+ attrs
85
+ }
86
+ }
87
+
88
+ /// Span attributes for step-level spans
89
+ pub struct StepSpanAttributes {
90
+ /// Execution ID
91
+ pub execution_id: String,
92
+ /// Step ID
93
+ pub step_id: String,
94
+ /// Step type
95
+ pub step_type: String,
96
+ /// Step name
97
+ pub name: String,
98
+ }
99
+
100
+ impl StepSpanAttributes {
101
+ /// Create from ExecutionId and StepId
102
+ pub fn new(
103
+ execution_id: &ExecutionId,
104
+ step_id: &StepId,
105
+ step_type: StepType,
106
+ name: impl Into<String>,
107
+ ) -> Self {
108
+ Self {
109
+ execution_id: execution_id.as_str().to_string(),
110
+ step_id: step_id.as_str().to_string(),
111
+ step_type: step_type.to_string(),
112
+ name: name.into(),
113
+ }
114
+ }
115
+
116
+ /// Convert to a map of attributes for OpenTelemetry
117
+ pub fn to_attributes(&self) -> Vec<(&'static str, String)> {
118
+ vec![
119
+ ("enact.execution_id", self.execution_id.clone()),
120
+ ("enact.step_id", self.step_id.clone()),
121
+ ("enact.step_type", self.step_type.clone()),
122
+ ("enact.step_name", self.name.clone()),
123
+ ]
124
+ }
125
+ }
126
+
127
+ /// Span attributes for tool calls
128
+ pub struct ToolSpanAttributes {
129
+ /// Execution ID
130
+ pub execution_id: String,
131
+ /// Step ID
132
+ pub step_id: String,
133
+ /// Tool name
134
+ pub tool_name: String,
135
+ }
136
+
137
+ impl ToolSpanAttributes {
138
+ /// Create from ExecutionId, StepId, and tool name
139
+ pub fn new(
140
+ execution_id: &ExecutionId,
141
+ step_id: &StepId,
142
+ tool_name: impl Into<String>,
143
+ ) -> Self {
144
+ Self {
145
+ execution_id: execution_id.as_str().to_string(),
146
+ step_id: step_id.as_str().to_string(),
147
+ tool_name: tool_name.into(),
148
+ }
149
+ }
150
+
151
+ /// Convert to a map of attributes for OpenTelemetry
152
+ pub fn to_attributes(&self) -> Vec<(&'static str, String)> {
153
+ vec![
154
+ ("enact.execution_id", self.execution_id.clone()),
155
+ ("enact.step_id", self.step_id.clone()),
156
+ ("enact.tool_name", self.tool_name.clone()),
157
+ ]
158
+ }
159
+ }
160
+
161
+ /// Span attributes for LLM calls
162
+ pub struct LlmSpanAttributes {
163
+ /// Execution ID
164
+ pub execution_id: String,
165
+ /// Step ID
166
+ pub step_id: String,
167
+ /// Model provider
168
+ pub provider: String,
169
+ /// Model name
170
+ pub model: String,
171
+ /// Input tokens (optional)
172
+ pub input_tokens: Option<u32>,
173
+ /// Output tokens (optional)
174
+ pub output_tokens: Option<u32>,
175
+ }
176
+
177
+ impl LlmSpanAttributes {
178
+ /// Create from ExecutionId, StepId, provider, and model
179
+ pub fn new(
180
+ execution_id: &ExecutionId,
181
+ step_id: &StepId,
182
+ provider: impl Into<String>,
183
+ model: impl Into<String>,
184
+ ) -> Self {
185
+ Self {
186
+ execution_id: execution_id.as_str().to_string(),
187
+ step_id: step_id.as_str().to_string(),
188
+ provider: provider.into(),
189
+ model: model.into(),
190
+ input_tokens: None,
191
+ output_tokens: None,
192
+ }
193
+ }
194
+
195
+ /// Add token counts
196
+ pub fn with_tokens(mut self, input: u32, output: u32) -> Self {
197
+ self.input_tokens = Some(input);
198
+ self.output_tokens = Some(output);
199
+ self
200
+ }
201
+
202
+ /// Convert to a map of attributes for OpenTelemetry
203
+ pub fn to_attributes(&self) -> Vec<(&'static str, String)> {
204
+ let mut attrs = vec![
205
+ ("enact.execution_id", self.execution_id.clone()),
206
+ ("enact.step_id", self.step_id.clone()),
207
+ ("gen_ai.system", self.provider.clone()),
208
+ ("gen_ai.request.model", self.model.clone()),
209
+ ];
210
+
211
+ if let Some(tokens) = self.input_tokens {
212
+ attrs.push(("gen_ai.usage.input_tokens", tokens.to_string()));
213
+ }
214
+ if let Some(tokens) = self.output_tokens {
215
+ attrs.push(("gen_ai.usage.output_tokens", tokens.to_string()));
216
+ }
217
+
218
+ attrs
219
+ }
220
+ }
221
+
222
+ /// Create span name for an execution
223
+ pub fn execution_span_name(execution_id: &ExecutionId) -> String {
224
+ format!("execution:{}", execution_id.as_str())
225
+ }
226
+
227
+ /// Create span name for a step
228
+ pub fn step_span_name(step_type: &StepType, name: &str) -> String {
229
+ format!("step:{}:{}", step_type, name)
230
+ }
231
+
232
+ /// Create span name for a tool call
233
+ pub fn tool_span_name(tool_name: &str) -> String {
234
+ format!("tool:{}", tool_name)
235
+ }
236
+
237
+ /// Create span name for an LLM call
238
+ pub fn llm_span_name(provider: &str, model: &str) -> String {
239
+ format!("llm:{}:{}", provider, model)
240
+ }
241
+
242
+ /// Extract trace context from a RuntimeContext trace
243
+ pub fn extract_trace_context(trace: &TraceContext) -> (String, String) {
244
+ (trace.trace_id.clone(), trace.span_id.clone())
245
+ }
@@ -0,0 +1,177 @@
1
+ //! AgentTool - wraps a Callable as a Tool (agent-as-tool pattern)
2
+ //!
3
+ //! ⚠️ **SECURITY WARNING: PRIVILEGED ADAPTER**
4
+ //!
5
+ //! `AgentTool` is **NOT** a general-purpose adapter. It is a **privileged** component
6
+ //! that must only be constructed by **trusted runtime components**.
7
+ //!
8
+ //! ## Why This Is Dangerous
9
+ //!
10
+ //! `AgentTool` allows exposing a `Callable` (which may be an agent, graph, or other
11
+ //! complex execution unit) as a `Tool`. This creates a powerful capability:
12
+ //!
13
+ //! - **Callable** = execution (agents, graphs, complex workflows)
14
+ //! - **Tool** = capability (side-effect functions for LLMs to invoke)
15
+ //!
16
+ //! Wrapping a Callable as a Tool means an LLM can invoke agents/graphs as tools,
17
+ //! creating recursive agent execution patterns. This is powerful but **dangerous**
18
+ //! if exposed to untrusted contexts.
19
+ //!
20
+ //! ## Security Invariant
21
+ //!
22
+ //! **AgentTool may only be constructed by trusted runtime components.**
23
+ //!
24
+ //! This means:
25
+ //! - ✅ **Allowed**: Kernel, runner, or other trusted execution components
26
+ //! - ❌ **Forbidden**: User-provided code, untrusted plugins, dynamic tool registries
27
+ //!
28
+ //! ## Why This Matters
29
+ //!
30
+ //! If `AgentTool` is exposed to untrusted contexts:
31
+ //! - Malicious users could expose arbitrary agents as tools
32
+ //! - Untrusted code could create recursive agent loops
33
+ //! - Policy boundaries could be bypassed
34
+ //! - Quota limits could be circumvented
35
+ //!
36
+ //! ## Usage Pattern
37
+ //!
38
+ //! ```rust,ignore
39
+ //! // ✅ CORRECT: Created by trusted runtime component
40
+ //! // In kernel or runner, with proper policy checks
41
+ //! let agent = Arc::new(LlmCallable::new(...));
42
+ //! let agent_tool = AgentTool::new(agent, "agent_name", "Agent description");
43
+ //!
44
+ //! // ❌ WRONG: Created from user input or untrusted source
45
+ //! // let agent_tool = AgentTool::from_user_input(...); // DON'T DO THIS!
46
+ //! ```
47
+ //!
48
+ //! ## Policy Enforcement
49
+ //!
50
+ //! Even when created by trusted components, `AgentTool` instances must still:
51
+ //! - Go through `ToolExecutor` for execution (policy enforcement)
52
+ //! - Respect `ToolPolicy` trust levels
53
+ //! - Be subject to quota limits
54
+ //! - Follow the same security boundaries as any other tool
55
+ //!
56
+ //! The privilege is in **construction**, not in **execution**.
57
+
58
+ use super::Tool;
59
+ use crate::callable::DynCallable;
60
+ use async_trait::async_trait;
61
+ use serde_json::{json, Value};
62
+
63
+ /// AgentTool - wraps a Callable as a Tool
64
+ ///
65
+ /// ⚠️ **PRIVILEGED**: This adapter may only be constructed by trusted runtime components.
66
+ /// See module-level documentation for security implications.
67
+ ///
68
+ /// This allows exposing agents, graphs, or other Callables as tools that can be
69
+ /// invoked by LLMs. The Callable's `run()` method is invoked with the tool's
70
+ /// JSON arguments serialized as a string.
71
+ pub struct AgentTool {
72
+ /// The underlying callable being wrapped
73
+ callable: DynCallable,
74
+ /// Tool name (may differ from callable name)
75
+ name: String,
76
+ /// Tool description
77
+ description: String,
78
+ /// JSON schema for tool parameters
79
+ parameters: Value,
80
+ }
81
+
82
+ impl AgentTool {
83
+ /// Create a new AgentTool
84
+ ///
85
+ /// ⚠️ **SECURITY**: This constructor is privileged. Only trusted runtime components
86
+ /// (kernel, runner) should create `AgentTool` instances.
87
+ ///
88
+ /// # Arguments
89
+ ///
90
+ /// * `callable` - The callable to wrap (agent, graph, etc.)
91
+ /// * `name` - Tool name (for LLM tool schema)
92
+ /// * `description` - Tool description (for LLM tool schema)
93
+ /// * `parameters` - JSON schema for tool parameters (defaults to empty object)
94
+ ///
95
+ /// # Example
96
+ ///
97
+ /// ```rust,ignore
98
+ /// // ✅ CORRECT: Created by trusted runtime
99
+ /// let agent = Arc::new(LlmCallable::new(...));
100
+ /// let tool = AgentTool::new(
101
+ /// agent,
102
+ /// "sub_agent",
103
+ /// "A sub-agent that handles specific tasks",
104
+ /// json!({
105
+ /// "type": "object",
106
+ /// "properties": {
107
+ /// "task": {"type": "string", "description": "Task description"}
108
+ /// },
109
+ /// "required": ["task"]
110
+ /// })
111
+ /// );
112
+ /// ```
113
+ pub fn new(
114
+ callable: DynCallable,
115
+ name: impl Into<String>,
116
+ description: impl Into<String>,
117
+ parameters: Value,
118
+ ) -> Self {
119
+ Self {
120
+ callable,
121
+ name: name.into(),
122
+ description: description.into(),
123
+ parameters,
124
+ }
125
+ }
126
+
127
+ /// Create a new AgentTool with default empty parameters schema
128
+ ///
129
+ /// ⚠️ **SECURITY**: Same privilege requirements as `new()`.
130
+ pub fn simple(
131
+ callable: DynCallable,
132
+ name: impl Into<String>,
133
+ description: impl Into<String>,
134
+ ) -> Self {
135
+ Self::new(
136
+ callable,
137
+ name,
138
+ description,
139
+ json!({
140
+ "type": "object",
141
+ "properties": {},
142
+ "required": []
143
+ }),
144
+ )
145
+ }
146
+ }
147
+
148
+ #[async_trait]
149
+ impl Tool for AgentTool {
150
+ fn name(&self) -> &str {
151
+ &self.name
152
+ }
153
+
154
+ fn description(&self) -> &str {
155
+ &self.description
156
+ }
157
+
158
+ fn parameters_schema(&self) -> Value {
159
+ self.parameters.clone()
160
+ }
161
+
162
+ async fn execute(&self, args: Value) -> anyhow::Result<Value> {
163
+ // Serialize JSON arguments to string for Callable::run()
164
+ let input = serde_json::to_string(&args)?;
165
+
166
+ // Execute the underlying callable
167
+ let output = self.callable.run(&input).await?;
168
+
169
+ // Parse output as JSON (callable returns string, but we expect JSON)
170
+ // If parsing fails, wrap the string in a JSON object
171
+ match serde_json::from_str::<Value>(&output) {
172
+ Ok(json) => Ok(json),
173
+ Err(_) => Ok(json!({ "result": output })),
174
+ }
175
+ }
176
+ }
177
+
File without changes
@@ -0,0 +1,247 @@
1
+ //! Cost tracking tool for monitoring API usage
2
+
3
+ use crate::tool::Tool;
4
+ use async_trait::async_trait;
5
+ use serde::{Deserialize, Serialize};
6
+ use serde_json::json;
7
+ use std::collections::HashMap;
8
+ use std::sync::atomic::{AtomicU64, Ordering};
9
+ use std::sync::Arc;
10
+ use std::time::SystemTime;
11
+
12
+ /// Cost tracking for LLM API calls
13
+ #[derive(Debug, Clone, Default, Serialize, Deserialize)]
14
+ pub struct CostMetrics {
15
+ pub total_requests: u64,
16
+ pub total_input_tokens: u64,
17
+ pub total_output_tokens: u64,
18
+ pub total_cost_usd: f64,
19
+ pub requests_by_model: HashMap<String, u64>,
20
+ pub cost_by_model: HashMap<String, f64>,
21
+ }
22
+
23
+ /// Cost tracker with thread-safe counters
24
+ pub struct CostTracker {
25
+ metrics: Arc<std::sync::Mutex<CostMetrics>>,
26
+ model_pricing: HashMap<String, ModelPricing>,
27
+ }
28
+
29
+ #[derive(Debug, Clone)]
30
+ struct ModelPricing {
31
+ input_price_per_1k: f64, // USD per 1K input tokens
32
+ output_price_per_1k: f64, // USD per 1K output tokens
33
+ }
34
+
35
+ impl CostTracker {
36
+ pub fn new() -> Self {
37
+ let mut model_pricing = HashMap::new();
38
+
39
+ // OpenAI pricing (as of 2024)
40
+ model_pricing.insert(
41
+ "gpt-4o".to_string(),
42
+ ModelPricing {
43
+ input_price_per_1k: 0.0025,
44
+ output_price_per_1k: 0.01,
45
+ },
46
+ );
47
+ model_pricing.insert(
48
+ "gpt-4o-mini".to_string(),
49
+ ModelPricing {
50
+ input_price_per_1k: 0.00015,
51
+ output_price_per_1k: 0.0006,
52
+ },
53
+ );
54
+ model_pricing.insert(
55
+ "gpt-4-turbo".to_string(),
56
+ ModelPricing {
57
+ input_price_per_1k: 0.01,
58
+ output_price_per_1k: 0.03,
59
+ },
60
+ );
61
+
62
+ // Anthropic pricing
63
+ model_pricing.insert(
64
+ "claude-3-opus".to_string(),
65
+ ModelPricing {
66
+ input_price_per_1k: 0.015,
67
+ output_price_per_1k: 0.075,
68
+ },
69
+ );
70
+ model_pricing.insert(
71
+ "claude-3-sonnet".to_string(),
72
+ ModelPricing {
73
+ input_price_per_1k: 0.003,
74
+ output_price_per_1k: 0.015,
75
+ },
76
+ );
77
+
78
+ // Gemini pricing
79
+ model_pricing.insert(
80
+ "gemini-pro".to_string(),
81
+ ModelPricing {
82
+ input_price_per_1k: 0.0005,
83
+ output_price_per_1k: 0.0015,
84
+ },
85
+ );
86
+
87
+ Self {
88
+ metrics: Arc::new(std::sync::Mutex::new(CostMetrics::default())),
89
+ model_pricing,
90
+ }
91
+ }
92
+
93
+ pub fn track_request(
94
+ &self,
95
+ model: &str,
96
+ input_tokens: u64,
97
+ output_tokens: u64,
98
+ ) {
99
+ let mut metrics = self.metrics.lock().unwrap();
100
+
101
+ metrics.total_requests += 1;
102
+ metrics.total_input_tokens += input_tokens;
103
+ metrics.total_output_tokens += output_tokens;
104
+
105
+ // Track by model
106
+ *metrics.requests_by_model.entry(model.to_string()).or_insert(0) += 1;
107
+
108
+ // Calculate cost
109
+ let cost = if let Some(pricing) = self.model_pricing.get(model) {
110
+ let input_cost = (input_tokens as f64 / 1000.0) * pricing.input_price_per_1k;
111
+ let output_cost = (output_tokens as f64 / 1000.0) * pricing.output_price_per_1k;
112
+ input_cost + output_cost
113
+ } else {
114
+ // Default pricing if model unknown
115
+ let input_cost = (input_tokens as f64 / 1000.0) * 0.001;
116
+ let output_cost = (output_tokens as f64 / 1000.0) * 0.002;
117
+ input_cost + output_cost
118
+ };
119
+
120
+ metrics.total_cost_usd += cost;
121
+ *metrics.cost_by_model.entry(model.to_string()).or_insert(0.0) += cost;
122
+ }
123
+
124
+ pub fn get_metrics(&self) -> CostMetrics {
125
+ self.metrics.lock().unwrap().clone()
126
+ }
127
+
128
+ pub fn reset(&self) {
129
+ let mut metrics = self.metrics.lock().unwrap();
130
+ *metrics = CostMetrics::default();
131
+ }
132
+ }
133
+
134
+ impl Default for CostTracker {
135
+ fn default() -> Self {
136
+ Self::new()
137
+ }
138
+ }
139
+
140
+ /// Cost tool for querying cost metrics
141
+ pub struct CostTool {
142
+ tracker: Arc<CostTracker>,
143
+ }
144
+
145
+ impl CostTool {
146
+ pub fn new(tracker: Arc<CostTracker>) -> Self {
147
+ Self { tracker }
148
+ }
149
+ }
150
+
151
+ #[async_trait]
152
+ impl Tool for CostTool {
153
+ fn name(&self) -> &str {
154
+ "cost"
155
+ }
156
+
157
+ fn description(&self) -> &str {
158
+ "Get cost metrics and usage statistics for LLM API calls"
159
+ }
160
+
161
+ fn parameters_schema(&self) -> serde_json::Value {
162
+ json!({
163
+ "type": "object",
164
+ "properties": {
165
+ "action": {
166
+ "type": "string",
167
+ "enum": ["get", "reset"],
168
+ "description": "Action to perform",
169
+ "default": "get"
170
+ }
171
+ }
172
+ })
173
+ }
174
+
175
+ fn requires_network(&self) -> bool {
176
+ false
177
+ }
178
+
179
+ async fn execute(&self, args: serde_json::Value) -> anyhow::Result<serde_json::Value> {
180
+ let action = args
181
+ .get("action")
182
+ .and_then(|v| v.as_str())
183
+ .unwrap_or("get");
184
+
185
+ match action {
186
+ "reset" => {
187
+ self.tracker.reset();
188
+ Ok(json!({
189
+ "success": true,
190
+ "message": "Cost metrics reset"
191
+ }))
192
+ }
193
+ _ => {
194
+ let metrics = self.tracker.get_metrics();
195
+ Ok(json!({
196
+ "success": true,
197
+ "metrics": metrics
198
+ }))
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ #[cfg(test)]
205
+ mod tests {
206
+ use super::*;
207
+
208
+ #[test]
209
+ fn test_cost_tracker() {
210
+ let tracker = CostTracker::new();
211
+
212
+ tracker.track_request("gpt-4o-mini", 1000, 500);
213
+ tracker.track_request("gpt-4o-mini", 2000, 1000);
214
+
215
+ let metrics = tracker.get_metrics();
216
+ assert_eq!(metrics.total_requests, 2);
217
+ assert_eq!(metrics.total_input_tokens, 3000);
218
+ assert_eq!(metrics.total_output_tokens, 1500);
219
+ assert!(metrics.total_cost_usd > 0.0);
220
+
221
+ // Check model breakdown
222
+ assert_eq!(metrics.requests_by_model.get("gpt-4o-mini"), Some(&2));
223
+ }
224
+
225
+ #[test]
226
+ fn test_cost_tracker_reset() {
227
+ let tracker = CostTracker::new();
228
+ tracker.track_request("gpt-4o-mini", 1000, 500);
229
+
230
+ tracker.reset();
231
+ let metrics = tracker.get_metrics();
232
+ assert_eq!(metrics.total_requests, 0);
233
+ assert_eq!(metrics.total_cost_usd, 0.0);
234
+ }
235
+
236
+ #[tokio::test]
237
+ async fn test_cost_tool_get() {
238
+ let tracker = Arc::new(CostTracker::new());
239
+ tracker.track_request("gpt-4o-mini", 1000, 500);
240
+
241
+ let tool = CostTool::new(tracker);
242
+ let result = tool.execute(json!({"action": "get"})).await.unwrap();
243
+
244
+ assert_eq!(result["success"], true);
245
+ assert!(result["metrics"]["total_requests"].as_u64().unwrap() > 0);
246
+ }
247
+ }
File without changes