agentirc-cli 4.3.6__tar.gz → 4.4.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-4.3.6 → agentirc_cli-4.4.0}/CHANGELOG.md +26 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/PKG-INFO +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/bot_manager.py +7 -3
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/config.py +1 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/agent.py +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/channel.py +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/mesh.py +1 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/server.py +23 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/constants.py +2 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/ipc.py +3 -3
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/agent_runner.py +8 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/daemon.py +20 -14
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/irc_transport.py +19 -9
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/skill/irc_client.py +2 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/socket_server.py +4 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/agent_runner.py +2 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/daemon.py +20 -14
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/irc_transport.py +19 -9
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/skill/irc_client.py +2 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/socket_server.py +4 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/agent_runner.py +8 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/daemon.py +20 -14
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/irc_transport.py +19 -9
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/skill/irc_client.py +2 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/socket_server.py +4 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/daemon.py +20 -14
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/irc_transport.py +19 -9
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/skill/irc_client.py +2 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/socket_server.py +4 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/client.py +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/observer.py +3 -3
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/collector.py +18 -18
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/web/style.css +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/pidfile.py +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/history.md +6 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/client.py +1 -1
- agentirc_cli-4.4.0/culture/server/history_store.py +91 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/ircd.py +2 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/server_link.py +6 -6
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skill.py +3 -3
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/history.py +48 -1
- agentirc_cli-4.4.0/docs/persistent-history.md +61 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/daemon.py +11 -10
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/irc_transport.py +19 -9
- agentirc_cli-4.4.0/packages/agent-harness/message_buffer.py +63 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/skill/irc_client.py +2 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/socket_server.py +4 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/pyproject.toml +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/conftest.py +8 -5
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_acp_daemon.py +0 -6
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_agent_runner.py +2 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_commands.py +0 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_connection.py +0 -4
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_icons.py +1 -2
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_federation.py +40 -34
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_history.py +178 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_irc_transport.py +55 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_link_reconnect.py +16 -15
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mentions.py +3 -7
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_rooms.py +5 -5
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_supervisor.py +2 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_webhook.py +1 -1
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/uv.lock +1 -1
- agentirc_cli-4.3.6/packages/agent-harness/message_buffer.py +0 -65
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.flake8 +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.gitignore +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.pr_agent.toml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.pylintrc +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/CLAUDE.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/CNAME +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/Gemfile +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/Gemfile.lock +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/LICENSE +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/README.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/SECURITY.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/_config.yml +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/__main__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/aio.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/bot.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/bot.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/display.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/skills.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/app.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/commands.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/credentials.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/formatting.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/learn_prompt.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/mesh_config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/model.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/persistence.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/commands.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/message.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/replies.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/__main__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/channel.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/remote_client.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/room_store.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/rooms_util.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/icon.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/rooms.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/threads.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/thread_store.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/agent-lifecycle.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/agentic-self-learn.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/agent-client.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/harness-conformance.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/index.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer1-core-irc.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer2-attention.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer3-skills.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer4-federation.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer5-agent-harness.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/server-architecture.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/threads.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/channel-polling.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/culture-cli.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/getting-started.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/index.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/SECURITY.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/bots.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/ci.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/cli.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/docs-site.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/index.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/ops-tooling.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/overview.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/publishing.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/rooms.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/server-rename.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases-index.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/what-is-culture.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/sonar-project.properties +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/__init__.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_archive.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bot.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bot_config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_channel.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_connection.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_client.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_integration.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_http_listener.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_message.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_modes.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_persistence.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_pidfile.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_skills.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_template_engine.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_threads.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_wait_for_port.py +0 -0
|
@@ -4,6 +4,32 @@ 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
|
+
## [4.4.0] - 2026-04-07
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- SQLite-backed persistent channel history (survives server restarts)
|
|
13
|
+
- --data-dir CLI flag for server start (default: ~/.culture/data)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Multi-line messages truncated to first line in send_privmsg and thread methods
|
|
19
|
+
- data_dir never wired to ServerConfig, silently disabling room/thread persistence
|
|
20
|
+
|
|
21
|
+
## [4.3.7] - 2026-04-07
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Extract duplicate string constants (S1192, #85)
|
|
27
|
+
- Remove redundant exception classes in except clauses (S5713, #86)
|
|
28
|
+
- Clean up unused variables and function parameters (S1481/S1172, #88)
|
|
29
|
+
- Remove f-strings without replacement fields (S3457, #89)
|
|
30
|
+
- Address hardcoded credential warnings with test constants (S2068, #90)
|
|
31
|
+
- Fix miscellaneous code quality issues: asyncio.timeout, nested ternaries, empty methods, CSS contrast (S7483/S3358/S1186/S7924, #91)
|
|
32
|
+
|
|
7
33
|
## [4.3.6] - 2026-04-07
|
|
8
34
|
|
|
9
35
|
|
|
@@ -6,15 +6,19 @@ import logging
|
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
from culture.bots.bot import Bot
|
|
9
|
-
from culture.bots.config import
|
|
9
|
+
from culture.bots.config import (
|
|
10
|
+
BOT_CONFIG_FILE,
|
|
11
|
+
BOTS_DIR,
|
|
12
|
+
BotConfig,
|
|
13
|
+
load_bot_config,
|
|
14
|
+
save_bot_config,
|
|
15
|
+
)
|
|
10
16
|
|
|
11
17
|
if TYPE_CHECKING:
|
|
12
18
|
from culture.server.ircd import IRCd
|
|
13
19
|
|
|
14
20
|
logger = logging.getLogger(__name__)
|
|
15
21
|
|
|
16
|
-
BOT_CONFIG_FILE = "bot.yaml"
|
|
17
|
-
|
|
18
22
|
|
|
19
23
|
class BotManager:
|
|
20
24
|
"""Loads, starts, stops, and dispatches webhooks to bots."""
|
|
@@ -399,7 +399,7 @@ def _probe_server_connection(host: str, port: int, server_name: str) -> None:
|
|
|
399
399
|
try:
|
|
400
400
|
with _socket.create_connection((host, port), timeout=2):
|
|
401
401
|
pass
|
|
402
|
-
except
|
|
402
|
+
except OSError:
|
|
403
403
|
hint = ""
|
|
404
404
|
server_pid = read_pid(f"server-{server_name}")
|
|
405
405
|
if not server_pid or not is_process_alive(server_pid):
|
|
@@ -55,7 +55,7 @@ def dispatch(args: argparse.Namespace) -> None:
|
|
|
55
55
|
sys.exit(1)
|
|
56
56
|
try:
|
|
57
57
|
handler(args)
|
|
58
|
-
except (
|
|
58
|
+
except (TimeoutError, OSError) as exc:
|
|
59
59
|
msg = str(exc)
|
|
60
60
|
if (
|
|
61
61
|
"Timed out" in msg
|
|
@@ -15,7 +15,6 @@ from culture.clients.claude.config import (
|
|
|
15
15
|
load_config,
|
|
16
16
|
load_config_or_default,
|
|
17
17
|
)
|
|
18
|
-
from culture.pidfile import is_process_alive, read_pid
|
|
19
18
|
|
|
20
19
|
from .shared.constants import AGENTS_YAML, CULTURE_DIR, DEFAULT_CONFIG
|
|
21
20
|
from .shared.mesh import build_server_start_cmd, generate_mesh_from_agents
|
|
@@ -469,7 +468,7 @@ def _wait_for_server_port(port: int, retries: int = 50, interval: float = 0.1) -
|
|
|
469
468
|
try:
|
|
470
469
|
with _socket.create_connection(("localhost", port), timeout=1):
|
|
471
470
|
return
|
|
472
|
-
except
|
|
471
|
+
except OSError:
|
|
473
472
|
time.sleep(interval)
|
|
474
473
|
|
|
475
474
|
|
|
@@ -74,6 +74,11 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
74
74
|
action="store_true",
|
|
75
75
|
help="Run in foreground (for service managers)",
|
|
76
76
|
)
|
|
77
|
+
srv_start.add_argument(
|
|
78
|
+
"--data-dir",
|
|
79
|
+
default=os.path.expanduser("~/.culture/data"),
|
|
80
|
+
help="Data directory for persistent storage (default: ~/.culture/data)",
|
|
81
|
+
)
|
|
77
82
|
|
|
78
83
|
srv_stop = server_sub.add_parser("stop", help="Stop the IRC server daemon")
|
|
79
84
|
srv_stop.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
|
|
@@ -279,7 +284,9 @@ def _run_foreground(args: argparse.Namespace, pid_name: str, links: list) -> Non
|
|
|
279
284
|
print(f" Webhook port: {args.webhook_port}")
|
|
280
285
|
_maybe_set_default_server(args.name)
|
|
281
286
|
try:
|
|
282
|
-
asyncio.run(
|
|
287
|
+
asyncio.run(
|
|
288
|
+
_run_server(args.name, args.host, args.port, links, args.webhook_port, args.data_dir)
|
|
289
|
+
)
|
|
283
290
|
finally:
|
|
284
291
|
remove_pid(pid_name)
|
|
285
292
|
|
|
@@ -369,7 +376,9 @@ def _daemonize_server(args: argparse.Namespace, pid_name: str, links: list) -> N
|
|
|
369
376
|
write_pid(pid_name, os.getpid())
|
|
370
377
|
|
|
371
378
|
try:
|
|
372
|
-
asyncio.run(
|
|
379
|
+
asyncio.run(
|
|
380
|
+
_run_server(args.name, args.host, args.port, links, args.webhook_port, args.data_dir)
|
|
381
|
+
)
|
|
373
382
|
finally:
|
|
374
383
|
remove_pid(pid_name)
|
|
375
384
|
os._exit(0)
|
|
@@ -391,14 +400,24 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
391
400
|
|
|
392
401
|
|
|
393
402
|
async def _run_server(
|
|
394
|
-
name: str,
|
|
403
|
+
name: str,
|
|
404
|
+
host: str,
|
|
405
|
+
port: int,
|
|
406
|
+
links: list | None = None,
|
|
407
|
+
webhook_port: int = 7680,
|
|
408
|
+
data_dir: str = "",
|
|
395
409
|
) -> None:
|
|
396
410
|
"""Run the IRC server (called in the daemon child process)."""
|
|
397
411
|
from culture.server.config import ServerConfig
|
|
398
412
|
from culture.server.ircd import IRCd
|
|
399
413
|
|
|
400
414
|
config = ServerConfig(
|
|
401
|
-
name=name,
|
|
415
|
+
name=name,
|
|
416
|
+
host=host,
|
|
417
|
+
port=port,
|
|
418
|
+
webhook_port=webhook_port,
|
|
419
|
+
links=links or [],
|
|
420
|
+
data_dir=data_dir,
|
|
402
421
|
)
|
|
403
422
|
ircd = IRCd(config)
|
|
404
423
|
await ircd.start()
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
from culture.bots.config import BOT_CONFIG_FILE # noqa: F401
|
|
6
|
+
|
|
5
7
|
DEFAULT_CONFIG = os.path.expanduser("~/.culture/agents.yaml")
|
|
6
8
|
LOG_DIR = os.path.expanduser("~/.culture/logs")
|
|
7
9
|
|
|
@@ -9,7 +11,6 @@ _CONFIG_HELP = "Config file path"
|
|
|
9
11
|
_SERVER_NAME_HELP = "Server name"
|
|
10
12
|
_BOT_NAME_HELP = "Bot name"
|
|
11
13
|
|
|
12
|
-
BOT_CONFIG_FILE = "bot.yaml"
|
|
13
14
|
DEFAULT_CHANNEL = "#general"
|
|
14
15
|
NO_AGENTS_MSG = "No agents configured"
|
|
15
16
|
CULTURE_DIR = ".culture"
|
|
@@ -24,7 +24,7 @@ async def ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
|
|
|
24
24
|
asyncio.open_unix_connection(socket_path),
|
|
25
25
|
timeout=3.0,
|
|
26
26
|
)
|
|
27
|
-
except
|
|
27
|
+
except OSError:
|
|
28
28
|
return None
|
|
29
29
|
try:
|
|
30
30
|
req = make_request(msg_type, **kwargs)
|
|
@@ -40,13 +40,13 @@ async def ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
|
|
|
40
40
|
msg = decode_message(data)
|
|
41
41
|
if msg and msg.get("type") == "response":
|
|
42
42
|
return msg
|
|
43
|
-
except
|
|
43
|
+
except OSError:
|
|
44
44
|
return None
|
|
45
45
|
finally:
|
|
46
46
|
writer.close()
|
|
47
47
|
try:
|
|
48
48
|
await writer.wait_closed()
|
|
49
|
-
except
|
|
49
|
+
except OSError:
|
|
50
50
|
pass
|
|
51
51
|
|
|
52
52
|
|
|
@@ -151,7 +151,8 @@ class ACPAgentRunner:
|
|
|
151
151
|
return
|
|
152
152
|
try:
|
|
153
153
|
self._process.terminate()
|
|
154
|
-
|
|
154
|
+
async with asyncio.timeout(5):
|
|
155
|
+
await self._process.wait()
|
|
155
156
|
except (asyncio.TimeoutError, ProcessLookupError):
|
|
156
157
|
try:
|
|
157
158
|
self._process.kill()
|
|
@@ -209,7 +210,8 @@ class ACPAgentRunner:
|
|
|
209
210
|
await self._process.stdin.drain()
|
|
210
211
|
|
|
211
212
|
try:
|
|
212
|
-
|
|
213
|
+
async with asyncio.timeout(timeout):
|
|
214
|
+
return await future
|
|
213
215
|
except (asyncio.TimeoutError, asyncio.CancelledError):
|
|
214
216
|
self._pending.pop(req_id, None)
|
|
215
217
|
if not future.done():
|
|
@@ -244,14 +246,16 @@ class ACPAgentRunner:
|
|
|
244
246
|
if not self._process:
|
|
245
247
|
return -1
|
|
246
248
|
try:
|
|
247
|
-
|
|
249
|
+
async with asyncio.timeout(5):
|
|
250
|
+
return await self._process.wait()
|
|
248
251
|
except asyncio.TimeoutError:
|
|
249
252
|
try:
|
|
250
253
|
self._process.kill()
|
|
251
254
|
except ProcessLookupError:
|
|
252
255
|
pass
|
|
253
256
|
try:
|
|
254
|
-
|
|
257
|
+
async with asyncio.timeout(1):
|
|
258
|
+
return await self._process.wait()
|
|
255
259
|
except asyncio.TimeoutError:
|
|
256
260
|
return -1
|
|
257
261
|
|
|
@@ -31,6 +31,8 @@ logger = logging.getLogger(__name__)
|
|
|
31
31
|
|
|
32
32
|
# IPC validation error messages
|
|
33
33
|
_ERR_MISSING_CHANNEL = "Missing 'channel'"
|
|
34
|
+
_ERR_MISSING_CHANNEL_THREAD = "Missing 'channel' or 'thread'"
|
|
35
|
+
_ERR_MISSING_CHANNEL_THREAD_MSG = "Missing 'channel', 'thread', or 'message'"
|
|
34
36
|
|
|
35
37
|
MAX_CRASH_COUNT = 3
|
|
36
38
|
CRASH_WINDOW_SECONDS = 300
|
|
@@ -304,7 +306,7 @@ class ACPDaemon:
|
|
|
304
306
|
prompt = (
|
|
305
307
|
f"[IRC Channel Poll: {channel}] Recent unread messages:\n"
|
|
306
308
|
f"{lines}\n\n"
|
|
307
|
-
|
|
309
|
+
"Respond naturally if any messages need your attention."
|
|
308
310
|
)
|
|
309
311
|
self._mention_targets.append(channel)
|
|
310
312
|
task = asyncio.create_task(self._agent_runner.send_prompt(prompt))
|
|
@@ -485,10 +487,10 @@ class ACPDaemon:
|
|
|
485
487
|
return self.agent.system_prompt
|
|
486
488
|
return (
|
|
487
489
|
f"You are {self.agent.nick}, an AI agent on the culture IRC network.\n"
|
|
488
|
-
|
|
490
|
+
"You have IRC tools available via the irc skill. Use them to communicate.\n"
|
|
489
491
|
f"Your working directory is {self.agent.directory}.\n"
|
|
490
|
-
|
|
491
|
-
|
|
492
|
+
"Check IRC channels periodically with irc_read() for new messages.\n"
|
|
493
|
+
"When you finish a task, share results in the appropriate channel with irc_send()."
|
|
492
494
|
)
|
|
493
495
|
|
|
494
496
|
async def _record_crash_time(self, exit_code: int) -> None:
|
|
@@ -642,6 +644,13 @@ class ACPDaemon:
|
|
|
642
644
|
if query and running and not self._paused:
|
|
643
645
|
description = await self._query_agent_status()
|
|
644
646
|
|
|
647
|
+
if self._paused:
|
|
648
|
+
activity = "paused"
|
|
649
|
+
elif running:
|
|
650
|
+
activity = "working"
|
|
651
|
+
else:
|
|
652
|
+
activity = "idle"
|
|
653
|
+
|
|
645
654
|
return make_response(
|
|
646
655
|
req_id,
|
|
647
656
|
ok=True,
|
|
@@ -650,7 +659,7 @@ class ACPDaemon:
|
|
|
650
659
|
"paused": self._paused,
|
|
651
660
|
"turn_count": turn_count,
|
|
652
661
|
"last_activation": self._last_activation,
|
|
653
|
-
"activity":
|
|
662
|
+
"activity": activity,
|
|
654
663
|
"description": description,
|
|
655
664
|
},
|
|
656
665
|
)
|
|
@@ -687,7 +696,8 @@ class ACPDaemon:
|
|
|
687
696
|
"[SYSTEM] Briefly describe what you are currently working on "
|
|
688
697
|
"in one sentence. Reply with just the description, no preamble."
|
|
689
698
|
)
|
|
690
|
-
|
|
699
|
+
async with asyncio.timeout(10.0):
|
|
700
|
+
await self._status_query_event.wait()
|
|
691
701
|
return self._truncate_first_line(self._status_query_response) or "nothing"
|
|
692
702
|
except asyncio.TimeoutError:
|
|
693
703
|
return "busy (no response)"
|
|
@@ -744,9 +754,7 @@ class ACPDaemon:
|
|
|
744
754
|
thread_name = msg.get("thread", "")
|
|
745
755
|
text = msg.get("message", "")
|
|
746
756
|
if not channel or not thread_name or not text:
|
|
747
|
-
return make_response(
|
|
748
|
-
req_id, ok=False, error="Missing 'channel', 'thread', or 'message'"
|
|
749
|
-
)
|
|
757
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD_MSG)
|
|
750
758
|
assert self._transport is not None
|
|
751
759
|
await self._transport.send_thread_create(channel, thread_name, text)
|
|
752
760
|
return make_response(req_id, ok=True)
|
|
@@ -756,9 +764,7 @@ class ACPDaemon:
|
|
|
756
764
|
thread_name = msg.get("thread", "")
|
|
757
765
|
text = msg.get("message", "")
|
|
758
766
|
if not channel or not thread_name or not text:
|
|
759
|
-
return make_response(
|
|
760
|
-
req_id, ok=False, error="Missing 'channel', 'thread', or 'message'"
|
|
761
|
-
)
|
|
767
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD_MSG)
|
|
762
768
|
assert self._transport is not None
|
|
763
769
|
await self._transport.send_thread_reply(channel, thread_name, text)
|
|
764
770
|
return make_response(req_id, ok=True)
|
|
@@ -776,7 +782,7 @@ class ACPDaemon:
|
|
|
776
782
|
thread_name = msg.get("thread", "")
|
|
777
783
|
summary = msg.get("summary", "")
|
|
778
784
|
if not channel or not thread_name:
|
|
779
|
-
return make_response(req_id, ok=False, error=
|
|
785
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD)
|
|
780
786
|
assert self._transport is not None
|
|
781
787
|
await self._transport.send_thread_close(channel, thread_name, summary)
|
|
782
788
|
return make_response(req_id, ok=True)
|
|
@@ -786,7 +792,7 @@ class ACPDaemon:
|
|
|
786
792
|
thread_name = msg.get("thread", "")
|
|
787
793
|
limit = int(msg.get("limit", 50))
|
|
788
794
|
if not channel or not thread_name:
|
|
789
|
-
return make_response(req_id, ok=False, error=
|
|
795
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD)
|
|
790
796
|
assert self._buffer is not None
|
|
791
797
|
messages = self._buffer.read_thread(channel, thread_name, limit=limit)
|
|
792
798
|
return make_response(
|
|
@@ -60,7 +60,7 @@ class IRCTransport:
|
|
|
60
60
|
async def _do_connect(self) -> None:
|
|
61
61
|
try:
|
|
62
62
|
self._reader, self._writer = await asyncio.open_connection(self.host, self.port)
|
|
63
|
-
except
|
|
63
|
+
except OSError as exc:
|
|
64
64
|
raise ConnectionError(
|
|
65
65
|
f"Cannot connect to IRC server at {self.host}:{self.port} "
|
|
66
66
|
f"- is the server running?"
|
|
@@ -77,26 +77,36 @@ class IRCTransport:
|
|
|
77
77
|
if self._writer:
|
|
78
78
|
try:
|
|
79
79
|
await self._send_raw("QUIT :daemon shutdown")
|
|
80
|
-
except
|
|
80
|
+
except OSError:
|
|
81
81
|
pass
|
|
82
82
|
self._writer.close()
|
|
83
83
|
try:
|
|
84
84
|
await self._writer.wait_closed()
|
|
85
|
-
except
|
|
85
|
+
except ConnectionError:
|
|
86
86
|
pass
|
|
87
87
|
self.connected = False
|
|
88
88
|
|
|
89
89
|
async def send_privmsg(self, target: str, text: str) -> None:
|
|
90
|
-
|
|
90
|
+
for line in text.splitlines():
|
|
91
|
+
if line:
|
|
92
|
+
await self._send_raw(f"PRIVMSG {target} :{line}")
|
|
91
93
|
|
|
92
94
|
async def send_thread_create(self, channel: str, thread_name: str, text: str) -> None:
|
|
93
|
-
|
|
95
|
+
lines = [l for l in text.splitlines() if l]
|
|
96
|
+
if not lines:
|
|
97
|
+
return
|
|
98
|
+
await self._send_raw(f"THREAD CREATE {channel} {thread_name} :{lines[0]}")
|
|
99
|
+
for line in lines[1:]:
|
|
100
|
+
await self._send_raw(f"THREAD REPLY {channel} {thread_name} :{line}")
|
|
94
101
|
|
|
95
102
|
async def send_thread_reply(self, channel: str, thread_name: str, text: str) -> None:
|
|
96
|
-
|
|
103
|
+
for line in text.splitlines():
|
|
104
|
+
if line:
|
|
105
|
+
await self._send_raw(f"THREAD REPLY {channel} {thread_name} :{line}")
|
|
97
106
|
|
|
98
107
|
async def send_thread_close(self, channel: str, thread_name: str, summary: str) -> None:
|
|
99
|
-
|
|
108
|
+
clean = " ".join(summary.splitlines()).strip()
|
|
109
|
+
await self._send_raw(f"THREADCLOSE {channel} {thread_name} :{clean}")
|
|
100
110
|
|
|
101
111
|
async def send_threads_list(self, channel: str) -> None:
|
|
102
112
|
await self._send_raw(f"THREADS {channel}")
|
|
@@ -139,7 +149,7 @@ class IRCTransport:
|
|
|
139
149
|
await self._handle(msg)
|
|
140
150
|
except asyncio.CancelledError:
|
|
141
151
|
raise
|
|
142
|
-
except
|
|
152
|
+
except OSError:
|
|
143
153
|
logger.warning("IRC connection lost")
|
|
144
154
|
finally:
|
|
145
155
|
self.connected = False
|
|
@@ -159,7 +169,7 @@ class IRCTransport:
|
|
|
159
169
|
logger.info("Reconnected to IRC")
|
|
160
170
|
self._reconnecting = False
|
|
161
171
|
return
|
|
162
|
-
except
|
|
172
|
+
except OSError:
|
|
163
173
|
delay = min(delay * 2, 60)
|
|
164
174
|
|
|
165
175
|
async def _handle(self, msg: Message) -> None:
|
|
@@ -56,7 +56,7 @@ class SkillClient:
|
|
|
56
56
|
self._writer.close()
|
|
57
57
|
try:
|
|
58
58
|
await self._writer.wait_closed()
|
|
59
|
-
except
|
|
59
|
+
except OSError:
|
|
60
60
|
pass
|
|
61
61
|
self._writer = None
|
|
62
62
|
self._reader = None
|
|
@@ -91,7 +91,7 @@ class SkillClient:
|
|
|
91
91
|
if msg is None:
|
|
92
92
|
continue
|
|
93
93
|
self._dispatch_message(msg)
|
|
94
|
-
except (asyncio.IncompleteReadError,
|
|
94
|
+
except (asyncio.IncompleteReadError, OSError):
|
|
95
95
|
pass
|
|
96
96
|
finally:
|
|
97
97
|
self._fail_pending("Connection lost")
|
|
@@ -32,7 +32,7 @@ class SocketServer:
|
|
|
32
32
|
try:
|
|
33
33
|
writer.close()
|
|
34
34
|
await writer.wait_closed()
|
|
35
|
-
except
|
|
35
|
+
except OSError:
|
|
36
36
|
pass
|
|
37
37
|
self._clients.clear()
|
|
38
38
|
if self._server:
|
|
@@ -52,7 +52,7 @@ class SocketServer:
|
|
|
52
52
|
try:
|
|
53
53
|
writer.write(data)
|
|
54
54
|
await writer.drain()
|
|
55
|
-
except
|
|
55
|
+
except OSError:
|
|
56
56
|
self._clients.remove(writer)
|
|
57
57
|
else:
|
|
58
58
|
await self._whisper_queue.put(data)
|
|
@@ -76,7 +76,7 @@ class SocketServer:
|
|
|
76
76
|
await writer.drain()
|
|
77
77
|
except asyncio.QueueEmpty:
|
|
78
78
|
break
|
|
79
|
-
except
|
|
79
|
+
except OSError:
|
|
80
80
|
break
|
|
81
81
|
|
|
82
82
|
async def _process_client_messages(
|
|
@@ -112,7 +112,7 @@ class SocketServer:
|
|
|
112
112
|
writer.write(encode_message(err_resp))
|
|
113
113
|
await writer.drain()
|
|
114
114
|
return True
|
|
115
|
-
except
|
|
115
|
+
except OSError:
|
|
116
116
|
return False
|
|
117
117
|
|
|
118
118
|
def _cleanup_client(self, writer: asyncio.StreamWriter) -> None:
|
|
@@ -81,7 +81,8 @@ class AgentRunner:
|
|
|
81
81
|
self._prompt_queue.put_nowait("")
|
|
82
82
|
if self._task and not self._task.done():
|
|
83
83
|
try:
|
|
84
|
-
|
|
84
|
+
async with asyncio.timeout(5.0):
|
|
85
|
+
await asyncio.shield(self._task)
|
|
85
86
|
except (asyncio.TimeoutError, asyncio.CancelledError):
|
|
86
87
|
self._task.cancel()
|
|
87
88
|
await asyncio.gather(self._task, return_exceptions=True)
|
|
@@ -25,6 +25,8 @@ CRASH_RESTART_DELAY = 5
|
|
|
25
25
|
|
|
26
26
|
# IPC validation error messages
|
|
27
27
|
_ERR_MISSING_CHANNEL = "Missing 'channel'"
|
|
28
|
+
_ERR_MISSING_CHANNEL_THREAD = "Missing 'channel' or 'thread'"
|
|
29
|
+
_ERR_MISSING_CHANNEL_THREAD_MSG = "Missing 'channel', 'thread', or 'message'"
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
class AgentDaemon:
|
|
@@ -260,7 +262,7 @@ class AgentDaemon:
|
|
|
260
262
|
prompt = (
|
|
261
263
|
f"[IRC Channel Poll: {channel}] Recent unread messages:\n"
|
|
262
264
|
f"{lines}\n\n"
|
|
263
|
-
|
|
265
|
+
"Respond naturally if any messages need your attention."
|
|
264
266
|
)
|
|
265
267
|
task = asyncio.create_task(self._agent_runner.send_prompt(prompt))
|
|
266
268
|
self._background_tasks.add(task)
|
|
@@ -398,10 +400,10 @@ class AgentDaemon:
|
|
|
398
400
|
return self.agent.system_prompt
|
|
399
401
|
return (
|
|
400
402
|
f"You are {self.agent.nick}, an AI agent on the culture IRC network.\n"
|
|
401
|
-
|
|
403
|
+
"You have IRC tools available via the irc skill. Use them to communicate.\n"
|
|
402
404
|
f"Your working directory is {self.agent.directory}.\n"
|
|
403
|
-
|
|
404
|
-
|
|
405
|
+
"Check IRC channels periodically with irc_read() for new messages.\n"
|
|
406
|
+
"When you finish a task, share results in the appropriate channel with irc_send()."
|
|
405
407
|
)
|
|
406
408
|
|
|
407
409
|
async def _record_crash_time(self, exit_code: int) -> None:
|
|
@@ -563,6 +565,13 @@ class AgentDaemon:
|
|
|
563
565
|
if query and running and not self._paused:
|
|
564
566
|
description = await self._query_agent_status()
|
|
565
567
|
|
|
568
|
+
if self._paused:
|
|
569
|
+
activity = "paused"
|
|
570
|
+
elif running:
|
|
571
|
+
activity = "working"
|
|
572
|
+
else:
|
|
573
|
+
activity = "idle"
|
|
574
|
+
|
|
566
575
|
return make_response(
|
|
567
576
|
req_id,
|
|
568
577
|
ok=True,
|
|
@@ -571,7 +580,7 @@ class AgentDaemon:
|
|
|
571
580
|
"paused": self._paused,
|
|
572
581
|
"turn_count": turn_count,
|
|
573
582
|
"last_activation": self._last_activation,
|
|
574
|
-
"activity":
|
|
583
|
+
"activity": activity,
|
|
575
584
|
"description": description,
|
|
576
585
|
},
|
|
577
586
|
)
|
|
@@ -605,7 +614,8 @@ class AgentDaemon:
|
|
|
605
614
|
"[SYSTEM] Briefly describe what you are currently working on "
|
|
606
615
|
"in one sentence. Reply with just the description, no preamble."
|
|
607
616
|
)
|
|
608
|
-
|
|
617
|
+
async with asyncio.timeout(10.0):
|
|
618
|
+
await self._status_query_event.wait()
|
|
609
619
|
return self._truncate_first_line(self._status_query_response) or "nothing"
|
|
610
620
|
except asyncio.TimeoutError:
|
|
611
621
|
return "busy (no response)"
|
|
@@ -662,9 +672,7 @@ class AgentDaemon:
|
|
|
662
672
|
thread_name = msg.get("thread", "")
|
|
663
673
|
text = msg.get("message", "")
|
|
664
674
|
if not channel or not thread_name or not text:
|
|
665
|
-
return make_response(
|
|
666
|
-
req_id, ok=False, error="Missing 'channel', 'thread', or 'message'"
|
|
667
|
-
)
|
|
675
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD_MSG)
|
|
668
676
|
assert self._transport is not None
|
|
669
677
|
await self._transport.send_thread_create(channel, thread_name, text)
|
|
670
678
|
return make_response(req_id, ok=True)
|
|
@@ -674,9 +682,7 @@ class AgentDaemon:
|
|
|
674
682
|
thread_name = msg.get("thread", "")
|
|
675
683
|
text = msg.get("message", "")
|
|
676
684
|
if not channel or not thread_name or not text:
|
|
677
|
-
return make_response(
|
|
678
|
-
req_id, ok=False, error="Missing 'channel', 'thread', or 'message'"
|
|
679
|
-
)
|
|
685
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD_MSG)
|
|
680
686
|
assert self._transport is not None
|
|
681
687
|
await self._transport.send_thread_reply(channel, thread_name, text)
|
|
682
688
|
return make_response(req_id, ok=True)
|
|
@@ -694,7 +700,7 @@ class AgentDaemon:
|
|
|
694
700
|
thread_name = msg.get("thread", "")
|
|
695
701
|
summary = msg.get("summary", "")
|
|
696
702
|
if not channel or not thread_name:
|
|
697
|
-
return make_response(req_id, ok=False, error=
|
|
703
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD)
|
|
698
704
|
assert self._transport is not None
|
|
699
705
|
await self._transport.send_thread_close(channel, thread_name, summary)
|
|
700
706
|
return make_response(req_id, ok=True)
|
|
@@ -704,7 +710,7 @@ class AgentDaemon:
|
|
|
704
710
|
thread_name = msg.get("thread", "")
|
|
705
711
|
limit = int(msg.get("limit", 50))
|
|
706
712
|
if not channel or not thread_name:
|
|
707
|
-
return make_response(req_id, ok=False, error=
|
|
713
|
+
return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD)
|
|
708
714
|
assert self._buffer is not None
|
|
709
715
|
messages = self._buffer.read_thread(channel, thread_name, limit=limit)
|
|
710
716
|
return make_response(
|