agentirc-cli 8.4.0__tar.gz → 8.5.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.4.0 → agentirc_cli-8.5.0}/CHANGELOG.md +15 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/PKG-INFO +1 -1
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/client.py +45 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/config.py +7 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/ircd.py +35 -2
- agentirc_cli-8.5.0/culture/protocol/extensions/audit.md +109 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/telemetry/__init__.py +5 -0
- agentirc_cli-8.5.0/culture/telemetry/audit.py +380 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/telemetry/metrics.py +12 -0
- agentirc_cli-8.5.0/docs/agentirc/audit.md +154 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/agentirc/telemetry.md +30 -4
- agentirc_cli-8.5.0/docs/superpowers/plans/2026-04-27-otel-audit.md +247 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/pyproject.toml +1 -1
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/conftest.py +22 -2
- agentirc_cli-8.5.0/tests/telemetry/test_audit_emit.py +145 -0
- agentirc_cli-8.5.0/tests/telemetry/test_audit_federation.py +132 -0
- agentirc_cli-8.5.0/tests/telemetry/test_audit_lifecycle.py +69 -0
- agentirc_cli-8.5.0/tests/telemetry/test_audit_module.py +306 -0
- agentirc_cli-8.5.0/tests/telemetry/test_audit_parse_error.py +177 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/uv.lock +1 -1
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.claude/agents/doc-test-alignment.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.flake8 +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.github/workflows/docs-check.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.gitignore +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.pr_agent.toml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/.pylintrc +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/CLAUDE.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/Gemfile +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/Gemfile.lock +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/LICENSE +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/README.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/SECURITY.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/_config.base.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/_config.culture.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/_data/sites.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/_includes/head_custom.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/_sass/color_schemes/dark-terminal.scss +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/assets/images/IMG_3183.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/assets/images/apple-touch-icon.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/assets/images/favicon-16x16.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/assets/images/favicon-32x32.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/assets/images/favicon.ico +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/assets/images/og-agentirc.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/assets/images/og-culture.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/__main__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/CLAUDE.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/__main__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/channel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/docs/agentirc-architecture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/docs/agentirc-features.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/docs/agentirc-skill.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/docs/agentirc.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/events.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/history_store.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/remote_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/room_store.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/rooms_util.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/server_link.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/skill.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/skills/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/skills/history.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/skills/icon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/skills/rooms.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/skills/threads.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/agentirc/thread_store.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/aio.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/bot.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/filter_dsl.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/system/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/system/welcome/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/system/welcome/bot.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/system/welcome/handler.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/_passthrough.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/afi.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/agent.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/bot.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/channel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/devex.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/introspect.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/mesh.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/shared/display.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/cli/skills.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/culture.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/culture.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/culture.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/culture.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/app.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/commands.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/status.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/constants.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/credentials.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/formatting.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/learn_prompt.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/mesh_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/observer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/overview/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/overview/collector.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/overview/model.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/overview/web/style.css +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/persistence.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/pidfile.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/commands.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/events.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/extensions/tracing.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/message.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/protocol/replies.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/telemetry/context.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/culture/telemetry/tracing.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/README.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/agentirc/architecture-overview.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/agentirc/bots.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/agentirc/events.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/agentirc/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/agentirc/otelcol-template.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/agentirc/why-agentirc.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/agent-lifecycle.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/choose-a-harness.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/features.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/mental-model.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/operate.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/patterns.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/quickstart.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/reflective-development.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/vision-patterns-index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/vision.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/culture/what-is-culture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/architecture/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/architecture/layers.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/architecture/subsites.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/architecture/threads.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/cli/afi.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/cli/commands.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/cli/devex.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/cli/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/console.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/harnesses/acp.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/harnesses/claude.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/harnesses/codex.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/harnesses/copilot.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/harnesses/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/server/architecture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/server/config.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/server/deployment.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/server/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/reference/server/security.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/resources/positioning.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/concepts/federation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/concepts/harnesses.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/concepts/humans-and-agents.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/concepts/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/concepts/persistence.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/concepts/rooms.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/demos/magic-demo.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/guides/first-session.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/guides/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/guides/join-as-human.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/guides/local-setup.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/guides/multi-machine.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/shared/use-cases-index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-15-mesh-events.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-18-culture-dev-positioning.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-22-agex-integration.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-24-otel-foundation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-25-otel-federation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/plans/2026-04-26-otel-metrics.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-15-mesh-events-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-17-sites-repositioning-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-18-culture-dev-positioning-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-22-agex-integration-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/docs/superpowers/specs/2026-04-24-otel-observability-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/favicon.ico +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/culture.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/robots.txt +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/sitemap-agentirc.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/sitemap-main.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/sitemap.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/sonar-project.properties +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/_fakes.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/_metrics_helpers.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_config_load.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_context.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_dispatch_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_emit_event_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_federation_propagation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_metrics_clients.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_metrics_events.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_metrics_init.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_metrics_irc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_metrics_s2s.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_metrics_trace_inbound.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_outbound_inject.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_parse_error.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_privmsg_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_s2s_dispatch_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_s2s_relay_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_s2s_session_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_server_init.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_server_link_inject.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_session_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/telemetry/test_tracing.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_archive.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_bot.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_bot_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_bot_config_fires_event_toplevel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_channel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_channel_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_cli_afi.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_cli_devex.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_cli_introspect.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_cli_passthrough.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_connection.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_chat_markdown.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_commands.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_connection.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_fixes_224_227.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_icons.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_integration.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_console_status.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_credentials.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_culture_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_display.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_basic.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_bot_chain.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_bot_trigger.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_cap_fallback.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_catalog.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_federation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_history.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_lifecycle.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_events_reserved_nick.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_federation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_filter_dsl.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_history.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_http_listener.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_irc_transport_tags.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_learn_prompt.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_manifest_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_mention_warning.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_mentions.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_mesh_readiness.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_message.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_message_tags.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_migrate_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_modes.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_persistence.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_persistence_timeout.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_pidfile.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_register_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_rooms.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_skill_docs.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_skills.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_template_engine.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_threads.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.5.0}/tests/test_welcome_bot.py +0 -0
|
@@ -4,6 +4,21 @@ 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.5.0] - 2026-04-25
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `culture/telemetry/audit.py` — `AuditSink` with bounded `asyncio.Queue` + dedicated writer task + daily/size rotation + `0600`/`0700` perms.
|
|
12
|
+
- Public `culture.telemetry.AuditSink`, `init_audit`, `build_audit_record`, `utc_iso_timestamp`.
|
|
13
|
+
- `TelemetryConfig.audit_enabled` (default `True`), `audit_dir`, `audit_max_file_bytes`, `audit_rotate_utc_midnight`, `audit_queue_depth` — independent of `telemetry.enabled` (audit fires even with OTEL off).
|
|
14
|
+
- `culture/protocol/extensions/audit.md` — JSONL record schema as a stable contract.
|
|
15
|
+
- `docs/agentirc/audit.md` — operator guide.
|
|
16
|
+
- Audit metrics extend the Plan-3 `MetricsRegistry`: `culture.audit.writes` (Counter, labels `outcome=ok|error`) and `culture.audit.queue_depth` (UpDownCounter).
|
|
17
|
+
- `IRCd.__init__` creates the sink; `IRCd.start()` awaits `sink.start()`; `IRCd.stop()` awaits `sink.shutdown()` so SERVER_WAKE / SERVER_SLEEP both land in the JSONL.
|
|
18
|
+
- `IRCd.emit_event` submits one record per event after the `irc.event.emit` span; `trace_id` / `span_id` captured inside the span for cross-pillar joins.
|
|
19
|
+
- `Client._process_buffer` submits `PARSE_ERROR` records for malformed inbound lines.
|
|
20
|
+
- Federation audit: federated `message` events arrive on the receiver with `origin=federated`, `peer=<peer_name>`. Federated lifecycle events (JOIN/PART/QUIT) are deferred — see #296.
|
|
21
|
+
|
|
7
22
|
## [8.4.0] - 2026-04-25
|
|
8
23
|
|
|
9
24
|
### Added
|
|
@@ -17,6 +17,7 @@ from culture.aio import maybe_await
|
|
|
17
17
|
from culture.constants import SYSTEM_USER_PREFIX
|
|
18
18
|
from culture.protocol import replies
|
|
19
19
|
from culture.protocol.message import Message
|
|
20
|
+
from culture.telemetry.audit import utc_iso_timestamp as _utc_iso_timestamp
|
|
20
21
|
from culture.telemetry.context import TRACEPARENT_TAG as _TP_TAG_NAME
|
|
21
22
|
from culture.telemetry.context import (
|
|
22
23
|
context_from_traceparent,
|
|
@@ -148,6 +149,7 @@ class Client:
|
|
|
148
149
|
"error": type(exc).__name__,
|
|
149
150
|
},
|
|
150
151
|
)
|
|
152
|
+
self._submit_parse_error_audit(line, exc)
|
|
151
153
|
continue
|
|
152
154
|
# Record received bytes + message size for every successfully-parsed
|
|
153
155
|
# line. +2 accounts for the \r\n that was stripped during line-split.
|
|
@@ -160,6 +162,49 @@ class Client:
|
|
|
160
162
|
await self._dispatch(msg)
|
|
161
163
|
return buffer
|
|
162
164
|
|
|
165
|
+
def _submit_parse_error_audit(self, line: str, exc: BaseException) -> None:
|
|
166
|
+
"""Build and submit a PARSE_ERROR audit record for a malformed inbound line.
|
|
167
|
+
|
|
168
|
+
The record cannot go through build_audit_record (which expects an Event);
|
|
169
|
+
PARSE_ERROR is a synthetic event_type with no Event object behind it.
|
|
170
|
+
"""
|
|
171
|
+
# Capture trace/span ids from the active span (the
|
|
172
|
+
# `irc.client.process_buffer` we're inside of).
|
|
173
|
+
span = _otel_trace.get_current_span()
|
|
174
|
+
ctx = span.get_span_context()
|
|
175
|
+
trace_id_hex = format(ctx.trace_id, "032x") if ctx.is_valid else ""
|
|
176
|
+
span_id_hex = format(ctx.span_id, "016x") if ctx.is_valid else ""
|
|
177
|
+
|
|
178
|
+
peer_info = self.writer.get_extra_info("peername")
|
|
179
|
+
remote_addr = f"{peer_info[0]}:{peer_info[1]}" if peer_info else ""
|
|
180
|
+
|
|
181
|
+
tags: dict[str, str] = {}
|
|
182
|
+
tp = current_traceparent()
|
|
183
|
+
if tp:
|
|
184
|
+
tags["culture.dev/traceparent"] = tp
|
|
185
|
+
|
|
186
|
+
record = {
|
|
187
|
+
"ts": _utc_iso_timestamp(time.time()),
|
|
188
|
+
"server": self.server.config.name,
|
|
189
|
+
"event_type": "PARSE_ERROR",
|
|
190
|
+
"origin": "local",
|
|
191
|
+
"peer": "",
|
|
192
|
+
"trace_id": trace_id_hex,
|
|
193
|
+
"span_id": span_id_hex,
|
|
194
|
+
"actor": {
|
|
195
|
+
"nick": self.nick or "",
|
|
196
|
+
"kind": "human",
|
|
197
|
+
"remote_addr": remote_addr,
|
|
198
|
+
},
|
|
199
|
+
"target": {"kind": "", "name": ""},
|
|
200
|
+
"payload": {
|
|
201
|
+
"line_preview": line[:64],
|
|
202
|
+
"error": type(exc).__name__,
|
|
203
|
+
},
|
|
204
|
+
"tags": tags,
|
|
205
|
+
}
|
|
206
|
+
self.server.audit.submit(record)
|
|
207
|
+
|
|
163
208
|
async def handle(self, initial_msg: str | None = None) -> None:
|
|
164
209
|
peer_info = self.writer.get_extra_info("peername")
|
|
165
210
|
remote_addr = f"{peer_info[0]}:{peer_info[1]}" if peer_info else ""
|
|
@@ -26,6 +26,13 @@ class TelemetryConfig:
|
|
|
26
26
|
traces_sampler: str = "parentbased_always_on"
|
|
27
27
|
metrics_enabled: bool = True
|
|
28
28
|
metrics_export_interval_ms: int = 10000
|
|
29
|
+
# Audit JSONL sink (Plan 4). Independent of `enabled` — audit fires
|
|
30
|
+
# even when telemetry is off so admins always have the trail.
|
|
31
|
+
audit_enabled: bool = True
|
|
32
|
+
audit_dir: str = "~/.culture/audit"
|
|
33
|
+
audit_max_file_bytes: int = 256 * 1024 * 1024 # 256 MiB
|
|
34
|
+
audit_rotate_utc_midnight: bool = True
|
|
35
|
+
audit_queue_depth: int = 10000
|
|
29
36
|
|
|
30
37
|
|
|
31
38
|
@dataclass
|
|
@@ -23,12 +23,14 @@ from culture.constants import (
|
|
|
23
23
|
SYSTEM_USER_PREFIX,
|
|
24
24
|
)
|
|
25
25
|
from culture.protocol.message import Message
|
|
26
|
+
from culture.telemetry import build_audit_record, current_traceparent
|
|
26
27
|
|
|
27
28
|
logger = logging.getLogger(__name__)
|
|
28
29
|
|
|
29
30
|
# Span/metric attribute keys defined once so a future rename has one edit point.
|
|
30
31
|
_ATTR_EVENT_TYPE = "event.type"
|
|
31
32
|
|
|
33
|
+
|
|
32
34
|
if TYPE_CHECKING:
|
|
33
35
|
from culture.agentirc.client import Client
|
|
34
36
|
from culture.agentirc.remote_client import RemoteClient
|
|
@@ -39,11 +41,12 @@ class IRCd:
|
|
|
39
41
|
"""The culture IRC server."""
|
|
40
42
|
|
|
41
43
|
def __init__(self, config: ServerConfig):
|
|
42
|
-
from culture.telemetry import init_metrics, init_telemetry
|
|
44
|
+
from culture.telemetry import init_audit, init_metrics, init_telemetry
|
|
43
45
|
|
|
44
46
|
self.config = config
|
|
45
47
|
self.tracer = init_telemetry(config)
|
|
46
48
|
self.metrics = init_metrics(config)
|
|
49
|
+
self.audit = init_audit(config, self.metrics)
|
|
47
50
|
self.clients: dict[str, Client | VirtualClient] = {} # nick -> Client
|
|
48
51
|
self.channels: dict[str, Channel] = {} # name -> Channel
|
|
49
52
|
self.skills: list[Skill] = []
|
|
@@ -74,6 +77,8 @@ class IRCd:
|
|
|
74
77
|
logger.info("Bootstrapping system identity...")
|
|
75
78
|
self._bootstrap_system_identity()
|
|
76
79
|
|
|
80
|
+
await self.audit.start()
|
|
81
|
+
|
|
77
82
|
await self.emit_event(
|
|
78
83
|
Event(
|
|
79
84
|
type=EventType.SERVER_WAKE,
|
|
@@ -223,14 +228,25 @@ class IRCd:
|
|
|
223
228
|
self.metrics.events_emitted.add(1, {_ATTR_EVENT_TYPE: event_type_str, "origin": origin_str})
|
|
224
229
|
render_started = time.perf_counter()
|
|
225
230
|
|
|
231
|
+
trace_id_hex = ""
|
|
232
|
+
span_id_hex = ""
|
|
233
|
+
tp: str | None = None
|
|
234
|
+
|
|
226
235
|
# Per-call get_tracer: the `tracing_exporter` test fixture swaps the
|
|
227
236
|
# global provider between tests; a cached Tracer would bind to the
|
|
228
237
|
# first test's provider and stop delivering to later ones.
|
|
229
238
|
with _otel_trace.get_tracer("culture.agentirc").start_as_current_span(
|
|
230
239
|
"irc.event.emit", attributes=attrs
|
|
231
|
-
):
|
|
240
|
+
) as span:
|
|
232
241
|
seq = self.next_seq()
|
|
233
242
|
self._event_log.append((seq, event))
|
|
243
|
+
ctx = span.get_span_context()
|
|
244
|
+
if ctx.is_valid:
|
|
245
|
+
trace_id_hex = format(ctx.trace_id, "032x")
|
|
246
|
+
span_id_hex = format(ctx.span_id, "016x")
|
|
247
|
+
# Capture traceparent inside the span so trace_flags reflect
|
|
248
|
+
# the actual sampling decision for this span, not a hardcoded -01.
|
|
249
|
+
tp = current_traceparent()
|
|
234
250
|
await self._run_skill_hooks(event)
|
|
235
251
|
if not origin_tag:
|
|
236
252
|
await self._relay_to_peers(event)
|
|
@@ -240,6 +256,22 @@ class IRCd:
|
|
|
240
256
|
render_ms = (time.perf_counter() - render_started) * 1000.0
|
|
241
257
|
self.metrics.events_render_duration.record(render_ms, {_ATTR_EVENT_TYPE: event_type_str})
|
|
242
258
|
|
|
259
|
+
# Audit submit happens after the span exits so it doesn't sit inside
|
|
260
|
+
# the irc.event.emit span (would skew render duration). The trace_id/
|
|
261
|
+
# span_id captured inside the span point back at it for cross-pillar
|
|
262
|
+
# joins.
|
|
263
|
+
tags: dict[str, str] = {"culture.dev/traceparent": tp} if tp else {}
|
|
264
|
+
self.audit.submit(
|
|
265
|
+
build_audit_record(
|
|
266
|
+
server_name=self.config.name,
|
|
267
|
+
event=event,
|
|
268
|
+
origin_tag=origin_tag,
|
|
269
|
+
trace_id=trace_id_hex,
|
|
270
|
+
span_id=span_id_hex,
|
|
271
|
+
extra_tags=tags,
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
|
|
243
275
|
_NO_SURFACE_TYPES = NO_SURFACE_EVENT_TYPES
|
|
244
276
|
|
|
245
277
|
@staticmethod
|
|
@@ -395,6 +427,7 @@ class IRCd:
|
|
|
395
427
|
if self._server:
|
|
396
428
|
self._server.close()
|
|
397
429
|
await self._server.wait_closed()
|
|
430
|
+
await self.audit.shutdown()
|
|
398
431
|
finally:
|
|
399
432
|
self._stopped.set()
|
|
400
433
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Extension: Audit JSONL Sink
|
|
2
|
+
|
|
3
|
+
The audit log is a durable, file-based JSON-Lines (`.jsonl`) trail of every event the server
|
|
4
|
+
emits. It is **separate from OTEL traces / metrics / logs** — audit lands directly on local disk
|
|
5
|
+
and never depends on a running collector. Admin-only "who said what to whom, when, via what path."
|
|
6
|
+
|
|
7
|
+
## File Layout
|
|
8
|
+
|
|
9
|
+
- **Path:** `<audit_dir>/<server_name>-<YYYY-MM-DD>.jsonl` where `<audit_dir>` defaults to
|
|
10
|
+
`~/.culture/audit/` (configurable via `telemetry.audit_dir`). The date is **UTC**.
|
|
11
|
+
- **File mode:** `0600` (owner read/write only).
|
|
12
|
+
- **Directory mode:** `0700` (owner only). Created on demand if missing; existing dir mode is
|
|
13
|
+
left as-is.
|
|
14
|
+
- **Rotation suffix:** when the daily file hits the size cap, the next file gets `.1`, then
|
|
15
|
+
`.2`, … same date. New day starts a fresh file with no suffix.
|
|
16
|
+
|
|
17
|
+
Example for server `spark` on 2026-04-27 with two size-cap rotations:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
~/.culture/audit/spark-2026-04-27.jsonl # first 256 MiB
|
|
21
|
+
~/.culture/audit/spark-2026-04-27.1.jsonl # next 256 MiB
|
|
22
|
+
~/.culture/audit/spark-2026-04-27.2.jsonl # current
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Record Schema
|
|
26
|
+
|
|
27
|
+
Each line in the file is a single JSON object. Lines never wrap. Keys are lowercase with `_`
|
|
28
|
+
separators. Order is canonicalized at write time (stable across writes). Future schema additions
|
|
29
|
+
are additive only — consumers must tolerate unknown keys.
|
|
30
|
+
|
|
31
|
+
Keys within each record are alphabetically sorted (the writer uses
|
|
32
|
+
`json.dumps(..., sort_keys=True)`). Consumers SHOULD NOT rely on
|
|
33
|
+
insertion order; future producers may emit keys in any order.
|
|
34
|
+
|
|
35
|
+
| Field | Type | Required | Description |
|
|
36
|
+
|-------|------|----------|-------------|
|
|
37
|
+
| `ts` | string | yes | ISO 8601 UTC timestamp with microsecond precision and trailing `Z` (e.g. `2026-04-27T14:32:05.123456Z`). |
|
|
38
|
+
| `server` | string | yes | Server name from `ServerConfig.name`. |
|
|
39
|
+
| `event_type` | string | yes | `EventType.value` (e.g. `message`, `user.join`, `room.create`) or the special string `PARSE_ERROR` for malformed inbound lines. |
|
|
40
|
+
| `origin` | string | yes | `local` if the event originated on this server; `federated` if it arrived via a peer link. |
|
|
41
|
+
| `peer` | string | yes | Peer server name when `origin=federated`; empty string `""` otherwise. |
|
|
42
|
+
| `trace_id` | string | yes | OTEL trace-id (32 hex chars) of the active span at submit time, or `""` if no span. |
|
|
43
|
+
| `span_id` | string | yes | OTEL span-id (16 hex chars) of the active span, or `""`. |
|
|
44
|
+
| `actor` | object | yes | `{nick, kind, remote_addr}` describing who/what produced the event. |
|
|
45
|
+
| `actor.nick` | string | yes | The nick from the event (`event.nick`), or `""`. |
|
|
46
|
+
| `actor.kind` | string | yes | One of `human`, `bot`, `harness`. v1 always emits `human` — Plans 5/6 refine. |
|
|
47
|
+
| `actor.remote_addr` | string | yes | `"<ip>:<port>"` if known (PARSE_ERROR via Client; empty for server-internal sites). |
|
|
48
|
+
| `target` | object | yes | `{kind, name}` describing what the event affected. |
|
|
49
|
+
| `target.kind` | string | yes | `channel` (event.channel set), `nick` (DM target), or `""` for global events. |
|
|
50
|
+
| `target.name` | string | yes | The channel or nick; `""` for global. |
|
|
51
|
+
| `payload` | object | yes | `event.data` with all underscore-prefix keys (`_origin`, etc.) stripped. May include `nick` / `channel` defaulted from `event.nick`/`event.channel`. |
|
|
52
|
+
| `tags` | object | yes | IRCv3-style tag bag. v1 emits at most `culture.dev/traceparent` derived from the active span; empty `{}` if no span. |
|
|
53
|
+
|
|
54
|
+
## Rotation
|
|
55
|
+
|
|
56
|
+
Rotation fires when **either** condition is met, checked at the top of every record write:
|
|
57
|
+
|
|
58
|
+
1. The current UTC date differs from `current_date` (daily roll, controlled by
|
|
59
|
+
`telemetry.audit_rotate_utc_midnight`).
|
|
60
|
+
2. The current file size + the about-to-be-written record size exceeds
|
|
61
|
+
`telemetry.audit_max_file_bytes` (default 256 MiB).
|
|
62
|
+
|
|
63
|
+
The new file is opened with `O_WRONLY | O_APPEND | O_CREAT` mode `0600`. Writes use a single
|
|
64
|
+
`os.write` per record so partial-line interleaving is impossible.
|
|
65
|
+
|
|
66
|
+
A record larger than `audit_max_file_bytes` is still written — the cap is a soft
|
|
67
|
+
ceiling for accumulated bytes, not a hard reject. The oversized record lands in
|
|
68
|
+
its own freshly-rotated file, and the next record triggers another rotation.
|
|
69
|
+
|
|
70
|
+
## Durability
|
|
71
|
+
|
|
72
|
+
Records flow through a bounded `asyncio.Queue` (depth `telemetry.audit_queue_depth`, default
|
|
73
|
+
10000). A dedicated writer task drains the queue and writes each record. On queue overflow, the
|
|
74
|
+
record is **dropped** and `culture.audit.writes{outcome=error}` increments. A stderr warning is
|
|
75
|
+
logged.
|
|
76
|
+
|
|
77
|
+
This is a deliberate trade-off: dropping records is preferable to blocking `IRCd.emit_event`. A
|
|
78
|
+
real-world audit gap is rare and recoverable; a blocked event loop is catastrophic.
|
|
79
|
+
|
|
80
|
+
No `fsync` per record — writes hit the page cache and rely on the OS to flush. A hard crash can
|
|
81
|
+
lose the in-flight record.
|
|
82
|
+
|
|
83
|
+
## Retention
|
|
84
|
+
|
|
85
|
+
Files are not auto-pruned in v1. Operators prune manually:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
find ~/.culture/audit -name 'spark-*.jsonl*' -mtime +30 -delete
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
A future `audit-prune` CLI is TODO.
|
|
92
|
+
|
|
93
|
+
## Compat
|
|
94
|
+
|
|
95
|
+
The schema is a stable contract:
|
|
96
|
+
|
|
97
|
+
- New fields can be added in future versions; old consumers must tolerate unknown keys.
|
|
98
|
+
- Existing keys keep their type and semantics across versions.
|
|
99
|
+
- If a future version needs a breaking change, a top-level `schema_version` integer will be
|
|
100
|
+
added and bumped — until that exists, treat the schema as version 1.
|
|
101
|
+
|
|
102
|
+
## Example
|
|
103
|
+
|
|
104
|
+
PRIVMSG from `alpha-alice` (a federated client on the `alpha` peer) to channel `#general` on
|
|
105
|
+
the local server `spark`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{"ts":"2026-04-27T14:32:05.123456Z","server":"spark","event_type":"message","origin":"federated","peer":"alpha","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","actor":{"nick":"alpha-alice","kind":"human","remote_addr":""},"target":{"kind":"channel","name":"#general"},"payload":{"text":"hi","nick":"alpha-alice","channel":"#general"},"tags":{"culture.dev/traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}}
|
|
109
|
+
```
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Public surface re-exported here; call sites import from `culture.telemetry`.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from culture.telemetry.audit import AuditSink, build_audit_record, init_audit, utc_iso_timestamp
|
|
6
7
|
from culture.telemetry.context import (
|
|
7
8
|
TRACEPARENT_TAG,
|
|
8
9
|
TRACESTATE_TAG,
|
|
@@ -16,14 +17,18 @@ from culture.telemetry.metrics import MetricsRegistry, init_metrics
|
|
|
16
17
|
from culture.telemetry.tracing import init_telemetry
|
|
17
18
|
|
|
18
19
|
__all__ = [
|
|
20
|
+
"AuditSink",
|
|
19
21
|
"ExtractResult",
|
|
20
22
|
"MetricsRegistry",
|
|
21
23
|
"TRACEPARENT_TAG",
|
|
22
24
|
"TRACESTATE_TAG",
|
|
25
|
+
"build_audit_record",
|
|
23
26
|
"context_from_traceparent",
|
|
24
27
|
"current_traceparent",
|
|
25
28
|
"extract_traceparent_from_tags",
|
|
29
|
+
"init_audit",
|
|
26
30
|
"init_metrics",
|
|
27
31
|
"init_telemetry",
|
|
28
32
|
"inject_traceparent",
|
|
33
|
+
"utc_iso_timestamp",
|
|
29
34
|
]
|