agentirc-cli 4.4.0__tar.gz → 4.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/CHANGELOG.md +13 -0
  2. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/PKG-INFO +1 -1
  3. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/agent.py +5 -0
  4. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/shared/display.py +2 -1
  5. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/shared/ipc.py +1 -1
  6. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/agent_runner.py +23 -3
  7. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/config.py +67 -38
  8. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/irc_transport.py +4 -0
  9. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/config.py +107 -74
  10. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/irc_transport.py +4 -0
  11. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/config.py +64 -37
  12. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/irc_transport.py +4 -0
  13. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/config.py +64 -37
  14. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/irc_transport.py +4 -0
  15. agentirc_cli-4.4.1/packages/agent-harness/irc_transport.py +238 -0
  16. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/pyproject.toml +1 -1
  17. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_acp_daemon.py +61 -0
  18. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_archive.py +167 -0
  19. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_daemon_config.py +90 -0
  20. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_mention_alias.py +29 -0
  21. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/uv.lock +1 -1
  22. agentirc_cli-4.4.0/packages/agent-harness/irc_transport.py +0 -229
  23. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.claude/skills/pr-review/SKILL.md +0 -0
  24. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.claude/skills/run-tests/SKILL.md +0 -0
  25. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  26. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.flake8 +0 -0
  27. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.github/workflows/pages.yml +0 -0
  28. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.github/workflows/publish.yml +0 -0
  29. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.github/workflows/security-checks.yml +0 -0
  30. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.github/workflows/tests.yml +0 -0
  31. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.gitignore +0 -0
  32. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.markdownlint-cli2.yaml +0 -0
  33. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.pr_agent.toml +0 -0
  34. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.pre-commit-config.yaml +0 -0
  35. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/.pylintrc +0 -0
  36. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/CLAUDE.md +0 -0
  37. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/CNAME +0 -0
  38. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/Gemfile +0 -0
  39. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/Gemfile.lock +0 -0
  40. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/LICENSE +0 -0
  41. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/README.md +0 -0
  42. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/SECURITY.md +0 -0
  43. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/_config.yml +0 -0
  44. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/_sass/color_schemes/anthropic.scss +0 -0
  45. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/_sass/custom/custom.scss +0 -0
  46. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/__init__.py +0 -0
  47. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/__main__.py +0 -0
  48. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/aio.py +0 -0
  49. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/bots/__init__.py +0 -0
  50. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/bots/bot.py +0 -0
  51. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/bots/bot_manager.py +0 -0
  52. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/bots/config.py +0 -0
  53. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/bots/http_listener.py +0 -0
  54. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/bots/template_engine.py +0 -0
  55. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/bots/virtual_client.py +0 -0
  56. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/__init__.py +0 -0
  57. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/bot.py +0 -0
  58. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/channel.py +0 -0
  59. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/mesh.py +0 -0
  60. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/server.py +0 -0
  61. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/shared/__init__.py +0 -0
  62. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/shared/constants.py +0 -0
  63. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/shared/formatting.py +0 -0
  64. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/shared/mesh.py +0 -0
  65. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/shared/process.py +0 -0
  66. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/cli/skills.py +0 -0
  67. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/__init__.py +0 -0
  68. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/__init__.py +0 -0
  69. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/daemon.py +0 -0
  70. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/ipc.py +0 -0
  71. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/message_buffer.py +0 -0
  72. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/skill/SKILL.md +0 -0
  73. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/skill/__init__.py +0 -0
  74. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/skill/irc_client.py +0 -0
  75. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/socket_server.py +0 -0
  76. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/supervisor.py +0 -0
  77. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/acp/webhook.py +0 -0
  78. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/__init__.py +0 -0
  79. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/__main__.py +0 -0
  80. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/agent_runner.py +0 -0
  81. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/daemon.py +0 -0
  82. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/ipc.py +0 -0
  83. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/message_buffer.py +0 -0
  84. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/skill/SKILL.md +0 -0
  85. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/skill/__init__.py +0 -0
  86. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/skill/irc_client.py +0 -0
  87. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/socket_server.py +0 -0
  88. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/supervisor.py +0 -0
  89. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/claude/webhook.py +0 -0
  90. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/__init__.py +0 -0
  91. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/agent_runner.py +0 -0
  92. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/daemon.py +0 -0
  93. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/ipc.py +0 -0
  94. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/message_buffer.py +0 -0
  95. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/skill/SKILL.md +0 -0
  96. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/skill/__init__.py +0 -0
  97. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/skill/irc_client.py +0 -0
  98. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/socket_server.py +0 -0
  99. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/supervisor.py +0 -0
  100. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/codex/webhook.py +0 -0
  101. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/__init__.py +0 -0
  102. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/agent_runner.py +0 -0
  103. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/daemon.py +0 -0
  104. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/ipc.py +0 -0
  105. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/message_buffer.py +0 -0
  106. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/skill/SKILL.md +0 -0
  107. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/skill/__init__.py +0 -0
  108. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/skill/irc_client.py +0 -0
  109. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/socket_server.py +0 -0
  110. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/supervisor.py +0 -0
  111. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/clients/copilot/webhook.py +0 -0
  112. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/__init__.py +0 -0
  113. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/app.py +0 -0
  114. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/client.py +0 -0
  115. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/commands.py +0 -0
  116. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/widgets/__init__.py +0 -0
  117. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/widgets/chat.py +0 -0
  118. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/widgets/info_panel.py +0 -0
  119. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/console/widgets/sidebar.py +0 -0
  120. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/credentials.py +0 -0
  121. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/formatting.py +0 -0
  122. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/learn_prompt.py +0 -0
  123. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/mesh_config.py +0 -0
  124. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/observer.py +0 -0
  125. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/overview/__init__.py +0 -0
  126. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/overview/collector.py +0 -0
  127. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/overview/model.py +0 -0
  128. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/overview/renderer_text.py +0 -0
  129. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/overview/renderer_web.py +0 -0
  130. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/overview/web/style.css +0 -0
  131. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/persistence.py +0 -0
  132. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/pidfile.py +0 -0
  133. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/__init__.py +0 -0
  134. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/commands.py +0 -0
  135. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/extensions/federation.md +0 -0
  136. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/extensions/history.md +0 -0
  137. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/extensions/icons.md +0 -0
  138. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/extensions/rooms.md +0 -0
  139. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/extensions/tags.md +0 -0
  140. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/extensions/threads.md +0 -0
  141. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/message.py +0 -0
  142. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/protocol-index.md +0 -0
  143. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/protocol/replies.py +0 -0
  144. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/__init__.py +0 -0
  145. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/__main__.py +0 -0
  146. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/channel.py +0 -0
  147. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/client.py +0 -0
  148. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/config.py +0 -0
  149. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/history_store.py +0 -0
  150. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/ircd.py +0 -0
  151. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/remote_client.py +0 -0
  152. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/room_store.py +0 -0
  153. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/rooms_util.py +0 -0
  154. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/server_link.py +0 -0
  155. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/skill.py +0 -0
  156. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/skills/__init__.py +0 -0
  157. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/skills/history.py +0 -0
  158. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/skills/icon.py +0 -0
  159. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/skills/rooms.py +0 -0
  160. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/skills/threads.py +0 -0
  161. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/server/thread_store.py +0 -0
  162. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/culture/skills/culture/SKILL.md +0 -0
  163. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/agent-lifecycle.md +0 -0
  164. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/agentic-self-learn.md +0 -0
  165. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/agent-client.md +0 -0
  166. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/agent-harness-spec.md +0 -0
  167. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/design.md +0 -0
  168. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/harness-conformance.md +0 -0
  169. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/index.md +0 -0
  170. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/layer1-core-irc.md +0 -0
  171. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/layer2-attention.md +0 -0
  172. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/layer3-skills.md +0 -0
  173. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/layer4-federation.md +0 -0
  174. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/layer5-agent-harness.md +0 -0
  175. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/server-architecture.md +0 -0
  176. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/architecture/threads.md +0 -0
  177. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/channel-polling.md +0 -0
  178. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/acp/overview.md +0 -0
  179. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/claude/configuration.md +0 -0
  180. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/claude/context-management.md +0 -0
  181. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/claude/irc-tools.md +0 -0
  182. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/claude/overview.md +0 -0
  183. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/claude/setup.md +0 -0
  184. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/claude/supervisor.md +0 -0
  185. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/claude/webhooks.md +0 -0
  186. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/codex/configuration.md +0 -0
  187. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/codex/context-management.md +0 -0
  188. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/codex/irc-tools.md +0 -0
  189. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/codex/overview.md +0 -0
  190. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/codex/setup.md +0 -0
  191. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/codex/supervisor.md +0 -0
  192. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/codex/webhooks.md +0 -0
  193. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/copilot/configuration.md +0 -0
  194. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/copilot/context-management.md +0 -0
  195. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/copilot/irc-tools.md +0 -0
  196. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/copilot/overview.md +0 -0
  197. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/copilot/setup.md +0 -0
  198. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/copilot/supervisor.md +0 -0
  199. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/clients/copilot/webhooks.md +0 -0
  200. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/culture-cli.md +0 -0
  201. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/getting-started.md +0 -0
  202. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/index.md +0 -0
  203. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/SECURITY.md +0 -0
  204. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/bots.md +0 -0
  205. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/ci.md +0 -0
  206. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/cli.md +0 -0
  207. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/docs-site.md +0 -0
  208. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/index.md +0 -0
  209. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/ops-tooling.md +0 -0
  210. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/overview.md +0 -0
  211. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/operations/publishing.md +0 -0
  212. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/persistent-history.md +0 -0
  213. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  214. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/rooms.md +0 -0
  215. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/server-rename.md +0 -0
  216. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  217. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  218. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  219. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  220. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  221. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  222. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  223. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  224. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  225. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  226. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  227. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  228. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  229. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  230. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  231. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  232. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  233. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  234. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  235. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  236. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  237. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  238. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/01-pair-programming.md +0 -0
  239. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/02-code-review-ensemble.md +0 -0
  240. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/03-cross-server-delegation.md +0 -0
  241. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/04-knowledge-propagation.md +0 -0
  242. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/05-the-observer.md +0 -0
  243. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/06-cross-server-ops.md +0 -0
  244. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/07-supervisor-intervention.md +0 -0
  245. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/08-apps-as-agents.md +0 -0
  246. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/09-research-swarm.md +0 -0
  247. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases/10-agent-lifecycle.md +0 -0
  248. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/use-cases-index.md +0 -0
  249. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/docs/what-is-culture.md +0 -0
  250. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/README.md +0 -0
  251. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/config.py +0 -0
  252. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/daemon.py +0 -0
  253. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/ipc.py +0 -0
  254. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/message_buffer.py +0 -0
  255. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/skill/SKILL.md +0 -0
  256. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/skill/irc_client.py +0 -0
  257. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/socket_server.py +0 -0
  258. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/packages/agent-harness/webhook.py +0 -0
  259. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  260. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  261. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  262. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  263. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/sonar-project.properties +0 -0
  264. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/__init__.py +0 -0
  265. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/conftest.py +0 -0
  266. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_agent_runner.py +0 -0
  267. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_bot.py +0 -0
  268. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_bot_config.py +0 -0
  269. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_bot_manager.py +0 -0
  270. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_bots_integration.py +0 -0
  271. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_channel.py +0 -0
  272. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_codex_daemon.py +0 -0
  273. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_connection.py +0 -0
  274. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_console_client.py +0 -0
  275. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_console_commands.py +0 -0
  276. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_console_connection.py +0 -0
  277. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_console_icons.py +0 -0
  278. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_console_integration.py +0 -0
  279. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_copilot_daemon.py +0 -0
  280. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_daemon.py +0 -0
  281. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_daemon_ipc.py +0 -0
  282. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_discovery.py +0 -0
  283. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_federation.py +0 -0
  284. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_history.py +0 -0
  285. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_http_listener.py +0 -0
  286. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_integration_layer5.py +0 -0
  287. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_ipc.py +0 -0
  288. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_irc_transport.py +0 -0
  289. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_link_reconnect.py +0 -0
  290. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_mention_target_cleanup.py +0 -0
  291. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_mentions.py +0 -0
  292. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_mesh_config.py +0 -0
  293. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_message.py +0 -0
  294. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_message_buffer.py +0 -0
  295. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_messaging.py +0 -0
  296. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_modes.py +0 -0
  297. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_overview_cli.py +0 -0
  298. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_overview_collector.py +0 -0
  299. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_overview_model.py +0 -0
  300. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_overview_renderer.py +0 -0
  301. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_overview_web.py +0 -0
  302. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_persistence.py +0 -0
  303. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_pidfile.py +0 -0
  304. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_poll_loop.py +0 -0
  305. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_room_persistence.py +0 -0
  306. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_rooms.py +0 -0
  307. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_rooms_federation.py +0 -0
  308. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_rooms_integration.py +0 -0
  309. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_server_icon_skill.py +0 -0
  310. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_setup_update_cli.py +0 -0
  311. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_skill_client.py +0 -0
  312. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_skills.py +0 -0
  313. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_socket_server.py +0 -0
  314. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_supervisor.py +0 -0
  315. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_template_engine.py +0 -0
  316. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_thread_buffer.py +0 -0
  317. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_threads.py +0 -0
  318. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_virtual_client.py +0 -0
  319. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_wait_for_port.py +0 -0
  320. {agentirc_cli-4.4.0 → agentirc_cli-4.4.1}/tests/test_webhook.py +0 -0
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [4.4.1] - 2026-04-07
8
+
9
+
10
+ ### Fixed
11
+
12
+ - Config save operations no longer strip backend-specific fields like acp_command (#150)
13
+ - Agent status detail uses cached description by default, --full for live query; IPC deadline increased to 15s (#152)
14
+ - DMs now activate agents — _detect_and_fire_mention handles direct messages in all backends (#153)
15
+ - ACP agent runner preserves HOME/XDG_CONFIG_HOME for auth tokens; warns on authMethods, fails fast on session creation failure (#154)
16
+ - _coerce_to_acp_agent now copies the icon field (#155)
17
+ - _make_backend_config passes supervisor, poll_interval, sleep_start, sleep_end to non-claude backends (#156)
18
+ - ACP load_config strips unknown fields, matching claude/codex/copilot pattern (#157)
19
+
7
20
  ## [4.4.0] - 2026-04-07
8
21
 
9
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 4.4.0
3
+ Version: 4.4.1
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
@@ -455,8 +455,12 @@ def _make_backend_config(config: DaemonConfig, backend_daemon_config_cls):
455
455
  """Build a backend-specific DaemonConfig from the base config."""
456
456
  return backend_daemon_config_cls(
457
457
  server=config.server,
458
+ supervisor=config.supervisor,
458
459
  webhooks=config.webhooks,
459
460
  buffer_size=config.buffer_size,
461
+ poll_interval=config.poll_interval,
462
+ sleep_start=config.sleep_start,
463
+ sleep_end=config.sleep_end,
460
464
  agents=config.agents,
461
465
  )
462
466
 
@@ -484,6 +488,7 @@ def _coerce_to_acp_agent(agent: AgentConfig):
484
488
  model=agent.model,
485
489
  system_prompt=agent.system_prompt,
486
490
  tags=agent.tags,
491
+ icon=agent.icon,
487
492
  )
488
493
 
489
494
 
@@ -37,7 +37,8 @@ def print_agent_detail(agent, config_path: str, args: argparse.Namespace) -> Non
37
37
  print(f" PID: {pid or '-'}")
38
38
 
39
39
  if status == "running":
40
- resp = asyncio.run(ipc_request(agent_socket_path(agent.nick), "status", query=True))
40
+ query = getattr(args, "full", False)
41
+ resp = asyncio.run(ipc_request(agent_socket_path(agent.nick), "status", query=query))
41
42
  if resp and resp.get("ok"):
42
43
  data = resp.get("data", {})
43
44
  print(f" Activity: {data.get('description', 'nothing')}")
@@ -31,7 +31,7 @@ async def ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
31
31
  writer.write(encode_message(req))
32
32
  await writer.drain()
33
33
  loop = asyncio.get_running_loop()
34
- deadline = loop.time() + 3.0
34
+ deadline = loop.time() + 15.0
35
35
  while True:
36
36
  remaining = deadline - loop.time()
37
37
  if remaining <= 0:
@@ -70,11 +70,12 @@ class ACPAgentRunner:
70
70
  """Start the ACP agent as a subprocess and initialize a session."""
71
71
  self._stopping = False
72
72
 
73
- # Isolate from host config (XDG, etc.)
73
+ # Isolate data/state dirs to prevent session interference, but
74
+ # preserve HOME and XDG_CONFIG_HOME so the agent finds auth tokens.
74
75
  self._isolated_home = tempfile.mkdtemp(prefix="culture-acp-")
75
76
  isolated_env = dict(os.environ)
76
- isolated_env["HOME"] = self._isolated_home
77
- isolated_env.pop("XDG_CONFIG_HOME", None)
77
+ isolated_env["XDG_DATA_HOME"] = os.path.join(self._isolated_home, ".local", "share")
78
+ isolated_env["XDG_STATE_HOME"] = os.path.join(self._isolated_home, ".local", "state")
78
79
 
79
80
  cmd_label = " ".join(self.acp_command)
80
81
  try:
@@ -108,6 +109,18 @@ class ACPAgentRunner:
108
109
  )
109
110
  logger.info("ACP initialized (%s): %s", cmd_label, resp)
110
111
 
112
+ # Log available auth methods as a hint
113
+ init_result = resp.get("result", {})
114
+ auth_methods = init_result.get("authMethods", [])
115
+ if auth_methods:
116
+ descriptions = [m.get("description", m.get("id", "unknown")) for m in auth_methods]
117
+ logger.warning(
118
+ "ACP agent (%s) reports auth methods: %s. "
119
+ "If prompts fail, configure auth tokens.",
120
+ cmd_label,
121
+ ", ".join(descriptions),
122
+ )
123
+
111
124
  # Create a session with model selection
112
125
  session_params = {
113
126
  "cwd": self.directory,
@@ -120,6 +133,13 @@ class ACPAgentRunner:
120
133
  logger.info("ACP session/new raw response: %s", json.dumps(resp)[:500])
121
134
  result = resp.get("result", {})
122
135
  self._session_id = result.get("sessionId")
136
+ if not self._session_id:
137
+ # Session creation failed — likely auth or model issue
138
+ error = resp.get("error", {})
139
+ error_msg = error.get("message", "unknown error")
140
+ if auth_methods:
141
+ error_msg += f". Auth may be required: {', '.join(descriptions)}"
142
+ raise RuntimeError(f"ACP agent ({cmd_label}) session creation failed: {error_msg}")
123
143
  self._running = True
124
144
  logger.info("ACP session started (%s): %s", cmd_label, self._session_id)
125
145
 
@@ -93,8 +93,10 @@ def load_config(path: str | Path) -> DaemonConfig:
93
93
  webhooks = WebhookConfig(**raw.get("webhooks", {}))
94
94
 
95
95
  agents = []
96
+ known_agent_fields = {f.name for f in AgentConfig.__dataclass_fields__.values()}
96
97
  for agent_raw in raw.get("agents", []):
97
- agents.append(AgentConfig(**agent_raw))
98
+ filtered = {k: v for k, v in agent_raw.items() if k in known_agent_fields}
99
+ agents.append(AgentConfig(**filtered))
98
100
 
99
101
  return DaemonConfig(
100
102
  server=server,
@@ -157,6 +159,32 @@ def save_config(path: str | Path, config: DaemonConfig) -> None:
157
159
  raise
158
160
 
159
161
 
162
+ def _load_raw_yaml(path: str | Path) -> dict:
163
+ """Load raw YAML from a config file, returning empty dict if missing."""
164
+ path = Path(path)
165
+ if not path.exists():
166
+ return {}
167
+ with open(path) as f:
168
+ return yaml.safe_load(f) or {}
169
+
170
+
171
+ def _save_raw_yaml(path: str | Path, raw: dict) -> None:
172
+ """Write raw YAML dict atomically."""
173
+ path = Path(path)
174
+ path.parent.mkdir(parents=True, exist_ok=True)
175
+ fd, tmp_path = tempfile.mkstemp(dir=str(path.parent), suffix=".yaml.tmp")
176
+ try:
177
+ with os.fdopen(fd, "w") as f:
178
+ yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
179
+ os.replace(tmp_path, str(path))
180
+ except BaseException:
181
+ try:
182
+ os.unlink(tmp_path)
183
+ except OSError:
184
+ pass
185
+ raise
186
+
187
+
160
188
  def add_agent_to_config(
161
189
  path: str | Path,
162
190
  agent: AgentConfig,
@@ -166,20 +194,21 @@ def add_agent_to_config(
166
194
 
167
195
  If server_name is provided, updates config.server.name.
168
196
  Raises ValueError if an agent with the same nick already exists.
197
+ Operates on raw YAML to preserve backend-specific fields.
169
198
  """
170
- config = load_config_or_default(path)
171
-
172
- if server_name is not None:
173
- config.server.name = server_name
199
+ raw = _load_raw_yaml(path)
200
+ agents = raw.setdefault("agents", [])
174
201
 
175
- # Check for nick collision
176
- for existing in config.agents:
177
- if existing.nick == agent.nick:
202
+ for existing in agents:
203
+ if existing.get("nick") == agent.nick:
178
204
  raise ValueError(f"Agent with nick {agent.nick!r} already exists in config")
179
205
 
180
- config.agents.append(agent)
181
- save_config(path, config)
182
- return config
206
+ if server_name is not None:
207
+ raw.setdefault("server", {})["name"] = server_name
208
+
209
+ agents.append(asdict(agent))
210
+ _save_raw_yaml(path, raw)
211
+ return load_config(path)
183
212
 
184
213
 
185
214
  def rename_server(
@@ -189,23 +218,26 @@ def rename_server(
189
218
  """Rename the server and update all agent nick prefixes.
190
219
 
191
220
  Returns (old_name, [(old_nick, new_nick), ...]).
221
+ Operates on raw YAML to preserve backend-specific fields.
192
222
  """
193
- config = load_config_or_default(path)
194
- old_name = config.server.name
223
+ raw = _load_raw_yaml(path)
224
+ server = raw.get("server", {})
225
+ old_name = server.get("name", ServerConnConfig().name)
195
226
 
196
227
  if old_name == new_name:
197
228
  return old_name, []
198
229
 
199
- # Plan renames and check for collisions before mutating
230
+ agents = raw.get("agents", [])
200
231
  prefix = f"{old_name}-"
201
232
  plan: list[tuple[int, str, str]] = []
202
- for i, agent in enumerate(config.agents):
203
- if agent.nick.startswith(prefix):
204
- new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
205
- plan.append((i, agent.nick, new_nick))
233
+ for i, agent_raw in enumerate(agents):
234
+ nick = agent_raw.get("nick", "")
235
+ if nick.startswith(prefix):
236
+ new_nick = f"{new_name}-{nick[len(prefix):]}"
237
+ plan.append((i, nick, new_nick))
206
238
 
207
239
  planned_nicks = {new_nick for _, _, new_nick in plan}
208
- existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
240
+ existing_nicks = {a.get("nick", "") for a in agents} - {old for _, old, _ in plan}
209
241
  collisions = planned_nicks & existing_nicks
210
242
  if collisions:
211
243
  raise ValueError(
@@ -213,14 +245,15 @@ def rename_server(
213
245
  f"duplicate nick(s): {', '.join(sorted(collisions))}"
214
246
  )
215
247
 
216
- config.server.name = new_name
248
+ server["name"] = new_name
249
+ raw["server"] = server
217
250
 
218
251
  renamed: list[tuple[str, str]] = []
219
252
  for i, old_nick, new_nick in plan:
220
- config.agents[i].nick = new_nick
253
+ agents[i]["nick"] = new_nick
221
254
  renamed.append((old_nick, new_nick))
222
255
 
223
- save_config(path, config)
256
+ _save_raw_yaml(path, raw)
224
257
  return old_name, renamed
225
258
 
226
259
 
@@ -232,19 +265,19 @@ def rename_agent(
232
265
  """Rename an agent's nick in the config.
233
266
 
234
267
  Raises ValueError if old_nick is not found or new_nick already exists.
268
+ Operates on raw YAML to preserve backend-specific fields.
235
269
  """
236
- config = load_config_or_default(path)
270
+ raw = _load_raw_yaml(path)
271
+ agents = raw.get("agents", [])
237
272
 
238
- # Check new nick doesn't collide
239
- for agent in config.agents:
240
- if agent.nick == new_nick:
273
+ for agent_raw in agents:
274
+ if agent_raw.get("nick") == new_nick:
241
275
  raise ValueError(f"Agent with nick {new_nick!r} already exists in config")
242
276
 
243
- # Find and rename
244
- for agent in config.agents:
245
- if agent.nick == old_nick:
246
- agent.nick = new_nick
247
- save_config(path, config)
277
+ for agent_raw in agents:
278
+ if agent_raw.get("nick") == old_nick:
279
+ agent_raw["nick"] = new_nick
280
+ _save_raw_yaml(path, raw)
248
281
  return
249
282
 
250
283
  raise ValueError(f"Agent {old_nick!r} not found in config")
@@ -260,18 +293,14 @@ def remove_agent(
260
293
  agents that the typed schema would strip.
261
294
  Raises ValueError if the agent is not found.
262
295
  """
263
- path = Path(path)
264
- if not path.exists():
296
+ raw = _load_raw_yaml(path)
297
+ if not raw:
265
298
  raise ValueError(f"Agent {nick!r} not found in config")
266
299
 
267
- with open(path) as f:
268
- raw = yaml.safe_load(f) or {}
269
-
270
300
  agents = raw.get("agents", [])
271
301
  for i, agent_raw in enumerate(agents):
272
302
  if agent_raw.get("nick") == nick:
273
303
  agents.pop(i)
274
- with open(path, "w") as f:
275
- yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
304
+ _save_raw_yaml(path, raw)
276
305
  return
277
306
  raise ValueError(f"Agent {nick!r} not found in config")
@@ -213,6 +213,10 @@ class IRCTransport:
213
213
  """Check if the message mentions this agent and fire the callback."""
214
214
  if not self.on_mention:
215
215
  return
216
+ # DMs always activate (target is the agent's own nick)
217
+ if target == self.nick:
218
+ self.on_mention(target, sender, text)
219
+ return
216
220
  short = self.nick.split("-", 1)[1] if "-" in self.nick else None
217
221
  if re.search(rf"@{re.escape(self.nick)}\b", text) or (
218
222
  short and re.search(rf"@{re.escape(short)}\b", text)
@@ -167,6 +167,32 @@ def save_config(path: str | Path, config: DaemonConfig) -> None:
167
167
  raise
168
168
 
169
169
 
170
+ def _load_raw_yaml(path: str | Path) -> dict:
171
+ """Load raw YAML from a config file, returning empty dict if missing."""
172
+ path = Path(path)
173
+ if not path.exists():
174
+ return {}
175
+ with open(path) as f:
176
+ return yaml.safe_load(f) or {}
177
+
178
+
179
+ def _save_raw_yaml(path: str | Path, raw: dict) -> None:
180
+ """Write raw YAML dict atomically."""
181
+ path = Path(path)
182
+ path.parent.mkdir(parents=True, exist_ok=True)
183
+ fd, tmp_path = tempfile.mkstemp(dir=str(path.parent), suffix=".yaml.tmp")
184
+ try:
185
+ with os.fdopen(fd, "w") as f:
186
+ yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
187
+ os.replace(tmp_path, str(path))
188
+ except BaseException:
189
+ try:
190
+ os.unlink(tmp_path)
191
+ except OSError:
192
+ pass
193
+ raise
194
+
195
+
170
196
  def add_agent_to_config(
171
197
  path: str | Path,
172
198
  agent: AgentConfig,
@@ -176,20 +202,21 @@ def add_agent_to_config(
176
202
 
177
203
  If server_name is provided, updates config.server.name.
178
204
  Raises ValueError if an agent with the same nick already exists.
205
+ Operates on raw YAML to preserve backend-specific fields.
179
206
  """
180
- config = load_config_or_default(path)
181
-
182
- if server_name is not None:
183
- config.server.name = server_name
207
+ raw = _load_raw_yaml(path)
208
+ agents = raw.setdefault("agents", [])
184
209
 
185
- # Check for nick collision
186
- for existing in config.agents:
187
- if existing.nick == agent.nick:
210
+ for existing in agents:
211
+ if existing.get("nick") == agent.nick:
188
212
  raise ValueError(f"Agent with nick {agent.nick!r} already exists in config")
189
213
 
190
- config.agents.append(agent)
191
- save_config(path, config)
192
- return config
214
+ if server_name is not None:
215
+ raw.setdefault("server", {})["name"] = server_name
216
+
217
+ agents.append(asdict(agent))
218
+ _save_raw_yaml(path, raw)
219
+ return load_config(path)
193
220
 
194
221
 
195
222
  def rename_server(
@@ -199,23 +226,26 @@ def rename_server(
199
226
  """Rename the server and update all agent nick prefixes.
200
227
 
201
228
  Returns (old_name, [(old_nick, new_nick), ...]).
229
+ Operates on raw YAML to preserve backend-specific fields.
202
230
  """
203
- config = load_config_or_default(path)
204
- old_name = config.server.name
231
+ raw = _load_raw_yaml(path)
232
+ server = raw.get("server", {})
233
+ old_name = server.get("name", ServerConnConfig().name)
205
234
 
206
235
  if old_name == new_name:
207
236
  return old_name, []
208
237
 
209
- # Plan renames and check for collisions before mutating
238
+ agents = raw.get("agents", [])
210
239
  prefix = f"{old_name}-"
211
240
  plan: list[tuple[int, str, str]] = []
212
- for i, agent in enumerate(config.agents):
213
- if agent.nick.startswith(prefix):
214
- new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
215
- plan.append((i, agent.nick, new_nick))
241
+ for i, agent_raw in enumerate(agents):
242
+ nick = agent_raw.get("nick", "")
243
+ if nick.startswith(prefix):
244
+ new_nick = f"{new_name}-{nick[len(prefix):]}"
245
+ plan.append((i, nick, new_nick))
216
246
 
217
247
  planned_nicks = {new_nick for _, _, new_nick in plan}
218
- existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
248
+ existing_nicks = {a.get("nick", "") for a in agents} - {old for _, old, _ in plan}
219
249
  collisions = planned_nicks & existing_nicks
220
250
  if collisions:
221
251
  raise ValueError(
@@ -223,14 +253,15 @@ def rename_server(
223
253
  f"duplicate nick(s): {', '.join(sorted(collisions))}"
224
254
  )
225
255
 
226
- config.server.name = new_name
256
+ server["name"] = new_name
257
+ raw["server"] = server
227
258
 
228
259
  renamed: list[tuple[str, str]] = []
229
260
  for i, old_nick, new_nick in plan:
230
- config.agents[i].nick = new_nick
261
+ agents[i]["nick"] = new_nick
231
262
  renamed.append((old_nick, new_nick))
232
263
 
233
- save_config(path, config)
264
+ _save_raw_yaml(path, raw)
234
265
  return old_name, renamed
235
266
 
236
267
 
@@ -242,19 +273,19 @@ def rename_agent(
242
273
  """Rename an agent's nick in the config.
243
274
 
244
275
  Raises ValueError if old_nick is not found or new_nick already exists.
276
+ Operates on raw YAML to preserve backend-specific fields.
245
277
  """
246
- config = load_config_or_default(path)
278
+ raw = _load_raw_yaml(path)
279
+ agents = raw.get("agents", [])
247
280
 
248
- # Check new nick doesn't collide
249
- for agent in config.agents:
250
- if agent.nick == new_nick:
281
+ for agent_raw in agents:
282
+ if agent_raw.get("nick") == new_nick:
251
283
  raise ValueError(f"Agent with nick {new_nick!r} already exists in config")
252
284
 
253
- # Find and rename
254
- for agent in config.agents:
255
- if agent.nick == old_nick:
256
- agent.nick = new_nick
257
- save_config(path, config)
285
+ for agent_raw in agents:
286
+ if agent_raw.get("nick") == old_nick:
287
+ agent_raw["nick"] = new_nick
288
+ _save_raw_yaml(path, raw)
258
289
  return
259
290
 
260
291
  raise ValueError(f"Agent {old_nick!r} not found in config")
@@ -268,16 +299,17 @@ def archive_agent(
268
299
  """Set the archived flag on an agent.
269
300
 
270
301
  Raises ValueError if the agent is not found.
302
+ Operates on raw YAML to preserve backend-specific fields.
271
303
  """
272
304
  import time
273
305
 
274
- config = load_config_or_default(path)
275
- for agent in config.agents:
276
- if agent.nick == nick:
277
- agent.archived = True
278
- agent.archived_at = time.strftime("%Y-%m-%d")
279
- agent.archived_reason = reason
280
- save_config(path, config)
306
+ raw = _load_raw_yaml(path)
307
+ for agent_raw in raw.get("agents", []):
308
+ if agent_raw.get("nick") == nick:
309
+ agent_raw["archived"] = True
310
+ agent_raw["archived_at"] = time.strftime("%Y-%m-%d")
311
+ agent_raw["archived_reason"] = reason
312
+ _save_raw_yaml(path, raw)
281
313
  return
282
314
  raise ValueError(f"Agent {nick!r} not found in config")
283
315
 
@@ -289,16 +321,17 @@ def unarchive_agent(
289
321
  """Clear the archived flag on an agent.
290
322
 
291
323
  Raises ValueError if not found or not currently archived.
324
+ Operates on raw YAML to preserve backend-specific fields.
292
325
  """
293
- config = load_config_or_default(path)
294
- for agent in config.agents:
295
- if agent.nick == nick:
296
- if not agent.archived:
326
+ raw = _load_raw_yaml(path)
327
+ for agent_raw in raw.get("agents", []):
328
+ if agent_raw.get("nick") == nick:
329
+ if not agent_raw.get("archived"):
297
330
  raise ValueError(f"Agent {nick!r} is not archived")
298
- agent.archived = False
299
- agent.archived_at = ""
300
- agent.archived_reason = ""
301
- save_config(path, config)
331
+ agent_raw["archived"] = False
332
+ agent_raw["archived_at"] = ""
333
+ agent_raw["archived_reason"] = ""
334
+ _save_raw_yaml(path, raw)
302
335
  return
303
336
  raise ValueError(f"Agent {nick!r} not found in config")
304
337
 
@@ -313,19 +346,15 @@ def remove_agent(
313
346
  acp_command) on other agents that the typed schema would strip.
314
347
  Raises ValueError if the agent is not found.
315
348
  """
316
- path = Path(path)
317
- if not path.exists():
349
+ raw = _load_raw_yaml(path)
350
+ if not raw:
318
351
  raise ValueError(f"Agent {nick!r} not found in config")
319
352
 
320
- with open(path) as f:
321
- raw = yaml.safe_load(f) or {}
322
-
323
353
  agents = raw.get("agents", [])
324
354
  for i, agent_raw in enumerate(agents):
325
355
  if agent_raw.get("nick") == nick:
326
356
  agents.pop(i)
327
- with open(path, "w") as f:
328
- yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
357
+ _save_raw_yaml(path, raw)
329
358
  return
330
359
  raise ValueError(f"Agent {nick!r} not found in config")
331
360
 
@@ -337,24 +366,26 @@ def archive_server(
337
366
  """Set the archived flag on the server and all its agents.
338
367
 
339
368
  Returns a list of archived agent nicks.
369
+ Operates on raw YAML to preserve backend-specific fields.
340
370
  """
341
371
  import time
342
372
 
343
- config = load_config_or_default(path)
373
+ raw = _load_raw_yaml(path)
344
374
  today = time.strftime("%Y-%m-%d")
345
375
 
346
- config.server.archived = True
347
- config.server.archived_at = today
348
- config.server.archived_reason = reason
376
+ server = raw.setdefault("server", {})
377
+ server["archived"] = True
378
+ server["archived_at"] = today
379
+ server["archived_reason"] = reason
349
380
 
350
381
  archived_nicks = []
351
- for agent in config.agents:
352
- agent.archived = True
353
- agent.archived_at = today
354
- agent.archived_reason = reason
355
- archived_nicks.append(agent.nick)
382
+ for agent_raw in raw.get("agents", []):
383
+ agent_raw["archived"] = True
384
+ agent_raw["archived_at"] = today
385
+ agent_raw["archived_reason"] = reason
386
+ archived_nicks.append(agent_raw.get("nick", ""))
356
387
 
357
- save_config(path, config)
388
+ _save_raw_yaml(path, raw)
358
389
  return archived_nicks
359
390
 
360
391
 
@@ -364,20 +395,22 @@ def unarchive_server(
364
395
  """Clear the archived flag on the server and all its agents.
365
396
 
366
397
  Returns a list of unarchived agent nicks.
398
+ Operates on raw YAML to preserve backend-specific fields.
367
399
  """
368
- config = load_config_or_default(path)
400
+ raw = _load_raw_yaml(path)
369
401
 
370
- config.server.archived = False
371
- config.server.archived_at = ""
372
- config.server.archived_reason = ""
402
+ server = raw.setdefault("server", {})
403
+ server["archived"] = False
404
+ server["archived_at"] = ""
405
+ server["archived_reason"] = ""
373
406
 
374
407
  unarchived_nicks = []
375
- for agent in config.agents:
376
- if agent.archived:
377
- agent.archived = False
378
- agent.archived_at = ""
379
- agent.archived_reason = ""
380
- unarchived_nicks.append(agent.nick)
381
-
382
- save_config(path, config)
408
+ for agent_raw in raw.get("agents", []):
409
+ if agent_raw.get("archived"):
410
+ agent_raw["archived"] = False
411
+ agent_raw["archived_at"] = ""
412
+ agent_raw["archived_reason"] = ""
413
+ unarchived_nicks.append(agent_raw.get("nick", ""))
414
+
415
+ _save_raw_yaml(path, raw)
383
416
  return unarchived_nicks
@@ -213,6 +213,10 @@ class IRCTransport:
213
213
  """Check if the message mentions this agent and fire the callback."""
214
214
  if not self.on_mention:
215
215
  return
216
+ # DMs always activate (target is the agent's own nick)
217
+ if target == self.nick:
218
+ self.on_mention(target, sender, text)
219
+ return
216
220
  short = self.nick.split("-", 1)[1] if "-" in self.nick else None
217
221
  if re.search(rf"@{re.escape(self.nick)}\b", text) or (
218
222
  short and re.search(rf"@{re.escape(short)}\b", text)