agentirc-cli 9.13.0__tar.gz → 9.14.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 (211) hide show
  1. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.github/workflows/tests.yml +18 -0
  2. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/CHANGELOG.md +79 -0
  3. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/PKG-INFO +1 -1
  4. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/event_subscriptions.py +23 -9
  5. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/protocol/replies.py +11 -0
  6. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/metrics.py +7 -0
  7. agentirc_cli-9.14.0/agentirc/accounts.py +574 -0
  8. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/channel.py +29 -11
  9. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/cli.py +10 -1
  10. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/client.py +706 -68
  11. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/config.py +121 -6
  12. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/ircd.py +154 -16
  13. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/protocol.py +28 -0
  14. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/skills/history.py +8 -1
  15. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/skills/rooms.py +88 -14
  16. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/skills/threads.py +56 -6
  17. agentirc_cli-9.14.0/docs/accounts.md +501 -0
  18. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/api-stability.md +27 -0
  19. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/cli.md +3 -0
  20. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/extension-api.md +24 -4
  21. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/pyproject.toml +1 -1
  22. agentirc_cli-9.14.0/scripts/sonar-pr-issues.py +168 -0
  23. agentirc_cli-9.14.0/sonar-project.properties +6 -0
  24. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/_helpers.py +3 -0
  25. agentirc_cli-9.14.0/tests/test_accounts_grants.py +230 -0
  26. agentirc_cli-9.14.0/tests/test_accounts_hardening.py +704 -0
  27. agentirc_cli-9.14.0/tests/test_accounts_hardening_medium.py +625 -0
  28. agentirc_cli-9.14.0/tests/test_accounts_review66.py +385 -0
  29. agentirc_cli-9.14.0/tests/test_accounts_revoke.py +310 -0
  30. agentirc_cli-9.14.0/tests/test_accounts_sasl.py +985 -0
  31. agentirc_cli-9.14.0/tests/test_accounts_sighup.py +133 -0
  32. agentirc_cli-9.14.0/tests/test_cap_registration_hold.py +132 -0
  33. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_events_reserved_nick.py +2 -2
  34. agentirc_cli-9.14.0/tests/test_sonar_pr_issues.py +128 -0
  35. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_verb_discovery.py +9 -10
  36. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/uv.lock +1 -1
  37. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/SKILL.md +0 -0
  38. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  39. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  40. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  41. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  42. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  43. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  44. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/cicd/SKILL.md +0 -0
  45. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  46. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  47. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  48. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  49. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  50. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/communicate/SKILL.md +0 -0
  51. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  52. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  53. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  54. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  55. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/recall/SKILL.md +0 -0
  56. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/recall/scripts/recall.sh +0 -0
  57. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/remember/SKILL.md +0 -0
  58. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/remember/scripts/remember.sh +0 -0
  59. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  60. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  61. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/think/SKILL.md +0 -0
  62. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills/think/scripts/think.sh +0 -0
  63. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.claude/skills.local.yaml.example +0 -0
  64. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/current +0 -0
  65. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/current_plan +0 -0
  66. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/frames/agentirc-9-7-0-ships-an-embedded-bot-framework-bot.json +0 -0
  67. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/frames/agentirc-now-speaks-presence-the-ircd-parses-resid.json +0 -0
  68. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/frames/agentirc-ships-an-agent-accessibility-release-ai-a.json +0 -0
  69. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/plans/agentirc-9-7-0-ships-an-embedded-bot-framework-bot.json +0 -0
  70. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/plans/agentirc-now-speaks-presence-the-ircd-parses-resid.json +0 -0
  71. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.devague/plans/agentirc-ships-an-agent-accessibility-release-ai-a.json +0 -0
  72. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.github/workflows/publish.yml +0 -0
  73. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/.gitignore +0 -0
  74. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/CLAUDE.md +0 -0
  75. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/LICENSE +0 -0
  76. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/README.md +0 -0
  77. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/TRADEMARKS.md +0 -0
  78. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/__init__.py +0 -0
  79. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/__main__.py +0 -0
  80. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/__init__.py +0 -0
  81. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/aio.py +0 -0
  82. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/bots/__init__.py +0 -0
  83. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/bots/bot_manager.py +0 -0
  84. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/bots/http_listener.py +0 -0
  85. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/cli_client.py +0 -0
  86. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/cli_shared/__init__.py +0 -0
  87. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/cli_shared/constants.py +0 -0
  88. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/cli_shared/mesh.py +0 -0
  89. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/constants.py +0 -0
  90. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/pidfile.py +0 -0
  91. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/protocol/__init__.py +0 -0
  92. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/protocol/message.py +0 -0
  93. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/__init__.py +0 -0
  94. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/audit.py +0 -0
  95. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/context.py +0 -0
  96. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/tracing.py +0 -0
  97. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/_internal/virtual_client.py +0 -0
  98. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/agent_client.py +0 -0
  99. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/__init__.py +0 -0
  100. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/bot.py +0 -0
  101. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/bot_manager.py +0 -0
  102. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/cli.py +0 -0
  103. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/config.py +0 -0
  104. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/filter_dsl.py +0 -0
  105. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/http_listener.py +0 -0
  106. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/template_engine.py +0 -0
  107. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/bots/virtual_client.py +0 -0
  108. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/events.py +0 -0
  109. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/history_store.py +0 -0
  110. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/remote_client.py +0 -0
  111. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/room_store.py +0 -0
  112. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/rooms_util.py +0 -0
  113. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/server_link.py +0 -0
  114. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/skill.py +0 -0
  115. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/skills/__init__.py +0 -0
  116. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/skills/icon.py +0 -0
  117. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/skills/presence.py +0 -0
  118. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/thread_store.py +0 -0
  119. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/agentirc/virtual_client.py +0 -0
  120. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/agent-walkthrough.md +0 -0
  121. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/bots.md +0 -0
  122. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/deployment.md +0 -0
  123. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/plans/2026-06-12-agentirc-9-7-0-ships-an-embedded-bot-framework-bot.md +0 -0
  124. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/plans/2026-07-01-agentirc-ships-an-agent-accessibility-release-ai-a.md +0 -0
  125. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/plans/2026-07-07-agentirc-now-speaks-presence-the-ircd-parses-resid.md +0 -0
  126. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/specs/2026-06-12-agentirc-9-7-0-ships-an-embedded-bot-framework-bot.md +0 -0
  127. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/specs/2026-07-01-agentirc-ships-an-agent-accessibility-release-ai-a.md +0 -0
  128. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/specs/2026-07-02-agent-accessibility-gap-verification.md +0 -0
  129. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/specs/2026-07-02-agent-accessibility-release-audit.md +0 -0
  130. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/specs/2026-07-07-agentirc-now-speaks-presence-the-ircd-parses-resid.md +0 -0
  131. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/steward/onboarding.md +0 -0
  132. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/superpowers/specs/2026-04-30-bootstrap-design.md +0 -0
  133. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/superpowers/specs/2026-05-01-bot-extension-api-design.md +0 -0
  134. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/docs/superpowers/specs/2026-05-01-task14-audit.md +0 -0
  135. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/__init__.py +0 -0
  136. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/__init__.py +0 -0
  137. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_bot.py +0 -0
  138. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_bot_host.py +0 -0
  139. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_bot_manager.py +0 -0
  140. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_cli_bot.py +0 -0
  141. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_config_bots.py +0 -0
  142. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_filter_dsl.py +0 -0
  143. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_http_listener.py +0 -0
  144. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_public_surface.py +0 -0
  145. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/bots/test_template_engine.py +0 -0
  146. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/conftest.py +0 -0
  147. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/__init__.py +0 -0
  148. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/_fakes.py +0 -0
  149. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/_metrics_helpers.py +0 -0
  150. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_emit.py +0 -0
  151. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_lifecycle.py +0 -0
  152. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_module.py +0 -0
  153. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_parse_error.py +0 -0
  154. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_config.py +0 -0
  155. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_dispatch_span.py +0 -0
  156. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_emit_event_span.py +0 -0
  157. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_metrics_init.py +0 -0
  158. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_metrics_s2s.py +0 -0
  159. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_outbound_inject.py +0 -0
  160. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_parse_error.py +0 -0
  161. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_s2s_relay_span.py +0 -0
  162. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_server_init.py +0 -0
  163. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_server_link_inject.py +0 -0
  164. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/telemetry/test_tracing.py +0 -0
  165. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_agent_client.py +0 -0
  166. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_agent_walkthrough.py +0 -0
  167. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_api_stability_embedding.py +0 -0
  168. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_bot_capability.py +0 -0
  169. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_channel.py +0 -0
  170. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_cli.py +0 -0
  171. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_cli_client_verbs.py +0 -0
  172. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_cli_json.py +0 -0
  173. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_client_backfill.py +0 -0
  174. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_config_loader.py +0 -0
  175. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_connection.py +0 -0
  176. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_discovery.py +0 -0
  177. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_disk_paths.py +0 -0
  178. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_dm_history.py +0 -0
  179. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_docs_promises.py +0 -0
  180. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_error_tokens.py +0 -0
  181. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_event_subscriptions.py +0 -0
  182. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_eventpub.py +0 -0
  183. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_events_basic.py +0 -0
  184. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_events_catalog.py +0 -0
  185. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_events_federation.py +0 -0
  186. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_events_history.py +0 -0
  187. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_events_lifecycle.py +0 -0
  188. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_federation.py +0 -0
  189. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_history.py +0 -0
  190. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_history_since.py +0 -0
  191. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_link_reconnect.py +0 -0
  192. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_liveness.py +0 -0
  193. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_long_messages.py +0 -0
  194. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_mentions.py +0 -0
  195. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_message_tags.py +0 -0
  196. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_messaging.py +0 -0
  197. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_modes.py +0 -0
  198. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_motd.py +0 -0
  199. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_presence.py +0 -0
  200. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_presence_federation.py +0 -0
  201. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_presence_stale.py +0 -0
  202. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_protocol_bot_exports.py +0 -0
  203. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_protocol_presence.py +0 -0
  204. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_reconnect_catchup.py +0 -0
  205. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_room_persistence.py +0 -0
  206. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_rooms_federation.py +0 -0
  207. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_rooms_integration.py +0 -0
  208. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_server_icon_skill.py +0 -0
  209. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_skills.py +0 -0
  210. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_threads.py +0 -0
  211. {agentirc_cli-9.13.0 → agentirc_cli-9.14.0}/tests/test_wire_format_envelope.py +0 -0
@@ -24,6 +24,11 @@ jobs:
24
24
  runs-on: ubuntu-latest
25
25
  permissions:
26
26
  contents: read
27
+ # A presence flag, not the secret: `secrets.*` isn't allowed in `if:`.
28
+ # Fork and Dependabot PRs get no SONAR_TOKEN, so the SonarCloud gate skips
29
+ # there instead of waiting out its 300 s timeout.
30
+ env:
31
+ SONAR_TOKEN_PRESENT: ${{ secrets.SONAR_TOKEN != '' }}
27
32
  steps:
28
33
  - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
29
34
 
@@ -57,6 +62,19 @@ jobs:
57
62
  UV_PROJECT_ENVIRONMENT: .venv
58
63
  run: uv run pytest -n auto -v
59
64
 
65
+ # No unresolved SonarCloud issue on the PR. SonarCloud's automatic analysis
66
+ # (the GitHub App) scans the PR; a code smell leaves its quality gate green,
67
+ # so this fails the job while the PR has any OPEN or CONFIRMED issue.
68
+ # ACCEPTED and FALSE_POSITIVE ones pass. Cited from culture-rules (#43).
69
+ - name: SonarCloud unresolved issues
70
+ if: steps.bootstrap.outputs.ready == 'true' && env.SONAR_TOKEN_PRESENT == 'true' && github.event_name == 'pull_request'
71
+ env:
72
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
73
+ SONAR_HOST_URL: https://sonarcloud.io
74
+ PR_NUMBER: ${{ github.event.pull_request.number }}
75
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
76
+ run: python3 scripts/sonar-pr-issues.py
77
+
60
78
  version-check:
61
79
  needs: lint
62
80
  runs-on: ubuntu-latest
@@ -4,6 +4,85 @@ 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
+ ## [9.14.0] - 2026-11-01
8
+
9
+ ### Added
10
+
11
+ - Agent accounts (culture#506): a reserved nick, a secret (only its sha256 is
12
+ stored), a role (`resident`, `operator`, `guest`), channel grants (`read`,
13
+ `speak`) and a state (`active`, `suspended`, `revoked`), read from
14
+ `<data_dir>/accounts.json` (written by culture). The file reloads within
15
+ about 1 s of a change and on SIGHUP; a malformed file never crashes the
16
+ server — it is rejected whole, the last good table is kept, and the nicks it
17
+ names stay reserved (fail closed). See docs/accounts.md.
18
+ - SASL PLAIN (IRCv3 `sasl` capability, numerics 900/903-908). An account nick
19
+ never gets `001` unless the connection authenticated as exactly that
20
+ account, on every registration path. SASL is accepted only from loopback
21
+ (`accounts.trust_loopback`, default true) or `accounts.trusted_cidrs`; other
22
+ peers are refused before they are asked for the secret. A correct login
23
+ reclaims the nick from any other local holder.
24
+ - Channel grants and roles are enforced on JOIN, PRIVMSG/NOTICE, HISTORY,
25
+ BACKFILL, EVENTSUB, EVENTPUB, THREAD, TOPIC and the room verbs; operator-only
26
+ room verbs need the `operator` role; guests may only hold grants on channels
27
+ under `accounts.guest_channel_prefixes`. Clients without an account are not
28
+ gated.
29
+ - Live revoke: a session whose account is revoked, suspended, removed or
30
+ re-keyed is closed within 5 s; a nick squatted before its account existed
31
+ is evicted too.
32
+ - Failed-authentication logging (nick, peer, reason code; never secret
33
+ material), a per (address, account) rate limit (`accounts.auth_failure_limit`,
34
+ default 10 per `accounts.auth_failure_window` 60 s) and the
35
+ `culture.auth.failures` counter (attribute `reason`).
36
+
37
+ ### Changed
38
+
39
+ - Registration now waits for IRCv3 capability negotiation to finish: a client
40
+ that opens negotiation with `CAP LS` gets no `001` welcome and no `SNICK`
41
+ relay to linked peers until it sends `CAP END` (even if `NICK` and `USER`
42
+ already arrived). `CAP END` completes the held registration. A client that
43
+ never sends `CAP` registers exactly as before, and a bare `CAP REQ` without a
44
+ preceding `CAP LS` does not hold registration (legacy-client compatibility).
45
+ A client that sends `CAP LS` and never `CAP END` stays unregistered. This is
46
+ the groundwork for SASL authentication of reserved nicks (culture#506).
47
+ - Before registration only CAP, NICK, USER, PASS, PING, PONG, AUTHENTICATE
48
+ and QUIT are accepted (plus the EVENT*/BACKFILL verbs, which keep their
49
+ `EVENTERR :not-registered` reply); anything else gets `451
50
+ ERR_NOTREGISTERED`, and a pre-registration QUIT emits no event. Applies to
51
+ all clients.
52
+ - Local nicks must be printable ASCII, and a nick that matches an account nick
53
+ under an ASCII case fold without being equal to it is refused (432).
54
+ - Accounts that are not operators are no longer auto-opped as a channel's
55
+ first joiner.
56
+ - EVENTSUB scopes channel-less events for non-operator account sessions: a
57
+ payload `channel` / `target` / `room_id` the session cannot read drops the
58
+ event, and a `channels` list (e.g. `user.quit`) is filtered to readable
59
+ channels (dropped when none remain). Clients without an account are
60
+ unchanged.
61
+ - Channel-less `EVENTPUB <type> *` is refused (`permission-denied`) for
62
+ `guest` accounts and for sessions whose account is suspended, revoked or
63
+ gone; `operator` and `resident` accounts and clients without an account
64
+ are unchanged.
65
+ - `ROOMINVITE` needs read access on the channel for an account session;
66
+ without it the reply is the same `403` as for a nonexistent channel.
67
+ - The failed-authentication limiter is a bounded LRU (4096 keys, O(1)
68
+ amortised per failure) instead of growing without bound under churning
69
+ `authcid`s (agentirc#65).
70
+ - Account lookups no longer `stat()` the accounts file: they read the cached
71
+ table, refreshed by the 1 s watcher, SIGHUP, and at most three file checks
72
+ per connection on the registration/SASL path. Grant and role changes reach
73
+ connected sessions within about 1 s instead of on the next lookup.
74
+
75
+ ## [9.13.1] - 2026-10-30
76
+
77
+ ### Added
78
+
79
+ - CI fails a pull request that has an unresolved SonarCloud issue: after
80
+ SonarCloud's automatic analysis of the PR's head commit,
81
+ `scripts/sonar-pr-issues.py` (cited from culture-rules) fails on any OPEN or
82
+ CONFIRMED issue. ACCEPTED and FALSE_POSITIVE issues pass. Same gate as
83
+ culture, irc-lens and culture-library. `sonar-project.properties` names the
84
+ project key for it.
85
+
7
86
  ## [9.13.0] - 2026-10-30
8
87
 
9
88
  First agentirc release that presents itself as CULTURE.DEV on the wire —
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentirc-cli
3
- Version: 9.13.0
3
+ Version: 9.14.0
4
4
  Summary: Agent-friendly IRCd: server core for AI agent meshes
5
5
  Project-URL: Homepage, https://github.com/agentculture/agentirc
6
6
  Project-URL: Issues, https://github.com/agentculture/agentirc/issues
@@ -207,15 +207,29 @@ class SubscriptionRegistry:
207
207
  # ``add`` or ``remove`` and mutate the dicts. The ``list()`` snapshots
208
208
  # avoid ``RuntimeError: dictionary changed size during iteration``.
209
209
  for client, per_client in list(self._subs.items()): # NOSONAR python:S7504: defensive snapshot vs. concurrent mutation
210
- for sub in list(per_client.values()): # NOSONAR python:S7504: defensive snapshot vs. concurrent mutation
211
- if sub.dropped:
212
- continue
213
- if not sub.matches(event):
214
- continue
215
- try:
216
- sub.queue.put_nowait(event)
217
- except asyncio.QueueFull:
218
- await self._handle_overflow(client, sub)
210
+ # Read grants (culture#506 h1): an account session only receives
211
+ # events it could read through any other path. Checked per event,
212
+ # live, so a grant change applies to the very next event.
213
+ # Channel-less events are scoped to the channels the session can
214
+ # read (agentirc#66 review): ``visible_event`` may return a copy
215
+ # with a filtered channel list, or ``None`` to drop the event.
216
+ visible_event = getattr(client, "visible_event", None)
217
+ delivered = event if visible_event is None else visible_event(event)
218
+ if delivered is None:
219
+ continue
220
+ await self._deliver_to_client(client, per_client, delivered)
221
+
222
+ async def _deliver_to_client(
223
+ self, client: Client, per_client: dict[str, Subscription], delivered: Event
224
+ ) -> None:
225
+ """Enqueue *delivered* on each live, matching subscription of *client*."""
226
+ for sub in list(per_client.values()): # NOSONAR python:S7504: defensive snapshot vs. concurrent mutation
227
+ if sub.dropped or not sub.matches(delivered):
228
+ continue
229
+ try:
230
+ sub.queue.put_nowait(delivered)
231
+ except asyncio.QueueFull:
232
+ await self._handle_overflow(client, sub)
219
233
 
220
234
  async def _handle_overflow(self, client: Client, sub: Subscription) -> None:
221
235
  sub.dropped = True
@@ -43,13 +43,24 @@ ERR_ERRONEUSNICKNAME = "432"
43
43
  ERR_NICKNAMEINUSE = "433"
44
44
  ERR_USERNOTINCHANNEL = "441"
45
45
  ERR_NOTONCHANNEL = "442"
46
+ ERR_NOTREGISTERED = "451"
46
47
  ERR_NEEDMOREPARAMS = "461"
47
48
  ERR_ALREADYREGISTRED = "462"
48
49
  ERR_CHANOPRIVSNEEDED = "482"
49
50
  ERR_USERSDONTMATCH = "502"
50
51
 
52
+ # SASL (IRCv3 sasl-3.1)
53
+ RPL_LOGGEDIN = "900"
54
+ RPL_SASLSUCCESS = "903"
55
+ ERR_SASLFAIL = "904"
56
+ ERR_SASLTOOLONG = "905"
57
+ ERR_SASLABORTED = "906"
58
+ ERR_SASLALREADY = "907"
59
+ RPL_SASLMECHS = "908"
60
+
51
61
  # Error/reply message texts
52
62
  MSG_NEEDMOREPARAMS = "Not enough parameters"
63
+ MSG_NOTREGISTERED = "You have not registered"
53
64
  MSG_NOSUCHCHANNEL = "No such channel"
54
65
  MSG_NOSUCHNICK = "No such nick"
55
66
  MSG_NOTONCHANNEL = "You're not on that channel"
@@ -66,6 +66,8 @@ class MetricsRegistry:
66
66
  # Bots (Plan 7)
67
67
  bot_invocations: Counter
68
68
  bot_webhook_duration: Histogram
69
+ # Agent accounts (culture#506 t10)
70
+ auth_failures: Counter
69
71
 
70
72
 
71
73
  def reset_for_tests() -> None:
@@ -181,6 +183,11 @@ def _build_registry(meter: Meter) -> MetricsRegistry:
181
183
  unit="s",
182
184
  description="Webhook request handler duration by bot and status_class=2xx|3xx|4xx|5xx",
183
185
  ),
186
+ # Agent accounts (culture#506 t10)
187
+ auth_failures=meter.create_counter(
188
+ "culture.auth.failures",
189
+ description="Failed authentications by reason (bad_secret, unknown_account, ...)",
190
+ ),
184
191
  )
185
192
 
186
193