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.
Files changed (302) hide show
  1. {echo_agent-0.2.2 → echo_agent-0.2.3}/PKG-INFO +1 -1
  2. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/__init__.py +1 -1
  3. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/loop.py +8 -3
  4. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/context_stage.py +39 -1
  5. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/inference_stage.py +38 -0
  6. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/app.py +6 -3
  7. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/schema.py +3 -1
  8. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/__init__.py +4 -0
  9. echo_agent-0.2.3/echo_agent/gateway/api/lifecycle.py +41 -0
  10. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/health.py +8 -0
  11. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/server.py +30 -1
  12. {echo_agent-0.2.2 → echo_agent-0.2.3}/pyproject.toml +1 -1
  13. {echo_agent-0.2.2 → echo_agent-0.2.3}/.gitignore +0 -0
  14. {echo_agent-0.2.2 → echo_agent-0.2.3}/LICENSE +0 -0
  15. {echo_agent-0.2.2 → echo_agent-0.2.3}/README.md +0 -0
  16. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/__main__.py +0 -0
  17. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/__init__.py +0 -0
  18. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/client.py +0 -0
  19. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/models.py +0 -0
  20. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/protocol.py +0 -0
  21. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/a2a/server.py +0 -0
  22. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/__init__.py +0 -0
  23. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/approval_gate.py +0 -0
  24. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/__init__.py +0 -0
  25. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/assembler.py +0 -0
  26. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/boundary.py +0 -0
  27. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/compressor.py +0 -0
  28. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/engine.py +0 -0
  29. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/pruner.py +0 -0
  30. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/summarizer.py +0 -0
  31. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/types.py +0 -0
  32. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/compression/validator.py +0 -0
  33. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/consolidation.py +0 -0
  34. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/context.py +0 -0
  35. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/context_cache.py +0 -0
  36. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/__init__.py +0 -0
  37. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/base.py +0 -0
  38. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/factory.py +0 -0
  39. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/executors/remote.py +0 -0
  40. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/__init__.py +0 -0
  41. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/audit.py +0 -0
  42. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/error_messages.py +0 -0
  43. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/error_types.py +0 -0
  44. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/models.py +0 -0
  45. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/registry.py +0 -0
  46. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/multi_agent/runtime.py +0 -0
  47. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/__init__.py +0 -0
  48. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/response_stage.py +0 -0
  49. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/pipeline/types.py +0 -0
  50. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/__init__.py +0 -0
  51. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/models.py +0 -0
  52. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/planner.py +0 -0
  53. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/reflection.py +0 -0
  54. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/planning/strategies.py +0 -0
  55. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/streaming.py +0 -0
  56. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/__init__.py +0 -0
  57. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/base.py +0 -0
  58. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/circuit_breaker.py +0 -0
  59. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/clarify.py +0 -0
  60. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/code_exec.py +0 -0
  61. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/cronjob.py +0 -0
  62. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/delegate.py +0 -0
  63. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/filesystem.py +0 -0
  64. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/image_gen.py +0 -0
  65. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/image_gen_fal.py +0 -0
  66. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/knowledge.py +0 -0
  67. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/memory.py +0 -0
  68. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/message.py +0 -0
  69. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/notify.py +0 -0
  70. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/patch.py +0 -0
  71. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/process.py +0 -0
  72. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/registry.py +0 -0
  73. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/search.py +0 -0
  74. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/session_search.py +0 -0
  75. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/shell.py +0 -0
  76. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/skill_install.py +0 -0
  77. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/skills.py +0 -0
  78. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/task.py +0 -0
  79. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/todo.py +0 -0
  80. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/tts.py +0 -0
  81. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/vision.py +0 -0
  82. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/web.py +0 -0
  83. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/agent/tools/workflow.py +0 -0
  84. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/__init__.py +0 -0
  85. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/events.py +0 -0
  86. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/queue.py +0 -0
  87. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/bus/rate_limiter.py +0 -0
  88. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/__init__.py +0 -0
  89. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/base.py +0 -0
  90. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/cli.py +0 -0
  91. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/cron.py +0 -0
  92. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/dingtalk.py +0 -0
  93. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/discord.py +0 -0
  94. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/email.py +0 -0
  95. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/feishu.py +0 -0
  96. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/manager.py +0 -0
  97. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/matrix.py +0 -0
  98. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/qqbot.py +0 -0
  99. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/qqbot_media.py +0 -0
  100. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/slack.py +0 -0
  101. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/telegram.py +0 -0
  102. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/webhook.py +0 -0
  103. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/wecom.py +0 -0
  104. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/weixin.py +0 -0
  105. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/channels/whatsapp.py +0 -0
  106. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/__init__.py +0 -0
  107. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/colors.py +0 -0
  108. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/evolution_cmd.py +0 -0
  109. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/i18n/__init__.py +0 -0
  110. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/i18n/en.py +0 -0
  111. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/i18n/zh.py +0 -0
  112. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/plugins_cmd.py +0 -0
  113. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/prompt.py +0 -0
  114. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/service.py +0 -0
  115. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/setup.py +0 -0
  116. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/cli/status.py +0 -0
  117. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/__init__.py +0 -0
  118. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/default.yaml +0 -0
  119. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/config/loader.py +0 -0
  120. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/__init__.py +0 -0
  121. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/cli.py +0 -0
  122. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/lazy_deps.py +0 -0
  123. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/dependencies/skill_require.py +0 -0
  124. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/__init__.py +0 -0
  125. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/dataset.py +0 -0
  126. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/metrics.py +0 -0
  127. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/reporter.py +0 -0
  128. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evaluation/runner.py +0 -0
  129. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/__init__.py +0 -0
  130. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/engine.py +0 -0
  131. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/evolver.py +0 -0
  132. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/gate.py +0 -0
  133. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/recorder.py +0 -0
  134. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/scheduler.py +0 -0
  135. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/store.py +0 -0
  136. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/tools.py +0 -0
  137. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/types.py +0 -0
  138. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/evolution/validation.py +0 -0
  139. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/__init__.py +0 -0
  140. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/channels.py +0 -0
  141. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/config.py +0 -0
  142. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/knowledge.py +0 -0
  143. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/memory.py +0 -0
  144. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/api/skills.py +0 -0
  145. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/auth.py +0 -0
  146. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/editor.py +0 -0
  147. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/hooks.py +0 -0
  148. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/media.py +0 -0
  149. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/rate_limiter.py +0 -0
  150. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/router.py +0 -0
  151. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/session_context.py +0 -0
  152. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/session_policy.py +0 -0
  153. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/gateway/static/index.html +0 -0
  154. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/knowledge/__init__.py +0 -0
  155. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/knowledge/index.py +0 -0
  156. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/__init__.py +0 -0
  157. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/client.py +0 -0
  158. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/manager.py +0 -0
  159. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/oauth.py +0 -0
  160. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/security.py +0 -0
  161. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/tool_adapter.py +0 -0
  162. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/mcp/transport.py +0 -0
  163. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/__init__.py +0 -0
  164. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/consolidator.py +0 -0
  165. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/contradiction.py +0 -0
  166. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/forgetting.py +0 -0
  167. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/retrieval.py +0 -0
  168. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/reviewer.py +0 -0
  169. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/store.py +0 -0
  170. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/tiers.py +0 -0
  171. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/types.py +0 -0
  172. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/memory/vectors.py +0 -0
  173. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/__init__.py +0 -0
  174. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/credential_pool.py +0 -0
  175. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/inference.py +0 -0
  176. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/provider.py +0 -0
  177. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/__init__.py +0 -0
  178. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/anthropic_provider.py +0 -0
  179. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/bedrock_provider.py +0 -0
  180. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/format_utils.py +0 -0
  181. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/gemini_provider.py +0 -0
  182. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/openai_provider.py +0 -0
  183. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/providers/openrouter_provider.py +0 -0
  184. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/rate_limiter.py +0 -0
  185. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/router.py +0 -0
  186. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/stub.py +0 -0
  187. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/models/tokenizer.py +0 -0
  188. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/__init__.py +0 -0
  189. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/monitor.py +0 -0
  190. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/spans.py +0 -0
  191. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/observability/telemetry.py +0 -0
  192. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/permissions/__init__.py +0 -0
  193. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/permissions/allowlist.py +0 -0
  194. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/permissions/manager.py +0 -0
  195. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/__init__.py +0 -0
  196. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/context.py +0 -0
  197. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/errors.py +0 -0
  198. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/hooks.py +0 -0
  199. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/loader.py +0 -0
  200. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/manager.py +0 -0
  201. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/manifest.py +0 -0
  202. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/plugins/sandbox.py +0 -0
  203. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/runtime_paths.py +0 -0
  204. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/scheduler/__init__.py +0 -0
  205. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/scheduler/delivery.py +0 -0
  206. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/scheduler/service.py +0 -0
  207. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/__init__.py +0 -0
  208. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/capabilities.py +0 -0
  209. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/guards.py +0 -0
  210. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/normalizer.py +0 -0
  211. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/path_policy.py +0 -0
  212. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/risk_classifier.py +0 -0
  213. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/smart_approval.py +0 -0
  214. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/tokenizer.py +0 -0
  215. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/security/tool_policy.py +0 -0
  216. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/session/__init__.py +0 -0
  217. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/session/manager.py +0 -0
  218. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/session/media_ref.py +0 -0
  219. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/__init__.py +0 -0
  220. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/manager.py +0 -0
  221. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/reviewer.py +0 -0
  222. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/skills/store.py +0 -0
  223. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/storage/__init__.py +0 -0
  224. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/storage/backend.py +0 -0
  225. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/storage/sqlite.py +0 -0
  226. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/__init__.py +0 -0
  227. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/manager.py +0 -0
  228. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/models.py +0 -0
  229. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tasks/workflow.py +0 -0
  230. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tools/__init__.py +0 -0
  231. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/tools/base.py +0 -0
  232. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/utils/__init__.py +0 -0
  233. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/utils/async_io.py +0 -0
  234. {echo_agent-0.2.2 → echo_agent-0.2.3}/echo_agent/utils/text.py +0 -0
  235. {echo_agent-0.2.2 → echo_agent-0.2.3}/scripts/install.sh +0 -0
  236. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/excel-author/SKILL.md +0 -0
  237. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/excel-author/scripts/create_xlsx.py +0 -0
  238. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/image-gen/SKILL.md +0 -0
  239. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/image-gen/scripts/generate_image.py +0 -0
  240. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/meme-gen/SKILL.md +0 -0
  241. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/meme-gen/scripts/make_meme.py +0 -0
  242. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/ppt-author/SKILL.md +0 -0
  243. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/creative/ppt-author/scripts/create_pptx.py +0 -0
  244. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/code-runner/SKILL.md +0 -0
  245. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/code-runner/scripts/safe_exec.py +0 -0
  246. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/github-ops/SKILL.md +0 -0
  247. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/plan/SKILL.md +0 -0
  248. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/SKILL.md +0 -0
  249. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/scripts/init_skill.py +0 -0
  250. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/scripts/package_skill.py +0 -0
  251. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/skill-creator/scripts/quick_validate.py +0 -0
  252. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/workflow-chain/SKILL.md +0 -0
  253. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/development/workflow-chain/scripts/workflow_engine.py +0 -0
  254. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/devops/docker-manage/SKILL.md +0 -0
  255. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/devops/system-monitor/SKILL.md +0 -0
  256. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/devops/system-monitor/scripts/system_check.py +0 -0
  257. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/finance-tracker/SKILL.md +0 -0
  258. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/finance-tracker/scripts/finance_manager.py +0 -0
  259. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/stocks/SKILL.md +0 -0
  260. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/finance/stocks/scripts/market_query.py +0 -0
  261. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/health/fitness-nutrition/SKILL.md +0 -0
  262. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/health/fitness-nutrition/scripts/health_query.py +0 -0
  263. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/learning/flashcards/SKILL.md +0 -0
  264. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/learning/flashcards/scripts/flashcard_engine.py +0 -0
  265. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/tts-voice/SKILL.md +0 -0
  266. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/tts-voice/scripts/text_to_speech.py +0 -0
  267. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/voice-note/SKILL.md +0 -0
  268. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/media/voice-note/scripts/voice_process.py +0 -0
  269. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/calendar/SKILL.md +0 -0
  270. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/calendar/scripts/calendar_client.py +0 -0
  271. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/daily-briefing/SKILL.md +0 -0
  272. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/daily-briefing/scripts/generate_briefing.py +0 -0
  273. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/email-assistant/SKILL.md +0 -0
  274. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/email-assistant/scripts/email_client.py +0 -0
  275. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/note-taking/SKILL.md +0 -0
  276. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/note-taking/scripts/notes_manager.py +0 -0
  277. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/notion-sync/SKILL.md +0 -0
  278. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/notion-sync/scripts/notion_client.py +0 -0
  279. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/ocr-document/SKILL.md +0 -0
  280. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/ocr-document/scripts/extract_document.py +0 -0
  281. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/reminder/SKILL.md +0 -0
  282. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/reminder/scripts/reminder_store.py +0 -0
  283. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/summarize/SKILL.md +0 -0
  284. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/productivity/weather/SKILL.md +0 -0
  285. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/arxiv/SKILL.md +0 -0
  286. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/arxiv/scripts/search_arxiv.py +0 -0
  287. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/deep-research/SKILL.md +0 -0
  288. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/deep-research/scripts/research_report.py +0 -0
  289. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/rss-watcher/SKILL.md +0 -0
  290. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/rss-watcher/scripts/feed_monitor.py +0 -0
  291. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-extract/SKILL.md +0 -0
  292. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-extract/scripts/extract_url.py +0 -0
  293. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-search/SKILL.md +0 -0
  294. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/research/web-search/scripts/web_search.py +0 -0
  295. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/calculator/SKILL.md +0 -0
  296. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/calculator/scripts/calc.py +0 -0
  297. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/file-convert/SKILL.md +0 -0
  298. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/file-convert/scripts/convert.py +0 -0
  299. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/maps-poi/SKILL.md +0 -0
  300. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/maps-poi/scripts/geo_query.py +0 -0
  301. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/text-tools/SKILL.md +0 -0
  302. {echo_agent-0.2.2 → echo_agent-0.2.3}/skills/utility/text-tools/scripts/text_process.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: echo-agent
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A modular AI agent framework with multi-channel support
5
5
  Author: Echo Agent contributors
6
6
  License: MIT
@@ -2,4 +2,4 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.1.0"
5
+ __version__ = "0.2.3"
@@ -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
- if channel.startswith("gateway:"):
734
- return False
735
- return channel in set(self.config.channels.stream_channels)
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, self._config.port,
150
+ self._config.host, actual_port,
122
151
  )
123
152
 
124
153
  def _check_bind_safety(self) -> None:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "echo-agent"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "A modular AI agent framework with multi-channel support"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes
File without changes