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.
Files changed (320) hide show
  1. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/CHANGELOG.md +26 -0
  2. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/PKG-INFO +1 -1
  3. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/bot_manager.py +7 -3
  4. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/config.py +1 -0
  5. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/agent.py +1 -1
  6. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/channel.py +1 -1
  7. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/mesh.py +1 -2
  8. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/server.py +23 -4
  9. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/constants.py +2 -1
  10. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/ipc.py +3 -3
  11. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/agent_runner.py +8 -4
  12. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/daemon.py +20 -14
  13. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/irc_transport.py +19 -9
  14. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/skill/irc_client.py +2 -2
  15. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/socket_server.py +4 -4
  16. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/agent_runner.py +2 -1
  17. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/daemon.py +20 -14
  18. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/irc_transport.py +19 -9
  19. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/skill/irc_client.py +2 -2
  20. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/socket_server.py +4 -4
  21. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/agent_runner.py +8 -4
  22. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/daemon.py +20 -14
  23. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/irc_transport.py +19 -9
  24. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/skill/irc_client.py +2 -2
  25. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/socket_server.py +4 -4
  26. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/daemon.py +20 -14
  27. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/irc_transport.py +19 -9
  28. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/skill/irc_client.py +2 -2
  29. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/socket_server.py +4 -4
  30. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/client.py +1 -1
  31. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/observer.py +3 -3
  32. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/collector.py +18 -18
  33. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/web/style.css +1 -1
  34. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/pidfile.py +1 -1
  35. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/history.md +6 -1
  36. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/client.py +1 -1
  37. agentirc_cli-4.4.0/culture/server/history_store.py +91 -0
  38. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/ircd.py +2 -2
  39. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/server_link.py +6 -6
  40. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skill.py +3 -3
  41. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/history.py +48 -1
  42. agentirc_cli-4.4.0/docs/persistent-history.md +61 -0
  43. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/daemon.py +11 -10
  44. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/irc_transport.py +19 -9
  45. agentirc_cli-4.4.0/packages/agent-harness/message_buffer.py +63 -0
  46. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/skill/irc_client.py +2 -2
  47. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/socket_server.py +4 -4
  48. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/pyproject.toml +1 -1
  49. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/conftest.py +8 -5
  50. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_acp_daemon.py +0 -6
  51. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_agent_runner.py +2 -1
  52. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_commands.py +0 -2
  53. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_connection.py +0 -4
  54. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_icons.py +1 -2
  55. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_federation.py +40 -34
  56. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_history.py +178 -0
  57. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_irc_transport.py +55 -0
  58. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_link_reconnect.py +16 -15
  59. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mentions.py +3 -7
  60. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_rooms.py +5 -5
  61. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_supervisor.py +2 -1
  62. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_webhook.py +1 -1
  63. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/uv.lock +1 -1
  64. agentirc_cli-4.3.6/packages/agent-harness/message_buffer.py +0 -65
  65. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.claude/skills/pr-review/SKILL.md +0 -0
  66. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.claude/skills/run-tests/SKILL.md +0 -0
  67. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  68. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.flake8 +0 -0
  69. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/pages.yml +0 -0
  70. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/publish.yml +0 -0
  71. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/security-checks.yml +0 -0
  72. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.github/workflows/tests.yml +0 -0
  73. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.gitignore +0 -0
  74. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.markdownlint-cli2.yaml +0 -0
  75. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.pr_agent.toml +0 -0
  76. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.pre-commit-config.yaml +0 -0
  77. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/.pylintrc +0 -0
  78. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/CLAUDE.md +0 -0
  79. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/CNAME +0 -0
  80. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/Gemfile +0 -0
  81. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/Gemfile.lock +0 -0
  82. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/LICENSE +0 -0
  83. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/README.md +0 -0
  84. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/SECURITY.md +0 -0
  85. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/_config.yml +0 -0
  86. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/_sass/color_schemes/anthropic.scss +0 -0
  87. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/_sass/custom/custom.scss +0 -0
  88. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/__init__.py +0 -0
  89. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/__main__.py +0 -0
  90. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/aio.py +0 -0
  91. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/__init__.py +0 -0
  92. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/bot.py +0 -0
  93. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/http_listener.py +0 -0
  94. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/template_engine.py +0 -0
  95. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/bots/virtual_client.py +0 -0
  96. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/__init__.py +0 -0
  97. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/bot.py +0 -0
  98. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/__init__.py +0 -0
  99. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/display.py +0 -0
  100. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/formatting.py +0 -0
  101. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/mesh.py +0 -0
  102. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/shared/process.py +0 -0
  103. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/cli/skills.py +0 -0
  104. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/__init__.py +0 -0
  105. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/__init__.py +0 -0
  106. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/config.py +0 -0
  107. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/ipc.py +0 -0
  108. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/message_buffer.py +0 -0
  109. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/skill/SKILL.md +0 -0
  110. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/skill/__init__.py +0 -0
  111. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/supervisor.py +0 -0
  112. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/acp/webhook.py +0 -0
  113. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/__init__.py +0 -0
  114. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/__main__.py +0 -0
  115. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/config.py +0 -0
  116. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/ipc.py +0 -0
  117. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/message_buffer.py +0 -0
  118. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/skill/SKILL.md +0 -0
  119. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/skill/__init__.py +0 -0
  120. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/supervisor.py +0 -0
  121. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/claude/webhook.py +0 -0
  122. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/__init__.py +0 -0
  123. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/config.py +0 -0
  124. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/ipc.py +0 -0
  125. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/message_buffer.py +0 -0
  126. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/skill/SKILL.md +0 -0
  127. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/skill/__init__.py +0 -0
  128. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/supervisor.py +0 -0
  129. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/codex/webhook.py +0 -0
  130. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/__init__.py +0 -0
  131. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/agent_runner.py +0 -0
  132. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/config.py +0 -0
  133. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/ipc.py +0 -0
  134. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/message_buffer.py +0 -0
  135. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/skill/SKILL.md +0 -0
  136. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/skill/__init__.py +0 -0
  137. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/supervisor.py +0 -0
  138. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/clients/copilot/webhook.py +0 -0
  139. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/__init__.py +0 -0
  140. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/app.py +0 -0
  141. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/commands.py +0 -0
  142. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/__init__.py +0 -0
  143. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/chat.py +0 -0
  144. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/info_panel.py +0 -0
  145. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/console/widgets/sidebar.py +0 -0
  146. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/credentials.py +0 -0
  147. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/formatting.py +0 -0
  148. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/learn_prompt.py +0 -0
  149. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/mesh_config.py +0 -0
  150. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/__init__.py +0 -0
  151. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/model.py +0 -0
  152. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/renderer_text.py +0 -0
  153. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/overview/renderer_web.py +0 -0
  154. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/persistence.py +0 -0
  155. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/__init__.py +0 -0
  156. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/commands.py +0 -0
  157. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/federation.md +0 -0
  158. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/icons.md +0 -0
  159. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/rooms.md +0 -0
  160. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/tags.md +0 -0
  161. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/extensions/threads.md +0 -0
  162. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/message.py +0 -0
  163. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/protocol-index.md +0 -0
  164. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/protocol/replies.py +0 -0
  165. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/__init__.py +0 -0
  166. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/__main__.py +0 -0
  167. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/channel.py +0 -0
  168. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/config.py +0 -0
  169. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/remote_client.py +0 -0
  170. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/room_store.py +0 -0
  171. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/rooms_util.py +0 -0
  172. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/__init__.py +0 -0
  173. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/icon.py +0 -0
  174. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/rooms.py +0 -0
  175. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/skills/threads.py +0 -0
  176. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/server/thread_store.py +0 -0
  177. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/culture/skills/culture/SKILL.md +0 -0
  178. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/agent-lifecycle.md +0 -0
  179. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/agentic-self-learn.md +0 -0
  180. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/agent-client.md +0 -0
  181. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/agent-harness-spec.md +0 -0
  182. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/design.md +0 -0
  183. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/harness-conformance.md +0 -0
  184. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/index.md +0 -0
  185. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer1-core-irc.md +0 -0
  186. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer2-attention.md +0 -0
  187. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer3-skills.md +0 -0
  188. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer4-federation.md +0 -0
  189. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/layer5-agent-harness.md +0 -0
  190. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/server-architecture.md +0 -0
  191. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/architecture/threads.md +0 -0
  192. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/channel-polling.md +0 -0
  193. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/acp/overview.md +0 -0
  194. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/configuration.md +0 -0
  195. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/context-management.md +0 -0
  196. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/irc-tools.md +0 -0
  197. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/overview.md +0 -0
  198. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/setup.md +0 -0
  199. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/supervisor.md +0 -0
  200. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/claude/webhooks.md +0 -0
  201. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/configuration.md +0 -0
  202. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/context-management.md +0 -0
  203. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/irc-tools.md +0 -0
  204. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/overview.md +0 -0
  205. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/setup.md +0 -0
  206. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/supervisor.md +0 -0
  207. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/codex/webhooks.md +0 -0
  208. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/configuration.md +0 -0
  209. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/context-management.md +0 -0
  210. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/irc-tools.md +0 -0
  211. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/overview.md +0 -0
  212. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/setup.md +0 -0
  213. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/supervisor.md +0 -0
  214. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/clients/copilot/webhooks.md +0 -0
  215. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/culture-cli.md +0 -0
  216. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/getting-started.md +0 -0
  217. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/index.md +0 -0
  218. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/SECURITY.md +0 -0
  219. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/bots.md +0 -0
  220. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/ci.md +0 -0
  221. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/cli.md +0 -0
  222. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/docs-site.md +0 -0
  223. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/index.md +0 -0
  224. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/ops-tooling.md +0 -0
  225. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/overview.md +0 -0
  226. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/operations/publishing.md +0 -0
  227. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  228. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/rooms.md +0 -0
  229. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/server-rename.md +0 -0
  230. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  231. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  232. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  233. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  234. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  235. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  236. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  237. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  238. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  239. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  240. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  241. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  242. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  243. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  244. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  245. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  246. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  247. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  248. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  249. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  250. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  251. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  252. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/01-pair-programming.md +0 -0
  253. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
  254. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
  255. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
  256. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/05-the-observer.md +0 -0
  257. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/06-cross-server-ops.md +0 -0
  258. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
  259. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/08-apps-as-agents.md +0 -0
  260. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/09-research-swarm.md +0 -0
  261. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases/10-agent-lifecycle.md +0 -0
  262. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/use-cases-index.md +0 -0
  263. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/docs/what-is-culture.md +0 -0
  264. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/README.md +0 -0
  265. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/config.py +0 -0
  266. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/ipc.py +0 -0
  267. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/skill/SKILL.md +0 -0
  268. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/packages/agent-harness/webhook.py +0 -0
  269. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  270. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  271. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  272. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  273. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/sonar-project.properties +0 -0
  274. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/__init__.py +0 -0
  275. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_archive.py +0 -0
  276. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bot.py +0 -0
  277. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bot_config.py +0 -0
  278. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bot_manager.py +0 -0
  279. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_bots_integration.py +0 -0
  280. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_channel.py +0 -0
  281. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_codex_daemon.py +0 -0
  282. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_connection.py +0 -0
  283. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_client.py +0 -0
  284. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_console_integration.py +0 -0
  285. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_copilot_daemon.py +0 -0
  286. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_daemon.py +0 -0
  287. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_daemon_config.py +0 -0
  288. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_daemon_ipc.py +0 -0
  289. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_discovery.py +0 -0
  290. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_http_listener.py +0 -0
  291. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_integration_layer5.py +0 -0
  292. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_ipc.py +0 -0
  293. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mention_alias.py +0 -0
  294. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mention_target_cleanup.py +0 -0
  295. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_mesh_config.py +0 -0
  296. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_message.py +0 -0
  297. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_message_buffer.py +0 -0
  298. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_messaging.py +0 -0
  299. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_modes.py +0 -0
  300. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_cli.py +0 -0
  301. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_collector.py +0 -0
  302. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_model.py +0 -0
  303. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_renderer.py +0 -0
  304. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_overview_web.py +0 -0
  305. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_persistence.py +0 -0
  306. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_pidfile.py +0 -0
  307. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_poll_loop.py +0 -0
  308. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_room_persistence.py +0 -0
  309. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_rooms_federation.py +0 -0
  310. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_rooms_integration.py +0 -0
  311. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_server_icon_skill.py +0 -0
  312. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_setup_update_cli.py +0 -0
  313. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_skill_client.py +0 -0
  314. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_skills.py +0 -0
  315. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_socket_server.py +0 -0
  316. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_template_engine.py +0 -0
  317. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_thread_buffer.py +0 -0
  318. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_threads.py +0 -0
  319. {agentirc_cli-4.3.6 → agentirc_cli-4.4.0}/tests/test_virtual_client.py +0 -0
  320. {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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 4.3.6
3
+ Version: 4.4.0
4
4
  Summary: Legacy alias for culture — install culture instead
5
5
  Project-URL: Homepage, https://github.com/OriNachum/culture
6
6
  Author: Ori Nachum
@@ -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 BOTS_DIR, BotConfig, load_bot_config, save_bot_config
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."""
@@ -10,6 +10,7 @@ from pathlib import Path
10
10
  import yaml
11
11
 
12
12
  BOTS_DIR = Path(os.path.expanduser("~/.culture/bots"))
13
+ BOT_CONFIG_FILE = "bot.yaml"
13
14
 
14
15
 
15
16
  @dataclass
@@ -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 (ConnectionRefusedError, OSError):
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 (ConnectionError, ConnectionRefusedError, TimeoutError, OSError) as exc:
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 (ConnectionRefusedError, OSError):
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(_run_server(args.name, args.host, args.port, links, args.webhook_port))
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(_run_server(args.name, args.host, args.port, links, args.webhook_port))
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, host: str, port: int, links: list | None = None, webhook_port: int = 7680
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, host=host, port=port, webhook_port=webhook_port, links=links or []
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 (ConnectionRefusedError, FileNotFoundError, OSError):
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 (asyncio.TimeoutError, ConnectionError, BrokenPipeError, OSError):
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 (ConnectionError, BrokenPipeError, OSError):
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
- await asyncio.wait_for(self._process.wait(), timeout=5)
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
- return await asyncio.wait_for(future, timeout=timeout)
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
- return await asyncio.wait_for(self._process.wait(), timeout=5)
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
- return await asyncio.wait_for(self._process.wait(), timeout=1)
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
- f"Respond naturally if any messages need your attention."
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
- f"You have IRC tools available via the irc skill. Use them to communicate.\n"
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
- f"Check IRC channels periodically with irc_read() for new messages.\n"
491
- f"When you finish a task, share results in the appropriate channel with irc_send()."
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": "paused" if self._paused else ("working" if running else "idle"),
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
- await asyncio.wait_for(self._status_query_event.wait(), timeout=10.0)
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="Missing 'channel' or 'thread'")
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="Missing 'channel' or 'thread'")
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 (ConnectionRefusedError, OSError) as exc:
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 (ConnectionError, OSError):
80
+ except OSError:
81
81
  pass
82
82
  self._writer.close()
83
83
  try:
84
84
  await self._writer.wait_closed()
85
- except (ConnectionError, BrokenPipeError):
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
- await self._send_raw(f"PRIVMSG {target} :{text}")
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
- await self._send_raw(f"THREAD CREATE {channel} {thread_name} :{text}")
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
- await self._send_raw(f"THREAD REPLY {channel} {thread_name} :{text}")
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
- await self._send_raw(f"THREADCLOSE {channel} {thread_name} :{summary}")
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 (ConnectionError, OSError):
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 (ConnectionError, OSError):
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 (ConnectionError, BrokenPipeError, OSError):
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, ConnectionError, OSError):
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 (ConnectionError, BrokenPipeError, OSError):
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 (ConnectionError, BrokenPipeError, OSError):
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 (ConnectionError, BrokenPipeError, OSError):
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 (ConnectionError, BrokenPipeError, OSError):
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
- await asyncio.wait_for(asyncio.shield(self._task), timeout=5.0)
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
- f"Respond naturally if any messages need your attention."
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
- f"You have IRC tools available via the irc skill. Use them to communicate.\n"
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
- f"Check IRC channels periodically with irc_read() for new messages.\n"
404
- f"When you finish a task, share results in the appropriate channel with irc_send()."
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": "paused" if self._paused else ("working" if running else "idle"),
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
- await asyncio.wait_for(self._status_query_event.wait(), timeout=10.0)
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="Missing 'channel' or 'thread'")
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="Missing 'channel' or 'thread'")
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(