agentirc-cli 6.2.3__tar.gz → 7.1.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-6.2.3 → agentirc_cli-7.1.0}/.github/workflows/security-checks.yml +1 -1
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/CHANGELOG.md +95 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/PKG-INFO +1 -1
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/client.py +185 -46
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/config.py +1 -0
- agentirc_cli-7.1.0/culture/agentirc/events.py +117 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/ircd.py +260 -27
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/server_link.py +131 -31
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/skill.py +19 -9
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/skills/history.py +28 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/skills/rooms.py +17 -1
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/bots/bot.py +121 -16
- agentirc_cli-7.1.0/culture/bots/bot_manager.py +197 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/bots/config.py +61 -10
- agentirc_cli-7.1.0/culture/bots/filter_dsl.py +339 -0
- agentirc_cli-7.1.0/culture/bots/system/__init__.py +36 -0
- agentirc_cli-7.1.0/culture/bots/system/welcome/__init__.py +1 -0
- agentirc_cli-7.1.0/culture/bots/system/welcome/bot.yaml +11 -0
- agentirc_cli-7.1.0/culture/bots/system/welcome/handler.py +1 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/bots/template_engine.py +11 -6
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/bots/virtual_client.py +43 -5
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/agent.py +11 -5
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/channel.py +14 -12
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/mesh.py +64 -33
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/server.py +58 -49
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/shared/display.py +49 -36
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/agent_runner.py +52 -47
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/daemon.py +16 -11
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/irc_transport.py +13 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/agent_runner.py +19 -16
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/daemon.py +36 -29
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/irc_transport.py +13 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/daemon.py +34 -20
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/irc_transport.py +13 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/daemon.py +16 -11
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/irc_transport.py +13 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/client.py +1 -1
- agentirc_cli-7.1.0/culture/constants.py +20 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/observer.py +34 -18
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/persistence.py +34 -15
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/commands.py +1 -0
- agentirc_cli-7.1.0/culture/protocol/extensions/events.md +145 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/extensions/federation.md +4 -0
- agentirc_cli-7.1.0/culture/protocol/extensions/icons.md +117 -0
- agentirc_cli-7.1.0/culture/protocol/message.py +128 -0
- agentirc_cli-7.1.0/docs/agentirc/bots.md +223 -0
- agentirc_cli-7.1.0/docs/agentirc/events.md +146 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/cli/index.md +6 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/server/architecture.md +4 -1
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/server/config.md +18 -0
- agentirc_cli-7.1.0/docs/superpowers/plans/2026-04-15-mesh-events.md +3252 -0
- agentirc_cli-7.1.0/docs/superpowers/specs/2026-04-15-mesh-events-design.md +380 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/daemon.py +38 -31
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/irc_transport.py +18 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/pyproject.toml +2 -1
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/conftest.py +121 -6
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_agent_runner.py +1 -2
- agentirc_cli-7.1.0/tests/test_bot_config_fires_event_toplevel.py +114 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_console_client.py +8 -6
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_console_fixes_224_227.py +4 -1
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_daemon_config.py +3 -7
- agentirc_cli-7.1.0/tests/test_events_basic.py +292 -0
- agentirc_cli-7.1.0/tests/test_events_bot_chain.py +59 -0
- agentirc_cli-7.1.0/tests/test_events_bot_trigger.py +99 -0
- agentirc_cli-7.1.0/tests/test_events_cap_fallback.py +59 -0
- agentirc_cli-7.1.0/tests/test_events_catalog.py +70 -0
- agentirc_cli-7.1.0/tests/test_events_federation.py +110 -0
- agentirc_cli-7.1.0/tests/test_events_history.py +64 -0
- agentirc_cli-7.1.0/tests/test_events_lifecycle.py +343 -0
- agentirc_cli-7.1.0/tests/test_events_reserved_nick.py +68 -0
- agentirc_cli-7.1.0/tests/test_filter_dsl.py +99 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_history.py +21 -19
- agentirc_cli-7.1.0/tests/test_irc_transport_tags.py +106 -0
- agentirc_cli-7.1.0/tests/test_message_tags.py +75 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_overview_collector.py +26 -12
- agentirc_cli-7.1.0/tests/test_persistence_timeout.py +59 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_skills.py +7 -3
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_supervisor.py +1 -2
- agentirc_cli-7.1.0/tests/test_welcome_bot.py +87 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/uv.lock +1 -1
- agentirc_cli-6.2.3/culture/bots/bot_manager.py +0 -111
- agentirc_cli-6.2.3/culture/protocol/extensions/icons.md +0 -89
- agentirc_cli-6.2.3/culture/protocol/message.py +0 -58
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.claude/agents/doc-test-alignment.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.flake8 +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.github/workflows/docs-check.yml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.gitignore +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.pr_agent.toml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/.pylintrc +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/CLAUDE.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/Gemfile +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/Gemfile.lock +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/LICENSE +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/README.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/SECURITY.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_config.agentirc.yml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_config.base.yml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_config.culture.yml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_data/sites.yml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_includes/head_custom.html +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_plugins/site_filter.rb +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_sass/color_schemes/dark-terminal.scss +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/assets/images/IMG_3183.png +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/assets/images/apple-touch-icon.png +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/assets/images/favicon-16x16.png +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/assets/images/favicon-32x32.png +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/assets/images/favicon.ico +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/assets/images/og-agentirc.png +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/assets/images/og-culture.png +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/__main__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/CLAUDE.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/__main__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/channel.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/docs/agentirc-architecture.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/docs/agentirc-features.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/docs/agentirc-skill.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/docs/agentirc.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/history_store.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/remote_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/room_store.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/rooms_util.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/skills/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/skills/icon.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/skills/threads.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/agentirc/thread_store.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/aio.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/bots/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/bot.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/cli/skills.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/culture.yaml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/culture.yaml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/culture.yaml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/culture.yaml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/app.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/commands.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/status.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/credentials.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/formatting.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/learn_prompt.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/mesh_config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/overview/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/overview/collector.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/overview/model.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/overview/web/style.css +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/pidfile.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/protocol/replies.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/README.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/agentirc/architecture-overview.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/agentirc/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/agentirc/why-agentirc.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/agent-lifecycle.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/choose-a-harness.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/mental-model.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/operate.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/patterns.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/quickstart.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/reflective-development.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/vision-patterns-index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/vision.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/culture/why-culture.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/architecture/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/architecture/layers.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/architecture/threads.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/cli/commands.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/harnesses/acp.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/harnesses/claude.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/harnesses/codex.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/harnesses/copilot.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/harnesses/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/server/deployment.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/server/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/reference/server/security.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/concepts/federation.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/concepts/harnesses.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/concepts/humans-and-agents.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/concepts/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/concepts/persistence.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/concepts/rooms.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/demos/magic-demo.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/guides/first-session.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/guides/index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/guides/join-as-human.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/guides/local-setup.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/guides/multi-machine.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/shared/use-cases-index.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/favicon.ico +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/culture.yaml +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/robots.txt +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/sonar-project.properties +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/__init__.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_archive.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_bot.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_bot_config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_channel.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_channel_cli.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_connection.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_console_commands.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_console_connection.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_console_icons.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_console_integration.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_console_status.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_credentials.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_culture_config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_display.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_federation.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_http_listener.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_learn_prompt.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_manifest_config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_mention_warning.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_mentions.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_mesh_readiness.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_message.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_migrate_cli.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_modes.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_persistence.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_pidfile.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_register_cli.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_rooms.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_skill_docs.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_template_engine.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_threads.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-6.2.3 → agentirc_cli-7.1.0}/tests/test_webhook.py +0 -0
|
@@ -33,7 +33,7 @@ jobs:
|
|
|
33
33
|
continue-on-error: true
|
|
34
34
|
|
|
35
35
|
- name: Run Safety dependency check
|
|
36
|
-
run: uv run safety check --
|
|
36
|
+
run: uv run safety check --output json > safety-results.json
|
|
37
37
|
continue-on-error: true
|
|
38
38
|
|
|
39
39
|
- name: Upload Security Results
|
|
@@ -4,6 +4,101 @@ 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
|
+
## [7.1.0] - 2026-04-17
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Refactored _handle_channel_mode in culture/agentirc/client.py: extracted _apply_mode_char and _broadcast_mode_change helpers to drop cognitive complexity from 21 to ≤15 (SonarCloud S3776)
|
|
13
|
+
- Background task GC safety in ircd.py _notify_local_quit: asyncio.ensure_future replaced with a tracked asyncio.create_task using the existing self._background_tasks set + add_done_callback(discard) pattern
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- culture mesh update no longer hangs on broken/unresponsive systemd units — all subprocess calls in persistence.py and cli/mesh.py now have explicit timeouts (30s for service restarts, 30s for CLI fallbacks, 120s for package upgrade)
|
|
19
|
+
- fires_event chain not triggering downstream bots (#260) — the bot.yaml loader now accepts fires_event at the top level as well as under output:, so configs that put the block at the top level emit events as expected
|
|
20
|
+
- Daemon log flushing stops after startup — replaced logging.basicConfig's default StreamHandler (which inherits stderr buffering from interpreter startup) with an explicit logging.FileHandler so runtime log records flush per-record
|
|
21
|
+
|
|
22
|
+
## [7.0.4] - 2026-04-17
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Reduce cognitive complexity and fix code quality issues across CLI modules (SonarCloud S3776, S1192, S5886, S108)
|
|
28
|
+
|
|
29
|
+
## [7.0.3] - 2026-04-17
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Duplicate _ERR_CHANNEL_PREFIX string in all 5 daemon files (S1192)
|
|
35
|
+
- Cognitive complexity in claude/daemon.py _poll_loop (CC 22, S3776)
|
|
36
|
+
- Cognitive complexity in agent-harness/daemon.py _poll_loop (CC 22, S3776)
|
|
37
|
+
- Cognitive complexity in codex/daemon.py _relay_response_to_irc (CC 44, S3776)
|
|
38
|
+
- Cognitive complexity in copilot/daemon.py _relay_response_to_irc (CC 23, S3776)
|
|
39
|
+
- Cognitive complexity in acp/daemon.py _relay_response_to_irc (CC 23, S3776)
|
|
40
|
+
- Cognitive complexity in acp/agent_runner.py start() (CC 17, S3776)
|
|
41
|
+
|
|
42
|
+
## [7.0.2] - 2026-04-17
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- Cognitive complexity in observer.py (CC 22→~13)
|
|
48
|
+
- Cognitive complexity in ircd.py (CC 18→~12)
|
|
49
|
+
- Cognitive complexity in server_link.py (CC 33→~14)
|
|
50
|
+
|
|
51
|
+
## [7.0.1] - 2026-04-17
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Wrong AgentConfig/DaemonConfig import types in test helpers (S5655)
|
|
57
|
+
- Constant if-False condition replaced with unreachable yield pattern (S5797)
|
|
58
|
+
|
|
59
|
+
## [7.0.0] - 2026-04-17
|
|
60
|
+
|
|
61
|
+
Mesh Events (issue #123) — lifecycle and activity notifications as IRCv3-tagged
|
|
62
|
+
PRIVMSGs, event-triggered bots, and pub/sub composition chains.
|
|
63
|
+
|
|
64
|
+
> Versions 6.3.0 through 6.11.2 were development increments for this feature
|
|
65
|
+
> and were never published. Their changes are consolidated here as 7.0.0.
|
|
66
|
+
|
|
67
|
+
### Breaking Changes
|
|
68
|
+
|
|
69
|
+
None. Existing clients, bots, and federation links continue to work unchanged.
|
|
70
|
+
The major bump reflects the scope of the feature addition (new protocol verb,
|
|
71
|
+
new subsystem, new bot trigger type).
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
|
|
75
|
+
- **Event system** — `system-<server>` pseudo-user surfaces lifecycle events
|
|
76
|
+
as IRCv3-tagged PRIVMSGs with `@event=<type>;event-data=<b64json>` tags
|
|
77
|
+
- **Built-in event catalog** — 18 event types across channel-scoped
|
|
78
|
+
(`user.join/part/quit`, `room.create/archive/meta`, `tags.update`) and global
|
|
79
|
+
(`agent.connect/disconnect`, `server.wake/sleep/link/unlink`,
|
|
80
|
+
`console.open/close`)
|
|
81
|
+
- **IRCv3 message-tags** — `Message.parse()` extracts tags; CAP negotiation
|
|
82
|
+
(`CAP REQ :message-tags`) in all agent backends and server
|
|
83
|
+
- **`#system` channel** — auto-created at startup for global event delivery;
|
|
84
|
+
`system-<server>` VirtualClient auto-joined
|
|
85
|
+
- **Reserved `system-*` nicks** — rejected for non-server clients
|
|
86
|
+
(`432 ERR_ERRONEUSNICKNAME`)
|
|
87
|
+
- **SEVENT S2S verb** — generic federation relay for lifecycle events with
|
|
88
|
+
`_origin` loop prevention and trust policy filtering
|
|
89
|
+
- **HistorySkill** stores lifecycle events — `HISTORY RECENT` replays
|
|
90
|
+
agent.connect, server.wake, room.create, etc.
|
|
91
|
+
- **Filter DSL** — safe recursive-descent expression parser (`==`, `!=`, `in`,
|
|
92
|
+
`and`, `or`, `not`, dotted field access) for bot event triggers
|
|
93
|
+
- **Bot event triggers** — `trigger.type: event` with filter DSL evaluation;
|
|
94
|
+
`fires_event` output for pub/sub bot chains with rate limiting (10/sec)
|
|
95
|
+
- **System bots** — package-bundled bots discovered at startup from
|
|
96
|
+
`culture/bots/system/<name>/bot.yaml`; welcome bot greets on `user.join`
|
|
97
|
+
- **All-backends CAP** — claude, codex, copilot, acp, and agent-harness
|
|
98
|
+
transports negotiate `message-tags` during connection
|
|
99
|
+
- **Documentation** — `docs/agentirc/events.md`, `docs/agentirc/bots.md`,
|
|
100
|
+
`culture/protocol/extensions/events.md`
|
|
101
|
+
|
|
7
102
|
## [6.2.3] - 2026-04-15
|
|
8
103
|
|
|
9
104
|
|
|
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
|
|
|
9
9
|
from culture.agentirc.channel import Channel
|
|
10
10
|
from culture.agentirc.skill import Event, EventType
|
|
11
11
|
from culture.aio import maybe_await
|
|
12
|
+
from culture.constants import SYSTEM_USER_PREFIX
|
|
12
13
|
from culture.protocol import replies
|
|
13
14
|
from culture.protocol.message import Message
|
|
14
15
|
|
|
@@ -35,6 +36,7 @@ class Client:
|
|
|
35
36
|
self.channels: set[Channel] = set()
|
|
36
37
|
self._registered = False
|
|
37
38
|
self.tags: list[str] = []
|
|
39
|
+
self.caps: set[str] = set()
|
|
38
40
|
self.modes: set[str] = set()
|
|
39
41
|
self.icon: str | None = None
|
|
40
42
|
|
|
@@ -49,6 +51,28 @@ class Client:
|
|
|
49
51
|
except OSError:
|
|
50
52
|
pass # Client disconnected; cleanup happens in ircd._handle_connection
|
|
51
53
|
|
|
54
|
+
async def send_raw(self, line: str) -> None:
|
|
55
|
+
"""Write a pre-formatted IRC line to the client socket.
|
|
56
|
+
|
|
57
|
+
Appends CRLF internally, matching ServerLink.send_raw convention.
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
self.writer.write(f"{line}\r\n".encode("utf-8"))
|
|
61
|
+
await self.writer.drain()
|
|
62
|
+
except OSError:
|
|
63
|
+
pass # Client disconnected; cleanup happens in ircd._handle_connection
|
|
64
|
+
|
|
65
|
+
async def send_tagged(self, msg: Message) -> None:
|
|
66
|
+
"""Send a Message, stripping tags for clients that haven't negotiated message-tags."""
|
|
67
|
+
if msg.tags and "message-tags" not in self.caps:
|
|
68
|
+
msg = Message(
|
|
69
|
+
tags={},
|
|
70
|
+
prefix=msg.prefix,
|
|
71
|
+
command=msg.command,
|
|
72
|
+
params=list(msg.params),
|
|
73
|
+
)
|
|
74
|
+
await self.send(msg)
|
|
75
|
+
|
|
52
76
|
async def send_numeric(self, code: str, *params: str) -> None:
|
|
53
77
|
target = self.nick or "*"
|
|
54
78
|
msg = Message(
|
|
@@ -114,6 +138,29 @@ class Client:
|
|
|
114
138
|
def _handle_pong(self, msg: Message) -> None:
|
|
115
139
|
pass # Client responding to our ping
|
|
116
140
|
|
|
141
|
+
async def _handle_cap(self, msg: Message) -> None:
|
|
142
|
+
sub = msg.params[0].upper() if msg.params else ""
|
|
143
|
+
if sub == "LS":
|
|
144
|
+
await self.send_raw(
|
|
145
|
+
f":{self.server.config.name} CAP {self.nick or '*'} LS :message-tags"
|
|
146
|
+
)
|
|
147
|
+
elif sub == "REQ":
|
|
148
|
+
requested = msg.params[1].split() if len(msg.params) >= 2 else []
|
|
149
|
+
supported = {"message-tags"}
|
|
150
|
+
if all(cap in supported for cap in requested):
|
|
151
|
+
self.caps.update(requested)
|
|
152
|
+
await self.send_raw(
|
|
153
|
+
f":{self.server.config.name} CAP {self.nick or '*'}"
|
|
154
|
+
f" ACK :{' '.join(requested)}"
|
|
155
|
+
)
|
|
156
|
+
else:
|
|
157
|
+
await self.send_raw(
|
|
158
|
+
f":{self.server.config.name} CAP {self.nick or '*'}"
|
|
159
|
+
f" NAK :{' '.join(requested)}"
|
|
160
|
+
)
|
|
161
|
+
elif sub == "END":
|
|
162
|
+
pass # no registration-gating in v1
|
|
163
|
+
|
|
117
164
|
async def _handle_nick(self, msg: Message) -> None:
|
|
118
165
|
if not msg.params:
|
|
119
166
|
await self.send_numeric(replies.ERR_NONICKNAMEGIVEN, "No nickname given")
|
|
@@ -121,6 +168,15 @@ class Client:
|
|
|
121
168
|
|
|
122
169
|
nick = msg.params[0]
|
|
123
170
|
|
|
171
|
+
# Reject reserved system-* nick prefix
|
|
172
|
+
if nick.startswith(SYSTEM_USER_PREFIX):
|
|
173
|
+
await self.send_numeric(
|
|
174
|
+
replies.ERR_ERRONEUSNICKNAME,
|
|
175
|
+
nick,
|
|
176
|
+
"Nickname prefix 'system-' is reserved",
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
|
|
124
180
|
# Enforce server name prefix
|
|
125
181
|
expected_prefix = f"{self.server.config.name}-"
|
|
126
182
|
if not nick.startswith(expected_prefix):
|
|
@@ -227,10 +283,6 @@ class Client:
|
|
|
227
283
|
for member in list(channel.members):
|
|
228
284
|
await member.send(join_msg)
|
|
229
285
|
|
|
230
|
-
await self.server.emit_event(
|
|
231
|
-
Event(type=EventType.JOIN, channel=channel_name, nick=self.nick)
|
|
232
|
-
)
|
|
233
|
-
|
|
234
286
|
# Send topic if set
|
|
235
287
|
if channel.topic:
|
|
236
288
|
await self.send_numeric(replies.RPL_TOPIC, channel_name, channel.topic)
|
|
@@ -238,6 +290,12 @@ class Client:
|
|
|
238
290
|
# Send names list
|
|
239
291
|
await self._send_names(channel)
|
|
240
292
|
|
|
293
|
+
# Emit event AFTER delivering all join-related numerics (topic, NAMES)
|
|
294
|
+
# so that the event PRIVMSG doesn't interleave with 353/366 in client buffers.
|
|
295
|
+
await self.server.emit_event(
|
|
296
|
+
Event(type=EventType.JOIN, channel=channel_name, nick=self.nick)
|
|
297
|
+
)
|
|
298
|
+
|
|
241
299
|
async def _handle_part(self, msg: Message) -> None:
|
|
242
300
|
if not msg.params:
|
|
243
301
|
await self.send_numeric(replies.ERR_NEEDMOREPARAMS, "PART", replies.MSG_NEEDMOREPARAMS)
|
|
@@ -386,6 +444,56 @@ class Client:
|
|
|
386
444
|
applied_modes.append(("+" if adding else "-") + ch)
|
|
387
445
|
applied_params.append(target_nick)
|
|
388
446
|
|
|
447
|
+
_PARAM_MODES = frozenset({"o", "v", "S"})
|
|
448
|
+
|
|
449
|
+
async def _apply_mode_char(
|
|
450
|
+
self,
|
|
451
|
+
channel,
|
|
452
|
+
channel_name: str,
|
|
453
|
+
ch: str,
|
|
454
|
+
adding: bool,
|
|
455
|
+
param_queue: list[str],
|
|
456
|
+
applied_modes: list[str],
|
|
457
|
+
applied_params: list[str],
|
|
458
|
+
) -> None:
|
|
459
|
+
"""Apply a single mode character. Consumes one param from param_queue when needed."""
|
|
460
|
+
if ch == "R":
|
|
461
|
+
self._apply_mode_r(channel, adding, applied_modes)
|
|
462
|
+
return
|
|
463
|
+
if ch not in self._PARAM_MODES or not param_queue:
|
|
464
|
+
return
|
|
465
|
+
param_value = param_queue.pop(0)
|
|
466
|
+
if ch == "S":
|
|
467
|
+
self._apply_mode_s(channel, adding, param_value, applied_modes, applied_params)
|
|
468
|
+
else:
|
|
469
|
+
await self._apply_mode_membership(
|
|
470
|
+
channel,
|
|
471
|
+
channel_name,
|
|
472
|
+
ch,
|
|
473
|
+
adding,
|
|
474
|
+
param_value,
|
|
475
|
+
applied_modes,
|
|
476
|
+
applied_params,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
async def _broadcast_mode_change(
|
|
480
|
+
self,
|
|
481
|
+
channel,
|
|
482
|
+
channel_name: str,
|
|
483
|
+
applied_modes: list[str],
|
|
484
|
+
applied_params: list[str],
|
|
485
|
+
) -> None:
|
|
486
|
+
"""Send the aggregated MODE message to all channel members."""
|
|
487
|
+
if not applied_modes:
|
|
488
|
+
return
|
|
489
|
+
mode_msg = Message(
|
|
490
|
+
prefix=self.prefix,
|
|
491
|
+
command="MODE",
|
|
492
|
+
params=[channel_name, "".join(applied_modes)] + applied_params,
|
|
493
|
+
)
|
|
494
|
+
for member in list(channel.members):
|
|
495
|
+
await member.send(mode_msg)
|
|
496
|
+
|
|
389
497
|
async def _handle_channel_mode(self, msg: Message) -> None:
|
|
390
498
|
channel_name = msg.params[0]
|
|
391
499
|
channel = self.server.channels.get(channel_name)
|
|
@@ -399,7 +507,6 @@ class Client:
|
|
|
399
507
|
await self.send_numeric(replies.RPL_CHANNELMODEIS, channel_name, "+")
|
|
400
508
|
return
|
|
401
509
|
|
|
402
|
-
modestring = msg.params[1]
|
|
403
510
|
if not channel.is_operator(self):
|
|
404
511
|
await self.send_numeric(
|
|
405
512
|
replies.ERR_CHANOPRIVSNEEDED,
|
|
@@ -408,50 +515,90 @@ class Client:
|
|
|
408
515
|
)
|
|
409
516
|
return
|
|
410
517
|
|
|
518
|
+
modestring = msg.params[1]
|
|
411
519
|
param_queue = list(msg.params[2:])
|
|
412
|
-
param_modes = {"o", "v", "S"}
|
|
413
|
-
|
|
414
520
|
adding = True
|
|
415
|
-
applied_modes = []
|
|
521
|
+
applied_modes: list[str] = []
|
|
416
522
|
applied_params: list[str] = []
|
|
417
523
|
for ch in modestring:
|
|
418
524
|
if ch == "+":
|
|
419
525
|
adding = True
|
|
420
526
|
elif ch == "-":
|
|
421
527
|
adding = False
|
|
422
|
-
|
|
423
|
-
self.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
channel,
|
|
433
|
-
channel_name,
|
|
434
|
-
ch,
|
|
435
|
-
adding,
|
|
436
|
-
param_value,
|
|
437
|
-
applied_modes,
|
|
438
|
-
applied_params,
|
|
439
|
-
)
|
|
528
|
+
else:
|
|
529
|
+
await self._apply_mode_char(
|
|
530
|
+
channel,
|
|
531
|
+
channel_name,
|
|
532
|
+
ch,
|
|
533
|
+
adding,
|
|
534
|
+
param_queue,
|
|
535
|
+
applied_modes,
|
|
536
|
+
applied_params,
|
|
537
|
+
)
|
|
440
538
|
|
|
441
539
|
# Auto-promote if no operators remain
|
|
442
540
|
if not channel.operators and channel.members:
|
|
443
541
|
channel.operators.add(min(channel.members, key=lambda m: m.nick))
|
|
444
542
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
543
|
+
await self._broadcast_mode_change(channel, channel_name, applied_modes, applied_params)
|
|
544
|
+
|
|
545
|
+
_VALID_USER_MODE_CHARS = frozenset("HABC")
|
|
546
|
+
_USER_MODE_EDGE_EVENTS: dict[tuple[str, bool], EventType] = {
|
|
547
|
+
("A", True): EventType.AGENT_CONNECT,
|
|
548
|
+
("A", False): EventType.AGENT_DISCONNECT,
|
|
549
|
+
("C", True): EventType.CONSOLE_OPEN,
|
|
550
|
+
("C", False): EventType.CONSOLE_CLOSE,
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
def _apply_user_mode_char(self, ch: str, adding: bool) -> EventType | None:
|
|
554
|
+
"""Mutate ``self.modes`` for a single mode char and return the edge event, if any.
|
|
555
|
+
|
|
556
|
+
Returns None if the char is unknown or the transition was a no-op
|
|
557
|
+
(setting an already-set mode or clearing an already-clear one).
|
|
558
|
+
"""
|
|
559
|
+
if ch not in self._VALID_USER_MODE_CHARS:
|
|
560
|
+
return None
|
|
561
|
+
had = ch in self.modes
|
|
562
|
+
if adding:
|
|
563
|
+
self.modes.add(ch)
|
|
564
|
+
else:
|
|
565
|
+
self.modes.discard(ch)
|
|
566
|
+
if had == adding:
|
|
567
|
+
return None
|
|
568
|
+
return self._USER_MODE_EDGE_EVENTS.get((ch, adding))
|
|
569
|
+
|
|
570
|
+
def _parse_mode_edges(self, modestring: str) -> list[EventType]:
|
|
571
|
+
"""Apply each char of ``modestring`` and collect the emitted edge events."""
|
|
572
|
+
pending: list[EventType] = []
|
|
573
|
+
adding = True
|
|
574
|
+
for ch in modestring:
|
|
575
|
+
if ch == "+":
|
|
576
|
+
adding = True
|
|
577
|
+
elif ch == "-":
|
|
578
|
+
adding = False
|
|
579
|
+
else:
|
|
580
|
+
event = self._apply_user_mode_char(ch, adding)
|
|
581
|
+
if event is not None:
|
|
582
|
+
pending.append(event)
|
|
583
|
+
return pending
|
|
584
|
+
|
|
585
|
+
async def _emit_user_mode_events(self, pending: list[EventType]) -> None:
|
|
586
|
+
for event_type in pending:
|
|
587
|
+
await self.server.emit_event(
|
|
588
|
+
Event(
|
|
589
|
+
type=event_type,
|
|
590
|
+
channel=None,
|
|
591
|
+
nick=self.nick,
|
|
592
|
+
data={"nick": self.nick},
|
|
593
|
+
)
|
|
450
594
|
)
|
|
451
|
-
for member in list(channel.members):
|
|
452
|
-
await member.send(mode_msg)
|
|
453
595
|
|
|
454
596
|
async def _handle_user_mode(self, msg: Message) -> None:
|
|
597
|
+
# Reject pre-registration so an unregistered socket cannot inject
|
|
598
|
+
# agent.connect / console.open into #system by sending MODE after NICK
|
|
599
|
+
# but before USER.
|
|
600
|
+
if not self._registered:
|
|
601
|
+
return
|
|
455
602
|
target_nick = msg.params[0]
|
|
456
603
|
if target_nick != self.nick:
|
|
457
604
|
await self.send_numeric(
|
|
@@ -459,19 +606,11 @@ class Client:
|
|
|
459
606
|
"Can't change mode for other users",
|
|
460
607
|
)
|
|
461
608
|
return
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
adding = True
|
|
468
|
-
elif ch == "-":
|
|
469
|
-
adding = False
|
|
470
|
-
elif ch in ("H", "A", "B"):
|
|
471
|
-
if adding:
|
|
472
|
-
self.modes.add(ch)
|
|
473
|
-
else:
|
|
474
|
-
self.modes.discard(ch)
|
|
609
|
+
|
|
610
|
+
modestring = msg.params[1] if len(msg.params) > 1 else ""
|
|
611
|
+
pending = self._parse_mode_edges(modestring)
|
|
612
|
+
await self._emit_user_mode_events(pending)
|
|
613
|
+
|
|
475
614
|
mode_str = "+" + "".join(sorted(self.modes)) if self.modes else "+"
|
|
476
615
|
await self.send_numeric(replies.RPL_UMODEIS, mode_str)
|
|
477
616
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Event type catalog and render-template registry.
|
|
2
|
+
|
|
3
|
+
Event type names follow the dotted-lowercase convention enforced by
|
|
4
|
+
`EVENT_TYPE_RE` in `culture.constants`. Render templates map a type to a
|
|
5
|
+
function that produces the human-readable PRIVMSG body for humans and
|
|
6
|
+
vanilla IRC clients. The structured payload is attached as IRCv3 message
|
|
7
|
+
tags by the server's emit path (see `culture/agentirc/ircd.py`); this
|
|
8
|
+
module is presentation-only.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Any, Callable
|
|
15
|
+
|
|
16
|
+
from culture.agentirc.skill import EventType
|
|
17
|
+
from culture.constants import EVENT_TYPE_RE
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# Event types whose content is already delivered to clients via the normal
|
|
22
|
+
# IRC path (PRIVMSG, NOTICE, TOPIC) or has dedicated storage (threads).
|
|
23
|
+
# Used by both IRCd._surface_event_privmsg (to avoid double-delivery) and
|
|
24
|
+
# HistorySkill (to avoid double-storage). Keep this as the single source
|
|
25
|
+
# of truth — do not duplicate.
|
|
26
|
+
NO_SURFACE_EVENT_TYPES: frozenset[str] = frozenset(
|
|
27
|
+
{
|
|
28
|
+
EventType.MESSAGE.value,
|
|
29
|
+
EventType.THREAD_CREATE.value,
|
|
30
|
+
EventType.THREAD_MESSAGE.value,
|
|
31
|
+
EventType.THREAD_CLOSE.value,
|
|
32
|
+
EventType.TOPIC.value,
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
RenderFn = Callable[[dict[str, Any], str | None], str]
|
|
37
|
+
|
|
38
|
+
_TEMPLATES: dict[str, RenderFn] = {}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def register(event_type: str, fn: RenderFn) -> None:
|
|
42
|
+
_TEMPLATES[event_type] = fn
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def validate_event_type(name: str) -> bool:
|
|
46
|
+
return bool(EVENT_TYPE_RE.match(name))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def render_event(event_type: str, data: dict[str, Any], channel: str | None) -> str:
|
|
50
|
+
fn = _TEMPLATES.get(event_type)
|
|
51
|
+
if fn is None:
|
|
52
|
+
return f"{event_type} {data}"
|
|
53
|
+
try:
|
|
54
|
+
return fn(data, channel)
|
|
55
|
+
except Exception:
|
|
56
|
+
logger.exception("render template for %s failed", event_type)
|
|
57
|
+
return f"{event_type} {data}"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# -------- built-in render templates --------
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _nick_action(verb: str) -> RenderFn:
|
|
64
|
+
def _render(data, channel):
|
|
65
|
+
nick = data.get("nick", "<unknown>")
|
|
66
|
+
if channel:
|
|
67
|
+
return f"{nick} {verb} {channel}"
|
|
68
|
+
return f"{nick} {verb}"
|
|
69
|
+
|
|
70
|
+
return _render
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
register("user.join", _nick_action("joined"))
|
|
74
|
+
register("user.part", _nick_action("left"))
|
|
75
|
+
register(
|
|
76
|
+
"user.quit",
|
|
77
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} quit: {d.get('reason', '')}".rstrip(": "),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
register("agent.connect", lambda d, c: f"{d.get('nick', '<unknown>')} connected")
|
|
81
|
+
register(
|
|
82
|
+
"agent.disconnect",
|
|
83
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} disconnected",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
register("console.open", lambda d, c: f"{d.get('nick', '<unknown>')} opened a console")
|
|
87
|
+
register("console.close", lambda d, c: f"{d.get('nick', '<unknown>')} closed their console")
|
|
88
|
+
|
|
89
|
+
register("server.wake", lambda d, c: f"server {d.get('server', '<unknown>')} is up")
|
|
90
|
+
register(
|
|
91
|
+
"server.sleep",
|
|
92
|
+
lambda d, c: f"server {d.get('server', '<unknown>')} is shutting down",
|
|
93
|
+
)
|
|
94
|
+
register("server.link", lambda d, c: f"linked to {d.get('peer', '<unknown>')}")
|
|
95
|
+
register("server.unlink", lambda d, c: f"unlinked from {d.get('peer', '<unknown>')}")
|
|
96
|
+
|
|
97
|
+
register("room.create", lambda d, c: f"{d.get('nick', '<unknown>')} created room {c}")
|
|
98
|
+
register("room.archive", lambda d, c: f"{d.get('nick', '<unknown>')} archived {c}")
|
|
99
|
+
register("room.meta", lambda d, c: f"{d.get('nick', '<unknown>')} updated {c} metadata")
|
|
100
|
+
|
|
101
|
+
register(
|
|
102
|
+
"thread.create",
|
|
103
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} started thread [{d.get('thread', '?')}] in {c}",
|
|
104
|
+
)
|
|
105
|
+
register(
|
|
106
|
+
"thread.message",
|
|
107
|
+
lambda d, c: f"[{d.get('thread', '?')}] {d.get('nick', '<unknown>')}: {d.get('text', '')}",
|
|
108
|
+
)
|
|
109
|
+
register(
|
|
110
|
+
"thread.close",
|
|
111
|
+
lambda d, c: f"thread [{d.get('thread', '?')}] in {c} closed",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
register(
|
|
115
|
+
"tags.update",
|
|
116
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} tags → {', '.join(d.get('tags', []))}",
|
|
117
|
+
)
|