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,826 @@
1
+ //! Result Condenser
2
+ //!
3
+ //! Condenses child execution traces to 1-2k token summaries.
4
+ //! Used to compress child callable results back into parent context.
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 CONDENSE_SEQUENCE: AtomicU64 = AtomicU64::new(3000);
18
+
19
+ fn next_sequence() -> u64 {
20
+ CONDENSE_SEQUENCE.fetch_add(1, Ordering::SeqCst)
21
+ }
22
+
23
+ /// Condensation configuration
24
+ #[derive(Debug, Clone, Serialize, Deserialize)]
25
+ #[serde(rename_all = "camelCase")]
26
+ pub struct CondenserConfig {
27
+ /// Target token count for condensed result
28
+ pub target_tokens: usize,
29
+
30
+ /// Maximum token count (hard limit)
31
+ pub max_tokens: usize,
32
+
33
+ /// Include step summaries
34
+ pub include_steps: bool,
35
+
36
+ /// Maximum steps to summarize
37
+ pub max_steps: usize,
38
+
39
+ /// Include tool call summaries
40
+ pub include_tools: bool,
41
+
42
+ /// Include error summaries
43
+ pub include_errors: bool,
44
+
45
+ /// Include timing information
46
+ pub include_timing: bool,
47
+
48
+ /// Preserve key decisions
49
+ pub preserve_decisions: bool,
50
+
51
+ /// Maximum decision count
52
+ pub max_decisions: usize,
53
+ }
54
+
55
+ impl Default for CondenserConfig {
56
+ fn default() -> Self {
57
+ Self {
58
+ target_tokens: 1500,
59
+ max_tokens: 2000,
60
+ include_steps: true,
61
+ max_steps: 10,
62
+ include_tools: true,
63
+ include_errors: true,
64
+ include_timing: true,
65
+ preserve_decisions: true,
66
+ max_decisions: 5,
67
+ }
68
+ }
69
+ }
70
+
71
+ impl CondenserConfig {
72
+ /// Minimal config for brief summaries
73
+ pub fn minimal() -> Self {
74
+ Self {
75
+ target_tokens: 500,
76
+ max_tokens: 750,
77
+ include_steps: false,
78
+ max_steps: 3,
79
+ include_tools: false,
80
+ include_errors: true,
81
+ include_timing: false,
82
+ preserve_decisions: true,
83
+ max_decisions: 2,
84
+ }
85
+ }
86
+
87
+ /// Detailed config for comprehensive summaries
88
+ pub fn detailed() -> Self {
89
+ Self {
90
+ target_tokens: 3000,
91
+ max_tokens: 4000,
92
+ include_steps: true,
93
+ max_steps: 20,
94
+ include_tools: true,
95
+ include_errors: true,
96
+ include_timing: true,
97
+ preserve_decisions: true,
98
+ max_decisions: 10,
99
+ }
100
+ }
101
+ }
102
+
103
+ /// Summary of a step for condensation
104
+ #[derive(Debug, Clone, Serialize, Deserialize)]
105
+ pub struct StepSummary {
106
+ /// Step ID
107
+ pub step_id: StepId,
108
+
109
+ /// Step type/name
110
+ pub step_type: String,
111
+
112
+ /// Brief description of what happened
113
+ pub summary: String,
114
+
115
+ /// Whether step succeeded
116
+ pub success: bool,
117
+
118
+ /// Duration in milliseconds
119
+ pub duration_ms: Option<u64>,
120
+
121
+ /// Key output (truncated if needed)
122
+ pub key_output: Option<String>,
123
+ }
124
+
125
+ /// Summary of a decision made during execution
126
+ #[derive(Debug, Clone, Serialize, Deserialize)]
127
+ pub struct DecisionSummary {
128
+ /// What decision was made
129
+ pub decision: String,
130
+
131
+ /// Rationale for the decision
132
+ pub rationale: String,
133
+
134
+ /// Confidence level
135
+ pub confidence: f64,
136
+
137
+ /// Step where decision was made
138
+ pub step_id: StepId,
139
+ }
140
+
141
+ /// Input for condensation - represents a child execution trace
142
+ #[derive(Debug, Clone, Serialize, Deserialize)]
143
+ pub struct ExecutionTrace {
144
+ /// Execution ID
145
+ pub execution_id: ExecutionId,
146
+
147
+ /// Parent execution ID (if any)
148
+ pub parent_execution_id: Option<ExecutionId>,
149
+
150
+ /// Parent step that spawned this execution
151
+ pub parent_step_id: Option<StepId>,
152
+
153
+ /// Execution start time
154
+ pub started_at: DateTime<Utc>,
155
+
156
+ /// Execution end time
157
+ pub ended_at: Option<DateTime<Utc>>,
158
+
159
+ /// Final status
160
+ pub status: ExecutionStatus,
161
+
162
+ /// Step summaries
163
+ pub steps: Vec<StepSummary>,
164
+
165
+ /// Key decisions made
166
+ pub decisions: Vec<DecisionSummary>,
167
+
168
+ /// Final output/result
169
+ pub final_output: Option<String>,
170
+
171
+ /// Error message if failed
172
+ pub error: Option<String>,
173
+
174
+ /// Metadata
175
+ pub metadata: HashMap<String, String>,
176
+ }
177
+
178
+ /// Execution status for trace
179
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
180
+ #[serde(rename_all = "snake_case")]
181
+ pub enum ExecutionStatus {
182
+ /// Completed successfully
183
+ Completed,
184
+ /// Failed with error
185
+ Failed,
186
+ /// Cancelled by user/system
187
+ Cancelled,
188
+ /// Timed out
189
+ TimedOut,
190
+ /// Still running
191
+ Running,
192
+ }
193
+
194
+ impl ExecutionTrace {
195
+ /// Create a new trace
196
+ pub fn new(execution_id: ExecutionId) -> Self {
197
+ Self {
198
+ execution_id,
199
+ parent_execution_id: None,
200
+ parent_step_id: None,
201
+ started_at: Utc::now(),
202
+ ended_at: None,
203
+ status: ExecutionStatus::Running,
204
+ steps: Vec::new(),
205
+ decisions: Vec::new(),
206
+ final_output: None,
207
+ error: None,
208
+ metadata: HashMap::new(),
209
+ }
210
+ }
211
+
212
+ /// Create with parent context
213
+ pub fn with_parent(
214
+ execution_id: ExecutionId,
215
+ parent_execution_id: ExecutionId,
216
+ parent_step_id: StepId,
217
+ ) -> Self {
218
+ Self {
219
+ execution_id,
220
+ parent_execution_id: Some(parent_execution_id),
221
+ parent_step_id: Some(parent_step_id),
222
+ started_at: Utc::now(),
223
+ ended_at: None,
224
+ status: ExecutionStatus::Running,
225
+ steps: Vec::new(),
226
+ decisions: Vec::new(),
227
+ final_output: None,
228
+ error: None,
229
+ metadata: HashMap::new(),
230
+ }
231
+ }
232
+
233
+ /// Mark as completed
234
+ pub fn complete(mut self, output: impl Into<String>) -> Self {
235
+ self.ended_at = Some(Utc::now());
236
+ self.status = ExecutionStatus::Completed;
237
+ self.final_output = Some(output.into());
238
+ self
239
+ }
240
+
241
+ /// Mark as failed
242
+ pub fn fail(mut self, error: impl Into<String>) -> Self {
243
+ self.ended_at = Some(Utc::now());
244
+ self.status = ExecutionStatus::Failed;
245
+ self.error = Some(error.into());
246
+ self
247
+ }
248
+
249
+ /// Add a step summary
250
+ pub fn add_step(mut self, step: StepSummary) -> Self {
251
+ self.steps.push(step);
252
+ self
253
+ }
254
+
255
+ /// Add a decision
256
+ pub fn add_decision(mut self, decision: DecisionSummary) -> Self {
257
+ self.decisions.push(decision);
258
+ self
259
+ }
260
+
261
+ /// Get duration in milliseconds
262
+ pub fn duration_ms(&self) -> Option<i64> {
263
+ self.ended_at.map(|end| {
264
+ (end - self.started_at).num_milliseconds()
265
+ })
266
+ }
267
+ }
268
+
269
+ /// Result of condensation
270
+ #[derive(Debug, Clone, Serialize, Deserialize)]
271
+ #[serde(rename_all = "camelCase")]
272
+ pub struct CondensedResult {
273
+ /// Source execution ID
274
+ pub execution_id: ExecutionId,
275
+
276
+ /// Condensed summary text
277
+ pub summary: String,
278
+
279
+ /// Key outcomes
280
+ pub outcomes: Vec<String>,
281
+
282
+ /// Important learnings
283
+ pub learnings: Vec<String>,
284
+
285
+ /// Context segment for parent
286
+ pub context_segment: ContextSegment,
287
+
288
+ /// Token count of condensed result
289
+ pub token_count: usize,
290
+
291
+ /// Original trace token estimate
292
+ pub original_tokens: usize,
293
+
294
+ /// Compression ratio
295
+ pub compression_ratio: f64,
296
+
297
+ /// Condensation timestamp
298
+ pub condensed_at: DateTime<Utc>,
299
+ }
300
+
301
+ /// Result Condenser - compresses execution traces for parent context
302
+ pub struct ResultCondenser {
303
+ token_counter: TokenCounter,
304
+ config: CondenserConfig,
305
+ }
306
+
307
+ impl ResultCondenser {
308
+ /// Create with default config
309
+ pub fn new() -> Self {
310
+ Self {
311
+ token_counter: TokenCounter::default(),
312
+ config: CondenserConfig::default(),
313
+ }
314
+ }
315
+
316
+ /// Create with custom config
317
+ pub fn with_config(config: CondenserConfig) -> Self {
318
+ Self {
319
+ token_counter: TokenCounter::default(),
320
+ config,
321
+ }
322
+ }
323
+
324
+ /// Condense an execution trace
325
+ pub fn condense(&self, trace: &ExecutionTrace) -> CondensedResult {
326
+ let mut parts: Vec<String> = Vec::new();
327
+ let mut outcomes: Vec<String> = Vec::new();
328
+ let mut learnings: Vec<String> = Vec::new();
329
+
330
+ // Header with execution info
331
+ let header = self.build_header(trace);
332
+ parts.push(header);
333
+
334
+ // Status and result
335
+ let status_section = self.build_status_section(trace, &mut outcomes);
336
+ parts.push(status_section);
337
+
338
+ // Steps summary (if enabled)
339
+ if self.config.include_steps && !trace.steps.is_empty() {
340
+ let steps_section = self.build_steps_section(&trace.steps);
341
+ parts.push(steps_section);
342
+ }
343
+
344
+ // Decisions (if enabled)
345
+ if self.config.preserve_decisions && !trace.decisions.is_empty() {
346
+ let decisions_section = self.build_decisions_section(&trace.decisions, &mut learnings);
347
+ parts.push(decisions_section);
348
+ }
349
+
350
+ // Error details (if any)
351
+ if self.config.include_errors {
352
+ if let Some(error) = &trace.error {
353
+ parts.push(format!("Error: {}", self.truncate(error, 200)));
354
+ learnings.push(format!("Failure mode: {}", self.truncate(error, 100)));
355
+ }
356
+ }
357
+
358
+ // Combine and check token count
359
+ let mut summary = parts.join("\n\n");
360
+ let mut token_count = self.token_counter.count(&summary);
361
+
362
+ // Truncate if over limit
363
+ if token_count > self.config.max_tokens {
364
+ let (truncated, new_count) = self.token_counter.truncate(&summary, self.config.target_tokens);
365
+ summary = truncated;
366
+ token_count = new_count;
367
+ }
368
+
369
+ // Estimate original token count
370
+ let original_tokens = self.estimate_original_tokens(trace);
371
+ let compression_ratio = if original_tokens > 0 {
372
+ token_count as f64 / original_tokens as f64
373
+ } else {
374
+ 1.0
375
+ };
376
+
377
+ // Create context segment for parent
378
+ let segment_content = format!(
379
+ "[Child Execution: {}]\n{}",
380
+ trace.execution_id.as_str(),
381
+ summary
382
+ );
383
+ let segment_tokens = self.token_counter.count(&segment_content);
384
+ let context_segment = ContextSegment::child_summary(
385
+ segment_content,
386
+ segment_tokens,
387
+ next_sequence(),
388
+ trace.parent_step_id.clone().unwrap_or_else(StepId::new),
389
+ ).with_priority(
390
+ if trace.status == ExecutionStatus::Completed {
391
+ ContextPriority::Medium
392
+ } else {
393
+ ContextPriority::High
394
+ }
395
+ );
396
+
397
+ CondensedResult {
398
+ execution_id: trace.execution_id.clone(),
399
+ summary,
400
+ outcomes,
401
+ learnings,
402
+ context_segment,
403
+ token_count,
404
+ original_tokens,
405
+ compression_ratio,
406
+ condensed_at: Utc::now(),
407
+ }
408
+ }
409
+
410
+ /// Build header section
411
+ fn build_header(&self, trace: &ExecutionTrace) -> String {
412
+ let mut header = format!("Execution: {}", trace.execution_id.as_str());
413
+
414
+ if self.config.include_timing {
415
+ if let Some(duration) = trace.duration_ms() {
416
+ header.push_str(&format!(" ({}ms)", duration));
417
+ }
418
+ }
419
+
420
+ if let Some(parent) = &trace.parent_step_id {
421
+ header.push_str(&format!("\nSpawned from: {}", parent.as_str()));
422
+ }
423
+
424
+ header
425
+ }
426
+
427
+ /// Build status section
428
+ fn build_status_section(&self, trace: &ExecutionTrace, outcomes: &mut Vec<String>) -> String {
429
+ let status_str = match trace.status {
430
+ ExecutionStatus::Completed => "COMPLETED",
431
+ ExecutionStatus::Failed => "FAILED",
432
+ ExecutionStatus::Cancelled => "CANCELLED",
433
+ ExecutionStatus::TimedOut => "TIMED_OUT",
434
+ ExecutionStatus::Running => "RUNNING",
435
+ };
436
+
437
+ let mut section = format!("Status: {}", status_str);
438
+
439
+ if let Some(output) = &trace.final_output {
440
+ let truncated = self.truncate(output, 300);
441
+ section.push_str(&format!("\nResult: {}", truncated));
442
+ outcomes.push(format!("Output: {}", self.truncate(output, 100)));
443
+ }
444
+
445
+ section
446
+ }
447
+
448
+ /// Build steps section
449
+ fn build_steps_section(&self, steps: &[StepSummary]) -> String {
450
+ let steps_to_show: Vec<_> = steps.iter().take(self.config.max_steps).collect();
451
+ let total = steps.len();
452
+ let shown = steps_to_show.len();
453
+
454
+ let mut lines: Vec<String> = vec![format!("Steps ({}/{}):", shown, total)];
455
+
456
+ for (i, step) in steps_to_show.iter().enumerate() {
457
+ let status = if step.success { "✓" } else { "✗" };
458
+ let mut line = format!(
459
+ " {}. {} {} - {}",
460
+ i + 1,
461
+ status,
462
+ step.step_type,
463
+ self.truncate(&step.summary, 50)
464
+ );
465
+
466
+ if self.config.include_timing {
467
+ if let Some(ms) = step.duration_ms {
468
+ line.push_str(&format!(" ({}ms)", ms));
469
+ }
470
+ }
471
+
472
+ lines.push(line);
473
+ }
474
+
475
+ if total > shown {
476
+ lines.push(format!(" ... and {} more steps", total - shown));
477
+ }
478
+
479
+ lines.join("\n")
480
+ }
481
+
482
+ /// Build decisions section
483
+ fn build_decisions_section(
484
+ &self,
485
+ decisions: &[DecisionSummary],
486
+ learnings: &mut Vec<String>,
487
+ ) -> String {
488
+ let decisions_to_show: Vec<_> = decisions.iter().take(self.config.max_decisions).collect();
489
+
490
+ let mut lines: Vec<String> = vec!["Key Decisions:".to_string()];
491
+
492
+ for decision in decisions_to_show {
493
+ lines.push(format!(
494
+ " • {} (confidence: {:.0}%)",
495
+ self.truncate(&decision.decision, 80),
496
+ decision.confidence * 100.0
497
+ ));
498
+
499
+ learnings.push(format!(
500
+ "Decision: {} - Rationale: {}",
501
+ self.truncate(&decision.decision, 50),
502
+ self.truncate(&decision.rationale, 50)
503
+ ));
504
+ }
505
+
506
+ lines.join("\n")
507
+ }
508
+
509
+ /// Estimate original token count from trace
510
+ fn estimate_original_tokens(&self, trace: &ExecutionTrace) -> usize {
511
+ let mut estimate = 0;
512
+
513
+ // Estimate from steps
514
+ for step in &trace.steps {
515
+ estimate += self.token_counter.count(&step.summary);
516
+ if let Some(output) = &step.key_output {
517
+ estimate += self.token_counter.count(output);
518
+ }
519
+ }
520
+
521
+ // Estimate from decisions
522
+ for decision in &trace.decisions {
523
+ estimate += self.token_counter.count(&decision.decision);
524
+ estimate += self.token_counter.count(&decision.rationale);
525
+ }
526
+
527
+ // Final output
528
+ if let Some(output) = &trace.final_output {
529
+ estimate += self.token_counter.count(output);
530
+ }
531
+
532
+ // Error
533
+ if let Some(error) = &trace.error {
534
+ estimate += self.token_counter.count(error);
535
+ }
536
+
537
+ estimate
538
+ }
539
+
540
+ /// Truncate text to max length
541
+ fn truncate(&self, text: &str, max_len: usize) -> String {
542
+ if text.len() <= max_len {
543
+ text.to_string()
544
+ } else {
545
+ format!("{}...", &text[..max_len.saturating_sub(3)])
546
+ }
547
+ }
548
+
549
+ /// Condense multiple traces (e.g., parallel child executions)
550
+ pub fn condense_multiple(&self, traces: &[ExecutionTrace]) -> CondensedResult {
551
+ if traces.is_empty() {
552
+ let empty_segment = ContextSegment::child_summary(
553
+ "No child executions".to_string(),
554
+ 3,
555
+ next_sequence(),
556
+ StepId::new(),
557
+ ).with_priority(ContextPriority::Low);
558
+
559
+ return CondensedResult {
560
+ execution_id: ExecutionId::new(),
561
+ summary: "No executions to condense".to_string(),
562
+ outcomes: Vec::new(),
563
+ learnings: Vec::new(),
564
+ context_segment: empty_segment,
565
+ token_count: 0,
566
+ original_tokens: 0,
567
+ compression_ratio: 1.0,
568
+ condensed_at: Utc::now(),
569
+ };
570
+ }
571
+
572
+ if traces.len() == 1 {
573
+ return self.condense(&traces[0]);
574
+ }
575
+
576
+ // Multi-trace condensation
577
+ let mut parts: Vec<String> = Vec::new();
578
+ let mut all_outcomes: Vec<String> = Vec::new();
579
+ let mut all_learnings: Vec<String> = Vec::new();
580
+ let mut total_original = 0;
581
+
582
+ parts.push(format!("Parallel Executions: {} total", traces.len()));
583
+
584
+ // Summarize each trace briefly
585
+ let tokens_per_trace = self.config.target_tokens / traces.len();
586
+ for (i, trace) in traces.iter().enumerate() {
587
+ let brief_config = CondenserConfig {
588
+ target_tokens: tokens_per_trace,
589
+ max_tokens: tokens_per_trace + 100,
590
+ include_steps: false,
591
+ max_steps: 3,
592
+ ..self.config.clone()
593
+ };
594
+
595
+ let condenser = ResultCondenser::with_config(brief_config);
596
+ let condensed = condenser.condense(trace);
597
+
598
+ parts.push(format!(
599
+ "\n[{}/{}] {}",
600
+ i + 1,
601
+ traces.len(),
602
+ condensed.summary
603
+ ));
604
+ all_outcomes.extend(condensed.outcomes);
605
+ all_learnings.extend(condensed.learnings);
606
+ total_original += condensed.original_tokens;
607
+ }
608
+
609
+ let summary = parts.join("\n");
610
+ let token_count = self.token_counter.count(&summary);
611
+
612
+ let segment_content = format!("[Parallel Executions]\n{}", summary);
613
+ let segment_tokens = self.token_counter.count(&segment_content);
614
+ let context_segment = ContextSegment::child_summary(
615
+ segment_content,
616
+ segment_tokens,
617
+ next_sequence(),
618
+ traces[0].parent_step_id.clone().unwrap_or_else(StepId::new),
619
+ ).with_priority(ContextPriority::Medium);
620
+
621
+ CondensedResult {
622
+ execution_id: traces[0].execution_id.clone(),
623
+ summary,
624
+ outcomes: all_outcomes,
625
+ learnings: all_learnings,
626
+ context_segment,
627
+ token_count,
628
+ original_tokens: total_original,
629
+ compression_ratio: if total_original > 0 {
630
+ token_count as f64 / total_original as f64
631
+ } else {
632
+ 1.0
633
+ },
634
+ condensed_at: Utc::now(),
635
+ }
636
+ }
637
+ }
638
+
639
+ impl Default for ResultCondenser {
640
+ fn default() -> Self {
641
+ Self::new()
642
+ }
643
+ }
644
+
645
+ #[cfg(test)]
646
+ mod tests {
647
+ use super::*;
648
+
649
+ fn test_execution_id() -> ExecutionId {
650
+ ExecutionId::new()
651
+ }
652
+
653
+ fn test_step_id() -> StepId {
654
+ StepId::new()
655
+ }
656
+
657
+ #[test]
658
+ fn test_condenser_config_defaults() {
659
+ let config = CondenserConfig::default();
660
+ assert_eq!(config.target_tokens, 1500);
661
+ assert!(config.include_steps);
662
+ }
663
+
664
+ #[test]
665
+ fn test_condense_simple_trace() {
666
+ let condenser = ResultCondenser::new();
667
+ let trace = ExecutionTrace::new(test_execution_id())
668
+ .complete("Task completed successfully");
669
+
670
+ let result = condenser.condense(&trace);
671
+
672
+ assert!(result.summary.contains("COMPLETED"));
673
+ assert!(result.token_count > 0);
674
+ }
675
+
676
+ #[test]
677
+ fn test_condense_with_steps() {
678
+ let condenser = ResultCondenser::new();
679
+ let trace = ExecutionTrace::new(test_execution_id())
680
+ .add_step(StepSummary {
681
+ step_id: test_step_id(),
682
+ step_type: "llm_call".to_string(),
683
+ summary: "Generated response".to_string(),
684
+ success: true,
685
+ duration_ms: Some(500),
686
+ key_output: Some("Response text".to_string()),
687
+ })
688
+ .add_step(StepSummary {
689
+ step_id: test_step_id(),
690
+ step_type: "tool_call".to_string(),
691
+ summary: "Called search API".to_string(),
692
+ success: true,
693
+ duration_ms: Some(200),
694
+ key_output: None,
695
+ })
696
+ .complete("Done");
697
+
698
+ let result = condenser.condense(&trace);
699
+
700
+ assert!(result.summary.contains("Steps"));
701
+ assert!(result.summary.contains("llm_call"));
702
+ assert!(result.summary.contains("tool_call"));
703
+ }
704
+
705
+ #[test]
706
+ fn test_condense_failed_trace() {
707
+ let condenser = ResultCondenser::new();
708
+ let trace = ExecutionTrace::new(test_execution_id())
709
+ .fail("Connection timeout after 30 seconds");
710
+
711
+ let result = condenser.condense(&trace);
712
+
713
+ assert!(result.summary.contains("FAILED"));
714
+ assert!(result.summary.contains("timeout"));
715
+ assert!(!result.learnings.is_empty());
716
+ }
717
+
718
+ #[test]
719
+ fn test_condense_with_decisions() {
720
+ let condenser = ResultCondenser::new();
721
+ let trace = ExecutionTrace::new(test_execution_id())
722
+ .add_decision(DecisionSummary {
723
+ decision: "Use caching strategy".to_string(),
724
+ rationale: "Reduce API calls".to_string(),
725
+ confidence: 0.85,
726
+ step_id: test_step_id(),
727
+ })
728
+ .complete("Done");
729
+
730
+ let result = condenser.condense(&trace);
731
+
732
+ assert!(result.summary.contains("Key Decisions"));
733
+ assert!(result.summary.contains("caching"));
734
+ assert!(!result.learnings.is_empty());
735
+ }
736
+
737
+ #[test]
738
+ fn test_condense_respects_token_limit() {
739
+ let config = CondenserConfig {
740
+ max_tokens: 100,
741
+ target_tokens: 50,
742
+ ..Default::default()
743
+ };
744
+ let condenser = ResultCondenser::with_config(config);
745
+
746
+ // Create trace with lots of content
747
+ let mut trace = ExecutionTrace::new(test_execution_id());
748
+ for i in 0..20 {
749
+ trace = trace.add_step(StepSummary {
750
+ step_id: test_step_id(),
751
+ step_type: format!("step_{}", i),
752
+ summary: format!("This is a detailed summary of step {} with lots of information", i),
753
+ success: true,
754
+ duration_ms: Some(100),
755
+ key_output: Some(format!("Output from step {}", i)),
756
+ });
757
+ }
758
+ trace = trace.complete("Final result with lots of detail");
759
+
760
+ let result = condenser.condense(&trace);
761
+
762
+ assert!(result.token_count <= 150); // Some tolerance
763
+ }
764
+
765
+ #[test]
766
+ fn test_condense_multiple_traces() {
767
+ let condenser = ResultCondenser::new();
768
+ let traces = vec![
769
+ ExecutionTrace::new(test_execution_id()).complete("Result 1"),
770
+ ExecutionTrace::new(test_execution_id()).complete("Result 2"),
771
+ ExecutionTrace::new(test_execution_id()).fail("Error in trace 3"),
772
+ ];
773
+
774
+ let result = condenser.condense_multiple(&traces);
775
+
776
+ assert!(result.summary.contains("Parallel Executions: 3"));
777
+ assert!(result.summary.contains("COMPLETED"));
778
+ assert!(result.summary.contains("FAILED"));
779
+ }
780
+
781
+ #[test]
782
+ fn test_compression_ratio() {
783
+ let condenser = ResultCondenser::new();
784
+ let mut trace = ExecutionTrace::new(test_execution_id());
785
+
786
+ // Add substantial content with long outputs
787
+ for i in 0..10 {
788
+ trace = trace.add_step(StepSummary {
789
+ step_id: test_step_id(),
790
+ step_type: "step".to_string(),
791
+ summary: format!("Detailed summary for step {} with additional context and more information to ensure we have enough content", i),
792
+ success: true,
793
+ duration_ms: Some(100),
794
+ key_output: Some(format!("Long output content for step {} that adds more tokens and even more details to increase the token count significantly beyond what will be included in the final summary. This should definitely be truncated.", i)),
795
+ });
796
+ }
797
+ trace = trace.complete("Comprehensive final output with all the details and extra information that extends the content significantly.");
798
+
799
+ let result = condenser.condense(&trace);
800
+
801
+ // Verify condensation happened (original should be larger due to key_output not being included)
802
+ assert!(result.original_tokens > 0);
803
+ assert!(result.token_count > 0);
804
+ // The condenser limits steps and truncates content, so we should see some compression
805
+ // Note: compression_ratio = token_count / original_tokens
806
+ // Due to formatting overhead, ratio might be close to 1.0 but original should still be larger
807
+ assert!(result.original_tokens >= result.token_count / 2,
808
+ "Original tokens ({}) should be at least half of final tokens ({})",
809
+ result.original_tokens, result.token_count);
810
+ }
811
+
812
+ #[test]
813
+ fn test_context_segment_priority() {
814
+ let condenser = ResultCondenser::new();
815
+
816
+ // Successful execution should have medium priority
817
+ let success_trace = ExecutionTrace::new(test_execution_id()).complete("Done");
818
+ let success_result = condenser.condense(&success_trace);
819
+ assert_eq!(success_result.context_segment.priority, ContextPriority::Medium);
820
+
821
+ // Failed execution should have high priority
822
+ let fail_trace = ExecutionTrace::new(test_execution_id()).fail("Error");
823
+ let fail_result = condenser.condense(&fail_trace);
824
+ assert_eq!(fail_result.context_segment.priority, ContextPriority::High);
825
+ }
826
+ }