agentirc-cli 4.2.0__tar.gz → 4.3.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 (310) hide show
  1. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/CHANGELOG.md +26 -0
  2. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/PKG-INFO +1 -1
  3. agentirc_cli-4.3.0/culture/aio.py +12 -0
  4. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/agent.py +29 -1
  5. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/agent_runner.py +4 -2
  6. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/config.py +27 -0
  7. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/daemon.py +11 -10
  8. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/irc_transport.py +5 -4
  9. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/config.py +27 -0
  10. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/daemon.py +10 -9
  11. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/irc_transport.py +5 -4
  12. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/agent_runner.py +4 -2
  13. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/config.py +27 -0
  14. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/daemon.py +11 -10
  15. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/irc_transport.py +5 -4
  16. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/agent_runner.py +4 -2
  17. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/config.py +27 -0
  18. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/daemon.py +11 -10
  19. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/irc_transport.py +5 -4
  20. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/app.py +8 -7
  21. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/client.py +2 -3
  22. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/client.py +3 -2
  23. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/server_link.py +9 -8
  24. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/skills/rooms.py +2 -1
  25. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/agent-harness-spec.md +5 -1
  26. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +52 -0
  27. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/config.py +27 -0
  28. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/daemon.py +11 -10
  29. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/irc_transport.py +5 -4
  30. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/pyproject.toml +1 -1
  31. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_archive.py +405 -0
  32. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_daemon_ipc.py +2 -2
  33. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_mention_target_cleanup.py +6 -6
  34. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/uv.lock +1 -1
  35. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.claude/skills/pr-review/SKILL.md +0 -0
  36. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.claude/skills/run-tests/SKILL.md +0 -0
  37. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  38. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.flake8 +0 -0
  39. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.github/workflows/pages.yml +0 -0
  40. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.github/workflows/publish.yml +0 -0
  41. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.github/workflows/security-checks.yml +0 -0
  42. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.github/workflows/tests.yml +0 -0
  43. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.gitignore +0 -0
  44. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.markdownlint-cli2.yaml +0 -0
  45. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.pr_agent.toml +0 -0
  46. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.pre-commit-config.yaml +0 -0
  47. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/.pylintrc +0 -0
  48. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/CLAUDE.md +0 -0
  49. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/CNAME +0 -0
  50. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/Gemfile +0 -0
  51. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/Gemfile.lock +0 -0
  52. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/LICENSE +0 -0
  53. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/README.md +0 -0
  54. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/SECURITY.md +0 -0
  55. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/_config.yml +0 -0
  56. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/_sass/color_schemes/anthropic.scss +0 -0
  57. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/_sass/custom/custom.scss +0 -0
  58. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/__init__.py +0 -0
  59. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/__main__.py +0 -0
  60. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/bots/__init__.py +0 -0
  61. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/bots/bot.py +0 -0
  62. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/bots/bot_manager.py +0 -0
  63. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/bots/config.py +0 -0
  64. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/bots/http_listener.py +0 -0
  65. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/bots/template_engine.py +0 -0
  66. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/bots/virtual_client.py +0 -0
  67. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/__init__.py +0 -0
  68. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/_helpers.py +0 -0
  69. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/bot.py +0 -0
  70. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/channel.py +0 -0
  71. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/mesh.py +0 -0
  72. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/server.py +0 -0
  73. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/cli/skills.py +0 -0
  74. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/__init__.py +0 -0
  75. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/__init__.py +0 -0
  76. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/ipc.py +0 -0
  77. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/message_buffer.py +0 -0
  78. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/skill/SKILL.md +0 -0
  79. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/skill/__init__.py +0 -0
  80. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/skill/irc_client.py +0 -0
  81. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/socket_server.py +0 -0
  82. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/supervisor.py +0 -0
  83. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/acp/webhook.py +0 -0
  84. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/__init__.py +0 -0
  85. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/__main__.py +0 -0
  86. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/agent_runner.py +0 -0
  87. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/ipc.py +0 -0
  88. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/message_buffer.py +0 -0
  89. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/skill/SKILL.md +0 -0
  90. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/skill/__init__.py +0 -0
  91. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/skill/irc_client.py +0 -0
  92. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/socket_server.py +0 -0
  93. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/supervisor.py +0 -0
  94. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/claude/webhook.py +0 -0
  95. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/__init__.py +0 -0
  96. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/ipc.py +0 -0
  97. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/message_buffer.py +0 -0
  98. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/skill/SKILL.md +0 -0
  99. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/skill/__init__.py +0 -0
  100. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/skill/irc_client.py +0 -0
  101. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/socket_server.py +0 -0
  102. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/supervisor.py +0 -0
  103. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/codex/webhook.py +0 -0
  104. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/__init__.py +0 -0
  105. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/ipc.py +0 -0
  106. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/message_buffer.py +0 -0
  107. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/skill/SKILL.md +0 -0
  108. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/skill/__init__.py +0 -0
  109. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/skill/irc_client.py +0 -0
  110. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/socket_server.py +0 -0
  111. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/supervisor.py +0 -0
  112. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/clients/copilot/webhook.py +0 -0
  113. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/__init__.py +0 -0
  114. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/commands.py +0 -0
  115. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/widgets/__init__.py +0 -0
  116. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/widgets/chat.py +0 -0
  117. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/widgets/info_panel.py +0 -0
  118. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/console/widgets/sidebar.py +0 -0
  119. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/credentials.py +0 -0
  120. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/learn_prompt.py +0 -0
  121. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/mesh_config.py +0 -0
  122. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/observer.py +0 -0
  123. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/overview/__init__.py +0 -0
  124. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/overview/collector.py +0 -0
  125. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/overview/model.py +0 -0
  126. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/overview/renderer_text.py +0 -0
  127. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/overview/renderer_web.py +0 -0
  128. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/overview/web/style.css +0 -0
  129. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/persistence.py +0 -0
  130. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/pidfile.py +0 -0
  131. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/__init__.py +0 -0
  132. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/commands.py +0 -0
  133. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/extensions/federation.md +0 -0
  134. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/extensions/history.md +0 -0
  135. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/extensions/icons.md +0 -0
  136. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/extensions/rooms.md +0 -0
  137. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/extensions/tags.md +0 -0
  138. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/extensions/threads.md +0 -0
  139. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/message.py +0 -0
  140. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/protocol-index.md +0 -0
  141. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/protocol/replies.py +0 -0
  142. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/__init__.py +0 -0
  143. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/__main__.py +0 -0
  144. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/channel.py +0 -0
  145. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/config.py +0 -0
  146. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/ircd.py +0 -0
  147. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/remote_client.py +0 -0
  148. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/room_store.py +0 -0
  149. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/rooms_util.py +0 -0
  150. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/skill.py +0 -0
  151. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/skills/__init__.py +0 -0
  152. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/skills/history.py +0 -0
  153. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/skills/icon.py +0 -0
  154. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/skills/threads.py +0 -0
  155. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/server/thread_store.py +0 -0
  156. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/culture/skills/culture/SKILL.md +0 -0
  157. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/agent-lifecycle.md +0 -0
  158. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/agentic-self-learn.md +0 -0
  159. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/agent-client.md +0 -0
  160. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/design.md +0 -0
  161. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/harness-conformance.md +0 -0
  162. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/index.md +0 -0
  163. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/layer1-core-irc.md +0 -0
  164. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/layer2-attention.md +0 -0
  165. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/layer3-skills.md +0 -0
  166. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/layer4-federation.md +0 -0
  167. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/layer5-agent-harness.md +0 -0
  168. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/server-architecture.md +0 -0
  169. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/architecture/threads.md +0 -0
  170. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/channel-polling.md +0 -0
  171. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/acp/overview.md +0 -0
  172. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/claude/configuration.md +0 -0
  173. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/claude/context-management.md +0 -0
  174. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/claude/irc-tools.md +0 -0
  175. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/claude/overview.md +0 -0
  176. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/claude/setup.md +0 -0
  177. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/claude/supervisor.md +0 -0
  178. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/claude/webhooks.md +0 -0
  179. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/codex/configuration.md +0 -0
  180. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/codex/context-management.md +0 -0
  181. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/codex/irc-tools.md +0 -0
  182. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/codex/overview.md +0 -0
  183. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/codex/setup.md +0 -0
  184. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/codex/supervisor.md +0 -0
  185. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/codex/webhooks.md +0 -0
  186. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/copilot/configuration.md +0 -0
  187. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/copilot/context-management.md +0 -0
  188. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/copilot/irc-tools.md +0 -0
  189. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/copilot/overview.md +0 -0
  190. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/copilot/setup.md +0 -0
  191. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/copilot/supervisor.md +0 -0
  192. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/clients/copilot/webhooks.md +0 -0
  193. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/culture-cli.md +0 -0
  194. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/getting-started.md +0 -0
  195. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/index.md +0 -0
  196. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/SECURITY.md +0 -0
  197. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/bots.md +0 -0
  198. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/ci.md +0 -0
  199. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/cli.md +0 -0
  200. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/docs-site.md +0 -0
  201. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/index.md +0 -0
  202. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/ops-tooling.md +0 -0
  203. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/overview.md +0 -0
  204. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/operations/publishing.md +0 -0
  205. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  206. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/rooms.md +0 -0
  207. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/server-rename.md +0 -0
  208. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  209. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  210. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  211. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  212. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  213. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  214. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  215. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  216. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  217. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  218. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  219. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  220. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  221. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  222. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  223. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  224. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  225. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  226. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  227. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  228. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  229. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/01-pair-programming.md +0 -0
  230. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
  231. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
  232. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
  233. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/05-the-observer.md +0 -0
  234. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/06-cross-server-ops.md +0 -0
  235. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
  236. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/08-apps-as-agents.md +0 -0
  237. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/09-research-swarm.md +0 -0
  238. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases/10-agent-lifecycle.md +0 -0
  239. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/use-cases-index.md +0 -0
  240. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/docs/what-is-culture.md +0 -0
  241. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/README.md +0 -0
  242. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/ipc.py +0 -0
  243. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/message_buffer.py +0 -0
  244. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/skill/SKILL.md +0 -0
  245. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/skill/irc_client.py +0 -0
  246. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/socket_server.py +0 -0
  247. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/packages/agent-harness/webhook.py +0 -0
  248. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  249. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  250. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  251. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  252. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/sonar-project.properties +0 -0
  253. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/__init__.py +0 -0
  254. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/conftest.py +0 -0
  255. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_acp_daemon.py +0 -0
  256. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_agent_runner.py +0 -0
  257. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_bot.py +0 -0
  258. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_bot_config.py +0 -0
  259. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_bot_manager.py +0 -0
  260. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_bots_integration.py +0 -0
  261. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_channel.py +0 -0
  262. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_codex_daemon.py +0 -0
  263. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_connection.py +0 -0
  264. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_console_client.py +0 -0
  265. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_console_commands.py +0 -0
  266. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_console_connection.py +0 -0
  267. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_console_icons.py +0 -0
  268. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_console_integration.py +0 -0
  269. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_copilot_daemon.py +0 -0
  270. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_daemon.py +0 -0
  271. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_daemon_config.py +0 -0
  272. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_discovery.py +0 -0
  273. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_federation.py +0 -0
  274. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_history.py +0 -0
  275. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_http_listener.py +0 -0
  276. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_integration_layer5.py +0 -0
  277. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_ipc.py +0 -0
  278. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_irc_transport.py +0 -0
  279. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_link_reconnect.py +0 -0
  280. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_mention_alias.py +0 -0
  281. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_mentions.py +0 -0
  282. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_mesh_config.py +0 -0
  283. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_message.py +0 -0
  284. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_message_buffer.py +0 -0
  285. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_messaging.py +0 -0
  286. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_modes.py +0 -0
  287. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_overview_cli.py +0 -0
  288. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_overview_collector.py +0 -0
  289. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_overview_model.py +0 -0
  290. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_overview_renderer.py +0 -0
  291. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_overview_web.py +0 -0
  292. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_persistence.py +0 -0
  293. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_pidfile.py +0 -0
  294. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_poll_loop.py +0 -0
  295. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_room_persistence.py +0 -0
  296. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_rooms.py +0 -0
  297. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_rooms_federation.py +0 -0
  298. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_rooms_integration.py +0 -0
  299. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_server_icon_skill.py +0 -0
  300. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_setup_update_cli.py +0 -0
  301. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_skill_client.py +0 -0
  302. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_skills.py +0 -0
  303. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_socket_server.py +0 -0
  304. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_supervisor.py +0 -0
  305. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_template_engine.py +0 -0
  306. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_thread_buffer.py +0 -0
  307. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_threads.py +0 -0
  308. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_virtual_client.py +0 -0
  309. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_wait_for_port.py +0 -0
  310. {agentirc_cli-4.2.0 → agentirc_cli-4.3.0}/tests/test_webhook.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.3.0] - 2026-04-07
8
+
9
+
10
+ ### Added
11
+
12
+ - agent delete command to remove agents from config entirely
13
+ - agent create now overwrites archived agents, enabling harness/model migration
14
+
15
+
16
+ ### Fixed
17
+
18
+ - agent create no longer blocks when the matching nick is archived
19
+
20
+ ## [4.2.1] - 2026-04-07
21
+
22
+
23
+ ### Changed
24
+
25
+ - Update dispatch patterns to use declarative maybe_await() utility for handling both sync and async handlers
26
+ - Remove unnecessary async keyword from ~40 handler functions that never use await
27
+
28
+
29
+ ### Fixed
30
+
31
+ - SonarCloud S7503: async functions that never await (issue #83)
32
+
7
33
  ## [4.2.0] - 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.2.0
3
+ Version: 4.3.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
@@ -0,0 +1,12 @@
1
+ """Async utilities for culture."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+
7
+
8
+ async def maybe_await(result):
9
+ """Await the result only if it's a coroutine, otherwise return directly."""
10
+ if asyncio.iscoroutine(result):
11
+ return await result
12
+ return result
@@ -16,6 +16,7 @@ from culture.clients.claude.config import (
16
16
  archive_agent,
17
17
  load_config,
18
18
  load_config_or_default,
19
+ remove_agent,
19
20
  sanitize_agent_name,
20
21
  unarchive_agent,
21
22
  )
@@ -158,11 +159,16 @@ def register(subparsers: argparse._SubParsersAction) -> None:
158
159
  unarchive_parser.add_argument("nick", help="Agent nick to unarchive")
159
160
  unarchive_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
160
161
 
162
+ # -- delete ---------------------------------------------------------------
163
+ delete_parser = agent_sub.add_parser("delete", help="Remove an agent from config entirely")
164
+ delete_parser.add_argument("nick", help="Agent nick to delete")
165
+ delete_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
166
+
161
167
 
162
168
  def dispatch(args: argparse.Namespace) -> None:
163
169
  if not args.agent_command:
164
170
  print(
165
- "Usage: culture agent {create|join|start|stop|status|rename|assign|sleep|wake|learn|message|read|archive|unarchive}",
171
+ "Usage: culture agent {create|join|start|stop|status|rename|assign|sleep|wake|learn|message|read|archive|unarchive|delete}",
166
172
  file=sys.stderr,
167
173
  )
168
174
  sys.exit(1)
@@ -182,6 +188,7 @@ def dispatch(args: argparse.Namespace) -> None:
182
188
  "read": _cmd_read,
183
189
  "archive": _cmd_archive,
184
190
  "unarchive": _cmd_unarchive,
191
+ "delete": _cmd_delete,
185
192
  }
186
193
  handler = handlers.get(args.agent_command)
187
194
  if handler:
@@ -264,6 +271,10 @@ def _cmd_create(args: argparse.Namespace) -> None:
264
271
 
265
272
  for existing in config.agents:
266
273
  if existing.nick == full_nick:
274
+ if existing.archived:
275
+ print(f"Replacing archived agent '{full_nick}'")
276
+ remove_agent(args.config, full_nick)
277
+ break
267
278
  channels = existing.channels if isinstance(existing.channels, list) else []
268
279
  print(f"Agent '{full_nick}' already exists in config", file=sys.stderr)
269
280
  print(f" Directory: {existing.directory}", file=sys.stderr)
@@ -831,3 +842,20 @@ def _cmd_unarchive(args: argparse.Namespace) -> None:
831
842
 
832
843
  print(f"Agent unarchived: {args.nick}")
833
844
  print(f"\nStart with: culture agent start {args.nick}")
845
+
846
+
847
+ def _cmd_delete(args: argparse.Namespace) -> None:
848
+ """Remove an agent from config entirely."""
849
+ config = load_config_or_default(args.config)
850
+ agent = config.get_agent(args.nick)
851
+ if not agent:
852
+ print(f"Agent '{args.nick}' not found in config", file=sys.stderr)
853
+ sys.exit(1)
854
+
855
+ # Stop the agent if it's running
856
+ pid = read_pid(f"agent-{args.nick}")
857
+ if pid and is_process_alive(pid):
858
+ stop_agent(args.nick)
859
+
860
+ remove_agent(args.config, args.nick)
861
+ print(f"Agent deleted: {args.nick}")
@@ -14,6 +14,8 @@ import shutil
14
14
  import tempfile
15
15
  from typing import Any, Awaitable, Callable
16
16
 
17
+ from culture.aio import maybe_await
18
+
17
19
  logger = logging.getLogger(__name__)
18
20
 
19
21
 
@@ -33,7 +35,7 @@ class ACPAgentRunner:
33
35
  system_prompt: str = "",
34
36
  on_exit: Callable[[int], Awaitable[None]] | None = None,
35
37
  on_message: Callable[[dict[str, Any]], Awaitable[None]] | None = None,
36
- on_turn_error: Callable[[], Awaitable[None]] | None = None,
38
+ on_turn_error: Callable[[], Awaitable[None] | None] | None = None,
37
39
  ) -> None:
38
40
  self.model = model
39
41
  self.directory = directory
@@ -387,7 +389,7 @@ class ACPAgentRunner:
387
389
  except Exception:
388
390
  logger.exception("ACP turn error")
389
391
  if self.on_turn_error:
390
- await self.on_turn_error()
392
+ await maybe_await(self.on_turn_error())
391
393
  finally:
392
394
  self._busy = False
393
395
 
@@ -248,3 +248,30 @@ def rename_agent(
248
248
  return
249
249
 
250
250
  raise ValueError(f"agent {old_nick!r} not found in config")
251
+
252
+
253
+ def remove_agent(
254
+ path: str | Path,
255
+ nick: str,
256
+ ) -> None:
257
+ """Remove an agent from config entirely.
258
+
259
+ Operates on raw YAML to preserve backend-specific fields on other
260
+ agents that the typed schema would strip.
261
+ Raises ValueError if the agent is not found.
262
+ """
263
+ path = Path(path)
264
+ if not path.exists():
265
+ raise ValueError(f"agent {nick!r} not found in config")
266
+
267
+ with open(path) as f:
268
+ raw = yaml.safe_load(f) or {}
269
+
270
+ agents = raw.get("agents", [])
271
+ for i, agent_raw in enumerate(agents):
272
+ if agent_raw.get("nick") == nick:
273
+ agents.pop(i)
274
+ with open(path, "w") as f:
275
+ yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
276
+ return
277
+ raise ValueError(f"agent {nick!r} not found in config")
@@ -16,6 +16,7 @@ import os
16
16
  import time
17
17
  from collections import deque
18
18
 
19
+ from culture.aio import maybe_await
19
20
  from culture.clients.acp.agent_runner import ACPAgentRunner
20
21
  from culture.clients.acp.config import AgentConfig, DaemonConfig
21
22
  from culture.clients.acp.ipc import make_response
@@ -318,7 +319,7 @@ class ACPDaemon:
318
319
  # Agent runner helpers
319
320
  # ------------------------------------------------------------------
320
321
 
321
- async def _on_turn_error(self) -> None:
322
+ def _on_turn_error(self) -> None:
322
323
  """Clean up stale relay target when a prompt fails."""
323
324
  if self._mention_targets:
324
325
  self._mention_targets.popleft()
@@ -436,7 +437,7 @@ class ACPDaemon:
436
437
  if line:
437
438
  await self._transport.send_privmsg(relay_target, line)
438
439
 
439
- async def _capture_agent_status(self, msg: dict) -> None:
440
+ def _capture_agent_status(self, msg: dict) -> None:
440
441
  """Capture the last assistant text for status reporting and fulfill any pending query."""
441
442
  if msg.get("type") == "assistant":
442
443
  for block in msg.get("content", []):
@@ -459,7 +460,7 @@ class ACPDaemon:
459
460
  if self._supervisor:
460
461
  await self._supervisor.observe(msg)
461
462
 
462
- await self._capture_agent_status(msg)
463
+ self._capture_agent_status(msg)
463
464
 
464
465
  def _build_system_prompt(self) -> str:
465
466
  if self.agent.system_prompt:
@@ -589,7 +590,7 @@ class ACPDaemon:
589
590
  handler = self._ipc_dispatch.get(msg_type)
590
591
  if handler is None:
591
592
  return make_response(req_id, ok=False, error=f"Unknown message type: {msg_type!r}")
592
- return await handler(req_id, msg)
593
+ return await maybe_await(handler(req_id, msg))
593
594
  except Exception as exc:
594
595
  logger.exception("IPC handler error for type %r", msg_type)
595
596
  return make_response(req_id, ok=False, error=str(exc))
@@ -598,12 +599,12 @@ class ACPDaemon:
598
599
  # IPC sub-handlers
599
600
  # ------------------------------------------------------------------
600
601
 
601
- async def _ipc_pause(self, req_id: str, msg: dict) -> dict:
602
+ def _ipc_pause(self, req_id: str, msg: dict) -> dict:
602
603
  self._paused = True
603
604
  logger.info("Agent %s paused", self.agent.nick)
604
605
  return make_response(req_id, ok=True)
605
606
 
606
- async def _ipc_resume(self, req_id: str, msg: dict) -> dict:
607
+ def _ipc_resume(self, req_id: str, msg: dict) -> dict:
607
608
  self._paused = False
608
609
  logger.info("Agent %s resumed", self.agent.nick)
609
610
  # NOTE: Catch-up on missed messages is not yet implemented.
@@ -689,7 +690,7 @@ class ACPDaemon:
689
690
  await self._transport.send_privmsg(channel, text)
690
691
  return make_response(req_id, ok=True)
691
692
 
692
- async def _ipc_irc_read(self, req_id: str, msg: dict) -> dict:
693
+ def _ipc_irc_read(self, req_id: str, msg: dict) -> dict:
693
694
  channel = msg.get("channel", "")
694
695
  limit = int(msg.get("limit", 50))
695
696
  if not channel:
@@ -764,7 +765,7 @@ class ACPDaemon:
764
765
  await self._transport.send_thread_close(channel, thread_name, summary)
765
766
  return make_response(req_id, ok=True)
766
767
 
767
- async def _ipc_irc_thread_read(self, req_id: str, msg: dict) -> dict:
768
+ def _ipc_irc_thread_read(self, req_id: str, msg: dict) -> dict:
768
769
  channel = msg.get("channel", "")
769
770
  thread_name = msg.get("thread", "")
770
771
  limit = int(msg.get("limit", 50))
@@ -783,7 +784,7 @@ class ACPDaemon:
783
784
  },
784
785
  )
785
786
 
786
- async def _ipc_irc_channels(self, req_id: str, msg: dict) -> dict:
787
+ def _ipc_irc_channels(self, req_id: str, msg: dict) -> dict:
787
788
  assert self._transport is not None
788
789
  return make_response(req_id, ok=True, data={"channels": self._transport.channels})
789
790
 
@@ -828,7 +829,7 @@ class ACPDaemon:
828
829
  await self._agent_runner.send_prompt("/clear")
829
830
  return make_response(req_id, ok=True)
830
831
 
831
- async def _ipc_shutdown(self, req_id: str, msg: dict) -> dict:
832
+ def _ipc_shutdown(self, req_id: str, msg: dict) -> dict:
832
833
  task = asyncio.create_task(self._graceful_shutdown())
833
834
  self._background_tasks.add(task)
834
835
  task.add_done_callback(self._background_tasks.discard)
@@ -5,6 +5,7 @@ import logging
5
5
  import re
6
6
  from typing import Callable
7
7
 
8
+ from culture.aio import maybe_await
8
9
  from culture.clients.acp.message_buffer import MessageBuffer
9
10
  from culture.protocol.message import Message
10
11
 
@@ -164,7 +165,7 @@ class IRCTransport:
164
165
  async def _handle(self, msg: Message) -> None:
165
166
  handler = self._cmd_handlers.get(msg.command)
166
167
  if handler:
167
- await handler(msg)
168
+ await maybe_await(handler(msg))
168
169
 
169
170
  async def _on_ping(self, msg: Message) -> None:
170
171
  token = msg.params[0] if msg.params else ""
@@ -180,7 +181,7 @@ class IRCTransport:
180
181
  if self.icon:
181
182
  await self._send_raw(f"ICON {self.icon}")
182
183
 
183
- async def _on_privmsg(self, msg: Message) -> None:
184
+ def _on_privmsg(self, msg: Message) -> None:
184
185
  if len(msg.params) < 2:
185
186
  return
186
187
  target = msg.params[0]
@@ -199,7 +200,7 @@ class IRCTransport:
199
200
  ):
200
201
  self.on_mention(target, sender, text)
201
202
 
202
- async def _on_notice(self, msg: Message) -> None:
203
+ def _on_notice(self, msg: Message) -> None:
203
204
  if len(msg.params) < 2:
204
205
  return
205
206
  target = msg.params[0]
@@ -208,7 +209,7 @@ class IRCTransport:
208
209
  if target.startswith("#"):
209
210
  self.buffer.add(target, sender, text)
210
211
 
211
- async def _on_roominvite(self, msg: Message) -> None:
212
+ def _on_roominvite(self, msg: Message) -> None:
212
213
  if len(msg.params) < 3:
213
214
  return
214
215
  channel = msg.params[0]
@@ -303,6 +303,33 @@ def unarchive_agent(
303
303
  raise ValueError(f"agent {nick!r} not found in config")
304
304
 
305
305
 
306
+ def remove_agent(
307
+ path: str | Path,
308
+ nick: str,
309
+ ) -> None:
310
+ """Remove an agent from config entirely.
311
+
312
+ Operates on raw YAML to preserve backend-specific fields (e.g.
313
+ acp_command) on other agents that the typed schema would strip.
314
+ Raises ValueError if the agent is not found.
315
+ """
316
+ path = Path(path)
317
+ if not path.exists():
318
+ raise ValueError(f"agent {nick!r} not found in config")
319
+
320
+ with open(path) as f:
321
+ raw = yaml.safe_load(f) or {}
322
+
323
+ agents = raw.get("agents", [])
324
+ for i, agent_raw in enumerate(agents):
325
+ if agent_raw.get("nick") == nick:
326
+ agents.pop(i)
327
+ with open(path, "w") as f:
328
+ yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
329
+ return
330
+ raise ValueError(f"agent {nick!r} not found in config")
331
+
332
+
306
333
  def archive_server(
307
334
  path: str | Path,
308
335
  reason: str = "",
@@ -6,6 +6,7 @@ import logging
6
6
  import os
7
7
  import time
8
8
 
9
+ from culture.aio import maybe_await
9
10
  from culture.clients.claude.agent_runner import AgentRunner
10
11
  from culture.clients.claude.config import AgentConfig, DaemonConfig
11
12
  from culture.clients.claude.ipc import make_response
@@ -378,7 +379,7 @@ class AgentDaemon:
378
379
  if self._supervisor:
379
380
  await self._supervisor.observe(msg)
380
381
 
381
- await self._capture_agent_status(msg)
382
+ self._capture_agent_status(msg)
382
383
 
383
384
  def _build_system_prompt(self) -> str:
384
385
  if self.agent.system_prompt:
@@ -433,7 +434,7 @@ class AgentDaemon:
433
434
  return True
434
435
  return False
435
436
 
436
- async def _capture_agent_status(self, msg: dict) -> None:
437
+ def _capture_agent_status(self, msg: dict) -> None:
437
438
  """Capture the last assistant text for status reporting and fulfill any pending query."""
438
439
  if msg.get("type") == "assistant":
439
440
  for block in msg.get("content", []):
@@ -516,7 +517,7 @@ class AgentDaemon:
516
517
  handler = self._ipc_dispatch.get(msg_type)
517
518
  if handler is None:
518
519
  return make_response(req_id, ok=False, error=f"Unknown message type: {msg_type!r}")
519
- return await handler(req_id, msg)
520
+ return await maybe_await(handler(req_id, msg))
520
521
  except Exception as exc:
521
522
  logger.exception("IPC handler error for type %r", msg_type)
522
523
  return make_response(req_id, ok=False, error=str(exc))
@@ -525,12 +526,12 @@ class AgentDaemon:
525
526
  # IPC sub-handlers
526
527
  # ------------------------------------------------------------------
527
528
 
528
- async def _ipc_pause(self, req_id: str, msg: dict) -> dict:
529
+ def _ipc_pause(self, req_id: str, msg: dict) -> dict:
529
530
  self._paused = True
530
531
  logger.info("Agent %s paused", self.agent.nick)
531
532
  return make_response(req_id, ok=True)
532
533
 
533
- async def _ipc_resume(self, req_id: str, msg: dict) -> dict:
534
+ def _ipc_resume(self, req_id: str, msg: dict) -> dict:
534
535
  self._paused = False
535
536
  logger.info("Agent %s resumed", self.agent.nick)
536
537
  # NOTE: Catch-up on missed messages is not yet implemented.
@@ -613,7 +614,7 @@ class AgentDaemon:
613
614
  await self._transport.send_privmsg(channel, text)
614
615
  return make_response(req_id, ok=True)
615
616
 
616
- async def _ipc_irc_read(self, req_id: str, msg: dict) -> dict:
617
+ def _ipc_irc_read(self, req_id: str, msg: dict) -> dict:
617
618
  channel = msg.get("channel", "")
618
619
  limit = int(msg.get("limit", 50))
619
620
  if not channel:
@@ -688,7 +689,7 @@ class AgentDaemon:
688
689
  await self._transport.send_thread_close(channel, thread_name, summary)
689
690
  return make_response(req_id, ok=True)
690
691
 
691
- async def _ipc_irc_thread_read(self, req_id: str, msg: dict) -> dict:
692
+ def _ipc_irc_thread_read(self, req_id: str, msg: dict) -> dict:
692
693
  channel = msg.get("channel", "")
693
694
  thread_name = msg.get("thread", "")
694
695
  limit = int(msg.get("limit", 50))
@@ -707,7 +708,7 @@ class AgentDaemon:
707
708
  },
708
709
  )
709
710
 
710
- async def _ipc_irc_channels(self, req_id: str, msg: dict) -> dict:
711
+ def _ipc_irc_channels(self, req_id: str, msg: dict) -> dict:
711
712
  assert self._transport is not None
712
713
  return make_response(req_id, ok=True, data={"channels": self._transport.channels})
713
714
 
@@ -752,7 +753,7 @@ class AgentDaemon:
752
753
  await self._agent_runner.send_prompt("/clear")
753
754
  return make_response(req_id, ok=True)
754
755
 
755
- async def _ipc_shutdown(self, req_id: str, msg: dict) -> dict:
756
+ def _ipc_shutdown(self, req_id: str, msg: dict) -> dict:
756
757
  task = asyncio.create_task(self._graceful_shutdown())
757
758
  self._background_tasks.add(task)
758
759
  task.add_done_callback(self._background_tasks.discard)
@@ -5,6 +5,7 @@ import logging
5
5
  import re
6
6
  from typing import Callable
7
7
 
8
+ from culture.aio import maybe_await
8
9
  from culture.clients.claude.message_buffer import MessageBuffer
9
10
  from culture.protocol.message import Message
10
11
 
@@ -164,7 +165,7 @@ class IRCTransport:
164
165
  async def _handle(self, msg: Message) -> None:
165
166
  handler = self._cmd_handlers.get(msg.command)
166
167
  if handler:
167
- await handler(msg)
168
+ await maybe_await(handler(msg))
168
169
 
169
170
  async def _on_ping(self, msg: Message) -> None:
170
171
  token = msg.params[0] if msg.params else ""
@@ -180,7 +181,7 @@ class IRCTransport:
180
181
  if self.icon:
181
182
  await self._send_raw(f"ICON {self.icon}")
182
183
 
183
- async def _on_privmsg(self, msg: Message) -> None:
184
+ def _on_privmsg(self, msg: Message) -> None:
184
185
  if len(msg.params) < 2:
185
186
  return
186
187
  target = msg.params[0]
@@ -199,7 +200,7 @@ class IRCTransport:
199
200
  ):
200
201
  self.on_mention(target, sender, text)
201
202
 
202
- async def _on_notice(self, msg: Message) -> None:
203
+ def _on_notice(self, msg: Message) -> None:
203
204
  if len(msg.params) < 2:
204
205
  return
205
206
  target = msg.params[0]
@@ -208,7 +209,7 @@ class IRCTransport:
208
209
  if target.startswith("#"):
209
210
  self.buffer.add(target, sender, text)
210
211
 
211
- async def _on_roominvite(self, msg: Message) -> None:
212
+ def _on_roominvite(self, msg: Message) -> None:
212
213
  if len(msg.params) < 3:
213
214
  return
214
215
  channel = msg.params[0]
@@ -10,6 +10,8 @@ import shutil
10
10
  import tempfile
11
11
  from typing import Any, Awaitable, Callable
12
12
 
13
+ from culture.aio import maybe_await
14
+
13
15
  logger = logging.getLogger(__name__)
14
16
 
15
17
 
@@ -23,7 +25,7 @@ class CodexAgentRunner:
23
25
  system_prompt: str = "",
24
26
  on_exit: Callable[[int], Awaitable[None]] | None = None,
25
27
  on_message: Callable[[dict[str, Any]], Awaitable[None]] | None = None,
26
- on_turn_error: Callable[[], Awaitable[None]] | None = None,
28
+ on_turn_error: Callable[[], Awaitable[None] | None] | None = None,
27
29
  ) -> None:
28
30
  self.model = model
29
31
  self.directory = directory
@@ -321,7 +323,7 @@ class CodexAgentRunner:
321
323
  except Exception:
322
324
  logger.exception("Codex turn error")
323
325
  if self.on_turn_error:
324
- await self.on_turn_error()
326
+ await maybe_await(self.on_turn_error())
325
327
 
326
328
  except asyncio.CancelledError:
327
329
  raise
@@ -248,3 +248,30 @@ def rename_agent(
248
248
  return
249
249
 
250
250
  raise ValueError(f"agent {old_nick!r} not found in config")
251
+
252
+
253
+ def remove_agent(
254
+ path: str | Path,
255
+ nick: str,
256
+ ) -> None:
257
+ """Remove an agent from config entirely.
258
+
259
+ Operates on raw YAML to preserve backend-specific fields on other
260
+ agents that the typed schema would strip.
261
+ Raises ValueError if the agent is not found.
262
+ """
263
+ path = Path(path)
264
+ if not path.exists():
265
+ raise ValueError(f"agent {nick!r} not found in config")
266
+
267
+ with open(path) as f:
268
+ raw = yaml.safe_load(f) or {}
269
+
270
+ agents = raw.get("agents", [])
271
+ for i, agent_raw in enumerate(agents):
272
+ if agent_raw.get("nick") == nick:
273
+ agents.pop(i)
274
+ with open(path, "w") as f:
275
+ yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
276
+ return
277
+ raise ValueError(f"agent {nick!r} not found in config")
@@ -13,6 +13,7 @@ import os
13
13
  import time
14
14
  from collections import deque
15
15
 
16
+ from culture.aio import maybe_await
16
17
  from culture.clients.codex.agent_runner import CodexAgentRunner
17
18
  from culture.clients.codex.config import AgentConfig, DaemonConfig
18
19
  from culture.clients.codex.ipc import make_response
@@ -293,7 +294,7 @@ class CodexDaemon:
293
294
  # Agent runner helpers
294
295
  # ------------------------------------------------------------------
295
296
 
296
- async def _on_turn_error(self) -> None:
297
+ def _on_turn_error(self) -> None:
297
298
  """Clean up stale relay target when a prompt fails."""
298
299
  if self._mention_targets:
299
300
  self._mention_targets.popleft()
@@ -404,7 +405,7 @@ class CodexDaemon:
404
405
  if line:
405
406
  await self._transport.send_privmsg(relay_target, line)
406
407
 
407
- async def _capture_agent_status(self, msg: dict) -> None:
408
+ def _capture_agent_status(self, msg: dict) -> None:
408
409
  """Capture the last assistant text for status reporting and fulfill any pending query."""
409
410
  if msg.get("type") == "assistant":
410
411
  for block in msg.get("content", []):
@@ -427,7 +428,7 @@ class CodexDaemon:
427
428
  if self._supervisor:
428
429
  await self._supervisor.observe(msg)
429
430
 
430
- await self._capture_agent_status(msg)
431
+ self._capture_agent_status(msg)
431
432
 
432
433
  def _build_system_prompt(self) -> str:
433
434
  if self.agent.system_prompt:
@@ -549,7 +550,7 @@ class CodexDaemon:
549
550
  handler = self._ipc_dispatch.get(msg_type)
550
551
  if handler is None:
551
552
  return make_response(req_id, ok=False, error=f"Unknown message type: {msg_type!r}")
552
- return await handler(req_id, msg)
553
+ return await maybe_await(handler(req_id, msg))
553
554
  except Exception as exc:
554
555
  logger.exception("IPC handler error for type %r", msg_type)
555
556
  return make_response(req_id, ok=False, error=str(exc))
@@ -558,12 +559,12 @@ class CodexDaemon:
558
559
  # IPC sub-handlers
559
560
  # ------------------------------------------------------------------
560
561
 
561
- async def _ipc_pause(self, req_id: str, msg: dict) -> dict:
562
+ def _ipc_pause(self, req_id: str, msg: dict) -> dict:
562
563
  self._paused = True
563
564
  logger.info("Agent %s paused", self.agent.nick)
564
565
  return make_response(req_id, ok=True)
565
566
 
566
- async def _ipc_resume(self, req_id: str, msg: dict) -> dict:
567
+ def _ipc_resume(self, req_id: str, msg: dict) -> dict:
567
568
  self._paused = False
568
569
  logger.info("Agent %s resumed", self.agent.nick)
569
570
  # NOTE: Catch-up on missed messages is not yet implemented.
@@ -649,7 +650,7 @@ class CodexDaemon:
649
650
  await self._transport.send_privmsg(channel, text)
650
651
  return make_response(req_id, ok=True)
651
652
 
652
- async def _ipc_irc_read(self, req_id: str, msg: dict) -> dict:
653
+ def _ipc_irc_read(self, req_id: str, msg: dict) -> dict:
653
654
  channel = msg.get("channel", "")
654
655
  limit = int(msg.get("limit", 50))
655
656
  if not channel:
@@ -724,7 +725,7 @@ class CodexDaemon:
724
725
  await self._transport.send_thread_close(channel, thread_name, summary)
725
726
  return make_response(req_id, ok=True)
726
727
 
727
- async def _ipc_irc_thread_read(self, req_id: str, msg: dict) -> dict:
728
+ def _ipc_irc_thread_read(self, req_id: str, msg: dict) -> dict:
728
729
  channel = msg.get("channel", "")
729
730
  thread_name = msg.get("thread", "")
730
731
  limit = int(msg.get("limit", 50))
@@ -743,7 +744,7 @@ class CodexDaemon:
743
744
  },
744
745
  )
745
746
 
746
- async def _ipc_irc_channels(self, req_id: str, msg: dict) -> dict:
747
+ def _ipc_irc_channels(self, req_id: str, msg: dict) -> dict:
747
748
  assert self._transport is not None
748
749
  return make_response(req_id, ok=True, data={"channels": self._transport.channels})
749
750
 
@@ -788,7 +789,7 @@ class CodexDaemon:
788
789
  await self._agent_runner.send_prompt("/clear")
789
790
  return make_response(req_id, ok=True)
790
791
 
791
- async def _ipc_shutdown(self, req_id: str, msg: dict) -> dict:
792
+ def _ipc_shutdown(self, req_id: str, msg: dict) -> dict:
792
793
  task = asyncio.create_task(self._graceful_shutdown())
793
794
  self._background_tasks.add(task)
794
795
  task.add_done_callback(self._background_tasks.discard)
@@ -5,6 +5,7 @@ import logging
5
5
  import re
6
6
  from typing import Callable
7
7
 
8
+ from culture.aio import maybe_await
8
9
  from culture.clients.codex.message_buffer import MessageBuffer
9
10
  from culture.protocol.message import Message
10
11
 
@@ -164,7 +165,7 @@ class IRCTransport:
164
165
  async def _handle(self, msg: Message) -> None:
165
166
  handler = self._cmd_handlers.get(msg.command)
166
167
  if handler:
167
- await handler(msg)
168
+ await maybe_await(handler(msg))
168
169
 
169
170
  async def _on_ping(self, msg: Message) -> None:
170
171
  token = msg.params[0] if msg.params else ""
@@ -180,7 +181,7 @@ class IRCTransport:
180
181
  if self.icon:
181
182
  await self._send_raw(f"ICON {self.icon}")
182
183
 
183
- async def _on_privmsg(self, msg: Message) -> None:
184
+ def _on_privmsg(self, msg: Message) -> None:
184
185
  if len(msg.params) < 2:
185
186
  return
186
187
  target = msg.params[0]
@@ -199,7 +200,7 @@ class IRCTransport:
199
200
  ):
200
201
  self.on_mention(target, sender, text)
201
202
 
202
- async def _on_notice(self, msg: Message) -> None:
203
+ def _on_notice(self, msg: Message) -> None:
203
204
  if len(msg.params) < 2:
204
205
  return
205
206
  target = msg.params[0]
@@ -208,7 +209,7 @@ class IRCTransport:
208
209
  if target.startswith("#"):
209
210
  self.buffer.add(target, sender, text)
210
211
 
211
- async def _on_roominvite(self, msg: Message) -> None:
212
+ def _on_roominvite(self, msg: Message) -> None:
212
213
  if len(msg.params) < 3:
213
214
  return
214
215
  channel = msg.params[0]