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,831 @@
|
|
|
1
|
+
//! Execution Model - Runtime instance types for graph/agent execution
|
|
2
|
+
//!
|
|
3
|
+
//! This module defines the core model types:
|
|
4
|
+
//! - `Execution`: One run of a blueprint (graph, agent, workflow)
|
|
5
|
+
//! - `Step`: A distinct action within an execution
|
|
6
|
+
//!
|
|
7
|
+
//! These are the **model types** that hold execution state.
|
|
8
|
+
//! The **engine** (ExecutionKernel) uses these models to manage execution.
|
|
9
|
+
//!
|
|
10
|
+
//! ## ⚠️ CRITICAL INVARIANT: Data + Bookkeeping Only
|
|
11
|
+
//!
|
|
12
|
+
//! **This module MUST NOT contain execution logic or side effects.**
|
|
13
|
+
//!
|
|
14
|
+
//! This module is strictly for:
|
|
15
|
+
//! - Data structures (Execution, Step)
|
|
16
|
+
//! - Bookkeeping methods (getters, setters, state queries)
|
|
17
|
+
//!
|
|
18
|
+
//! This module MUST NEVER:
|
|
19
|
+
//! - Execute logic (no business logic, no decision-making)
|
|
20
|
+
//! - Call providers (no external service calls)
|
|
21
|
+
//! - Embed policy checks (policy belongs in kernel::enforcement)
|
|
22
|
+
//! - Perform side effects (no I/O, no mutations beyond self)
|
|
23
|
+
//!
|
|
24
|
+
//! If you find yourself adding execution logic here, it belongs in:
|
|
25
|
+
//! - `kernel::reducer` (for state transitions)
|
|
26
|
+
//! - `kernel::enforcement` (for policy checks)
|
|
27
|
+
//! - `kernel::execution_kernel` (for orchestration)
|
|
28
|
+
//!
|
|
29
|
+
//! ## Error Handling (feat-02)
|
|
30
|
+
//!
|
|
31
|
+
//! All errors are represented using `ExecutionError` which provides:
|
|
32
|
+
//! - Deterministic retry policies
|
|
33
|
+
//! - Structured error categories
|
|
34
|
+
//! - HTTP status code mapping
|
|
35
|
+
//! - Idempotency tracking
|
|
36
|
+
|
|
37
|
+
use super::error::ExecutionError;
|
|
38
|
+
use super::execution_state::{ExecutionState, StepState};
|
|
39
|
+
use super::ids::{CallableType, ExecutionId, ParentLink, StepId, StepSource, StepType, TenantId};
|
|
40
|
+
use serde::{Deserialize, Serialize};
|
|
41
|
+
use std::collections::HashMap;
|
|
42
|
+
use std::time::Instant;
|
|
43
|
+
|
|
44
|
+
/// Execution - one run of a blueprint
|
|
45
|
+
#[derive(Debug)]
|
|
46
|
+
pub struct Execution {
|
|
47
|
+
/// Unique execution ID
|
|
48
|
+
pub id: ExecutionId,
|
|
49
|
+
/// Tenant ID (REQUIRED for audit trail and multi-tenant isolation)
|
|
50
|
+
/// This is set from TenantContext when the kernel is created.
|
|
51
|
+
pub tenant_id: Option<TenantId>,
|
|
52
|
+
/// Current state in the lifecycle
|
|
53
|
+
pub state: ExecutionState,
|
|
54
|
+
/// Parent execution (if this is a sub-execution)
|
|
55
|
+
pub parent: Option<ParentLink>,
|
|
56
|
+
/// All steps in this execution
|
|
57
|
+
pub steps: HashMap<StepId, Step>,
|
|
58
|
+
/// Ordered list of step IDs (for replay)
|
|
59
|
+
pub step_order: Vec<StepId>,
|
|
60
|
+
/// Schema version hash (for replay validation)
|
|
61
|
+
pub schema_version: Option<String>,
|
|
62
|
+
/// Start time
|
|
63
|
+
pub started_at: Option<Instant>,
|
|
64
|
+
/// End time
|
|
65
|
+
pub ended_at: Option<Instant>,
|
|
66
|
+
/// Final output (if completed)
|
|
67
|
+
pub output: Option<String>,
|
|
68
|
+
/// Structured error (if failed) - see feat-02 Error Taxonomy
|
|
69
|
+
pub error: Option<ExecutionError>,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
impl Execution {
|
|
73
|
+
/// Create a new execution
|
|
74
|
+
pub fn new() -> Self {
|
|
75
|
+
Self {
|
|
76
|
+
id: ExecutionId::new(),
|
|
77
|
+
tenant_id: None,
|
|
78
|
+
state: ExecutionState::Created,
|
|
79
|
+
parent: None,
|
|
80
|
+
steps: HashMap::new(),
|
|
81
|
+
step_order: Vec::new(),
|
|
82
|
+
schema_version: None,
|
|
83
|
+
started_at: None,
|
|
84
|
+
ended_at: None,
|
|
85
|
+
output: None,
|
|
86
|
+
error: None,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// Create a new execution with a specific ID
|
|
91
|
+
pub fn with_id(id: ExecutionId) -> Self {
|
|
92
|
+
Self {
|
|
93
|
+
id,
|
|
94
|
+
tenant_id: None,
|
|
95
|
+
state: ExecutionState::Created,
|
|
96
|
+
parent: None,
|
|
97
|
+
steps: HashMap::new(),
|
|
98
|
+
step_order: Vec::new(),
|
|
99
|
+
schema_version: None,
|
|
100
|
+
started_at: None,
|
|
101
|
+
ended_at: None,
|
|
102
|
+
output: None,
|
|
103
|
+
error: None,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// Create a new execution with tenant ID
|
|
108
|
+
pub fn with_tenant(tenant_id: TenantId) -> Self {
|
|
109
|
+
Self {
|
|
110
|
+
id: ExecutionId::new(),
|
|
111
|
+
tenant_id: Some(tenant_id),
|
|
112
|
+
state: ExecutionState::Created,
|
|
113
|
+
parent: None,
|
|
114
|
+
steps: HashMap::new(),
|
|
115
|
+
step_order: Vec::new(),
|
|
116
|
+
schema_version: None,
|
|
117
|
+
started_at: None,
|
|
118
|
+
ended_at: None,
|
|
119
|
+
output: None,
|
|
120
|
+
error: None,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Create a child execution
|
|
125
|
+
/// Inherits tenant_id from parent for multi-tenant isolation
|
|
126
|
+
pub fn child(&self) -> Self {
|
|
127
|
+
let mut child = Self::new();
|
|
128
|
+
child.parent = Some(ParentLink::execution(self.id.clone()));
|
|
129
|
+
child.tenant_id = self.tenant_id.clone(); // Inherit tenant from parent
|
|
130
|
+
child
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Set schema version for replay validation
|
|
134
|
+
pub fn with_schema_version(mut self, version: impl Into<String>) -> Self {
|
|
135
|
+
self.schema_version = Some(version.into());
|
|
136
|
+
self
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// Get a step by ID
|
|
140
|
+
pub fn get_step(&self, id: &StepId) -> Option<&Step> {
|
|
141
|
+
self.steps.get(id)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Get a mutable step by ID
|
|
145
|
+
pub fn get_step_mut(&mut self, id: &StepId) -> Option<&mut Step> {
|
|
146
|
+
self.steps.get_mut(id)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// Add a new step
|
|
150
|
+
pub fn add_step(&mut self, step: Step) {
|
|
151
|
+
self.step_order.push(step.id.clone());
|
|
152
|
+
self.steps.insert(step.id.clone(), step);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Get execution duration in milliseconds
|
|
156
|
+
pub fn duration_ms(&self) -> Option<u64> {
|
|
157
|
+
match (self.started_at, self.ended_at) {
|
|
158
|
+
(Some(start), Some(end)) => Some(end.duration_since(start).as_millis() as u64),
|
|
159
|
+
(Some(start), None) => Some(start.elapsed().as_millis() as u64),
|
|
160
|
+
_ => None,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Check if execution is in a terminal state
|
|
165
|
+
pub fn is_terminal(&self) -> bool {
|
|
166
|
+
self.state.is_terminal()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
impl Default for Execution {
|
|
171
|
+
fn default() -> Self {
|
|
172
|
+
Self::new()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Step - a distinct action within an execution
|
|
177
|
+
///
|
|
178
|
+
/// @see packages/enact-schemas/src/streaming.schemas.ts - stepEventDataSchema
|
|
179
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
180
|
+
#[serde(rename_all = "camelCase")]
|
|
181
|
+
pub struct Step {
|
|
182
|
+
/// Unique step ID
|
|
183
|
+
#[serde(rename = "stepId")]
|
|
184
|
+
pub id: StepId,
|
|
185
|
+
/// Parent step (for nested operations)
|
|
186
|
+
#[serde(rename = "parentStepId")]
|
|
187
|
+
pub parent_step_id: Option<StepId>,
|
|
188
|
+
/// Step type
|
|
189
|
+
pub step_type: StepType,
|
|
190
|
+
/// Step name/label
|
|
191
|
+
pub name: String,
|
|
192
|
+
/// Current state (matches `state` in TypeScript schema)
|
|
193
|
+
pub state: StepState,
|
|
194
|
+
/// Input to this step
|
|
195
|
+
pub input: Option<String>,
|
|
196
|
+
/// Output from this step
|
|
197
|
+
pub output: Option<String>,
|
|
198
|
+
/// Structured error (if failed) - see feat-02 Error Taxonomy
|
|
199
|
+
pub error: Option<ExecutionError>,
|
|
200
|
+
/// Duration in milliseconds
|
|
201
|
+
pub duration_ms: Option<u64>,
|
|
202
|
+
/// Timestamp when step started (unix millis)
|
|
203
|
+
pub started_at: Option<i64>,
|
|
204
|
+
/// Timestamp when step ended (unix millis)
|
|
205
|
+
pub ended_at: Option<i64>,
|
|
206
|
+
/// Source/origin of this step (how/why it was created)
|
|
207
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
208
|
+
pub source: Option<StepSource>,
|
|
209
|
+
/// Callable ID (stable identifier for billing/traceability)
|
|
210
|
+
/// Unlike `name` which can change, this provides a stable reference.
|
|
211
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
212
|
+
pub callable_id: Option<String>,
|
|
213
|
+
/// Callable type (for billing differentiation and audit trails)
|
|
214
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
215
|
+
pub callable_type: Option<CallableType>,
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
impl Step {
|
|
219
|
+
/// Create a new step
|
|
220
|
+
pub fn new(step_type: StepType, name: impl Into<String>) -> Self {
|
|
221
|
+
Self {
|
|
222
|
+
id: StepId::new(),
|
|
223
|
+
parent_step_id: None,
|
|
224
|
+
step_type,
|
|
225
|
+
name: name.into(),
|
|
226
|
+
state: StepState::Pending,
|
|
227
|
+
input: None,
|
|
228
|
+
output: None,
|
|
229
|
+
error: None,
|
|
230
|
+
duration_ms: None,
|
|
231
|
+
started_at: None,
|
|
232
|
+
ended_at: None,
|
|
233
|
+
source: None,
|
|
234
|
+
callable_id: None,
|
|
235
|
+
callable_type: None,
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/// Create a nested step under a parent
|
|
240
|
+
pub fn nested(parent_id: &StepId, step_type: StepType, name: impl Into<String>) -> Self {
|
|
241
|
+
let mut step = Self::new(step_type, name);
|
|
242
|
+
step.parent_step_id = Some(parent_id.clone());
|
|
243
|
+
step
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/// Set input
|
|
247
|
+
pub fn with_input(mut self, input: impl Into<String>) -> Self {
|
|
248
|
+
self.input = Some(input.into());
|
|
249
|
+
self
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// Set source/origin of this step
|
|
253
|
+
pub fn with_source(mut self, source: StepSource) -> Self {
|
|
254
|
+
self.source = Some(source);
|
|
255
|
+
self
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/// Set callable info (for billing/traceability)
|
|
259
|
+
///
|
|
260
|
+
/// Unlike `name` which can change, callable_id provides a stable reference
|
|
261
|
+
/// for cost attribution and audit trails.
|
|
262
|
+
pub fn with_callable(
|
|
263
|
+
mut self,
|
|
264
|
+
callable_id: impl Into<String>,
|
|
265
|
+
callable_type: CallableType,
|
|
266
|
+
) -> Self {
|
|
267
|
+
self.callable_id = Some(callable_id.into());
|
|
268
|
+
self.callable_type = Some(callable_type);
|
|
269
|
+
self
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#[cfg(test)]
|
|
274
|
+
mod tests {
|
|
275
|
+
use super::super::execution_state::WaitReason;
|
|
276
|
+
use super::super::ids::StepSourceType;
|
|
277
|
+
use super::*;
|
|
278
|
+
|
|
279
|
+
// =========================================================================
|
|
280
|
+
// Execution Tests
|
|
281
|
+
// =========================================================================
|
|
282
|
+
|
|
283
|
+
#[test]
|
|
284
|
+
fn test_execution_new() {
|
|
285
|
+
let exec = Execution::new();
|
|
286
|
+
assert!(exec.id.as_str().starts_with("exec_"));
|
|
287
|
+
assert_eq!(exec.state, ExecutionState::Created);
|
|
288
|
+
assert!(exec.parent.is_none());
|
|
289
|
+
assert!(exec.steps.is_empty());
|
|
290
|
+
assert!(exec.step_order.is_empty());
|
|
291
|
+
assert!(exec.schema_version.is_none());
|
|
292
|
+
assert!(exec.started_at.is_none());
|
|
293
|
+
assert!(exec.ended_at.is_none());
|
|
294
|
+
assert!(exec.output.is_none());
|
|
295
|
+
assert!(exec.error.is_none());
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#[test]
|
|
299
|
+
fn test_execution_with_id() {
|
|
300
|
+
let id = ExecutionId::from_string("exec_custom_id");
|
|
301
|
+
let exec = Execution::with_id(id.clone());
|
|
302
|
+
assert_eq!(exec.id.as_str(), "exec_custom_id");
|
|
303
|
+
assert_eq!(exec.state, ExecutionState::Created);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
#[test]
|
|
307
|
+
fn test_execution_child() {
|
|
308
|
+
let parent = Execution::new();
|
|
309
|
+
let child = parent.child();
|
|
310
|
+
|
|
311
|
+
assert!(child.parent.is_some());
|
|
312
|
+
let parent_link = child.parent.unwrap();
|
|
313
|
+
assert_eq!(parent_link.parent_id, parent.id.as_str());
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
#[test]
|
|
317
|
+
fn test_execution_with_schema_version() {
|
|
318
|
+
let exec = Execution::new().with_schema_version("v1.0.0");
|
|
319
|
+
assert_eq!(exec.schema_version, Some("v1.0.0".to_string()));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#[test]
|
|
323
|
+
fn test_execution_with_schema_version_owned_string() {
|
|
324
|
+
let exec = Execution::new().with_schema_version(String::from("v2.0.0"));
|
|
325
|
+
assert_eq!(exec.schema_version, Some("v2.0.0".to_string()));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
#[test]
|
|
329
|
+
fn test_execution_add_step() {
|
|
330
|
+
let mut exec = Execution::new();
|
|
331
|
+
let step = Step::new(StepType::LlmNode, "test_step");
|
|
332
|
+
let step_id = step.id.clone();
|
|
333
|
+
|
|
334
|
+
exec.add_step(step);
|
|
335
|
+
|
|
336
|
+
assert_eq!(exec.steps.len(), 1);
|
|
337
|
+
assert_eq!(exec.step_order.len(), 1);
|
|
338
|
+
assert!(exec.steps.contains_key(&step_id));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
#[test]
|
|
342
|
+
fn test_execution_get_step() {
|
|
343
|
+
let mut exec = Execution::new();
|
|
344
|
+
let step = Step::new(StepType::ToolNode, "get_step_test");
|
|
345
|
+
let step_id = step.id.clone();
|
|
346
|
+
exec.add_step(step);
|
|
347
|
+
|
|
348
|
+
let retrieved = exec.get_step(&step_id);
|
|
349
|
+
assert!(retrieved.is_some());
|
|
350
|
+
assert_eq!(retrieved.unwrap().name, "get_step_test");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
#[test]
|
|
354
|
+
fn test_execution_get_step_not_found() {
|
|
355
|
+
let exec = Execution::new();
|
|
356
|
+
let nonexistent_id = StepId::from_string("step_nonexistent");
|
|
357
|
+
assert!(exec.get_step(&nonexistent_id).is_none());
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
#[test]
|
|
361
|
+
fn test_execution_get_step_mut() {
|
|
362
|
+
let mut exec = Execution::new();
|
|
363
|
+
let step = Step::new(StepType::FunctionNode, "mutable_step");
|
|
364
|
+
let step_id = step.id.clone();
|
|
365
|
+
exec.add_step(step);
|
|
366
|
+
|
|
367
|
+
let step_mut = exec.get_step_mut(&step_id);
|
|
368
|
+
assert!(step_mut.is_some());
|
|
369
|
+
|
|
370
|
+
step_mut.unwrap().output = Some("modified".to_string());
|
|
371
|
+
|
|
372
|
+
let step = exec.get_step(&step_id).unwrap();
|
|
373
|
+
assert_eq!(step.output, Some("modified".to_string()));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
#[test]
|
|
377
|
+
fn test_execution_step_order_preserved() {
|
|
378
|
+
let mut exec = Execution::new();
|
|
379
|
+
let step1 = Step::new(StepType::LlmNode, "step1");
|
|
380
|
+
let step2 = Step::new(StepType::ToolNode, "step2");
|
|
381
|
+
let step3 = Step::new(StepType::FunctionNode, "step3");
|
|
382
|
+
|
|
383
|
+
let id1 = step1.id.clone();
|
|
384
|
+
let id2 = step2.id.clone();
|
|
385
|
+
let id3 = step3.id.clone();
|
|
386
|
+
|
|
387
|
+
exec.add_step(step1);
|
|
388
|
+
exec.add_step(step2);
|
|
389
|
+
exec.add_step(step3);
|
|
390
|
+
|
|
391
|
+
assert_eq!(exec.step_order[0], id1);
|
|
392
|
+
assert_eq!(exec.step_order[1], id2);
|
|
393
|
+
assert_eq!(exec.step_order[2], id3);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
#[test]
|
|
397
|
+
fn test_execution_duration_ms_not_started() {
|
|
398
|
+
let exec = Execution::new();
|
|
399
|
+
assert!(exec.duration_ms().is_none());
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#[test]
|
|
403
|
+
fn test_execution_duration_ms_started_not_ended() {
|
|
404
|
+
let mut exec = Execution::new();
|
|
405
|
+
exec.started_at = Some(Instant::now());
|
|
406
|
+
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
407
|
+
|
|
408
|
+
let duration = exec.duration_ms();
|
|
409
|
+
assert!(duration.is_some());
|
|
410
|
+
assert!(duration.unwrap() >= 10);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
#[test]
|
|
414
|
+
fn test_execution_duration_ms_completed() {
|
|
415
|
+
let mut exec = Execution::new();
|
|
416
|
+
let start = Instant::now();
|
|
417
|
+
std::thread::sleep(std::time::Duration::from_millis(20));
|
|
418
|
+
let end = Instant::now();
|
|
419
|
+
|
|
420
|
+
exec.started_at = Some(start);
|
|
421
|
+
exec.ended_at = Some(end);
|
|
422
|
+
|
|
423
|
+
let duration = exec.duration_ms();
|
|
424
|
+
assert!(duration.is_some());
|
|
425
|
+
assert!(duration.unwrap() >= 20);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
#[test]
|
|
429
|
+
fn test_execution_is_terminal_created() {
|
|
430
|
+
let exec = Execution::new();
|
|
431
|
+
assert!(!exec.is_terminal());
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
#[test]
|
|
435
|
+
fn test_execution_is_terminal_running() {
|
|
436
|
+
let mut exec = Execution::new();
|
|
437
|
+
exec.state = ExecutionState::Running;
|
|
438
|
+
assert!(!exec.is_terminal());
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
#[test]
|
|
442
|
+
fn test_execution_is_terminal_completed() {
|
|
443
|
+
let mut exec = Execution::new();
|
|
444
|
+
exec.state = ExecutionState::Completed;
|
|
445
|
+
assert!(exec.is_terminal());
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
#[test]
|
|
449
|
+
fn test_execution_is_terminal_failed() {
|
|
450
|
+
let mut exec = Execution::new();
|
|
451
|
+
exec.state = ExecutionState::Failed;
|
|
452
|
+
assert!(exec.is_terminal());
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
#[test]
|
|
456
|
+
fn test_execution_is_terminal_cancelled() {
|
|
457
|
+
let mut exec = Execution::new();
|
|
458
|
+
exec.state = ExecutionState::Cancelled;
|
|
459
|
+
assert!(exec.is_terminal());
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
#[test]
|
|
463
|
+
fn test_execution_is_terminal_paused() {
|
|
464
|
+
let mut exec = Execution::new();
|
|
465
|
+
exec.state = ExecutionState::Paused;
|
|
466
|
+
assert!(!exec.is_terminal());
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
#[test]
|
|
470
|
+
fn test_execution_is_terminal_waiting() {
|
|
471
|
+
let mut exec = Execution::new();
|
|
472
|
+
exec.state = ExecutionState::Waiting(WaitReason::Approval);
|
|
473
|
+
assert!(!exec.is_terminal());
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
#[test]
|
|
477
|
+
fn test_execution_default() {
|
|
478
|
+
let exec: Execution = Default::default();
|
|
479
|
+
assert!(exec.id.as_str().starts_with("exec_"));
|
|
480
|
+
assert_eq!(exec.state, ExecutionState::Created);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// =========================================================================
|
|
484
|
+
// Step Tests
|
|
485
|
+
// =========================================================================
|
|
486
|
+
|
|
487
|
+
#[test]
|
|
488
|
+
fn test_step_new() {
|
|
489
|
+
let step = Step::new(StepType::LlmNode, "test_step");
|
|
490
|
+
assert!(step.id.as_str().starts_with("step_"));
|
|
491
|
+
assert_eq!(step.step_type, StepType::LlmNode);
|
|
492
|
+
assert_eq!(step.name, "test_step");
|
|
493
|
+
assert_eq!(step.state, StepState::Pending);
|
|
494
|
+
assert!(step.parent_step_id.is_none());
|
|
495
|
+
assert!(step.input.is_none());
|
|
496
|
+
assert!(step.output.is_none());
|
|
497
|
+
assert!(step.error.is_none());
|
|
498
|
+
assert!(step.duration_ms.is_none());
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
#[test]
|
|
502
|
+
fn test_step_new_all_types() {
|
|
503
|
+
let types = vec![
|
|
504
|
+
StepType::LlmNode,
|
|
505
|
+
StepType::GraphNode,
|
|
506
|
+
StepType::ToolNode,
|
|
507
|
+
StepType::FunctionNode,
|
|
508
|
+
StepType::RouterNode,
|
|
509
|
+
StepType::BranchNode,
|
|
510
|
+
StepType::LoopNode,
|
|
511
|
+
];
|
|
512
|
+
|
|
513
|
+
for step_type in types {
|
|
514
|
+
let step = Step::new(step_type.clone(), "test");
|
|
515
|
+
assert_eq!(step.step_type, step_type);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
#[test]
|
|
520
|
+
fn test_step_nested() {
|
|
521
|
+
let parent_id = StepId::from_string("step_parent");
|
|
522
|
+
let step = Step::nested(&parent_id, StepType::ToolNode, "nested_step");
|
|
523
|
+
|
|
524
|
+
assert!(step.parent_step_id.is_some());
|
|
525
|
+
assert_eq!(step.parent_step_id.unwrap().as_str(), "step_parent");
|
|
526
|
+
assert_eq!(step.step_type, StepType::ToolNode);
|
|
527
|
+
assert_eq!(step.name, "nested_step");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#[test]
|
|
531
|
+
fn test_step_with_input() {
|
|
532
|
+
let step = Step::new(StepType::LlmNode, "input_step").with_input("Hello, AI!");
|
|
533
|
+
|
|
534
|
+
assert!(step.input.is_some());
|
|
535
|
+
assert_eq!(step.input.unwrap(), "Hello, AI!");
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
#[test]
|
|
539
|
+
fn test_step_with_input_owned_string() {
|
|
540
|
+
let step =
|
|
541
|
+
Step::new(StepType::LlmNode, "input_step").with_input(String::from("Owned input"));
|
|
542
|
+
|
|
543
|
+
assert!(step.input.is_some());
|
|
544
|
+
assert_eq!(step.input.unwrap(), "Owned input");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
#[test]
|
|
548
|
+
fn test_step_clone() {
|
|
549
|
+
let step = Step::new(StepType::FunctionNode, "cloneable").with_input("input data");
|
|
550
|
+
let cloned = step.clone();
|
|
551
|
+
|
|
552
|
+
assert_eq!(step.id.as_str(), cloned.id.as_str());
|
|
553
|
+
assert_eq!(step.name, cloned.name);
|
|
554
|
+
assert_eq!(step.input, cloned.input);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
#[test]
|
|
558
|
+
fn test_step_serde() {
|
|
559
|
+
let step = Step::new(StepType::GraphNode, "serializable").with_input("input");
|
|
560
|
+
|
|
561
|
+
let json = serde_json::to_string(&step).unwrap();
|
|
562
|
+
let parsed: Step = serde_json::from_str(&json).unwrap();
|
|
563
|
+
|
|
564
|
+
assert_eq!(step.name, parsed.name);
|
|
565
|
+
assert_eq!(step.step_type, parsed.step_type);
|
|
566
|
+
assert_eq!(step.input, parsed.input);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
#[test]
|
|
570
|
+
fn test_step_serde_field_names() {
|
|
571
|
+
// Verify JSON field names match TypeScript schema (camelCase)
|
|
572
|
+
let step = Step::new(StepType::LlmNode, "test_step").with_input("test input");
|
|
573
|
+
|
|
574
|
+
let json = serde_json::to_string(&step).unwrap();
|
|
575
|
+
|
|
576
|
+
// Check that camelCase field names are used
|
|
577
|
+
assert!(json.contains("\"stepId\""), "Should have stepId field");
|
|
578
|
+
assert!(json.contains("\"stepType\""), "Should have stepType field");
|
|
579
|
+
assert!(json.contains("\"state\""), "Should have state field");
|
|
580
|
+
assert!(
|
|
581
|
+
json.contains("\"durationMs\""),
|
|
582
|
+
"Should have durationMs field"
|
|
583
|
+
);
|
|
584
|
+
assert!(
|
|
585
|
+
json.contains("\"startedAt\""),
|
|
586
|
+
"Should have startedAt field"
|
|
587
|
+
);
|
|
588
|
+
assert!(json.contains("\"endedAt\""), "Should have endedAt field");
|
|
589
|
+
|
|
590
|
+
// Verify no snake_case field names
|
|
591
|
+
assert!(
|
|
592
|
+
!json.contains("\"step_id\""),
|
|
593
|
+
"Should NOT have step_id field"
|
|
594
|
+
);
|
|
595
|
+
assert!(
|
|
596
|
+
!json.contains("\"step_type\""),
|
|
597
|
+
"Should NOT have step_type field"
|
|
598
|
+
);
|
|
599
|
+
assert!(
|
|
600
|
+
!json.contains("\"duration_ms\""),
|
|
601
|
+
"Should NOT have duration_ms field"
|
|
602
|
+
);
|
|
603
|
+
assert!(
|
|
604
|
+
!json.contains("\"started_at\""),
|
|
605
|
+
"Should NOT have started_at field"
|
|
606
|
+
);
|
|
607
|
+
assert!(
|
|
608
|
+
!json.contains("\"ended_at\""),
|
|
609
|
+
"Should NOT have ended_at field"
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
#[test]
|
|
614
|
+
fn test_step_timestamps() {
|
|
615
|
+
let mut step = Step::new(StepType::ToolNode, "timestamped");
|
|
616
|
+
let now = chrono::Utc::now().timestamp_millis();
|
|
617
|
+
|
|
618
|
+
step.started_at = Some(now);
|
|
619
|
+
step.ended_at = Some(now + 1000);
|
|
620
|
+
step.duration_ms = Some(1000);
|
|
621
|
+
|
|
622
|
+
assert_eq!(step.started_at, Some(now));
|
|
623
|
+
assert_eq!(step.ended_at, Some(now + 1000));
|
|
624
|
+
assert_eq!(step.duration_ms, Some(1000));
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
#[test]
|
|
628
|
+
fn test_step_state_modifications() {
|
|
629
|
+
let mut step = Step::new(StepType::LlmNode, "state_step");
|
|
630
|
+
|
|
631
|
+
assert_eq!(step.state, StepState::Pending);
|
|
632
|
+
|
|
633
|
+
step.state = StepState::Running;
|
|
634
|
+
assert_eq!(step.state, StepState::Running);
|
|
635
|
+
|
|
636
|
+
step.state = StepState::Completed;
|
|
637
|
+
step.output = Some("Result".to_string());
|
|
638
|
+
assert_eq!(step.state, StepState::Completed);
|
|
639
|
+
assert_eq!(step.output, Some("Result".to_string()));
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
#[test]
|
|
643
|
+
fn test_step_error_handling() {
|
|
644
|
+
let mut step = Step::new(StepType::ToolNode, "error_step");
|
|
645
|
+
|
|
646
|
+
step.state = StepState::Failed;
|
|
647
|
+
step.error = Some(ExecutionError::kernel_internal("Test error"));
|
|
648
|
+
|
|
649
|
+
assert!(step.error.is_some());
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// =========================================================================
|
|
653
|
+
// Integration Tests
|
|
654
|
+
// =========================================================================
|
|
655
|
+
|
|
656
|
+
#[test]
|
|
657
|
+
fn test_execution_with_multiple_steps() {
|
|
658
|
+
let mut exec = Execution::new();
|
|
659
|
+
exec.state = ExecutionState::Running;
|
|
660
|
+
exec.started_at = Some(Instant::now());
|
|
661
|
+
|
|
662
|
+
// Add multiple steps
|
|
663
|
+
for i in 0..5 {
|
|
664
|
+
let step = Step::new(StepType::FunctionNode, format!("step_{}", i))
|
|
665
|
+
.with_input(format!("input_{}", i));
|
|
666
|
+
exec.add_step(step);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
assert_eq!(exec.steps.len(), 5);
|
|
670
|
+
assert_eq!(exec.step_order.len(), 5);
|
|
671
|
+
|
|
672
|
+
// Verify all steps are accessible
|
|
673
|
+
for step_id in &exec.step_order {
|
|
674
|
+
assert!(exec.get_step(step_id).is_some());
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
#[test]
|
|
679
|
+
fn test_nested_execution_structure() {
|
|
680
|
+
let root = Execution::new();
|
|
681
|
+
let child1 = root.child();
|
|
682
|
+
let child2 = root.child();
|
|
683
|
+
|
|
684
|
+
// Both children should have the same parent
|
|
685
|
+
assert!(child1.parent.is_some());
|
|
686
|
+
assert!(child2.parent.is_some());
|
|
687
|
+
assert_eq!(
|
|
688
|
+
child1.parent.as_ref().unwrap().parent_id,
|
|
689
|
+
child2.parent.as_ref().unwrap().parent_id
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
// But different IDs
|
|
693
|
+
assert_ne!(child1.id.as_str(), child2.id.as_str());
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
#[test]
|
|
697
|
+
fn test_nested_steps_structure() {
|
|
698
|
+
let parent_step = Step::new(StepType::GraphNode, "parent");
|
|
699
|
+
let parent_id = parent_step.id.clone();
|
|
700
|
+
|
|
701
|
+
let child1 = Step::nested(&parent_id, StepType::LlmNode, "child1");
|
|
702
|
+
let child2 = Step::nested(&parent_id, StepType::ToolNode, "child2");
|
|
703
|
+
|
|
704
|
+
assert_eq!(
|
|
705
|
+
child1.parent_step_id.as_ref().unwrap().as_str(),
|
|
706
|
+
parent_id.as_str()
|
|
707
|
+
);
|
|
708
|
+
assert_eq!(
|
|
709
|
+
child2.parent_step_id.as_ref().unwrap().as_str(),
|
|
710
|
+
parent_id.as_str()
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// =========================================================================
|
|
715
|
+
// Callable Tracking Tests (for billing/traceability)
|
|
716
|
+
// =========================================================================
|
|
717
|
+
|
|
718
|
+
#[test]
|
|
719
|
+
fn test_step_with_callable() {
|
|
720
|
+
let step = Step::new(StepType::GraphNode, "Research Agent")
|
|
721
|
+
.with_callable("research-agent-v2", CallableType::Agent);
|
|
722
|
+
|
|
723
|
+
assert!(step.callable_id.is_some());
|
|
724
|
+
assert_eq!(step.callable_id.unwrap(), "research-agent-v2");
|
|
725
|
+
assert!(step.callable_type.is_some());
|
|
726
|
+
assert_eq!(step.callable_type.unwrap(), CallableType::Agent);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
#[test]
|
|
730
|
+
fn test_step_callable_serde() {
|
|
731
|
+
let step = Step::new(StepType::GraphNode, "Chat Handler")
|
|
732
|
+
.with_callable("chat-handler", CallableType::Chat);
|
|
733
|
+
|
|
734
|
+
let json = serde_json::to_string(&step).unwrap();
|
|
735
|
+
let parsed: Step = serde_json::from_str(&json).unwrap();
|
|
736
|
+
|
|
737
|
+
assert_eq!(parsed.callable_id, Some("chat-handler".to_string()));
|
|
738
|
+
assert_eq!(parsed.callable_type, Some(CallableType::Chat));
|
|
739
|
+
|
|
740
|
+
// Verify camelCase field names
|
|
741
|
+
assert!(
|
|
742
|
+
json.contains("\"callableId\""),
|
|
743
|
+
"Should have callableId field"
|
|
744
|
+
);
|
|
745
|
+
assert!(
|
|
746
|
+
json.contains("\"callableType\""),
|
|
747
|
+
"Should have callableType field"
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
#[test]
|
|
752
|
+
fn test_step_callable_none_not_serialized() {
|
|
753
|
+
let step = Step::new(StepType::LlmNode, "No Callable Info");
|
|
754
|
+
|
|
755
|
+
let json = serde_json::to_string(&step).unwrap();
|
|
756
|
+
|
|
757
|
+
// Optional None fields should not appear in JSON (skip_serializing_if)
|
|
758
|
+
assert!(
|
|
759
|
+
!json.contains("callableId"),
|
|
760
|
+
"Should NOT serialize None callableId"
|
|
761
|
+
);
|
|
762
|
+
assert!(
|
|
763
|
+
!json.contains("callableType"),
|
|
764
|
+
"Should NOT serialize None callableType"
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
#[test]
|
|
769
|
+
fn test_callable_type_display() {
|
|
770
|
+
assert_eq!(format!("{}", CallableType::Completion), "completion");
|
|
771
|
+
assert_eq!(format!("{}", CallableType::Chat), "chat");
|
|
772
|
+
assert_eq!(format!("{}", CallableType::Agent), "agent");
|
|
773
|
+
assert_eq!(format!("{}", CallableType::Workflow), "workflow");
|
|
774
|
+
assert_eq!(format!("{}", CallableType::Background), "background");
|
|
775
|
+
assert_eq!(format!("{}", CallableType::Composite), "composite");
|
|
776
|
+
assert_eq!(format!("{}", CallableType::Tool), "tool");
|
|
777
|
+
assert_eq!(format!("{}", CallableType::Custom), "custom");
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
#[test]
|
|
781
|
+
fn test_callable_type_default() {
|
|
782
|
+
let default_type = CallableType::default();
|
|
783
|
+
assert_eq!(default_type, CallableType::Agent);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
#[test]
|
|
787
|
+
fn test_callable_type_serde_all_variants() {
|
|
788
|
+
let variants = vec![
|
|
789
|
+
CallableType::Completion,
|
|
790
|
+
CallableType::Chat,
|
|
791
|
+
CallableType::Agent,
|
|
792
|
+
CallableType::Workflow,
|
|
793
|
+
CallableType::Background,
|
|
794
|
+
CallableType::Composite,
|
|
795
|
+
CallableType::Tool,
|
|
796
|
+
CallableType::Custom,
|
|
797
|
+
];
|
|
798
|
+
|
|
799
|
+
for variant in variants {
|
|
800
|
+
let json = serde_json::to_string(&variant).unwrap();
|
|
801
|
+
let parsed: CallableType = serde_json::from_str(&json).unwrap();
|
|
802
|
+
assert_eq!(parsed, variant);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
#[test]
|
|
807
|
+
fn test_step_chained_builders() {
|
|
808
|
+
// Test that all builders can be chained
|
|
809
|
+
let parent_id = StepId::from_string("step_parent");
|
|
810
|
+
let step = Step::new(StepType::GraphNode, "Full Step")
|
|
811
|
+
.with_input("User request")
|
|
812
|
+
.with_callable("research-agent", CallableType::Agent)
|
|
813
|
+
.with_source(StepSource {
|
|
814
|
+
source_type: StepSourceType::Discovered,
|
|
815
|
+
triggered_by: Some("step_123".to_string()),
|
|
816
|
+
reason: Some("LLM suggested sub-task".to_string()),
|
|
817
|
+
depth: Some(1),
|
|
818
|
+
spawn_mode: None,
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
assert_eq!(step.name, "Full Step");
|
|
822
|
+
assert_eq!(step.input, Some("User request".to_string()));
|
|
823
|
+
assert_eq!(step.callable_id, Some("research-agent".to_string()));
|
|
824
|
+
assert_eq!(step.callable_type, Some(CallableType::Agent));
|
|
825
|
+
assert!(step.source.is_some());
|
|
826
|
+
assert_eq!(
|
|
827
|
+
step.source.as_ref().unwrap().source_type,
|
|
828
|
+
StepSourceType::Discovered
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
}
|