agentirc-cli 8.4.0__tar.gz → 8.6.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.6.0}/CHANGELOG.md +34 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/PKG-INFO +1 -1
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/client.py +45 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/config.py +7 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/ircd.py +35 -2
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/agent_runner.py +53 -11
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/config.py +24 -0
- agentirc_cli-8.6.0/culture/clients/acp/culture.yaml +32 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/daemon.py +11 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/irc_transport.py +109 -18
- agentirc_cli-8.6.0/culture/clients/acp/telemetry.py +318 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/agent_runner.py +71 -14
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/config.py +24 -0
- agentirc_cli-8.6.0/culture/clients/claude/culture.yaml +29 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/daemon.py +11 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/irc_transport.py +126 -35
- agentirc_cli-8.6.0/culture/clients/claude/telemetry.py +318 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/agent_runner.py +60 -22
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/config.py +26 -0
- agentirc_cli-8.6.0/culture/clients/codex/culture.yaml +29 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/daemon.py +11 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/irc_transport.py +109 -18
- agentirc_cli-8.6.0/culture/clients/codex/telemetry.py +318 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/agent_runner.py +48 -6
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/config.py +24 -0
- agentirc_cli-8.6.0/culture/clients/copilot/culture.yaml +29 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/daemon.py +11 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/irc_transport.py +109 -18
- agentirc_cli-8.6.0/culture/clients/copilot/telemetry.py +318 -0
- agentirc_cli-8.6.0/culture/protocol/extensions/audit.md +109 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/telemetry/__init__.py +5 -0
- agentirc_cli-8.6.0/culture/telemetry/audit.py +380 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/telemetry/metrics.py +12 -0
- agentirc_cli-8.6.0/docs/agentirc/audit.md +154 -0
- agentirc_cli-8.6.0/docs/agentirc/harness-telemetry.md +302 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/agentirc/telemetry.md +68 -6
- agentirc_cli-8.6.0/docs/superpowers/plans/2026-04-27-otel-audit.md +247 -0
- agentirc_cli-8.6.0/docs/superpowers/plans/2026-04-28-otel-harness.md +272 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/README.md +11 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/config.py +24 -0
- agentirc_cli-8.6.0/packages/agent-harness/culture.yaml +28 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/daemon.py +13 -1
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/irc_transport.py +108 -18
- agentirc_cli-8.6.0/packages/agent-harness/telemetry.py +315 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/pyproject.toml +1 -1
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/conftest.py +22 -2
- agentirc_cli-8.6.0/tests/harness/conftest.py +89 -0
- agentirc_cli-8.6.0/tests/harness/test_agent_runner_acp.py +233 -0
- agentirc_cli-8.6.0/tests/harness/test_agent_runner_claude.py +449 -0
- agentirc_cli-8.6.0/tests/harness/test_agent_runner_codex.py +229 -0
- agentirc_cli-8.6.0/tests/harness/test_agent_runner_copilot.py +256 -0
- agentirc_cli-8.6.0/tests/harness/test_all_backends_parity.py +398 -0
- agentirc_cli-8.6.0/tests/harness/test_daemon_telemetry.py +72 -0
- agentirc_cli-8.6.0/tests/harness/test_irc_transport_propagation.py +447 -0
- agentirc_cli-8.6.0/tests/harness/test_record_llm_call.py +245 -0
- agentirc_cli-8.6.0/tests/harness/test_telemetry_module.py +264 -0
- agentirc_cli-8.6.0/tests/telemetry/__init__.py +0 -0
- agentirc_cli-8.6.0/tests/telemetry/test_audit_emit.py +145 -0
- agentirc_cli-8.6.0/tests/telemetry/test_audit_federation.py +132 -0
- agentirc_cli-8.6.0/tests/telemetry/test_audit_lifecycle.py +69 -0
- agentirc_cli-8.6.0/tests/telemetry/test_audit_module.py +306 -0
- agentirc_cli-8.6.0/tests/telemetry/test_audit_parse_error.py +177 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/uv.lock +1 -1
- agentirc_cli-8.4.0/culture/clients/acp/culture.yaml +0 -17
- agentirc_cli-8.4.0/culture/clients/claude/culture.yaml +0 -14
- agentirc_cli-8.4.0/culture/clients/codex/culture.yaml +0 -14
- agentirc_cli-8.4.0/culture/clients/copilot/culture.yaml +0 -14
- agentirc_cli-8.4.0/packages/agent-harness/culture.yaml +0 -13
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.claude/agents/doc-test-alignment.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.flake8 +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.github/workflows/docs-check.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.gitignore +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.pr_agent.toml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/.pylintrc +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/CLAUDE.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/Gemfile +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/Gemfile.lock +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/LICENSE +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/README.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/SECURITY.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/_config.base.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/_config.culture.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/_data/sites.yml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/_includes/head_custom.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/_sass/color_schemes/dark-terminal.scss +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/assets/images/IMG_3183.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/assets/images/apple-touch-icon.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/assets/images/favicon-16x16.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/assets/images/favicon-32x32.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/assets/images/favicon.ico +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/assets/images/og-agentirc.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/assets/images/og-culture.png +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/__main__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/CLAUDE.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/__main__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/channel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/docs/agentirc-architecture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/docs/agentirc-features.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/docs/agentirc-skill.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/docs/agentirc.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/events.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/history_store.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/remote_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/room_store.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/rooms_util.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/server_link.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/skill.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/skills/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/skills/history.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/skills/icon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/skills/rooms.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/skills/threads.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/agentirc/thread_store.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/aio.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/bot.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/filter_dsl.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/system/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/system/welcome/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/system/welcome/bot.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/system/welcome/handler.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/_passthrough.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/afi.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/agent.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/bot.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/channel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/devex.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/introspect.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/mesh.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/shared/display.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/cli/skills.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/app.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/commands.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/status.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/constants.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/credentials.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/formatting.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/learn_prompt.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/mesh_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/observer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/overview/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/overview/collector.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/overview/model.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/overview/web/style.css +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/persistence.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/pidfile.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/commands.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/events.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/extensions/tracing.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/message.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/protocol/replies.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/telemetry/context.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/culture/telemetry/tracing.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/README.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/agentirc/architecture-overview.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/agentirc/bots.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/agentirc/events.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/agentirc/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/agentirc/otelcol-template.yaml +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/agentirc/why-agentirc.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/agent-lifecycle.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/choose-a-harness.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/features.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/mental-model.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/operate.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/patterns.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/quickstart.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/reflective-development.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/vision-patterns-index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/vision.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/culture/what-is-culture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/architecture/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/architecture/layers.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/architecture/subsites.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/architecture/threads.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/cli/afi.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/cli/commands.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/cli/devex.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/cli/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/console.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/harnesses/acp.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/harnesses/claude.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/harnesses/codex.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/harnesses/copilot.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/harnesses/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/server/architecture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/server/config.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/server/deployment.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/server/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/reference/server/security.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/resources/positioning.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/concepts/federation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/concepts/harnesses.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/concepts/humans-and-agents.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/concepts/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/concepts/persistence.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/concepts/rooms.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/demos/magic-demo.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/guides/first-session.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/guides/index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/guides/join-as-human.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/guides/local-setup.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/guides/multi-machine.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/shared/use-cases-index.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-15-mesh-events.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-18-culture-dev-positioning.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-22-agex-integration.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-24-otel-foundation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-25-otel-federation.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/plans/2026-04-26-otel-metrics.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-15-mesh-events-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-17-sites-repositioning-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-18-culture-dev-positioning-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-22-agex-integration-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/docs/superpowers/specs/2026-04-24-otel-observability-design.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/favicon.ico +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/robots.txt +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/sitemap-agentirc.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/sitemap-main.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/sitemap.html +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/sonar-project.properties +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/__init__.py +0 -0
- {agentirc_cli-8.4.0/tests/telemetry → agentirc_cli-8.6.0/tests/harness}/__init__.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/_fakes.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/_metrics_helpers.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_config_load.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_context.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_dispatch_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_emit_event_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_federation_propagation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_metrics_clients.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_metrics_events.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_metrics_init.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_metrics_irc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_metrics_s2s.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_metrics_trace_inbound.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_outbound_inject.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_parse_error.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_privmsg_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_s2s_dispatch_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_s2s_relay_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_s2s_session_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_server_init.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_server_link_inject.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_session_span.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/telemetry/test_tracing.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_archive.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_bot.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_bot_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_bot_config_fires_event_toplevel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_channel.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_channel_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_cli_afi.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_cli_devex.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_cli_introspect.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_cli_passthrough.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_connection.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_chat_markdown.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_commands.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_connection.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_fixes_224_227.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_icons.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_integration.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_console_status.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_credentials.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_culture_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_display.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_basic.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_bot_chain.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_bot_trigger.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_cap_fallback.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_catalog.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_federation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_history.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_lifecycle.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_events_reserved_nick.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_federation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_filter_dsl.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_history.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_http_listener.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_irc_transport_tags.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_learn_prompt.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_manifest_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_mention_warning.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_mentions.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_mesh_readiness.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_message.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_message_tags.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_migrate_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_modes.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_persistence.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_persistence_timeout.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_pidfile.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_register_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_rooms.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_skill_docs.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_skills.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_supervisor.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_template_engine.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_threads.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_webhook.py +0 -0
- {agentirc_cli-8.4.0 → agentirc_cli-8.6.0}/tests/test_welcome_bot.py +0 -0
|
@@ -4,6 +4,40 @@ 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.6.0] - 2026-04-26
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Harness-side OTEL: 3 spans (harness.irc.connect, harness.irc.message.handle, harness.llm.call) and 4 LLM metrics (culture.harness.llm.tokens.input/output, call.duration, calls).
|
|
12
|
+
- W3C traceparent injection on outbound IRC + extraction on inbound — single trace_id now spans server, federation, and harness in the cross-process tree.
|
|
13
|
+
- Per-backend telemetry citation across claude/codex/copilot/acp with all-backends parity test (24 tests across 6 dimensions) locking down drift.
|
|
14
|
+
- docs/agentirc/harness-telemetry.md — new operator guide for the harness OTEL pillar.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- packages/agent-harness/{telemetry.py,config.py,culture.yaml,irc_transport.py,daemon.py} — reference module for the citation pattern.
|
|
19
|
+
- culture/clients/{claude,codex,copilot,acp}/{telemetry.py,config.py,culture.yaml,irc_transport.py,daemon.py,agent_runner.py} — telemetry citation, harness.llm.call span wrap, record_llm_call invocation.
|
|
20
|
+
- tests/harness/ — 70 new tests (24 parity + 46 module/runner/transport/daemon).
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Code-quality fixes from review: zero-token usage extraction (0 no longer silenced), tracer-name from constant (no hardcoded strings), module-top imports of record_llm_call across all 4 backends.
|
|
25
|
+
|
|
26
|
+
## [8.5.0] - 2026-04-25
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- `culture/telemetry/audit.py` — `AuditSink` with bounded `asyncio.Queue` + dedicated writer task + daily/size rotation + `0600`/`0700` perms.
|
|
31
|
+
- Public `culture.telemetry.AuditSink`, `init_audit`, `build_audit_record`, `utc_iso_timestamp`.
|
|
32
|
+
- `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).
|
|
33
|
+
- `culture/protocol/extensions/audit.md` — JSONL record schema as a stable contract.
|
|
34
|
+
- `docs/agentirc/audit.md` — operator guide.
|
|
35
|
+
- Audit metrics extend the Plan-3 `MetricsRegistry`: `culture.audit.writes` (Counter, labels `outcome=ok|error`) and `culture.audit.queue_depth` (UpDownCounter).
|
|
36
|
+
- `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.
|
|
37
|
+
- `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.
|
|
38
|
+
- `Client._process_buffer` submits `PARSE_ERROR` records for malformed inbound lines.
|
|
39
|
+
- 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.
|
|
40
|
+
|
|
7
41
|
## [8.4.0] - 2026-04-25
|
|
8
42
|
|
|
9
43
|
### 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
|
|
|
@@ -12,9 +12,16 @@ import logging
|
|
|
12
12
|
import os
|
|
13
13
|
import shutil
|
|
14
14
|
import tempfile
|
|
15
|
-
|
|
15
|
+
import time
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Awaitable, Callable
|
|
17
|
+
|
|
18
|
+
from opentelemetry import trace as _otel_trace
|
|
16
19
|
|
|
17
20
|
from culture.aio import maybe_await
|
|
21
|
+
from culture.clients.acp.telemetry import _HARNESS_TRACER_NAME, record_llm_call
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from culture.clients.acp.telemetry import HarnessMetricsRegistry
|
|
18
25
|
|
|
19
26
|
logger = logging.getLogger(__name__)
|
|
20
27
|
|
|
@@ -36,6 +43,8 @@ class ACPAgentRunner:
|
|
|
36
43
|
on_exit: Callable[[int], Awaitable[None]] | None = None,
|
|
37
44
|
on_message: Callable[[dict[str, Any]], Awaitable[None]] | None = None,
|
|
38
45
|
on_turn_error: Callable[[], Awaitable[None] | None] | None = None,
|
|
46
|
+
metrics: HarnessMetricsRegistry | None = None,
|
|
47
|
+
nick: str = "",
|
|
39
48
|
) -> None:
|
|
40
49
|
self.model = model
|
|
41
50
|
self.directory = directory
|
|
@@ -44,6 +53,8 @@ class ACPAgentRunner:
|
|
|
44
53
|
self.on_exit = on_exit
|
|
45
54
|
self.on_message = on_message
|
|
46
55
|
self.on_turn_error = on_turn_error
|
|
56
|
+
self._metrics = metrics
|
|
57
|
+
self._nick = nick
|
|
47
58
|
|
|
48
59
|
self._isolated_home: str | None = None
|
|
49
60
|
self._process: asyncio.subprocess.Process | None = None
|
|
@@ -431,16 +442,47 @@ class ACPAgentRunner:
|
|
|
431
442
|
|
|
432
443
|
async def _execute_single_prompt(self, text: str) -> None:
|
|
433
444
|
"""Send one prompt and handle its result, managing the busy flag."""
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
445
|
+
start_perf = time.perf_counter()
|
|
446
|
+
outcome = "success"
|
|
447
|
+
tracer = _otel_trace.get_tracer(_HARNESS_TRACER_NAME)
|
|
448
|
+
with tracer.start_as_current_span(
|
|
449
|
+
"harness.llm.call",
|
|
450
|
+
attributes={
|
|
451
|
+
"harness.backend": "acp",
|
|
452
|
+
"harness.model": self.model,
|
|
453
|
+
"harness.nick": self._nick,
|
|
454
|
+
},
|
|
455
|
+
):
|
|
456
|
+
try:
|
|
457
|
+
self._busy = True
|
|
458
|
+
resp = await self._send_prompt_with_retry(text)
|
|
459
|
+
await self._handle_prompt_result(resp)
|
|
460
|
+
except TimeoutError: # bubbles from _send_prompt_with_retry's retry-then-fail
|
|
461
|
+
outcome = "timeout"
|
|
462
|
+
logger.exception("ACP turn timeout")
|
|
463
|
+
if self.on_turn_error:
|
|
464
|
+
await maybe_await(self.on_turn_error())
|
|
465
|
+
except Exception:
|
|
466
|
+
outcome = "error"
|
|
467
|
+
logger.exception("ACP turn error")
|
|
468
|
+
if self.on_turn_error:
|
|
469
|
+
await maybe_await(self.on_turn_error())
|
|
470
|
+
finally:
|
|
471
|
+
self._busy = False
|
|
472
|
+
duration_ms = (time.perf_counter() - start_perf) * 1000.0
|
|
473
|
+
if self._metrics is not None:
|
|
474
|
+
# ACP token usage MAY arrive in session/update stopReason payload;
|
|
475
|
+
# current implementation does not extract — usage=None for v1.
|
|
476
|
+
# When we add extraction (per backing agent), thread through here.
|
|
477
|
+
record_llm_call(
|
|
478
|
+
self._metrics,
|
|
479
|
+
backend="acp",
|
|
480
|
+
model=self.model,
|
|
481
|
+
nick=self._nick,
|
|
482
|
+
usage=None,
|
|
483
|
+
duration_ms=duration_ms,
|
|
484
|
+
outcome=outcome,
|
|
485
|
+
)
|
|
444
486
|
|
|
445
487
|
async def _prompt_loop(self) -> None:
|
|
446
488
|
"""Process queued prompts one at a time."""
|
|
@@ -62,6 +62,27 @@ class AgentConfig:
|
|
|
62
62
|
icon: str | None = None
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
@dataclass
|
|
66
|
+
class TelemetryConfig:
|
|
67
|
+
"""OpenTelemetry settings for the agent harness.
|
|
68
|
+
|
|
69
|
+
``enabled: false`` by default so freshly installed harnesses don't
|
|
70
|
+
try to connect to a non-existent OTLP collector. Flip to ``true``
|
|
71
|
+
once your collector is running.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
enabled: bool = False
|
|
75
|
+
service_name: str = "culture.harness.acp"
|
|
76
|
+
otlp_endpoint: str = "http://localhost:4317"
|
|
77
|
+
otlp_protocol: str = "grpc" # grpc | http/protobuf (only grpc supported initially)
|
|
78
|
+
otlp_timeout_ms: int = 5000
|
|
79
|
+
otlp_compression: str = "gzip"
|
|
80
|
+
traces_enabled: bool = True
|
|
81
|
+
traces_sampler: str = "parentbased_always_on"
|
|
82
|
+
metrics_enabled: bool = True
|
|
83
|
+
metrics_export_interval_ms: int = 10000
|
|
84
|
+
|
|
85
|
+
|
|
65
86
|
@dataclass
|
|
66
87
|
class DaemonConfig:
|
|
67
88
|
"""Top-level daemon configuration."""
|
|
@@ -69,6 +90,7 @@ class DaemonConfig:
|
|
|
69
90
|
server: ServerConnConfig = field(default_factory=ServerConnConfig)
|
|
70
91
|
supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
|
|
71
92
|
webhooks: WebhookConfig = field(default_factory=WebhookConfig)
|
|
93
|
+
telemetry: TelemetryConfig = field(default_factory=TelemetryConfig)
|
|
72
94
|
buffer_size: int = 500
|
|
73
95
|
poll_interval: int = 60
|
|
74
96
|
sleep_start: str = "23:00"
|
|
@@ -91,6 +113,7 @@ def load_config(path: str | Path) -> DaemonConfig:
|
|
|
91
113
|
supervisor = SupervisorConfig(**raw.get("supervisor", {}))
|
|
92
114
|
|
|
93
115
|
webhooks = WebhookConfig(**raw.get("webhooks", {}))
|
|
116
|
+
telemetry = TelemetryConfig(**raw.get("telemetry", {}))
|
|
94
117
|
|
|
95
118
|
agents = []
|
|
96
119
|
known_agent_fields = {f.name for f in AgentConfig.__dataclass_fields__.values()}
|
|
@@ -102,6 +125,7 @@ def load_config(path: str | Path) -> DaemonConfig:
|
|
|
102
125
|
server=server,
|
|
103
126
|
supervisor=supervisor,
|
|
104
127
|
webhooks=webhooks,
|
|
128
|
+
telemetry=telemetry,
|
|
105
129
|
buffer_size=raw.get("buffer_size", 500),
|
|
106
130
|
poll_interval=raw.get("poll_interval", 60),
|
|
107
131
|
sleep_start=raw.get("sleep_start", "23:00"),
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
suffix: harness-acp
|
|
2
|
+
backend: claude
|
|
3
|
+
model: claude-opus-4-6
|
|
4
|
+
channels:
|
|
5
|
+
- "#harness"
|
|
6
|
+
acp_command:
|
|
7
|
+
- opencode
|
|
8
|
+
- acp
|
|
9
|
+
system_prompt: |
|
|
10
|
+
You maintain the ACP agent backend in culture/clients/acp/.
|
|
11
|
+
Listen on #harness for propagation instructions from spark-harness.
|
|
12
|
+
Apply changes using the citation pattern (cite, don't import),
|
|
13
|
+
adapting for ACP (Cline, OpenCode, Kiro)
|
|
14
|
+
specifics (agent_runner.py, supervisor.py).
|
|
15
|
+
tags:
|
|
16
|
+
- harness
|
|
17
|
+
- acp
|
|
18
|
+
|
|
19
|
+
# OpenTelemetry configuration for the agent harness.
|
|
20
|
+
# Set enabled: true once your OTLP collector is running.
|
|
21
|
+
# See docs/agentirc/harness-telemetry.md for a setup guide.
|
|
22
|
+
telemetry:
|
|
23
|
+
enabled: false # master switch — flip to true to start exporting
|
|
24
|
+
service_name: culture.harness.acp # overridden per-backend (e.g. culture.harness.acp)
|
|
25
|
+
otlp_endpoint: http://localhost:4317 # OTLP/gRPC receiver endpoint
|
|
26
|
+
otlp_protocol: grpc # grpc or http/protobuf
|
|
27
|
+
otlp_timeout_ms: 5000 # export request timeout in milliseconds
|
|
28
|
+
otlp_compression: gzip # gzip | none
|
|
29
|
+
traces_enabled: true # enable distributed tracing
|
|
30
|
+
traces_sampler: parentbased_always_on # honor the server's sampling decision
|
|
31
|
+
metrics_enabled: true # enable LLM call metrics export
|
|
32
|
+
metrics_export_interval_ms: 10000 # how often to push metric batches (ms)
|
|
@@ -25,6 +25,7 @@ from culture.clients.acp.irc_transport import IRCTransport
|
|
|
25
25
|
from culture.clients.acp.message_buffer import MessageBuffer
|
|
26
26
|
from culture.clients.acp.socket_server import SocketServer
|
|
27
27
|
from culture.clients.acp.supervisor import Supervisor, make_sdk_evaluate_fn
|
|
28
|
+
from culture.clients.acp.telemetry import init_harness_telemetry
|
|
28
29
|
from culture.clients.acp.webhook import AlertEvent, WebhookClient
|
|
29
30
|
from culture.pidfile import remove_pid, write_pid
|
|
30
31
|
|
|
@@ -71,6 +72,8 @@ class ACPDaemon:
|
|
|
71
72
|
self._socket_server: SocketServer | None = None
|
|
72
73
|
self._agent_runner: ACPAgentRunner | None = None
|
|
73
74
|
self._supervisor: Supervisor | None = None
|
|
75
|
+
self._tracer = None
|
|
76
|
+
self._metrics = None
|
|
74
77
|
|
|
75
78
|
# FIFO queue of relay targets — each @mention enqueues a target,
|
|
76
79
|
# each agent response dequeues one, ensuring correct routing even
|
|
@@ -134,6 +137,9 @@ class ACPDaemon:
|
|
|
134
137
|
self._pid_name = f"agent-{self.agent.nick}"
|
|
135
138
|
write_pid(self._pid_name, os.getpid())
|
|
136
139
|
|
|
140
|
+
# 0.5. OTEL telemetry (if telemetry.enabled, installs SDK providers; else no-op).
|
|
141
|
+
self._tracer, self._metrics = init_harness_telemetry(self.config)
|
|
142
|
+
|
|
137
143
|
# 1. Message buffer
|
|
138
144
|
self._buffer = MessageBuffer(max_per_channel=self.config.buffer_size)
|
|
139
145
|
|
|
@@ -148,6 +154,9 @@ class ACPDaemon:
|
|
|
148
154
|
on_mention=self._on_mention,
|
|
149
155
|
tags=list(self.agent.tags),
|
|
150
156
|
on_roominvite=self._on_roominvite,
|
|
157
|
+
tracer=self._tracer,
|
|
158
|
+
metrics=self._metrics,
|
|
159
|
+
backend="acp",
|
|
151
160
|
)
|
|
152
161
|
await self._transport.connect()
|
|
153
162
|
|
|
@@ -389,6 +398,8 @@ class ACPDaemon:
|
|
|
389
398
|
on_exit=self._on_agent_exit,
|
|
390
399
|
on_message=self._on_agent_message,
|
|
391
400
|
on_turn_error=self._on_turn_error,
|
|
401
|
+
metrics=self._metrics,
|
|
402
|
+
nick=self.agent.nick,
|
|
392
403
|
)
|
|
393
404
|
# Absorb the system prompt response without relaying to IRC
|
|
394
405
|
self._mention_targets.append(None)
|
|
@@ -1,20 +1,38 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import contextlib
|
|
4
5
|
import logging
|
|
5
6
|
import re
|
|
6
|
-
from
|
|
7
|
+
from contextlib import AbstractContextManager
|
|
8
|
+
from typing import TYPE_CHECKING, Callable
|
|
7
9
|
|
|
8
10
|
from culture.aio import maybe_await
|
|
9
11
|
from culture.clients.acp.message_buffer import MessageBuffer
|
|
10
12
|
from culture.constants import SYSTEM_USER_PREFIX
|
|
11
13
|
from culture.protocol.message import Message
|
|
14
|
+
from culture.telemetry.context import (
|
|
15
|
+
TRACEPARENT_TAG,
|
|
16
|
+
context_from_traceparent,
|
|
17
|
+
current_traceparent,
|
|
18
|
+
extract_traceparent_from_tags,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from opentelemetry.trace import Tracer
|
|
23
|
+
|
|
24
|
+
from culture.clients.acp.telemetry import HarnessMetricsRegistry
|
|
12
25
|
|
|
13
26
|
logger = logging.getLogger(__name__)
|
|
14
27
|
|
|
15
28
|
|
|
16
29
|
class IRCTransport:
|
|
17
|
-
"""Async IRC client for the daemon.
|
|
30
|
+
"""Async IRC client for the daemon.
|
|
31
|
+
|
|
32
|
+
Optional kwargs ``tracer``, ``metrics``, and ``backend`` enable OTEL
|
|
33
|
+
tracing and LLM metrics when an SDK provider is installed. Pass ``None``
|
|
34
|
+
(the default) for all three to run without instrumentation.
|
|
35
|
+
"""
|
|
18
36
|
|
|
19
37
|
def __init__(
|
|
20
38
|
self,
|
|
@@ -28,6 +46,9 @@ class IRCTransport:
|
|
|
28
46
|
tags: list[str] | None = None,
|
|
29
47
|
on_roominvite: Callable[[str, str], None] | None = None,
|
|
30
48
|
icon: str | None = None,
|
|
49
|
+
tracer: Tracer | None = None,
|
|
50
|
+
metrics: HarnessMetricsRegistry | None = None,
|
|
51
|
+
backend: str = "acp",
|
|
31
52
|
):
|
|
32
53
|
self.host = host
|
|
33
54
|
self.port = port
|
|
@@ -39,6 +60,10 @@ class IRCTransport:
|
|
|
39
60
|
self.tags = tags or []
|
|
40
61
|
self.on_roominvite = on_roominvite
|
|
41
62
|
self.icon = icon
|
|
63
|
+
self._tracer = tracer
|
|
64
|
+
# accepted for future per-message metrics (e.g. byte counters); unused in v1
|
|
65
|
+
self._metrics = metrics
|
|
66
|
+
self._backend = backend
|
|
42
67
|
self.connected = False
|
|
43
68
|
self._reader: asyncio.StreamReader | None = None
|
|
44
69
|
self._writer: asyncio.StreamWriter | None = None
|
|
@@ -57,23 +82,46 @@ class IRCTransport:
|
|
|
57
82
|
"332": self._on_numeric_topic,
|
|
58
83
|
}
|
|
59
84
|
|
|
85
|
+
def _span(self, name: str, attrs: dict | None = None) -> AbstractContextManager:
|
|
86
|
+
"""Return a real span context manager when tracing is enabled, else a no-op.
|
|
87
|
+
|
|
88
|
+
Keeps the no-tracer fast path clean — all callers write the same
|
|
89
|
+
``with self._span(...):`` pattern regardless of whether a tracer is
|
|
90
|
+
configured.
|
|
91
|
+
|
|
92
|
+
Does NOT accept a ``context=`` argument; callers that need a span
|
|
93
|
+
parented to a remote trace context (e.g. inbound message handling in
|
|
94
|
+
``_handle``) call ``self._tracer.start_as_current_span`` directly.
|
|
95
|
+
"""
|
|
96
|
+
if self._tracer is not None:
|
|
97
|
+
return self._tracer.start_as_current_span(name, attributes=attrs or {})
|
|
98
|
+
return contextlib.nullcontext()
|
|
99
|
+
|
|
60
100
|
async def connect(self) -> None:
|
|
61
101
|
self._should_run = True
|
|
62
102
|
await self._do_connect()
|
|
63
103
|
|
|
64
104
|
async def _do_connect(self) -> None:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
105
|
+
with self._span(
|
|
106
|
+
"harness.irc.connect",
|
|
107
|
+
attrs={
|
|
108
|
+
"harness.backend": self._backend,
|
|
109
|
+
"harness.nick": self.nick,
|
|
110
|
+
"harness.server": f"{self.host}:{self.port}",
|
|
111
|
+
},
|
|
112
|
+
):
|
|
113
|
+
try:
|
|
114
|
+
self._reader, self._writer = await asyncio.open_connection(self.host, self.port)
|
|
115
|
+
except OSError as exc:
|
|
116
|
+
raise ConnectionError(
|
|
117
|
+
f"Cannot connect to IRC server at {self.host}:{self.port} "
|
|
118
|
+
f"- is the server running?"
|
|
119
|
+
) from exc
|
|
120
|
+
await self._send_raw("CAP REQ :message-tags")
|
|
121
|
+
await self._send_raw(f"NICK {self.nick}")
|
|
122
|
+
await self._send_raw(f"USER {self.user} 0 * :{self.user}")
|
|
123
|
+
await self._send_raw("CAP END")
|
|
124
|
+
self._read_task = asyncio.create_task(self._read_loop())
|
|
77
125
|
|
|
78
126
|
async def disconnect(self) -> None:
|
|
79
127
|
self._should_run = False
|
|
@@ -145,12 +193,23 @@ class IRCTransport:
|
|
|
145
193
|
await self._send_raw(f"TOPIC {channel}")
|
|
146
194
|
|
|
147
195
|
async def send_raw(self, line: str) -> None:
|
|
148
|
-
"""Send a raw IRC line. Public for commands like HISTORY.
|
|
196
|
+
"""Send a raw IRC line. Public for commands like HISTORY.
|
|
197
|
+
|
|
198
|
+
When a tracer is configured and a span is active, prepends the W3C
|
|
199
|
+
``@culture.dev/traceparent=`` IRCv3 tag so all outbound paths — whether
|
|
200
|
+
called via the internal ``_send_raw`` helper or directly by daemon code
|
|
201
|
+
— carry trace context consistently.
|
|
202
|
+
"""
|
|
203
|
+
if self._tracer is not None and not line.startswith("@"):
|
|
204
|
+
tp = current_traceparent()
|
|
205
|
+
if tp is not None:
|
|
206
|
+
line = f"@{TRACEPARENT_TAG}={tp} {line}"
|
|
149
207
|
if self._writer:
|
|
150
208
|
self._writer.write(f"{line}\r\n".encode())
|
|
151
209
|
await self._writer.drain()
|
|
152
210
|
|
|
153
211
|
async def _send_raw(self, line: str) -> None:
|
|
212
|
+
"""Internal send helper; delegates to send_raw (injection lives there)."""
|
|
154
213
|
await self.send_raw(line)
|
|
155
214
|
|
|
156
215
|
async def _read_loop(self) -> None:
|
|
@@ -193,9 +252,41 @@ class IRCTransport:
|
|
|
193
252
|
delay = min(delay * 2, 60)
|
|
194
253
|
|
|
195
254
|
async def _handle(self, msg: Message) -> None:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
255
|
+
# Extract inbound traceparent before opening any span so the new span
|
|
256
|
+
# can be correctly parented to the remote trace context.
|
|
257
|
+
result = extract_traceparent_from_tags(msg, peer=None)
|
|
258
|
+
|
|
259
|
+
if self._tracer is not None:
|
|
260
|
+
if result.status == "valid":
|
|
261
|
+
ctx = context_from_traceparent(result.traceparent)
|
|
262
|
+
span_cm = self._tracer.start_as_current_span(
|
|
263
|
+
"harness.irc.message.handle",
|
|
264
|
+
context=ctx,
|
|
265
|
+
attributes={
|
|
266
|
+
"irc.command": msg.command,
|
|
267
|
+
"irc.client.nick": self.nick,
|
|
268
|
+
"culture.trace.origin": "remote",
|
|
269
|
+
},
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
attrs = {
|
|
273
|
+
"irc.command": msg.command,
|
|
274
|
+
"irc.client.nick": self.nick,
|
|
275
|
+
"culture.trace.origin": ("local" if result.status == "missing" else "remote"),
|
|
276
|
+
}
|
|
277
|
+
if result.status in ("malformed", "too_long"):
|
|
278
|
+
attrs["culture.trace.dropped_reason"] = result.status
|
|
279
|
+
span_cm = self._tracer.start_as_current_span(
|
|
280
|
+
"harness.irc.message.handle",
|
|
281
|
+
attributes=attrs,
|
|
282
|
+
)
|
|
283
|
+
else:
|
|
284
|
+
span_cm = contextlib.nullcontext()
|
|
285
|
+
|
|
286
|
+
with span_cm:
|
|
287
|
+
handler = self._cmd_handlers.get(msg.command)
|
|
288
|
+
if handler:
|
|
289
|
+
await maybe_await(handler(msg))
|
|
199
290
|
|
|
200
291
|
async def _on_ping(self, msg: Message) -> None:
|
|
201
292
|
token = msg.params[0] if msg.params else ""
|