agentirc-cli 4.1.0__tar.gz → 4.1.2__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 (307) hide show
  1. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/CHANGELOG.md +16 -0
  2. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/PKG-INFO +1 -1
  3. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/agent_runner.py +26 -8
  4. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/config.py +2 -2
  5. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/daemon.py +9 -1
  6. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/config.py +2 -2
  7. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/agent_runner.py +4 -0
  8. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/config.py +2 -2
  9. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/daemon.py +9 -1
  10. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/agent_runner.py +4 -0
  11. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/config.py +2 -2
  12. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/daemon.py +9 -1
  13. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/channel-polling.md +1 -1
  14. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/config.py +2 -2
  15. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/daemon.py +16 -0
  16. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/pyproject.toml +1 -1
  17. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_acp_daemon.py +46 -0
  18. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_mention_alias.py +0 -1
  19. agentirc_cli-4.1.2/tests/test_mention_target_cleanup.py +135 -0
  20. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/uv.lock +1 -1
  21. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.claude/skills/pr-review/SKILL.md +0 -0
  22. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.claude/skills/run-tests/SKILL.md +0 -0
  23. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.claude/skills/run-tests/scripts/test.sh +0 -0
  24. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.flake8 +0 -0
  25. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.github/workflows/pages.yml +0 -0
  26. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.github/workflows/publish.yml +0 -0
  27. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.github/workflows/security-checks.yml +0 -0
  28. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.github/workflows/tests.yml +0 -0
  29. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.gitignore +0 -0
  30. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.markdownlint-cli2.yaml +0 -0
  31. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.pr_agent.toml +0 -0
  32. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.pre-commit-config.yaml +0 -0
  33. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/.pylintrc +0 -0
  34. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/CLAUDE.md +0 -0
  35. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/CNAME +0 -0
  36. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/Gemfile +0 -0
  37. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/Gemfile.lock +0 -0
  38. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/LICENSE +0 -0
  39. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/README.md +0 -0
  40. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/SECURITY.md +0 -0
  41. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/_config.yml +0 -0
  42. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/_sass/color_schemes/anthropic.scss +0 -0
  43. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/_sass/custom/custom.scss +0 -0
  44. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/__init__.py +0 -0
  45. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/__main__.py +0 -0
  46. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/bots/__init__.py +0 -0
  47. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/bots/bot.py +0 -0
  48. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/bots/bot_manager.py +0 -0
  49. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/bots/config.py +0 -0
  50. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/bots/http_listener.py +0 -0
  51. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/bots/template_engine.py +0 -0
  52. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/bots/virtual_client.py +0 -0
  53. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/__init__.py +0 -0
  54. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/_helpers.py +0 -0
  55. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/agent.py +0 -0
  56. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/bot.py +0 -0
  57. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/channel.py +0 -0
  58. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/mesh.py +0 -0
  59. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/server.py +0 -0
  60. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/cli/skills.py +0 -0
  61. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/__init__.py +0 -0
  62. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/__init__.py +0 -0
  63. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/ipc.py +0 -0
  64. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/irc_transport.py +0 -0
  65. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/message_buffer.py +0 -0
  66. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/skill/SKILL.md +0 -0
  67. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/skill/__init__.py +0 -0
  68. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/skill/irc_client.py +0 -0
  69. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/socket_server.py +0 -0
  70. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/supervisor.py +0 -0
  71. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/acp/webhook.py +0 -0
  72. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/__init__.py +0 -0
  73. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/__main__.py +0 -0
  74. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/agent_runner.py +0 -0
  75. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/daemon.py +0 -0
  76. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/ipc.py +0 -0
  77. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/irc_transport.py +0 -0
  78. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/message_buffer.py +0 -0
  79. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/skill/SKILL.md +0 -0
  80. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/skill/__init__.py +0 -0
  81. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/skill/irc_client.py +0 -0
  82. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/socket_server.py +0 -0
  83. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/supervisor.py +0 -0
  84. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/claude/webhook.py +0 -0
  85. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/__init__.py +0 -0
  86. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/ipc.py +0 -0
  87. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/irc_transport.py +0 -0
  88. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/message_buffer.py +0 -0
  89. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/skill/SKILL.md +0 -0
  90. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/skill/__init__.py +0 -0
  91. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/skill/irc_client.py +0 -0
  92. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/socket_server.py +0 -0
  93. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/supervisor.py +0 -0
  94. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/codex/webhook.py +0 -0
  95. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/__init__.py +0 -0
  96. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/ipc.py +0 -0
  97. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/irc_transport.py +0 -0
  98. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/message_buffer.py +0 -0
  99. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/skill/SKILL.md +0 -0
  100. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/skill/__init__.py +0 -0
  101. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/skill/irc_client.py +0 -0
  102. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/socket_server.py +0 -0
  103. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/supervisor.py +0 -0
  104. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/clients/copilot/webhook.py +0 -0
  105. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/__init__.py +0 -0
  106. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/app.py +0 -0
  107. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/client.py +0 -0
  108. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/commands.py +0 -0
  109. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/widgets/__init__.py +0 -0
  110. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/widgets/chat.py +0 -0
  111. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/widgets/info_panel.py +0 -0
  112. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/console/widgets/sidebar.py +0 -0
  113. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/credentials.py +0 -0
  114. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/learn_prompt.py +0 -0
  115. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/mesh_config.py +0 -0
  116. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/observer.py +0 -0
  117. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/overview/__init__.py +0 -0
  118. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/overview/collector.py +0 -0
  119. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/overview/model.py +0 -0
  120. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/overview/renderer_text.py +0 -0
  121. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/overview/renderer_web.py +0 -0
  122. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/overview/web/style.css +0 -0
  123. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/persistence.py +0 -0
  124. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/pidfile.py +0 -0
  125. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/__init__.py +0 -0
  126. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/commands.py +0 -0
  127. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/extensions/federation.md +0 -0
  128. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/extensions/history.md +0 -0
  129. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/extensions/icons.md +0 -0
  130. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/extensions/rooms.md +0 -0
  131. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/extensions/tags.md +0 -0
  132. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/extensions/threads.md +0 -0
  133. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/message.py +0 -0
  134. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/protocol-index.md +0 -0
  135. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/protocol/replies.py +0 -0
  136. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/__init__.py +0 -0
  137. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/__main__.py +0 -0
  138. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/channel.py +0 -0
  139. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/client.py +0 -0
  140. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/config.py +0 -0
  141. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/ircd.py +0 -0
  142. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/remote_client.py +0 -0
  143. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/room_store.py +0 -0
  144. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/rooms_util.py +0 -0
  145. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/server_link.py +0 -0
  146. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/skill.py +0 -0
  147. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/skills/__init__.py +0 -0
  148. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/skills/history.py +0 -0
  149. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/skills/icon.py +0 -0
  150. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/skills/rooms.py +0 -0
  151. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/skills/threads.py +0 -0
  152. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/server/thread_store.py +0 -0
  153. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/culture/skills/culture/SKILL.md +0 -0
  154. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/agent-lifecycle.md +0 -0
  155. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/agentic-self-learn.md +0 -0
  156. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/agent-client.md +0 -0
  157. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/agent-harness-spec.md +0 -0
  158. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/design.md +0 -0
  159. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/harness-conformance.md +0 -0
  160. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/index.md +0 -0
  161. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/layer1-core-irc.md +0 -0
  162. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/layer2-attention.md +0 -0
  163. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/layer3-skills.md +0 -0
  164. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/layer4-federation.md +0 -0
  165. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/layer5-agent-harness.md +0 -0
  166. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/server-architecture.md +0 -0
  167. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/architecture/threads.md +0 -0
  168. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/acp/overview.md +0 -0
  169. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/claude/configuration.md +0 -0
  170. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/claude/context-management.md +0 -0
  171. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/claude/irc-tools.md +0 -0
  172. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/claude/overview.md +0 -0
  173. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/claude/setup.md +0 -0
  174. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/claude/supervisor.md +0 -0
  175. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/claude/webhooks.md +0 -0
  176. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/codex/configuration.md +0 -0
  177. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/codex/context-management.md +0 -0
  178. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/codex/irc-tools.md +0 -0
  179. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/codex/overview.md +0 -0
  180. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/codex/setup.md +0 -0
  181. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/codex/supervisor.md +0 -0
  182. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/codex/webhooks.md +0 -0
  183. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/copilot/configuration.md +0 -0
  184. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/copilot/context-management.md +0 -0
  185. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/copilot/irc-tools.md +0 -0
  186. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/copilot/overview.md +0 -0
  187. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/copilot/setup.md +0 -0
  188. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/copilot/supervisor.md +0 -0
  189. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/clients/copilot/webhooks.md +0 -0
  190. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/culture-cli.md +0 -0
  191. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/getting-started.md +0 -0
  192. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/index.md +0 -0
  193. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/SECURITY.md +0 -0
  194. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/bots.md +0 -0
  195. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/ci.md +0 -0
  196. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/cli.md +0 -0
  197. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/docs-site.md +0 -0
  198. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/index.md +0 -0
  199. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/ops-tooling.md +0 -0
  200. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/overview.md +0 -0
  201. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/operations/publishing.md +0 -0
  202. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  203. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/rooms.md +0 -0
  204. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/server-rename.md +0 -0
  205. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  206. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  207. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  208. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  209. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  210. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  211. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  212. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  213. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  214. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  215. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  216. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  217. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  218. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  219. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  220. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  221. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  222. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  223. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  224. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  225. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  226. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/01-pair-programming.md +0 -0
  227. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/02-code-review-ensemble.md +0 -0
  228. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/03-cross-server-delegation.md +0 -0
  229. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/04-knowledge-propagation.md +0 -0
  230. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/05-the-observer.md +0 -0
  231. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/06-cross-server-ops.md +0 -0
  232. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/07-supervisor-intervention.md +0 -0
  233. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/08-apps-as-agents.md +0 -0
  234. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/09-research-swarm.md +0 -0
  235. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases/10-agent-lifecycle.md +0 -0
  236. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/use-cases-index.md +0 -0
  237. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/docs/what-is-culture.md +0 -0
  238. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/README.md +0 -0
  239. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/ipc.py +0 -0
  240. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/irc_transport.py +0 -0
  241. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/message_buffer.py +0 -0
  242. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/skill/SKILL.md +0 -0
  243. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/skill/irc_client.py +0 -0
  244. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/socket_server.py +0 -0
  245. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/packages/agent-harness/webhook.py +0 -0
  246. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  247. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  248. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  249. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  250. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/sonar-project.properties +0 -0
  251. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/__init__.py +0 -0
  252. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/conftest.py +0 -0
  253. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_agent_runner.py +0 -0
  254. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_bot.py +0 -0
  255. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_bot_config.py +0 -0
  256. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_bot_manager.py +0 -0
  257. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_bots_integration.py +0 -0
  258. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_channel.py +0 -0
  259. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_codex_daemon.py +0 -0
  260. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_connection.py +0 -0
  261. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_console_client.py +0 -0
  262. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_console_commands.py +0 -0
  263. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_console_connection.py +0 -0
  264. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_console_icons.py +0 -0
  265. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_console_integration.py +0 -0
  266. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_copilot_daemon.py +0 -0
  267. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_daemon.py +0 -0
  268. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_daemon_config.py +0 -0
  269. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_daemon_ipc.py +0 -0
  270. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_discovery.py +0 -0
  271. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_federation.py +0 -0
  272. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_history.py +0 -0
  273. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_http_listener.py +0 -0
  274. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_integration_layer5.py +0 -0
  275. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_ipc.py +0 -0
  276. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_irc_transport.py +0 -0
  277. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_link_reconnect.py +0 -0
  278. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_mentions.py +0 -0
  279. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_mesh_config.py +0 -0
  280. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_message.py +0 -0
  281. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_message_buffer.py +0 -0
  282. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_messaging.py +0 -0
  283. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_modes.py +0 -0
  284. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_overview_cli.py +0 -0
  285. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_overview_collector.py +0 -0
  286. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_overview_model.py +0 -0
  287. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_overview_renderer.py +0 -0
  288. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_overview_web.py +0 -0
  289. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_persistence.py +0 -0
  290. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_pidfile.py +0 -0
  291. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_poll_loop.py +0 -0
  292. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_room_persistence.py +0 -0
  293. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_rooms.py +0 -0
  294. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_rooms_federation.py +0 -0
  295. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_rooms_integration.py +0 -0
  296. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_server_icon_skill.py +0 -0
  297. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_setup_update_cli.py +0 -0
  298. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_skill_client.py +0 -0
  299. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_skills.py +0 -0
  300. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_socket_server.py +0 -0
  301. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_supervisor.py +0 -0
  302. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_template_engine.py +0 -0
  303. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_thread_buffer.py +0 -0
  304. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_threads.py +0 -0
  305. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_virtual_client.py +0 -0
  306. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_wait_for_port.py +0 -0
  307. {agentirc_cli-4.1.0 → agentirc_cli-4.1.2}/tests/test_webhook.py +0 -0
@@ -4,6 +4,22 @@ 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.1.2] - 2026-04-06
8
+
9
+
10
+ ### Fixed
11
+
12
+ - Clean up _mention_targets deque on prompt failure to prevent misrouted responses
13
+
14
+ ## [4.1.1] - 2026-04-06
15
+
16
+
17
+ ### Fixed
18
+
19
+ - Fix ACP/Codex/Copilot poll loop to use fire-and-forget (race condition fix)
20
+ - Increase ACP prompt timeout from 120s to 300s with retry on timeout (issue #115)
21
+ - Lower default poll_interval from 300s to 60s across all backends
22
+
7
23
  ## [4.1.0] - 2026-04-06
8
24
 
9
25
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 4.1.0
3
+ Version: 4.1.2
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
@@ -33,6 +33,7 @@ class ACPAgentRunner:
33
33
  system_prompt: str = "",
34
34
  on_exit: Callable[[int], Awaitable[None]] | None = None,
35
35
  on_message: Callable[[dict[str, Any]], Awaitable[None]] | None = None,
36
+ on_turn_error: Callable[[], Awaitable[None]] | None = None,
36
37
  ) -> None:
37
38
  self.model = model
38
39
  self.directory = directory
@@ -40,6 +41,7 @@ class ACPAgentRunner:
40
41
  self.system_prompt = system_prompt
41
42
  self.on_exit = on_exit
42
43
  self.on_message = on_message
44
+ self.on_turn_error = on_turn_error
43
45
 
44
46
  self._isolated_home: str | None = None
45
47
  self._process: asyncio.subprocess.Process | None = None
@@ -353,14 +355,26 @@ class ACPAgentRunner:
353
355
 
354
356
  try:
355
357
  self._busy = True
356
- resp = await self._send_request(
357
- "session/prompt",
358
- {
359
- "sessionId": self._session_id,
360
- "prompt": [{"type": "text", "text": text}],
361
- },
362
- timeout=120,
363
- )
358
+ prompt_params = {
359
+ "sessionId": self._session_id,
360
+ "prompt": [{"type": "text", "text": text}],
361
+ }
362
+ try:
363
+ resp = await self._send_request(
364
+ "session/prompt",
365
+ prompt_params,
366
+ timeout=300,
367
+ )
368
+ except TimeoutError:
369
+ logger.warning(
370
+ "ACP prompt timed out, retrying once: %s",
371
+ text[:80],
372
+ )
373
+ resp = await self._send_request(
374
+ "session/prompt",
375
+ prompt_params,
376
+ timeout=300,
377
+ )
364
378
 
365
379
  result = resp.get("result", {})
366
380
  if "stopReason" in result:
@@ -372,6 +386,10 @@ class ACPAgentRunner:
372
386
 
373
387
  except Exception:
374
388
  logger.exception("ACP turn error")
389
+ if self.on_turn_error:
390
+ await self.on_turn_error()
391
+ finally:
392
+ self._busy = False
375
393
 
376
394
  except asyncio.CancelledError:
377
395
  raise
@@ -70,7 +70,7 @@ class DaemonConfig:
70
70
  supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
71
71
  webhooks: WebhookConfig = field(default_factory=WebhookConfig)
72
72
  buffer_size: int = 500
73
- poll_interval: int = 300
73
+ poll_interval: int = 60
74
74
  sleep_start: str = "23:00"
75
75
  sleep_end: str = "08:00"
76
76
  agents: list[AgentConfig] = field(default_factory=list)
@@ -101,7 +101,7 @@ def load_config(path: str | Path) -> DaemonConfig:
101
101
  supervisor=supervisor,
102
102
  webhooks=webhooks,
103
103
  buffer_size=raw.get("buffer_size", 500),
104
- poll_interval=raw.get("poll_interval", 300),
104
+ poll_interval=raw.get("poll_interval", 60),
105
105
  sleep_start=raw.get("sleep_start", "23:00"),
106
106
  sleep_end=raw.get("sleep_end", "08:00"),
107
107
  agents=agents,
@@ -293,7 +293,9 @@ class ACPDaemon:
293
293
  f"Respond naturally if any messages need your attention."
294
294
  )
295
295
  self._mention_targets.append(channel)
296
- await self._agent_runner.send_prompt(prompt)
296
+ task = asyncio.create_task(self._agent_runner.send_prompt(prompt))
297
+ self._background_tasks.add(task)
298
+ task.add_done_callback(self._background_tasks.discard)
297
299
  except asyncio.CancelledError:
298
300
  raise
299
301
  except Exception:
@@ -316,6 +318,11 @@ class ACPDaemon:
316
318
  # Agent runner helpers
317
319
  # ------------------------------------------------------------------
318
320
 
321
+ async def _on_turn_error(self) -> None:
322
+ """Clean up stale relay target when a prompt fails."""
323
+ if self._mention_targets:
324
+ self._mention_targets.popleft()
325
+
319
326
  async def _start_agent_runner(self) -> None:
320
327
  self._agent_runner = ACPAgentRunner(
321
328
  model=self.agent.model,
@@ -324,6 +331,7 @@ class ACPDaemon:
324
331
  system_prompt=self._build_system_prompt(),
325
332
  on_exit=self._on_agent_exit,
326
333
  on_message=self._on_agent_message,
334
+ on_turn_error=self._on_turn_error,
327
335
  )
328
336
  # Absorb the system prompt response without relaying to IRC
329
337
  self._mention_targets.append(None)
@@ -70,7 +70,7 @@ class DaemonConfig:
70
70
  supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
71
71
  webhooks: WebhookConfig = field(default_factory=WebhookConfig)
72
72
  buffer_size: int = 500
73
- poll_interval: int = 300
73
+ poll_interval: int = 60
74
74
  sleep_start: str = "23:00"
75
75
  sleep_end: str = "08:00"
76
76
  agents: list[AgentConfig] = field(default_factory=list)
@@ -105,7 +105,7 @@ def load_config(path: str | Path) -> DaemonConfig:
105
105
  supervisor=supervisor,
106
106
  webhooks=webhooks,
107
107
  buffer_size=raw.get("buffer_size", 500),
108
- poll_interval=raw.get("poll_interval", 300),
108
+ poll_interval=raw.get("poll_interval", 60),
109
109
  sleep_start=raw.get("sleep_start", "23:00"),
110
110
  sleep_end=raw.get("sleep_end", "08:00"),
111
111
  agents=agents,
@@ -23,12 +23,14 @@ class CodexAgentRunner:
23
23
  system_prompt: str = "",
24
24
  on_exit: Callable[[int], Awaitable[None]] | None = None,
25
25
  on_message: Callable[[dict[str, Any]], Awaitable[None]] | None = None,
26
+ on_turn_error: Callable[[], Awaitable[None]] | None = None,
26
27
  ) -> None:
27
28
  self.model = model
28
29
  self.directory = directory
29
30
  self.system_prompt = system_prompt
30
31
  self.on_exit = on_exit
31
32
  self.on_message = on_message
33
+ self.on_turn_error = on_turn_error
32
34
 
33
35
  self._isolated_home: str | None = None
34
36
  self._process: asyncio.subprocess.Process | None = None
@@ -318,6 +320,8 @@ class CodexAgentRunner:
318
320
 
319
321
  except Exception:
320
322
  logger.exception("Codex turn error")
323
+ if self.on_turn_error:
324
+ await self.on_turn_error()
321
325
 
322
326
  except asyncio.CancelledError:
323
327
  raise
@@ -68,7 +68,7 @@ class DaemonConfig:
68
68
  supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
69
69
  webhooks: WebhookConfig = field(default_factory=WebhookConfig)
70
70
  buffer_size: int = 500
71
- poll_interval: int = 300
71
+ poll_interval: int = 60
72
72
  sleep_start: str = "23:00"
73
73
  sleep_end: str = "08:00"
74
74
  agents: list[AgentConfig] = field(default_factory=list)
@@ -101,7 +101,7 @@ def load_config(path: str | Path) -> DaemonConfig:
101
101
  supervisor=supervisor,
102
102
  webhooks=webhooks,
103
103
  buffer_size=raw.get("buffer_size", 500),
104
- poll_interval=raw.get("poll_interval", 300),
104
+ poll_interval=raw.get("poll_interval", 60),
105
105
  sleep_start=raw.get("sleep_start", "23:00"),
106
106
  sleep_end=raw.get("sleep_end", "08:00"),
107
107
  agents=agents,
@@ -268,7 +268,9 @@ class CodexDaemon:
268
268
  f"Respond naturally if any messages need your attention."
269
269
  )
270
270
  self._mention_targets.append(channel)
271
- await self._agent_runner.send_prompt(prompt)
271
+ task = asyncio.create_task(self._agent_runner.send_prompt(prompt))
272
+ self._background_tasks.add(task)
273
+ task.add_done_callback(self._background_tasks.discard)
272
274
  except asyncio.CancelledError:
273
275
  raise
274
276
  except Exception:
@@ -291,6 +293,11 @@ class CodexDaemon:
291
293
  # Agent runner helpers
292
294
  # ------------------------------------------------------------------
293
295
 
296
+ async def _on_turn_error(self) -> None:
297
+ """Clean up stale relay target when a prompt fails."""
298
+ if self._mention_targets:
299
+ self._mention_targets.popleft()
300
+
294
301
  async def _start_agent_runner(self) -> None:
295
302
  self._agent_runner = CodexAgentRunner(
296
303
  model=self.agent.model,
@@ -298,6 +305,7 @@ class CodexDaemon:
298
305
  system_prompt=self._build_system_prompt(),
299
306
  on_exit=self._on_agent_exit,
300
307
  on_message=self._on_agent_message,
308
+ on_turn_error=self._on_turn_error,
301
309
  )
302
310
  await self._agent_runner.start()
303
311
  logger.info("CodexAgentRunner started for %s", self.agent.nick)
@@ -23,6 +23,7 @@ class CopilotAgentRunner:
23
23
  skill_directories: list[str] | None = None,
24
24
  on_exit: Callable[[int], Awaitable[None]] | None = None,
25
25
  on_message: Callable[[dict[str, Any]], Awaitable[None]] | None = None,
26
+ on_turn_error: Callable[[], Awaitable[None]] | None = None,
26
27
  ) -> None:
27
28
  self.model = model
28
29
  self.directory = directory
@@ -30,6 +31,7 @@ class CopilotAgentRunner:
30
31
  self.skill_directories = skill_directories or []
31
32
  self.on_exit = on_exit
32
33
  self.on_message = on_message
34
+ self.on_turn_error = on_turn_error
33
35
 
34
36
  self._isolated_home: str | None = None
35
37
  self._client: Any = None
@@ -158,6 +160,8 @@ class CopilotAgentRunner:
158
160
 
159
161
  except Exception:
160
162
  logger.exception("Copilot session turn error")
163
+ if self.on_turn_error:
164
+ await self.on_turn_error()
161
165
  if not self._stopping:
162
166
  self._running = False
163
167
  if self.on_exit:
@@ -68,7 +68,7 @@ class DaemonConfig:
68
68
  supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
69
69
  webhooks: WebhookConfig = field(default_factory=WebhookConfig)
70
70
  buffer_size: int = 500
71
- poll_interval: int = 300
71
+ poll_interval: int = 60
72
72
  sleep_start: str = "23:00"
73
73
  sleep_end: str = "08:00"
74
74
  agents: list[AgentConfig] = field(default_factory=list)
@@ -101,7 +101,7 @@ def load_config(path: str | Path) -> DaemonConfig:
101
101
  supervisor=supervisor,
102
102
  webhooks=webhooks,
103
103
  buffer_size=raw.get("buffer_size", 500),
104
- poll_interval=raw.get("poll_interval", 300),
104
+ poll_interval=raw.get("poll_interval", 60),
105
105
  sleep_start=raw.get("sleep_start", "23:00"),
106
106
  sleep_end=raw.get("sleep_end", "08:00"),
107
107
  agents=agents,
@@ -268,7 +268,9 @@ class CopilotDaemon:
268
268
  f"Respond naturally if any messages need your attention."
269
269
  )
270
270
  self._mention_targets.append(channel)
271
- await self._agent_runner.send_prompt(prompt)
271
+ task = asyncio.create_task(self._agent_runner.send_prompt(prompt))
272
+ self._background_tasks.add(task)
273
+ task.add_done_callback(self._background_tasks.discard)
272
274
  except asyncio.CancelledError:
273
275
  raise
274
276
  except Exception:
@@ -291,6 +293,11 @@ class CopilotDaemon:
291
293
  # Agent runner helpers
292
294
  # ------------------------------------------------------------------
293
295
 
296
+ async def _on_turn_error(self) -> None:
297
+ """Clean up stale relay target when a prompt fails."""
298
+ if self._mention_targets:
299
+ self._mention_targets.popleft()
300
+
294
301
  async def _start_agent_runner(self) -> None:
295
302
  # Resolve installed skill path for the Copilot session
296
303
  skill_dirs: list[str] = []
@@ -305,6 +312,7 @@ class CopilotDaemon:
305
312
  skill_directories=skill_dirs,
306
313
  on_exit=self._on_agent_exit,
307
314
  on_message=self._on_agent_message,
315
+ on_turn_error=self._on_turn_error,
308
316
  )
309
317
  await self._agent_runner.start()
310
318
  logger.info("CopilotAgentRunner started for %s", self.agent.nick)
@@ -22,7 +22,7 @@ Respond naturally if any messages need your attention.
22
22
  Add `poll_interval` to `agents.yaml`:
23
23
 
24
24
  ```yaml
25
- poll_interval: 300 # seconds (default: 5 minutes)
25
+ poll_interval: 60 # seconds (default: 1 minute)
26
26
  buffer_size: 500
27
27
  sleep_start: '23:00'
28
28
  sleep_end: '08:00'
@@ -68,7 +68,7 @@ class DaemonConfig:
68
68
  supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
69
69
  webhooks: WebhookConfig = field(default_factory=WebhookConfig)
70
70
  buffer_size: int = 500
71
- poll_interval: int = 300
71
+ poll_interval: int = 60
72
72
  sleep_start: str = "23:00"
73
73
  sleep_end: str = "08:00"
74
74
  agents: list[AgentConfig] = field(default_factory=list)
@@ -99,7 +99,7 @@ def load_config(path: str | Path) -> DaemonConfig:
99
99
  supervisor=supervisor,
100
100
  webhooks=webhooks,
101
101
  buffer_size=raw.get("buffer_size", 500),
102
- poll_interval=raw.get("poll_interval", 300),
102
+ poll_interval=raw.get("poll_interval", 60),
103
103
  sleep_start=raw.get("sleep_start", "23:00"),
104
104
  sleep_end=raw.get("sleep_end", "08:00"),
105
105
  agents=agents,
@@ -60,8 +60,15 @@ class AgentDaemon:
60
60
  self._buffer: MessageBuffer | None = None
61
61
  self._socket_server: SocketServer | None = None
62
62
  self._webhook: WebhookClient | None = None
63
+ self._agent_runner: Any = None
63
64
  self._stop_event: asyncio.Event | None = None
64
65
 
66
+ # FIFO queue of relay targets — each @mention or poll enqueues a
67
+ # target, each agent response dequeues one for correct routing.
68
+ from collections import deque
69
+
70
+ self._mention_targets: deque[str] = deque()
71
+
65
72
  # Pause/sleep state
66
73
  self._paused: bool = False
67
74
  self._last_activation: float | None = None
@@ -251,6 +258,15 @@ class AgentDaemon:
251
258
  except Exception:
252
259
  logger.exception("Poll loop error")
253
260
 
261
+ async def _on_turn_error(self) -> None:
262
+ """Clean up stale relay target when a prompt fails.
263
+
264
+ Wire this as the ``on_turn_error`` callback on your agent runner so
265
+ the ``_mention_targets`` deque stays in sync with the prompt queue.
266
+ """
267
+ if self._mention_targets:
268
+ self._mention_targets.popleft()
269
+
254
270
  def _on_mention(self, target: str, sender: str, text: str) -> None:
255
271
  """Called when the agent is @mentioned. Sends prompt to runner.
256
272
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentirc-cli"
3
- version = "4.1.0"
3
+ version = "4.1.2"
4
4
  description = "Legacy alias for culture — install culture instead"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import os
3
3
  import tempfile
4
+ from unittest.mock import AsyncMock, MagicMock
4
5
 
5
6
  import pytest
6
7
 
@@ -153,3 +154,48 @@ async def test_acp_relay_target_fifo(server, make_client):
153
154
  assert any("dm response" in l for l in lines), f"Expected 'dm response', got: {lines}"
154
155
 
155
156
  await daemon.stop()
157
+
158
+
159
+ def _inject_fake_acp_runner(daemon):
160
+ """Inject a fake agent runner that records prompts."""
161
+ runner = MagicMock()
162
+ runner.is_running.return_value = True
163
+ runner.send_prompt = AsyncMock()
164
+ runner.stop = AsyncMock()
165
+ daemon._agent_runner = runner
166
+ return runner
167
+
168
+
169
+ @pytest.mark.asyncio
170
+ async def test_acp_poll_loop_uses_fire_and_forget(server, make_client):
171
+ """ACP poll loop should use create_task (fire-and-forget), not blocking await."""
172
+ config = DaemonConfig(
173
+ server=ServerConnConfig(host="127.0.0.1", port=server.config.port),
174
+ poll_interval=1,
175
+ )
176
+ agent = AgentConfig(nick="testserv-opencode", directory="/tmp", channels=["#general"])
177
+ sock_dir = tempfile.mkdtemp()
178
+ daemon = ACPDaemon(config, agent, socket_dir=sock_dir, skip_agent=True)
179
+ await daemon.start()
180
+ runner = _inject_fake_acp_runner(daemon)
181
+ await asyncio.sleep(0.5)
182
+
183
+ # Human sends a message (no @mention)
184
+ human = await make_client(nick="testserv-ori", user="ori")
185
+ await human.send("JOIN #general")
186
+ await human.recv_all(timeout=0.3)
187
+ await human.send("PRIVMSG #general :hello from poll test")
188
+
189
+ # Wait for poll to fire
190
+ await asyncio.sleep(1.5)
191
+
192
+ # Poll loop should have sent the prompt
193
+ assert runner.send_prompt.call_count >= 1
194
+ prompt = runner.send_prompt.call_args[0][0]
195
+ assert "hello from poll test" in prompt
196
+ assert "[IRC Channel Poll: #general]" in prompt
197
+
198
+ # The poll should also have enqueued a relay target
199
+ assert len(daemon._mention_targets) >= 1
200
+
201
+ await daemon.stop()
@@ -22,7 +22,6 @@ async def test_mention_full_nick(server, make_client):
22
22
  daemon = AgentDaemon(config, agent, socket_dir=sock_dir, skip_claude=True)
23
23
 
24
24
  mentions = []
25
- original_on_mention = daemon._on_mention
26
25
  daemon._on_mention = lambda t, s, txt: mentions.append((t, s, txt))
27
26
  daemon._transport_on_mention = daemon._on_mention
28
27
 
@@ -0,0 +1,135 @@
1
+ """Tests for _mention_targets deque cleanup on prompt failure.
2
+
3
+ When a prompt fails (timeout, error), the corresponding _mention_targets
4
+ entry must be cleaned up so that future responses route correctly.
5
+ """
6
+
7
+ import asyncio
8
+ import tempfile
9
+ from unittest.mock import AsyncMock, MagicMock
10
+
11
+ import pytest
12
+
13
+ from culture.clients.acp.config import (
14
+ AgentConfig,
15
+ DaemonConfig,
16
+ ServerConnConfig,
17
+ )
18
+ from culture.clients.acp.daemon import ACPDaemon
19
+
20
+
21
+ def _make_daemon(server_port: int) -> ACPDaemon:
22
+ config = DaemonConfig(
23
+ server=ServerConnConfig(host="127.0.0.1", port=server_port),
24
+ poll_interval=0,
25
+ )
26
+ agent = AgentConfig(
27
+ nick="testserv-bot",
28
+ directory="/tmp",
29
+ channels=["#general"],
30
+ acp_command=["echo"],
31
+ )
32
+ sock_dir = tempfile.mkdtemp()
33
+ return ACPDaemon(config, agent, socket_dir=sock_dir, skip_agent=True)
34
+
35
+
36
+ def _inject_fake_runner(daemon):
37
+ """Inject a fake agent runner that records prompts."""
38
+ runner = MagicMock()
39
+ runner.is_running.return_value = True
40
+ runner.send_prompt = AsyncMock()
41
+ runner.stop = AsyncMock()
42
+ daemon._agent_runner = runner
43
+ return runner
44
+
45
+
46
+ @pytest.mark.asyncio
47
+ async def test_on_turn_error_pops_stale_target(server):
48
+ """_on_turn_error should pop the front entry from _mention_targets."""
49
+ daemon = _make_daemon(server.config.port)
50
+ await daemon.start()
51
+
52
+ # Simulate: poll enqueued a target, then the prompt failed
53
+ daemon._mention_targets.append("#general")
54
+ daemon._mention_targets.append("#code-review")
55
+ assert len(daemon._mention_targets) == 2
56
+
57
+ await daemon._on_turn_error()
58
+ assert len(daemon._mention_targets) == 1
59
+ assert daemon._mention_targets[0] == "#code-review"
60
+
61
+ await daemon.stop()
62
+
63
+
64
+ @pytest.mark.asyncio
65
+ async def test_on_turn_error_empty_deque_is_safe(server):
66
+ """_on_turn_error should be a no-op on an empty deque."""
67
+ daemon = _make_daemon(server.config.port)
68
+ await daemon.start()
69
+
70
+ assert len(daemon._mention_targets) == 0
71
+ await daemon._on_turn_error() # should not raise
72
+ assert len(daemon._mention_targets) == 0
73
+
74
+ await daemon.stop()
75
+
76
+
77
+ @pytest.mark.asyncio
78
+ async def test_relay_routes_correctly_after_error_cleanup(server, make_client):
79
+ """After a failed prompt is cleaned up, the next response should route correctly."""
80
+ daemon = _make_daemon(server.config.port)
81
+ await daemon.start()
82
+ _inject_fake_runner(daemon)
83
+ await asyncio.sleep(0.5)
84
+
85
+ # Simulate: system prompt enqueued None, then timed out and was cleaned up
86
+ daemon._mention_targets.append(None)
87
+ await daemon._on_turn_error() # cleans up None
88
+
89
+ # Now simulate a real mention that succeeds
90
+ daemon._mention_targets.append("#general")
91
+
92
+ # Simulate agent response
93
+ sent_messages = []
94
+ original_send = daemon._transport.send_privmsg
95
+
96
+ async def capture_send(target, text):
97
+ sent_messages.append((target, text))
98
+ await original_send(target, text)
99
+
100
+ daemon._transport.send_privmsg = capture_send
101
+
102
+ msg = {
103
+ "type": "assistant",
104
+ "content": [{"type": "text", "text": "Hello from bot!"}],
105
+ }
106
+ await daemon._relay_response_to_irc(msg)
107
+
108
+ assert len(sent_messages) == 1
109
+ assert sent_messages[0][0] == "#general"
110
+ assert "Hello from bot!" in sent_messages[0][1]
111
+ assert len(daemon._mention_targets) == 0
112
+
113
+ await daemon.stop()
114
+
115
+
116
+ @pytest.mark.asyncio
117
+ async def test_multiple_errors_drain_deque_correctly(server):
118
+ """Multiple consecutive errors should each pop one entry."""
119
+ daemon = _make_daemon(server.config.port)
120
+ await daemon.start()
121
+
122
+ # Simulate 3 failed prompts
123
+ daemon._mention_targets.append(None) # system prompt
124
+ daemon._mention_targets.append("#general") # poll 1
125
+ daemon._mention_targets.append("#general") # poll 2
126
+
127
+ await daemon._on_turn_error() # pops None
128
+ await daemon._on_turn_error() # pops #general
129
+ assert len(daemon._mention_targets) == 1
130
+ assert daemon._mention_targets[0] == "#general"
131
+
132
+ await daemon._on_turn_error() # pops last #general
133
+ assert len(daemon._mention_targets) == 0
134
+
135
+ await daemon.stop()
@@ -561,7 +561,7 @@ wheels = [
561
561
 
562
562
  [[package]]
563
563
  name = "culture"
564
- version = "4.1.0"
564
+ version = "4.1.2"
565
565
  source = { editable = "." }
566
566
  dependencies = [
567
567
  { name = "aiohttp" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes