agentirc-cli 8.6.0__tar.gz → 8.7.0__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 (473) hide show
  1. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/CHANGELOG.md +10 -0
  2. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/PKG-INFO +2 -1
  3. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/bot.py +24 -13
  4. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/bot_manager.py +53 -24
  5. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/http_listener.py +35 -1
  6. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/metrics.py +13 -0
  7. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/harness-telemetry.md +1 -1
  8. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/telemetry.md +39 -3
  9. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-24-otel-observability-design.md +1 -1
  10. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/pyproject.toml +2 -1
  11. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/_metrics_helpers.py +9 -0
  12. agentirc_cli-8.7.0/tests/telemetry/conftest.py +33 -0
  13. agentirc_cli-8.7.0/tests/telemetry/test_bot_event_dispatch_span.py +138 -0
  14. agentirc_cli-8.7.0/tests/telemetry/test_bot_run_span.py +91 -0
  15. agentirc_cli-8.7.0/tests/telemetry/test_metrics_bots.py +127 -0
  16. agentirc_cli-8.7.0/tests/telemetry/test_metrics_webhook.py +148 -0
  17. agentirc_cli-8.7.0/tests/telemetry/test_webhook_http_span.py +45 -0
  18. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/uv.lock +107 -1
  19. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/agents/doc-test-alignment.md +0 -0
  20. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/skills/pr-review/SKILL.md +0 -0
  21. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/skills/run-tests/SKILL.md +0 -0
  22. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  23. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.flake8 +0 -0
  24. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/docs-check.yml +0 -0
  25. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/publish.yml +0 -0
  26. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/security-checks.yml +0 -0
  27. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/tests.yml +0 -0
  28. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.gitignore +0 -0
  29. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.markdownlint-cli2.yaml +0 -0
  30. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.pr_agent.toml +0 -0
  31. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.pre-commit-config.yaml +0 -0
  32. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.pylintrc +0 -0
  33. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/CLAUDE.md +0 -0
  34. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/Gemfile +0 -0
  35. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/Gemfile.lock +0 -0
  36. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/LICENSE +0 -0
  37. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/README.md +0 -0
  38. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/SECURITY.md +0 -0
  39. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_config.base.yml +0 -0
  40. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_config.culture.yml +0 -0
  41. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_data/sites.yml +0 -0
  42. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_includes/head_custom.html +0 -0
  43. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_sass/color_schemes/anthropic.scss +0 -0
  44. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_sass/color_schemes/dark-terminal.scss +0 -0
  45. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_sass/custom/custom.scss +0 -0
  46. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/IMG_3183.png +0 -0
  47. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/apple-touch-icon.png +0 -0
  48. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/favicon-16x16.png +0 -0
  49. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/favicon-32x32.png +0 -0
  50. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/favicon.ico +0 -0
  51. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/og-agentirc.png +0 -0
  52. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/og-culture.png +0 -0
  53. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/__init__.py +0 -0
  54. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/__main__.py +0 -0
  55. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/CLAUDE.md +0 -0
  56. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/__init__.py +0 -0
  57. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/__main__.py +0 -0
  58. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/channel.py +0 -0
  59. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/client.py +0 -0
  60. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/config.py +0 -0
  61. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc-architecture.md +0 -0
  62. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc-features.md +0 -0
  63. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc-skill.md +0 -0
  64. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc.md +0 -0
  65. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/events.py +0 -0
  66. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/history_store.py +0 -0
  67. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/ircd.py +0 -0
  68. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/remote_client.py +0 -0
  69. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/room_store.py +0 -0
  70. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/rooms_util.py +0 -0
  71. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/server_link.py +0 -0
  72. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skill.py +0 -0
  73. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/__init__.py +0 -0
  74. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/history.py +0 -0
  75. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/icon.py +0 -0
  76. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/rooms.py +0 -0
  77. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/threads.py +0 -0
  78. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/thread_store.py +0 -0
  79. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/aio.py +0 -0
  80. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/__init__.py +0 -0
  81. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/config.py +0 -0
  82. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/filter_dsl.py +0 -0
  83. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/__init__.py +0 -0
  84. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/welcome/__init__.py +0 -0
  85. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/welcome/bot.yaml +0 -0
  86. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/welcome/handler.py +0 -0
  87. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/template_engine.py +0 -0
  88. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/virtual_client.py +0 -0
  89. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/__init__.py +0 -0
  90. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/_passthrough.py +0 -0
  91. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/afi.py +0 -0
  92. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/agent.py +0 -0
  93. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/bot.py +0 -0
  94. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/channel.py +0 -0
  95. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/devex.py +0 -0
  96. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/introspect.py +0 -0
  97. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/mesh.py +0 -0
  98. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/server.py +0 -0
  99. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/__init__.py +0 -0
  100. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/constants.py +0 -0
  101. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/display.py +0 -0
  102. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/formatting.py +0 -0
  103. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/ipc.py +0 -0
  104. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/mesh.py +0 -0
  105. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/process.py +0 -0
  106. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/skills.py +0 -0
  107. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/__init__.py +0 -0
  108. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/__init__.py +0 -0
  109. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/agent_runner.py +0 -0
  110. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/config.py +0 -0
  111. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/culture.yaml +0 -0
  112. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/daemon.py +0 -0
  113. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/ipc.py +0 -0
  114. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/irc_transport.py +0 -0
  115. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/message_buffer.py +0 -0
  116. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/skill/SKILL.md +0 -0
  117. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/skill/__init__.py +0 -0
  118. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/skill/irc_client.py +0 -0
  119. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/socket_server.py +0 -0
  120. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/supervisor.py +0 -0
  121. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/telemetry.py +0 -0
  122. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/webhook.py +0 -0
  123. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/__init__.py +0 -0
  124. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/__main__.py +0 -0
  125. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/agent_runner.py +0 -0
  126. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/config.py +0 -0
  127. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/culture.yaml +0 -0
  128. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/daemon.py +0 -0
  129. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/ipc.py +0 -0
  130. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/irc_transport.py +0 -0
  131. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/message_buffer.py +0 -0
  132. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/skill/SKILL.md +0 -0
  133. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/skill/__init__.py +0 -0
  134. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/skill/irc_client.py +0 -0
  135. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/socket_server.py +0 -0
  136. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/supervisor.py +0 -0
  137. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/telemetry.py +0 -0
  138. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/webhook.py +0 -0
  139. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/__init__.py +0 -0
  140. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/agent_runner.py +0 -0
  141. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/config.py +0 -0
  142. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/culture.yaml +0 -0
  143. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/daemon.py +0 -0
  144. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/ipc.py +0 -0
  145. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/irc_transport.py +0 -0
  146. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/message_buffer.py +0 -0
  147. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/skill/SKILL.md +0 -0
  148. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/skill/__init__.py +0 -0
  149. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/skill/irc_client.py +0 -0
  150. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/socket_server.py +0 -0
  151. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/supervisor.py +0 -0
  152. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/telemetry.py +0 -0
  153. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/webhook.py +0 -0
  154. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/__init__.py +0 -0
  155. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/agent_runner.py +0 -0
  156. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/config.py +0 -0
  157. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/culture.yaml +0 -0
  158. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/daemon.py +0 -0
  159. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/ipc.py +0 -0
  160. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/irc_transport.py +0 -0
  161. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/message_buffer.py +0 -0
  162. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/skill/SKILL.md +0 -0
  163. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/skill/__init__.py +0 -0
  164. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/skill/irc_client.py +0 -0
  165. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/socket_server.py +0 -0
  166. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/supervisor.py +0 -0
  167. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/telemetry.py +0 -0
  168. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/webhook.py +0 -0
  169. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/config.py +0 -0
  170. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/__init__.py +0 -0
  171. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/app.py +0 -0
  172. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/client.py +0 -0
  173. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/commands.py +0 -0
  174. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/status.py +0 -0
  175. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/__init__.py +0 -0
  176. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/chat.py +0 -0
  177. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/info_panel.py +0 -0
  178. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/sidebar.py +0 -0
  179. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/constants.py +0 -0
  180. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/credentials.py +0 -0
  181. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/formatting.py +0 -0
  182. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/learn_prompt.py +0 -0
  183. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/mesh_config.py +0 -0
  184. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/observer.py +0 -0
  185. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/__init__.py +0 -0
  186. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/collector.py +0 -0
  187. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/model.py +0 -0
  188. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/renderer_text.py +0 -0
  189. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/renderer_web.py +0 -0
  190. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/web/style.css +0 -0
  191. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/persistence.py +0 -0
  192. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/pidfile.py +0 -0
  193. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/__init__.py +0 -0
  194. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/commands.py +0 -0
  195. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/audit.md +0 -0
  196. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/events.md +0 -0
  197. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/federation.md +0 -0
  198. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/history.md +0 -0
  199. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/icons.md +0 -0
  200. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/rooms.md +0 -0
  201. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/tags.md +0 -0
  202. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/threads.md +0 -0
  203. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/tracing.md +0 -0
  204. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/message.py +0 -0
  205. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/protocol-index.md +0 -0
  206. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/replies.py +0 -0
  207. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/skills/culture/SKILL.md +0 -0
  208. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/__init__.py +0 -0
  209. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/audit.py +0 -0
  210. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/context.py +0 -0
  211. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/tracing.py +0 -0
  212. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/README.md +0 -0
  213. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/architecture-overview.md +0 -0
  214. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/audit.md +0 -0
  215. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/bots.md +0 -0
  216. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/events.md +0 -0
  217. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/index.md +0 -0
  218. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/otelcol-template.yaml +0 -0
  219. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/why-agentirc.md +0 -0
  220. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/agent-lifecycle.md +0 -0
  221. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/choose-a-harness.md +0 -0
  222. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/features.md +0 -0
  223. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/index.md +0 -0
  224. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/mental-model.md +0 -0
  225. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/operate.md +0 -0
  226. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/patterns.md +0 -0
  227. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/quickstart.md +0 -0
  228. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/reflective-development.md +0 -0
  229. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/vision-patterns-index.md +0 -0
  230. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/vision.md +0 -0
  231. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/what-is-culture.md +0 -0
  232. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/agent-harness-spec.md +0 -0
  233. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/index.md +0 -0
  234. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/layers.md +0 -0
  235. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/subsites.md +0 -0
  236. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/threads.md +0 -0
  237. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/afi.md +0 -0
  238. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/commands.md +0 -0
  239. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/devex.md +0 -0
  240. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/index.md +0 -0
  241. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/console.md +0 -0
  242. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/acp.md +0 -0
  243. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/claude.md +0 -0
  244. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/codex.md +0 -0
  245. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/copilot.md +0 -0
  246. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/index.md +0 -0
  247. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/index.md +0 -0
  248. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/architecture.md +0 -0
  249. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/config.md +0 -0
  250. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/deployment.md +0 -0
  251. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/index.md +0 -0
  252. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/security.md +0 -0
  253. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  254. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/resources/positioning.md +0 -0
  255. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/federation.md +0 -0
  256. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/harnesses.md +0 -0
  257. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/humans-and-agents.md +0 -0
  258. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/index.md +0 -0
  259. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/persistence.md +0 -0
  260. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/rooms.md +0 -0
  261. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/demos/magic-demo.md +0 -0
  262. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/first-session.md +0 -0
  263. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/index.md +0 -0
  264. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/join-as-human.md +0 -0
  265. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/local-setup.md +0 -0
  266. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/multi-machine.md +0 -0
  267. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/01-pair-programming.md +0 -0
  268. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
  269. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
  270. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
  271. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/05-the-observer.md +0 -0
  272. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
  273. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
  274. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
  275. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/09-research-swarm.md +0 -0
  276. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
  277. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases-index.md +0 -0
  278. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  279. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  280. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  281. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  282. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  283. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  284. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  285. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  286. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  287. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
  288. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
  289. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-15-mesh-events.md +0 -0
  290. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-18-culture-dev-positioning.md +0 -0
  291. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-22-agex-integration.md +0 -0
  292. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-24-otel-foundation.md +0 -0
  293. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-25-otel-federation.md +0 -0
  294. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-26-otel-metrics.md +0 -0
  295. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-27-otel-audit.md +0 -0
  296. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-28-otel-harness.md +0 -0
  297. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  298. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  299. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  300. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  301. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  302. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  303. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  304. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  305. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  306. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  307. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  308. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  309. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  310. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
  311. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
  312. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
  313. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
  314. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-15-mesh-events-design.md +0 -0
  315. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-17-sites-repositioning-design.md +0 -0
  316. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-18-culture-dev-positioning-design.md +0 -0
  317. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-22-agex-integration-design.md +0 -0
  318. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/favicon.ico +0 -0
  319. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/README.md +0 -0
  320. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/config.py +0 -0
  321. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/culture.yaml +0 -0
  322. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/daemon.py +0 -0
  323. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/ipc.py +0 -0
  324. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/irc_transport.py +0 -0
  325. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/message_buffer.py +0 -0
  326. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/skill/SKILL.md +0 -0
  327. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/skill/irc_client.py +0 -0
  328. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/socket_server.py +0 -0
  329. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/telemetry.py +0 -0
  330. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/webhook.py +0 -0
  331. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  332. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  333. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  334. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  335. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/robots.txt +0 -0
  336. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sitemap-agentirc.html +0 -0
  337. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sitemap-main.html +0 -0
  338. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sitemap.html +0 -0
  339. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sonar-project.properties +0 -0
  340. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/__init__.py +0 -0
  341. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/conftest.py +0 -0
  342. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/__init__.py +0 -0
  343. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/conftest.py +0 -0
  344. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_acp.py +0 -0
  345. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_claude.py +0 -0
  346. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_codex.py +0 -0
  347. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_copilot.py +0 -0
  348. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_all_backends_parity.py +0 -0
  349. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_daemon_telemetry.py +0 -0
  350. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_irc_transport_propagation.py +0 -0
  351. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_record_llm_call.py +0 -0
  352. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_telemetry_module.py +0 -0
  353. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/__init__.py +0 -0
  354. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/_fakes.py +0 -0
  355. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_emit.py +0 -0
  356. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_federation.py +0 -0
  357. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_lifecycle.py +0 -0
  358. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_module.py +0 -0
  359. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_parse_error.py +0 -0
  360. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_config.py +0 -0
  361. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_config_load.py +0 -0
  362. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_context.py +0 -0
  363. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_dispatch_span.py +0 -0
  364. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_emit_event_span.py +0 -0
  365. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_federation_propagation.py +0 -0
  366. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_clients.py +0 -0
  367. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_events.py +0 -0
  368. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_init.py +0 -0
  369. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_irc.py +0 -0
  370. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_s2s.py +0 -0
  371. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_trace_inbound.py +0 -0
  372. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_outbound_inject.py +0 -0
  373. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_parse_error.py +0 -0
  374. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_privmsg_span.py +0 -0
  375. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_s2s_dispatch_span.py +0 -0
  376. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_s2s_relay_span.py +0 -0
  377. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_s2s_session_span.py +0 -0
  378. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_server_init.py +0 -0
  379. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_server_link_inject.py +0 -0
  380. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_session_span.py +0 -0
  381. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_tracing.py +0 -0
  382. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_acp_daemon.py +0 -0
  383. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_agent_runner.py +0 -0
  384. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_archive.py +0 -0
  385. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot.py +0 -0
  386. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot_config.py +0 -0
  387. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot_config_fires_event_toplevel.py +0 -0
  388. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot_manager.py +0 -0
  389. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bots_integration.py +0 -0
  390. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_channel.py +0 -0
  391. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_channel_cli.py +0 -0
  392. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_afi.py +0 -0
  393. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_devex.py +0 -0
  394. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_introspect.py +0 -0
  395. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_passthrough.py +0 -0
  396. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_codex_daemon.py +0 -0
  397. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_connection.py +0 -0
  398. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_chat_markdown.py +0 -0
  399. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_client.py +0 -0
  400. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_commands.py +0 -0
  401. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_connection.py +0 -0
  402. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_fixes_224_227.py +0 -0
  403. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_icons.py +0 -0
  404. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_integration.py +0 -0
  405. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_status.py +0 -0
  406. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_copilot_daemon.py +0 -0
  407. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_credentials.py +0 -0
  408. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_culture_config.py +0 -0
  409. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_daemon.py +0 -0
  410. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_daemon_config.py +0 -0
  411. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_daemon_ipc.py +0 -0
  412. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_discovery.py +0 -0
  413. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_display.py +0 -0
  414. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_basic.py +0 -0
  415. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_bot_chain.py +0 -0
  416. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_bot_trigger.py +0 -0
  417. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_cap_fallback.py +0 -0
  418. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_catalog.py +0 -0
  419. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_federation.py +0 -0
  420. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_history.py +0 -0
  421. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_lifecycle.py +0 -0
  422. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_reserved_nick.py +0 -0
  423. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_federation.py +0 -0
  424. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_filter_dsl.py +0 -0
  425. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_history.py +0 -0
  426. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_http_listener.py +0 -0
  427. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_integration_layer5.py +0 -0
  428. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_ipc.py +0 -0
  429. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_irc_transport.py +0 -0
  430. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_irc_transport_tags.py +0 -0
  431. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_learn_prompt.py +0 -0
  432. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_link_reconnect.py +0 -0
  433. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_manifest_config.py +0 -0
  434. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mention_alias.py +0 -0
  435. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mention_target_cleanup.py +0 -0
  436. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mention_warning.py +0 -0
  437. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mentions.py +0 -0
  438. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mesh_config.py +0 -0
  439. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mesh_readiness.py +0 -0
  440. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_message.py +0 -0
  441. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_message_buffer.py +0 -0
  442. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_message_tags.py +0 -0
  443. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_messaging.py +0 -0
  444. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_migrate_cli.py +0 -0
  445. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_modes.py +0 -0
  446. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_cli.py +0 -0
  447. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_collector.py +0 -0
  448. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_model.py +0 -0
  449. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_renderer.py +0 -0
  450. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_web.py +0 -0
  451. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_persistence.py +0 -0
  452. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_persistence_timeout.py +0 -0
  453. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_pidfile.py +0 -0
  454. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_poll_loop.py +0 -0
  455. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_register_cli.py +0 -0
  456. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_room_persistence.py +0 -0
  457. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_rooms.py +0 -0
  458. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_rooms_federation.py +0 -0
  459. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_rooms_integration.py +0 -0
  460. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_server_icon_skill.py +0 -0
  461. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_setup_update_cli.py +0 -0
  462. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_skill_client.py +0 -0
  463. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_skill_docs.py +0 -0
  464. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_skills.py +0 -0
  465. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_socket_server.py +0 -0
  466. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_supervisor.py +0 -0
  467. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_template_engine.py +0 -0
  468. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_thread_buffer.py +0 -0
  469. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_threads.py +0 -0
  470. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_virtual_client.py +0 -0
  471. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_wait_for_port.py +0 -0
  472. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_webhook.py +0 -0
  473. {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_welcome_bot.py +0 -0
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [8.7.0] - 2026-04-26
8
+
9
+ ### Added
10
+
11
+ - OTEL bot instrumentation (Plan 7): bot.event.dispatch and bot.run spans, culture.bot.invocations counter, culture.bot.webhook.duration histogram, aiohttp-server auto-instrumentation on the webhook listener.
12
+
13
+ ### Changed
14
+
15
+ - docs/agentirc/telemetry.md updated with the 8.7.0 section; harness-telemetry.md cross-link updated.
16
+
7
17
  ## [8.6.0] - 2026-04-26
8
18
 
9
19
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 8.6.0
3
+ Version: 8.7.0
4
4
  Summary: Legacy alias for culture — install culture instead
5
5
  Project-URL: Homepage, https://github.com/OriNachum/culture
6
6
  Author: Ori Nachum
@@ -20,6 +20,7 @@ Requires-Dist: claude-agent-sdk>=0.1
20
20
  Requires-Dist: mistune>=3.0
21
21
  Requires-Dist: opentelemetry-api>=1.25
22
22
  Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.25
23
+ Requires-Dist: opentelemetry-instrumentation-aiohttp-server>=0.46b0
23
24
  Requires-Dist: opentelemetry-sdk>=1.25
24
25
  Requires-Dist: opentelemetry-semantic-conventions>=0.46b0
25
26
  Requires-Dist: pyyaml>=6.0
@@ -8,6 +8,8 @@ import time
8
8
  from pathlib import Path
9
9
  from typing import TYPE_CHECKING, Any
10
10
 
11
+ from opentelemetry import trace as _otel_trace
12
+
11
13
  from culture.agentirc.skill import Event, EventType
12
14
  from culture.bots.config import BOTS_DIR, BotConfig
13
15
  from culture.bots.template_engine import render_fallback, render_template
@@ -125,19 +127,28 @@ class Bot:
125
127
 
126
128
  Returns the rendered message text.
127
129
  """
128
- if not self.active or not self.virtual_client:
129
- raise RuntimeError(f"Bot {self.config.name} is not active")
130
-
131
- message = await self._resolve_message(payload)
132
- if not message:
133
- return ""
134
-
135
- if self.config.mention:
136
- message = f"@{self.config.mention} {message}"
137
-
138
- await self._deliver(message, payload)
139
- await self._maybe_fire_event(payload)
140
- return message
130
+ # Span opens before the active-check so, if handle() is invoked for an
131
+ # inactive bot, the failure still surfaces in tracing with ERROR
132
+ # status and bot.name set instead of only raising.
133
+ with _otel_trace.get_tracer("culture.agentirc").start_as_current_span(
134
+ "bot.run",
135
+ attributes={"bot.name": self.config.name},
136
+ ) as span:
137
+ if not self.active or not self.virtual_client:
138
+ span.set_status(_otel_trace.StatusCode.ERROR, "bot not active")
139
+ raise RuntimeError(f"Bot {self.config.name} is not active")
140
+
141
+ message = await self._resolve_message(payload)
142
+ if not message:
143
+ span.set_attribute("bot.run.empty_message", True)
144
+ return ""
145
+
146
+ if self.config.mention:
147
+ message = f"@{self.config.mention} {message}"
148
+
149
+ await self._deliver(message, payload)
150
+ await self._maybe_fire_event(payload)
151
+ return message
141
152
 
142
153
  async def _resolve_message(self, payload: dict) -> str:
143
154
  """Render the message from custom handler or template."""
@@ -2,9 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import logging
6
7
  from typing import TYPE_CHECKING
7
8
 
9
+ from opentelemetry import trace as _otel_trace
10
+
8
11
  from culture.bots.bot import Bot
9
12
  from culture.bots.config import (
10
13
  BOT_CONFIG_FILE,
@@ -88,32 +91,58 @@ class BotManager:
88
91
  async def on_event(self, event) -> None:
89
92
  """Evaluate event-triggered bots against an event and dispatch matches."""
90
93
  # Snapshot: handle() may call emit_event() which re-enters on_event().
91
- bots_snapshot = list(self.bots.values())
92
- for bot in bots_snapshot:
93
- cfg = bot.config
94
- if cfg.trigger_type != "event":
95
- continue
96
- compiled = getattr(cfg, "_compiled_filter", None)
97
- if compiled is None:
98
- continue
99
- ctx = {
100
- "type": event.type.value if hasattr(event.type, "value") else str(event.type),
101
- "channel": event.channel,
102
- "nick": event.nick,
103
- "data": dict(event.data),
104
- }
105
- try:
106
- if not evaluate(compiled, ctx):
107
- continue
108
- except Exception:
109
- logger.exception("Filter evaluation failed for bot %s", cfg.name)
110
- continue
111
- if not await self._try_start_bot(bot):
112
- continue
94
+ ctx = {
95
+ "type": event.type.value if hasattr(event.type, "value") else str(event.type),
96
+ "channel": event.channel,
97
+ "nick": event.nick,
98
+ "data": dict(event.data),
99
+ }
100
+ for bot in list(self.bots.values()):
101
+ if self._matches_event(bot, ctx):
102
+ await self._dispatch_to_bot(bot, ctx)
103
+
104
+ def _matches_event(self, bot: Bot, ctx: dict) -> bool:
105
+ """True iff `bot` is event-triggered and its filter accepts `ctx`."""
106
+ cfg = bot.config
107
+ if cfg.trigger_type != "event":
108
+ return False
109
+ compiled = getattr(cfg, "_compiled_filter", None)
110
+ if compiled is None:
111
+ return False
112
+ try:
113
+ return bool(evaluate(compiled, ctx))
114
+ except Exception:
115
+ logger.exception("Filter evaluation failed for bot %s", cfg.name)
116
+ return False
117
+
118
+ async def _dispatch_to_bot(self, bot: Bot, ctx: dict) -> None:
119
+ """Lazily start the bot and run handle() inside a bot.event.dispatch span."""
120
+ if not await self._try_start_bot(bot):
121
+ return
122
+ cfg = bot.config
123
+ event_type_str = ctx["type"]
124
+ with _otel_trace.get_tracer("culture.agentirc").start_as_current_span(
125
+ "bot.event.dispatch",
126
+ attributes={"bot.name": cfg.name, "event.type": event_type_str},
127
+ ) as span:
128
+ outcome = "success"
113
129
  try:
114
130
  await bot.handle({"event": ctx})
115
- except Exception:
116
- logger.exception("Bot %s handle() failed for event %s", cfg.name, event.type)
131
+ except asyncio.CancelledError:
132
+ raise
133
+ except Exception as exc:
134
+ outcome = "error"
135
+ span.set_status(_otel_trace.StatusCode.ERROR, str(exc))
136
+ logger.exception("Bot %s handle() failed for event %s", cfg.name, ctx["type"])
137
+ finally:
138
+ self.server.metrics.bot_invocations.add(
139
+ 1,
140
+ {
141
+ "bot": cfg.name,
142
+ "event.type": event_type_str,
143
+ "outcome": outcome,
144
+ },
145
+ )
117
146
 
118
147
  def load_system_bots(self) -> None:
119
148
  """Discover and register system bots from the package."""
@@ -4,9 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import logging
7
+ import time
7
8
  from typing import TYPE_CHECKING
8
9
 
9
10
  from aiohttp import web
11
+ from opentelemetry.instrumentation.aiohttp_server import AioHttpServerInstrumentor
10
12
 
11
13
  if TYPE_CHECKING:
12
14
  from culture.bots.bot_manager import BotManager
@@ -25,7 +27,16 @@ class HttpListener:
25
27
  self._runner: web.AppRunner | None = None
26
28
 
27
29
  async def start(self) -> None:
28
- self._app = web.Application()
30
+ # Patch aiohttp.web.Application to auto-inject the OTEL server
31
+ # middleware. Deferred from import time so just importing this module
32
+ # has no side effect. Re-instrument each start() so the captured
33
+ # tracer/meter rebinds to the *current* TracerProvider — important for
34
+ # tests that swap providers between runs, harmless in production.
35
+ instrumentor = AioHttpServerInstrumentor()
36
+ if instrumentor.is_instrumented_by_opentelemetry:
37
+ instrumentor.uninstrument()
38
+ instrumentor.instrument()
39
+ self._app = web.Application(middlewares=[self._record_webhook_duration])
29
40
  self._app.router.add_get("/health", self._handle_health)
30
41
  self._app.router.add_post("/{bot_name}", self._handle_webhook)
31
42
 
@@ -41,6 +52,29 @@ class HttpListener:
41
52
  self._runner = None
42
53
  self._app = None
43
54
 
55
+ @web.middleware
56
+ async def _record_webhook_duration(self, request, handler):
57
+ """Record per-request duration into culture.bot.webhook.duration."""
58
+ bot_name = request.match_info.get("bot_name") or "_unrouted"
59
+ start = time.perf_counter()
60
+ # Default for the "handler raised a non-HTTPException" path: aiohttp
61
+ # converts unhandled exceptions to 500 responses outside this
62
+ # middleware, so the histogram should report 5xx for them.
63
+ status_class = "5xx"
64
+ try:
65
+ response = await handler(request)
66
+ status_class = f"{response.status // 100}xx"
67
+ return response
68
+ except web.HTTPException as exc:
69
+ status_class = f"{exc.status // 100}xx"
70
+ raise
71
+ finally:
72
+ duration = time.perf_counter() - start
73
+ self.bot_manager.server.metrics.bot_webhook_duration.record(
74
+ duration,
75
+ {"bot": bot_name, "status_class": status_class},
76
+ )
77
+
44
78
  async def _handle_health(self, request: web.Request) -> web.Response:
45
79
  return web.json_response({"status": "ok"})
46
80
 
@@ -63,6 +63,9 @@ class MetricsRegistry:
63
63
  # Audit (Plan 4)
64
64
  audit_writes: Counter
65
65
  audit_queue_depth: UpDownCounter
66
+ # Bots (Plan 7)
67
+ bot_invocations: Counter
68
+ bot_webhook_duration: Histogram
66
69
 
67
70
 
68
71
  def reset_for_tests() -> None:
@@ -168,6 +171,16 @@ def _build_registry(meter: Meter) -> MetricsRegistry:
168
171
  "culture.audit.queue_depth",
169
172
  description="Audit queue depth (records waiting to flush to disk)",
170
173
  ),
174
+ # Bots (Plan 7)
175
+ bot_invocations=meter.create_counter(
176
+ "culture.bot.invocations",
177
+ description="Bot dispatch invocations by bot, event.type, outcome=success|error",
178
+ ),
179
+ bot_webhook_duration=meter.create_histogram(
180
+ "culture.bot.webhook.duration",
181
+ unit="s",
182
+ description="Webhook request handler duration by bot and status_class=2xx|3xx|4xx|5xx",
183
+ ),
171
184
  )
172
185
 
173
186
 
@@ -230,7 +230,7 @@ corresponding key is present in `usage` with an `int` value.
230
230
 
231
231
  ## What's not in 8.6.0
232
232
 
233
- - **Bot-side OTEL instrumentation** — Plan 6.
233
+ - **Bot-side OTEL instrumentation** — shipped in 8.7.0 (Plan 7). See [`telemetry.md`](telemetry.html#what-you-get-in-870).
234
234
  - **Tracestate injection** — server-parity-deferred. Both server-side
235
235
  `client.py` / `server_link.py` and the harness currently pass `tracestate=None`
236
236
  when injecting. A future plan will add `current_tracestate()` to
@@ -9,7 +9,7 @@ nav_order: 90
9
9
 
10
10
  Culture ships with first-class OpenTelemetry support: traces for every IRC command and event, W3C trace context carried across federation via a new IRCv3 tag, and a local collector pattern that keeps Culture's surface small.
11
11
 
12
- This page covers the **Foundation + Server Tracing** release (culture 8.2.0), **Federation Trace-Context Relay** (culture 8.3.0), the **Metrics Pillar** (culture 8.4.0), the **Audit JSONL Sink** (culture 8.5.0), and **Harness-side OTEL** (culture 8.6.0). Bot instrumentation ships in a subsequent release.
12
+ This page covers the **Foundation + Server Tracing** release (culture 8.2.0), **Federation Trace-Context Relay** (culture 8.3.0), the **Metrics Pillar** (culture 8.4.0), the **Audit JSONL Sink** (culture 8.5.0), **Harness-side OTEL** (culture 8.6.0), and **Bot Instrumentation** (culture 8.7.0).
13
13
 
14
14
  ## What you get in 8.2.0
15
15
 
@@ -189,11 +189,47 @@ For full configuration details, the per-backend `service.name` table, the
189
189
  end-to-end test recipe, and a list of what's deferred, see the operator guide at
190
190
  [`docs/agentirc/harness-telemetry.html`](harness-telemetry.html).
191
191
 
192
- ## What's not in 8.6.0
192
+ ## What you get in 8.7.0
193
+
194
+ Bot instrumentation lands. Event-triggered dispatch and webhook-triggered dispatch each get their own span tree, both stitched into the same trace as the upstream client/server activity that triggered them.
195
+
196
+ Event-trigger path:
197
+
198
+ ```text
199
+ irc.command.PRIVMSG (or any event-emitting verb)
200
+ └── irc.event.emit
201
+ └── bot.event.dispatch (one per matched bot)
202
+ └── bot.run (Bot.handle body)
203
+ └── irc.privmsg.deliver.channel | .dm
204
+ ```
205
+
206
+ Webhook path:
207
+
208
+ ```text
209
+ HTTP POST /<bot_name> (auto-instrumented inbound span)
210
+ └── bot.run (Bot.handle body)
211
+ └── irc.privmsg.deliver.*
212
+ ```
213
+
214
+ New spans:
215
+
216
+ - `bot.event.dispatch` — one per matched bot inside `BotManager.on_event`. Attributes: `bot.name`, `event.type`. Status `ERROR` if `Bot.handle` raises.
217
+ - `bot.run` — wraps `Bot.handle` for both event and webhook paths. Attributes: `bot.name`, optional `bot.run.empty_message=True` if the rendered message is empty.
218
+
219
+ The webhook HTTP server is auto-instrumented via [`opentelemetry-instrumentation-aiohttp-server`](https://pypi.org/project/opentelemetry-instrumentation-aiohttp-server/). Each request produces an inbound HTTP server span (verb + path + status code) that becomes the parent of `bot.run`. If the caller sends a `traceparent` header, the request joins their existing trace.
220
+
221
+ New metrics:
222
+
223
+ - `culture.bot.invocations` — Counter. Labels: `bot`, `event.type`, `outcome=success|error`. Increments only after the bot has matched the filter and been started — filter rejections and startup failures are logged but not counted.
224
+ - `culture.bot.webhook.duration` — Histogram, `s`. Labels: `bot`, `status_class=2xx|3xx|4xx|5xx`. Recorded by a per-request middleware on the webhook listener. `bot=_unrouted` is used for `/health` and other non-bot paths so the histogram never silently mis-attributes.
225
+
226
+ Bots add no new wire protocol surface. Trace context flows in via the existing IRCv3 `culture.dev/traceparent` tag for events and via the standard W3C `traceparent` HTTP header for webhooks.
227
+
228
+ ## What's not in 8.7.0
193
229
 
194
230
  The design spec at `docs/superpowers/specs/2026-04-24-otel-observability-design.md` covers the full three-pillar scope. These pieces remain deferred:
195
231
 
196
- - Bot webhook HTTP instrumentation + bot metrics (`culture.bot.invocations`, `culture.bot.webhook.duration`).
232
+ - Outbound webhook delivery instrumentation (`opentelemetry-instrumentation-aiohttp-client`) bots currently make no outbound HTTP calls; will be added when that feature lands.
197
233
  - Outbound `culture.s2s.messages` (records inbound only — outbound needs a clean verb-extraction site).
198
234
  - OTEL Logs export of audit records (best-effort duplicate; JSONL stays source of truth either way).
199
235
  - `audit-prune` CLI for retention; operators prune manually in v1.
@@ -91,7 +91,7 @@ Documents the audit JSONL schema and file layout as a stable contract.
91
91
  ### `culture/bots/`
92
92
 
93
93
  - `BotManager.on_event` — span `bot.event.dispatch` per matched bot, attrs `bot.name`, `event.type`. Metric `culture.bot.invocations{bot, event.type, outcome}`.
94
- - `http_listener` webhook sender — enable `opentelemetry-instrumentation-aiohttp-client`. Outbound HTTP becomes child spans with traceparent sent as HTTP header automatically. Post-span hook maps status to `status_class` label for `culture.bot.webhook.duration` histogram.
94
+ - `http_listener` webhook receiver — enable `opentelemetry-instrumentation-aiohttp-server`. Inbound HTTP becomes a server span that parents `bot.run`; W3C `traceparent` headers from callers stitch incoming webhooks into their existing trace. A local middleware records the `culture.bot.webhook.duration` histogram (unit `s`) with `{bot, status_class}` labels. **Note (Phase 7, 8.7.0):** the originally-specified outbound `aiohttp-client` instrumentation is deferred — bots make no outbound HTTP calls today; will be added when that feature lands.
95
95
  - `Bot` execution — span `bot.run`.
96
96
 
97
97
  ### `packages/agent-harness/irc_transport.py` (reference, cited into each backend)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentirc-cli"
3
- version = "8.6.0"
3
+ version = "8.7.0"
4
4
  description = "Legacy alias for culture — install culture instead"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -26,6 +26,7 @@ dependencies = [
26
26
  "opentelemetry-sdk>=1.25",
27
27
  "opentelemetry-exporter-otlp-proto-grpc>=1.25",
28
28
  "opentelemetry-semantic-conventions>=0.46b0",
29
+ "opentelemetry-instrumentation-aiohttp-server>=0.46b0",
29
30
  ]
30
31
 
31
32
  [project.urls]
@@ -64,3 +64,12 @@ def get_histogram_count(reader, name: str, attrs: dict | None = None) -> int:
64
64
  if _attrs_match(dict(point.attributes), attrs):
65
65
  total += point.count
66
66
  return total
67
+
68
+
69
+ def get_histogram_sum(reader, name: str, attrs: dict | None = None) -> float:
70
+ """Sum of recorded values for histogram points matching `attrs`. 0.0 if absent."""
71
+ total = 0.0
72
+ for point in _walk_data_points(reader, name):
73
+ if _attrs_match(dict(point.attributes), attrs):
74
+ total += point.sum
75
+ return total
@@ -0,0 +1,33 @@
1
+ """Shared fixtures for telemetry integration tests."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest_asyncio
6
+
7
+ from culture.bots.bot_manager import BotManager
8
+ from culture.bots.http_listener import HttpListener
9
+
10
+
11
+ @pytest_asyncio.fixture
12
+ async def webhook_server(server, tmp_path, monkeypatch):
13
+ """IRCd + BotManager + running HttpListener on a random local port.
14
+
15
+ Yields ``(server, mgr, port)``. The HttpListener is torn down and all
16
+ bots stopped at the end of the test.
17
+ """
18
+ monkeypatch.setattr("culture.bots.config.BOTS_DIR", tmp_path)
19
+ monkeypatch.setattr("culture.bots.bot.BOTS_DIR", tmp_path)
20
+ monkeypatch.setattr("culture.bots.bot_manager.BOTS_DIR", tmp_path)
21
+
22
+ mgr = BotManager(server)
23
+ server.bot_manager = mgr
24
+
25
+ listener = HttpListener(mgr, "127.0.0.1", 0)
26
+ await listener.start()
27
+ site = next(iter(listener._runner._sites))
28
+ port = site._server.sockets[0].getsockname()[1]
29
+
30
+ yield server, mgr, port
31
+
32
+ await listener.stop()
33
+ await mgr.stop_all()
@@ -0,0 +1,138 @@
1
+ """Spans for BotManager.on_event — bot.event.dispatch (Plan 7)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+
7
+ from culture.agentirc.skill import Event, EventType
8
+
9
+
10
+ @pytest.mark.asyncio
11
+ async def test_bot_event_dispatch_span_per_match(tracing_exporter, server_with_bot):
12
+ """A matched event-triggered bot produces one bot.event.dispatch span with attrs."""
13
+ server, _ = server_with_bot(
14
+ bot_name="testserv-watcher",
15
+ trigger_type="event",
16
+ event_filter="type == 'topic'",
17
+ channels=["#general"],
18
+ template="hi {event.nick}",
19
+ )
20
+
21
+ await server.emit_event(
22
+ Event(
23
+ type=EventType.TOPIC,
24
+ channel="#general",
25
+ nick="testserv-newcomer",
26
+ data={"body": "hi"},
27
+ )
28
+ )
29
+
30
+ spans = tracing_exporter.get_finished_spans()
31
+ dispatch_spans = [
32
+ s
33
+ for s in spans
34
+ if s.name == "bot.event.dispatch" and s.attributes.get("bot.name") == "testserv-watcher"
35
+ ]
36
+ assert len(dispatch_spans) == 1, [s.name for s in spans]
37
+ span = dispatch_spans[0]
38
+ assert span.attributes["bot.name"] == "testserv-watcher"
39
+ assert span.attributes["event.type"] == "topic"
40
+
41
+ # Parented under irc.event.emit (contextvars-propagated).
42
+ emit_spans = [s for s in spans if s.name == "irc.event.emit"]
43
+ assert span.parent is not None
44
+ assert any(span.parent.span_id == e.context.span_id for e in emit_spans)
45
+
46
+
47
+ @pytest.mark.asyncio
48
+ async def test_bot_event_dispatch_span_one_per_matched_bot(tracing_exporter, server_with_bots):
49
+ """Two bots match the same event → two bot.event.dispatch spans, one per bot."""
50
+ server, _ = server_with_bots(
51
+ [
52
+ {
53
+ "bot_name": "testserv-a",
54
+ "trigger_type": "event",
55
+ "event_filter": "type == 'topic'",
56
+ "channels": ["#x"],
57
+ "template": "a",
58
+ },
59
+ {
60
+ "bot_name": "testserv-b",
61
+ "trigger_type": "event",
62
+ "event_filter": "type == 'topic'",
63
+ "channels": ["#x"],
64
+ "template": "b",
65
+ },
66
+ ]
67
+ )
68
+
69
+ await server.emit_event(
70
+ Event(type=EventType.TOPIC, channel="#x", nick="testserv-z", data={"body": "y"})
71
+ )
72
+
73
+ dispatch_spans = [
74
+ s
75
+ for s in tracing_exporter.get_finished_spans()
76
+ if s.name == "bot.event.dispatch"
77
+ and s.attributes.get("bot.name") in {"testserv-a", "testserv-b"}
78
+ ]
79
+ bot_names = sorted(s.attributes["bot.name"] for s in dispatch_spans)
80
+ assert bot_names == ["testserv-a", "testserv-b"]
81
+
82
+
83
+ @pytest.mark.asyncio
84
+ async def test_no_dispatch_span_when_filter_rejects(tracing_exporter, server_with_bot):
85
+ """Filter rejection produces no bot.event.dispatch span for that bot."""
86
+ server, _ = server_with_bot(
87
+ bot_name="testserv-silent",
88
+ trigger_type="event",
89
+ event_filter="channel == '#never'",
90
+ channels=["#general"],
91
+ template="x",
92
+ )
93
+
94
+ await server.emit_event(
95
+ Event(type=EventType.TOPIC, channel="#other", nick="testserv-q", data={"body": "x"})
96
+ )
97
+
98
+ dispatch_spans = [
99
+ s
100
+ for s in tracing_exporter.get_finished_spans()
101
+ if s.name == "bot.event.dispatch" and s.attributes.get("bot.name") == "testserv-silent"
102
+ ]
103
+ assert dispatch_spans == []
104
+
105
+
106
+ @pytest.mark.asyncio
107
+ async def test_bot_event_dispatch_span_error_status_on_handle_raise(
108
+ tracing_exporter, server_with_bot, monkeypatch
109
+ ):
110
+ """When Bot.handle raises, the dispatch span carries StatusCode.ERROR."""
111
+ from opentelemetry.trace import StatusCode
112
+
113
+ server, _ = server_with_bot(
114
+ bot_name="testserv-broken",
115
+ trigger_type="event",
116
+ event_filter="type == 'topic'",
117
+ channels=["#err"],
118
+ template="x",
119
+ )
120
+ bot = server.bot_manager.bots["testserv-broken"]
121
+
122
+ async def boom(payload):
123
+ raise RuntimeError("kaboom")
124
+
125
+ monkeypatch.setattr(bot, "handle", boom)
126
+
127
+ await server.emit_event(
128
+ Event(type=EventType.TOPIC, channel="#err", nick="testserv-x", data={"body": "x"})
129
+ )
130
+
131
+ dispatch_spans = [
132
+ s
133
+ for s in tracing_exporter.get_finished_spans()
134
+ if s.name == "bot.event.dispatch" and s.attributes.get("bot.name") == "testserv-broken"
135
+ ]
136
+ assert len(dispatch_spans) == 1
137
+ assert dispatch_spans[0].status.status_code == StatusCode.ERROR
138
+ assert "kaboom" in (dispatch_spans[0].status.description or "")
@@ -0,0 +1,91 @@
1
+ """Spans for Bot.handle — bot.run (Plan 7)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+
7
+ from culture.agentirc.skill import Event, EventType
8
+
9
+
10
+ @pytest.mark.asyncio
11
+ async def test_bot_run_span_parented_under_dispatch(tracing_exporter, server_with_bot):
12
+ """bot.run span exists and is parented under bot.event.dispatch in the event path."""
13
+ server, _ = server_with_bot(
14
+ bot_name="testserv-runner",
15
+ trigger_type="event",
16
+ event_filter="type == 'topic'",
17
+ channels=["#room"],
18
+ template="hi {event.nick}",
19
+ )
20
+
21
+ await server.emit_event(
22
+ Event(type=EventType.TOPIC, channel="#room", nick="testserv-x", data={"body": "y"})
23
+ )
24
+
25
+ spans = tracing_exporter.get_finished_spans()
26
+ run_spans = [
27
+ s
28
+ for s in spans
29
+ if s.name == "bot.run" and s.attributes.get("bot.name") == "testserv-runner"
30
+ ]
31
+ assert len(run_spans) == 1
32
+ run = run_spans[0]
33
+ assert run.attributes["bot.name"] == "testserv-runner"
34
+
35
+ dispatch = next(
36
+ s
37
+ for s in spans
38
+ if s.name == "bot.event.dispatch" and s.attributes.get("bot.name") == "testserv-runner"
39
+ )
40
+ assert run.parent is not None
41
+ assert run.parent.span_id == dispatch.context.span_id
42
+
43
+
44
+ @pytest.mark.asyncio
45
+ async def test_bot_run_span_marks_empty_message(tracing_exporter, server_with_bot):
46
+ """Empty rendered message sets bot.run.empty_message=True."""
47
+ # Whitespace-only template strips to "" inside Bot._render_message,
48
+ # forcing the empty-message branch deterministically.
49
+ server, _ = server_with_bot(
50
+ bot_name="testserv-empty",
51
+ trigger_type="event",
52
+ event_filter="type == 'topic'",
53
+ channels=["#z"],
54
+ template=" ",
55
+ )
56
+
57
+ await server.emit_event(
58
+ Event(type=EventType.TOPIC, channel="#z", nick="testserv-y", data={"body": "x"})
59
+ )
60
+
61
+ run_spans = [
62
+ s
63
+ for s in tracing_exporter.get_finished_spans()
64
+ if s.name == "bot.run" and s.attributes.get("bot.name") == "testserv-empty"
65
+ ]
66
+ assert len(run_spans) == 1
67
+ assert run_spans[0].attributes.get("bot.run.empty_message") is True
68
+
69
+
70
+ @pytest.mark.asyncio
71
+ async def test_bot_run_span_via_dispatch_method(tracing_exporter, server_with_bot):
72
+ """Calling BotManager.dispatch(...) directly (webhook path proxy) still produces bot.run."""
73
+ server, _ = server_with_bot(
74
+ bot_name="testserv-direct",
75
+ trigger_type="webhook",
76
+ channels=["#hooks"],
77
+ template="hi {body}",
78
+ )
79
+
80
+ bot = server.bot_manager.bots["testserv-direct"]
81
+ await bot.start()
82
+
83
+ await server.bot_manager.dispatch("testserv-direct", {"body": "ping"})
84
+
85
+ run_spans = [
86
+ s
87
+ for s in tracing_exporter.get_finished_spans()
88
+ if s.name == "bot.run" and s.attributes.get("bot.name") == "testserv-direct"
89
+ ]
90
+ assert len(run_spans) == 1
91
+ assert run_spans[0].attributes["bot.name"] == "testserv-direct"