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,182 @@
|
|
|
1
|
+
+++
|
|
2
|
+
title = "Telegram"
|
|
3
|
+
weight = 1
|
|
4
|
+
+++
|
|
5
|
+
|
|
6
|
+
# Telegram Channel
|
|
7
|
+
|
|
8
|
+
Deploy your Enact agent to Telegram.
|
|
9
|
+
|
|
10
|
+
## Status
|
|
11
|
+
|
|
12
|
+
| Feature | Status |
|
|
13
|
+
|---------|--------|
|
|
14
|
+
| Bot API Integration | ✅ Implemented |
|
|
15
|
+
| Text Messages | ✅ Implemented |
|
|
16
|
+
| Inline Keyboards | ✅ Implemented |
|
|
17
|
+
| Media Support | ✅ Implemented |
|
|
18
|
+
| Webhook Mode | ✅ Implemented |
|
|
19
|
+
| Polling Mode | ✅ Implemented |
|
|
20
|
+
| Group Chats | ✅ Implemented |
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
### 1. Create a Bot
|
|
25
|
+
|
|
26
|
+
1. Open Telegram and message [@BotFather](https://t.me/botfather)
|
|
27
|
+
2. Send `/newbot`
|
|
28
|
+
3. Follow prompts to name your bot
|
|
29
|
+
4. Copy the bot token (looks like `123456:ABC-DEF...`)
|
|
30
|
+
|
|
31
|
+
### 2. Configure Environment
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# .env
|
|
35
|
+
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Run Channel (Rust API)
|
|
39
|
+
|
|
40
|
+
```rust
|
|
41
|
+
use enact_channels::telegram::{TelegramChannel, TelegramConfig};
|
|
42
|
+
|
|
43
|
+
#[tokio::main]
|
|
44
|
+
async fn main() -> anyhow::Result<()> {
|
|
45
|
+
let config = TelegramConfig {
|
|
46
|
+
bot_token: std::env::var("TELEGRAM_BOT_TOKEN")?,
|
|
47
|
+
..Default::default()
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
let channel = TelegramChannel::new(config)?;
|
|
51
|
+
channel.run().await?;
|
|
52
|
+
|
|
53
|
+
Ok(())
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Configuration Options
|
|
58
|
+
|
|
59
|
+
```rust
|
|
60
|
+
pub struct TelegramConfig {
|
|
61
|
+
/// Bot API token from BotFather
|
|
62
|
+
pub bot_token: String,
|
|
63
|
+
|
|
64
|
+
/// Allowed user IDs (empty = allow all)
|
|
65
|
+
pub allowed_users: Vec<i64>,
|
|
66
|
+
|
|
67
|
+
/// Polling timeout in seconds
|
|
68
|
+
pub poll_timeout: u64, // default: 30
|
|
69
|
+
|
|
70
|
+
/// Webhook URL (if using webhook mode)
|
|
71
|
+
pub webhook_url: Option<String>,
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
### Receiving Messages
|
|
78
|
+
|
|
79
|
+
```rust
|
|
80
|
+
let message = channel.receive().await?;
|
|
81
|
+
|
|
82
|
+
match message.message_type {
|
|
83
|
+
MessageType::Text(text) => {
|
|
84
|
+
println!("Received: {}", text);
|
|
85
|
+
}
|
|
86
|
+
MessageType::Photo(photo) => {
|
|
87
|
+
println!("Photo: {}", photo.file_id);
|
|
88
|
+
}
|
|
89
|
+
MessageType::Callback(data) => {
|
|
90
|
+
println!("Callback: {}", data);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Sending Messages
|
|
96
|
+
|
|
97
|
+
```rust
|
|
98
|
+
// Simple text
|
|
99
|
+
channel.send(OutgoingMessage {
|
|
100
|
+
chat_id: message.chat_id,
|
|
101
|
+
text: "Hello!".to_string(),
|
|
102
|
+
..Default::default()
|
|
103
|
+
}).await?;
|
|
104
|
+
|
|
105
|
+
// With inline keyboard
|
|
106
|
+
let keyboard = InlineKeyboard::new()
|
|
107
|
+
.row(vec![
|
|
108
|
+
("Option A", "callback_a"),
|
|
109
|
+
("Option B", "callback_b"),
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
channel.send(OutgoingMessage {
|
|
113
|
+
chat_id: message.chat_id,
|
|
114
|
+
text: "Choose:".to_string(),
|
|
115
|
+
reply_markup: Some(keyboard.into()),
|
|
116
|
+
..Default::default()
|
|
117
|
+
}).await?;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Webhook Mode
|
|
121
|
+
|
|
122
|
+
For production, use webhooks instead of polling:
|
|
123
|
+
|
|
124
|
+
```rust
|
|
125
|
+
let config = TelegramConfig {
|
|
126
|
+
bot_token: std::env::var("TELEGRAM_BOT_TOKEN")?,
|
|
127
|
+
webhook_url: Some("https://your-domain.com/telegram/webhook".to_string()),
|
|
128
|
+
..Default::default()
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Requirements:**
|
|
133
|
+
- Valid HTTPS domain
|
|
134
|
+
- SSL certificate
|
|
135
|
+
- Public accessibility
|
|
136
|
+
|
|
137
|
+
## Security
|
|
138
|
+
|
|
139
|
+
### User Allowlist
|
|
140
|
+
|
|
141
|
+
Restrict who can use the bot:
|
|
142
|
+
|
|
143
|
+
```rust
|
|
144
|
+
let config = TelegramConfig {
|
|
145
|
+
allowed_users: vec![123456789, 987654321],
|
|
146
|
+
..Default::default()
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Rate Limiting
|
|
151
|
+
|
|
152
|
+
Built-in rate limiting prevents abuse:
|
|
153
|
+
|
|
154
|
+
```rust
|
|
155
|
+
use enact_channels::security::RateLimiter;
|
|
156
|
+
|
|
157
|
+
let limiter = RateLimiter::new(
|
|
158
|
+
10, // max messages
|
|
159
|
+
Duration::from_secs(60), // per minute
|
|
160
|
+
);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Integration with Agents (🚧 Future)
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
# agent.yaml
|
|
167
|
+
name: telegram-assistant
|
|
168
|
+
triggers:
|
|
169
|
+
- "channel:telegram"
|
|
170
|
+
|
|
171
|
+
nodes:
|
|
172
|
+
respond:
|
|
173
|
+
type: llm
|
|
174
|
+
system_prompt: "You are a helpful Telegram bot."
|
|
175
|
+
edges:
|
|
176
|
+
_default: END
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Deploy to Telegram
|
|
181
|
+
enact deploy --channel telegram --agent agent.yaml
|
|
182
|
+
```
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
+++
|
|
2
|
+
title = "Generic Webhook Channel"
|
|
3
|
+
weight = 9
|
|
4
|
+
+++
|
|
5
|
+
|
|
6
|
+
# Generic Webhook Channel
|
|
7
|
+
|
|
8
|
+
The `WebhookChannel` provides a generic HTTP webhook endpoint for receiving messages from any service that can send HTTP POST requests.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
The webhook channel enables:
|
|
13
|
+
|
|
14
|
+
- **Custom integrations** with any HTTP-capable service
|
|
15
|
+
- **Message queuing** for async processing
|
|
16
|
+
- **Secret verification** for security
|
|
17
|
+
- **Response storage** for reply tracking
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```rust
|
|
22
|
+
use enact_channels::WebhookChannel;
|
|
23
|
+
|
|
24
|
+
// Create webhook channel
|
|
25
|
+
let webhook = WebhookChannel::new("/webhook/incoming");
|
|
26
|
+
|
|
27
|
+
// Or with secret verification
|
|
28
|
+
let webhook = WebhookChannel::new("/webhook/incoming")
|
|
29
|
+
.with_secret("my-secret-token");
|
|
30
|
+
|
|
31
|
+
// Parse incoming payload
|
|
32
|
+
let payload = serde_json::json!({
|
|
33
|
+
"from": "user@example.com",
|
|
34
|
+
"message": "Hello from webhook!"
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
let headers = std::collections::HashMap::new();
|
|
38
|
+
|
|
39
|
+
if let Some(message) = webhook.parse_payload(&payload, &headers)? {
|
|
40
|
+
println!("From: {}", message.sender);
|
|
41
|
+
println!("Content: {}", message.content);
|
|
42
|
+
|
|
43
|
+
// Queue for processing
|
|
44
|
+
webhook.queue_message(message).await;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Integration with Gateway
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
use enact_channels::WebhookChannel;
|
|
52
|
+
use enact_gateway::{GatewayState, serve, GatewayConfig};
|
|
53
|
+
use std::sync::Arc;
|
|
54
|
+
|
|
55
|
+
#[tokio::main]
|
|
56
|
+
async fn main() -> anyhow::Result<()> {
|
|
57
|
+
// Create webhook channel
|
|
58
|
+
let webhook = Arc::new(
|
|
59
|
+
WebhookChannel::new("/webhook/custom")
|
|
60
|
+
.with_secret(std::env::var("WEBHOOK_SECRET")?)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Create gateway state with webhook responder
|
|
64
|
+
let state = GatewayState {
|
|
65
|
+
channels: vec![webhook.clone()],
|
|
66
|
+
..Default::default()
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Start gateway
|
|
70
|
+
let config = GatewayConfig {
|
|
71
|
+
port: 8080,
|
|
72
|
+
..Default::default()
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
serve(config, state).await
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Payload Format
|
|
80
|
+
|
|
81
|
+
### Standard Format
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"from": "user@example.com",
|
|
86
|
+
"message": "Hello, this is the message content",
|
|
87
|
+
"id": "optional-message-id",
|
|
88
|
+
"timestamp": 1704067200
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Minimal Format
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"from": "service-name",
|
|
97
|
+
"message": "Message content"
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### With Extra Fields
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"from": "user@example.com",
|
|
106
|
+
"message": "Hello!",
|
|
107
|
+
"id": "msg-123",
|
|
108
|
+
"timestamp": 1704067200,
|
|
109
|
+
"custom_field": "value",
|
|
110
|
+
"metadata": {
|
|
111
|
+
"priority": "high"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Security
|
|
117
|
+
|
|
118
|
+
### Secret Verification
|
|
119
|
+
|
|
120
|
+
```rust
|
|
121
|
+
use enact_channels::WebhookChannel;
|
|
122
|
+
use std::collections::HashMap;
|
|
123
|
+
|
|
124
|
+
let webhook = WebhookChannel::new("/webhook")
|
|
125
|
+
.with_secret("super-secret-token");
|
|
126
|
+
|
|
127
|
+
// Incoming request must include Authorization header
|
|
128
|
+
let mut headers = HashMap::new();
|
|
129
|
+
headers.insert(
|
|
130
|
+
"Authorization".to_string(),
|
|
131
|
+
"Bearer super-secret-token".to_string()
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// This will succeed
|
|
135
|
+
let result = webhook.parse_payload(&payload, &headers
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Without header or wrong token
|
|
139
|
+
let empty_headers = HashMap::new();
|
|
140
|
+
let result = webhook.parse_payload(&payload, &empty_headers
|
|
141
|
+
);
|
|
142
|
+
// Returns error: "Invalid or missing authorization header"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Custom Header Names
|
|
146
|
+
|
|
147
|
+
```rust
|
|
148
|
+
// Verify using custom header
|
|
149
|
+
headers.insert(
|
|
150
|
+
"X-Webhook-Secret".to_string(),
|
|
151
|
+
"super-secret-token".to_string()
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// The tool checks both:
|
|
155
|
+
// - Authorization header
|
|
156
|
+
// - X-Webhook-Secret header
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Message Processing
|
|
160
|
+
|
|
161
|
+
### Async Queue
|
|
162
|
+
|
|
163
|
+
```rust
|
|
164
|
+
use enact_channels::WebhookChannel;
|
|
165
|
+
|
|
166
|
+
let webhook = WebhookChannel::new("/webhook");
|
|
167
|
+
|
|
168
|
+
// In your HTTP handler
|
|
169
|
+
async fn handle_webhook(
|
|
170
|
+
webhook: Arc<WebhookChannel>,
|
|
171
|
+
payload: serde_json::Value,
|
|
172
|
+
) -> anyhow::Result<()> {
|
|
173
|
+
let headers = extract_headers(&payload);
|
|
174
|
+
|
|
175
|
+
if let Some(message) = webhook.parse_payload(&payload, &headers)? {
|
|
176
|
+
// Queue for async processing
|
|
177
|
+
webhook.queue_message(message).await;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Ok(())
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Process queue in background
|
|
184
|
+
async fn process_queue(webhook: Arc<WebhookChannel>) {
|
|
185
|
+
loop {
|
|
186
|
+
let messages = webhook.drain_messages().await;
|
|
187
|
+
|
|
188
|
+
for message in messages {
|
|
189
|
+
// Process each message
|
|
190
|
+
process_message(message).await;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Response Storage
|
|
199
|
+
|
|
200
|
+
```rust
|
|
201
|
+
// Store a response for a message
|
|
202
|
+
webhook.store_response("msg-123", "Processing complete").await;
|
|
203
|
+
|
|
204
|
+
// Retrieve response later
|
|
205
|
+
if let Some(response) = webhook.get_response("msg-123").await {
|
|
206
|
+
println!("Response: {}", response);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Complete Example
|
|
211
|
+
|
|
212
|
+
```rust
|
|
213
|
+
use enact_channels::{WebhookChannel, ChannelMessage};
|
|
214
|
+
use axum::{
|
|
215
|
+
extract::State,
|
|
216
|
+
http::StatusCode,
|
|
217
|
+
routing::post,
|
|
218
|
+
Json, Router,
|
|
219
|
+
};
|
|
220
|
+
use std::sync::Arc;
|
|
221
|
+
use std::collections::HashMap;
|
|
222
|
+
|
|
223
|
+
struct AppState {
|
|
224
|
+
webhook: Arc<WebhookChannel>,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#[tokio::main]
|
|
228
|
+
async fn main() {
|
|
229
|
+
let webhook = Arc::new(
|
|
230
|
+
WebhookChannel::new("/webhook/custom")
|
|
231
|
+
.with_secret("my-secret")
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
let app = Router::new()
|
|
235
|
+
.route("/webhook/custom", post(webhook_handler))
|
|
236
|
+
.with_state(AppState { webhook: webhook.clone() });
|
|
237
|
+
|
|
238
|
+
// Start message processor in background
|
|
239
|
+
tokio::spawn(process_messages(webhook));
|
|
240
|
+
|
|
241
|
+
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080")
|
|
242
|
+
.await
|
|
243
|
+
.unwrap();
|
|
244
|
+
|
|
245
|
+
axum::serve(listener, app).await.unwrap();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async fn webhook_handler(
|
|
249
|
+
State(state): State<AppState>,
|
|
250
|
+
headers: axum::http::HeaderMap,
|
|
251
|
+
Json(payload): Json<serde_json::Value>,
|
|
252
|
+
) -> StatusCode {
|
|
253
|
+
// Convert headers to HashMap
|
|
254
|
+
let header_map: HashMap<String, String> = headers
|
|
255
|
+
.iter()
|
|
256
|
+
.filter_map(|(k, v)| {
|
|
257
|
+
v.to_str().ok().map(|v| (k.to_string(), v.to_string()))
|
|
258
|
+
})
|
|
259
|
+
.collect();
|
|
260
|
+
|
|
261
|
+
// Parse payload
|
|
262
|
+
match state.webhook.parse_payload(&payload, &header_map) {
|
|
263
|
+
Ok(Some(message)) => {
|
|
264
|
+
// Queue for processing
|
|
265
|
+
state.webhook.queue_message(message).await;
|
|
266
|
+
StatusCode::OK
|
|
267
|
+
}
|
|
268
|
+
Ok(None) => {
|
|
269
|
+
// Empty message, ignore
|
|
270
|
+
StatusCode::OK
|
|
271
|
+
}
|
|
272
|
+
Err(e) => {
|
|
273
|
+
eprintln!("Webhook error: {}", e);
|
|
274
|
+
StatusCode::BAD_REQUEST
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async fn process_messages(webhook: Arc<WebhookChannel>) {
|
|
280
|
+
loop {
|
|
281
|
+
let messages = webhook.drain_messages().await;
|
|
282
|
+
|
|
283
|
+
for message in messages {
|
|
284
|
+
// Process the message
|
|
285
|
+
let response = process_message(&message).await;
|
|
286
|
+
|
|
287
|
+
// Store response
|
|
288
|
+
webhook.store_response(&message.id, &response).await;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async fn process_message(message: &ChannelMessage) -> String {
|
|
296
|
+
// Your processing logic here
|
|
297
|
+
format!("Processed message from {}: {}",
|
|
298
|
+
message.sender,
|
|
299
|
+
message.content
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Testing
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Test webhook endpoint
|
|
308
|
+
curl -X POST http://localhost:8080/webhook/custom \
|
|
309
|
+
-H "Content-Type: application/json" \
|
|
310
|
+
-H "Authorization: Bearer my-secret" \
|
|
311
|
+
-d '{
|
|
312
|
+
"from": "test-user",
|
|
313
|
+
"message": "Hello from webhook!"
|
|
314
|
+
}'
|
|
315
|
+
|
|
316
|
+
# Should return 200 OK
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Use Cases
|
|
320
|
+
|
|
321
|
+
### Custom Service Integration
|
|
322
|
+
|
|
323
|
+
```rust
|
|
324
|
+
// Receive notifications from your backend
|
|
325
|
+
let webhook = WebhookChannel::new("/webhook/notifications");
|
|
326
|
+
|
|
327
|
+
// Backend sends:
|
|
328
|
+
// {
|
|
329
|
+
// "from": "billing-service",
|
|
330
|
+
// "message": "Payment received: $99.00",
|
|
331
|
+
// "metadata": { "order_id": "12345" }
|
|
332
|
+
// }
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Slack/Discord Webhooks
|
|
336
|
+
|
|
337
|
+
```rust
|
|
338
|
+
// Receive Slack outgoing webhooks
|
|
339
|
+
let webhook = WebhookChannel::new("/webhook/slack")
|
|
340
|
+
.with_secret(slack_signing_secret);
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### GitHub Webhooks
|
|
344
|
+
|
|
345
|
+
```rust
|
|
346
|
+
// Receive GitHub events
|
|
347
|
+
let webhook = WebhookChannel::new("/webhook/github")
|
|
348
|
+
.with_secret(github_webhook_secret);
|
|
349
|
+
|
|
350
|
+
// Process push events, PR updates, etc.
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Custom Chatbots
|
|
354
|
+
|
|
355
|
+
```rust
|
|
356
|
+
// Your own chat service
|
|
357
|
+
let webhook = WebhookChannel::new("/webhook/chat");
|
|
358
|
+
|
|
359
|
+
// Mobile app sends messages via webhook
|
|
360
|
+
// Agent processes and responds
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Comparison with Platform Channels
|
|
364
|
+
|
|
365
|
+
| Feature | Webhook | WhatsApp | Telegram | Teams |
|
|
366
|
+
|---------|---------|----------|----------|-------|
|
|
367
|
+
| **Setup** | Manual | Meta API | Bot API | Azure |
|
|
368
|
+
| **Flexibility** | ✅ Any service | ❌ WhatsApp only | ❌ Telegram only | ❌ Teams only |
|
|
369
|
+
| **Security** | Custom secret | Signature | Token | Signature |
|
|
370
|
+
| **Complexity** | Low | Medium | Low | Medium |
|
|
371
|
+
| **Use Case** | Custom integrations | Mobile chat | Mobile chat | Enterprise |
|
|
372
|
+
|
|
373
|
+
## Configuration
|
|
374
|
+
|
|
375
|
+
```yaml
|
|
376
|
+
# config.yaml
|
|
377
|
+
channels:
|
|
378
|
+
webhook:
|
|
379
|
+
enabled: true
|
|
380
|
+
endpoint: "/webhook/custom"
|
|
381
|
+
secret: "${WEBHOOK_SECRET}"
|
|
382
|
+
|
|
383
|
+
# Optional: custom response handling
|
|
384
|
+
store_responses: true
|
|
385
|
+
max_queue_size: 1000
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Error Handling
|
|
389
|
+
|
|
390
|
+
```rust
|
|
391
|
+
match webhook.parse_payload(&payload, &headers) {
|
|
392
|
+
Ok(Some(message)) => {
|
|
393
|
+
// Process message
|
|
394
|
+
}
|
|
395
|
+
Ok(None) => {
|
|
396
|
+
// Empty or invalid message
|
|
397
|
+
println!("No message in payload");
|
|
398
|
+
}
|
|
399
|
+
Err(e) if e.to_string().contains("authorization") => {
|
|
400
|
+
// Secret verification failed
|
|
401
|
+
eprintln!("Unauthorized webhook request");
|
|
402
|
+
}
|
|
403
|
+
Err(e) => {
|
|
404
|
+
// Other error
|
|
405
|
+
eprintln!("Webhook error: {}", e);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## Best Practices
|
|
411
|
+
|
|
412
|
+
1. **Always use secrets** in production
|
|
413
|
+
2. **Validate payloads** before processing
|
|
414
|
+
3. **Queue messages** for async processing
|
|
415
|
+
4. **Implement retries** for failed processing
|
|
416
|
+
5. **Log webhook events** for debugging
|
|
417
|
+
6. **Rate limit** endpoints to prevent abuse
|
|
418
|
+
|
|
419
|
+
## Resources
|
|
420
|
+
|
|
421
|
+
- [enact-channels crate](/docs/crates/enact-channels/)
|
|
422
|
+
- [Axum web framework](https://github.com/tokio-rs/axum)
|
|
423
|
+
- [Webhook security best practices](https://webhooks.fyi/)
|