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.
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/CHANGELOG.md +10 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/PKG-INFO +2 -1
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/bot.py +24 -13
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/bot_manager.py +53 -24
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/http_listener.py +35 -1
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/metrics.py +13 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/harness-telemetry.md +1 -1
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/telemetry.md +39 -3
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-24-otel-observability-design.md +1 -1
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/pyproject.toml +2 -1
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/_metrics_helpers.py +9 -0
- agentirc_cli-8.7.0/tests/telemetry/conftest.py +33 -0
- agentirc_cli-8.7.0/tests/telemetry/test_bot_event_dispatch_span.py +138 -0
- agentirc_cli-8.7.0/tests/telemetry/test_bot_run_span.py +91 -0
- agentirc_cli-8.7.0/tests/telemetry/test_metrics_bots.py +127 -0
- agentirc_cli-8.7.0/tests/telemetry/test_metrics_webhook.py +148 -0
- agentirc_cli-8.7.0/tests/telemetry/test_webhook_http_span.py +45 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/uv.lock +107 -1
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/agents/doc-test-alignment.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.flake8 +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/docs-check.yml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.gitignore +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.pr_agent.toml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/.pylintrc +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/CLAUDE.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/Gemfile +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/Gemfile.lock +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/LICENSE +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/README.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/SECURITY.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_config.base.yml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_config.culture.yml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_data/sites.yml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_includes/head_custom.html +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_sass/color_schemes/dark-terminal.scss +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/IMG_3183.png +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/apple-touch-icon.png +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/favicon-16x16.png +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/favicon-32x32.png +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/favicon.ico +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/og-agentirc.png +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/assets/images/og-culture.png +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/__main__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/CLAUDE.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/__main__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/channel.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc-architecture.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc-features.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc-skill.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/docs/agentirc.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/events.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/history_store.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/ircd.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/remote_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/room_store.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/rooms_util.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/server_link.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skill.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/history.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/icon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/rooms.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/skills/threads.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/agentirc/thread_store.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/aio.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/filter_dsl.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/welcome/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/welcome/bot.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/system/welcome/handler.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/_passthrough.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/afi.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/agent.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/bot.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/channel.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/devex.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/introspect.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/mesh.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/server.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/display.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/cli/skills.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/culture.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/telemetry.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/culture.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/telemetry.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/culture.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/telemetry.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/culture.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/telemetry.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/app.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/commands.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/status.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/constants.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/credentials.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/formatting.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/learn_prompt.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/mesh_config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/observer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/collector.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/model.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/overview/web/style.css +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/persistence.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/pidfile.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/commands.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/audit.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/events.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/extensions/tracing.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/message.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/protocol/replies.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/audit.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/context.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/culture/telemetry/tracing.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/README.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/architecture-overview.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/audit.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/bots.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/events.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/otelcol-template.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/agentirc/why-agentirc.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/agent-lifecycle.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/choose-a-harness.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/features.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/mental-model.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/operate.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/patterns.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/quickstart.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/reflective-development.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/vision-patterns-index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/vision.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/culture/what-is-culture.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/layers.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/subsites.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/architecture/threads.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/afi.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/commands.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/devex.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/cli/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/console.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/acp.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/claude.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/codex.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/copilot.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/harnesses/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/architecture.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/config.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/deployment.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/reference/server/security.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/resources/positioning.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/federation.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/harnesses.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/humans-and-agents.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/persistence.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/concepts/rooms.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/demos/magic-demo.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/first-session.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/join-as-human.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/local-setup.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/guides/multi-machine.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/shared/use-cases-index.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-15-mesh-events.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-18-culture-dev-positioning.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-22-agex-integration.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-24-otel-foundation.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-25-otel-federation.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-26-otel-metrics.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-27-otel-audit.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/plans/2026-04-28-otel-harness.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-15-mesh-events-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-17-sites-repositioning-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-18-culture-dev-positioning-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/docs/superpowers/specs/2026-04-22-agex-integration-design.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/favicon.ico +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/culture.yaml +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/telemetry.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/robots.txt +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sitemap-agentirc.html +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sitemap-main.html +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sitemap.html +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/sonar-project.properties +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/conftest.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/conftest.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_acp.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_claude.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_codex.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_agent_runner_copilot.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_all_backends_parity.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_daemon_telemetry.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_irc_transport_propagation.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_record_llm_call.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/harness/test_telemetry_module.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/__init__.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/_fakes.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_emit.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_federation.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_lifecycle.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_module.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_audit_parse_error.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_config_load.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_context.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_dispatch_span.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_emit_event_span.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_federation_propagation.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_clients.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_events.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_init.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_irc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_s2s.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_metrics_trace_inbound.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_outbound_inject.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_parse_error.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_privmsg_span.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_s2s_dispatch_span.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_s2s_relay_span.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_s2s_session_span.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_server_init.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_server_link_inject.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_session_span.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/telemetry/test_tracing.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_archive.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot_config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot_config_fires_event_toplevel.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_channel.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_channel_cli.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_afi.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_devex.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_introspect.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_cli_passthrough.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_connection.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_chat_markdown.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_commands.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_connection.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_fixes_224_227.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_icons.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_integration.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_console_status.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_credentials.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_culture_config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_display.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_basic.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_bot_chain.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_bot_trigger.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_cap_fallback.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_catalog.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_federation.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_history.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_lifecycle.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_events_reserved_nick.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_federation.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_filter_dsl.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_history.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_http_listener.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_irc_transport_tags.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_learn_prompt.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_manifest_config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mention_warning.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mentions.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_mesh_readiness.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_message.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_message_tags.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_migrate_cli.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_modes.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_persistence.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_persistence_timeout.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_pidfile.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_register_cli.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_rooms.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_skill_docs.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_skills.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_supervisor.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_template_engine.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_threads.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-8.6.0 → agentirc_cli-8.7.0}/tests/test_webhook.py +0 -0
- {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.
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
116
|
-
|
|
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
|
-
|
|
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
|
|
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),
|
|
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
|
|
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
|
-
-
|
|
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
|
|
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.
|
|
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"
|