echo-agent 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {echo_agent-0.2.2 → echo_agent-0.2.3}/PKG-INFO +1 -1
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/__init__.py +1 -1
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/loop.py +8 -3
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/context_stage.py +39 -1
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/inference_stage.py +38 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/app.py +6 -3
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/schema.py +3 -1
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/__init__.py +4 -0
- echo_agent-0.2.3/echo_agent/gateway/api/lifecycle.py +41 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/health.py +8 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/server.py +30 -1
- {echo_agent-0.2.2 → echo_agent-0.2.3}/pyproject.toml +1 -1
- {echo_agent-0.2.2 → echo_agent-0.2.3}/.gitignore +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/LICENSE +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/README.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/__main__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/client.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/models.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/protocol.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/server.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/approval_gate.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/assembler.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/boundary.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/compressor.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/engine.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/pruner.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/summarizer.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/types.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/validator.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/consolidation.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/context.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/context_cache.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/base.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/factory.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/remote.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/audit.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/error_messages.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/error_types.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/models.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/registry.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/runtime.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/response_stage.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/types.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/models.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/planner.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/reflection.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/strategies.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/streaming.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/base.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/circuit_breaker.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/clarify.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/code_exec.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/cronjob.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/delegate.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/filesystem.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/image_gen.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/image_gen_fal.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/knowledge.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/memory.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/message.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/notify.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/patch.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/process.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/registry.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/search.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/session_search.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/shell.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/skill_install.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/skills.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/task.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/todo.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/tts.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/vision.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/web.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/workflow.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/events.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/queue.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/rate_limiter.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/base.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/cli.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/cron.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/dingtalk.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/discord.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/email.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/feishu.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/matrix.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/qqbot.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/qqbot_media.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/slack.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/telegram.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/webhook.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/wecom.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/weixin.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/whatsapp.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/colors.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/evolution_cmd.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/i18n/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/i18n/en.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/i18n/zh.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/plugins_cmd.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/prompt.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/service.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/setup.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/status.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/default.yaml +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/loader.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/cli.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/lazy_deps.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/skill_require.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/dataset.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/metrics.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/reporter.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/runner.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/engine.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/evolver.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/gate.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/recorder.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/scheduler.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/store.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/tools.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/types.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/validation.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/channels.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/config.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/knowledge.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/memory.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/skills.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/auth.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/editor.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/hooks.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/media.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/rate_limiter.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/router.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/session_context.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/session_policy.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/static/index.html +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/knowledge/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/knowledge/index.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/client.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/oauth.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/security.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/tool_adapter.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/transport.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/consolidator.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/contradiction.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/forgetting.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/retrieval.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/reviewer.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/store.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/tiers.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/types.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/vectors.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/credential_pool.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/inference.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/provider.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/anthropic_provider.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/bedrock_provider.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/format_utils.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/gemini_provider.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/openai_provider.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/openrouter_provider.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/rate_limiter.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/router.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/stub.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/tokenizer.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/monitor.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/spans.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/telemetry.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/permissions/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/permissions/allowlist.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/permissions/manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/context.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/errors.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/hooks.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/loader.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/manifest.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/sandbox.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/runtime_paths.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/scheduler/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/scheduler/delivery.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/scheduler/service.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/capabilities.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/guards.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/normalizer.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/path_policy.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/risk_classifier.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/smart_approval.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/tokenizer.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/tool_policy.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/session/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/session/manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/session/media_ref.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/reviewer.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/store.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/storage/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/storage/backend.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/storage/sqlite.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/models.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/workflow.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tools/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tools/base.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/utils/__init__.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/utils/async_io.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/utils/text.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/scripts/install.sh +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/excel-author/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/excel-author/scripts/create_xlsx.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/image-gen/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/image-gen/scripts/generate_image.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/meme-gen/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/meme-gen/scripts/make_meme.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/ppt-author/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/ppt-author/scripts/create_pptx.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/code-runner/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/code-runner/scripts/safe_exec.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/github-ops/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/plan/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/scripts/init_skill.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/scripts/package_skill.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/scripts/quick_validate.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/workflow-chain/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/workflow-chain/scripts/workflow_engine.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/devops/docker-manage/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/devops/system-monitor/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/devops/system-monitor/scripts/system_check.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/finance-tracker/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/finance-tracker/scripts/finance_manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/stocks/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/stocks/scripts/market_query.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/health/fitness-nutrition/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/health/fitness-nutrition/scripts/health_query.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/learning/flashcards/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/learning/flashcards/scripts/flashcard_engine.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/tts-voice/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/tts-voice/scripts/text_to_speech.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/voice-note/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/voice-note/scripts/voice_process.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/calendar/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/calendar/scripts/calendar_client.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/daily-briefing/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/daily-briefing/scripts/generate_briefing.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/email-assistant/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/email-assistant/scripts/email_client.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/note-taking/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/note-taking/scripts/notes_manager.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/notion-sync/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/notion-sync/scripts/notion_client.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/ocr-document/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/ocr-document/scripts/extract_document.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/reminder/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/reminder/scripts/reminder_store.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/summarize/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/weather/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/arxiv/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/arxiv/scripts/search_arxiv.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/deep-research/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/deep-research/scripts/research_report.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/rss-watcher/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/rss-watcher/scripts/feed_monitor.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-extract/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-extract/scripts/extract_url.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-search/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-search/scripts/web_search.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/calculator/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/calculator/scripts/calc.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/file-convert/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/file-convert/scripts/convert.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/maps-poi/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/maps-poi/scripts/geo_query.py +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/text-tools/SKILL.md +0 -0
- {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/text-tools/scripts/text_process.py +0 -0
|
@@ -259,6 +259,7 @@ class AgentLoop:
|
|
|
259
259
|
memory_snapshots=self._memory_snapshots,
|
|
260
260
|
snapshot_enabled=self._snapshot_enabled,
|
|
261
261
|
tool_definitions_fn=self.tools.get_definitions,
|
|
262
|
+
bus=bus,
|
|
262
263
|
)
|
|
263
264
|
self._inference_stage = InferenceStage(
|
|
264
265
|
config=config,
|
|
@@ -730,9 +731,13 @@ class AgentLoop:
|
|
|
730
731
|
return template
|
|
731
732
|
|
|
732
733
|
def _should_stream_channel(self, channel: str) -> bool:
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
734
|
+
channels = set(self.config.channels.stream_channels)
|
|
735
|
+
if channel in channels:
|
|
736
|
+
return True
|
|
737
|
+
for pattern in channels:
|
|
738
|
+
if pattern.endswith(":*") and channel.startswith(pattern[:-1]):
|
|
739
|
+
return True
|
|
740
|
+
return False
|
|
736
741
|
|
|
737
742
|
async def process_direct(self, content: str, session_key: str = "cli:direct") -> str:
|
|
738
743
|
"""Process a message directly (for CLI or testing)."""
|
|
@@ -15,7 +15,7 @@ from echo_agent.agent.context import (
|
|
|
15
15
|
build_skills_context,
|
|
16
16
|
)
|
|
17
17
|
from echo_agent.agent.pipeline.types import PipelineContext
|
|
18
|
-
from echo_agent.bus.events import InboundEvent
|
|
18
|
+
from echo_agent.bus.events import InboundEvent, OutboundEvent
|
|
19
19
|
from echo_agent.session.manager import Session
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
@@ -56,6 +56,7 @@ class ContextStage:
|
|
|
56
56
|
memory_snapshots: OrderedDict,
|
|
57
57
|
snapshot_enabled: bool,
|
|
58
58
|
tool_definitions_fn: Any,
|
|
59
|
+
bus: Any = None,
|
|
59
60
|
):
|
|
60
61
|
self._config = config
|
|
61
62
|
self._sessions = sessions
|
|
@@ -71,6 +72,19 @@ class ContextStage:
|
|
|
71
72
|
self._memory_snapshots = memory_snapshots
|
|
72
73
|
self._snapshot_enabled = snapshot_enabled
|
|
73
74
|
self._tool_definitions_fn = tool_definitions_fn
|
|
75
|
+
self._bus = bus
|
|
76
|
+
|
|
77
|
+
async def _emit_progress(self, event: InboundEvent, metadata: dict[str, Any]) -> None:
|
|
78
|
+
if not getattr(self._config.gateway, 'emit_progress_events', True):
|
|
79
|
+
return
|
|
80
|
+
out = OutboundEvent.text_reply(
|
|
81
|
+
channel=event.channel, chat_id=event.chat_id, text="", reply_to_id=event.reply_to_id,
|
|
82
|
+
)
|
|
83
|
+
out.is_final = False
|
|
84
|
+
out.message_kind = "progress"
|
|
85
|
+
out.metadata = {"_progress": True, "_inbound_event_id": event.event_id}
|
|
86
|
+
out.metadata.update(metadata)
|
|
87
|
+
await self._bus.publish_outbound(out)
|
|
74
88
|
|
|
75
89
|
async def build(
|
|
76
90
|
self,
|
|
@@ -155,6 +169,18 @@ class ContextStage:
|
|
|
155
169
|
"Relevant memory:\n"
|
|
156
170
|
+ "\n".join(f"- {r.key}: {r.content}" for r, _ in scored)
|
|
157
171
|
)
|
|
172
|
+
if publish_response and self._bus:
|
|
173
|
+
_debug = getattr(self._config.gateway, 'progress_debug', False)
|
|
174
|
+
_mem_meta: dict[str, Any] = {
|
|
175
|
+
"progress_type": "memory_retrieved",
|
|
176
|
+
"count": len(scored),
|
|
177
|
+
}
|
|
178
|
+
if _debug:
|
|
179
|
+
_mem_meta["entries"] = [
|
|
180
|
+
{"key": r.key, "content_preview": r.content[:100]}
|
|
181
|
+
for r, _ in scored[:5]
|
|
182
|
+
]
|
|
183
|
+
await self._emit_progress(event, _mem_meta)
|
|
158
184
|
|
|
159
185
|
if self._knowledge:
|
|
160
186
|
knowledge_results = self._knowledge.search(
|
|
@@ -165,6 +191,18 @@ class ContextStage:
|
|
|
165
191
|
knowledge_context = self._knowledge.format_results(knowledge_results)
|
|
166
192
|
if knowledge_context:
|
|
167
193
|
retrieval_parts.append(knowledge_context)
|
|
194
|
+
if publish_response and self._bus:
|
|
195
|
+
_debug = getattr(self._config.gateway, 'progress_debug', False)
|
|
196
|
+
_know_meta: dict[str, Any] = {
|
|
197
|
+
"progress_type": "knowledge_cited",
|
|
198
|
+
"count": len(knowledge_results) if knowledge_results else 0,
|
|
199
|
+
}
|
|
200
|
+
if _debug:
|
|
201
|
+
_know_meta["citations"] = [
|
|
202
|
+
{"path": getattr(r, 'path', ''), "chunk_preview": getattr(r, 'text', '')[:200], "score": getattr(r, 'score', 0.0)}
|
|
203
|
+
for r in (knowledge_results[:5] if knowledge_results else [])
|
|
204
|
+
]
|
|
205
|
+
await self._emit_progress(event, _know_meta)
|
|
168
206
|
|
|
169
207
|
task_type = self._infer_task_type(event.text)
|
|
170
208
|
|
|
@@ -92,6 +92,20 @@ class InferenceStage:
|
|
|
92
92
|
out.metadata.update({"_progress": True, "_tool_hint": tool_hint, "_inbound_event_id": event.event_id})
|
|
93
93
|
await self._bus.publish_outbound(out)
|
|
94
94
|
|
|
95
|
+
async def _emit_tool_event(metadata: dict[str, Any]) -> None:
|
|
96
|
+
if not ctx.publish_response:
|
|
97
|
+
return
|
|
98
|
+
if not getattr(self._config.gateway, 'emit_progress_events', True):
|
|
99
|
+
return
|
|
100
|
+
out = OutboundEvent.text_reply(
|
|
101
|
+
channel=event.channel, chat_id=event.chat_id, text="", reply_to_id=event.reply_to_id,
|
|
102
|
+
)
|
|
103
|
+
out.is_final = False
|
|
104
|
+
out.message_kind = "progress"
|
|
105
|
+
out.metadata = {"_progress": True, "_inbound_event_id": event.event_id}
|
|
106
|
+
out.metadata.update(metadata)
|
|
107
|
+
await self._bus.publish_outbound(out)
|
|
108
|
+
|
|
95
109
|
# Standard inference loop
|
|
96
110
|
response_text = ""
|
|
97
111
|
should_review_skills = False
|
|
@@ -272,6 +286,19 @@ class InferenceStage:
|
|
|
272
286
|
if _hook_cancelled:
|
|
273
287
|
continue
|
|
274
288
|
|
|
289
|
+
import time as _time
|
|
290
|
+
_tool_start_ts = _time.monotonic()
|
|
291
|
+
|
|
292
|
+
_debug_progress = getattr(self._config.gateway, 'progress_debug', False)
|
|
293
|
+
_tool_start_meta: dict[str, Any] = {
|
|
294
|
+
"progress_type": "tool_call",
|
|
295
|
+
"tool": tool_call.name,
|
|
296
|
+
"status": "started",
|
|
297
|
+
}
|
|
298
|
+
if _debug_progress:
|
|
299
|
+
_tool_start_meta["args"] = str(tool_call.arguments)[:500]
|
|
300
|
+
await _emit_tool_event(_tool_start_meta)
|
|
301
|
+
|
|
275
302
|
result = await self._tools.execute(tool_call.name, tool_call.arguments, tool_exec_ctx)
|
|
276
303
|
|
|
277
304
|
# post_tool_call hook
|
|
@@ -280,6 +307,17 @@ class InferenceStage:
|
|
|
280
307
|
"post_tool_call", result, tool_call.name, tool_call.arguments, tool_exec_ctx,
|
|
281
308
|
)
|
|
282
309
|
|
|
310
|
+
_tool_duration_ms = int((_time.monotonic() - _tool_start_ts) * 1000)
|
|
311
|
+
_tool_result_meta: dict[str, Any] = {
|
|
312
|
+
"progress_type": "tool_result",
|
|
313
|
+
"tool": tool_call.name,
|
|
314
|
+
"duration_ms": _tool_duration_ms,
|
|
315
|
+
"status": "done" if result.success else "error",
|
|
316
|
+
}
|
|
317
|
+
if _debug_progress:
|
|
318
|
+
_tool_result_meta["result_preview"] = result.text[:500]
|
|
319
|
+
await _emit_tool_event(_tool_result_meta)
|
|
320
|
+
|
|
283
321
|
result_text = result.text
|
|
284
322
|
if len(result_text) > self._MAX_TOOL_RESULT_CHARS:
|
|
285
323
|
result_text = result_text[:self._MAX_TOOL_RESULT_CHARS] + "\n...(truncated)"
|
|
@@ -230,10 +230,11 @@ class AppRuntime:
|
|
|
230
230
|
the storage close — from running.
|
|
231
231
|
"""
|
|
232
232
|
|
|
233
|
-
def __init__(self, ctx: BootstrapResult):
|
|
233
|
+
def __init__(self, ctx: BootstrapResult, shutdown_event: asyncio.Event | None = None):
|
|
234
234
|
self._ctx = ctx
|
|
235
235
|
self._gateway: Any = None
|
|
236
236
|
self._started = False
|
|
237
|
+
self._shutdown_event = shutdown_event
|
|
237
238
|
|
|
238
239
|
@property
|
|
239
240
|
def gateway(self) -> Any:
|
|
@@ -271,6 +272,8 @@ class AppRuntime:
|
|
|
271
272
|
agent_loop=ctx.agent,
|
|
272
273
|
a2a_config=ctx.config.a2a,
|
|
273
274
|
)
|
|
275
|
+
if self._shutdown_event:
|
|
276
|
+
self._gateway.set_shutdown_event(self._shutdown_event)
|
|
274
277
|
await self._gateway.start()
|
|
275
278
|
logger.info("Gateway started on {}:{}", ctx.config.gateway.host, ctx.config.gateway.port)
|
|
276
279
|
return True
|
|
@@ -313,7 +316,7 @@ async def run(config_path: str | None = None, workspace: str | None = None) -> N
|
|
|
313
316
|
logger.info("Echo Agent starting — workspace: {}", ctx.workspace)
|
|
314
317
|
|
|
315
318
|
install_signal_handler(shutdown)
|
|
316
|
-
runtime = AppRuntime(ctx)
|
|
319
|
+
runtime = AppRuntime(ctx, shutdown_event=shutdown)
|
|
317
320
|
try:
|
|
318
321
|
if not await runtime.start():
|
|
319
322
|
return
|
|
@@ -347,7 +350,7 @@ async def run_gateway(
|
|
|
347
350
|
ctx.config.gateway.port = port
|
|
348
351
|
|
|
349
352
|
install_signal_handler(shutdown)
|
|
350
|
-
runtime = AppRuntime(ctx)
|
|
353
|
+
runtime = AppRuntime(ctx, shutdown_event=shutdown)
|
|
351
354
|
try:
|
|
352
355
|
if not await runtime.start():
|
|
353
356
|
return
|
|
@@ -159,7 +159,7 @@ class ChannelsConfig(_Base):
|
|
|
159
159
|
matrix: MatrixChannelConfig = Field(default_factory=MatrixChannelConfig)
|
|
160
160
|
send_progress: bool = True
|
|
161
161
|
send_tool_hints: bool = True
|
|
162
|
-
stream_channels: list[str] = Field(default_factory=lambda: ["cli", "telegram", "discord", "slack"])
|
|
162
|
+
stream_channels: list[str] = Field(default_factory=lambda: ["cli", "telegram", "discord", "slack", "gateway:*"])
|
|
163
163
|
stream_flush_chars: int = 180
|
|
164
164
|
stream_flush_interval_ms: int = 1500
|
|
165
165
|
stream_paragraph_mode: bool = True
|
|
@@ -528,6 +528,8 @@ class GatewayConfig(_Base):
|
|
|
528
528
|
media_cache_max_mb: int = 500
|
|
529
529
|
max_agent_cache_size: int = 50
|
|
530
530
|
enable_progressive_edit: bool = True
|
|
531
|
+
emit_progress_events: bool = True
|
|
532
|
+
progress_debug: bool = False
|
|
531
533
|
hooks_dir: str = ""
|
|
532
534
|
|
|
533
535
|
|
|
@@ -20,12 +20,14 @@ def register_management_routes(app: web.Application, prefix: str, server: Gatewa
|
|
|
20
20
|
from echo_agent.gateway.api.channels import ChannelsAPI
|
|
21
21
|
from echo_agent.gateway.api.knowledge import KnowledgeAPI
|
|
22
22
|
from echo_agent.gateway.api.config import ConfigAPI
|
|
23
|
+
from echo_agent.gateway.api.lifecycle import LifecycleAPI
|
|
23
24
|
|
|
24
25
|
memory_api = MemoryAPI(server)
|
|
25
26
|
skills_api = SkillsAPI(server)
|
|
26
27
|
channels_api = ChannelsAPI(server)
|
|
27
28
|
knowledge_api = KnowledgeAPI(server)
|
|
28
29
|
config_api = ConfigAPI(server)
|
|
30
|
+
lifecycle_api = LifecycleAPI(server)
|
|
29
31
|
|
|
30
32
|
app.router.add_get(f"{prefix}/memory", memory_api.list_entries)
|
|
31
33
|
app.router.add_get(f"{prefix}/memory/stats", memory_api.stats)
|
|
@@ -48,3 +50,5 @@ def register_management_routes(app: web.Application, prefix: str, server: Gatewa
|
|
|
48
50
|
|
|
49
51
|
app.router.add_get(f"{prefix}/config", config_api.get_config)
|
|
50
52
|
app.router.add_get(f"{prefix}/config/models", config_api.get_models)
|
|
53
|
+
|
|
54
|
+
app.router.add_post(f"{prefix}/shutdown", lifecycle_api.shutdown)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from aiohttp import web
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from echo_agent.gateway.server import GatewayServer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LifecycleAPI:
|
|
12
|
+
def __init__(self, server: GatewayServer):
|
|
13
|
+
self._server = server
|
|
14
|
+
|
|
15
|
+
def _guard(self, request: web.Request, action: str) -> web.Response | None:
|
|
16
|
+
return self._server._require_api_token(request, action=action)
|
|
17
|
+
|
|
18
|
+
async def shutdown(self, request: web.Request) -> web.Response:
|
|
19
|
+
guard = self._guard(request, "shutdown")
|
|
20
|
+
if guard:
|
|
21
|
+
return guard
|
|
22
|
+
|
|
23
|
+
if not self._server._shutdown_event:
|
|
24
|
+
return web.json_response({
|
|
25
|
+
"error": "shutdown not available",
|
|
26
|
+
"message": "Gateway was not started with lifecycle management support",
|
|
27
|
+
}, status=503)
|
|
28
|
+
|
|
29
|
+
self._server.request_shutdown()
|
|
30
|
+
|
|
31
|
+
return web.json_response({
|
|
32
|
+
"status": "shutting_down",
|
|
33
|
+
"drain_timeout_seconds": 10,
|
|
34
|
+
"message": "Agent will shut down after draining in-flight requests (max 10s)",
|
|
35
|
+
}, status=202)
|
|
36
|
+
|
|
37
|
+
async def health(self, request: web.Request) -> web.Response:
|
|
38
|
+
"""Extended health endpoint for lifecycle management."""
|
|
39
|
+
data = await self._server.health.check()
|
|
40
|
+
status_code = 200 if data["status"] != "unhealthy" else 503
|
|
41
|
+
return web.json_response(data, status=status_code)
|
|
@@ -39,10 +39,18 @@ class GatewayHealthProvider:
|
|
|
39
39
|
if is_running and not channel_status:
|
|
40
40
|
status = "degraded"
|
|
41
41
|
|
|
42
|
+
provider_status = "ok"
|
|
43
|
+
if self._gw._agent_loop:
|
|
44
|
+
provider = getattr(self._gw._agent_loop, 'provider', None)
|
|
45
|
+
if provider and getattr(provider, 'is_stub', False) is True:
|
|
46
|
+
status = "degraded"
|
|
47
|
+
provider_status = "stub"
|
|
48
|
+
|
|
42
49
|
return {
|
|
43
50
|
"status": status,
|
|
44
51
|
"server_running": is_running,
|
|
45
52
|
"active_channels": channel_status,
|
|
53
|
+
"provider": provider_status,
|
|
46
54
|
"rate_limiter": rate_stats,
|
|
47
55
|
"media_cache_mb": round(media_size, 1),
|
|
48
56
|
"active_sessions": session_count,
|
|
@@ -70,6 +70,8 @@ class GatewayServer:
|
|
|
70
70
|
self._pending_http: dict[str, asyncio.Future[dict[str, Any]]] = {}
|
|
71
71
|
self._MAX_PENDING_HTTP = 500
|
|
72
72
|
self._running = False
|
|
73
|
+
self._actual_port: int | None = None
|
|
74
|
+
self._shutdown_event: asyncio.Event | None = None
|
|
73
75
|
|
|
74
76
|
data_dir = workspace / "data"
|
|
75
77
|
self.auth = GatewayAuth(config.auth, data_dir)
|
|
@@ -98,6 +100,20 @@ class GatewayServer:
|
|
|
98
100
|
def is_running(self) -> bool:
|
|
99
101
|
return self._running
|
|
100
102
|
|
|
103
|
+
@property
|
|
104
|
+
def actual_port(self) -> int:
|
|
105
|
+
"""Return the actual bound port (useful when configured port is 0)."""
|
|
106
|
+
if self._actual_port is not None:
|
|
107
|
+
return self._actual_port
|
|
108
|
+
return self._config.port
|
|
109
|
+
|
|
110
|
+
def set_shutdown_event(self, event: asyncio.Event) -> None:
|
|
111
|
+
self._shutdown_event = event
|
|
112
|
+
|
|
113
|
+
def request_shutdown(self) -> None:
|
|
114
|
+
if self._shutdown_event:
|
|
115
|
+
self._shutdown_event.set()
|
|
116
|
+
|
|
101
117
|
# ── Lifecycle ────────────────────────────────────────────────────────────
|
|
102
118
|
|
|
103
119
|
async def start(self) -> None:
|
|
@@ -113,12 +129,25 @@ class GatewayServer:
|
|
|
113
129
|
self._config.port,
|
|
114
130
|
)
|
|
115
131
|
await self._site.start()
|
|
132
|
+
|
|
133
|
+
actual_port = self._config.port
|
|
134
|
+
if self._runner.addresses:
|
|
135
|
+
actual_port = self._runner.addresses[0][1]
|
|
136
|
+
self._actual_port = actual_port
|
|
137
|
+
|
|
116
138
|
self._running = True
|
|
117
139
|
|
|
140
|
+
import sys
|
|
141
|
+
print(
|
|
142
|
+
f"ECHO_AGENT_READY port={actual_port} ws={self._config.ws_path} health={self._config.api_prefix}/health",
|
|
143
|
+
flush=True,
|
|
144
|
+
file=sys.stdout,
|
|
145
|
+
)
|
|
146
|
+
|
|
118
147
|
await self.hooks.emit("gateway_start")
|
|
119
148
|
logger.info(
|
|
120
149
|
"Gateway listening on {}:{}",
|
|
121
|
-
self._config.host,
|
|
150
|
+
self._config.host, actual_port,
|
|
122
151
|
)
|
|
123
152
|
|
|
124
153
|
def _check_bind_safety(self) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|