agentirc-cli 8.1.0__tar.gz → 8.2.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.1.0 → agentirc_cli-8.2.0}/CHANGELOG.md +11 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/PKG-INFO +5 -1
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/_config.culture.yml +3 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/_data/sites.yml +1 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/_includes/head_custom.html +1 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/client.py +200 -74
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/config.py +15 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/ircd.py +49 -20
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/config.py +10 -0
- agentirc_cli-8.2.0/culture/protocol/extensions/tracing.md +31 -0
- agentirc_cli-8.2.0/culture/telemetry/__init__.py +22 -0
- agentirc_cli-8.2.0/culture/telemetry/context.py +84 -0
- agentirc_cli-8.2.0/culture/telemetry/tracing.py +114 -0
- agentirc_cli-8.2.0/docs/agentirc/otelcol-template.yaml +25 -0
- agentirc_cli-8.2.0/docs/agentirc/telemetry.md +81 -0
- agentirc_cli-8.2.0/docs/reference/architecture/index.md +27 -0
- agentirc_cli-8.2.0/docs/reference/architecture/subsites.md +111 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/server/config.md +26 -0
- agentirc_cli-8.2.0/docs/superpowers/plans/2026-04-24-otel-foundation.md +2159 -0
- agentirc_cli-8.2.0/docs/superpowers/specs/2026-04-24-otel-observability-design.md +334 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/pyproject.toml +5 -1
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/sitemap.html +1 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/conftest.py +26 -0
- agentirc_cli-8.2.0/tests/telemetry/__init__.py +0 -0
- agentirc_cli-8.2.0/tests/telemetry/_fakes.py +26 -0
- agentirc_cli-8.2.0/tests/telemetry/test_config.py +26 -0
- agentirc_cli-8.2.0/tests/telemetry/test_config_load.py +36 -0
- agentirc_cli-8.2.0/tests/telemetry/test_context.py +105 -0
- agentirc_cli-8.2.0/tests/telemetry/test_dispatch_span.py +45 -0
- agentirc_cli-8.2.0/tests/telemetry/test_emit_event_span.py +42 -0
- agentirc_cli-8.2.0/tests/telemetry/test_outbound_inject.py +70 -0
- agentirc_cli-8.2.0/tests/telemetry/test_parse_error.py +33 -0
- agentirc_cli-8.2.0/tests/telemetry/test_privmsg_span.py +45 -0
- agentirc_cli-8.2.0/tests/telemetry/test_server_init.py +29 -0
- agentirc_cli-8.2.0/tests/telemetry/test_tracing.py +49 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/uv.lock +185 -1
- agentirc_cli-8.1.0/docs/reference/architecture/index.md +0 -13
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.claude/agents/doc-test-alignment.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.flake8 +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.github/workflows/docs-check.yml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.gitignore +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.pr_agent.toml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/.pylintrc +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/CLAUDE.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/Gemfile +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/Gemfile.lock +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/LICENSE +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/README.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/SECURITY.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/_config.base.yml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/_sass/color_schemes/dark-terminal.scss +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/assets/images/IMG_3183.png +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/assets/images/apple-touch-icon.png +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/assets/images/favicon-16x16.png +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/assets/images/favicon-32x32.png +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/assets/images/favicon.ico +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/assets/images/og-agentirc.png +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/assets/images/og-culture.png +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/__main__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/CLAUDE.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/__main__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/channel.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/docs/agentirc-architecture.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/docs/agentirc-features.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/docs/agentirc-skill.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/docs/agentirc.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/events.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/history_store.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/remote_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/room_store.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/rooms_util.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/server_link.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/skill.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/skills/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/skills/history.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/skills/icon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/skills/rooms.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/skills/threads.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/agentirc/thread_store.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/aio.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/bot.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/filter_dsl.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/system/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/system/welcome/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/system/welcome/bot.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/system/welcome/handler.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/_passthrough.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/afi.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/agent.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/bot.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/channel.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/devex.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/introspect.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/mesh.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/server.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/shared/display.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/cli/skills.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/culture.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/culture.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/culture.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/culture.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/app.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/commands.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/status.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/constants.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/credentials.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/formatting.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/learn_prompt.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/mesh_config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/observer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/overview/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/overview/collector.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/overview/model.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/overview/web/style.css +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/persistence.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/pidfile.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/commands.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/extensions/events.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/message.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/protocol/replies.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/README.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/agentirc/architecture-overview.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/agentirc/bots.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/agentirc/events.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/agentirc/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/agentirc/why-agentirc.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/agent-lifecycle.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/choose-a-harness.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/features.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/mental-model.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/operate.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/patterns.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/quickstart.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/reflective-development.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/vision-patterns-index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/vision.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/culture/what-is-culture.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/architecture/layers.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/architecture/threads.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/cli/afi.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/cli/commands.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/cli/devex.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/cli/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/console.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/harnesses/acp.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/harnesses/claude.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/harnesses/codex.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/harnesses/copilot.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/harnesses/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/server/architecture.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/server/deployment.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/server/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/reference/server/security.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/resources/positioning.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/concepts/federation.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/concepts/harnesses.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/concepts/humans-and-agents.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/concepts/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/concepts/persistence.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/concepts/rooms.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/demos/magic-demo.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/guides/first-session.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/guides/index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/guides/join-as-human.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/guides/local-setup.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/guides/multi-machine.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/shared/use-cases-index.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-15-mesh-events.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-18-culture-dev-positioning.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/plans/2026-04-22-agex-integration.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-15-mesh-events-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-17-sites-repositioning-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-18-culture-dev-positioning-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/docs/superpowers/specs/2026-04-22-agex-integration-design.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/favicon.ico +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/culture.yaml +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/robots.txt +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/sitemap-agentirc.html +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/sitemap-main.html +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/sonar-project.properties +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/__init__.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_archive.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_bot.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_bot_config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_bot_config_fires_event_toplevel.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_channel.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_channel_cli.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_cli_afi.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_cli_devex.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_cli_introspect.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_cli_passthrough.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_connection.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_chat_markdown.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_commands.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_connection.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_fixes_224_227.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_icons.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_integration.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_console_status.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_credentials.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_culture_config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_display.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_basic.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_bot_chain.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_bot_trigger.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_cap_fallback.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_catalog.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_federation.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_history.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_lifecycle.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_events_reserved_nick.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_federation.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_filter_dsl.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_history.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_http_listener.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_irc_transport_tags.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_learn_prompt.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_manifest_config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_mention_warning.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_mentions.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_mesh_readiness.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_message.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_message_tags.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_migrate_cli.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_modes.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_persistence.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_persistence_timeout.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_pidfile.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_register_cli.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_rooms.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_skill_docs.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_skills.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_supervisor.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_template_engine.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_threads.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_webhook.py +0 -0
- {agentirc_cli-8.1.0 → agentirc_cli-8.2.0}/tests/test_welcome_bot.py +0 -0
|
@@ -4,6 +4,17 @@ 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.2.0] - 2026-04-24
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- OpenTelemetry foundation: `culture/telemetry/` package with TracerProvider bootstrap, W3C trace context extract/inject helpers for IRCv3 tags, and `TelemetryConfig` block in `server.yaml`.
|
|
12
|
+
- Protocol extension: `culture.dev/traceparent` and `culture.dev/tracestate` IRCv3 tags (`culture/protocol/extensions/tracing.md`).
|
|
13
|
+
- Server-side tracing: `IRCd.emit_event`, `Client._dispatch`, `Client._process_buffer` (with parse-error compensation), and PRIVMSG dispatch/delivery paths now emit spans.
|
|
14
|
+
- Outbound traceparent injection on `Client.send` and `Client.send_raw` when a span is active.
|
|
15
|
+
- Operator docs at `docs/agentirc/telemetry.md` and starter collector config at `docs/agentirc/otelcol-template.yaml`; `docs/reference/server/config.md` documents the new `telemetry` block.
|
|
16
|
+
- Dependencies: `opentelemetry-api`, `opentelemetry-sdk`, `opentelemetry-exporter-otlp-proto-grpc`, `opentelemetry-semantic-conventions`.
|
|
17
|
+
|
|
7
18
|
## [8.1.0] - 2026-04-23
|
|
8
19
|
|
|
9
20
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentirc-cli
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.2.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
|
|
@@ -18,6 +18,10 @@ Requires-Dist: aiohttp>=3.9
|
|
|
18
18
|
Requires-Dist: anthropic>=0.40
|
|
19
19
|
Requires-Dist: claude-agent-sdk>=0.1
|
|
20
20
|
Requires-Dist: mistune>=3.0
|
|
21
|
+
Requires-Dist: opentelemetry-api>=1.25
|
|
22
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.25
|
|
23
|
+
Requires-Dist: opentelemetry-sdk>=1.25
|
|
24
|
+
Requires-Dist: opentelemetry-semantic-conventions>=0.46b0
|
|
21
25
|
Requires-Dist: pyyaml>=6.0
|
|
22
26
|
Requires-Dist: textual>=1.0
|
|
23
27
|
Provides-Extra: copilot
|
|
@@ -36,6 +36,8 @@ aux_links:
|
|
|
36
36
|
- "/agentirc/"
|
|
37
37
|
"Agent Experience":
|
|
38
38
|
- "https://culture.dev/agex/"
|
|
39
|
+
"Agent First Interop":
|
|
40
|
+
- "https://culture.dev/afi/"
|
|
39
41
|
"Citation CLI":
|
|
40
42
|
- "/citation-cli/"
|
|
41
43
|
"GitHub":
|
|
@@ -49,5 +51,6 @@ footer_content: >-
|
|
|
49
51
|
Inspectable CLI via <a href="/reference/cli/devex/">culture devex</a>
|
|
50
52
|
(explain / overview / learn at every level).
|
|
51
53
|
Agent Experience via <a href="https://culture.dev/agex/">agex-cli</a>.
|
|
54
|
+
Agent First Interop via <a href="https://culture.dev/afi/">afi-cli</a>.
|
|
52
55
|
Code distribution via <a href="/citation-cli/">Citation CLI</a>.
|
|
53
56
|
Source on <a href="https://github.com/agentculture/culture">GitHub</a>.
|
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
<link rel="icon" type="image/png" sizes="16x16" href="{{ '/assets/images/favicon-16x16.png' | relative_url }}">
|
|
5
5
|
<link rel="apple-touch-icon" sizes="180x180" href="{{ '/assets/images/apple-touch-icon.png' | relative_url }}">
|
|
6
6
|
<link rel="related" href="{{ site.data.sites.agex }}" title="Agent Experience">
|
|
7
|
+
<link rel="related" href="{{ site.data.sites.afi }}" title="Agent First Interop">
|
|
7
8
|
<link rel="related" href="{{ site.data.sites.citation_cli }}" title="Citation CLI">
|
|
@@ -6,12 +6,59 @@ import logging
|
|
|
6
6
|
import re
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
|
+
from opentelemetry import trace as _otel_trace
|
|
10
|
+
from opentelemetry.trace import NonRecordingSpan, SpanContext, TraceFlags
|
|
11
|
+
from opentelemetry.trace.propagation import set_span_in_context
|
|
12
|
+
|
|
9
13
|
from culture.agentirc.channel import Channel
|
|
10
14
|
from culture.agentirc.skill import Event, EventType
|
|
11
15
|
from culture.aio import maybe_await
|
|
12
16
|
from culture.constants import SYSTEM_USER_PREFIX
|
|
13
17
|
from culture.protocol import replies
|
|
14
18
|
from culture.protocol.message import Message
|
|
19
|
+
from culture.telemetry.context import TRACEPARENT_TAG as _TP_TAG_NAME
|
|
20
|
+
from culture.telemetry.context import (
|
|
21
|
+
extract_traceparent_from_tags,
|
|
22
|
+
)
|
|
23
|
+
from culture.telemetry.context import inject_traceparent as _inject_traceparent
|
|
24
|
+
|
|
25
|
+
# OTEL instrumentation name (must match `_CULTURE_TRACER_NAME` in
|
|
26
|
+
# culture/telemetry/tracing.py so trace consumers see one consistent value).
|
|
27
|
+
_TRACER_NAME = "culture.agentirc"
|
|
28
|
+
# Span attribute keys for PRIVMSG body capture, defined once so a future
|
|
29
|
+
# rename / sanitization layer has one edit point.
|
|
30
|
+
_ATTR_BODY = "irc.message.body"
|
|
31
|
+
_ATTR_SIZE = "irc.message.size"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _context_from_traceparent(tp: str):
|
|
35
|
+
"""Build an OTEL context whose current span is a NonRecordingSpan
|
|
36
|
+
synthesized from a W3C traceparent string. The `_dispatch` span we
|
|
37
|
+
start next will be a child of this context."""
|
|
38
|
+
# Format: 00-<trace-id>-<parent-id>-<flags>
|
|
39
|
+
_, trace_hex, parent_hex, flags_hex = tp.split("-")
|
|
40
|
+
span_ctx = SpanContext(
|
|
41
|
+
trace_id=int(trace_hex, 16),
|
|
42
|
+
span_id=int(parent_hex, 16),
|
|
43
|
+
is_remote=True,
|
|
44
|
+
trace_flags=TraceFlags(int(flags_hex, 16)),
|
|
45
|
+
)
|
|
46
|
+
return set_span_in_context(NonRecordingSpan(span_ctx))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _current_traceparent() -> str | None:
|
|
50
|
+
"""Return the W3C traceparent for the currently-active span, or None
|
|
51
|
+
if no span is recording (no-op tracer / sampler dropped).
|
|
52
|
+
"""
|
|
53
|
+
span = _otel_trace.get_current_span()
|
|
54
|
+
ctx = span.get_span_context()
|
|
55
|
+
if not ctx.is_valid:
|
|
56
|
+
return None
|
|
57
|
+
return (
|
|
58
|
+
f"00-{format(ctx.trace_id, '032x')}-{format(ctx.span_id, '016x')}"
|
|
59
|
+
f"-{format(int(ctx.trace_flags), '02x')}"
|
|
60
|
+
)
|
|
61
|
+
|
|
15
62
|
|
|
16
63
|
if TYPE_CHECKING:
|
|
17
64
|
from culture.agentirc.ircd import IRCd
|
|
@@ -45,6 +92,14 @@ class Client:
|
|
|
45
92
|
return f"{self.nick}!{self.user}@{self.host}"
|
|
46
93
|
|
|
47
94
|
async def send(self, message: Message) -> None:
|
|
95
|
+
# Only inject trace context for clients that negotiated IRCv3
|
|
96
|
+
# message-tags; otherwise older clients would see an unexpected @-tag
|
|
97
|
+
# block and `send_tagged`'s tag-stripping for non-capable clients
|
|
98
|
+
# would be undone here.
|
|
99
|
+
if "message-tags" in self.caps:
|
|
100
|
+
tp = _current_traceparent()
|
|
101
|
+
if tp is not None:
|
|
102
|
+
_inject_traceparent(message, traceparent=tp, tracestate=None)
|
|
48
103
|
try:
|
|
49
104
|
self.writer.write(message.format().encode("utf-8"))
|
|
50
105
|
await self.writer.drain()
|
|
@@ -55,7 +110,15 @@ class Client:
|
|
|
55
110
|
"""Write a pre-formatted IRC line to the client socket.
|
|
56
111
|
|
|
57
112
|
Appends CRLF internally, matching ServerLink.send_raw convention.
|
|
113
|
+
Injects `culture.dev/traceparent` as an IRCv3 tag when a span is active
|
|
114
|
+
AND the client negotiated the `message-tags` capability.
|
|
58
115
|
"""
|
|
116
|
+
if "message-tags" in self.caps:
|
|
117
|
+
tp = _current_traceparent()
|
|
118
|
+
if tp is not None:
|
|
119
|
+
# send_raw takes a pre-formatted line without an existing tag
|
|
120
|
+
# block; prefix a fresh @tag.
|
|
121
|
+
line = f"@{_TP_TAG_NAME}={tp} {line}"
|
|
59
122
|
try:
|
|
60
123
|
self.writer.write(f"{line}\r\n".encode("utf-8"))
|
|
61
124
|
await self.writer.drain()
|
|
@@ -84,13 +147,28 @@ class Client:
|
|
|
84
147
|
|
|
85
148
|
async def _process_buffer(self, buffer: str) -> str:
|
|
86
149
|
"""Parse and dispatch all complete lines from buffer, return remainder."""
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
150
|
+
# Per-call get_tracer: test fixture swaps provider between tests.
|
|
151
|
+
with _otel_trace.get_tracer(_TRACER_NAME).start_as_current_span(
|
|
152
|
+
"irc.client.process_buffer"
|
|
153
|
+
) as span:
|
|
154
|
+
while "\n" in buffer:
|
|
155
|
+
line, buffer = buffer.split("\n", 1)
|
|
156
|
+
if not line.strip():
|
|
157
|
+
continue
|
|
158
|
+
try:
|
|
159
|
+
msg = Message.parse(line)
|
|
160
|
+
except Exception as exc: # noqa: BLE001 -- widen for any parser failure
|
|
161
|
+
span.add_event(
|
|
162
|
+
"irc.parse_error",
|
|
163
|
+
attributes={
|
|
164
|
+
"line_preview": line[:64],
|
|
165
|
+
"error": type(exc).__name__,
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
continue
|
|
91
169
|
if msg.command:
|
|
92
170
|
await self._dispatch(msg)
|
|
93
|
-
|
|
171
|
+
return buffer
|
|
94
172
|
|
|
95
173
|
async def handle(self, initial_msg: str | None = None) -> None:
|
|
96
174
|
buffer = ""
|
|
@@ -110,20 +188,41 @@ class Client:
|
|
|
110
188
|
buffer = await self._process_buffer(buffer)
|
|
111
189
|
|
|
112
190
|
async def _dispatch(self, msg: Message) -> None:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
191
|
+
extract = extract_traceparent_from_tags(msg, peer=None)
|
|
192
|
+
parent_ctx = None
|
|
193
|
+
if extract.status == "valid":
|
|
194
|
+
parent_ctx = _context_from_traceparent(extract.traceparent)
|
|
195
|
+
|
|
196
|
+
attrs = {
|
|
197
|
+
"irc.command": msg.command,
|
|
198
|
+
"irc.prefix_nick": (msg.prefix.split("!")[0] if msg.prefix else ""),
|
|
199
|
+
"culture.trace.origin": "local" if extract.status == "missing" else "remote",
|
|
200
|
+
}
|
|
201
|
+
if extract.status in ("malformed", "too_long"):
|
|
202
|
+
attrs["culture.trace.dropped_reason"] = extract.status
|
|
203
|
+
|
|
204
|
+
# Per-call get_tracer: test fixture swaps provider between tests.
|
|
205
|
+
with _otel_trace.get_tracer(_TRACER_NAME).start_as_current_span(
|
|
206
|
+
f"irc.command.{msg.command}",
|
|
207
|
+
context=parent_ctx,
|
|
208
|
+
attributes=attrs,
|
|
209
|
+
):
|
|
210
|
+
handler = getattr(self, f"_handle_{msg.command.lower()}", None)
|
|
211
|
+
if handler:
|
|
212
|
+
await maybe_await(handler(msg))
|
|
125
213
|
else:
|
|
126
|
-
|
|
214
|
+
skill = self.server.get_skill_for_command(msg.command)
|
|
215
|
+
if skill and self._registered:
|
|
216
|
+
try:
|
|
217
|
+
await skill.on_command(self, msg)
|
|
218
|
+
except Exception:
|
|
219
|
+
logging.getLogger(__name__).exception(
|
|
220
|
+
"Skill %s failed on command %s", skill.name, msg.command
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
await self.send_numeric(
|
|
224
|
+
replies.ERR_UNKNOWNCOMMAND, msg.command, "Unknown command"
|
|
225
|
+
)
|
|
127
226
|
|
|
128
227
|
async def _handle_ping(self, msg: Message) -> None:
|
|
129
228
|
token = msg.params[0] if msg.params else ""
|
|
@@ -615,46 +714,64 @@ class Client:
|
|
|
615
714
|
await self.send_numeric(replies.RPL_UMODEIS, mode_str)
|
|
616
715
|
|
|
617
716
|
async def _send_to_channel(self, channel, target, relay, text, is_notice):
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
717
|
+
with _otel_trace.get_tracer(_TRACER_NAME).start_as_current_span(
|
|
718
|
+
"irc.privmsg.deliver.channel",
|
|
719
|
+
attributes={
|
|
720
|
+
"irc.channel": target,
|
|
721
|
+
_ATTR_BODY: text,
|
|
722
|
+
_ATTR_SIZE: len(text),
|
|
723
|
+
"irc.notice": is_notice,
|
|
724
|
+
},
|
|
725
|
+
):
|
|
726
|
+
for member in list(channel.members):
|
|
727
|
+
if member is not self:
|
|
728
|
+
await member.send(relay)
|
|
729
|
+
event_data = {"text": text}
|
|
730
|
+
if is_notice:
|
|
731
|
+
event_data["notice"] = True
|
|
732
|
+
await self.server.emit_event(
|
|
733
|
+
Event(
|
|
734
|
+
type=EventType.MESSAGE,
|
|
735
|
+
channel=target,
|
|
736
|
+
nick=self.nick,
|
|
737
|
+
data=event_data,
|
|
738
|
+
)
|
|
630
739
|
)
|
|
631
|
-
)
|
|
632
740
|
|
|
633
741
|
async def _send_to_client(self, target, relay, text, is_notice):
|
|
634
742
|
from culture.agentirc.remote_client import RemoteClient
|
|
635
743
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
744
|
+
with _otel_trace.get_tracer(_TRACER_NAME).start_as_current_span(
|
|
745
|
+
"irc.privmsg.deliver.dm",
|
|
746
|
+
attributes={
|
|
747
|
+
"irc.target.nick": target,
|
|
748
|
+
_ATTR_BODY: text,
|
|
749
|
+
_ATTR_SIZE: len(text),
|
|
750
|
+
"irc.notice": is_notice,
|
|
751
|
+
},
|
|
752
|
+
):
|
|
753
|
+
recipient = self.server.get_client(target)
|
|
754
|
+
if not recipient:
|
|
755
|
+
return False
|
|
756
|
+
if isinstance(recipient, RemoteClient):
|
|
757
|
+
s2s_cmd = "SNOTICE" if is_notice else "SMSG"
|
|
758
|
+
await recipient.link.send_raw(
|
|
759
|
+
f":{self.server.config.name} {s2s_cmd} {target} {self.nick} :{text}"
|
|
760
|
+
)
|
|
761
|
+
else:
|
|
762
|
+
await recipient.send(relay)
|
|
763
|
+
event_data = {"text": text, "target": target}
|
|
764
|
+
if is_notice:
|
|
765
|
+
event_data["notice"] = True
|
|
766
|
+
await self.server.emit_event(
|
|
767
|
+
Event(
|
|
768
|
+
type=EventType.MESSAGE,
|
|
769
|
+
channel=None,
|
|
770
|
+
nick=self.nick,
|
|
771
|
+
data=event_data,
|
|
772
|
+
)
|
|
655
773
|
)
|
|
656
|
-
|
|
657
|
-
return True
|
|
774
|
+
return True
|
|
658
775
|
|
|
659
776
|
async def _handle_privmsg(self, msg: Message) -> None:
|
|
660
777
|
if len(msg.params) < 2:
|
|
@@ -665,28 +782,37 @@ class Client:
|
|
|
665
782
|
|
|
666
783
|
target = msg.params[0]
|
|
667
784
|
text = msg.params[1]
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
)
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
785
|
+
# Per-call get_tracer: test fixture swaps provider between tests.
|
|
786
|
+
with _otel_trace.get_tracer(_TRACER_NAME).start_as_current_span(
|
|
787
|
+
"irc.privmsg.dispatch",
|
|
788
|
+
attributes={
|
|
789
|
+
"irc.target": target,
|
|
790
|
+
_ATTR_BODY: text,
|
|
791
|
+
_ATTR_SIZE: len(text),
|
|
792
|
+
},
|
|
793
|
+
):
|
|
794
|
+
relay = Message(prefix=self.prefix, command="PRIVMSG", params=[target, text])
|
|
795
|
+
|
|
796
|
+
if target.startswith("#"):
|
|
797
|
+
channel = self.server.channels.get(target)
|
|
798
|
+
if not channel:
|
|
799
|
+
await self.send_numeric(
|
|
800
|
+
replies.ERR_NOSUCHCHANNEL, target, replies.MSG_NOSUCHCHANNEL
|
|
801
|
+
)
|
|
802
|
+
return
|
|
803
|
+
if self not in channel.members:
|
|
804
|
+
await self.send_numeric(
|
|
805
|
+
replies.ERR_CANNOTSENDTOCHAN, target, "Cannot send to channel"
|
|
806
|
+
)
|
|
807
|
+
return
|
|
808
|
+
await self._send_to_channel(channel, target, relay, text, False)
|
|
809
|
+
await self._notify_mentions(target, text)
|
|
810
|
+
else:
|
|
811
|
+
found = await self._send_to_client(target, relay, text, False)
|
|
812
|
+
if not found:
|
|
813
|
+
await self.send_numeric(replies.ERR_NOSUCHNICK, target, replies.MSG_NOSUCHNICK)
|
|
814
|
+
return
|
|
815
|
+
await self._notify_mentions(None, text)
|
|
690
816
|
|
|
691
817
|
async def _notify_mentions(self, channel_name: str | None, text: str) -> None:
|
|
692
818
|
from culture.agentirc.remote_client import RemoteClient
|
|
@@ -12,6 +12,20 @@ class LinkConfig:
|
|
|
12
12
|
trust: str = "full" # "full" or "restricted"
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
@dataclass
|
|
16
|
+
class TelemetryConfig:
|
|
17
|
+
"""OpenTelemetry settings. Mirrors server.yaml `telemetry:` block."""
|
|
18
|
+
|
|
19
|
+
enabled: bool = False
|
|
20
|
+
service_name: str = "culture.agentirc"
|
|
21
|
+
otlp_endpoint: str = "http://localhost:4317"
|
|
22
|
+
otlp_protocol: str = "grpc" # grpc | http/protobuf (only grpc supported initially)
|
|
23
|
+
otlp_timeout_ms: int = 5000
|
|
24
|
+
otlp_compression: str = "gzip" # gzip | none
|
|
25
|
+
traces_enabled: bool = True
|
|
26
|
+
traces_sampler: str = "parentbased_always_on"
|
|
27
|
+
|
|
28
|
+
|
|
15
29
|
@dataclass
|
|
16
30
|
class ServerConfig:
|
|
17
31
|
"""Configuration for a culture server instance."""
|
|
@@ -23,3 +37,4 @@ class ServerConfig:
|
|
|
23
37
|
data_dir: str = ""
|
|
24
38
|
links: list[LinkConfig] = field(default_factory=list)
|
|
25
39
|
system_bots: dict = field(default_factory=dict)
|
|
40
|
+
telemetry: TelemetryConfig = field(default_factory=TelemetryConfig)
|
|
@@ -8,6 +8,8 @@ import logging
|
|
|
8
8
|
from collections import deque
|
|
9
9
|
from typing import TYPE_CHECKING
|
|
10
10
|
|
|
11
|
+
from opentelemetry import trace as _otel_trace
|
|
12
|
+
|
|
11
13
|
from culture.agentirc.channel import Channel
|
|
12
14
|
from culture.agentirc.config import ServerConfig
|
|
13
15
|
from culture.agentirc.events import NO_SURFACE_EVENT_TYPES, render_event
|
|
@@ -33,7 +35,10 @@ class IRCd:
|
|
|
33
35
|
"""The culture IRC server."""
|
|
34
36
|
|
|
35
37
|
def __init__(self, config: ServerConfig):
|
|
38
|
+
from culture.telemetry import init_telemetry
|
|
39
|
+
|
|
36
40
|
self.config = config
|
|
41
|
+
self.tracer = init_telemetry(config)
|
|
37
42
|
self.clients: dict[str, Client | VirtualClient] = {} # nick -> Client
|
|
38
43
|
self.channels: dict[str, Channel] = {} # name -> Channel
|
|
39
44
|
self.skills: list[Skill] = []
|
|
@@ -166,36 +171,60 @@ class IRCd:
|
|
|
166
171
|
self._seq += 1
|
|
167
172
|
return self._seq
|
|
168
173
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
@staticmethod
|
|
175
|
+
def _build_event_span_attrs(event: Event, origin_tag: str | None) -> dict[str, str]:
|
|
176
|
+
# event.type may be an EventType enum OR a plain string — federated
|
|
177
|
+
# events forward unknown types verbatim (see _parse_event_type in
|
|
178
|
+
# server_link.py).
|
|
179
|
+
event_type_str = event.type.value if hasattr(event.type, "value") else str(event.type)
|
|
180
|
+
attrs: dict[str, str] = {
|
|
181
|
+
"event.type": event_type_str,
|
|
182
|
+
"event.origin": "federated" if origin_tag else "local",
|
|
183
|
+
}
|
|
184
|
+
if event.channel:
|
|
185
|
+
attrs["event.channel"] = event.channel
|
|
186
|
+
if origin_tag:
|
|
187
|
+
attrs["culture.federation.peer"] = origin_tag
|
|
188
|
+
return attrs
|
|
173
189
|
|
|
174
|
-
|
|
190
|
+
async def _run_skill_hooks(self, event: Event) -> None:
|
|
175
191
|
for skill in self.skills:
|
|
176
192
|
try:
|
|
177
193
|
await skill.on_event(event)
|
|
178
194
|
except Exception:
|
|
179
195
|
logger.exception("Skill %s failed on event %s", skill.name, event.type)
|
|
180
196
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if not event.data.get("_origin"):
|
|
184
|
-
for peer_name, link in list(self.links.items()):
|
|
185
|
-
try:
|
|
186
|
-
await link.relay_event(event)
|
|
187
|
-
except Exception:
|
|
188
|
-
logger.exception("Failed to relay event to %s", peer_name)
|
|
189
|
-
|
|
190
|
-
# 4) Dispatch to event-triggered bots.
|
|
191
|
-
if self.bot_manager is not None:
|
|
197
|
+
async def _relay_to_peers(self, event: Event) -> None:
|
|
198
|
+
for peer_name, link in list(self.links.items()):
|
|
192
199
|
try:
|
|
193
|
-
await
|
|
200
|
+
await link.relay_event(event)
|
|
194
201
|
except Exception:
|
|
195
|
-
logger.exception("
|
|
202
|
+
logger.exception("Failed to relay event to %s", peer_name)
|
|
203
|
+
|
|
204
|
+
async def _dispatch_to_bots(self, event: Event) -> None:
|
|
205
|
+
if self.bot_manager is None:
|
|
206
|
+
return
|
|
207
|
+
try:
|
|
208
|
+
await self.bot_manager.on_event(event)
|
|
209
|
+
except Exception:
|
|
210
|
+
logger.exception("bot_manager.on_event failed")
|
|
196
211
|
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
async def emit_event(self, event: Event) -> None:
|
|
213
|
+
origin_tag = event.data.get("_origin")
|
|
214
|
+
attrs = self._build_event_span_attrs(event, origin_tag)
|
|
215
|
+
# Per-call get_tracer: the `tracing_exporter` test fixture swaps the
|
|
216
|
+
# global provider between tests; a cached Tracer would bind to the
|
|
217
|
+
# first test's provider and stop delivering to later ones.
|
|
218
|
+
with _otel_trace.get_tracer("culture.agentirc").start_as_current_span(
|
|
219
|
+
"irc.event.emit", attributes=attrs
|
|
220
|
+
):
|
|
221
|
+
seq = self.next_seq()
|
|
222
|
+
self._event_log.append((seq, event))
|
|
223
|
+
await self._run_skill_hooks(event)
|
|
224
|
+
if not origin_tag:
|
|
225
|
+
await self._relay_to_peers(event)
|
|
226
|
+
await self._dispatch_to_bots(event)
|
|
227
|
+
await self._surface_event_privmsg(event)
|
|
199
228
|
|
|
200
229
|
_NO_SURFACE_TYPES = NO_SURFACE_EVENT_TYPES
|
|
201
230
|
|
|
@@ -15,6 +15,8 @@ from pathlib import Path
|
|
|
15
15
|
|
|
16
16
|
import yaml
|
|
17
17
|
|
|
18
|
+
from culture.agentirc.config import TelemetryConfig
|
|
19
|
+
|
|
18
20
|
logger = logging.getLogger("culture")
|
|
19
21
|
|
|
20
22
|
|
|
@@ -98,6 +100,7 @@ class ServerConfig:
|
|
|
98
100
|
server: ServerConnConfig = field(default_factory=ServerConnConfig)
|
|
99
101
|
supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
|
|
100
102
|
webhooks: WebhookConfig = field(default_factory=WebhookConfig)
|
|
103
|
+
telemetry: TelemetryConfig = field(default_factory=TelemetryConfig)
|
|
101
104
|
buffer_size: int = 500
|
|
102
105
|
poll_interval: int = 60
|
|
103
106
|
sleep_start: str = "23:00"
|
|
@@ -202,6 +205,7 @@ def load_server_config(path: str | Path) -> ServerConfig:
|
|
|
202
205
|
server = ServerConnConfig(**raw.get("server", {}))
|
|
203
206
|
supervisor = SupervisorConfig(**raw.get("supervisor", {}))
|
|
204
207
|
webhooks = WebhookConfig(**raw.get("webhooks", {}))
|
|
208
|
+
telemetry = TelemetryConfig(**raw.get("telemetry", {}))
|
|
205
209
|
|
|
206
210
|
manifest = raw.get("agents") or {}
|
|
207
211
|
if not isinstance(manifest, dict):
|
|
@@ -211,6 +215,7 @@ def load_server_config(path: str | Path) -> ServerConfig:
|
|
|
211
215
|
server=server,
|
|
212
216
|
supervisor=supervisor,
|
|
213
217
|
webhooks=webhooks,
|
|
218
|
+
telemetry=telemetry,
|
|
214
219
|
buffer_size=raw.get("buffer_size", 500),
|
|
215
220
|
poll_interval=raw.get("poll_interval", 60),
|
|
216
221
|
sleep_start=raw.get("sleep_start", "23:00"),
|
|
@@ -258,6 +263,7 @@ def _load_legacy_config(path: str | Path) -> ServerConfig:
|
|
|
258
263
|
server = ServerConnConfig(**raw.get("server", {}))
|
|
259
264
|
supervisor = SupervisorConfig(**raw.get("supervisor", {}))
|
|
260
265
|
webhooks = WebhookConfig(**raw.get("webhooks", {}))
|
|
266
|
+
telemetry = TelemetryConfig(**raw.get("telemetry", {}))
|
|
261
267
|
|
|
262
268
|
agents = []
|
|
263
269
|
known = _KNOWN_AGENT_FIELDS | {"nick", "directory"}
|
|
@@ -278,6 +284,7 @@ def _load_legacy_config(path: str | Path) -> ServerConfig:
|
|
|
278
284
|
server=server,
|
|
279
285
|
supervisor=supervisor,
|
|
280
286
|
webhooks=webhooks,
|
|
287
|
+
telemetry=telemetry,
|
|
281
288
|
buffer_size=raw.get("buffer_size", 500),
|
|
282
289
|
poll_interval=raw.get("poll_interval", 60),
|
|
283
290
|
sleep_start=raw.get("sleep_start", "23:00"),
|
|
@@ -354,10 +361,12 @@ def migrate_legacy_to_manifest(path: str | Path) -> ServerConfig:
|
|
|
354
361
|
server = ServerConnConfig(**raw.get("server", {}))
|
|
355
362
|
supervisor = SupervisorConfig(**raw.get("supervisor", {}))
|
|
356
363
|
webhooks = WebhookConfig(**raw.get("webhooks", {}))
|
|
364
|
+
telemetry = TelemetryConfig(**raw.get("telemetry", {}))
|
|
357
365
|
config = ServerConfig(
|
|
358
366
|
server=server,
|
|
359
367
|
supervisor=supervisor,
|
|
360
368
|
webhooks=webhooks,
|
|
369
|
+
telemetry=telemetry,
|
|
361
370
|
buffer_size=raw.get("buffer_size", 500),
|
|
362
371
|
poll_interval=raw.get("poll_interval", 60),
|
|
363
372
|
sleep_start=raw.get("sleep_start", "23:00"),
|
|
@@ -516,6 +525,7 @@ def save_server_config(path: str | Path, config: ServerConfig) -> None:
|
|
|
516
525
|
"server": asdict(config.server),
|
|
517
526
|
"supervisor": asdict(config.supervisor),
|
|
518
527
|
"webhooks": asdict(config.webhooks),
|
|
528
|
+
"telemetry": asdict(config.telemetry),
|
|
519
529
|
"buffer_size": config.buffer_size,
|
|
520
530
|
"poll_interval": config.poll_interval,
|
|
521
531
|
"sleep_start": config.sleep_start,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Extension: W3C Trace Context over IRC
|
|
2
|
+
|
|
3
|
+
**Tags:**
|
|
4
|
+
|
|
5
|
+
- `culture.dev/traceparent` — the W3C `traceparent` header value, verbatim (55 chars, format `00-<32-hex-trace-id>-<16-hex-parent-id>-<2-hex-flags>`). MUST match the W3C regex. If malformed, servers MUST silently drop the tag (treat as absent).
|
|
6
|
+
- `culture.dev/tracestate` — the W3C `tracestate` header value (comma-separated `key=value` list, vendor-specific). Optional. MUST be ≤ 512 bytes after IRCv3 unescape. If longer, servers MUST drop the tag.
|
|
7
|
+
|
|
8
|
+
**Scope:** outbound on every client-originated message when a span context is active (PRIVMSG, NOTICE, JOIN, PART, KICK, MODE, SEVENT, SMSG, SNOTICE). On federation relay, the tags are re-injected from the *current* server's span context — they are not copied verbatim from the received message. This produces a parent-per-hop span tree instead of collapsing all hops into one.
|
|
9
|
+
|
|
10
|
+
**Inbound handling.** On every ingress (local client `_dispatch` and S2S `_dispatch`):
|
|
11
|
+
|
|
12
|
+
1. Tag absent → start a new root span. Attribute: `culture.trace.origin=local`.
|
|
13
|
+
2. Tag present and valid → start a child span linked to the extracted context. Attributes: `culture.trace.origin=remote`, `culture.federation.peer=<peer>`.
|
|
14
|
+
3. Tag present but malformed or over the length cap → drop the tag, start a new root span. Attributes: `culture.trace.origin=remote`, `culture.trace.dropped_reason=malformed|too_long`, `culture.federation.peer=<peer>`. Log a rate-limited warning. Increment `culture.trace.inbound{result=malformed|too_long, peer=<peer>}`.
|
|
15
|
+
|
|
16
|
+
**Length caps** (hard-coded, not configurable):
|
|
17
|
+
|
|
18
|
+
- `traceparent`: exactly 55 characters.
|
|
19
|
+
- `tracestate`: ≤ 512 bytes post-unescape.
|
|
20
|
+
|
|
21
|
+
**Non-goal.** Trace context is not authenticated. Federation peer trust is the authorization mechanism; tracing is observability only. An operator who cares about forged trace IDs from upstream peers should rely on peer trust, not on tag validation.
|
|
22
|
+
|
|
23
|
+
**Compat.** Additive IRCv3 tag. Peers that don't recognize the tag pass it through on relay (standard IRCv3 tag behavior). No wire version bump. Project version bumps as a minor per `CLAUDE.md`.
|
|
24
|
+
|
|
25
|
+
**Example.**
|
|
26
|
+
|
|
27
|
+
User on `spark` sends:
|
|
28
|
+
|
|
29
|
+
@culture.dev/traceparent=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 PRIVMSG #general :hi
|
|
30
|
+
|
|
31
|
+
Server `spark` starts span `irc.command.PRIVMSG` as a child of the extracted context, relays the event via SEVENT to `thor`, re-injecting its own span's traceparent. Server `thor` sees trace ID `4bf92f35…4736` and starts another child span — the collector stitches both spans into one trace.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""OpenTelemetry integration for Culture.
|
|
2
|
+
|
|
3
|
+
Public surface re-exported here; call sites import from `culture.telemetry`.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from culture.telemetry.context import (
|
|
7
|
+
TRACEPARENT_TAG,
|
|
8
|
+
TRACESTATE_TAG,
|
|
9
|
+
ExtractResult,
|
|
10
|
+
extract_traceparent_from_tags,
|
|
11
|
+
inject_traceparent,
|
|
12
|
+
)
|
|
13
|
+
from culture.telemetry.tracing import init_telemetry
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"ExtractResult",
|
|
17
|
+
"TRACEPARENT_TAG",
|
|
18
|
+
"TRACESTATE_TAG",
|
|
19
|
+
"extract_traceparent_from_tags",
|
|
20
|
+
"init_telemetry",
|
|
21
|
+
"inject_traceparent",
|
|
22
|
+
]
|