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.
- package/.env.example +20 -0
- package/.github/workflows/ci.yml +70 -0
- package/.github/workflows/publish.yml +250 -0
- package/.gitmodules +3 -0
- package/Cargo.lock +3584 -0
- package/Cargo.toml +97 -0
- package/crates/enact/Cargo.toml +27 -0
- package/crates/enact/src/lib.rs +60 -0
- package/crates/enact-a2a/Cargo.toml +25 -0
- package/crates/enact-a2a/src/lib.rs +411 -0
- package/crates/enact-channels/Cargo.toml +64 -0
- package/crates/enact-channels/examples/README.md +80 -0
- package/crates/enact-channels/examples/channel_bot.rs +169 -0
- package/crates/enact-channels/examples/telegram-echo.rs +34 -0
- package/crates/enact-channels/examples/whatsapp-echo.rs +142 -0
- package/crates/enact-channels/src/config.rs +213 -0
- package/crates/enact-channels/src/lib.rs +25 -0
- package/crates/enact-channels/src/runtime.rs +237 -0
- package/crates/enact-channels/src/security/mod.rs +5 -0
- package/crates/enact-channels/src/security/pairing.rs +205 -0
- package/crates/enact-channels/src/teams.rs +601 -0
- package/crates/enact-channels/src/telegram.rs +2833 -0
- package/crates/enact-channels/src/traits.rs +200 -0
- package/crates/enact-channels/src/webhook.rs +262 -0
- package/crates/enact-channels/src/whatsapp.rs +310 -0
- package/crates/enact-cli/Cargo.toml +40 -0
- package/crates/enact-cli/src/commands/doctor.rs +62 -0
- package/crates/enact-cli/src/commands/mod.rs +3 -0
- package/crates/enact-cli/src/commands/run.rs +69 -0
- package/crates/enact-cli/src/commands/serve.rs +81 -0
- package/crates/enact-cli/src/config.rs +2 -0
- package/crates/enact-cli/src/main.rs +79 -0
- package/crates/enact-config/Cargo.toml +36 -0
- package/crates/enact-config/ENV_VAR_MAPPING.md +135 -0
- package/crates/enact-config/QUICK_REFERENCE.md +92 -0
- package/crates/enact-config/README.md +107 -0
- package/crates/enact-config/TESTING.md +161 -0
- package/crates/enact-config/examples/test-env-vars.rs +100 -0
- package/crates/enact-config/src/config.rs +399 -0
- package/crates/enact-config/src/encrypted_store.rs +211 -0
- package/crates/enact-config/src/lib.rs +298 -0
- package/crates/enact-config/src/secrets.rs +149 -0
- package/crates/enact-config/src/sync.rs +260 -0
- package/crates/enact-config/test-env-vars.sh +34 -0
- package/crates/enact-config/tests/README.md +99 -0
- package/crates/enact-config/tests/config_integration_test.rs +202 -0
- package/crates/enact-config/tests/security_test.rs +140 -0
- package/crates/enact-context/Cargo.toml +41 -0
- package/crates/enact-context/src/budget.rs +314 -0
- package/crates/enact-context/src/calibrator.rs +535 -0
- package/crates/enact-context/src/compactor.rs +392 -0
- package/crates/enact-context/src/condenser.rs +826 -0
- package/crates/enact-context/src/lib.rs +94 -0
- package/crates/enact-context/src/segment.rs +238 -0
- package/crates/enact-context/src/step_context.rs +645 -0
- package/crates/enact-context/src/token_counter.rs +148 -0
- package/crates/enact-context/src/window.rs +372 -0
- package/crates/enact-core/Cargo.toml +42 -0
- package/crates/enact-core/README.md +98 -0
- package/crates/enact-core/src/background/executor.rs +524 -0
- package/crates/enact-core/src/background/mod.rs +48 -0
- package/crates/enact-core/src/background/target_binding.rs +390 -0
- package/crates/enact-core/src/background/trigger.rs +511 -0
- package/crates/enact-core/src/callable/callable.rs +152 -0
- package/crates/enact-core/src/callable/composite.rs +817 -0
- package/crates/enact-core/src/callable/graph.rs +104 -0
- package/crates/enact-core/src/callable/llm.rs +211 -0
- package/crates/enact-core/src/callable/mod.rs +64 -0
- package/crates/enact-core/src/callable/registry.rs +206 -0
- package/crates/enact-core/src/context/execution_context.rs +757 -0
- package/crates/enact-core/src/context/invocation.rs +99 -0
- package/crates/enact-core/src/context/mod.rs +50 -0
- package/crates/enact-core/src/context/tenant.rs +175 -0
- package/crates/enact-core/src/context/trace.rs +127 -0
- package/crates/enact-core/src/flow/conditional.rs +293 -0
- package/crates/enact-core/src/flow/mod.rs +43 -0
- package/crates/enact-core/src/flow/parallel.rs +437 -0
- package/crates/enact-core/src/flow/repeat.rs +534 -0
- package/crates/enact-core/src/flow/sequential.rs +248 -0
- package/crates/enact-core/src/graph/checkpoint.rs +79 -0
- package/crates/enact-core/src/graph/checkpoint_store.rs +76 -0
- package/crates/enact-core/src/graph/compiled.rs +189 -0
- package/crates/enact-core/src/graph/edge.rs +59 -0
- package/crates/enact-core/src/graph/graph_schema.rs +218 -0
- package/crates/enact-core/src/graph/loader.rs +155 -0
- package/crates/enact-core/src/graph/mod.rs +18 -0
- package/crates/enact-core/src/graph/node/function.rs +49 -0
- package/crates/enact-core/src/graph/node/mod.rs +48 -0
- package/crates/enact-core/src/graph/schema.rs +62 -0
- package/crates/enact-core/src/inbox/message.rs +405 -0
- package/crates/enact-core/src/inbox/mod.rs +31 -0
- package/crates/enact-core/src/inbox/store.rs +355 -0
- package/crates/enact-core/src/kernel/artifact/filesystem.rs +546 -0
- package/crates/enact-core/src/kernel/artifact/metadata.rs +283 -0
- package/crates/enact-core/src/kernel/artifact/mod.rs +27 -0
- package/crates/enact-core/src/kernel/artifact/store.rs +427 -0
- package/crates/enact-core/src/kernel/enforcement.rs +1315 -0
- package/crates/enact-core/src/kernel/error.rs +1200 -0
- package/crates/enact-core/src/kernel/event.rs +1394 -0
- package/crates/enact-core/src/kernel/execution_model.rs +831 -0
- package/crates/enact-core/src/kernel/execution_state.rs +189 -0
- package/crates/enact-core/src/kernel/execution_strategy.rs +117 -0
- package/crates/enact-core/src/kernel/ids.rs +2086 -0
- package/crates/enact-core/src/kernel/interrupt.rs +125 -0
- package/crates/enact-core/src/kernel/kernel.rs +1283 -0
- package/crates/enact-core/src/kernel/mod.rs +205 -0
- package/crates/enact-core/src/kernel/persistence/event_store.rs +270 -0
- package/crates/enact-core/src/kernel/persistence/message_store.rs +908 -0
- package/crates/enact-core/src/kernel/persistence/mod.rs +102 -0
- package/crates/enact-core/src/kernel/persistence/state_store.rs +228 -0
- package/crates/enact-core/src/kernel/persistence/vector_store.rs +299 -0
- package/crates/enact-core/src/kernel/reducer.rs +808 -0
- package/crates/enact-core/src/kernel/replay.rs +153 -0
- package/crates/enact-core/src/lib.rs +413 -0
- package/crates/enact-core/src/memory/episodic.rs +0 -0
- package/crates/enact-core/src/memory/mod.rs +6 -0
- package/crates/enact-core/src/memory/semantic.rs +0 -0
- package/crates/enact-core/src/memory/trait.rs +0 -0
- package/crates/enact-core/src/memory/vector_db.rs +0 -0
- package/crates/enact-core/src/memory/working.rs +0 -0
- package/crates/enact-core/src/policy/execution_policy.rs +292 -0
- package/crates/enact-core/src/policy/filters.rs +458 -0
- package/crates/enact-core/src/policy/input_processor.rs +407 -0
- package/crates/enact-core/src/policy/long_running.rs +134 -0
- package/crates/enact-core/src/policy/mod.rs +193 -0
- package/crates/enact-core/src/policy/pii_input.rs +274 -0
- package/crates/enact-core/src/policy/tenant_policy.rs +453 -0
- package/crates/enact-core/src/policy/tool_policy.rs +407 -0
- package/crates/enact-core/src/providers/mod.rs +63 -0
- package/crates/enact-core/src/providers/trait.rs +292 -0
- package/crates/enact-core/src/runner/callbacks.rs +6 -0
- package/crates/enact-core/src/runner/execution_runner.rs +476 -0
- package/crates/enact-core/src/runner/loop.rs +117 -0
- package/crates/enact-core/src/runner/mod.rs +58 -0
- package/crates/enact-core/src/runner/protected_runner.rs +280 -0
- package/crates/enact-core/src/signal/inmemory.rs +231 -0
- package/crates/enact-core/src/signal/mod.rs +108 -0
- package/crates/enact-core/src/streaming/event_logger.rs +195 -0
- package/crates/enact-core/src/streaming/event_stream.rs +1423 -0
- package/crates/enact-core/src/streaming/mod.rs +108 -0
- package/crates/enact-core/src/streaming/pause_cancel.rs +0 -0
- package/crates/enact-core/src/streaming/protected_emitter.rs +173 -0
- package/crates/enact-core/src/streaming/protection/context.rs +136 -0
- package/crates/enact-core/src/streaming/protection/encryption.rs +289 -0
- package/crates/enact-core/src/streaming/protection/mod.rs +43 -0
- package/crates/enact-core/src/streaming/protection/pii_protection.rs +243 -0
- package/crates/enact-core/src/streaming/protection/processor.rs +166 -0
- package/crates/enact-core/src/streaming/sse.rs +0 -0
- package/crates/enact-core/src/telemetry/exporter.rs +0 -0
- package/crates/enact-core/src/telemetry/init.rs +0 -0
- package/crates/enact-core/src/telemetry/mod.rs +49 -0
- package/crates/enact-core/src/telemetry/spans.rs +245 -0
- package/crates/enact-core/src/tool/agent_tool.rs +177 -0
- package/crates/enact-core/src/tool/browser/mod.rs +0 -0
- package/crates/enact-core/src/tool/browser/webdriver.rs +0 -0
- package/crates/enact-core/src/tool/cost.rs +247 -0
- package/crates/enact-core/src/tool/discovery.rs +0 -0
- package/crates/enact-core/src/tool/dispatcher.rs +347 -0
- package/crates/enact-core/src/tool/filesystem.rs +231 -0
- package/crates/enact-core/src/tool/function.rs +99 -0
- package/crates/enact-core/src/tool/git.rs +162 -0
- package/crates/enact-core/src/tool/http.rs +214 -0
- package/crates/enact-core/src/tool/mcp/client.rs +0 -0
- package/crates/enact-core/src/tool/mcp/mod.rs +0 -0
- package/crates/enact-core/src/tool/mod.rs +51 -0
- package/crates/enact-core/src/tool/reasoning/debugging.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/mcts.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/mod.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/sequential.rs +0 -0
- package/crates/enact-core/src/tool/sandbox/dagger.rs +0 -0
- package/crates/enact-core/src/tool/sandbox/mod.rs +0 -0
- package/crates/enact-core/src/tool/shell.rs +147 -0
- package/crates/enact-core/src/tool/trait.rs +33 -0
- package/crates/enact-core/src/tool/web_search.rs +277 -0
- package/crates/enact-core/src/util/config.rs +0 -0
- package/crates/enact-core/src/util/errors.rs +0 -0
- package/crates/enact-core/src/util/mod.rs +6 -0
- package/crates/enact-core/tests/airgapped_e2e_test.rs +291 -0
- package/crates/enact-core/tests/e2e_agentic_loop.rs +119 -0
- package/crates/enact-core/tests/e2e_test.rs +259 -0
- package/crates/enact-core/tests/graph_test.rs +130 -0
- package/crates/enact-core/tests/stream_event_id_validation.rs +435 -0
- package/crates/enact-cron/Cargo.toml +28 -0
- package/crates/enact-cron/src/lib.rs +44 -0
- package/crates/enact-cron/src/schedule.rs +156 -0
- package/crates/enact-cron/src/store.rs +589 -0
- package/crates/enact-cron/src/types.rs +148 -0
- package/crates/enact-gateway/Cargo.toml +31 -0
- package/crates/enact-gateway/README.md +30 -0
- package/crates/enact-gateway/examples/whatsapp-gateway-runner-mock.rs +59 -0
- package/crates/enact-gateway/examples/whatsapp-gateway.rs +42 -0
- package/crates/enact-gateway/src/lib.rs +582 -0
- package/crates/enact-mcp/Cargo.toml +24 -0
- package/crates/enact-mcp/src/lib.rs +178 -0
- package/crates/enact-memory/Cargo.toml +25 -0
- package/crates/enact-memory/src/backend.rs +20 -0
- package/crates/enact-memory/src/chunker.rs +230 -0
- package/crates/enact-memory/src/embeddings.rs +221 -0
- package/crates/enact-memory/src/lib.rs +67 -0
- package/crates/enact-memory/src/markdown.rs +127 -0
- package/crates/enact-memory/src/none.rs +61 -0
- package/crates/enact-memory/src/sqlite.rs +276 -0
- package/crates/enact-memory/src/traits.rs +65 -0
- package/crates/enact-memory/src/vector.rs +198 -0
- package/crates/enact-oauth/Cargo.toml +27 -0
- package/crates/enact-oauth/src/lib.rs +584 -0
- package/crates/enact-observability/Cargo.toml +22 -0
- package/crates/enact-observability/src/lib.rs +197 -0
- package/crates/enact-providers/Cargo.toml +33 -0
- package/crates/enact-providers/examples/hello-agent.rs +33 -0
- package/crates/enact-providers/src/anthropic.rs +182 -0
- package/crates/enact-providers/src/azure.rs +96 -0
- package/crates/enact-providers/src/bridge.rs +221 -0
- package/crates/enact-providers/src/gemini.rs +227 -0
- package/crates/enact-providers/src/http.rs +78 -0
- package/crates/enact-providers/src/lib.rs +53 -0
- package/crates/enact-providers/src/openai_compatible.rs +167 -0
- package/crates/enact-providers/src/openrouter.rs +33 -0
- package/crates/enact-runner/Cargo.toml +24 -0
- package/crates/enact-runner/README.md +76 -0
- package/crates/enact-runner/src/compaction.rs +225 -0
- package/crates/enact-runner/src/config.rs +118 -0
- package/crates/enact-runner/src/lib.rs +63 -0
- package/crates/enact-runner/src/loop_driver.rs +414 -0
- package/crates/enact-runner/src/parser.rs +421 -0
- package/crates/enact-runner/src/retry.rs +262 -0
- package/crates/enact-runner/tests/integration.rs +278 -0
- package/crates/enact-security/Cargo.toml +22 -0
- package/crates/enact-security/src/audit.rs +375 -0
- package/crates/enact-security/src/lib.rs +37 -0
- package/crates/enact-security/src/policy.rs +406 -0
- package/crates/enact-skills/Cargo.toml +25 -0
- package/crates/enact-skills/src/lib.rs +506 -0
- package/crates/enact-tools/Cargo.toml +22 -0
- package/crates/enact-tools/src/file_read.rs +166 -0
- package/crates/enact-tools/src/file_write.rs +216 -0
- package/crates/enact-tools/src/git_operations.rs +513 -0
- package/crates/enact-tools/src/http_request.rs +417 -0
- package/crates/enact-tools/src/lib.rs +104 -0
- package/crates/enact-tools/src/security.rs +227 -0
- package/crates/enact-tools/src/shell.rs +191 -0
- package/crates/enact-tools/src/traits.rs +159 -0
- package/docs/Makefile +74 -0
- package/docs/config.toml +62 -0
- package/docs/content/_index.md +174 -0
- package/docs/content/a2a/_index.md +431 -0
- package/docs/content/api/_index.md +323 -0
- package/docs/content/channels/_index.md +160 -0
- package/docs/content/channels/teams.md +205 -0
- package/docs/content/channels/telegram.md +182 -0
- package/docs/content/channels/webhook.md +423 -0
- package/docs/content/channels/whatsapp.md +240 -0
- package/docs/content/cli/_index.md +261 -0
- package/docs/content/concepts/_index.md +273 -0
- package/docs/content/configuration/_index.md +241 -0
- package/docs/content/cron/_index.md +248 -0
- package/docs/content/developers/_index.md +278 -0
- package/docs/content/getting-started/_index.md +180 -0
- package/docs/content/installation/_index.md +186 -0
- package/docs/content/installation/uninstall.md +101 -0
- package/docs/content/installation/updating.md +120 -0
- package/docs/content/mcp/_index.md +215 -0
- package/docs/content/memory/_index.md +163 -0
- package/docs/content/oauth/_index.md +515 -0
- package/docs/content/providers/_index.md +206 -0
- package/docs/content/roadmap/_index.md +199 -0
- package/docs/content/security/_index.md +219 -0
- package/docs/content/skills/_index.md +228 -0
- package/docs/content/tools/_index.md +485 -0
- package/docs/content/troubleshooting/_index.md +259 -0
- package/docs/content/yaml-schema/_index.md +294 -0
- package/docs/static/giallo-dark.css +91 -0
- package/docs/static/giallo-light.css +91 -0
- package/docs/themes/tanuki/.github/workflows/deploy.yml +44 -0
- package/docs/themes/tanuki/LICENSE +21 -0
- package/docs/themes/tanuki/README.md +166 -0
- package/docs/themes/tanuki/examples/blog/config.toml +58 -0
- package/docs/themes/tanuki/examples/blog/content/_index.md +4 -0
- package/docs/themes/tanuki/examples/blog/content/about.md +33 -0
- package/docs/themes/tanuki/examples/blog/content/blog/_index.md +7 -0
- package/docs/themes/tanuki/examples/blog/content/blog/api-design-best-practices.md +245 -0
- package/docs/themes/tanuki/examples/blog/content/blog/building-accessible-websites.md +147 -0
- package/docs/themes/tanuki/examples/blog/content/blog/css-grid-vs-flexbox.md +165 -0
- package/docs/themes/tanuki/examples/blog/content/blog/customizing-catppuccin-colors.md +137 -0
- package/docs/themes/tanuki/examples/blog/content/blog/dark-mode-best-practices.md +82 -0
- package/docs/themes/tanuki/examples/blog/content/blog/docker-essentials.md +301 -0
- package/docs/themes/tanuki/examples/blog/content/blog/getting-started-with-zola.md +129 -0
- package/docs/themes/tanuki/examples/blog/content/blog/git-workflow-for-content.md +112 -0
- package/docs/themes/tanuki/examples/blog/content/blog/introduction-to-webassembly.md +183 -0
- package/docs/themes/tanuki/examples/blog/content/blog/modern-javascript-features.md +234 -0
- package/docs/themes/tanuki/examples/blog/content/blog/testing-strategies.md +311 -0
- package/docs/themes/tanuki/examples/blog/content/blog/typography-for-developers.md +104 -0
- package/docs/themes/tanuki/examples/blog/content/blog/welcome-to-tanuki.md +67 -0
- package/docs/themes/tanuki/examples/blog/content/blog/why-static-sites.md +85 -0
- package/docs/themes/tanuki/examples/blog/content/projects.md +64 -0
- package/docs/themes/tanuki/examples/book/config.toml +17 -0
- package/docs/themes/tanuki/examples/book/content/_index.md +12 -0
- package/docs/themes/tanuki/examples/book/content/chapter-1.md +90 -0
- package/docs/themes/tanuki/examples/book/content/chapter-2.md +143 -0
- package/docs/themes/tanuki/examples/book/content/chapter-3.md +217 -0
- package/docs/themes/tanuki/examples/book/content/chapter-4.md +224 -0
- package/docs/themes/tanuki/examples/book/content/chapter-5.md +297 -0
- package/docs/themes/tanuki/examples/book/content/print.md +6 -0
- package/docs/themes/tanuki/examples/docs/config.toml +28 -0
- package/docs/themes/tanuki/examples/docs/content/_index.md +20 -0
- package/docs/themes/tanuki/examples/docs/content/components.md +156 -0
- package/docs/themes/tanuki/examples/docs/content/configuration.md +94 -0
- package/docs/themes/tanuki/examples/docs/content/customization.md +202 -0
- package/docs/themes/tanuki/examples/docs/content/deployment.md +204 -0
- package/docs/themes/tanuki/examples/docs/content/installation.md +59 -0
- package/docs/themes/tanuki/examples/docs/content/print.md +6 -0
- package/docs/themes/tanuki/examples/docs/static/img/tanuki-icon.avif +0 -0
- package/docs/themes/tanuki/examples/index.html +2104 -0
- package/docs/themes/tanuki/mise.toml +108 -0
- package/docs/themes/tanuki/sass/base/_catppuccin.scss +164 -0
- package/docs/themes/tanuki/sass/base/_fonts.scss +64 -0
- package/docs/themes/tanuki/sass/base/_reset.scss +152 -0
- package/docs/themes/tanuki/sass/base/_typography.scss +523 -0
- package/docs/themes/tanuki/sass/components/_buttons.scss +209 -0
- package/docs/themes/tanuki/sass/components/_code.scss +457 -0
- package/docs/themes/tanuki/sass/components/_landing.scss +633 -0
- package/docs/themes/tanuki/sass/components/_layout.scss +294 -0
- package/docs/themes/tanuki/sass/components/_navigation.scss +1200 -0
- package/docs/themes/tanuki/sass/components/_print.scss +237 -0
- package/docs/themes/tanuki/sass/components/_search.scss +224 -0
- package/docs/themes/tanuki/sass/components/_sidebar.scss +473 -0
- package/docs/themes/tanuki/sass/components/_theme-toggle.scss +186 -0
- package/docs/themes/tanuki/sass/modes/_blog.scss +366 -0
- package/docs/themes/tanuki/sass/modes/_product.scss +875 -0
- package/docs/themes/tanuki/sass/modes/_raskell.scss +1696 -0
- package/docs/themes/tanuki/sass/patterns/_buttons.scss +183 -0
- package/docs/themes/tanuki/sass/patterns/_cards.scss +144 -0
- package/docs/themes/tanuki/sass/patterns/_index.scss +9 -0
- package/docs/themes/tanuki/sass/patterns/_lists.scss +259 -0
- package/docs/themes/tanuki/sass/patterns/_sections.scss +243 -0
- package/docs/themes/tanuki/sass/style.scss +47 -0
- package/docs/themes/tanuki/sass/tokens/_colors.scss +139 -0
- package/docs/themes/tanuki/sass/tokens/_spacing.scss +100 -0
- package/docs/themes/tanuki/sass/tokens/_typography.scss +186 -0
- package/docs/themes/tanuki/screenshot.png +0 -0
- package/docs/themes/tanuki/sentinel.kdl +59 -0
- package/docs/themes/tanuki/static/elasticlunr.min.js +10 -0
- package/docs/themes/tanuki/static/fonts/GEIST-LICENSE.txt +92 -0
- package/docs/themes/tanuki/static/fonts/Geist-Variable.woff2 +0 -0
- package/docs/themes/tanuki/static/fonts/GeistMono-Variable.woff2 +0 -0
- package/docs/themes/tanuki/static/img/tanuki-icon.avif +0 -0
- package/docs/themes/tanuki/static/img/tanuki-icon.png +0 -0
- package/docs/themes/tanuki/static/js/anchors.js +18 -0
- package/docs/themes/tanuki/static/js/app.js +274 -0
- package/docs/themes/tanuki/static/js/code.js +394 -0
- package/docs/themes/tanuki/static/js/navigation.js +778 -0
- package/docs/themes/tanuki/static/js/scroll-to-top.js +33 -0
- package/docs/themes/tanuki/static/js/search-raskell.js +240 -0
- package/docs/themes/tanuki/static/js/search.js +215 -0
- package/docs/themes/tanuki/static/js/theme.js +169 -0
- package/docs/themes/tanuki/static/syntax-dark.css +151 -0
- package/docs/themes/tanuki/static/syntax-light.css +151 -0
- package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm.js +486 -0
- package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm_bg.wasm +0 -0
- package/docs/themes/tanuki/templates/404.html +52 -0
- package/docs/themes/tanuki/templates/base.html +428 -0
- package/docs/themes/tanuki/templates/blog.html +66 -0
- package/docs/themes/tanuki/templates/home.html +108 -0
- package/docs/themes/tanuki/templates/index.html +178 -0
- package/docs/themes/tanuki/templates/landing.html +168 -0
- package/docs/themes/tanuki/templates/macros/nav.html +128 -0
- package/docs/themes/tanuki/templates/macros/posts.html +101 -0
- package/docs/themes/tanuki/templates/macros/ui.html +159 -0
- package/docs/themes/tanuki/templates/page.html +135 -0
- package/docs/themes/tanuki/templates/partials/footer.html +38 -0
- package/docs/themes/tanuki/templates/partials/header.html +366 -0
- package/docs/themes/tanuki/templates/partials/nav-buttons.html +55 -0
- package/docs/themes/tanuki/templates/partials/nav-overlay.html +81 -0
- package/docs/themes/tanuki/templates/partials/page-toc-panel.html +43 -0
- package/docs/themes/tanuki/templates/partials/search.html +52 -0
- package/docs/themes/tanuki/templates/partials/sidebar.html +107 -0
- package/docs/themes/tanuki/templates/partials/theme-toggle.html +35 -0
- package/docs/themes/tanuki/templates/partials/toc-overlay.html +146 -0
- package/docs/themes/tanuki/templates/partials/version-picker.html +38 -0
- package/docs/themes/tanuki/templates/print.html +244 -0
- package/docs/themes/tanuki/templates/section.html +186 -0
- package/docs/themes/tanuki/templates/taxonomy_list.html +18 -0
- package/docs/themes/tanuki/templates/taxonomy_single.html +31 -0
- package/docs/themes/tanuki/theme.toml +58 -0
- package/examples/hello-agent.rs +55 -0
- package/package.json +36 -0
- package/proto/config.proto +60 -0
- package/proto/events.proto +0 -0
- package/proto/runtime.proto +215 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
//! Runner - executes callables and graphs with flow features
|
|
2
|
+
|
|
3
|
+
use crate::callable::Callable;
|
|
4
|
+
use crate::graph::{Checkpoint, CheckpointStore, CompiledGraph, NodeState};
|
|
5
|
+
use crate::kernel::{ExecutionError, ExecutionId, StepId, StepType};
|
|
6
|
+
use crate::streaming::{EventEmitter, StreamEvent};
|
|
7
|
+
use std::sync::Arc;
|
|
8
|
+
use std::time::Instant;
|
|
9
|
+
use tokio_util::sync::CancellationToken;
|
|
10
|
+
|
|
11
|
+
/// Runner - executes agents/graphs with abort, pause, and event streaming
|
|
12
|
+
pub struct Runner<S: CheckpointStore> {
|
|
13
|
+
execution_id: ExecutionId,
|
|
14
|
+
cancellation_token: CancellationToken,
|
|
15
|
+
checkpoint_store: Arc<S>,
|
|
16
|
+
emitter: EventEmitter,
|
|
17
|
+
paused: std::sync::atomic::AtomicBool,
|
|
18
|
+
start_time: Option<Instant>,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl<S: CheckpointStore> Runner<S> {
|
|
22
|
+
/// Create a new runner
|
|
23
|
+
pub fn new(checkpoint_store: Arc<S>) -> Self {
|
|
24
|
+
Self {
|
|
25
|
+
execution_id: ExecutionId::new(),
|
|
26
|
+
cancellation_token: CancellationToken::new(),
|
|
27
|
+
checkpoint_store,
|
|
28
|
+
emitter: EventEmitter::new(),
|
|
29
|
+
paused: std::sync::atomic::AtomicBool::new(false),
|
|
30
|
+
start_time: None,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Get the execution ID
|
|
35
|
+
pub fn execution_id(&self) -> &ExecutionId {
|
|
36
|
+
&self.execution_id
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Backward compatibility alias
|
|
40
|
+
#[deprecated(note = "Use execution_id() instead")]
|
|
41
|
+
pub fn run_id(&self) -> &ExecutionId {
|
|
42
|
+
&self.execution_id
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Get the event emitter
|
|
46
|
+
pub fn emitter(&self) -> &EventEmitter {
|
|
47
|
+
&self.emitter
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Cancel the run (abort signal)
|
|
51
|
+
pub fn cancel(&self) {
|
|
52
|
+
self.cancellation_token.cancel();
|
|
53
|
+
self.emitter.emit(StreamEvent::execution_cancelled(
|
|
54
|
+
&self.execution_id,
|
|
55
|
+
"Run cancelled by user",
|
|
56
|
+
));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Check if cancelled
|
|
60
|
+
pub fn is_cancelled(&self) -> bool {
|
|
61
|
+
self.cancellation_token.is_cancelled()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Pause the run
|
|
65
|
+
pub async fn pause(&self) -> anyhow::Result<()> {
|
|
66
|
+
self.paused.store(true, std::sync::atomic::Ordering::SeqCst);
|
|
67
|
+
self.emitter.emit(StreamEvent::execution_paused(
|
|
68
|
+
&self.execution_id,
|
|
69
|
+
"Paused by user",
|
|
70
|
+
));
|
|
71
|
+
Ok(())
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Resume the run
|
|
75
|
+
pub fn resume(&self) {
|
|
76
|
+
self.paused.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
77
|
+
self.emitter.emit(StreamEvent::execution_resumed(&self.execution_id));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// Check if paused
|
|
81
|
+
pub fn is_paused(&self) -> bool {
|
|
82
|
+
self.paused.load(std::sync::atomic::Ordering::SeqCst)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// Save checkpoint
|
|
86
|
+
pub async fn save_checkpoint(&self, state: NodeState, node: Option<&str>) -> anyhow::Result<Checkpoint> {
|
|
87
|
+
let mut checkpoint = Checkpoint::new(self.execution_id.clone())
|
|
88
|
+
.with_state(state.data);
|
|
89
|
+
|
|
90
|
+
if let Some(n) = node {
|
|
91
|
+
checkpoint = checkpoint.with_node(n);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
self.checkpoint_store.save(checkpoint.clone()).await?;
|
|
95
|
+
Ok(checkpoint)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Load latest checkpoint
|
|
99
|
+
pub async fn load_checkpoint(&self) -> anyhow::Result<Option<Checkpoint>> {
|
|
100
|
+
self.checkpoint_store.load_latest(self.execution_id.as_str()).await
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// Run a callable with event streaming
|
|
104
|
+
pub async fn run_callable<A: Callable + ?Sized>(&mut self, callable: &A, input: &str) -> anyhow::Result<String> {
|
|
105
|
+
self.start_time = Some(Instant::now());
|
|
106
|
+
self.emitter.emit(StreamEvent::execution_start(&self.execution_id));
|
|
107
|
+
|
|
108
|
+
// Check for cancellation before running
|
|
109
|
+
if self.is_cancelled() {
|
|
110
|
+
anyhow::bail!("Run cancelled");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let result = callable.run(input).await;
|
|
114
|
+
let duration_ms = self.start_time.map(|t| t.elapsed().as_millis() as u64).unwrap_or(0);
|
|
115
|
+
|
|
116
|
+
match &result {
|
|
117
|
+
Ok(output) => {
|
|
118
|
+
self.emitter.emit(StreamEvent::execution_end(
|
|
119
|
+
&self.execution_id,
|
|
120
|
+
Some(output.clone()),
|
|
121
|
+
duration_ms,
|
|
122
|
+
));
|
|
123
|
+
}
|
|
124
|
+
Err(e) => {
|
|
125
|
+
let error = ExecutionError::kernel_internal(e.to_string());
|
|
126
|
+
self.emitter.emit(StreamEvent::execution_failed(
|
|
127
|
+
&self.execution_id,
|
|
128
|
+
error,
|
|
129
|
+
));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
result
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Run a compiled graph with event streaming
|
|
137
|
+
pub async fn run_graph(&mut self, graph: &CompiledGraph, input: &str) -> anyhow::Result<NodeState> {
|
|
138
|
+
self.start_time = Some(Instant::now());
|
|
139
|
+
self.emitter.emit(StreamEvent::execution_start(&self.execution_id));
|
|
140
|
+
|
|
141
|
+
let mut state = NodeState::from_str(input);
|
|
142
|
+
let mut current_node = graph.entry_point().to_string();
|
|
143
|
+
|
|
144
|
+
loop {
|
|
145
|
+
// Check for cancellation
|
|
146
|
+
if self.is_cancelled() {
|
|
147
|
+
anyhow::bail!("Run cancelled");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Check for pause - wait until resumed
|
|
151
|
+
while self.is_paused() {
|
|
152
|
+
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
|
153
|
+
if self.is_cancelled() {
|
|
154
|
+
anyhow::bail!("Run cancelled while paused");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Get the node
|
|
159
|
+
let node = graph
|
|
160
|
+
.get_node(¤t_node)
|
|
161
|
+
.ok_or_else(|| anyhow::anyhow!("Node '{}' not found", current_node))?;
|
|
162
|
+
|
|
163
|
+
// Create step ID for this node execution
|
|
164
|
+
let step_id = StepId::new();
|
|
165
|
+
let step_start = Instant::now();
|
|
166
|
+
|
|
167
|
+
// Emit step started
|
|
168
|
+
self.emitter.emit(StreamEvent::step_start(
|
|
169
|
+
&self.execution_id,
|
|
170
|
+
&step_id,
|
|
171
|
+
StepType::FunctionNode, // Graph nodes are function nodes by default
|
|
172
|
+
current_node.clone(),
|
|
173
|
+
));
|
|
174
|
+
|
|
175
|
+
// Execute node
|
|
176
|
+
state = node.execute(state).await?;
|
|
177
|
+
|
|
178
|
+
// Emit step completed
|
|
179
|
+
let step_duration = step_start.elapsed().as_millis() as u64;
|
|
180
|
+
self.emitter.emit(StreamEvent::step_end(
|
|
181
|
+
&self.execution_id,
|
|
182
|
+
&step_id,
|
|
183
|
+
Some(state.as_str().unwrap_or_default().to_string()),
|
|
184
|
+
step_duration,
|
|
185
|
+
));
|
|
186
|
+
|
|
187
|
+
// Get next nodes
|
|
188
|
+
let output = state.as_str().unwrap_or_default();
|
|
189
|
+
let next = graph.get_next(¤t_node, output);
|
|
190
|
+
|
|
191
|
+
if next.is_empty() {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
match &next[0] {
|
|
196
|
+
crate::graph::EdgeTarget::End => break,
|
|
197
|
+
crate::graph::EdgeTarget::Node(n) => {
|
|
198
|
+
current_node = n.clone();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let duration_ms = self.start_time.map(|t| t.elapsed().as_millis() as u64).unwrap_or(0);
|
|
204
|
+
self.emitter.emit(StreamEvent::execution_end(
|
|
205
|
+
&self.execution_id,
|
|
206
|
+
Some(state.as_str().unwrap_or_default().to_string()),
|
|
207
|
+
duration_ms,
|
|
208
|
+
));
|
|
209
|
+
|
|
210
|
+
Ok(state)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Runner with in-memory checkpoint store (default)
|
|
215
|
+
pub type DefaultRunner = Runner<crate::graph::InMemoryCheckpointStore>;
|
|
216
|
+
|
|
217
|
+
impl DefaultRunner {
|
|
218
|
+
/// Create a new runner with in-memory checkpoint store
|
|
219
|
+
pub fn default_new() -> Self {
|
|
220
|
+
Self::new(Arc::new(crate::graph::InMemoryCheckpointStore::new()))
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
#[cfg(test)]
|
|
225
|
+
mod tests {
|
|
226
|
+
use super::*;
|
|
227
|
+
use async_trait::async_trait;
|
|
228
|
+
use crate::graph::InMemoryCheckpointStore;
|
|
229
|
+
|
|
230
|
+
/// Mock callable for testing
|
|
231
|
+
struct MockCallable {
|
|
232
|
+
name: String,
|
|
233
|
+
response: Result<String, String>,
|
|
234
|
+
delay_ms: Option<u64>,
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
impl MockCallable {
|
|
238
|
+
fn success(name: &str, response: &str) -> Self {
|
|
239
|
+
Self {
|
|
240
|
+
name: name.to_string(),
|
|
241
|
+
response: Ok(response.to_string()),
|
|
242
|
+
delay_ms: None,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
fn failing(name: &str, error: &str) -> Self {
|
|
247
|
+
Self {
|
|
248
|
+
name: name.to_string(),
|
|
249
|
+
response: Err(error.to_string()),
|
|
250
|
+
delay_ms: None,
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#[async_trait]
|
|
256
|
+
impl Callable for MockCallable {
|
|
257
|
+
fn name(&self) -> &str {
|
|
258
|
+
&self.name
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async fn run(&self, input: &str) -> anyhow::Result<String> {
|
|
262
|
+
if let Some(delay) = self.delay_ms {
|
|
263
|
+
tokio::time::sleep(tokio::time::Duration::from_millis(delay)).await;
|
|
264
|
+
}
|
|
265
|
+
match &self.response {
|
|
266
|
+
Ok(r) => Ok(format!("{}:{}", r, input)),
|
|
267
|
+
Err(e) => anyhow::bail!("{}", e),
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ============ Construction Tests ============
|
|
273
|
+
|
|
274
|
+
#[test]
|
|
275
|
+
fn test_runner_new() {
|
|
276
|
+
let store = Arc::new(InMemoryCheckpointStore::new());
|
|
277
|
+
let runner = Runner::new(store);
|
|
278
|
+
|
|
279
|
+
// Should have a valid execution ID
|
|
280
|
+
assert!(!runner.execution_id().as_str().is_empty());
|
|
281
|
+
// Should not be cancelled initially
|
|
282
|
+
assert!(!runner.is_cancelled());
|
|
283
|
+
// Should not be paused initially
|
|
284
|
+
assert!(!runner.is_paused());
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
#[test]
|
|
288
|
+
fn test_default_runner_new() {
|
|
289
|
+
let runner = DefaultRunner::default_new();
|
|
290
|
+
assert!(!runner.execution_id().as_str().is_empty());
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#[test]
|
|
294
|
+
fn test_runner_execution_id_unique() {
|
|
295
|
+
let store = Arc::new(InMemoryCheckpointStore::new());
|
|
296
|
+
let runner1 = Runner::new(store.clone());
|
|
297
|
+
let runner2 = Runner::new(store);
|
|
298
|
+
|
|
299
|
+
// Each runner should have unique execution ID
|
|
300
|
+
assert_ne!(runner1.execution_id().as_str(), runner2.execution_id().as_str());
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ============ Cancellation Tests ============
|
|
304
|
+
|
|
305
|
+
#[test]
|
|
306
|
+
fn test_runner_cancel() {
|
|
307
|
+
let runner = DefaultRunner::default_new();
|
|
308
|
+
|
|
309
|
+
assert!(!runner.is_cancelled());
|
|
310
|
+
runner.cancel();
|
|
311
|
+
assert!(runner.is_cancelled());
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#[tokio::test]
|
|
315
|
+
async fn test_runner_callable_checks_cancellation_before_run() {
|
|
316
|
+
let mut runner = DefaultRunner::default_new();
|
|
317
|
+
let callable = MockCallable::success("test", "response");
|
|
318
|
+
|
|
319
|
+
// Cancel before running
|
|
320
|
+
runner.cancel();
|
|
321
|
+
|
|
322
|
+
let result = runner.run_callable(&callable, "input").await;
|
|
323
|
+
assert!(result.is_err());
|
|
324
|
+
assert!(result.unwrap_err().to_string().contains("cancelled"));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ============ Pause/Resume Tests ============
|
|
328
|
+
|
|
329
|
+
#[tokio::test]
|
|
330
|
+
async fn test_runner_pause_resume() {
|
|
331
|
+
let runner = DefaultRunner::default_new();
|
|
332
|
+
|
|
333
|
+
assert!(!runner.is_paused());
|
|
334
|
+
|
|
335
|
+
runner.pause().await.unwrap();
|
|
336
|
+
assert!(runner.is_paused());
|
|
337
|
+
|
|
338
|
+
runner.resume();
|
|
339
|
+
assert!(!runner.is_paused());
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ============ Run Callable Tests ============
|
|
343
|
+
|
|
344
|
+
#[tokio::test]
|
|
345
|
+
async fn test_run_callable_success() {
|
|
346
|
+
let mut runner = DefaultRunner::default_new();
|
|
347
|
+
let callable = MockCallable::success("test", "hello");
|
|
348
|
+
|
|
349
|
+
let result = runner.run_callable(&callable, "world").await;
|
|
350
|
+
assert!(result.is_ok());
|
|
351
|
+
assert_eq!(result.unwrap(), "hello:world");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#[tokio::test]
|
|
355
|
+
async fn test_run_callable_failure() {
|
|
356
|
+
let mut runner = DefaultRunner::default_new();
|
|
357
|
+
let callable = MockCallable::failing("test", "Something went wrong");
|
|
358
|
+
|
|
359
|
+
let result = runner.run_callable(&callable, "input").await;
|
|
360
|
+
assert!(result.is_err());
|
|
361
|
+
assert!(result.unwrap_err().to_string().contains("Something went wrong"));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#[tokio::test]
|
|
365
|
+
async fn test_run_callable_emits_events() {
|
|
366
|
+
let mut runner = DefaultRunner::default_new();
|
|
367
|
+
let callable = MockCallable::success("test", "response");
|
|
368
|
+
|
|
369
|
+
runner.run_callable(&callable, "input").await.unwrap();
|
|
370
|
+
|
|
371
|
+
// Drain and check collected events
|
|
372
|
+
let events = runner.emitter().drain();
|
|
373
|
+
|
|
374
|
+
// Should have execution start and end events
|
|
375
|
+
assert!(events.len() >= 2);
|
|
376
|
+
|
|
377
|
+
// First event should be execution start
|
|
378
|
+
let first = &events[0];
|
|
379
|
+
assert!(matches!(first, StreamEvent::ExecutionStart { .. }));
|
|
380
|
+
|
|
381
|
+
// Last event should be execution end
|
|
382
|
+
let last = &events[events.len() - 1];
|
|
383
|
+
assert!(matches!(last, StreamEvent::ExecutionEnd { .. }));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
#[tokio::test]
|
|
387
|
+
async fn test_run_callable_failure_emits_failed_event() {
|
|
388
|
+
let mut runner = DefaultRunner::default_new();
|
|
389
|
+
let callable = MockCallable::failing("test", "error message");
|
|
390
|
+
|
|
391
|
+
let _ = runner.run_callable(&callable, "input").await;
|
|
392
|
+
|
|
393
|
+
// Drain and check collected events
|
|
394
|
+
let events = runner.emitter().drain();
|
|
395
|
+
|
|
396
|
+
// Should have execution start and failed events
|
|
397
|
+
assert!(events.len() >= 2);
|
|
398
|
+
|
|
399
|
+
// Last event should be execution failed
|
|
400
|
+
let last = &events[events.len() - 1];
|
|
401
|
+
assert!(matches!(last, StreamEvent::ExecutionFailed { .. }));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// ============ Checkpoint Tests ============
|
|
405
|
+
|
|
406
|
+
#[tokio::test]
|
|
407
|
+
async fn test_runner_save_and_load_checkpoint() {
|
|
408
|
+
let runner = DefaultRunner::default_new();
|
|
409
|
+
|
|
410
|
+
// Save a checkpoint
|
|
411
|
+
let state = NodeState::from_str("test state data");
|
|
412
|
+
let checkpoint = runner.save_checkpoint(state, Some("node1")).await.unwrap();
|
|
413
|
+
|
|
414
|
+
assert_eq!(checkpoint.current_node.as_ref().unwrap(), "node1");
|
|
415
|
+
|
|
416
|
+
// Load the checkpoint
|
|
417
|
+
let loaded = runner.load_checkpoint().await.unwrap();
|
|
418
|
+
assert!(loaded.is_some());
|
|
419
|
+
|
|
420
|
+
let loaded = loaded.unwrap();
|
|
421
|
+
assert_eq!(loaded.state, serde_json::Value::String("test state data".to_string()));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
#[tokio::test]
|
|
425
|
+
async fn test_runner_checkpoint_without_node() {
|
|
426
|
+
let runner = DefaultRunner::default_new();
|
|
427
|
+
|
|
428
|
+
let state = NodeState::from_str("some data");
|
|
429
|
+
let checkpoint = runner.save_checkpoint(state, None).await.unwrap();
|
|
430
|
+
|
|
431
|
+
assert!(checkpoint.current_node.is_none());
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
#[tokio::test]
|
|
435
|
+
async fn test_runner_load_checkpoint_no_data() {
|
|
436
|
+
let runner = DefaultRunner::default_new();
|
|
437
|
+
|
|
438
|
+
// Without saving, load should return None
|
|
439
|
+
let loaded = runner.load_checkpoint().await.unwrap();
|
|
440
|
+
assert!(loaded.is_none());
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ============ Emitter Tests ============
|
|
444
|
+
|
|
445
|
+
#[test]
|
|
446
|
+
fn test_runner_emitter_access() {
|
|
447
|
+
let runner = DefaultRunner::default_new();
|
|
448
|
+
let emitter = runner.emitter();
|
|
449
|
+
|
|
450
|
+
// Should be able to emit and drain events
|
|
451
|
+
emitter.emit(StreamEvent::execution_start(runner.execution_id()));
|
|
452
|
+
let events = emitter.drain();
|
|
453
|
+
assert_eq!(events.len(), 1);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
#[test]
|
|
457
|
+
fn test_emitter_mode() {
|
|
458
|
+
use crate::streaming::StreamMode;
|
|
459
|
+
|
|
460
|
+
let runner = DefaultRunner::default_new();
|
|
461
|
+
let emitter = runner.emitter();
|
|
462
|
+
|
|
463
|
+
// Default mode should be Full
|
|
464
|
+
assert_eq!(emitter.mode(), StreamMode::Full);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ============ Deprecated API Tests ============
|
|
468
|
+
|
|
469
|
+
#[test]
|
|
470
|
+
#[allow(deprecated)]
|
|
471
|
+
fn test_runner_run_id_deprecated() {
|
|
472
|
+
let runner = DefaultRunner::default_new();
|
|
473
|
+
// run_id should return same as execution_id
|
|
474
|
+
assert_eq!(runner.run_id().as_str(), runner.execution_id().as_str());
|
|
475
|
+
}
|
|
476
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//! Agentic Loop implementation
|
|
2
|
+
//!
|
|
3
|
+
//! The core loop that drives long-running executions.
|
|
4
|
+
//! It handles the "Discovery -> Execute -> Repeat" cycle.
|
|
5
|
+
|
|
6
|
+
use crate::callable::{Callable, DynCallable};
|
|
7
|
+
use crate::graph::CheckpointStore;
|
|
8
|
+
use crate::policy::LongRunningExecutionPolicy;
|
|
9
|
+
use crate::runner::Runner;
|
|
10
|
+
use crate::streaming::StreamEvent;
|
|
11
|
+
use std::time::{Duration, Instant};
|
|
12
|
+
|
|
13
|
+
/// The agentic loop driver
|
|
14
|
+
#[allow(dead_code)]
|
|
15
|
+
pub struct AgenticLoop;
|
|
16
|
+
|
|
17
|
+
#[allow(dead_code)]
|
|
18
|
+
impl AgenticLoop {
|
|
19
|
+
/// Run the agentic loop for a callable
|
|
20
|
+
pub async fn run<S: CheckpointStore>(
|
|
21
|
+
runner: &mut Runner<S>,
|
|
22
|
+
callable: DynCallable,
|
|
23
|
+
input: String,
|
|
24
|
+
policy: LongRunningExecutionPolicy,
|
|
25
|
+
) -> anyhow::Result<String> {
|
|
26
|
+
// Initial setup
|
|
27
|
+
let start_time = Instant::now();
|
|
28
|
+
let mut steps_executed = 0;
|
|
29
|
+
let mut _tokens_used = 0;
|
|
30
|
+
let mut history: Vec<String> = Vec::new();
|
|
31
|
+
|
|
32
|
+
// Push initial input
|
|
33
|
+
history.push(format!("User: {}", input));
|
|
34
|
+
|
|
35
|
+
// Initial execution
|
|
36
|
+
let current_input = input.clone();
|
|
37
|
+
let current_callable = callable;
|
|
38
|
+
|
|
39
|
+
// Main Loop
|
|
40
|
+
loop {
|
|
41
|
+
// 1. Check Limits (simplified for now, full enforcement in kernel/enforcement.rs)
|
|
42
|
+
if let Some(max_steps) = policy.max_discovered_steps {
|
|
43
|
+
if steps_executed > max_steps {
|
|
44
|
+
runner.emitter().emit(StreamEvent::execution_failed(
|
|
45
|
+
runner.execution_id(),
|
|
46
|
+
crate::kernel::ExecutionError::quota_exceeded("Max steps exceeded"),
|
|
47
|
+
));
|
|
48
|
+
anyhow::bail!("Max steps exceeded");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if let Some(timeout) = policy.idle_timeout_seconds {
|
|
53
|
+
if start_time.elapsed() > Duration::from_secs(timeout) {
|
|
54
|
+
runner.emitter().emit(StreamEvent::execution_failed(
|
|
55
|
+
runner.execution_id(),
|
|
56
|
+
crate::kernel::ExecutionError::timeout(format!(
|
|
57
|
+
"Idle timeout after {}s",
|
|
58
|
+
timeout
|
|
59
|
+
)),
|
|
60
|
+
));
|
|
61
|
+
anyhow::bail!("Idle timeout");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 2. Execute Step
|
|
66
|
+
let result = runner
|
|
67
|
+
.run_callable(current_callable.as_ref() as &dyn Callable, ¤t_input)
|
|
68
|
+
.await;
|
|
69
|
+
|
|
70
|
+
match result {
|
|
71
|
+
Ok(output) => {
|
|
72
|
+
history.push(format!("Assistant: {}", output));
|
|
73
|
+
steps_executed += 1;
|
|
74
|
+
|
|
75
|
+
// 3. Check for Checkpoint
|
|
76
|
+
if policy.checkpointing.on_discovery
|
|
77
|
+
|| policy
|
|
78
|
+
.checkpointing
|
|
79
|
+
.interval_steps
|
|
80
|
+
.map_or(false, |i| steps_executed % i == 0)
|
|
81
|
+
{
|
|
82
|
+
// Create a checkpoint state (simplified)
|
|
83
|
+
let state = crate::graph::NodeState::from_str(&output);
|
|
84
|
+
if let Err(e) = runner
|
|
85
|
+
.save_checkpoint(state, Some(current_callable.name()))
|
|
86
|
+
.await
|
|
87
|
+
{
|
|
88
|
+
// Log warning but continue?
|
|
89
|
+
eprintln!("Failed to save checkpoint: {}", e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 4. Determine Next Step (Discovery)
|
|
94
|
+
// In a real agentic DAG, the output might contain instructions to run more tools
|
|
95
|
+
// or discover new steps. For this v0 implementation, we check if the callable
|
|
96
|
+
// returned a "DONE" signal or if it's a single-shot callable.
|
|
97
|
+
|
|
98
|
+
// TODO: Parse output for discovered steps if this is a DiscoveryAgent
|
|
99
|
+
// For now, we assume simple single-turn or limited loop
|
|
100
|
+
|
|
101
|
+
// IF output suggests more work, continue.
|
|
102
|
+
// IF output suggests completion, break.
|
|
103
|
+
|
|
104
|
+
// Simple heuristic for now: if it's just a generic callable, run once.
|
|
105
|
+
// If we want a loop, we'd need a "LoopCallable" wrapper or parsing logic.
|
|
106
|
+
// Given the prompt asks for "loop for long running executions", we implement
|
|
107
|
+
// the framework here but defaults to single pass unless discovered steps are found.
|
|
108
|
+
|
|
109
|
+
// (Placeholder for proper Discovery logic which would be in the Callable result)
|
|
110
|
+
|
|
111
|
+
return Ok(output);
|
|
112
|
+
}
|
|
113
|
+
Err(e) => return Err(e),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//! Runner module - thin execution shell
|
|
2
|
+
//!
|
|
3
|
+
//! The runner is a thin orchestration layer that:
|
|
4
|
+
//! - Accepts invocations
|
|
5
|
+
//! - Passes to Kernel
|
|
6
|
+
//! - Wires callbacks
|
|
7
|
+
//! - Returns streams
|
|
8
|
+
//!
|
|
9
|
+
//! Core execution logic lives in the kernel module.
|
|
10
|
+
//! Context types are in the context module.
|
|
11
|
+
//!
|
|
12
|
+
//! ## CRITICAL INVARIANT: Runner Never Mutates State
|
|
13
|
+
//!
|
|
14
|
+
//! Runner is a thin orchestration shell. All execution semantics live in kernel/.
|
|
15
|
+
//!
|
|
16
|
+
//! Runner MUST NOT:
|
|
17
|
+
//! - Mutate execution state directly
|
|
18
|
+
//! - Retry steps (kernel handles retries)
|
|
19
|
+
//! - Handle backoff (kernel handles backoff)
|
|
20
|
+
//! - Skip nodes (kernel handles node execution)
|
|
21
|
+
//!
|
|
22
|
+
//! If Runner starts doing these things → duplicated kernel logic (architectural leak).
|
|
23
|
+
//!
|
|
24
|
+
//! **What Runner Does:**
|
|
25
|
+
//! - Wires services (session, memory, artifacts)
|
|
26
|
+
//! - Handles callbacks (before/after hooks)
|
|
27
|
+
//! - Delegates execution to ExecutionKernel
|
|
28
|
+
//! - Streams events to clients
|
|
29
|
+
//!
|
|
30
|
+
//! **What Runner Does NOT Do:**
|
|
31
|
+
//! - Mutate execution state directly
|
|
32
|
+
//! - Implement execution semantics
|
|
33
|
+
//! - Make policy decisions
|
|
34
|
+
//!
|
|
35
|
+
//! @see docs/TECHNICAL/01-EXECUTION-TELEMETRY.md
|
|
36
|
+
//! @see docs/TECHNICAL/25-STREAM-PROCESSORS.md
|
|
37
|
+
|
|
38
|
+
mod callbacks;
|
|
39
|
+
mod execution_runner;
|
|
40
|
+
mod r#loop;
|
|
41
|
+
mod protected_runner;
|
|
42
|
+
|
|
43
|
+
// Runner types
|
|
44
|
+
pub use execution_runner::{DefaultRunner, Runner};
|
|
45
|
+
|
|
46
|
+
// Protected runner (feat-09: Guardrails)
|
|
47
|
+
pub use protected_runner::{DefaultProtectedRunner, InputBlockedError, ProtectedRunner};
|
|
48
|
+
|
|
49
|
+
// Re-export from kernel (kernel owns parallel/interrupt logic)
|
|
50
|
+
pub use crate::kernel::{
|
|
51
|
+
run_parallel, InterruptDecision, InterruptReason, InterruptableRunner, ParallelResult,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Re-export from context
|
|
55
|
+
pub use crate::context::{
|
|
56
|
+
InvocationContext, InvocationServices, ResourceLimits, RuntimeContext, RuntimeContextBuilder,
|
|
57
|
+
SessionContext, TraceContext,
|
|
58
|
+
};
|