agentirc-cli 8.6.0__tar.gz → 8.7.1__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 (476) hide show
  1. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/CHANGELOG.md +25 -0
  2. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/PKG-INFO +2 -1
  3. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/bot.py +24 -13
  4. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/bot_manager.py +53 -24
  5. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/http_listener.py +35 -1
  6. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/channel.py +36 -1
  7. agentirc_cli-8.7.1/culture/cli/shared/constants.py +54 -0
  8. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/daemon.py +2 -1
  9. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/skill/irc_client.py +2 -2
  10. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/daemon.py +2 -1
  11. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/skill/irc_client.py +2 -2
  12. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/daemon.py +2 -1
  13. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/skill/irc_client.py +2 -2
  14. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/daemon.py +2 -1
  15. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/skill/irc_client.py +2 -2
  16. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/overview/collector.py +2 -2
  17. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/telemetry/metrics.py +13 -0
  18. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/harness-telemetry.md +1 -1
  19. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/telemetry.md +39 -3
  20. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/architecture/agent-harness-spec.md +12 -1
  21. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/cli/index.md +13 -0
  22. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-24-otel-observability-design.md +1 -1
  23. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/daemon.py +2 -1
  24. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/skill/irc_client.py +2 -2
  25. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/pyproject.toml +2 -1
  26. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/_metrics_helpers.py +9 -0
  27. agentirc_cli-8.7.1/tests/telemetry/conftest.py +33 -0
  28. agentirc_cli-8.7.1/tests/telemetry/test_bot_event_dispatch_span.py +138 -0
  29. agentirc_cli-8.7.1/tests/telemetry/test_bot_run_span.py +91 -0
  30. agentirc_cli-8.7.1/tests/telemetry/test_metrics_bots.py +127 -0
  31. agentirc_cli-8.7.1/tests/telemetry/test_metrics_webhook.py +148 -0
  32. agentirc_cli-8.7.1/tests/telemetry/test_webhook_http_span.py +45 -0
  33. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_channel_cli.py +78 -1
  34. agentirc_cli-8.7.1/tests/test_constants.py +86 -0
  35. agentirc_cli-8.7.1/tests/test_socket_path_convergence.py +153 -0
  36. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/uv.lock +107 -1
  37. agentirc_cli-8.6.0/culture/cli/shared/constants.py +0 -43
  38. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.claude/agents/doc-test-alignment.md +0 -0
  39. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.claude/skills/pr-review/SKILL.md +0 -0
  40. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.claude/skills/run-tests/SKILL.md +0 -0
  41. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  42. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.flake8 +0 -0
  43. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.github/workflows/docs-check.yml +0 -0
  44. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.github/workflows/publish.yml +0 -0
  45. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.github/workflows/security-checks.yml +0 -0
  46. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.github/workflows/tests.yml +0 -0
  47. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.gitignore +0 -0
  48. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.markdownlint-cli2.yaml +0 -0
  49. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.pr_agent.toml +0 -0
  50. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.pre-commit-config.yaml +0 -0
  51. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/.pylintrc +0 -0
  52. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/CLAUDE.md +0 -0
  53. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/Gemfile +0 -0
  54. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/Gemfile.lock +0 -0
  55. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/LICENSE +0 -0
  56. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/README.md +0 -0
  57. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/SECURITY.md +0 -0
  58. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/_config.base.yml +0 -0
  59. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/_config.culture.yml +0 -0
  60. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/_data/sites.yml +0 -0
  61. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/_includes/head_custom.html +0 -0
  62. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/_sass/color_schemes/anthropic.scss +0 -0
  63. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/_sass/color_schemes/dark-terminal.scss +0 -0
  64. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/_sass/custom/custom.scss +0 -0
  65. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/assets/images/IMG_3183.png +0 -0
  66. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/assets/images/apple-touch-icon.png +0 -0
  67. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/assets/images/favicon-16x16.png +0 -0
  68. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/assets/images/favicon-32x32.png +0 -0
  69. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/assets/images/favicon.ico +0 -0
  70. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/assets/images/og-agentirc.png +0 -0
  71. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/assets/images/og-culture.png +0 -0
  72. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/__init__.py +0 -0
  73. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/__main__.py +0 -0
  74. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/CLAUDE.md +0 -0
  75. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/__init__.py +0 -0
  76. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/__main__.py +0 -0
  77. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/channel.py +0 -0
  78. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/client.py +0 -0
  79. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/config.py +0 -0
  80. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/docs/agentirc-architecture.md +0 -0
  81. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/docs/agentirc-features.md +0 -0
  82. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/docs/agentirc-skill.md +0 -0
  83. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/docs/agentirc.md +0 -0
  84. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/events.py +0 -0
  85. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/history_store.py +0 -0
  86. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/ircd.py +0 -0
  87. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/remote_client.py +0 -0
  88. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/room_store.py +0 -0
  89. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/rooms_util.py +0 -0
  90. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/server_link.py +0 -0
  91. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/skill.py +0 -0
  92. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/skills/__init__.py +0 -0
  93. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/skills/history.py +0 -0
  94. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/skills/icon.py +0 -0
  95. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/skills/rooms.py +0 -0
  96. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/skills/threads.py +0 -0
  97. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/agentirc/thread_store.py +0 -0
  98. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/aio.py +0 -0
  99. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/__init__.py +0 -0
  100. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/config.py +0 -0
  101. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/filter_dsl.py +0 -0
  102. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/system/__init__.py +0 -0
  103. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/system/welcome/__init__.py +0 -0
  104. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/system/welcome/bot.yaml +0 -0
  105. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/system/welcome/handler.py +0 -0
  106. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/template_engine.py +0 -0
  107. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/bots/virtual_client.py +0 -0
  108. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/__init__.py +0 -0
  109. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/_passthrough.py +0 -0
  110. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/afi.py +0 -0
  111. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/agent.py +0 -0
  112. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/bot.py +0 -0
  113. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/devex.py +0 -0
  114. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/introspect.py +0 -0
  115. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/mesh.py +0 -0
  116. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/server.py +0 -0
  117. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/shared/__init__.py +0 -0
  118. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/shared/display.py +0 -0
  119. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/shared/formatting.py +0 -0
  120. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/shared/ipc.py +0 -0
  121. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/shared/mesh.py +0 -0
  122. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/shared/process.py +0 -0
  123. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/cli/skills.py +0 -0
  124. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/__init__.py +0 -0
  125. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/__init__.py +0 -0
  126. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/agent_runner.py +0 -0
  127. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/config.py +0 -0
  128. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/culture.yaml +0 -0
  129. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/ipc.py +0 -0
  130. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/irc_transport.py +0 -0
  131. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/message_buffer.py +0 -0
  132. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/skill/SKILL.md +0 -0
  133. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/skill/__init__.py +0 -0
  134. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/socket_server.py +0 -0
  135. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/supervisor.py +0 -0
  136. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/telemetry.py +0 -0
  137. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/acp/webhook.py +0 -0
  138. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/__init__.py +0 -0
  139. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/__main__.py +0 -0
  140. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/agent_runner.py +0 -0
  141. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/config.py +0 -0
  142. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/culture.yaml +0 -0
  143. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/ipc.py +0 -0
  144. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/irc_transport.py +0 -0
  145. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/message_buffer.py +0 -0
  146. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/skill/SKILL.md +0 -0
  147. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/skill/__init__.py +0 -0
  148. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/socket_server.py +0 -0
  149. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/supervisor.py +0 -0
  150. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/telemetry.py +0 -0
  151. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/claude/webhook.py +0 -0
  152. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/__init__.py +0 -0
  153. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/agent_runner.py +0 -0
  154. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/config.py +0 -0
  155. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/culture.yaml +0 -0
  156. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/ipc.py +0 -0
  157. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/irc_transport.py +0 -0
  158. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/message_buffer.py +0 -0
  159. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/skill/SKILL.md +0 -0
  160. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/skill/__init__.py +0 -0
  161. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/socket_server.py +0 -0
  162. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/supervisor.py +0 -0
  163. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/telemetry.py +0 -0
  164. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/codex/webhook.py +0 -0
  165. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/__init__.py +0 -0
  166. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/agent_runner.py +0 -0
  167. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/config.py +0 -0
  168. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/culture.yaml +0 -0
  169. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/ipc.py +0 -0
  170. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/irc_transport.py +0 -0
  171. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/message_buffer.py +0 -0
  172. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/skill/SKILL.md +0 -0
  173. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/skill/__init__.py +0 -0
  174. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/socket_server.py +0 -0
  175. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/supervisor.py +0 -0
  176. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/telemetry.py +0 -0
  177. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/clients/copilot/webhook.py +0 -0
  178. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/config.py +0 -0
  179. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/__init__.py +0 -0
  180. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/app.py +0 -0
  181. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/client.py +0 -0
  182. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/commands.py +0 -0
  183. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/status.py +0 -0
  184. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/widgets/__init__.py +0 -0
  185. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/widgets/chat.py +0 -0
  186. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/widgets/info_panel.py +0 -0
  187. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/console/widgets/sidebar.py +0 -0
  188. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/constants.py +0 -0
  189. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/credentials.py +0 -0
  190. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/formatting.py +0 -0
  191. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/learn_prompt.py +0 -0
  192. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/mesh_config.py +0 -0
  193. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/observer.py +0 -0
  194. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/overview/__init__.py +0 -0
  195. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/overview/model.py +0 -0
  196. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/overview/renderer_text.py +0 -0
  197. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/overview/renderer_web.py +0 -0
  198. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/overview/web/style.css +0 -0
  199. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/persistence.py +0 -0
  200. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/pidfile.py +0 -0
  201. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/__init__.py +0 -0
  202. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/commands.py +0 -0
  203. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/audit.md +0 -0
  204. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/events.md +0 -0
  205. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/federation.md +0 -0
  206. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/history.md +0 -0
  207. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/icons.md +0 -0
  208. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/rooms.md +0 -0
  209. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/tags.md +0 -0
  210. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/threads.md +0 -0
  211. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/extensions/tracing.md +0 -0
  212. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/message.py +0 -0
  213. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/protocol-index.md +0 -0
  214. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/protocol/replies.py +0 -0
  215. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/skills/culture/SKILL.md +0 -0
  216. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/telemetry/__init__.py +0 -0
  217. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/telemetry/audit.py +0 -0
  218. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/telemetry/context.py +0 -0
  219. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/culture/telemetry/tracing.py +0 -0
  220. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/README.md +0 -0
  221. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/architecture-overview.md +0 -0
  222. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/audit.md +0 -0
  223. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/bots.md +0 -0
  224. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/events.md +0 -0
  225. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/index.md +0 -0
  226. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/otelcol-template.yaml +0 -0
  227. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/agentirc/why-agentirc.md +0 -0
  228. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/agent-lifecycle.md +0 -0
  229. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/choose-a-harness.md +0 -0
  230. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/features.md +0 -0
  231. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/index.md +0 -0
  232. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/mental-model.md +0 -0
  233. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/operate.md +0 -0
  234. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/patterns.md +0 -0
  235. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/quickstart.md +0 -0
  236. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/reflective-development.md +0 -0
  237. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/vision-patterns-index.md +0 -0
  238. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/vision.md +0 -0
  239. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/culture/what-is-culture.md +0 -0
  240. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/architecture/index.md +0 -0
  241. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/architecture/layers.md +0 -0
  242. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/architecture/subsites.md +0 -0
  243. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/architecture/threads.md +0 -0
  244. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/cli/afi.md +0 -0
  245. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/cli/commands.md +0 -0
  246. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/cli/devex.md +0 -0
  247. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/console.md +0 -0
  248. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/harnesses/acp.md +0 -0
  249. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/harnesses/claude.md +0 -0
  250. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/harnesses/codex.md +0 -0
  251. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/harnesses/copilot.md +0 -0
  252. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/harnesses/index.md +0 -0
  253. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/index.md +0 -0
  254. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/server/architecture.md +0 -0
  255. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/server/config.md +0 -0
  256. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/server/deployment.md +0 -0
  257. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/server/index.md +0 -0
  258. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/reference/server/security.md +0 -0
  259. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  260. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/resources/positioning.md +0 -0
  261. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/concepts/federation.md +0 -0
  262. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/concepts/harnesses.md +0 -0
  263. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/concepts/humans-and-agents.md +0 -0
  264. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/concepts/index.md +0 -0
  265. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/concepts/persistence.md +0 -0
  266. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/concepts/rooms.md +0 -0
  267. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/demos/magic-demo.md +0 -0
  268. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/guides/first-session.md +0 -0
  269. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/guides/index.md +0 -0
  270. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/guides/join-as-human.md +0 -0
  271. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/guides/local-setup.md +0 -0
  272. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/guides/multi-machine.md +0 -0
  273. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/01-pair-programming.md +0 -0
  274. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
  275. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
  276. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
  277. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/05-the-observer.md +0 -0
  278. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
  279. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
  280. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
  281. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/09-research-swarm.md +0 -0
  282. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
  283. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/shared/use-cases-index.md +0 -0
  284. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  285. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  286. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  287. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  288. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  289. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  290. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  291. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  292. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  293. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
  294. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
  295. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-15-mesh-events.md +0 -0
  296. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-18-culture-dev-positioning.md +0 -0
  297. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-22-agex-integration.md +0 -0
  298. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-24-otel-foundation.md +0 -0
  299. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-25-otel-federation.md +0 -0
  300. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-26-otel-metrics.md +0 -0
  301. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-27-otel-audit.md +0 -0
  302. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/plans/2026-04-28-otel-harness.md +0 -0
  303. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  304. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  305. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  306. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  307. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  308. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  309. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  310. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  311. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  312. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  313. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  314. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  315. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  316. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
  317. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
  318. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
  319. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
  320. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-15-mesh-events-design.md +0 -0
  321. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-17-sites-repositioning-design.md +0 -0
  322. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-18-culture-dev-positioning-design.md +0 -0
  323. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/docs/superpowers/specs/2026-04-22-agex-integration-design.md +0 -0
  324. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/favicon.ico +0 -0
  325. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/README.md +0 -0
  326. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/config.py +0 -0
  327. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/culture.yaml +0 -0
  328. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/ipc.py +0 -0
  329. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/irc_transport.py +0 -0
  330. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/message_buffer.py +0 -0
  331. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/skill/SKILL.md +0 -0
  332. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/socket_server.py +0 -0
  333. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/telemetry.py +0 -0
  334. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/packages/agent-harness/webhook.py +0 -0
  335. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  336. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  337. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  338. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  339. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/robots.txt +0 -0
  340. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/sitemap-agentirc.html +0 -0
  341. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/sitemap-main.html +0 -0
  342. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/sitemap.html +0 -0
  343. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/sonar-project.properties +0 -0
  344. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/__init__.py +0 -0
  345. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/conftest.py +0 -0
  346. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/__init__.py +0 -0
  347. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/conftest.py +0 -0
  348. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_agent_runner_acp.py +0 -0
  349. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_agent_runner_claude.py +0 -0
  350. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_agent_runner_codex.py +0 -0
  351. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_agent_runner_copilot.py +0 -0
  352. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_all_backends_parity.py +0 -0
  353. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_daemon_telemetry.py +0 -0
  354. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_irc_transport_propagation.py +0 -0
  355. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_record_llm_call.py +0 -0
  356. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/harness/test_telemetry_module.py +0 -0
  357. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/__init__.py +0 -0
  358. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/_fakes.py +0 -0
  359. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_audit_emit.py +0 -0
  360. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_audit_federation.py +0 -0
  361. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_audit_lifecycle.py +0 -0
  362. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_audit_module.py +0 -0
  363. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_audit_parse_error.py +0 -0
  364. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_config.py +0 -0
  365. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_config_load.py +0 -0
  366. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_context.py +0 -0
  367. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_dispatch_span.py +0 -0
  368. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_emit_event_span.py +0 -0
  369. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_federation_propagation.py +0 -0
  370. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_metrics_clients.py +0 -0
  371. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_metrics_events.py +0 -0
  372. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_metrics_init.py +0 -0
  373. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_metrics_irc.py +0 -0
  374. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_metrics_s2s.py +0 -0
  375. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_metrics_trace_inbound.py +0 -0
  376. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_outbound_inject.py +0 -0
  377. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_parse_error.py +0 -0
  378. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_privmsg_span.py +0 -0
  379. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_s2s_dispatch_span.py +0 -0
  380. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_s2s_relay_span.py +0 -0
  381. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_s2s_session_span.py +0 -0
  382. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_server_init.py +0 -0
  383. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_server_link_inject.py +0 -0
  384. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_session_span.py +0 -0
  385. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/telemetry/test_tracing.py +0 -0
  386. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_acp_daemon.py +0 -0
  387. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_agent_runner.py +0 -0
  388. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_archive.py +0 -0
  389. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_bot.py +0 -0
  390. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_bot_config.py +0 -0
  391. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_bot_config_fires_event_toplevel.py +0 -0
  392. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_bot_manager.py +0 -0
  393. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_bots_integration.py +0 -0
  394. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_channel.py +0 -0
  395. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_cli_afi.py +0 -0
  396. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_cli_devex.py +0 -0
  397. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_cli_introspect.py +0 -0
  398. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_cli_passthrough.py +0 -0
  399. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_codex_daemon.py +0 -0
  400. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_connection.py +0 -0
  401. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_chat_markdown.py +0 -0
  402. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_client.py +0 -0
  403. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_commands.py +0 -0
  404. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_connection.py +0 -0
  405. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_fixes_224_227.py +0 -0
  406. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_icons.py +0 -0
  407. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_integration.py +0 -0
  408. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_console_status.py +0 -0
  409. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_copilot_daemon.py +0 -0
  410. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_credentials.py +0 -0
  411. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_culture_config.py +0 -0
  412. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_daemon.py +0 -0
  413. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_daemon_config.py +0 -0
  414. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_daemon_ipc.py +0 -0
  415. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_discovery.py +0 -0
  416. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_display.py +0 -0
  417. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_basic.py +0 -0
  418. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_bot_chain.py +0 -0
  419. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_bot_trigger.py +0 -0
  420. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_cap_fallback.py +0 -0
  421. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_catalog.py +0 -0
  422. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_federation.py +0 -0
  423. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_history.py +0 -0
  424. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_lifecycle.py +0 -0
  425. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_events_reserved_nick.py +0 -0
  426. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_federation.py +0 -0
  427. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_filter_dsl.py +0 -0
  428. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_history.py +0 -0
  429. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_http_listener.py +0 -0
  430. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_integration_layer5.py +0 -0
  431. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_ipc.py +0 -0
  432. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_irc_transport.py +0 -0
  433. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_irc_transport_tags.py +0 -0
  434. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_learn_prompt.py +0 -0
  435. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_link_reconnect.py +0 -0
  436. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_manifest_config.py +0 -0
  437. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_mention_alias.py +0 -0
  438. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_mention_target_cleanup.py +0 -0
  439. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_mention_warning.py +0 -0
  440. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_mentions.py +0 -0
  441. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_mesh_config.py +0 -0
  442. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_mesh_readiness.py +0 -0
  443. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_message.py +0 -0
  444. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_message_buffer.py +0 -0
  445. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_message_tags.py +0 -0
  446. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_messaging.py +0 -0
  447. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_migrate_cli.py +0 -0
  448. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_modes.py +0 -0
  449. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_overview_cli.py +0 -0
  450. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_overview_collector.py +0 -0
  451. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_overview_model.py +0 -0
  452. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_overview_renderer.py +0 -0
  453. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_overview_web.py +0 -0
  454. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_persistence.py +0 -0
  455. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_persistence_timeout.py +0 -0
  456. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_pidfile.py +0 -0
  457. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_poll_loop.py +0 -0
  458. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_register_cli.py +0 -0
  459. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_room_persistence.py +0 -0
  460. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_rooms.py +0 -0
  461. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_rooms_federation.py +0 -0
  462. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_rooms_integration.py +0 -0
  463. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_server_icon_skill.py +0 -0
  464. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_setup_update_cli.py +0 -0
  465. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_skill_client.py +0 -0
  466. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_skill_docs.py +0 -0
  467. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_skills.py +0 -0
  468. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_socket_server.py +0 -0
  469. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_supervisor.py +0 -0
  470. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_template_engine.py +0 -0
  471. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_thread_buffer.py +0 -0
  472. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_threads.py +0 -0
  473. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_virtual_client.py +0 -0
  474. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_wait_for_port.py +0 -0
  475. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_webhook.py +0 -0
  476. {agentirc_cli-8.6.0 → agentirc_cli-8.7.1}/tests/test_welcome_bot.py +0 -0
@@ -4,6 +4,31 @@ 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.1] - 2026-04-27
8
+
9
+ ### Added
10
+
11
+ - `tests/test_socket_path_convergence.py`: parametric regression test asserting all 8 daemon+skill resolvers agree with the CLI's `agent_socket_path()` for both `XDG_RUNTIME_DIR` set and unset.
12
+ - `tests/test_constants.py`: unit tests pinning `culture_runtime_dir()` contract (env precedence, `~/.culture/run/` fallback, mode 0700).
13
+
14
+ ### Changed
15
+
16
+ - `culture channel {message,list,read}`: when `CULTURE_NICK` is set but the daemon IPC is unreachable or rejects the request, the CLI now prints a stderr warning naming the nick, the socket path, and the GitHub issue tracker before falling back to the peek-nick observer. The fallback itself is preserved for human use (no `CULTURE_NICK`).
17
+
18
+ ### Fixed
19
+
20
+ - Converged all socket-path resolvers (4 backend daemons, 4 skill irc_clients, overview collector, harness package reference impl) on `culture_runtime_dir()` so the CLI and daemon agree on the IPC socket path when `XDG_RUNTIME_DIR` is unset (macOS default). Previously the daemon listened on `/tmp/culture-<nick>.sock` while the CLI looked in `~/.culture/run/`, causing `culture channel message` to silently fall back to an anonymous `_peek<random>` nick (#302, regression of #203).
21
+
22
+ ## [8.7.0] - 2026-04-26
23
+
24
+ ### Added
25
+
26
+ - 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.
27
+
28
+ ### Changed
29
+
30
+ - docs/agentirc/telemetry.md updated with the 8.7.0 section; harness-telemetry.md cross-link updated.
31
+
7
32
  ## [8.6.0] - 2026-04-26
8
33
 
9
34
  ### 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.1
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
 
@@ -24,11 +24,43 @@ def _valid_nick(nick: str) -> bool:
24
24
  return len(parts) == 2 and all(parts)
25
25
 
26
26
 
27
+ _ISSUE_TRACKER_URL = "https://github.com/agentculture/culture/issues"
28
+
29
+
30
+ def _warn_observer_fallback(operation: str) -> None:
31
+ """Warn on stderr when an observer-fallback command silently went anonymous.
32
+
33
+ Called only by the three commands that do fall back to the observer
34
+ connection (`message`, `list`, `read`). `topic` and the `_require_ipc`
35
+ commands exit on failure instead, so this helper is not used there —
36
+ a misleading "falling back" notice would contradict the actual error.
37
+
38
+ Issue #302: a daemon/CLI socket-path mismatch on macOS hid behind the
39
+ silent peek fallback for two releases. The warning names the issue
40
+ tracker so the next reproducer takes seconds to file.
41
+ """
42
+ nick = os.environ.get("CULTURE_NICK")
43
+ if not nick or not _valid_nick(nick):
44
+ return # Human use without CULTURE_NICK — observer is the intended path.
45
+ sock = agent_socket_path(nick)
46
+ print(
47
+ f"Warning: agent daemon IPC for {nick} failed ({sock}).\n"
48
+ f" Falling back to observer connection — `{operation}` will not run\n"
49
+ f" through the agent daemon and the action will not appear under {nick}.\n"
50
+ f" Verify the daemon is running: culture agent status {nick}\n"
51
+ f" If it is running, this is a bug. Please open an issue:\n"
52
+ f" {_ISSUE_TRACKER_URL}",
53
+ file=sys.stderr,
54
+ )
55
+
56
+
27
57
  def _try_ipc(msg_type: str, **kwargs) -> dict | None:
28
58
  """Try to route a command through the agent daemon's IPC socket.
29
59
 
30
60
  Returns the response dict if CULTURE_NICK is set and the daemon is
31
- reachable, otherwise None (caller should fall back to observer).
61
+ reachable, otherwise None (caller should fall back to observer or
62
+ surface its own error — see `_warn_observer_fallback` for the
63
+ observer-fallback path).
32
64
  """
33
65
  nick = os.environ.get("CULTURE_NICK")
34
66
  if not nick or not _valid_nick(nick):
@@ -169,6 +201,7 @@ def _cmd_list(args: argparse.Namespace) -> None:
169
201
  print(f" {ch}")
170
202
  return
171
203
 
204
+ _warn_observer_fallback("channel list")
172
205
  observer = get_observer(args.config)
173
206
  channels = asyncio.run(observer.list_channels())
174
207
 
@@ -199,6 +232,7 @@ def _cmd_read(args: argparse.Namespace) -> None:
199
232
  print(f"<{nick}> {text}")
200
233
  return
201
234
 
235
+ _warn_observer_fallback("channel read")
202
236
  observer = get_observer(args.config)
203
237
  messages = asyncio.run(observer.read_channel(channel, limit=args.limit))
204
238
 
@@ -267,6 +301,7 @@ def _cmd_message(args: argparse.Namespace) -> None:
267
301
  print(f"Sent to {target}")
268
302
  return
269
303
 
304
+ _warn_observer_fallback("channel message")
270
305
  observer = get_observer(args.config)
271
306
  asyncio.run(observer.send_message(target, text))
272
307
  print(f"Sent to {target}")
@@ -0,0 +1,54 @@
1
+ """Shared constants for culture CLI modules."""
2
+
3
+ import os
4
+ import stat
5
+
6
+ from culture.bots.config import BOT_CONFIG_FILE # noqa: F401
7
+
8
+ DEFAULT_CONFIG = os.path.expanduser("~/.culture/server.yaml")
9
+ LOG_DIR = os.path.expanduser("~/.culture/logs")
10
+
11
+ _CONFIG_HELP = "Config file path"
12
+ _SERVER_NAME_HELP = "Server name"
13
+ _BOT_NAME_HELP = "Bot name"
14
+
15
+ DEFAULT_CHANNEL = "#general"
16
+ NO_AGENTS_MSG = "No agents configured"
17
+ CULTURE_DIR = ".culture"
18
+ AGENTS_YAML = "agents.yaml"
19
+
20
+ DEFAULT_SERVER_CONFIG = os.path.expanduser("~/.culture/server.yaml")
21
+ LEGACY_CONFIG = os.path.expanduser("~/.culture/agents.yaml")
22
+
23
+
24
+ def culture_runtime_dir() -> str:
25
+ """Return a user-private directory for culture daemon sockets.
26
+
27
+ Resolution order:
28
+
29
+ 1. ``$XDG_RUNTIME_DIR`` when set (Linux/systemd default — already
30
+ user-private at ``/run/user/<uid>``).
31
+ 2. ``~/.culture/run/`` otherwise (typical macOS path), created mode
32
+ 0700 if missing and re-tightened to 0700 on every call so a
33
+ hand-created or pre-existing dir cannot leak sockets.
34
+
35
+ Raises ``RuntimeError`` when neither ``XDG_RUNTIME_DIR`` nor a
36
+ resolvable home directory is available — silently writing a literal
37
+ ``~/.culture/run`` directory in CWD would surprise callers and the
38
+ daemons (which now route through this resolver) would fail at
39
+ socket-bind time anyway.
40
+ """
41
+ xdg = os.environ.get("XDG_RUNTIME_DIR")
42
+ if xdg:
43
+ return xdg
44
+ home = os.path.expanduser("~")
45
+ if not home or home == "~" or not os.path.isabs(home):
46
+ raise RuntimeError(
47
+ "culture_runtime_dir(): cannot resolve a home directory "
48
+ "(os.path.expanduser('~') returned %r). Set $HOME or "
49
+ "$XDG_RUNTIME_DIR before running culture commands." % home
50
+ )
51
+ fallback = os.path.join(home, ".culture", "run")
52
+ os.makedirs(fallback, mode=0o700, exist_ok=True)
53
+ os.chmod(fallback, stat.S_IRWXU)
54
+ return fallback
@@ -18,6 +18,7 @@ import time
18
18
  from collections import deque
19
19
 
20
20
  from culture.aio import maybe_await
21
+ from culture.cli.shared.constants import culture_runtime_dir
21
22
  from culture.clients.acp.agent_runner import ACPAgentRunner
22
23
  from culture.clients.acp.config import AgentConfig, DaemonConfig
23
24
  from culture.clients.acp.ipc import make_response
@@ -62,7 +63,7 @@ class ACPDaemon:
62
63
  self.skip_agent = skip_agent
63
64
 
64
65
  self._socket_path = os.path.join(
65
- socket_dir or os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
66
+ socket_dir or culture_runtime_dir(),
66
67
  f"culture-{agent.nick}.sock",
67
68
  )
68
69
 
@@ -16,6 +16,7 @@ import os
16
16
  import sys
17
17
  from typing import Any
18
18
 
19
+ from culture.cli.shared.constants import culture_runtime_dir
19
20
  from culture.clients.acp.ipc import (
20
21
  MSG_TYPE_RESPONSE,
21
22
  MSG_TYPE_WHISPER,
@@ -192,8 +193,7 @@ def _sock_path_from_env() -> str:
192
193
  if not nick:
193
194
  print("ERROR: CULTURE_NICK environment variable is not set", file=sys.stderr)
194
195
  sys.exit(1)
195
- runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
196
- return os.path.join(runtime_dir, f"culture-{nick}.sock")
196
+ return os.path.join(culture_runtime_dir(), f"culture-{nick}.sock")
197
197
 
198
198
 
199
199
  def _parse_ask_timeout(remaining: list[str]) -> tuple[int, list[str]]:
@@ -8,6 +8,7 @@ import re
8
8
  import time
9
9
 
10
10
  from culture.aio import maybe_await
11
+ from culture.cli.shared.constants import culture_runtime_dir
11
12
  from culture.clients.claude.agent_runner import AgentRunner
12
13
  from culture.clients.claude.config import AgentConfig, DaemonConfig
13
14
  from culture.clients.claude.ipc import make_response
@@ -51,7 +52,7 @@ class AgentDaemon:
51
52
  self.skip_claude = skip_claude
52
53
 
53
54
  self._socket_path = os.path.join(
54
- socket_dir or os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
55
+ socket_dir or culture_runtime_dir(),
55
56
  f"culture-{agent.nick}.sock",
56
57
  )
57
58
 
@@ -16,6 +16,7 @@ import os
16
16
  import sys
17
17
  from typing import Any
18
18
 
19
+ from culture.cli.shared.constants import culture_runtime_dir
19
20
  from culture.clients.claude.ipc import (
20
21
  MSG_TYPE_RESPONSE,
21
22
  MSG_TYPE_WHISPER,
@@ -189,8 +190,7 @@ def _sock_path_from_env() -> str:
189
190
  if not nick:
190
191
  print("ERROR: CULTURE_NICK environment variable is not set", file=sys.stderr)
191
192
  sys.exit(1)
192
- runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
193
- return os.path.join(runtime_dir, f"culture-{nick}.sock")
193
+ return os.path.join(culture_runtime_dir(), f"culture-{nick}.sock")
194
194
 
195
195
 
196
196
  def _parse_ask_timeout(remaining: list[str]) -> tuple[int, list[str]]:
@@ -15,6 +15,7 @@ import time
15
15
  from collections import deque
16
16
 
17
17
  from culture.aio import maybe_await
18
+ from culture.cli.shared.constants import culture_runtime_dir
18
19
  from culture.clients.codex.agent_runner import CodexAgentRunner
19
20
  from culture.clients.codex.config import AgentConfig, DaemonConfig
20
21
  from culture.clients.codex.ipc import make_response
@@ -66,7 +67,7 @@ class CodexDaemon:
66
67
  self.skip_codex = skip_codex
67
68
 
68
69
  self._socket_path = os.path.join(
69
- socket_dir or os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
70
+ socket_dir or culture_runtime_dir(),
70
71
  f"culture-{agent.nick}.sock",
71
72
  )
72
73
 
@@ -16,6 +16,7 @@ import os
16
16
  import sys
17
17
  from typing import Any
18
18
 
19
+ from culture.cli.shared.constants import culture_runtime_dir
19
20
  from culture.clients.codex.ipc import (
20
21
  MSG_TYPE_RESPONSE,
21
22
  MSG_TYPE_WHISPER,
@@ -189,8 +190,7 @@ def _sock_path_from_env() -> str:
189
190
  if not nick:
190
191
  print("ERROR: CULTURE_NICK environment variable is not set", file=sys.stderr)
191
192
  sys.exit(1)
192
- runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
193
- return os.path.join(runtime_dir, f"culture-{nick}.sock")
193
+ return os.path.join(culture_runtime_dir(), f"culture-{nick}.sock")
194
194
 
195
195
 
196
196
  def _parse_ask_timeout(remaining: list[str]) -> tuple[int, list[str]]:
@@ -15,6 +15,7 @@ import time
15
15
  from collections import deque
16
16
 
17
17
  from culture.aio import maybe_await
18
+ from culture.cli.shared.constants import culture_runtime_dir
18
19
  from culture.clients.copilot.agent_runner import CopilotAgentRunner
19
20
  from culture.clients.copilot.config import AgentConfig, DaemonConfig
20
21
  from culture.clients.copilot.ipc import make_response
@@ -59,7 +60,7 @@ class CopilotDaemon:
59
60
  self.skip_copilot = skip_copilot
60
61
 
61
62
  self._socket_path = os.path.join(
62
- socket_dir or os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
63
+ socket_dir or culture_runtime_dir(),
63
64
  f"culture-{agent.nick}.sock",
64
65
  )
65
66
 
@@ -16,6 +16,7 @@ import os
16
16
  import sys
17
17
  from typing import Any
18
18
 
19
+ from culture.cli.shared.constants import culture_runtime_dir
19
20
  from culture.clients.copilot.ipc import (
20
21
  MSG_TYPE_RESPONSE,
21
22
  MSG_TYPE_WHISPER,
@@ -192,8 +193,7 @@ def _sock_path_from_env() -> str:
192
193
  if not nick:
193
194
  print("ERROR: CULTURE_NICK environment variable is not set", file=sys.stderr)
194
195
  sys.exit(1)
195
- runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
196
- return os.path.join(runtime_dir, f"culture-{nick}.sock")
196
+ return os.path.join(culture_runtime_dir(), f"culture-{nick}.sock")
197
197
 
198
198
 
199
199
  def _parse_ask_timeout(remaining: list[str]) -> tuple[int, list[str]]:
@@ -7,6 +7,7 @@ import glob
7
7
  import os
8
8
 
9
9
  from culture.bots.config import BOT_CONFIG_FILE
10
+ from culture.cli.shared.constants import culture_runtime_dir
10
11
  from culture.protocol.message import Message as IRCMessage
11
12
 
12
13
  from .model import Agent, BotInfo, MeshState, Message, Room
@@ -428,8 +429,7 @@ async def _enrich_via_ipc(agents: dict[str, Agent], server_name: str) -> None:
428
429
  """Enrich local agents with daemon IPC status data."""
429
430
  from culture.clients.claude.ipc import decode_message, encode_message, make_request
430
431
 
431
- runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
432
- socket_pattern = os.path.join(runtime_dir, "culture-*.sock")
432
+ socket_pattern = os.path.join(culture_runtime_dir(), "culture-*.sock")
433
433
 
434
434
  for sock_path in glob.glob(socket_pattern):
435
435
  # Extract nick from socket filename: culture-<nick>.sock
@@ -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.