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,645 @@
1
+ //! Step Context Builder
2
+ //!
3
+ //! Extracts learnings and builds context when steps are discovered.
4
+ //! Used during agentic execution when StepSource::Agent spawns new steps.
5
+ //!
6
+ //! @see packages/enact-schemas/src/context.schemas.ts
7
+
8
+ use crate::segment::{ContextPriority, ContextSegment, ContextSegmentType};
9
+ use crate::token_counter::TokenCounter;
10
+ use chrono::{DateTime, Utc};
11
+ use enact_core::kernel::{ExecutionId, StepId};
12
+ use serde::{Deserialize, Serialize};
13
+ use std::collections::HashMap;
14
+ use std::sync::atomic::{AtomicU64, Ordering};
15
+
16
+ /// Global sequence counter for segments
17
+ static STEP_SEQUENCE: AtomicU64 = AtomicU64::new(2000);
18
+
19
+ fn next_sequence() -> u64 {
20
+ STEP_SEQUENCE.fetch_add(1, Ordering::SeqCst)
21
+ }
22
+
23
+ /// Context extraction configuration
24
+ #[derive(Debug, Clone, Serialize, Deserialize)]
25
+ #[serde(rename_all = "camelCase")]
26
+ pub struct StepContextConfig {
27
+ /// Maximum tokens for step context
28
+ pub max_tokens: usize,
29
+
30
+ /// Include tool call results
31
+ pub include_tool_results: bool,
32
+
33
+ /// Include reasoning/chain-of-thought
34
+ pub include_reasoning: bool,
35
+
36
+ /// Include error context
37
+ pub include_errors: bool,
38
+
39
+ /// Maximum tool results to include
40
+ pub max_tool_results: usize,
41
+
42
+ /// Truncate long content
43
+ pub truncate_long_content: bool,
44
+
45
+ /// Maximum content length before truncation
46
+ pub max_content_length: usize,
47
+ }
48
+
49
+ impl Default for StepContextConfig {
50
+ fn default() -> Self {
51
+ Self {
52
+ max_tokens: 2000,
53
+ include_tool_results: true,
54
+ include_reasoning: true,
55
+ include_errors: true,
56
+ max_tool_results: 5,
57
+ truncate_long_content: true,
58
+ max_content_length: 1000,
59
+ }
60
+ }
61
+ }
62
+
63
+ /// Extracted learning from a step
64
+ #[derive(Debug, Clone, Serialize, Deserialize)]
65
+ #[serde(rename_all = "camelCase")]
66
+ pub struct StepLearning {
67
+ /// Unique ID for this learning
68
+ pub id: String,
69
+
70
+ /// Step that produced this learning
71
+ pub step_id: StepId,
72
+
73
+ /// Execution containing the step
74
+ pub execution_id: ExecutionId,
75
+
76
+ /// Type of learning
77
+ pub learning_type: LearningType,
78
+
79
+ /// The learning content
80
+ pub content: String,
81
+
82
+ /// Confidence in this learning (0.0 - 1.0)
83
+ pub confidence: f64,
84
+
85
+ /// Relevance to future steps (0.0 - 1.0)
86
+ pub relevance: f64,
87
+
88
+ /// Tags for categorization
89
+ pub tags: Vec<String>,
90
+
91
+ /// Timestamp
92
+ pub created_at: DateTime<Utc>,
93
+ }
94
+
95
+ /// Types of learnings that can be extracted
96
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
97
+ #[serde(rename_all = "snake_case")]
98
+ pub enum LearningType {
99
+ /// Successful action pattern
100
+ SuccessPattern,
101
+ /// Error and recovery
102
+ ErrorRecovery,
103
+ /// Tool usage insight
104
+ ToolInsight,
105
+ /// Decision rationale
106
+ DecisionRationale,
107
+ /// Domain knowledge
108
+ DomainKnowledge,
109
+ /// Constraint discovered
110
+ ConstraintDiscovered,
111
+ /// User preference
112
+ UserPreference,
113
+ }
114
+
115
+ /// Result of step context extraction
116
+ #[derive(Debug, Clone, Serialize, Deserialize)]
117
+ #[serde(rename_all = "camelCase")]
118
+ pub struct StepContextResult {
119
+ /// Execution ID
120
+ pub execution_id: ExecutionId,
121
+
122
+ /// Step ID that was processed
123
+ pub step_id: StepId,
124
+
125
+ /// Extracted context segments
126
+ pub segments: Vec<ContextSegment>,
127
+
128
+ /// Extracted learnings
129
+ pub learnings: Vec<StepLearning>,
130
+
131
+ /// Total tokens in extracted context
132
+ pub total_tokens: usize,
133
+
134
+ /// Processing timestamp
135
+ pub processed_at: DateTime<Utc>,
136
+ }
137
+
138
+ /// Step Context Builder - extracts learnings when steps are discovered
139
+ pub struct StepContextBuilder {
140
+ token_counter: TokenCounter,
141
+ config: StepContextConfig,
142
+ }
143
+
144
+ impl StepContextBuilder {
145
+ /// Create a new builder with default config
146
+ pub fn new() -> Self {
147
+ Self {
148
+ token_counter: TokenCounter::default(),
149
+ config: StepContextConfig::default(),
150
+ }
151
+ }
152
+
153
+ /// Create with custom config
154
+ pub fn with_config(config: StepContextConfig) -> Self {
155
+ Self {
156
+ token_counter: TokenCounter::default(),
157
+ config,
158
+ }
159
+ }
160
+
161
+ /// Build context from step data
162
+ pub fn build_context(
163
+ &self,
164
+ execution_id: ExecutionId,
165
+ step_id: StepId,
166
+ step_type: &str,
167
+ input: &str,
168
+ output: Option<&str>,
169
+ tool_calls: &[ToolCallInfo],
170
+ error: Option<&str>,
171
+ metadata: &HashMap<String, String>,
172
+ ) -> StepContextResult {
173
+ let mut segments = Vec::new();
174
+ let mut learnings = Vec::new();
175
+ let mut total_tokens = 0;
176
+
177
+ // Extract main step context
178
+ let step_summary = self.build_step_summary(step_type, input, output);
179
+ let summary_tokens = self.token_counter.count(&step_summary);
180
+
181
+ if total_tokens + summary_tokens <= self.config.max_tokens {
182
+ segments.push(ContextSegment::history(
183
+ step_summary.clone(),
184
+ summary_tokens,
185
+ next_sequence(),
186
+ ));
187
+ total_tokens += summary_tokens;
188
+ }
189
+
190
+ // Extract tool results
191
+ if self.config.include_tool_results {
192
+ let tool_context = self.extract_tool_context(tool_calls, step_id.clone());
193
+ for segment in tool_context {
194
+ let tokens = segment.token_count;
195
+ if total_tokens + tokens <= self.config.max_tokens {
196
+ total_tokens += tokens;
197
+ segments.push(segment);
198
+ }
199
+ }
200
+ }
201
+
202
+ // Extract error context
203
+ if self.config.include_errors {
204
+ if let Some(err) = error {
205
+ let error_learning = self.extract_error_learning(
206
+ execution_id.clone(),
207
+ step_id.clone(),
208
+ err,
209
+ );
210
+ learnings.push(error_learning);
211
+
212
+ let error_content = format!("Error encountered: {}", self.truncate_content(err));
213
+ let error_tokens = self.token_counter.count(&error_content);
214
+ let error_segment = ContextSegment::tool_results(
215
+ error_content,
216
+ error_tokens,
217
+ next_sequence(),
218
+ step_id.clone(),
219
+ ).with_priority(ContextPriority::High);
220
+
221
+ if total_tokens + error_tokens <= self.config.max_tokens {
222
+ total_tokens += error_tokens;
223
+ segments.push(error_segment);
224
+ }
225
+ }
226
+ }
227
+
228
+ // Extract learnings from successful execution
229
+ if error.is_none() && output.is_some() {
230
+ let success_learnings = self.extract_success_learnings(
231
+ execution_id.clone(),
232
+ step_id.clone(),
233
+ step_type,
234
+ tool_calls,
235
+ metadata,
236
+ );
237
+ learnings.extend(success_learnings);
238
+ }
239
+
240
+ StepContextResult {
241
+ execution_id,
242
+ step_id,
243
+ segments,
244
+ learnings,
245
+ total_tokens,
246
+ processed_at: Utc::now(),
247
+ }
248
+ }
249
+
250
+ /// Build a summary of the step
251
+ fn build_step_summary(&self, step_type: &str, input: &str, output: Option<&str>) -> String {
252
+ let truncated_input = self.truncate_content(input);
253
+ let truncated_output = output
254
+ .map(|o| self.truncate_content(o))
255
+ .unwrap_or_else(|| "(pending)".to_string());
256
+
257
+ format!(
258
+ "[Step: {}]\nInput: {}\nOutput: {}",
259
+ step_type, truncated_input, truncated_output
260
+ )
261
+ }
262
+
263
+ /// Extract context from tool calls
264
+ fn extract_tool_context(&self, tool_calls: &[ToolCallInfo], step_id: StepId) -> Vec<ContextSegment> {
265
+ tool_calls
266
+ .iter()
267
+ .take(self.config.max_tool_results)
268
+ .map(|tc| {
269
+ let content = format!(
270
+ "Tool: {}\nArgs: {}\nResult: {}",
271
+ tc.tool_name,
272
+ self.truncate_content(&tc.arguments),
273
+ tc.result
274
+ .as_ref()
275
+ .map(|r| self.truncate_content(r))
276
+ .unwrap_or_else(|| "(pending)".to_string())
277
+ );
278
+ let tokens = self.token_counter.count(&content);
279
+
280
+ ContextSegment::tool_results(
281
+ content,
282
+ tokens,
283
+ next_sequence(),
284
+ step_id.clone(),
285
+ ).with_priority(if tc.success {
286
+ ContextPriority::Medium
287
+ } else {
288
+ ContextPriority::High
289
+ })
290
+ })
291
+ .collect()
292
+ }
293
+
294
+ /// Extract learning from an error
295
+ fn extract_error_learning(
296
+ &self,
297
+ execution_id: ExecutionId,
298
+ step_id: StepId,
299
+ error: &str,
300
+ ) -> StepLearning {
301
+ StepLearning {
302
+ id: format!("learn_{}", uuid::Uuid::new_v4()),
303
+ step_id,
304
+ execution_id,
305
+ learning_type: LearningType::ErrorRecovery,
306
+ content: format!("Error encountered: {}. Consider alternative approaches.", error),
307
+ confidence: 0.7,
308
+ relevance: 0.8,
309
+ tags: vec!["error".to_string(), "recovery".to_string()],
310
+ created_at: Utc::now(),
311
+ }
312
+ }
313
+
314
+ /// Extract learnings from successful execution
315
+ fn extract_success_learnings(
316
+ &self,
317
+ execution_id: ExecutionId,
318
+ step_id: StepId,
319
+ step_type: &str,
320
+ tool_calls: &[ToolCallInfo],
321
+ metadata: &HashMap<String, String>,
322
+ ) -> Vec<StepLearning> {
323
+ let mut learnings = Vec::new();
324
+
325
+ // Learn from successful tool usage
326
+ for tc in tool_calls.iter().filter(|tc| tc.success) {
327
+ learnings.push(StepLearning {
328
+ id: format!("learn_{}", uuid::Uuid::new_v4()),
329
+ step_id: step_id.clone(),
330
+ execution_id: execution_id.clone(),
331
+ learning_type: LearningType::ToolInsight,
332
+ content: format!(
333
+ "Tool '{}' succeeded with pattern: {}",
334
+ tc.tool_name,
335
+ self.truncate_content(&tc.arguments)
336
+ ),
337
+ confidence: 0.8,
338
+ relevance: 0.6,
339
+ tags: vec!["tool".to_string(), tc.tool_name.clone()],
340
+ created_at: Utc::now(),
341
+ });
342
+ }
343
+
344
+ // Learn from metadata hints
345
+ if let Some(pattern) = metadata.get("success_pattern") {
346
+ learnings.push(StepLearning {
347
+ id: format!("learn_{}", uuid::Uuid::new_v4()),
348
+ step_id: step_id.clone(),
349
+ execution_id: execution_id.clone(),
350
+ learning_type: LearningType::SuccessPattern,
351
+ content: format!("Step '{}' success pattern: {}", step_type, pattern),
352
+ confidence: 0.9,
353
+ relevance: 0.7,
354
+ tags: vec!["pattern".to_string(), step_type.to_string()],
355
+ created_at: Utc::now(),
356
+ });
357
+ }
358
+
359
+ learnings
360
+ }
361
+
362
+ /// Truncate content if too long
363
+ fn truncate_content(&self, content: &str) -> String {
364
+ if self.config.truncate_long_content && content.len() > self.config.max_content_length {
365
+ format!(
366
+ "{}... [truncated, {} chars total]",
367
+ &content[..self.config.max_content_length],
368
+ content.len()
369
+ )
370
+ } else {
371
+ content.to_string()
372
+ }
373
+ }
374
+
375
+ /// Build context for a child step being spawned
376
+ pub fn build_child_context(
377
+ &self,
378
+ parent_execution_id: ExecutionId,
379
+ parent_step_id: StepId,
380
+ child_step_id: StepId,
381
+ task: &str,
382
+ parent_context: &[ContextSegment],
383
+ ) -> StepContextResult {
384
+ let mut segments = Vec::new();
385
+ let mut total_tokens = 0;
386
+
387
+ // Add child task context
388
+ let task_content = format!(
389
+ "Sub-task spawned from parent step.\nTask: {}\nParent step: {}",
390
+ task,
391
+ parent_step_id.as_str()
392
+ );
393
+ let task_tokens = self.token_counter.count(&task_content);
394
+ let task_segment = ContextSegment::system(task_content, task_tokens);
395
+ total_tokens += task_tokens;
396
+ segments.push(task_segment);
397
+
398
+ // Include relevant parent context
399
+ for segment in parent_context {
400
+ if segment.priority >= ContextPriority::Medium {
401
+ let tokens = segment.token_count;
402
+ if total_tokens + tokens <= self.config.max_tokens {
403
+ total_tokens += tokens;
404
+ segments.push(segment.clone());
405
+ }
406
+ }
407
+ }
408
+
409
+ StepContextResult {
410
+ execution_id: parent_execution_id,
411
+ step_id: child_step_id,
412
+ segments,
413
+ learnings: Vec::new(),
414
+ total_tokens,
415
+ processed_at: Utc::now(),
416
+ }
417
+ }
418
+ }
419
+
420
+ impl Default for StepContextBuilder {
421
+ fn default() -> Self {
422
+ Self::new()
423
+ }
424
+ }
425
+
426
+ /// Information about a tool call
427
+ #[derive(Debug, Clone, Serialize, Deserialize)]
428
+ pub struct ToolCallInfo {
429
+ /// Tool name
430
+ pub tool_name: String,
431
+
432
+ /// Arguments as JSON string
433
+ pub arguments: String,
434
+
435
+ /// Result if completed
436
+ pub result: Option<String>,
437
+
438
+ /// Whether the call succeeded
439
+ pub success: bool,
440
+
441
+ /// Duration in milliseconds
442
+ pub duration_ms: Option<u64>,
443
+ }
444
+
445
+ impl ToolCallInfo {
446
+ /// Create a successful tool call
447
+ pub fn success(tool_name: impl Into<String>, arguments: impl Into<String>, result: impl Into<String>) -> Self {
448
+ Self {
449
+ tool_name: tool_name.into(),
450
+ arguments: arguments.into(),
451
+ result: Some(result.into()),
452
+ success: true,
453
+ duration_ms: None,
454
+ }
455
+ }
456
+
457
+ /// Create a failed tool call
458
+ pub fn failed(tool_name: impl Into<String>, arguments: impl Into<String>, error: impl Into<String>) -> Self {
459
+ Self {
460
+ tool_name: tool_name.into(),
461
+ arguments: arguments.into(),
462
+ result: Some(error.into()),
463
+ success: false,
464
+ duration_ms: None,
465
+ }
466
+ }
467
+ }
468
+
469
+ #[cfg(test)]
470
+ mod tests {
471
+ use super::*;
472
+
473
+ fn test_execution_id() -> ExecutionId {
474
+ ExecutionId::new()
475
+ }
476
+
477
+ fn test_step_id() -> StepId {
478
+ StepId::new()
479
+ }
480
+
481
+ #[test]
482
+ fn test_step_context_config_defaults() {
483
+ let config = StepContextConfig::default();
484
+ assert_eq!(config.max_tokens, 2000);
485
+ assert!(config.include_tool_results);
486
+ assert!(config.include_errors);
487
+ }
488
+
489
+ #[test]
490
+ fn test_build_context_basic() {
491
+ let builder = StepContextBuilder::new();
492
+ let result = builder.build_context(
493
+ test_execution_id(),
494
+ test_step_id(),
495
+ "llm_call",
496
+ "What is 2+2?",
497
+ Some("4"),
498
+ &[],
499
+ None,
500
+ &HashMap::new(),
501
+ );
502
+
503
+ assert!(!result.segments.is_empty());
504
+ assert!(result.total_tokens > 0);
505
+ }
506
+
507
+ #[test]
508
+ fn test_build_context_with_error() {
509
+ let builder = StepContextBuilder::new();
510
+ let result = builder.build_context(
511
+ test_execution_id(),
512
+ test_step_id(),
513
+ "tool_call",
514
+ "fetch data",
515
+ None,
516
+ &[],
517
+ Some("Connection timeout"),
518
+ &HashMap::new(),
519
+ );
520
+
521
+ assert!(!result.learnings.is_empty());
522
+ assert_eq!(result.learnings[0].learning_type, LearningType::ErrorRecovery);
523
+ }
524
+
525
+ #[test]
526
+ fn test_build_context_with_tool_calls() {
527
+ let builder = StepContextBuilder::new();
528
+ let tool_calls = vec![
529
+ ToolCallInfo::success("search", r#"{"query": "test"}"#, "Found 5 results"),
530
+ ToolCallInfo::failed("fetch", r#"{"url": "..."}"#, "404 Not Found"),
531
+ ];
532
+
533
+ let result = builder.build_context(
534
+ test_execution_id(),
535
+ test_step_id(),
536
+ "multi_tool",
537
+ "search and fetch",
538
+ Some("partial results"),
539
+ &tool_calls,
540
+ None,
541
+ &HashMap::new(),
542
+ );
543
+
544
+ // Should have tool result segments
545
+ assert!(result.segments.len() >= 2);
546
+ // Should have tool insight learning from successful call
547
+ assert!(result
548
+ .learnings
549
+ .iter()
550
+ .any(|l| l.learning_type == LearningType::ToolInsight));
551
+ }
552
+
553
+ #[test]
554
+ fn test_truncate_long_content() {
555
+ let config = StepContextConfig {
556
+ max_content_length: 50,
557
+ ..Default::default()
558
+ };
559
+ let builder = StepContextBuilder::with_config(config);
560
+
561
+ let long_content = "a".repeat(100);
562
+ let result = builder.build_context(
563
+ test_execution_id(),
564
+ test_step_id(),
565
+ "test",
566
+ &long_content,
567
+ None,
568
+ &[],
569
+ None,
570
+ &HashMap::new(),
571
+ );
572
+
573
+ // Content should be truncated
574
+ assert!(result.segments[0].content.contains("truncated"));
575
+ }
576
+
577
+ #[test]
578
+ fn test_build_child_context() {
579
+ let builder = StepContextBuilder::new();
580
+ let token_counter = TokenCounter::default();
581
+
582
+ let system_content = "Parent system context";
583
+ let system_tokens = token_counter.count(system_content);
584
+
585
+ let history_content = "Some history";
586
+ let history_tokens = token_counter.count(history_content);
587
+
588
+ let parent_context = vec![
589
+ ContextSegment::system(system_content, system_tokens),
590
+ ContextSegment::new(
591
+ ContextSegmentType::History,
592
+ history_content.to_string(),
593
+ history_tokens,
594
+ 1,
595
+ ).with_priority(ContextPriority::Low),
596
+ ];
597
+
598
+ let result = builder.build_child_context(
599
+ test_execution_id(),
600
+ test_step_id(),
601
+ StepId::new(),
602
+ "Analyze the data",
603
+ &parent_context,
604
+ );
605
+
606
+ // Should have task segment
607
+ assert!(result
608
+ .segments
609
+ .iter()
610
+ .any(|s| s.content.contains("Sub-task")));
611
+ // Should include high-priority parent context but not low
612
+ assert!(result
613
+ .segments
614
+ .iter()
615
+ .any(|s| s.content.contains("Parent system")));
616
+ }
617
+
618
+ #[test]
619
+ fn test_learning_types() {
620
+ let builder = StepContextBuilder::new();
621
+ let mut metadata = HashMap::new();
622
+ metadata.insert("success_pattern".to_string(), "retry with backoff".to_string());
623
+
624
+ let result = builder.build_context(
625
+ test_execution_id(),
626
+ test_step_id(),
627
+ "api_call",
628
+ "fetch user",
629
+ Some("user data"),
630
+ &[ToolCallInfo::success("http", "{}", "200 OK")],
631
+ None,
632
+ &metadata,
633
+ );
634
+
635
+ // Should have both tool insight and success pattern learnings
636
+ assert!(result
637
+ .learnings
638
+ .iter()
639
+ .any(|l| l.learning_type == LearningType::ToolInsight));
640
+ assert!(result
641
+ .learnings
642
+ .iter()
643
+ .any(|l| l.learning_type == LearningType::SuccessPattern));
644
+ }
645
+ }